paint-brush
Eine Reise durch die Geheimnisse der Firmware: Vom BIOS/UEFI zum Betriebssystemvon@tristejoursoir
730 Lesungen
730 Lesungen

Eine Reise durch die Geheimnisse der Firmware: Vom BIOS/UEFI zum Betriebssystem

von Aleksandr Goncharov20m2024/08/22
Read on Terminal Reader

Zu lang; Lesen

Erkunden Sie die Entwicklung vom traditionellen BIOS zur modernen UEFI-Firmware, erfahren Sie, wie Startreihenfolgen verwaltet werden, und entdecken Sie die Rollen von Startdiensten und Laufzeitdiensten. Tauchen Sie ein in die Komplexität von Betriebssystem-Bootloadern und sehen Sie, wie Firmware jetzt erweiterte Funktionen und Anwendungen unterstützt.
featured image - Eine Reise durch die Geheimnisse der Firmware: Vom BIOS/UEFI zum Betriebssystem
Aleksandr Goncharov HackerNoon profile picture
0-item
1-item


Haben Sie sich schon einmal gefragt, was passiert, wenn Sie den Einschaltknopf Ihres Computers drücken? In dieser kurzen Pause, bevor Ihr Bildschirm aufleuchtet, findet eine komplexe Reihe von Prozessen statt. Dieser Artikel taucht in die faszinierende Welt der Firmware ein und untersucht, wie verschiedene Komponenten während des Startvorgangs interagieren .


Wenn Sie diese Zusammenhänge verstehen, erhalten Sie ein klareres Bild der grundlegenden Elemente, die Ihr System zum Leben erwecken. Unser Hauptaugenmerk liegt auf der Intel x86-Architektur , aber viele Prinzipien gelten auch für andere Architekturen.


Wenn Sie den ersten Teil unserer Serie verpasst haben, klicken Sie hier , um ihn nachzuholen. Lassen Sie uns nun die Geheimnisse hinter der Firmware lüften.

Inhaltsverzeichnis:

  • Definitionen
  • Allgemeine Firmware-Architektur
  • Bootloader der ersten Stufe (FSBL)
    • BIOS (POST-Phase)
    • UEFI-Plattforminitialisierung (PI)
    • coreboot
    • Andere Lösungen
  • Bootloader der zweiten Stufe (SSBL)
    • BIOS
    • UEFI
  • Betriebssystem-Bootloader


Definitionen

  • Firmware : Ein spezieller Typ von Software, der in die Hardware eingebettet ist, eine Steuerung auf niedriger Ebene bietet und die korrekte Funktion der Hardware sowie die Interaktion mit anderen Systemkomponenten ermöglicht.


  • Basic Input/Output System (BIOS) : eine ältere Firmware (ursprünglich für den IBM PC entwickelt), die für die Initialisierung der Hardware nach dem Einschalten der Plattform verantwortlich ist. Heutzutage wird es oft vage als vollständiger Satz der Firmware bezeichnet.


  • Bootloader : eine allgemeine Bezeichnung für Firmware, die für das Booten eines Computers verantwortlich ist. Es wird als modernes Konzept anstelle des BIOS verwendet und bietet häufig ein Framework mit Bootstrap-Code zum Initialisieren des Prozessors und des Chipsatzes sowie Schnittstellen für Dritte (z. B. Motherboard-Entwickler), um eine plattformspezifische Initialisierung durchzuführen.


  • Payload : Eine Software, die ausgeführt werden soll, wenn der Bootloader beendet wird. Dies kann ein Bootloader der zweiten Stufe, ein Betriebssystem, eine BIOS/UEFI-Anwendung usw. sein. Normalerweise kümmert sie sich um den Bootstrapping-Flow gemäß dem Firmware-Design.


  • Die Verwendung der Begriffe BIOS und Bootloader kann verwirrend sein, da ihre Bedeutung kontextabhängig ist. Wenn jedoch jemand Firmware , BIOS oder Bootloader erwähnt, bezieht er sich im Allgemeinen auf den vollständigen Satz der Firmware, der zwischen dem Betriebssystem und der Hardware ausgeführt wird .


  • EFI vs. UEFI : Das Extensible Firmware Interface (EFI) war die ursprüngliche Spezifikation, die von Intel entwickelt wurde. Das Unified Extensible Firmware Interface (UEFI) ist der Nachfolger von EFI und wurde vom UEFI-Forum entwickelt, um die ursprüngliche Spezifikation zu standardisieren und zu erweitern. In den meisten Fällen werden EFI und UEFI synonym verwendet.

Allgemeine Firmware-Architektur

Um zu verstehen, wie die Firmware-Komponenten interagieren, werden wir die gesamte Architektur mit allen verbundenen Teilen untersuchen. Der Ausführungsfluss, der im folgenden Diagramm dargestellt ist, beginnt mit dem Reset-Vektor , der Teil des First-Stage-Bootloaders ist. Von dort aus durchläuft er verschiedene Firmware-Phasen:



Firmware oder BIOS können im Allgemeinen in zwei Hauptteile unterteilt werden, zwischen denen normalerweise nur eine minimale Schnittstelle besteht:


  1. Hardwareinitialisierung : Verantwortlich für die Initialisierung der Hardwarekomponenten des Systems.
  2. Schnittstelle zum Betriebssystem und Benutzer : Bietet die erforderlichen Schnittstellen zum Betriebssystem und zum Benutzer.


Das Design der Plattform-Firmware kann entweder monolithisch sein und Hardwareinitialisierung und Startfunktionalität kombinieren oder einem modularen und stufenweisen Startablauf folgen. Die Wahl des Designs hängt von den Systemanforderungen ab und kann für bestimmte Geräte vorzuziehen sein.


Das folgende Diagramm veranschaulicht, wie verschiedene Firmware-Komponenten interagieren und gemeinsam verwendet werden können, um den Startvorgang zu unterstützen (Pfeile zeigen die Ausführungsreihenfolge an):



Machen Sie sich keine Sorgen, wenn Ihnen diese Diagramme jetzt kompliziert erscheinen. Sehen Sie sie sich nach dem Lesen dieses Artikels noch einmal an, dann werden sie klarer.

Bootloader der ersten Stufe (FSBL)

Diese Firmware dient zum Initialisieren von Computern und eingebetteten Systemen mit einem Schwerpunkt auf minimaler Hardware-Initialisierung : Es wird nur das getan, was unbedingt nötig ist, und dann wird die Kontrolle an den Second-Stage Bootloader übergeben, um das Betriebssystem zu booten. Der FSBL lädt keine Betriebssysteme von anderen Speichermedien als dem Flash-Chip . Da er nur die zugrunde liegende Hardware initialisiert und keine Bootmedien wie Festplatten, SSDs oder USB-Flash-Laufwerke verarbeitet, ist ein weiteres Stück Software erforderlich, um ein Betriebssystem tatsächlich zu booten.


Hauptaufgaben von FSBL :


  1. CPU : Wechseln vom 16-Bit- Realmodus in den 32-Bit -geschützten Modus ( Hinweis : oder im BIOS in den virtuellen 8086-Modus ).
  2. Cache-Auslastung : Aufruf von FSP-T, um Cache-As-RAM für die C-Umgebung zu konfigurieren.
  3. Debug-Port : Initialisieren des konfigurierten Debug-Ports durch Aufrufen platinenspezifischer Initialisierungsmethoden.
  4. Speicherinitialisierung : Aufrufen von FSP-M, um den Hauptspeicher des Systems zu initialisieren und wichtige Systemspeicherinformationen zu speichern.
  5. GPIO : Konfigurieren von GPIO-Pins (General-Purpose Input/Output) für die Schnittstelle mit externen Geräten.
  6. Silizium : Durchführen einer frühen Plattforminitialisierung und Verwenden von FSP-S zum Abschließen der Initialisierung von Chipsatz, CPU und IO-Controller.
  7. PCI-Aufzählung : Aufzählung von PCI-Geräten und Zuweisung von Ressourcen wie Speicheradressen und IRQs.
  8. Vorbereitung der Nutzlast : Einrichten von SMBIOS- und ACPI- Tabellen, einschließlich der Vorbereitungsinformationen (Coreboot-Tabellen, HOBs), die an die Nutzlast übergeben werden müssen.
  9. Laden und Übergabe : Laden und Übergeben der Kontrolle an die Nutzlast.

BIOS (POST-Phase)

In den frühen Tagen der Computertechnik war Open-Source-Software nicht sehr beliebt und die meisten BIOS-Implementierungen waren proprietär. Es gibt nur wenige verfügbare offene Lösungen, die BIOS-POST-Quellcode bereitstellen, wie Super PC/Turbo XT BIOS und GLaBIOS . Diese Projekte wurden für die Verwendung auf IBM 5150/5155/5160-Systemen und den meisten XT-Klonen entwickelt.


Die bekannteren Open-Source-BIOS-Implementierungen wie OpenBIOS und SeaBIOS führen jedoch keine Hardware-Initialisierung durch, da sie nicht für die Ausführung auf nackter Hardware vorgesehen sind. Sie werden jedoch häufig als Second-Stage-Bootloader verwendet und laufen nativ in virtuellen Umgebungen wie QEMU und Bochs.


In jedem Fall besteht kaum die Möglichkeit, dass Sie direkt mit diesen frühen BIOS arbeiten oder sich eingehend mit deren Besonderheiten befassen müssen. Wenn Sie sich jedoch näher damit befassen möchten, sind die genannten Repositories ein guter Ausgangspunkt.


Was die aktuellen Entwicklungstrends angeht, scheint es keine Weiterentwicklung proprietärer BIOS-Lösungen zu geben und solche Projekte sind angesichts moderner Alternativen obsolet geworden.

UEFI-Plattforminitialisierung (PI)

Der Startvorgang folgt einem schrittweisen Ablauf, der in der nächsten Abbildung von links nach rechts beginnt. Die Zeitleiste des Plattformstartvorgangs ist in die folgenden Abschnitte unterteilt, die durch gelbe Kästchen angezeigt werden:



  • Sicherheit (SEC) : Die erste Phase nach einem Reset-Vektor , ihre Hauptfunktion besteht darin, temporären RAM (CPU- Cache-As-RAM oder SRAM) einzurichten.
  • Pre-EFI-Initialisierung (PEI) : In dieser Phase werden spezielle Treiber, sogenannte Pre-EFI-Initialisierungsmodule (PEIMs), gestartet. Diese Module übernehmen die grundlegende Initialisierung der Hardware, z. B. die Konfiguration der CPU und des Chipsatzes sowie die Einrichtung des Hauptspeichers (DRAM) .
  • Driver Execution Environment (DXE) : In dieser Phase wird die restliche Systeminitialisierung durchgeführt. Die DXE-Phase stellt UEFI-Dienste bereit und unterstützt verschiedene Protokolle und Treiber, die für den Betrieb des Systems erforderlich sind.
  • Boot Device Select (BDS) : In dieser Phase wird die Startrichtlinie der Plattform implementiert, die Startreihenfolge bestimmt und das entsprechende Startgerät/den entsprechenden Bootloader ausgewählt.
  • Transient System Load (TSL) : Während dieser Phase führt das System Anwendungen mithilfe von UEFI-Diensten aus, um das Betriebssystem vorzubereiten. Dies umfasst den Übergang von der UEFI-Umgebung zum Betriebssystem und endet mit dem Aufruf von ExitBootServices() .
  • Laufzeit (RT) : In dieser Phase ist das Betriebssystem voll funktionsfähig und verwaltet das System unter seiner Kontrolle.
  • After Life (AL) : In dieser Phase geht es um Szenarien, in denen die Hardware oder das Betriebssystem abstürzt/herunterfährt/neu startet. Die Firmware kann Wiederherstellungsmaßnahmen versuchen, die UEFI PI-Spezifikation definiert jedoch keine spezifischen Anforderungen oder Verhaltensweisen für diese Phase.


Dieser Prozess und seine Ausführungsphasen werden in der UEFI Platform Initialization (PI) Specification behandelt. Es gibt jedoch auch die UEFI-Schnittstelle (im Bild durch die fette blaue Linie gekennzeichnet), die nicht Teil des vorherigen Dokuments ist und in der UEFI Specification beschrieben wird. Obwohl die Namen und die häufige Verwendung von UEFI verwirrend sein können, haben diese beiden Dokumente unterschiedliche Schwerpunkte:


  • UEFI PI-Spezifikation : Konzentriert sich auf Schnittstellen zwischen Low-Level-Firmwarekomponenten und beschreibt im Detail, wie diese Module interagieren, um die Plattform zu initialisieren.


  • UEFI-Spezifikation : Definiert die Schnittstellen für die Interaktion zwischen dem Betriebssystem (OS) und der Firmware. Dies wird im Zusammenhang mit dem Second-Stage-Bootloader ausführlicher erläutert. Beachten Sie, dass die UEFI-Spezifikation auf der PI-Spezifikation basiert.


Im Wesentlichen geht es in beiden Spezifikationen um Schnittstellen, allerdings auf unterschiedlichen Ebenen. Ausführliche Informationen zu beiden Spezifikationen finden Sie auf der Website des UEFI-Forums .


UEFI PI wurde ursprünglich als einheitliche Firmware-Lösung entwickelt, ohne die Unterscheidung zwischen Bootloadern der ersten und zweiten Stufe zu berücksichtigen. Wenn wir UEFI jedoch als Bootloader der ersten Stufe bezeichnen, umfasst dies die Phasen SEC , PEI und frühe DXE . Der Grund, warum wir DXE in frühe und späte Phasen unterteilen, liegt in ihren unterschiedlichen Rollen im Initialisierungsprozess.


In der frühen DXE- Phase führen Treiber normalerweise die grundlegende Initialisierung von CPU/PCH/Board durch und erstellen außerdem DXE Architectural Protocols (APs) , die dabei helfen, die DXE-Phase von der plattformspezifischen Hardware zu isolieren. APs kapseln die plattformspezifischen Details ein, sodass die späte DXE- Phase unabhängig von den Hardwarespezifikationen ausgeführt werden kann.



Coreboot

Detaillierte Artikel zur Funktionsweise von Coreboot folgen in Kürze. Folgen Sie mir auf den sozialen Medien – sie werden in Kürze veröffentlicht!

Andere Lösungen

  • Intel Slim Bootloader (SBL) : reiner Bootloader der ersten Stufe, der nur die Initialisierung der Kernhardwarekomponenten und anschließend das Laden der Nutzlast ermöglicht. Funktioniert jedoch nur auf Intel x86-Plattformen und unterstützt weder AMD x86 noch andere Architekturen.
  • Das U-Boot : sowohl ein Bootloader der ersten als auch der zweiten Stufe. Die Unterstützung für das direkte Booten vom x86- Reset-Vektor der Plattform (bekannt als Bare-Modus) ist jedoch im Vergleich zu anderer Firmware eingeschränkt. Es ist beliebter auf eingebetteten Systemen und ARM-basierten Geräten. Als Bootloader der zweiten Stufe implementiert U-Boot eine Teilmenge des UEFI, konzentriert sich jedoch auf eingebettete Systeme.

Bootloader der zweiten Stufe (SSBL)

Nach Abschluss der anfänglichen Hardwareeinrichtung kommt die zweite Phase ins Spiel. Ihre Hauptaufgabe besteht darin, eine Softwareschnittstelle zwischen dem Betriebssystem und der Plattform-Firmware einzurichten und sicherzustellen, dass das Betriebssystem Systemressourcen verwalten und mit Hardwarekomponenten interagieren kann.


Das Ziel des SSBL besteht darin, Hardwarevariationen so weit wie möglich zu verbergen und die Entwicklung von Betriebssystemen und Anwendungen zu vereinfachen, indem die meisten Schnittstellen auf Hardwareebene verwaltet werden. Diese Abstraktion ermöglicht es Entwicklern, sich auf Funktionen auf höherer Ebene zu konzentrieren, ohne sich um die zugrunde liegenden Hardwareunterschiede kümmern zu müssen.


Hauptaufgaben von SSBL :


  1. Abrufen von Plattforminformationen : Ruft plattformspezifische Informationen vom First-Stage-Bootloader ab, einschließlich Speicherzuordnung, SMBIOS, ACPI-Tabellen, SPI-Flash usw.


  2. Plattformunabhängige Treiber ausführen : Enthält Treiber für SMM, SPI, PCI, SCSI/ATA/IDE/DISK, USB, ACPI, Netzwerkschnittstellen usw.


  3. Implementierung von Diensten (auch Schnittstelle genannt) : Bietet eine Reihe von Diensten, die die Kommunikation zwischen dem Betriebssystem und den Hardwarekomponenten erleichtern.


  4. Setup-Menü : Bietet ein Setup-Menü zur Systemkonfiguration, in dem Benutzer Einstellungen bezüglich Startreihenfolge, Hardwareeinstellungen und anderen Systemparametern anpassen können.


  5. Boot-Logik : Mechanismus zum Lokalisieren und Laden der Nutzlast (wahrscheinlich Betriebssystem) von verfügbaren Boot-Medien.

BIOS

Die Schnittstelle im BIOS wird als BIOS-Dienste/Funktionen/Interruptaufrufe bezeichnet. Diese Funktionen stellen eine Reihe von Routinen für den Hardwarezugriff bereit, aber die spezifischen Details ihrer Ausführung auf der jeweiligen Hardware des Systems sind für den Benutzer verborgen.


Im 16-Bit- Realmodus können sie einfach durch Aufrufen eines Software-Interrupts über einen INT -x86-Assemblersprachenbefehl aufgerufen werden. Im 32-Bit- geschützten Modus sind aufgrund der unterschiedlichen Verarbeitung von Segmentwerten fast alle BIOS-Dienste nicht verfügbar.




Als Beispiel für die Verwendung dieser Schnittstelle nehmen wir beispielsweise Disk Services ( INT 13h ), das sektorbasierte Lese- und Schreibdienste für Festplatten und Disketten unter Verwendung der Cylinder-Head-Sector (CHS) -Adressierung bereitstellt. Angenommen, wir möchten 2 Sektoren (1024 Bytes) lesen und sie an der Speicheradresse 0x9020 laden, dann könnte der folgende Code ausgeführt werden:


 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


Wenn Sie wissen möchten, wie dieser Dienst in SeaBios geschrieben ist, sehen Sie sich src/disk.c an.

BOOT-Phase

  • Beginnt mit der Suche nach einem bootfähigen Gerät (es kann eine Festplatte, CD-ROM, Diskette usw. sein), indem der erste 512-Byte-Sektor (Sektor Null) von den Geräten gelesen wird.


  • Die Bootstrap-Sequenz im BIOS lädt den ersten gültigen Master Boot Record (MBR) , den sie findet, in den physischen Speicher des Computers an der physischen Adresse 0x7C00 (Hinweis: 0x0000:0x7c00 und 0x7c0:0x0000 beziehen sich auf dieselbe physische Adresse).


  • Das BIOS überträgt die Kontrolle auf die ersten 512 Bytes der Nutzlast. Dieser geladene Sektor , der zu klein ist, um den gesamten Nutzlastcode aufzunehmen, dient dazu, den Rest der Nutzlast vom bootfähigen Gerät zu laden. An diesem Punkt kann die Nutzlast die vom BIOS freigegebene Schnittstelle verwenden.


Es ist bemerkenswert, dass es damals noch keine BIOS-Spezifikationen gab . BIOS ist ein De-facto-Standard – es funktioniert so, wie es in den 1980er Jahren auf echten IBM-PCs funktionierte. Die übrigen Hersteller haben einfach Reverse Engineering betrieben und IBM-kompatible BIOSe erstellt. Daher gab es keine Regelung, die BIOS-Hersteller daran hinderte, neue BIOS-Funktionen zu entwickeln oder überlappende Funktionalitäten einzuführen.

Einheitliche erweiterbare Firmware-Schnittstelle (UEFI)

Wie bereits erwähnt, ist UEFI selbst nur eine Spezifikation und verfügt über viele Implementierungen. Die am weitesten verbreitete ist TianoCore EDK II , eine Open-Source- Referenzimplementierung der UEFI- und PI-Spezifikationen. Obwohl EDKII allein nicht ausreicht, um eine voll funktionsfähige Boot-Firmware zu erstellen, bietet es eine solide Grundlage für die meisten kommerziellen Lösungen.


Um verschiedene First-Stage-Bootloader zu unterstützen und eine UEFI-Schnittstelle bereitzustellen, wird das UEFI-Payload -Projekt verwendet. Es basiert auf der anfänglichen Einrichtung und den von der Boot-Firmware bereitgestellten Plattforminformationen, um das System für die UEFI-Umgebung vorzubereiten.


Die UEFI-Nutzlast verwendet die DXE- und BDS- Phasen, die plattformunabhängig konzipiert sind. Sie bietet eine generische Nutzlast, die sich an verschiedene Plattformen anpassen lässt. In den meisten Fällen sind keine Anpassungen oder plattformspezifischen Anpassungen erforderlich und sie kann unverändert verwendet werden, indem Plattforminformationen vom First-Stage-Bootloader verwendet werden.


Varianten der UEFI-Nutzlast :


  1. Legacy-UEFI-Payload : Erfordert eine Parse-Bibliothek, um erforderliche implementierungsspezifische Plattforminformationen zu extrahieren. Wenn der Bootloader seine API aktualisiert, muss auch die Payload aktualisiert werden.



  2. Universelle UEFI-Nutzlast : Befolgt die Universal Scalable Firmware (USF)-Spezifikation und verwendet Executable and Linkable Format (ELF) oder Flat Image Tree (FIT) als gemeinsames Bildformat. Anstatt sie selbst zu analysieren, erwartet es, die Hand Off Blocks (HOBs) beim Nutzlasteintrag zu erhalten.


Während die Legacy UEFI Payload gut funktioniert, versucht die EDK2-Community, die Branche in Richtung Universal UEFI Payload zu bewegen. Die Wahl zwischen den Payloads hängt von Ihren Firmware-Komponenten ab. Beispielsweise ist es nicht möglich, die Legacy Payload mit SMM-Unterstützung auf Slim Bootloader ohne meinen Patch auszuführen. Andererseits erfordert die Verwendung der Universal Payload mit Coreboot eine Shim-Schicht, um Coreboot-Tabellen in HOBs zu übersetzen, eine Funktion, die nur im StarLabs EDK2-Fork verfügbar ist.

Schnittstelle

Jedes UEFI-kompatible System stellt eine Systemtabelle bereit, die an jeden Code weitergegeben wird, der in der UEFI-Umgebung ausgeführt wird (Treiber, Anwendungen, Betriebssystemlader). Diese Datenstruktur ermöglicht einer UEFI-ausführbaren Datei den Zugriff auf Systemkonfigurationstabellen wie ACPI , SMBIOS und eine Sammlung von UEFI-Diensten .



Die Tabellenstruktur wird in MdePkg/Include/Uefi/UefiSpec.h beschrieben:


 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;


Zu den Diensttypen gehören die folgenden: Boot-Dienste , Runtime-Dienste und durch Protokolle bereitgestellte Dienste .


UEFI abstrahiert den Zugriff auf das Gerät durch die Einrichtung von UEFI-Protokollen . Diese Protokolle sind Datenstrukturen, die Funktionszeiger enthalten und durch einen Globally Unique Identifier (GUID) identifiziert werden, der es anderen Modulen ermöglicht, sie zu finden und zu verwenden. Sie können über Boot Services ermittelt werden.


Ein UEFI-Treiber erstellt diese Protokolle und die eigentlichen Funktionen (keine Zeiger!) sind im Treiber selbst enthalten. Dieser Mechanismus ermöglicht es verschiedenen Komponenten innerhalb der UEFI-Umgebung, miteinander zu kommunizieren, und stellt sicher, dass das Betriebssystem mit Geräten interagieren kann, bevor es seine eigenen Treiber lädt.



Während einige Protokolle vordefiniert und in der UEFI-Spezifikation beschrieben sind, können Firmware-Anbieter auch eigene benutzerdefinierte Protokolle erstellen, um die Funktionalität einer Plattform zu erweitern.


Boot-Dienste

Stellen Sie Funktionen bereit, die nur während des Startvorgangs verwendet werden können. Diese Dienste bleiben verfügbar, bis die Funktion EFI_BOOT_SERVICES.ExitBootServices() aufgerufen wird ( MdeModulePkg/Core/Dxe/DxeMain/DxeMain.c ).


Zeiger auf alle Boot-Dienste werden in der Boot Services Table ( MdePkg/Include/Uefi/UefiSpec.h ) gespeichert:


 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;


Laufzeitdienste

Ein minimaler Satz von Diensten ist weiterhin zugänglich, während das Betriebssystem ausgeführt wird. Im Gegensatz zu Boot Services sind diese Dienste weiterhin gültig, nachdem eine Nutzlast (z. B. der OS-Bootloader) über einen Aufruf von EFI_BOOT_SERVICES.ExitBootServices() die Kontrolle über die Plattform übernommen hat.


Zeiger auf alle Laufzeitdienste werden in der Runtime Services Table ( MdePkg/Include/Uefi/UefiSpec.h ) gespeichert:


 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;


Das Bild unten zeigt die Zeitleiste für Boot- und Laufzeitdienste, sodass Sie genau sehen können, wann jeder aktiv ist.



Boot Device Select (BDS)-Phase

Die UEFI-Spezifikation definiert eine Boot-Policy-Engine namens UEFI-Boot-Manager . Sie versucht , UEFI-Anwendungen in einer bestimmten Reihenfolge zu laden. Diese Reihenfolge und andere Einstellungen können durch Ändern globaler NVRAM-Variablen (Non-Volatile Random-Access Memory) konfiguriert werden. Lassen Sie uns die wichtigsten davon besprechen:


  • Boot#### ( #### wird durch einen eindeutigen Hex-Wert ersetzt) – eine Boot-/Ladeoption.
  • BootCurrent – die Startoption zum Starten des aktuell laufenden Systems.
  • BootNext – die Startoption nur für den nächsten Start. Dies ersetzt BootOrder nur für einen Start und wird vom Bootmanager nach der ersten Verwendung gelöscht. Auf diese Weise können Sie das nächste Startverhalten ändern, ohne BootOrder zu ändern.
  • BootOrder – die geordnete Ladeliste der Startoptionen. Der Bootmanager versucht, die erste aktive Option in dieser Liste zu starten. Wenn dies nicht gelingt, versucht er es mit der nächsten Option und so weiter.
  • BootOptionSupport – die vom Boot-Manager unterstützten Arten von Boot-Optionen.
  • Timeout – das Timeout des Bootmanagers der Firmware in Sekunden, bevor automatisch der Startwert aus BootNext oder BootOrder ausgewählt wird.


Diese Variablen können einfach unter Linux mit efibootmgr(8) abgerufen werden:


 [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


Sehen wir uns das Booten anhand des obigen Codeausschnitts an. UEFI beginnt mit der Iteration der BootOrder -Liste. Für jeden Eintrag in der Liste sucht es nach einer entsprechenden Boot#### -Variable – Boot0000 für 0000, Boot2003 für 2003 und so weiter. Wenn die Variable nicht vorhanden ist, wird mit dem nächsten Eintrag fortgefahren. Wenn die Variable vorhanden ist, wird der Inhalt der Variablen gelesen. Jede Bootoptionsvariable enthält einen EFI_LOAD_OPTION Deskriptor, der ein byteweise gepackter Puffer mit Feldern variabler Länge ist (es ist nur die Datenstruktur).


Die Datenstruktur ist in [MdePkg/Include/Uefi/UefiSpec.h][ https://github.com/tianocore/edk2/blob/edk2-stable202405/MdePkg/Include/Uefi/UefiSpec.h#L2122 ] beschrieben.


 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;


An diesem Punkt untersucht die Firmware einen Gerätepfad ( EFI_DEVICE_PATH_PROTOCOL ). In den meisten Fällen wird unser Computer von einem Speichergerät (Festplatte/SSD/NVMe/usw.) hochgefahren. Der Gerätepfad würde also den Knoten HD(Partition Number, Type, Signature, Start sector, Size in sectors) enthalten.


  • Typ — gibt das für das Partitionierungsschema verwendete Format mit den Schlüsselwörtern MBR (1) oder GPT (2) an.
  • Signatur – eine 4-Byte-MBR-Signatur, wenn der Typ MBR ist, oder eine 16-Byte- UUID , wenn der Typ GPT ist.


Hinweis : Wenn Sie wissen möchten, wie andere Pfade übersetzt werden, lesen Sie die UEFI-Spezifikation v2.10, 10.6.1.6 Text Device Node Reference .


UEFI prüft die Festplatte und sucht nach einer Partition, die mit dem Knoten übereinstimmt. Wenn dies der Fall ist, sollte sie mit einem bestimmten Globally Unique Identifier (GUID) gekennzeichnet sein, der sie als EFI-Systempartition (ESP) kennzeichnet. Diese ist mit einem Dateisystem formatiert, dessen Spezifikation auf der spezifischen Version des FAT-Dateisystems basiert und EFI-Dateisystem heißt; eigentlich ist es nur ein normales FAT12/16/32 .


  • Nativer Start : Wenn der Gerätepfad einen expliziten Pfad zur Datei File(\Path\To\The\File.efi) enthält, sucht UEFI nach dieser bestimmten Datei. Beispielsweise enthält die Option Boot0000 File(\EFI\ARCHLINUX\grubx64.efi) .
  • Fallback-Boot : Wenn der Gerätepfad einfach auf eine Festplatte verweist, verwendet die Firmware in solchen Situationen einen Fallback-Boot-Pfad , der auf der Architektur basiert – \EFI\BOOT\BOOT{arch}.EFI ( BOOTx64.EFI für amd64 oder BOOTia32.EFI für i386 / IA32 ). Dieser Mechanismus ermöglicht es bootfähigen Wechselmedien (z. B. einem USB-Laufwerk), in UEFI zu funktionieren; sie verwenden einfach einen Fallback-Boot-Pfad . Beispielsweise verwendet die Option Boot0002 diesen Mechanismus.


Hinweis: Alle oben genannten Boot#### Optionen beziehen sich auf die Boot-Optionen, die in der Beispielausgabe von efibootmgr angezeigt werden.


In beiden Fällen lädt der UEFI-Bootmanager die UEFI-Anwendung (das kann der OS-Bootloader , die UEFI-Shell, Dienstprogramme, das System-Setup usw. sein) in den Speicher. In diesem Moment wird die Kontrolle dann an den Einstiegspunkt der UEFI-Anwendung übertragen. Anders als das BIOS kann die UEFI-Anwendung die Kontrolle an die Firmware zurückgeben (außer in der Situation, in der die Anwendung die Kontrolle über das System übernimmt). Wenn dies geschieht oder etwas schief geht, fährt der Bootmanager mit dem nächsten Boot#### Eintrag fort und folgt genau demselben Prozess.


In der Spezifikation wird erwähnt, dass der Bootmanager die Datenbankvariablen automatisch verwalten kann. Dazu gehört das Entfernen von Ladeoptionsvariablen, die nicht referenziert werden oder nicht analysiert werden können. Darüber hinaus kann er jede geordnete Liste neu schreiben, um alle Ladeoptionen ohne entsprechende Ladeoptionsvariablen zu entfernen.


Der obige Text beschreibt den UEFI-Bootvorgang . Außerdem kann die UEFI-Firmware im Compatibility Support Module (CSM) -Modus ausgeführt werden, der ein BIOS emuliert.

Betriebssystem-Bootloader

Eine Software, die von der Firmware gestartet wird (normalerweise Second-Stage Bootloader ) und deren Schnittstelle zum Laden des Betriebssystemkernels verwendet. Sie kann so komplex wie ein Betriebssystem sein und Funktionen bieten wie:


  • Lesen aus verschiedenen Dateisystemen (HFS+, ext4, XFS usw.)
  • Interaktion über ein Netzwerk (z. B. TFTP, HTTP)
  • Booten von Multiboot -kompatiblen Kerneln
  • Kettenladen
  • Laden der ersten Ramdisks ( initrd )
  • Und mehr!


Die allgemeinen Designs dieser Programme gehen über den Rahmen dieses Artikels hinaus. Einen detaillierten Vergleich beliebter Betriebssystem-Bootloader finden Sie im ArchLinux-Wiki und im Wikipedia-Artikel .


Das Windows-System verwendet seinen proprietären Betriebssystem-Bootloader, der als Windows Boot Manager (BOOTMGR) bekannt ist.


Firmware ist nicht länger ein kleines, komplexes Stück Code. Es ist eine riesige Menge an komplexem Code geworden, und aktuelle Trends tragen nur dazu bei. Wir können Doom , Twitter und viele andere interessante Anwendungen darauf ausführen.


Das Verständnis der Gesamtarchitektur hilft Ihnen dabei, diese Komponenten gedanklich zu organisieren. Indem Sie das Design vorhandener Firmware untersuchen, erhalten Sie Einblick in den faszinierenden Prozess, der sich jedes Mal entfaltet, wenn ein Computer eingeschaltet wird. Diese Top-Down-Perspektive verdeutlicht nicht nur die Rolle jedes Teils, sondern unterstreicht auch die anspruchsvolle und sich weiterentwickelnde Natur moderner Firmware-Systeme.

Ressourcen