2 puntos por GN⁺ 2025-10-26 | Aún no hay comentarios. | Compartir por WhatsApp
  • 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
  • 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

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_kernel para 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_kernel y 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
  • 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 nokaslr se 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.

Aún no hay comentarios.