- Explicación técnica paso a paso del proceso desde el momento en que se presiona el botón de encendido de la computadora hasta que se ejecuta el kernel de Linux
- Aborda en detalle cómo la CPU comienza en modo real (real mode) y luego entra en modo protegido (protected mode) y modo largo (long mode)
- Describe en detalle el rol y el funcionamiento de cada etapa, como el firmware BIOS/UEFI, el bootloader (GRUB) y la descompresión del kernel y reubicación de direcciones
- Explica conceptos clave necesarios para la inicialización del kernel, como mapeo de memoria, interrupciones, tablas de páginas y kASLR, con ejemplos breves
- Al entender los mecanismos internos del arranque de Linux, ofrece una perspectiva sobre arquitectura de sistemas, seguridad y optimización de rendimiento
Part 1 — Del botón de encendido a la primera ejecución del kernel
-
Cuando se presiona el botón de encendido, la CPU se reinicia en modo real (real mode) y ejecuta sus primeras instrucciones
- El modo real es un esquema de direccionamiento simple que existe desde la era del 8086, y calcula direcciones físicas combinando segmento (segment) y desplazamiento (offset)
- Ejemplo:
physical_address = (segment << 4) + offset - Después del reset, la CPU salta a la dirección
0xFFFFFFF0(reset vector) y cede el control al firmware
-
Los registros (registers) son ranuras de almacenamiento ultrarrápidas dentro de la CPU, como CS (code segment) e IP (instruction pointer)
- CS indica la ubicación del código actual, e IP apunta a la siguiente instrucción a ejecutar
BIOS y UEFI
- BIOS es un firmware antiguo que, después del POST (autoprueba de encendido), verifica el orden de arranque y busca un disco arrancable
- Un disco arrancable se identifica porque el final de su primer sector de 512 bytes está marcado con
0x55AA - BIOS copia ese sector a la dirección
0x7C00, luego salta allí para ejecutarlo
- Un disco arrancable se identifica porque el final de su primer sector de 512 bytes está marcado con
- UEFI es la alternativa moderna, capaz de entender directamente el sistema de archivos y cargar programas de arranque más grandes
- A diferencia de BIOS, no tiene la restricción del “primer sector” y entrega información del sistema más rica al sistema operativo
Bootloader
- El bootloader es el programa que carga el kernel en memoria y lo prepara para su ejecución
- Uno de los más usados es GRUB, que lee archivos de configuración y carga en memoria el kernel y el initrd (ramdisk inicial)
- El archivo del kernel está compuesto por un pequeño programa de configuración para modo real y el cuerpo principal comprimido del kernel
- GRUB escribe información como la ubicación del kernel, la línea de comandos y la posición del initrd en la estructura setup header, y luego salta al código de configuración del kernel
Programa de configuración (setup code)
- Su función es crear un espacio de trabajo predecible antes de ejecutar el kernel
- Alinea los registros de segmento (CS, DS, SS) y limpia el direction flag para que las copias de memoria funcionen de forma consistente
- Crea una pila (stack) para guardar datos temporales durante las llamadas a funciones
- Inicializa en cero la sección BSS (espacio de variables globales que debe comenzar con valor 0)
- Si está la opción
earlyprintk, puede configurar el puerto serial para mostrar mensajes de depuración temprana - Solicita al firmware el mapa de RAM (e820) para conocer las regiones de memoria disponibles y las reservadas
- Cuando todo está listo, llama a
main, la primera función en C, y luego entra en la etapa de cambio de modo
Interrupciones (interrupts)
- Las interrupciones son un mecanismo por el que la CPU pausa temporalmente su trabajo actual para atender algo urgente
- Ejemplos típicos son eventos de hardware como entradas de teclado o temporizadores
- Las interrupciones enmascarables pueden bloquearse temporalmente, mientras que NMI (Non-Maskable Interrupt) siempre se atiende
- Durante el cambio de modo se bloquean temporalmente para evitar interrupciones inesperadas
Part 2 — Del modo real a 32 bits, y luego a 64 bits
- Linux moderno funciona en modo largo (long mode) de la arquitectura x86_64
- Se requiere una transición gradual en este orden: modo real → modo protegido → modo largo
Modo protegido (protected mode)
- Es un modo de 32 bits introducido para superar las limitaciones de los años 80, con dos estructuras clave
- GDT (Global Descriptor Table): define la dirección base, el tamaño y los permisos de los segmentos
- Linux usa el modelo plano (flat model) para simplificar todo el espacio de 32 bits como una sola región continua
- IDT (Interrupt Descriptor Table): almacena la dirección del handler que debe llamarse cuando ocurre una interrupción
- Durante el arranque se carga solo una IDT mínima, y después de la inicialización del kernel se instala una IDT completa
- GDT (Global Descriptor Table): define la dirección base, el tamaño y los permisos de los segmentos
Proceso de cambio de modo
- El código de configuración primero realiza desactivación de interrupciones, detención del chip PIC, activación de la línea A20 e inicialización del coprocesador matemático
- La línea A20 es un mecanismo histórico para resolver el problema del wrapping de direcciones en 1 MB
- Después de cargar una GDT e IDT mínimas, configura el bit PE del registro CR0 y realiza un far jump
- Con esto entra en modo protegido, y vuelve a configurar los segmentos y el puntero de pila según el nuevo esquema de direcciones
Registros de control (control registers)
- CR0: activa el modo protegido
- CR3: almacena la dirección de nivel superior de las tablas de páginas
- CR4: activa funciones extendidas como PAE
Preparación para entrar en modo largo (long mode)
- Para cambiar a modo de 64 bits se necesitan dos condiciones
- Activar la paginación (paging): realiza el mapeo entre direcciones virtuales y físicas
- Configurar el bit LME (Long Mode Enable) del registro EFER
- Las tablas de páginas mapean páginas en unidades de 4 KB, pero en el arranque inicial se usan de forma simple con un identity map de 2 MB
Procedimiento para activar la paginación
- Activa PAE en CR4 y crea una tabla de páginas mínima que cubre la zona de direcciones bajas en bloques de 2 MB
- Escribe en CR3 la dirección de la tabla superior y luego activa la paginación
- Configura el bit LME de EFER y salta al código de 64 bits para entrar en modo largo
- Con las direcciones y los registros extendidos a 64 bits, el kernel queda listo para ejecutarse
Part 3 — Descompresión del kernel, ajuste de direcciones y auto-reubicación
- En este punto la CPU ya está en modo de 64 bits y en memoria existe una imagen comprimida del kernel
- Un pequeño stub de 64 bits se encarga de descomprimir el kernel y ajustar sus direcciones
Limpieza inicial y configuración de salvaguardas
- El stub calcula su ubicación real de ejecución y, si el kernel se superpone, se mueve a una posición segura mediante self-relocation
- Inicializa su propia sección BSS y carga una IDT simple (incluye handlers para page fault y NMI)
- Si ocurre un page fault, agrega de inmediato el mapeo faltante para recuperarse
- Crea mapeos de identidad para las áreas necesarias, como el kernel, los parámetros de arranque y el búfer de línea de comandos
Descompresión del kernel
- Se ejecuta la función
extract_kernelpara descomprimir el kernel- Soporta varios algoritmos de compresión, como gzip, xz, zstd y lzo
- Tras descomprimir, lee el encabezado ELF para copiar las secciones de código y datos a las direcciones correctas
- Si la dirección para la que se compiló el kernel y la dirección real de carga son distintas, se realiza una reubicación (relocation)
- Se ajustan las instrucciones o punteros que contienen direcciones para que coincidan con la ubicación real en memoria
- Cuando todo está listo, salta a la función
start_kernely comienza la inicialización formal del kernel
Auto-reubicación del kernel (kASLR)
- kASLR (Kernel Address Space Layout Randomization) aleatoriza las direcciones físicas y virtuales del kernel para dificultar los ataques
- Durante el arranque se eligen aleatoriamente dos bases (base)
- Base física: la dirección de RAM donde realmente quedará ubicado el kernel
- Base virtual: el punto inicial de la dirección virtual que usará el kernel
- Durante el arranque se eligen aleatoriamente dos bases (base)
- Proceso de selección
- Se prepara una lista de áreas que deben protegerse, como el bootloader, el initrd y el búfer de línea de comandos
- Se escanea el mapa de memoria del firmware para encontrar un espacio libre lo bastante grande
- Se elige un slot aleatorio usando entropía obtenida de instrucciones de números aleatorios por hardware u otras fuentes
- Si no hay una región adecuada, vuelve a la dirección predeterminada, y con la opción
nokaslrse desactiva la aleatorización
Resumen de términos
- Hexadecimal (hexadecimal): se representa con el prefijo
0x, útil para la estructura de bits y la alineación en hardware - Register: almacenamiento temporal dentro de la CPU (CS, DS, SS, IP, SP, etc.)
- Segment/Offset: método de cálculo de direcciones en modo real
(segment * 16 + offset) - BIOS/UEFI: firmware encargado de la inicialización del sistema y la carga del programa de arranque
- Bootloader (GRUB): carga del kernel y entrega de información del sistema
- Stack/BSS: almacenamiento temporal para funciones y zona de variables globales inicializadas en cero
- Interrupt/NMI: mecanismo para manejar eventos de hardware y software
- GDT/IDT: tablas de definición de segmentos e interrupciones
- A20 Line: interruptor para evitar el wrapping de direcciones en 1 MB
- Protected Mode/Long Mode: modos de ejecución de 32 y 64 bits
- Paging/Page Tables: mapeo entre direcciones virtuales y físicas
Aún no hay comentarios.