Varje operativsystem börjar innan det verkligen är sig självt. CPU:n startar i en miljö som definieras av plattformen, firmware initialiserar tillräckligt med hårdvara för att ladda den första körbara filen, och den körbara filen förbereder maskinen för kernel. Först när den kedjan har gjort sitt arbete kan operativsystemet börja upprätthålla sina egna regler.

Det är lätt att behandla den tidiga vägen som ren VVS, men bootprocessen är en del av säkerhetsmodellen. För EriX är boot det första stället där opålitliga bytes blir betrodd exekvering, och det är också det första stället där maskinens auktoritet översätts till explicit struktur: verifierade images, minneskartor, moduldeskriptorer, entry-adresser, framebuffer-metadata, ACPI-pekare och till slut kernelobjekt.

Om den här vägen är slarvig börjar capability-systemet med en lögn. Det här inlägget går igenom EriX bootväg från firmware till kernel: vad UEFI ger, vad bootloadern måste göra, vad handoffen innehåller och varför kernel går in som en higher-half-exekverbar fil.


Boot börjar med firmware-auktoritet

På nuvarande EriX-mål börjar bootvägen med UEFIx86_64. UEFI är firmware, och det körs före operativsystemet. Det tillhandahåller boottjänsterna som låter en EFI-applikation läsa filer, allokera minne, inspektera plattformens minneskarta, upptäcka konfigurationstabeller som ACPI, fråga grafikutmatning via GOP och lämna boottjänsterna innan kernel tar över.

EriX bootloader byggs som en UEFI-applikation, så firmware laddar bootloadern först och anropar dess UEFI-entrypoint. I implementationen är den entrypointen efi_main, med UEFI:s x86_64-anropskonvention. Vid den här punkten styr EriX inte maskinen ännu; det körs inne i en miljö som firmware tillhandahåller.

Bootloadern kan be UEFI läsa en fil, allokera minne, inspektera firmwaretabeller och förbereda sidtabeller, men all den auktoriteten är tillfällig. UEFI:s boottjänster är inte operativsystemet. De är byggställningar som bootloadern måste använda varsamt, sammanfatta som explicita fakta och sedan lämna bakom sig.


Bootloadern är betrodd kod

Bootloadern körs innan kernel kan upprätthålla någonting, vilket placerar den i den betrodda beräkningsbasen. Om bootloadern laddar fel bytes, hoppar till fel adress, accepterar en manipulerad image, märker minne fel eller hittar på auktoritet som inte kom från verifierad input, börjar kernel från en komprometterad grund. För att hålla den risken granskningsbar håller EriX bootloaderns arbete smalt och explicit:

  • lokalisera och ladda boot.img
  • validera imagen innan den betros
  • validera dynamic boot store
  • relokera den dynamiska kerneln och dess nödvändiga tidiga objekt
  • bygga en deterministisk handoff-struktur
  • förbereda sidtabeller och en bootstrap-stack
  • lämna UEFI:s boottjänster
  • hoppa till kernel en gång

Detta är medvetet inte en generell bootmiljö. Det finns ingen bootmenypolicy i nuvarande scope, ingen fjärråterställningsväg och inget försök att fortsätta efter felformad bootkritisk input. Regeln är verifiera före exekvering: om boot imagen inte kan parsas, verifieras, laddas, mappas eller beskrivas konsekvent misslyckas bootförsöket innan kernel får kontrollen.


Ladda boot.img

Den nuvarande UEFI-vägen letar efter den här filen på bootvolymen. Vägen är fast för den nuvarande profilen, vilket håller den tidiga mediahanteringen smal och undviker att göra boot till ett policydetekteringsproblem:

\ERIX\BOOT.IMG

Bootloadern öppnar bootvolymen genom UEFI:s filprotokoll, läser filen till UEFI-allokerat minne och gör en billig första kontroll av containermagin ERIXBOOT innan djupare parsning. Därefter tar lib-bootimg över: bootloadern parsar imagen som en BootImage, verifierar dess signatur och hashvärden, kontrollerar manifestets arkitektur och börjar först därefter extrahera payloads.

Den separationen spelar roll eftersom bootmediet inte är betrott. Filen kan saknas, vara trunkerad, vara felformad eller ha manipulerats, och bootloadern får inte behandla en sektionsdeskriptor som sann bara för att den kom från disk. I EriX är boot.img-parsern och verifieraren en del av den betrodda bootvägen, och de måste avvisa dålig struktur, dåliga gränser, dåliga hashvärden, versioner utan stöd och inkompatibla arkitekturdata innan någon laddad byte blir körbar.

Nästa inlägg går djupare in i boot.img-formatet och imageverifiering. För det här inlägget är den viktiga punkten ordningen:

  1. läs bytes
  2. parsa struktur
  3. verifiera integritet
  4. kontrollera målkompatibilitet
  5. validera och relokera dynamiska bootartefakter
  6. bygg handoff-metadata

Exekvering kommer efter validering, inte före. Den ordningen är skillnaden mellan att behandla boot imagen som input och att behandla den som auktoritet.


Ladda kernel

När boot.img har accepterats behöver bootloadern en kernel, och i EriX bootväg laddas kernel från den signerade dynamic boot store. Bootloadern validerar den dynamiska kerneln som ett ELF64 ET_DYN-objekt, kontrollerar EriX dynamic-link-metadata, verifierar beroendenamn och objekthashar mot signerad metadata, laddar nödvändiga delade objekt, applicerar godkända relokeringar, tvingar W^X-begränsningar och förbereder en dynamisk bootkatalog åt kernel.

Det låter som mycket eftersom det är mycket, men säkerhetsformen förblir direkt och granskningsbar:

  • data kommer från en verifierad boot image
  • parsning av exekverbart format är explicit
  • segmentintervall kontrolleras
  • relokeringars sidoeffekter begränsas
  • saknad eller inkonsekvent dynamisk metadata misslyckas före kernel-entry

Bootloadern finns för att validera och relokera den dynamiska kerneln, och sedan beskriva den verifierade tidiga objektgrafen för kernel. Den blir inte en generell dynamisk linker för det körande systemet; den rollen hör hemma senare, efter att kernel och modellen för user-space-tjänster finns.


Bevara bootmetadata

Den dynamiska kerneln är inte det enda i boot imagen. Bootloadern bevarar också nödvändig icke-körbar bootmetadata för kernel och det tidiga user-space-systemet, medan körbara tjänster hör till den dynamiska objektgrafen. De körbara artefakterna beskrivs av den dynamiska katalogen som objekt, segment och beroendekanter härledda från den signerade storen och manifestet.

Andra nödvändiga sektioner är icke-körbara blobs. Exempel är bootkonfiguration, dynamic-link-metadatastores och konsolfontdata. De kopieras till mappat minne och beskrivs som skrivskyddade moduler utan entrypoint, eftersom en blob inte är körbar bara för att den förekommer i boot imagen.

Den distinktionen är viktig för capability-design. En bootkonfigurationspayload ska kunna läsas av det tidiga systemet, men den ska inte behandlas som kod. En fontblob kan behövas för framebuffer-kontinuitet, men den behöver inte exekveringsauktoritet. EriX bevarar distinktionen i handoffen:

  • dynamiska objektdeskriptorer för körbara artefakter
  • dynamiska segmentdeskriptorer för mappade körbara och dataområden
  • dynamiska beroendedeskriptorer för tidiga objektrelationer
  • SECTION_TYPE_BOOT_CONFIG för bootpolicydata
  • moduldeskriptorer för icke-körbara bootblobs

Handoffen för skillnaden vidare så att kernel och rootd inte behöver gissa om en bit bootdata är körbar auktoritet, skrivskyddad konfiguration eller vanlig metadata.


Handoffen är kontraktet

Bootloadern anropar inte ett kernel-API, eftersom det ännu inte finns någon körande kernel. I stället bygger bootloadern en handoff-blob: ett versionerat binärt kontrakt definierat av lib-handoff. I den nuvarande profilen från bootloader till kernel börjar den med magin ERIXHK01, major/minor-versioner, total storlek, arkitektur- och plattforms-ID, och därefter offsets och antal för tabellerna som följer.

Handoffen kan innehålla flera klasser av data som kernel behöver innan den kan bygga sin egen runtime-vy av maskinen:

  • normaliserade minneskarteposter
  • deskriptorer för laddade moduler
  • ACPI RSDP-pekare
  • verifierat build ID och imagehash
  • metadata för framebuffer-kontinuitet
  • dynamiska objektdeskriptorer
  • dynamiska segmentdeskriptorer
  • dynamiska beroendekanter

Detta är den första strukturerade överföringen av auktoritet. Firmware gav bootloadern råa fakta och tillfälliga tjänster, och den verifierade boot imagen gav bootloadern signerad payloadmetadata. Bootloadern kombinerar detta till en deterministisk beskrivning av vad den laddade, var den placerade det och vad kernel kan lita på.

Handoffen är inte en vink eller “best effort”-metadata. Den är inputen som kernel börjar bygga sin egen bild av maskinen från, och därför bär den antal, entrystorlekar, offsets, hashvärden, typer, intervall och versionsfält. Det måste vara möjligt för kernel att avvisa den.


Minneskartor behöver normalisering

Firmwares minneskartor är inte automatiskt formade för kernelpolicy. UEFI rapporterar regioner med firmware-minnestyper, medan bootloadern också känner till minnet den har allokerat för den dynamiska kerneln, tidiga objektmappningar, nödvändiga bootblobs, bootstrap-stack, handoff-sidor, framebuffer-mappningar och den ursprungliga boot imagen. De vyerna måste slås ihop innan kernel kan resonera om tillgängligt minne.

I EriX tar bootloadern en snapshot av UEFI-minneskartan, lägger till explicit bootägda regioner och normaliserar resultatet till icke-överlappande intervall med EriX-minnestyper som:

  • användbart RAM
  • reserverat minne
  • ACPI reclaimable-minne
  • ACPI NVS-minne
  • MMIO/enhetsminne
  • bootloaderägt minne
  • boot-imageägt minne

Explicit bootägda regioner vinner över generiska firmwareklassificeringar. Det spelar roll eftersom kernel inte av misstag ska behandla minnet som håller boot imagen, handoff-bloben, dynamiska objektmappningar, nödvändiga bootblobs eller bootstrap-stacken som vanligt fritt RAM. Minne är auktoritet i EriX, så redan så här tidigt är systemet noga med vem som får återanvända vilka bytes.


Lämna UEFI bakom sig

UEFI:s boottjänster är användbara, men tillfälliga. Innan bootloadern hoppar till kernel anropar den ExitBootServices, vilket i praktiken är en enkelriktad övergång. Efter en lyckad exit måste bootloadern behandla pekare till boottjänster som ogiltiga; den kan inte fortsätta be firmware allokera minne eller läsa filer efter att operativsystemet tar över.

Den övergången är känslig eftersom UEFI kräver att bootloadern lämnar med en aktuell minneskartnyckel. Om kartan ändras mellan snapshot och exit kan anropet misslyckas och loadern måste försöka igen med en färsk karta. EriX UEFI-adapter hanterar den retry-loopen i plattformslagret.

Den viktiga designpunkten är att kernel får kontroll efter att bootloadern har slutat använda firmwaretjänster. Kernel ska inte ärva ett halvöppet firmwareberoende; den ska ärva explicit data.


Bygga de första sidtabellerna

Kernel får kontroll med paging redan aktivt. För den nuvarande x86_64 UEFI-profilen bygger bootloadern minimala sidtabeller med två sorters mappningar: en identitetsmappad lågmemory-region för tidig bring-up och specifika higher-half-mappningar för objekten som kernel kommer att använda direkt.

Den nuvarande implementationen identitetsmappar första 1 GiB med 2 MiB-sidor och identitetsmappar också APIC MMIO-fönster. Sedan mappar den higher-half- virtuella intervall för kernel, laddade dynamiska objekt, nödvändiga bootblobs, handoff-bloben, framebuffer och bootstrap-stack. Det räcker för att kernel ska starta i adressrymden den förväntar sig.

Sidtabellerna är inte det slutliga virtuella minnessystemet. De är en bro som låter kernel exekvera, validera handoffen, installera tidigt CPU-tillstånd och börja bygga den riktiga kernel- och user-space-miljön. Bron måste ändå vara korrekt: om kerneln entry-sida inte är mappad faultar maskinen direkt; om handoff-bloben är mappad på fel virtuell adress läser kernel nonsens; om stacken saknas misslyckas entry innan Rust-kod kan göra särskilt mycket.


Varför en higher-half-kernel?

EriX går in i kernel i den övre halvan av den virtuella adressrymden. Det betyder att kernel körs på höga virtuella adresser i stället för att länkas och köras bara i det låga identitetsmappade intervallet. Det är en vanlig kerneldesign eftersom den ger kernel en stabil virtuell adressregion oberoende av var fysiskt minne allokerades.

Higher-half-layouten separerar också kerneln virtuella utrymme från vanliga user-space-intervall och låter kernel behålla sina egna mappningar när adressrymder ändras senare, medan user-space-mappningar kan variera per task. Adresslayouten upprätthåller inte hela säkerhetsmodellen på egen hand, men den stödjer gränsen genom att göra kernelminne skilt från vanligt processminne.

I EriX är bootloadern ansvarig för att göra den första higher-half-entryn möjlig. Den laddar kernel enligt ELF-virtuella adresser, mappar dessa virtuella adresser till allokerade fysiska sidor, skapar en bootstrap-stack i higher half, mappar handoff-bloben på en känd higher-half-adress, laddar cr3 och hoppar till kerneln entrypoint.

Vid hoppet är det nuvarande x86_64-kontraktet medvetet litet och explicit. Bootloadern tillhandahåller bara det tillstånd kernel behöver för att börja validera handoffen och installera sitt eget tidiga CPU-tillstånd:

  • SysV ABI
  • rdi innehåller handoff-pekaren
  • rsp pekar på bootstrap-stacken med förväntad alignment
  • paging är aktivt
  • kontrollen återvänder inte

Detta ABI är litet avsiktligt. Ju mindre implicit tillstånd kerneln entry beror på, desto lättare är gränsen att granska.


Gå in i kernel

På kernelsidan börjar entry innan hela kernel-runtime finns. Den dynamiska kerneln exponerar erix_dynlink_entry, som går in i den tidiga kernelvägen med handoff-pekaren, och det första arbetet är defensivt snarare än policytungt.

Kernel stänger av interrupts, initialiserar tidig seriell utmatning, kontrollerar att handoff-pekaren inte är null, läser handoff-storleken från headern, upprätthåller en maximal handoff-storlek och validerar sedan hela bloben genom lib-handoff. Efter strukturell validering applicerar kernel sina egna policykontroller:

  • arkitekturen måste vara x86_64
  • plattformen måste vara UEFI
  • build ID får inte vara tomt
  • tabellerna i den dynamiska katalogen måste vara internt konsekventa
  • dynamiska objektnamn, hashvärden, segment, beroenden och store-intervall måste valideras före användning

Bootloadern byggde redan handoffen, men kernel validerar den ändå. Betrodda komponenter får inte hoppa över kontrakt bara för att en annan betrodd komponent producerade datan. Poängen med en versionerad handoff är att båda sidor kan vara överens exakt om vad som överfördes.

Först därefter går kernel djupare in i tidig initialisering: GDT-installation, IDT-installation, syscall-vägssetup, valfri tidig konsolinitialisering och slutligen bootstrap-orkestrering för den första root-tasken. Kernel blir kernel gradvis, och det första den gör är att kontrollera marken under fötterna.


Boot är auktoritetsöversättning

Det är lockande att beskriva boot som “ladda kernel och hoppa”. Det är tekniskt sant, men det missar operativsystemets designpoäng. För EriX är boot auktoritetsöversättning: firmware-auktoritet blir explicita bootfakta, boot image-bytes blir verifierade sektioner, ELF-filer blir mappade körbara intervall, blobs blir icke-körbara moduldeskriptorer, firmwares minneskartor blir normaliserade minnesregioner, dynamic-link-metadata blir en begränsad objektgraf och framebuffer-tillstånd blir kontinuitetsmetadata.

Allt detta blir en handoff-blob, och kernel tar emot bloben och avgör om den är acceptabel. Först då kan den börja förvandla maskinresurser till kernelobjekt, capabilities, adressrymder, endpoints och den första user-space-tasken. Det är därför bootprocessen hör hemma i en diskussion om capability-baserade operativsystem: capability-modellen börjar inte efter boot som ett tillägg; den är beroende av att boot inte smugglar in ambient auktoritet.

Bootloadern ska inte bara säga att den laddade några saker. Den ska säga exakt vad den laddade, var det finns, vad det är, hur det verifierades och vilka plattformfakta den observerade. Det är skillnaden mellan ett hopp och en handoff.


Vad EriX håller utanför bootloadern

Bootloadern är kraftfull eftersom den körs tidigt, och just därför ska den förbli liten. EriX vill inte att bootloadern ska besluta runtime-policy: den ska inte bestämma vilken tjänst som äger minnespolicy, hur processer övervakas, hur drivrutiner hanteras eller hur filsystem komponeras.

De besluten hör till kernel och systemtjänster i user space. Bootloaderns arbete är smalare: validera bootartefakten, förbered den minimala exekveringsmiljön, beskriv vad den gjorde och överför kontrollen.

Den gränsen spelar roll för TCB-storlek. En bootloader med fler funktioner är inte automatiskt bättre, eftersom varje funktion i tidig boot är kod som körs innan kernel kan isolera den. Varje parser, fallback-väg, interaktivt läge och policyundantag ökar mängden betrott beteende som måste vara korrekt innan systemet startar.


Framåt

Det här inlägget behandlade boot.img mest som en verifierad container. Nästa steg är att öppna den containern och titta direkt på dess design.

Nästa inlägg förklarar EriX boot.img-format: varför systemet använder en enhetlig image, hur sektioner är utlagda, vilken metadata som bärs med och hur formatet stödjer reproducerbara, deterministiska bootartefakter.