paint-brush
Un viaje a través de los secretos del firmware: desde la BIOS/UEFI hasta el sistema operativopor@tristejoursoir
476 lecturas
476 lecturas

Un viaje a través de los secretos del firmware: desde la BIOS/UEFI hasta el sistema operativo

por Aleksandr Goncharov20m2024/08/22
Read on Terminal Reader

Demasiado Largo; Para Leer

Explore la evolución del BIOS tradicional al firmware UEFI moderno, comprenda cómo se administran las secuencias de arranque y descubra las funciones de los servicios de arranque y los servicios de tiempo de ejecución. Profundice en las complejidades de los cargadores de arranque del SO y vea cómo el firmware ahora admite funciones y aplicaciones avanzadas.
featured image - Un viaje a través de los secretos del firmware: desde la BIOS/UEFI hasta el sistema operativo
Aleksandr Goncharov HackerNoon profile picture
0-item
1-item


¿Alguna vez te has preguntado qué sucede en el momento en que presionas el botón de encendido de tu computadora? Detrás de esa breve pausa, antes de que se encienda tu pantalla, se están llevando a cabo una serie compleja de procesos. Este artículo se sumergirá en el fascinante mundo del firmware , explorando cómo interactúan los diferentes componentes durante el proceso de arranque .


Al comprender estas conexiones, obtendrá una imagen más clara de los elementos fundamentales que dan vida a su sistema. Nos centraremos principalmente en la arquitectura Intel x86 , pero muchos principios también se aplican a otras arquitecturas.


Si te perdiste la primera parte de nuestra serie, haz clic aquí para ponerte al día. Ahora, descubramos los misterios detrás del firmware.

Tabla de contenido:

  • Definiciones
  • Arquitectura general del firmware
  • Cargador de arranque de primera etapa (FSBL)
    • BIOS (fase POST)
    • Inicialización de la plataforma UEFI (PI)
    • arranque del núcleo
    • Otras soluciones
  • Cargador de arranque de segunda etapa (SSBL)
    • BIOS
    • UEFI
  • Cargador de arranque del sistema operativo


Definiciones

  • Firmware : un tipo especializado de software integrado en el hardware, que proporciona control de bajo nivel y permite que el hardware funcione correctamente e interactúe con otros componentes del sistema.


  • Sistema básico de entrada y salida (BIOS) : firmware heredado (creado originalmente para IBM PC ) responsable de la inicialización del hardware después de encender la plataforma. En la actualidad, se lo suele denominar vagamente como el conjunto completo de firmware.


  • Cargador de arranque : nombre general para el firmware que se encarga de arrancar una computadora. Se utiliza como un concepto moderno en lugar de BIOS y suele proporcionar un marco con código de arranque para inicializar el procesador y el chipset, así como interfaces para que terceros (por ejemplo, desarrolladores de placas base) realicen una inicialización específica de la plataforma.


  • Carga útil : software que se ejecuta cuando finaliza el gestor de arranque. Puede ser un gestor de arranque de segunda etapa, un sistema operativo, una aplicación BIOS/UEFI, etc. Normalmente se encarga del flujo de arranque según el diseño del firmware.


  • El uso de los términos BIOS y bootloader puede ser confuso, ya que sus significados dependen del contexto. Sin embargo, cuando alguien menciona firmware , BIOS o bootloader , generalmente se refiere al conjunto completo de firmware que se ejecuta entre el sistema operativo y el hardware .


  • EFI vs. UEFI : La interfaz de firmware extensible (EFI) fue la especificación original desarrollada por Intel. La interfaz de firmware extensible unificada (UEFI) es la sucesora de EFI , creada por el foro UEFI para estandarizar y ampliar la especificación original. En la mayoría de los casos, EFI y UEFI se utilizan indistintamente.

Arquitectura general del firmware

Para entender cómo interactúan los componentes del firmware, exploraremos toda la arquitectura con todas sus partes conectadas. El flujo de ejecución, que se muestra en el diagrama a continuación, comienza desde el vector de reinicio , que forma parte del cargador de arranque de primera etapa . A partir de allí, avanza a través de varias etapas del firmware:



El firmware o BIOS generalmente se puede dividir en dos partes principales, con una interfaz típicamente mínima entre ellas:


  1. Inicialización de hardware : Responsable de inicializar los componentes de hardware del sistema.
  2. Interfaz con el sistema operativo y el usuario : proporciona las interfaces necesarias para el sistema operativo y el usuario.


El diseño del firmware de la plataforma puede ser monolítico , combinando la inicialización del hardware y la funcionalidad de arranque, o puede seguir un flujo de arranque modular y por etapas. La elección del diseño depende de los requisitos del sistema y puede ser preferible para determinados dispositivos.


El siguiente diagrama ilustra cómo interactúan los diferentes componentes del firmware y cómo pueden usarse juntos para respaldar el proceso de arranque (las flechas indican la secuencia de ejecución):



Si estos diagramas te parecen complejos ahora, no te preocupes. Vuelve a revisarlos después de leer este artículo y te resultarán más claros.

Cargador de arranque de primera etapa (FSBL)

Este firmware está diseñado para inicializar computadoras y sistemas integrados con un enfoque en la inicialización mínima del hardware : hacer solo lo que es absolutamente necesario y luego pasar el control al cargador de arranque de segunda etapa para iniciar el sistema operativo. El FSBL no carga sistemas operativos desde medios de almacenamiento que no sean el chip flash . Dado que solo inicializa el hardware subyacente y no maneja medios de arranque como discos duros, SSD o unidades flash USB, se requiere otra pieza de software para iniciar realmente un sistema operativo .


Responsabilidades clave de FSBL :


  1. CPU : Cambio del modo real de 16 bits al modo protegido de 32 bits ( nota : o en modo virtual 8086 en el caso del BIOS).
  2. Utilización de caché : llamar a FSP-T para configurar Cache-As-RAM para el entorno C.
  3. Puerto de depuración : inicializa el puerto de depuración configurado llamando a los métodos de inicialización específicos de la placa.
  4. Inicialización de memoria : invocar FSP-M para inicializar la memoria principal del sistema y guardar información crucial de la memoria del sistema.
  5. GPIO : configuración de pines de entrada/salida de propósito general (GPIO) para interactuar con dispositivos externos.
  6. Silicio : realización de la inicialización temprana de la plataforma y uso de FSP-S para completar la inicialización del chipset, la CPU y el controlador IO.
  7. Enumeración PCI : enumeración de dispositivos PCI y asignación de recursos como direcciones de memoria e IRQ.
  8. Preparación de la carga útil : configuración de las tablas SMBIOS y ACPI , incluida la información de preparación (tablas coreboot, HOB) que debe pasarse a la carga útil.
  9. Carga y transferencia : Carga y transferencia de control a la carga útil.

BIOS (fase POST)

En los primeros tiempos de la informática, el software de código abierto no era muy popular y la mayoría de las implementaciones de BIOS eran propietarias. Solo hay unas pocas soluciones abiertas disponibles que proporcionan código fuente de BIOS POST, como Super PC/Turbo XT BIOS y GLaBIOS . Estos proyectos fueron diseñados para funcionar en sistemas IBM 5150/5155/5160 y la mayoría de los clones XT.


Sin embargo, las implementaciones de BIOS de código abierto más conocidas, como OpenBIOS y SeaBIOS , no realizan la inicialización del hardware porque no están diseñadas para ejecutarse en hardware desnudo. Pero se usan ampliamente como cargadores de arranque de segunda etapa y se ejecutan de forma nativa en entornos virtuales como QEMU y Bochs.


En cualquier caso, es poco probable que necesites trabajar directamente con estas primeras BIOS o profundizar en sus particularidades. Pero si te interesa explorar, los repositorios mencionados son un buen punto de partida.


En cuanto a las tendencias de desarrollo actuales, no parece haber un desarrollo continuo de soluciones BIOS propietarias, y dichos proyectos se han vuelto obsoletos frente a las alternativas modernas.

Inicialización de la plataforma UEFI (PI)

El proceso de arranque sigue un flujo escalonado, que comienza desde la izquierda y avanza hacia la derecha en la siguiente figura. La línea de tiempo del proceso de arranque de la plataforma se divide en las siguientes fases, como se indica mediante cuadros amarillos:



  • Seguridad (SEC) : La primera fase después de un vector de reinicio , su función principal es configurar la RAM temporal (CPU Cache-As-RAM o SRAM).
  • Inicialización previa a EFI (PEI) : esta fase envía controladores especializados denominados módulos de inicialización previa a EFI (PEIM) . Estos módulos se encargan de la inicialización esencial del hardware, como la configuración de la CPU y el chipset y la configuración de la memoria principal (DRAM) .
  • Entorno de ejecución del controlador (DXE) : en esta fase se realiza el resto de la inicialización del sistema. La fase DXE proporciona servicios UEFI y admite varios protocolos y controladores necesarios para el funcionamiento del sistema.
  • Selección de dispositivo de arranque (BDS) : esta fase implementa la política de arranque de la plataforma, determinando la secuencia de arranque y seleccionando el dispositivo/cargador de arranque adecuado.
  • Carga transitoria del sistema (TSL) : durante esta fase, el sistema ejecuta aplicaciones que utilizan los servicios UEFI para preparar el SO. Incluye la transición del entorno UEFI al sistema operativo y concluye con la llamada ExitBootServices() .
  • Tiempo de Ejecución (RT) : En esta fase, el sistema operativo está completamente operativo, gestionando el sistema bajo su control.
  • After Life (AL) : esta fase se ocupa de situaciones en las que el hardware o el sistema operativo fallan, se apagan o se reinician. El firmware puede intentar realizar acciones de recuperación; sin embargo, la especificación UEFI PI no define requisitos ni comportamientos específicos para esta fase.


Este proceso y sus fases de ejecución están contemplados en la Especificación de Inicialización de la Plataforma UEFI (PI) . Sin embargo, también existe la Interfaz UEFI (indicada por la línea azul en negrita en la imagen), que no forma parte del documento anterior y se describe en la Especificación UEFI . Aunque los nombres y el uso frecuente de UEFI pueden resultar confusos, estos dos documentos tienen enfoques diferentes:


  • Especificación UEFI PI : se centra en las interfaces entre los componentes de firmware de bajo nivel y detalla cómo estos módulos interactúan para inicializar la plataforma.


  • Especificación UEFI : define las interfaces para la interacción entre el sistema operativo (OS) y el firmware. Esto se analizará más a fondo en el contexto de Cargador de arranque de segunda etapa . Tenga en cuenta que la especificación UEFI se basa en la especificación PI.


Básicamente, ambas especificaciones tratan sobre interfaces, pero a diferentes niveles. Para obtener información detallada, puede acceder a ambas especificaciones en el sitio web del Foro UEFI .


UEFI PI se diseñó inicialmente como una solución de firmware unificada, sin tener en cuenta la distinción entre cargadores de arranque de primera y segunda etapa. Sin embargo, cuando nos referimos a UEFI como cargador de arranque de primera etapa , incluye las fases SEC , PEI y DXE temprana . La razón por la que dividimos DXE en etapas tempranas y tardías se debe a sus diferentes funciones en el proceso de inicialización.


En la fase inicial de DXE , los controladores suelen realizar la inicialización esencial de la CPU/PCH/placa y también generan protocolos arquitectónicos (AP) de DXE , que ayudan a aislar la fase de DXE del hardware específico de la plataforma. Los AP encapsulan los detalles específicos de la plataforma, lo que permite que la fase tardía de DXE funcione independientemente de las especificaciones del hardware.



Arranque básico

Próximamente publicaré artículos detallados sobre cómo funciona Coreboot. ¡Sigue mis redes sociales, se publicarán muy pronto!

Otras soluciones

  • Intel Slim Bootloader (SBL) : cargador de arranque puro de primera etapa que proporciona únicamente la inicialización de los componentes de hardware principales, seguida de la carga de la carga útil. Sin embargo, funciona únicamente en plataformas Intel x86 y no es compatible con AMD x86 u otras arquitecturas.
  • Das U-Boot : es un gestor de arranque de primera y segunda etapa. Sin embargo, la compatibilidad con el arranque directo desde el vector de reinicio x86 de la plataforma (conocido como modo básico) es limitada en comparación con otros firmware. Es más popular en sistemas integrados y dispositivos basados en ARM. Como gestor de arranque de segunda etapa, U-Boot implementa un subconjunto de la UEFI pero se centra en los sistemas integrados.

Cargador de arranque de segunda etapa (SSBL)

Una vez finalizada la configuración inicial del hardware, entra en juego la segunda etapa , cuya función principal es configurar una interfaz de software entre el sistema operativo y el firmware de la plataforma , garantizando que el SO pueda administrar los recursos del sistema e interactuar con los componentes del hardware.


El objetivo de SSBL es ocultar las variaciones de hardware tanto como sea posible, simplificando el desarrollo de sistemas operativos y aplicaciones al manejar la mayoría de las interfaces a nivel de hardware. Esta abstracción permite a los desarrolladores centrarse en funcionalidades de nivel superior sin preocuparse por las diferencias de hardware subyacentes.


Responsabilidades clave de SSBL :


  1. Recuperación de información de la plataforma : obtiene información específica de la plataforma del cargador de arranque de primera etapa , incluido el mapeo de memoria, SMBIOS, tablas ACPI, flash SPI, etc.


  2. Ejecutar controladores independientes de la plataforma : incluye controladores para SMM, SPI, PCI, SCSI/ATA/IDE/DISK, USB, ACPI, interfaces de red, etc.


  3. Implementación de servicios (también conocida como interfaz) : proporciona un conjunto de servicios que facilitan la comunicación entre el sistema operativo y los componentes de hardware.


  4. Menú de configuración : ofrece un menú de configuración para la configuración del sistema, permitiendo a los usuarios ajustar configuraciones relacionadas con el orden de arranque, preferencias de hardware y otros parámetros del sistema.


  5. Lógica de arranque : mecanismo para localizar y cargar la carga útil (probablemente el sistema operativo) desde el medio de arranque disponible.

BIOS

La interfaz del BIOS se conoce como servicios/funciones/llamadas de interrupción del BIOS . Estas funciones proporcionan un conjunto de rutinas para el acceso al hardware, pero los detalles específicos de cómo se ejecutan en el hardware particular del sistema están ocultos para el usuario.


En el modo real de 16 bits, se puede acceder a ellos fácilmente invocando una interrupción de software mediante una instrucción en lenguaje ensamblador INT x86. En el modo protegido de 32 bits, casi todos los servicios del BIOS no están disponibles debido a la forma diferente en que se manejan los valores de los segmentos .




Tomemos como ejemplo Disk Services ( INT 13h ), que proporciona servicios de lectura y escritura de discos duros y disquetes basados en sectores mediante el direccionamiento Cylinder-Head-Sector (CHS) , como ejemplo de cómo se puede utilizar esta interfaz. Supongamos que queremos leer 2 sectores (1024 bytes) y cargarlos en la dirección de memoria 0x9020 ; en ese caso, se podría ejecutar el siguiente código:


 mov $0x02, %ah # Set BIOS read sector routine mov $0x00, %ch # Select cylinder 0 mov $0x00, %dh # Select head 0 [has a base of 0] mov $0x02, %cl # Select sector 2 (next after the # boot sector) [has a base of 1] mov $0x02, %al # Read 2 sectors mov $0x00, %bx # Set BX general register to 0 mov %bx, %es # Set ES segment register to 0 mov $0x9020, %bx # Load sectors to ES:BX (0:0x9020) int $0x13 # Start reading from drive jmp $0x9020 # Jump to loaded code


Si está interesado en cómo está escrito este servicio en SeaBios, eche un vistazo a src/disk.c .

Fase de arranque

  • Comienza a buscar un dispositivo de arranque (puede ser un disco duro, un CD-ROM, un disquete, etc.) leyendo el primer sector de 512 bytes (sector cero) de los dispositivos.


  • La secuencia de arranque en el BIOS carga el primer registro de arranque maestro (MBR) válido que encuentra en la memoria física de la computadora en la dirección física 0x7C00 (pista: 0x0000:0x7c00 y 0x7c0:0x0000 se refieren a la misma dirección física).


  • El BIOS transfiere el control a los primeros 512 bytes de la carga útil. Este sector cargado , que es demasiado pequeño para contener todo el código de la carga útil, sirve para cargar el resto de la carga útil desde el dispositivo de arranque . En este punto, la carga útil puede utilizar la interfaz expuesta por el BIOS.


Cabe destacar que en los primeros tiempos no existían especificaciones de BIOS . La BIOS es un estándar de facto : funciona de la misma manera que en las computadoras IBM reales, en la década de 1980. El resto de los fabricantes simplemente hicieron ingeniería inversa y crearon BIOS compatibles con IBM. Como resultado, no había ninguna regulación que impidiera a los fabricantes de BIOS inventar nuevas funciones de BIOS o tener funcionalidades superpuestas.

Interfaz de firmware extensible unificada (UEFI)

Como se mencionó anteriormente, UEFI en sí es solo una especificación y tiene muchas implementaciones. La más utilizada es TianoCore EDK II , una implementación de referencia de código abierto de las especificaciones UEFI y PI. Si bien EDKII por sí solo no es suficiente para crear un firmware de arranque completamente funcional, proporciona una base sólida para la mayoría de las soluciones comerciales.


Para admitir distintos cargadores de arranque de primera etapa y proporcionar una interfaz UEFI, se utiliza el proyecto UEFI Payload . Se basa en la configuración inicial realizada y en la información de la plataforma proporcionada por el firmware de arranque para preparar el sistema para el entorno UEFI.


La carga útil UEFI utiliza las fases DXE y BDS , que están diseñadas para ser independientes de la plataforma. Ofrece una carga útil genérica que puede adaptarse a diferentes plataformas. En la mayoría de los casos, no requiere ninguna personalización ni ajustes específicos de la plataforma y se puede utilizar tal como está consumiendo información de la plataforma desde el cargador de arranque de primera etapa .


Variantes de la carga útil UEFI :


  1. Carga útil UEFI heredada : requiere una biblioteca de análisis para extraer la información necesaria de la plataforma específica de la implementación. Si el gestor de arranque actualiza su API, también se debe actualizar la carga útil.



  2. Carga útil de UEFI universal : sigue la especificación de firmware escalable universal (USF) , y utiliza el formato ejecutable y enlazable (ELF) o el árbol de imágenes planas (FIT) como formato de imagen común. En lugar de analizarlos por sí mismo, espera recibir los bloques de transferencia (HOB) en la entrada de la carga útil.


Si bien la carga útil UEFI heredada funciona bien, la comunidad EDK2 está intentando cambiar la industria hacia la carga útil UEFI universal . La elección entre cargas útiles depende de los componentes de su firmware. Por ejemplo, no es posible ejecutar la carga útil heredada con soporte SMM en Slim Bootloader sin mi parche . Por otro lado, usar la carga útil universal con coreboot requiere una capa de corrección para traducir las tablas de coreboot en HOB , una característica que solo está disponible en la bifurcación EDK2 de StarLabs .

Interfaz

Cada sistema compatible con UEFI proporciona una tabla de sistema que se pasa a cada código que se ejecuta en el entorno UEFI (controladores, aplicaciones, cargadores de SO). Esta estructura de datos permite que un ejecutable UEFI acceda a las tablas de configuración del sistema , como ACPI , SMBIOS y una colección de servicios UEFI .



La estructura de la tabla se describe en MdePkg/Include/Uefi/UefiSpec.h :


 typedef struct { EFI_TABLE_HEADER Hdr; CHAR16 *FirmwareVendor; UINT32 FirmwareRevision; EFI_HANDLE ConsoleInHandle; EFI_SIMPLE_TEXT_INPUT_PROTOCOL *ConIn; EFI_HANDLE ConsoleOutHandle; EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ConOut; EFI_HANDLE StandardErrorHandle; EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *StdErr; // // A pointer to the EFI Runtime Services Table. // EFI_RUNTIME_SERVICES *RuntimeServices; // // A pointer to the EFI Boot Services Table. // EFI_BOOT_SERVICES *BootServices; UINTN NumberOfTableEntries; EFI_CONFIGURATION_TABLE *ConfigurationTable; } EFI_SYSTEM_TABLE;


Los servicios incluyen los siguientes tipos: Servicios de arranque , Servicios de tiempo de ejecución y Servicios proporcionados por protocolos .


UEFI abstrae el acceso al dispositivo mediante la configuración de los Protocolos UEFI . Estos protocolos son estructuras de datos que contienen punteros de función y se identifican mediante un Identificador único global (GUID) que permite que otros módulos los localicen y los utilicen. Se pueden descubrir a través de los Servicios de arranque.


Un controlador UEFI produce estos protocolos y las funciones reales (¡no los punteros!) están contenidas dentro del propio controlador. Este mecanismo permite que los diferentes componentes dentro del entorno UEFI se comuniquen entre sí y garantiza que el sistema operativo pueda interactuar con los dispositivos antes de cargar sus propios controladores.



Si bien algunos protocolos están predefinidos y descritos en la especificación UEFI, los proveedores de firmware también pueden crear sus propios protocolos personalizados para ampliar la funcionalidad de una plataforma.


Servicios de arranque

Proporcionar funciones que se puedan utilizar únicamente durante el arranque. Estos servicios permanecen disponibles hasta que se llama a la función EFI_BOOT_SERVICES.ExitBootServices() ( MdeModulePkg/Core/Dxe/DxeMain/DxeMain.c ).


Los punteros a todos los servicios de arranque se almacenan en la tabla de servicios de arranque ( MdePkg/Include/Uefi/UefiSpec.h ):


 typedef struct { EFI_TABLE_HEADER Hdr; ... EFI_GET_MEMORY_MAP GetMemoryMap; EFI_ALLOCATE_POOL AllocatePool; EFI_FREE_POOL FreePool; ... EFI_HANDLE_PROTOCOL HandleProtocol; ... EFI_EXIT_BOOT_SERVICES ExitBootServices; ... } EFI_BOOT_SERVICES;


Servicios en tiempo de ejecución

Un conjunto mínimo de servicios sigue estando accesible mientras el sistema operativo está en funcionamiento. A diferencia de los servicios de arranque, estos servicios siguen siendo válidos después de que cualquier carga útil (por ejemplo, el cargador de arranque del sistema operativo) haya tomado el control de la plataforma mediante una llamada a EFI_BOOT_SERVICES.ExitBootServices() .


Los punteros a todos los servicios de tiempo de ejecución se almacenan en la tabla de servicios de tiempo de ejecución ( MdePkg/Include/Uefi/UefiSpec.h ):


 typedef struct { EFI_TABLE_HEADER Hdr; ... EFI_GET_TIME GetTime; EFI_SET_TIME SetTime; ... EFI_GET_VARIABLE GetVariable; EFI_GET_NEXT_VARIABLE_NAME GetNextVariableName; EFI_SET_VARIABLE SetVariable; ... EFI_GET_NEXT_HIGH_MONO_COUNT GetNextHighMonotonicCount; EFI_RESET_SYSTEM ResetSystem; ... } EFI_RUNTIME_SERVICES;


La siguiente imagen muestra la línea de tiempo de los servicios de arranque y tiempo de ejecución, para que pueda ver exactamente cuándo está activo cada uno.



Fase de selección de dispositivo de arranque (BDS)

La especificación UEFI define un motor de políticas de arranque denominado gestor de arranque UEFI . Intentará cargar las aplicaciones UEFI en un orden específico. Este orden y otras configuraciones se pueden configurar modificando las variables globales de NVRAM (memoria de acceso aleatorio no volátil) . Analicemos las más importantes:


  • Boot#### ( #### se reemplaza por un valor hexadecimal único): una opción de arranque/carga.
  • BootCurrent : la opción de arranque utilizada para iniciar el sistema que se está ejecutando actualmente.
  • BootNext : la opción de arranque solo para el siguiente arranque. Reemplaza BootOrder solo para un arranque y el administrador de arranque la elimina después del primer uso. Esto le permite cambiar el comportamiento del siguiente arranque sin cambiar BootOrder .
  • BootOrder : lista ordenada de carga de opciones de arranque. El administrador de arranque intenta arrancar la primera opción activa de esta lista. Si no lo logra, prueba con la siguiente opción, y así sucesivamente.
  • BootOptionSupport : los tipos de opciones de arranque compatibles con el administrador de arranque.
  • Timeout : el tiempo de espera de los administradores de arranque del firmware, en segundos, antes de elegir automáticamente el valor de inicio de BootNext o BootOrder .


Estas variables se pueden obtener fácilmente desde Linux usando efibootmgr(8) :


 [root@localhost ~]# efibootmgr BootCurrent: 0000 Timeout: 5 seconds BootOrder: 0000,0001,2001,2002,2003 Boot0000* ARCHLINUX HD(5,GPT,d03ca3cf-1511-d94e-8400-c7a125866442,0x40164000,0x100000)/File(\EFI\ARCHLINUX\grubx64.efi) Boot0001* Windows Boot Manager HD(1,GPT,6f185443-09fc-4f15-afdf-01c523565e52,0x800,0x32000)/File(\EFI\Microsoft\Boot\bootmgfw.efi)57a94e544f5753000100000088900100780000004200430044039f0a42004a004500430054003d007b00390064006500610038003600320063002d1139006300640064002d0034006500370030102d0061006300630031002d006600330032006200330034003400640034003700390035007d00000033000300000710000000040000007fff0400 Boot0002* ARCHLINUX HD(5,GPT,d03ca3cf-1511-d94e-8400-c7a125866442,0x40164000,0x100000) Boot2001* EFI USB Device RC Boot2002* EFI DVD/CDROM RC Boot2003* EFI Network RC


Echemos un vistazo al arranque basándonos en el fragmento de código anterior. UEFI comenzará a iterar la lista BootOrder . Para cada entrada de la lista, busca una variable Boot#### correspondiente: Boot0000 para 0000, Boot2003 para 2003, y así sucesivamente. Si la variable no existe, continúa con la siguiente entrada. Si la variable existe, lee el contenido de la variable. Cada variable de opción de arranque contiene un descriptor EFI_LOAD_OPTION que es un búfer lleno de bytes de campos de longitud variable (es solo la estructura de datos).


La estructura de datos se describe en [MdePkg/Include/Uefi/UefiSpec.h][ https://github.com/tianocore/edk2/blob/edk2-stable202405/MdePkg/Include/Uefi/UefiSpec.h#L2122 )


 typedef struct _EFI_LOAD_OPTION { /// The attributes for this load option entry. UINT32 Attributes; /// Length in bytes of the FilePathList. UINT16 FilePathListLength; /// The user readable description for the load option. /// Example: 'ARCHLINUX' / 'Windows Boot Manager' / `EFI USB Device` // CHAR16 Description[]; /// A packed array of UEFI device paths. /// Example: 'HD(5,GPT,d03ca3cf-1511-d94e-8400-c7a125866442,0x40164000,0x100000)/File(\EFI\ARCHLINUX\grubx64.efi)' // EFI_DEVICE_PATH_PROTOCOL FilePathList[]; /// The remaining bytes in the load option descriptor are a binary data buffer that is passed to the loaded image. /// Example: '57a9...0400' in Boot0001 variable // UINT8 OptionalData[]; } EFI_LOAD_OPTION;


En este punto, el firmware examinará una ruta de dispositivo ( EFI_DEVICE_PATH_PROTOCOL ). En la mayoría de los casos, nuestra computadora se inicia desde un dispositivo de almacenamiento (disco duro/SSD/NVMe/etc.). Por lo tanto, la ruta del dispositivo contendría el nodo HD(Partition Number, Type, Signature, Start sector, Size in sectors) .


  • Tipo : indica el formato utilizado para el esquema de partición con las palabras clave MBR (1) o GPT (2).
  • Firma : una firma MBR de 4 bytes si el tipo es MBR , o un UUID de 16 bytes si el tipo es GPT .


Nota : Si está interesado en saber cómo traducir otras rutas, lea la Especificación UEFI v2.10, Referencia de nodo de dispositivo de texto 10.6.1.6 .


UEFI examinará el disco y verá si tiene una partición que coincida con el nodo. Si existe, debe estar etiquetada con un identificador único global (GUID) específico que la marque como la partición del sistema EFI (ESP) . Esta está formateada con un sistema de archivos cuya especificación se basa en la versión específica del sistema de archivos FAT y se denomina sistema de archivos EFI ; en realidad, es simplemente un FAT12/16/32 normal.


  • Arranque nativo : si la ruta del dispositivo contiene una ruta explícita al File(\Path\To\The\File.efi) , UEFI buscará ese archivo específico. Por ejemplo, la opción Boot0000 contiene File(\EFI\ARCHLINUX\grubx64.efi) .
  • Arranque de respaldo : si la ruta del dispositivo simplemente apunta a un disco, entonces, en tales situaciones, el firmware utilizará una ruta de arranque de respaldo que se basa en la arquitectura: \EFI\BOOT\BOOT{arch}.EFI ( BOOTx64.EFI para amd64 o BOOTia32.EFI para i386 / IA32 ). Este mecanismo permite que los medios extraíbles de arranque (por ejemplo, una unidad USB) funcionen en UEFI; solo utilizan una ruta de arranque de respaldo . Por ejemplo, la opción Boot0002 utilizará este mecanismo.


Nota: Todas las opciones Boot#### mencionadas anteriormente se refieren a las opciones de arranque que se muestran en la salida de ejemplo de efibootmgr .


En ambos casos, el gestor de arranque UEFI cargará la aplicación UEFI (puede ser el gestor de arranque del sistema operativo , el shell UEFI, el software de utilidad, la configuración del sistema, etc.) en la memoria. En este momento, el control se transfiere al punto de entrada de la aplicación UEFI . A diferencia del BIOS , la aplicación UEFI puede devolver el control al firmware (además de la situación en la que la aplicación toma el control del sistema). Si esto sucede o algo sale mal, el gestor de arranque pasa a la siguiente entrada Boot#### y sigue exactamente el mismo proceso.


La especificación menciona que el gestor de arranque puede mantener automáticamente las variables de la base de datos. Esto incluye la eliminación de las variables de opciones de carga que no están referenciadas o que no se pueden analizar. Además, puede reescribir cualquier lista ordenada para eliminar cualquier opción de carga sin las variables de opciones de carga correspondientes.


El texto anterior describe el arranque UEFI . Además, el firmware UEFI puede ejecutarse en modo Módulo de soporte de compatibilidad (CSM) que emula un BIOS.

Cargador de arranque del sistema operativo

Un programa que se inicia con el firmware (normalmente el cargador de arranque de segunda etapa ) y que utiliza su interfaz para cargar el núcleo del sistema operativo . Puede ser tan complejo como un sistema operativo y ofrecer funciones como:


  • Lectura desde varios sistemas de archivos (HFS+, ext4, XFS, etc.)
  • Interactuar a través de una red (por ejemplo, TFTP, HTTP)
  • Arranque de kernels compatibles con Multiboot
  • Carga en cadena
  • Cargando los discos RAM iniciales ( initrd )
  • ¡Y más!


Los diseños más comunes de estos programas quedan fuera del alcance de este artículo. Para obtener una comparación detallada de los cargadores de arranque de sistemas operativos más populares, puede consultar la wiki de ArchLinux y el artículo de Wikipedia .


El sistema Windows utiliza su propio gestor de arranque del sistema operativo conocido como Administrador de arranque de Windows (BOOTMGR) .


El firmware ya no es un pequeño y complejo fragmento de código, sino una enorme cantidad de código complejo , y las tendencias actuales no hacen más que contribuir a ello. Podemos ejecutar Doom , Twitter y muchas otras aplicaciones interesantes en él.


Comprender la arquitectura general ayuda a organizar estos componentes en la mente. Al examinar el diseño del firmware existente, se obtiene una idea del fascinante proceso que se desarrolla cada vez que se enciende una computadora. Esta perspectiva de arriba hacia abajo no solo aclara el papel de cada parte, sino que también destaca la naturaleza sofisticada y evolutiva de los sistemas de firmware modernos.

Recursos