LPIC-1 101.2: comprendiendo la secuencia de arranque

 

Seguimos con los tutoriales sobre LPIC-1, ahora pasando al tema 101.2, es decir, sobre todo lo relacionado con la secuencia o proceso de arranque. Intentaré dividir este tema en dos artículos. Este primero donde se analizan todas las etapas del proceso de inicio del sistema y otro para tratar systemd, SysV y las herramientas para consultar eventos del proceso de arranque en los registros del sistema.

BIOS/UEFI/EFI y alternativas:

Firmware diagrama

En 1975, Gary Kildall… ehh! Stop. No voy a contar toda la historia. Simplemente debes saber que BIOS / UEFI / EFI es un firmware para ejecutar una serie de rutinas almacenadas en una memoria ROM programable (EEPROM, flash…). Son las primeras instrucciones o programa que se ejecuta durante el arranque tras una señal de reset, o al encender la máquina. Sirve para establecer un setup básico e inicializar el hardware de la máquina.

No voy a entrar en comparar las ventajas o desventajas de cada uno de ellos, ni en nada más. Solo entiende que es necesario para que la CPU comience con un estado conocido de los registros, para que los distintos componentes de hardware o periféricos presentes se inicien adecuadamente, realizar unas comprobaciones, detectar dónde se encuentra el bootloader o el sistema operativo instalado, y luego ceder el control al kernel de éste.

El BIOS/UEFI/EFI no es el único firmware presente en la máquina. La mayor parte de componentes suelen tener su propio firmware, un código almacenado en una memoria no volátil que se relacionará muy estrechamente con el driver o controlador del dispositivo en cuestión. Aunque cada vez es más común sacar el firmware del dispositivo físico e incluirlo en el controlador, así cuando el kernel inicie dicho controlador se enviarán los cientos de KBs de firmware al dispositivo. De esa forma es más fácilmente actualizable, etc.

Ahora «se ha puesto de moda» el microcódigo con las dichosas chapuzas de algunos diseñadores de CPU y las vulnerabilidades como Spectre, Meltdown, etc. No confundas tampoco firmware con microcódigo, aunque pueda ser algo similar. Se puede ver el microcódigo como un firmware específico de la CPU. Pero no todas las CPUs necesitan microcódigo. El microcódigo solo están en aquellas CPUs con una unidad de control programada. Otras, como las RISC, suelen tener una unidad de control más simple y cableada, lo que aporta mucho más rendimiento y eficiencia energética.

El microcódigo es, por simplificarlo, el traductor de las instrucciones de la ISA en señales de control u operaciones de más bajo nivel que le servirán a la unidad de control para controlar las unidades funcionales implicadas en la ejecución de una instrucción. Los diseñadores de CPUs lo optimizan para que ejecuten el repertorio de instrucciones de la forma más eficiente y rápida posible, pero cuando se hacen modificaciones en él se puede romper esa armonía y que haya una pérdida de rendimiento…

El microcódigo de la CPU puede ser cargado directamente por el BIOS/UEFI/EFI o retrasar la carga para que sea el kernel el que lo haga. Por eso, las actualizaciones de microcódigo no implican flashear el BIOS/UEFI/EFI.

BIOS tenía graves carencias y por ello fue sustituido por EFI/UEFI en las máquinas más modernas. UEFI tampoco está libre de problemas, y los desarrolladores (AMI, Phoenix,…) de este tipo de firmware cierran el código de algunas partes, y eso no es muy halagüeño de cara a la seguridad. Son implementaciones propietarias a pesar de que el EFI original fue liberado bajo licencia BSD (recuerda, licencia permisiva) con el nombre de TianoCore.

¿Por qué cuento esto? Pues, porque aunque LPIC no lo contempla ahora y solo se centra en BIOS/UEFI, espero que en un futuro próximo incluyan también algo sobre algunas alternativas que ojalá se transformen con el tiempo en los sistemas estándares de facto. Por ejemplo, los chicos de Slimbook ya trabajan para implementar CoreBoot en sus máquinas y aportarnos algo más de libertad.

Aunque solo soy una persona y no tengo capacidad alguna desde esta plataforma para cambiar nada, ojalá de vez en cuando alguien se de una vuelta y se conciencie de algo con los artículos OFF-TOPIC o con estas pinceladas…

Los dos frentes alternativos a los que más futuro le veo son: LinuxBOOT y CoreBOOT. Pero para no generar confusión, ya que es una pregunta que me hacen frecuentemente, vamos a ver las etapas de UEFI:

  • SEC (Security): es la primera fase cuando se arranca el sistema y maneja todos los eventos iniciales y puede pasar alguna información básica para la fase PEI. Básicamente contiene el código necesario para la inicialización de la CPU en un estado de registros conocido.
  • PEI (Pre-Initialization): tras la fase anterior, se invocará a esta nueva fase que entra en marcha para configurar la plataforma entera para dejarlo todo preparado para luego ceder el control a DXE.
  • DXE (Driver eXecution Environment): cuando se cargan los drivers o controladores de los dispositivos o periféricos del equipo. Si es necesario, también monta discos, y busca y ejecuta el código de arranque del sistema. Tras este paso, se transfiere el control al sistema operativo localizado…

Dicho esto, Coreboot es un proyecto de código abierto pensado para sustituir el firmware propietario de las implementaciones BIOS/UEFI. Pero solo permite reemplazar las fases SEC y PEI. Por tanto, se seguiría necesitando de DXE. Y por eso se creó LinuxBoot, otro proyecto de código abierto para sustituir la fase DXE.

Ambos se complementan. CoreBoot se ejecuta en una fase muy temprana durante el arranque y hace posible todo aquello de lo que Linux no es capaz por sí mismo, como por ejemplo  inicializar la plataforma (CPU, RAM, ACPI, enumeración de dispositivos PCI, carga del bootloader o gestor de arranque,…). Luego se le puede ceder el control a Linux para que siga cargando los controladores, etc.

Con el trabajo puesto sobre CoreBoot y LinuxBoot no solo tenemos un sistema más confiable al ser abierto, también se consiguen mejoras de velocidad considerables para el tiempo de arranque y mayor seguridad.

Proceso de arranque y bootloader:

Diagrama de arranque de un PC

Cuando aprietas el botón de reset, o de encendido de un equipo, transcurren solo unos segundos hasta que tienes el sistema operativo en marcha. Pero durante ese lapso ocurren demasiadas cosas que vamos a describir aquí:

  1. A nivel electrónico tenemos una serie de circuitería para arrancar. Como el RTC (Real-Time Clock) que mantiene la hora almacenada y que sirve como base para la temporización del sistema operativo, el circuito que genera la señal reset, un oscilador que genera la señal de reloj para marcar el «ritmo» de la RAM, CPU, buses, etc. Al apretar el botón de encendido (arranque frío), o el reset (arranque en caliente), la PSU o fuente de alimentación suministra la energía. Una señal Power Good determina que la energía llega de forma estable a la CPU. La CPU pondrá sus registros a cero tras un reset (borra datos residuales del funcionamiento anterior) o los establece a un valor conocido en caso de que sea un arranque desde cero. En el registro CS se carga una dirección específica. En los 8086 era la dirección FFFF0h y el registro IP se ponía inmediatamente a 0000h, es decir, la dirección segmentada FFFFh:0000h correspondiente a la dirección FFFF0h del mapa físico de memoria. Con la llegada de los 286 se emplea la dirección 00FFFF0h, o la FFFFFFF0h para los 386, etc. En otras ISA son direcciones también diferentes, como la dirección efectiva 0x00000100 o 0x0000000000000100 para PowerPC/POWER de 32 y 64-bit respectivamente. En SPARC seria 0x00 o 0x20, etc. Bien, sea la que sea, apunta hacia una rutina de arranque…
  2. A partir de ese momento se comienza a ejecutar un programa almacenado en una memoria no volátil. Concretamente se hace referencia a una dirección que almacena unas instrucciones que ponen en marcha una rutina (reset code) para iniciar algunos componentes de hardware que complementan a la CPU, como el controlador de interrupciones. Cada vez que se ejecuta una instrucción del programa de arranque, el registro PC (Program Counter) va incrementando la dirección en +1 para ir ejecutando toda la secuencia completa.
  3. Cuando finaliza la rutina anterior se comienza a ejecutar otra (startup code) de la memoria no volátil. Durante el arranque también se ejecuta un proceso de diagnóstico conocido como POST (Power-On Self-Test) para verificar que el hardware está OK. El autodiagnóstico comienza cuando la CPU se revisa a sí misma, luego envía una señal sobre el bus del sistema para comprobar que todos los elementos conectados a él responden, evalúa la memoria de la GPU, las señales que controlan la pantalla, prueba los chips RAM (mediante un acceso escribe unos datos en las primeras direcciones y luego los lee para comprobar que son iguales), comprueba el teclado, envía más información por otros buses del sistema para comprobar otros periféricos, etc. Los resultados del test se contrastan con un registro de los dispositivos guardado en un chip (CMOS Setup). Si algo va mal se mostrará un mensaje de error o emitirá unos beeps o pitidos indicando el problema.
  4. La siguiente rutina a ejecutar es la correspondiente al bootstrapping. Es decir, las instrucciones necesarias para que inicie el sistema operativo gracias al gestor de arranque o bootloader. En GNU/Linux, el gestor de arranque puede ser GRUB, LiLo, Syslinux, etc. Si no se detecta ningún medio de almacenamiento con un sistema operativo almacenado, se muestra el mensaje «No boot device available» y de detiene el proceso de arranque.
  5. El siguiente paso en el proceso de arranque es diferente según sea un sistema BIOS o UEFI:
    1. En los sistemas tipo UNIX como Linux, cuando se trabaja en modo BIOS Legacy o CSM la rutina activará el IPL (Initial Program Loader), es decir, carga los primeros 512 bytes del primer sector del disco duro llamado MBR (Master Boot Record). Los 446 bytes primeros del MBR se reservan al código de programas y los siguientes 64 bytes para la tabla de particiones. Los 2 bytes últimos contienen el magic number o cifra mágica, que siempre será el hexadecimal AA55, considerando errónea cualquier otra cifra. Realmente el MBR de Linux está «vacío» y por ello no puede autoarrancarse, por eso necesita del bootloader.
    2. En el caso de ser EFI/UEFI, el firmware es capaz de reconocer y leer tanto la tabla de particiones como los sistemas de archivos o FS (FAT16, FAT32, ISO9660, UDF y HFS/HFS+). Por eso no se restringe a los primeros bytes o MBR. No obstante, se puede operar tanto con MBR como con el moderno GPT (e incluso con otro tipo de tablas nativas, como las de Apple). En este caso, se emplea una ESP (EFI System Partition), una tabla de particiones especial en los que se guardan los archivos del firmware para lanzar (<EFI_SYSTEM_PARTITION>/EFI/<FABRICANTE_EFI>/).
  6. Según la configuración Boot Priority, el firmware buscará en una u otra unidad de almacenamiento el gestor de arranque (/boot) y el sistema operativo o sistemas operativos (multiboot). Y el bootloader será el que asuma el control desde este mismo instante para arrancar el sistema operativo:
    1. Stage 1: la primera etapa se carga en la memoria RAM y se ejecuta por la CPU. Se corresponde con los primeros 512 bytes del MBR. Dicha etapa hace que sea posible cargar la siguiente etapa.
    2. Stage 1.5: una vez cargada en la memoria principal busca la etapa 2 en el sistema de ficheros de la partición adecuada.
    3. Stage 2: es más compleja que las anteriores. En GRUB lee el menu.lst o grub.cfg (GRUB2) con la configuración u opciones de arranque para el o los sistemas operativos instalados. Cuando aparece el menú del gestor en pantalla se nos permite elegir la opción que necesitemos arrancar. Una vez elegida, en caso de ser Linux, el boot loader apunta al kernel y el initramfs (initial RAM file system) se monta en /dev/ram0. Y aquí termina la tarea del bootloader…
  7. Comienza la etapa del kernel:
    1. En la primera fase se carga el kernel (Kernel loading stage), la imagen initrd/initramfs del kernel se descomprime en la zona alta de la RAM y establece unas funciones esenciales para trabajar con el hardware. Entre los problemas que se ponen en marcha está el llamado linuxrc. Se encarga de generar un sistema raíz (/ o root) provisional. También está usplash para arranques gráficos. Todo este proceso se hace como kernelspace o modo privilegiado.
    2. La segunda fase (Kernel startup stage) es para ejecutar el kernel gracias a una función intercambiador (swapper) o proceso 0 conocida como startup (diferente para las distintas arquitecturas). Se establece entonces el manejador de memoria (tablas de paginación, mapas,…), se detecta el tipo de CPU y funcionalidades adicionales de coma flotante, y se procede por las funcionalidades no dependientes de la arquitectura (syscall start_kernel()). Esa llamada establecerá la mayor parte de la configuración del sistema, como el manejo de interrupciones (IRQ), apoyándose sobre el reloj del sistema, configuraciones de memoria, monta el ramdisk cargado antes en el sistema raíz temporal, y carga en la RAM algunos módulos de controladores (usando udev o scripts…) que no fueron compilados como parte del kernel. Luego, pivot_root() desmontará el sistema temporal de archivos y lo reemplaza por el real (ese espacio desocupado queda libre para ser usado por otros procesos). En caso de no poderse crear po cualquier motivo, se genera un kernel panic. Después también se montan los sistemas de ficheros o FS y se inician algunos dispositivos. El planificador tomará el control de la gestión para decidir qué procesos pasarán a cargarse en la RAM para ser ejecutados. Esto da lugar al proceso 1 o PID=1.
  8. Ahora el sistmea ya ha sido iniciado. El PID=1, cuando usamos un kernel bare-metal sin nada más, podría ser asumido por un shell, por ejemplo. Pero en una distro es systemd, init de SysV, etc. Del proceso primero colgarán el resto de procesos ejecutados durante la sesión. En este caso, PID=1 puede ser una serie de scripts para poner en marcha estructuras de procesos para modos monousuario o multiusuario, se lanzará un entorno de escritorio en caso de tener una sesión gráfica, etc.

Y todo eso ocurre en segundos

En mi cruso C2GL explico el proceso algo más detallado y también lo expico usando SYSLINUX e incluso la descripción del proceso de arranque desde el punto de vista de los procesos. Pero esto no es algo que entre en los LPIC.

En el próximo artículo finiquitamos LPIC-1 Tema 101.2 con SysV, systemd y logs del sitema

Isaac

Apasionado de la computación y la tecnología en general. Siempre intentando desaprender para apreHender.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

A %d blogueros les gusta esto:

Si continuas utilizando este sitio aceptas el uso de cookies. más información

Los ajustes de cookies de esta web están configurados para "permitir cookies" y así ofrecerte la mejor experiencia de navegación posible. Si sigues utilizando esta web sin cambiar tus ajustes de cookies o haces clic en "Aceptar" estarás dando tu consentimiento a esto.

Cerrar