Tot sistema operatiu comença abans de ser realment ell mateix. La CPU arrenca en un entorn definit per la plataforma, el firmware inicialitza prou maquinari per carregar el primer executable, i aquest executable prepara la màquina per al kernel. Només després que aquesta cadena hagi fet la seva feina pot el sistema operatiu començar a imposar les seves pròpies regles.

És fàcil tractar aquest camí inicial com a simple fontaneria, però el procés d’arrencada forma part del model de seguretat. Per a EriX, el boot és el primer lloc on bytes no fiables es converteixen en execució fiable, i també és el primer lloc on l’autoritat de la màquina es tradueix en estructura explícita: imatges verificades, mapes de memòria, descriptors de mòduls, adreces d’entrada, metadades de framebuffer, punters ACPI i, finalment, objectes del kernel.

Si aquest camí és descuidat, el sistema de capacitats comença des d’una mentida. Aquest article recorre el camí d’arrencada d’EriX del firmware al kernel: què proporciona UEFI, què ha de fer el bootloader, què conté el handoff i per què el kernel s’entra com un executable de la meitat alta.


El boot comença amb autoritat de firmware

En els objectius actuals d’EriX, el camí d’arrencada comença amb UEFI sobre x86_64. UEFI és firmware, i s’executa abans del sistema operatiu. Proporciona els serveis de boot que permeten a una aplicació EFI llegir fitxers, assignar memòria, inspeccionar el mapa de memòria de la plataforma, descobrir taules de configuració com ACPI, consultar la sortida gràfica mitjançant GOP i sortir dels serveis de boot abans que el kernel prengui el control.

El bootloader d’EriX està construït com una aplicació UEFI, de manera que el firmware carrega primer el bootloader i crida el seu punt d’entrada UEFI. En la implementació, aquest punt d’entrada és efi_main, que utilitza la convenció de crida UEFI per a x86_64. En aquest moment EriX encara no controla la màquina; s’executa dins d’un entorn proporcionat pel firmware.

El bootloader pot demanar a UEFI que llegeixi un fitxer, assigni memòria, inspeccioni taules del firmware i prepari taules de pàgines, però tota aquesta autoritat és temporal. Els serveis de boot d’UEFI no són el sistema operatiu. Són bastida que el bootloader ha d’utilitzar amb cura, resumir en fets explícits i després deixar enrere.


El bootloader és codi de confiança

El bootloader s’executa abans que el kernel pugui imposar res, i això el posa dins de la base de computació de confiança. Si el bootloader carrega els bytes equivocats, salta a l’adreça equivocada, accepta una imatge manipulada, etiqueta malament la memòria o inventa autoritat que no prové d’entrada verificada, el kernel comença des d’un fonament compromès. Per mantenir aquest risc auditable, EriX manté la feina del bootloader estreta i explícita:

  • localitzar i carregar boot.img
  • validar la imatge abans de confiar-hi
  • validar el dynamic boot store
  • reubicar el kernel dinàmic i els seus objectes inicials requerits
  • construir una estructura de handoff determinista
  • preparar taules de pàgines i una pila de bootstrap
  • sortir dels serveis de boot d’UEFI
  • saltar al kernel una sola vegada

Això no és deliberadament un entorn de boot de propòsit general. No hi ha política de menú d’arrencada en l’abast actual, ni camí de recuperació remot, ni cap intent de continuar després d’una entrada malformada crítica per al boot. La regla és verificar abans d’executar: si la imatge de boot no es pot parsejar, verificar, carregar, mapar o descriure de manera coherent, l’intent d’arrencada falla abans que el kernel rebi el control.


Carregar boot.img

El camí UEFI actual busca aquest fitxer al volum de boot. El camí és fix per al perfil actual, cosa que manté estret el tractament inicial del mitjà i evita convertir el boot en un problema de descobriment de política:

\ERIX\BOOT.IMG

El bootloader obre el volum de boot mitjançant protocols de fitxer UEFI, llegeix el fitxer en memòria assignada per UEFI i fa una primera comprovació barata de la màgia de contenidor ERIXBOOT abans del parseig més profund. Després, lib-bootimg pren el relleu: el bootloader parseja la imatge com un BootImage, en verifica la signatura i els hashes, comprova l’arquitectura del manifest i només llavors comença a extreure payloads.

Aquesta separació importa perquè el mitjà de boot no és fiable. El fitxer pot faltar, estar truncat, estar malformat o haver estat manipulat, i el bootloader no pot tractar un descriptor de secció com a cert només perquè ve del disc. A EriX, el parser i verificador de boot.img formen part del camí de boot de confiança, i han de rebutjar estructura dolenta, límits dolents, hashes dolents, versions no suportades i dades d’arquitectura incompatibles abans que qualsevol byte carregat esdevingui executable.

Els propers articles aprofundiran en el format boot.img i la verificació d’imatges. Per a aquest article, el punt important és l’ordre:

  1. llegir bytes
  2. parsejar estructura
  3. verificar integritat
  4. comprovar compatibilitat amb l’objectiu
  5. validar i reubicar artefactes de boot dinàmics
  6. construir metadades de handoff

L’execució ve després de la validació, no abans. Aquest ordre és la diferència entre tractar la imatge de boot com una entrada i tractar-la com a autoritat.


Carregar el kernel

Un cop boot.img ha estat acceptat, el bootloader necessita un kernel, i en el camí de boot d’EriX el kernel es carrega des del dynamic boot store signat. El bootloader valida el kernel dinàmic com un objecte ELF64 ET_DYN, comprova les metadades de dynamic-link d’EriX, verifica noms de dependències i hashes d’objectes contra metadades signades, carrega objectes compartits requerits, aplica reubicacions aprovades, imposa restriccions W^X i prepara un catàleg de boot dinàmic per al kernel.

Això sona com molt perquè ho és, però la forma de seguretat continua sent directa i revisable:

  • les dades provenen d’una imatge de boot verificada
  • el parseig del format executable és explícit
  • els rangs de segments es comproven
  • els efectes laterals de reubicació estan restringits
  • metadades dinàmiques absents o inconsistents fallen abans de l’entrada al kernel

El bootloader existeix per validar i reubicar el kernel dinàmic, i després descriure al kernel el graf d’objectes inicial verificat. No es converteix en un enllaçador dinàmic general per al sistema en execució; aquest paper pertany més tard, després que existeixin el kernel i el model de serveis en espai d’usuari.


Preservar metadades de boot

El kernel dinàmic no és l’única cosa dins la imatge de boot. El bootloader també preserva metadades de boot no executables requerides per al kernel i el sistema inicial en espai d’usuari, mentre que els serveis executables pertanyen al graf d’objectes dinàmic. Aquests artefactes executables són descrits pel catàleg dinàmic com a objectes, segments i arestes de dependència derivades del store i del manifest signats.

Altres seccions requerides són blobs no executables. Alguns exemples són la configuració de boot, stores de metadades de dynamic-link i dades de font de consola. Es copien a memòria mapada i es descriuen com a mòduls de només lectura sense punt d’entrada, perquè un blob no és executable només perquè apareix a la imatge de boot.

Aquesta distinció és important per al disseny de capacitats. Un payload de configuració de boot hauria de ser llegible pel sistema inicial, però no hauria de ser tractat com a codi. Un blob de font pot ser necessari per a la continuïtat del framebuffer, però no necessita autoritat d’execució. EriX preserva aquesta distinció en el handoff:

  • descriptors d’objectes dinàmics per a artefactes executables
  • descriptors de segments dinàmics per a rangs executables i de dades mapats
  • descriptors de dependències dinàmiques per a relacions d’objectes inicials
  • SECTION_TYPE_BOOT_CONFIG per a dades de política de boot
  • descriptors de mòduls per a blobs de boot no executables

El handoff porta aquesta diferència endavant perquè el kernel i rootd no hagin d’endevinar si una peça de dades de boot és autoritat executable, configuració de només lectura o metadades ordinàries.


El handoff és el contracte

El bootloader no crida una API del kernel, perquè encara no hi ha cap kernel en execució. En lloc d’això, el bootloader construeix un blob de handoff: un contracte binari versionat definit per lib-handoff. En el perfil actual de bootloader a kernel, comença amb la màgia ERIXHK01, camps de versió major i minor, mida total, identificadors d’arquitectura i plataforma, i després offsets i recomptes per a les taules que segueixen.

El handoff pot incloure diverses classes de dades que el kernel necessita abans de poder construir la seva pròpia vista runtime de la màquina:

  • entrades de mapa de memòria normalitzades
  • descriptors de mòduls carregats
  • punter ACPI RSDP
  • build ID i hash d’imatge verificats
  • metadades de continuïtat de framebuffer
  • descriptors d’objectes dinàmics
  • descriptors de segments dinàmics
  • arestes de dependència dinàmiques

Aquesta és la primera transferència estructurada d’autoritat. El firmware va donar al bootloader fets crus i serveis temporals, i la imatge de boot verificada va donar al bootloader metadades de payload signades. El bootloader combina tot això en una descripció determinista de què ha carregat, on ho ha posat i en què pot confiar el kernel.

El handoff no és una pista ni metadades “best effort”. És l’entrada a partir de la qual el kernel comença a construir la seva pròpia visió de la màquina, i per això porta recomptes, mides d’entrada, offsets, hashes, tipus, rangs i camps de versió. Ha de ser possible que el kernel el rebutgi.


Els mapes de memòria necessiten normalització

Els mapes de memòria del firmware no estan automàticament modelats per a la política del kernel. UEFI informa de regions amb tipus de memòria de firmware, mentre que el bootloader també coneix la memòria que ha assignat per al kernel dinàmic, mapatges d’objectes inicials, blobs de boot requerits, pila de bootstrap, pàgines de handoff, mapatges de framebuffer i la imatge de boot original. Aquestes visions s’han de fusionar abans que el kernel pugui raonar sobre la memòria disponible.

A EriX, el bootloader captura el mapa de memòria UEFI, afegeix regions explícitament posseïdes pel boot i normalitza el resultat en rangs no superposats amb tipus de memòria d’EriX com:

  • RAM usable
  • memòria reservada
  • memòria ACPI reclamable
  • memòria ACPI NVS
  • memòria MMIO/dispositiu
  • memòria posseïda pel bootloader
  • memòria posseïda per la imatge de boot

Les regions explícitament posseïdes pel boot guanyen sobre classificacions genèriques del firmware. Això importa perquè el kernel no ha de tractar accidentalment la memòria que conté la imatge de boot, el blob de handoff, els mapatges d’objectes dinàmics, els blobs de boot requerits o la pila de bootstrap com a RAM lliure ordinària. La memòria és autoritat a EriX, així que fins i tot tan aviat el sistema és curós sobre qui pot reutilitzar quins bytes.


Deixar UEFI enrere

Els serveis de boot d’UEFI són útils, però temporals. Abans de saltar al kernel, el bootloader crida ExitBootServices, que a la pràctica és una transició d’una sola direcció. Després d’una sortida reeixida, el bootloader ha de tractar els punters a serveis de boot com a invàlids; no pot continuar demanant al firmware que assigni memòria o llegeixi fitxers després que el sistema operatiu prengui el control.

Aquesta transició és delicada perquè UEFI requereix que el bootloader surti utilitzant una clau de mapa de memòria actual. Si el mapa canvia entre la captura i la sortida, la crida pot fallar i el loader ha de tornar-ho a intentar amb un mapa fresc. L’adaptador UEFI d’EriX gestiona aquest bucle de reintents a la capa de plataforma.

El punt de disseny important és que s’entra al kernel després que el bootloader hagi acabat d’utilitzar serveis de firmware. El kernel no hauria d’heretar una dependència de firmware mig oberta; hauria d’heretar dades explícites.


Construir les primeres taules de pàgines

S’entra al kernel amb la paginació ja activa. Per al perfil UEFI x86_64 actual, el bootloader construeix taules de pàgines mínimes amb dos tipus de mapatges: una regió de memòria baixa mapada identitàriament per al bring-up inicial i mapatges específics de la meitat alta per als objectes que el kernel farà servir immediatament.

La implementació actual mapa el primer 1 GiB amb pàgines de 2 MiB i també mapa identitàriament les finestres MMIO d’APIC. Després mapa rangs virtuals de la meitat alta per al kernel, objectes dinàmics carregats, blobs de boot requerits, blob de handoff, framebuffer i pila de bootstrap. Això és suficient perquè el kernel comenci en l’espai d’adreces que espera.

Les taules de pàgines no són el sistema final de memòria virtual. Són un pont que permet al kernel executar-se, validar el handoff, instal·lar estat inicial de CPU i començar a construir l’entorn real de kernel i espai d’usuari. El pont encara ha de ser correcte: si la pàgina d’entrada del kernel no està mapada, la màquina fa fault immediatament; si el blob de handoff està mapat a l’adreça virtual equivocada, el kernel llegeix disbarats; si falta la pila, l’entrada falla abans que el codi Rust pugui fer gaire cosa.


Per què un kernel de la meitat alta?

EriX entra al kernel a la meitat alta de l’espai d’adreces virtual. Això vol dir que el kernel s’executa en adreces virtuals altes en lloc d’estar enllaçat i executat només en el rang baix mapat identitàriament. És un disseny habitual de kernel perquè dona al kernel una regió d’adreces virtuals estable independent d’on s’hagi assignat la memòria física.

El layout de la meitat alta també separa l’espai virtual del kernel dels rangs ordinaris d’espai d’usuari i permet al kernel mantenir presents els seus propis mapatges a través de canvis d’espai d’adreces més tard, mentre que els mapatges d’espai d’usuari poden variar per tasca. El layout d’adreces no imposa per si sol tot el model de seguretat, però dona suport al límit fent que la memòria del kernel sigui distinta de la memòria normal de procés.

A EriX, el bootloader és responsable de fer possible l’entrada inicial a la meitat alta. Carrega el kernel segons adreces virtuals ELF, mapa aquestes adreces virtuals a pàgines físiques assignades, crea una pila de bootstrap a la meitat alta, mapa el blob de handoff a una adreça coneguda de la meitat alta, carrega cr3 i salta al punt d’entrada del kernel.

En el salt, el contracte x86_64 actual és intencionadament petit i explícit. El bootloader subministra només l’estat que el kernel necessita per començar a validar el handoff i instal·lar el seu propi estat inicial de CPU:

  • ABI SysV
  • rdi conté el punter de handoff
  • rsp apunta a la pila de bootstrap amb l’alineació esperada
  • la paginació és activa
  • el control no retorna

Aquesta ABI és petita per disseny. Com menys estat implícit depengui de l’entrada del kernel, més fàcil és auditar el límit.


Entrar al kernel

Al costat del kernel, l’entrada comença abans que existeixi el runtime complet del kernel. El kernel dinàmic exposa erix_dynlink_entry, que entra al camí inicial del kernel amb el punter de handoff, i la primera feina és defensiva més que carregada de política.

El kernel desactiva interrupcions, inicialitza la sortida serial inicial, comprova que el punter de handoff no sigui nul, llegeix la mida del handoff des de la capçalera, imposa una mida màxima de handoff i després valida tot el blob mitjançant lib-handoff. Després de la validació estructural, el kernel aplica les seves pròpies comprovacions de política:

  • l’arquitectura ha de ser x86_64
  • la plataforma ha de ser UEFI
  • el build ID no pot ser buit
  • les taules de catàleg dinàmic han de ser internament coherents
  • noms d’objectes dinàmics, hashes, segments, dependències i rangs del store s’han de validar abans d’utilitzar-los

El bootloader ja ha construït el handoff, però el kernel encara el valida. Els components de confiança no poden saltar-se contractes només perquè un altre component de confiança ha produït les dades. El sentit d’un handoff versionat és que totes dues bandes puguin estar d’acord exactament sobre què s’ha transferit.

Només després d’això el kernel es mou més endins en la inicialització inicial: configuració de GDT, configuració d’IDT, configuració del camí de syscall, inicialització opcional de consola primerenca i finalment orquestració de bootstrap per a la primera tasca root. El kernel es converteix gradualment en el kernel, i el primer que fa és comprovar el terra sota els seus peus.


El boot és traducció d’autoritat

És temptador descriure el boot com “carregar el kernel i saltar”. Això és tècnicament cert, però perd el punt de disseny del sistema operatiu. Per a EriX, el boot és traducció d’autoritat: l’autoritat de firmware es converteix en fets explícits de boot, els bytes de la imatge de boot es converteixen en seccions verificades, fitxers ELF es converteixen en rangs executables mapats, blobs es converteixen en descriptors de mòduls no executables, mapes de memòria del firmware es converteixen en regions de memòria normalitzades, metadades de dynamic-link es converteixen en un graf d’objectes acotat i l’estat de framebuffer es converteix en metadades de continuïtat.

Tot això es converteix en un blob de handoff, i el kernel rep aquest blob i decideix si és acceptable. Només llavors pot començar a convertir recursos de la màquina en objectes del kernel, capacitats, espais d’adreces, endpoints i la primera tasca d’espai d’usuari. Per això el procés de boot pertany a una discussió sobre sistemes operatius de capacitats: el model de capacitats no comença després del boot com un afegit; depèn que el boot no hi introdueixi autoritat ambient.

El bootloader no hauria de dir simplement que ha carregat algunes coses. Hauria de dir exactament què ha carregat, on és, què és, com s’ha verificat i quins fets de plataforma ha observat. Aquesta és la diferència entre un salt i un handoff.


Què deixa EriX fora del bootloader

El bootloader és poderós perquè s’executa aviat, i precisament per això ha de continuar sent petit. EriX no vol que el bootloader decideixi política runtime: no hauria de decidir quin servei posseeix la política de memòria, com se supervisen els processos, com es gestionen els drivers o com es componen els sistemes de fitxers.

Aquestes decisions pertanyen al kernel i als serveis de sistema en espai d’usuari. La feina del bootloader és més estreta: validar l’artefacte de boot, preparar l’entorn mínim d’execució, descriure què ha fet i transferir el control.

Aquesta línia importa per a la mida del TCB. Un bootloader amb més funcionalitat no és automàticament millor, perquè cada funcionalitat en el boot inicial és codi que s’executa abans que el kernel pugui aïllar-lo. Cada parser, camí de fallback, mode interactiu i excepció de política augmenta la quantitat de comportament de confiança que ha de ser correcte abans que el sistema arrenqui.


Mirant endavant

Aquest article ha tractat boot.img principalment com un contenidor verificat. El pas següent és obrir aquest contenidor i mirar-ne directament el disseny.

El proper article explicarà el format boot.img d’EriX: per què el sistema fa servir una imatge unificada, com es disposen les seccions, quines metadades s’hi porten i com el format dona suport a artefactes de boot reproduïbles i deterministes.