- Eine technische Erklärung, die den Ablauf vom Drücken des Einschaltknopfs bis zur Ausführung des Linux-Kernels Schritt für Schritt beschreibt
- Behandelt konkret, wie die CPU im Real Mode startet und in den Protected Mode sowie den Long Mode wechselt
- Beschreibt detailliert die Rolle und Funktionsweise der einzelnen Schritte, darunter BIOS/UEFI-Firmware, Bootloader (GRUB) sowie Kernel-Dekomprimierung und Adress-Relokation
- Erläutert die zentralen Konzepte für die Kernel-Initialisierung wie Memory Mapping, Interrupts, Seitentabellen und kASLR anhand knapper Beispiele
- Vermittelt durch das Verständnis der internen Mechanismen des Linux-Bootvorgangs Einblicke in Systemarchitektur, Sicherheit und Performance-Optimierung
Teil 1 — Vom Einschaltknopf bis zur ersten Ausführung des Kernels
-
Wenn der Einschaltknopf gedrückt wird, wird die CPU in den Real Mode zurückgesetzt und führt die ersten Instruktionen aus
- Der Real Mode ist ein einfaches Adressschema aus der 8086-Ära, bei dem die physische Adresse aus Segment und Offset berechnet wird
- Beispiel:
physical_address = (segment << 4) + offset - Nach dem Reset springt die CPU zur Adresse
0xFFFFFFF0(Reset-Vektor) und übergibt die Kontrolle an die Firmware
-
Register sind ultraschnelle Speicherplätze innerhalb der CPU, darunter CS (Code Segment), IP (Instruction Pointer) usw.
- CS gibt die Position des aktuellen Codes an, IP zeigt auf die nächste auszuführende Instruktion
BIOS und UEFI
- BIOS ist die ältere Firmware, die nach dem POST (Power-On Self Test) die Boot-Reihenfolge prüft und nach einem bootfähigen Datenträger sucht
- Ein bootfähiger Datenträger ist dadurch markiert, dass die ersten 512 Byte des Sektors mit
0x55AAenden - Das BIOS kopiert diesen Sektor an die Adresse
0x7C00und springt dorthin zur Ausführung
- Ein bootfähiger Datenträger ist dadurch markiert, dass die ersten 512 Byte des Sektors mit
- UEFI ist die moderne Alternative, die Dateisysteme direkt versteht und größere Boot-Programme laden kann
- Anders als beim BIOS gibt es keine Beschränkung auf den „ersten Sektor“, und dem Betriebssystem werden umfangreichere Systeminformationen übergeben
Bootloader
- Der Bootloader ist das Programm, das den Kernel in den Speicher lädt und für die Ausführung vorbereitet
- Typischerweise wird GRUB verwendet, das Konfigurationsdateien liest und den Kernel sowie die initiale RAM-Disk (initrd) in den Speicher lädt
- Die Kernel-Datei besteht aus einem kleinen Setup-Programm für den Real Mode und dem komprimierten Kernel-Hauptteil
- GRUB schreibt Informationen wie Kernel-Position, Kommandozeile und initrd-Position in die Struktur setup header und springt dann in den Kernel-Setup-Code
Setup-Programm (Setup Code)
- Es sorgt vor der Kernel-Ausführung für einen vorhersagbaren Arbeitsbereich
- Segmentregister (CS, DS, SS) werden ausgerichtet und das Direction Flag wird gelöscht, damit Speicherkopien konsistent funktionieren
- Es wird ein Stack angelegt, um temporäre Daten bei Funktionsaufrufen zu speichern
- Der BSS-Bereich (der Bereich globaler Variablen, die mit dem Anfangswert 0 starten müssen) wird auf 0 initialisiert
- Wenn die Option
earlyprintkgesetzt ist, kann ein serieller Port konfiguriert werden, um frühe Debug-Meldungen auszugeben - Von der Firmware wird eine RAM-Map (e820) angefordert, um nutzbare und reservierte Speicherbereiche zu erfassen
- Nach Abschluss aller Vorbereitungen wird die erste C-Funktion
mainaufgerufen, danach beginnt die Phase des Moduswechsels
Interrupts
- Interrupts sind ein Mechanismus, bei dem die CPU ihre aktuelle Arbeit kurz unterbricht, um dringende Ereignisse zu verarbeiten
- Typische Beispiele sind Hardware-Ereignisse wie Tastatureingaben oder Timer
- Maskierbare Interrupts können vorübergehend blockiert werden, NMI (Non-Maskable Interrupt) wird immer verarbeitet
- Während des Moduswechsels werden sie vorübergehend blockiert, um unerwartete Interrupts zu vermeiden
Teil 2 — Vom Real Mode zu 32 Bit und dann zu 64 Bit
- Modernes Linux läuft im Long Mode der x86_64-Architektur
- Dafür ist ein schrittweiser Übergang nötig: Real Mode → Protected Mode → Long Mode
Protected Mode
- Dies ist ein 32-Bit-Modus, der eingeführt wurde, um die Grenzen der 1980er Jahre zu überwinden, und er besitzt zwei zentrale Strukturen
- GDT (Global Descriptor Table): definiert Startadresse, Größe und Rechte von Segmenten
- Linux verwendet das Flat Model, das den gesamten 32-Bit-Adressraum zu einem zusammenhängenden Bereich vereinfacht
- IDT (Interrupt Descriptor Table): speichert die Adressen der Handler, die bei Interrupts aufgerufen werden
- Während des Bootvorgangs wird nur eine minimale IDT geladen, die vollständige IDT wird nach der Kernel-Initialisierung eingerichtet
- GDT (Global Descriptor Table): definiert Startadresse, Größe und Rechte von Segmenten
Ablauf des Moduswechsels
- Der Setup-Code führt zunächst das Deaktivieren von Interrupts, das Stoppen des PIC-Chips, das Aktivieren der A20-Leitung und die Initialisierung des mathematischen Koprozessors aus
- Die A20-Leitung ist ein historischer Mechanismus zur Behebung des 1-MB-Adress-Wrapping-Problems
- Danach werden eine minimale GDT und IDT geladen, anschließend wird das PE-Bit im Register CR0 gesetzt und ein Far Jump ausgeführt
- Damit erfolgt der Eintritt in den Protected Mode, und Segment- sowie Stack-Pointer werden an das neue Adressschema angepasst
Kontrollregister
- CR0: aktiviert den Protected Mode
- CR3: speichert die oberste Adresse der Seitentabellen
- CR4: aktiviert Erweiterungen wie PAE
Vorbereitung auf den Long Mode
- Für den Wechsel in den 64-Bit-Modus sind zwei Bedingungen erforderlich
- Paging muss aktiviert sein: Es übernimmt die Zuordnung zwischen virtuellen und physischen Adressen
- Das LME-Bit (Long Mode Enable) im EFER-Register muss gesetzt werden
- Seitentabellen mappen Seiten in 4-KB-Einheiten; während des frühen Bootvorgangs wird dies zunächst einfach über eine Identity Map in 2-MB-Einheiten aufgebaut
Aktivierung von Paging
- Die Funktion PAE wird in CR4 aktiviert, und es wird eine minimale Seitentabelle erstellt, die den niedrigen Adressbereich in 2-MB-Einheiten abdeckt
- Die Adresse der obersten Tabelle wird in CR3 geschrieben, danach wird Paging aktiviert
- Anschließend wird das LME-Bit in EFER gesetzt und in 64-Bit-Code gesprungen, womit der Eintritt in den Long Mode erfolgt
- Mit auf 64 Bit erweiterten Adressen und Registern ist die Ausführung des Kernels vorbereitet
Teil 3 — Kernel-Dekomprimierung, Adressanpassung und Selbstverschiebung
- Die CPU befindet sich nun im 64-Bit-Modus, und im Speicher liegt ein komprimiertes Kernel-Image
- Ein kleiner 64-Bit-Stub übernimmt das Entpacken des Kernels und die Adressanpassung
Frühes Aufräumen und Einrichten von Sicherheitsmechanismen
- Der Stub berechnet seine tatsächliche Ausführungsposition und verschiebt sich bei Überlappungen per Self-Relocation an eine sichere Stelle
- Er initialisiert seinen BSS-Bereich und lädt eine einfache IDT (einschließlich Handlern für Page Faults und NMI)
- Tritt ein Page Fault auf, wird die fehlende Zuordnung sofort ergänzt, um die Ausführung wiederherzustellen
- Für benötigte Bereiche wie Kernel, Boot-Parameter und Kommandozeilenpuffer wird ein Identity Mapping erzeugt
Kernel-Dekomprimierung
- Die Funktion
extract_kernelwird ausgeführt und entpackt den Kernel- Unterstützt werden verschiedene Kompressionsalgorithmen wie gzip, xz, zstd und lzo
- Nach dem Entpacken wird der ELF-Header gelesen, um Code- und Datenabschnitte an die korrekten Adressen zu kopieren
- Wenn sich die Build-Adresse des Kernels von der tatsächlich geladenen Adresse unterscheidet, wird eine Relokation durchgeführt
- Dabei werden Instruktionen oder Pointer mit eingebetteten Adressen an die tatsächliche Speicherposition angepasst
- Sind alle Vorbereitungen abgeschlossen, erfolgt der Sprung in die Funktion
start_kernel, womit die eigentliche Kernel-Initialisierung beginnt
Selbstverschiebung des Kernels (kASLR)
- kASLR (Kernel Address Space Layout Randomization) randomisiert die physischen und virtuellen Adressen des Kernels und erhöht damit die Schwierigkeit von Angriffen
- Beim Booten werden zwei Basiswerte zufällig gewählt
- Physische Basis: die RAM-Adresse, an der sich der Kernel tatsächlich befindet
- Virtuelle Basis: die Startadresse des virtuellen Adressraums, den der Kernel verwendet
- Beim Booten werden zwei Basiswerte zufällig gewählt
- Ablauf der Auswahl
- Es wird eine Liste der zu schützenden Bereiche erstellt, darunter Bootloader, initrd und Kommandozeilenpuffer
- Die Speicherkarte der Firmware wird nach ausreichend großen freien Bereichen durchsucht
- Mithilfe von Entropie, etwa aus Hardware-Zufallsinstruktionen, wird ein zufälliger Slot ausgewählt
- Falls kein geeigneter Bereich gefunden wird, wird auf die Standardadresse zurückgefallen; mit der Option
nokaslrwird die Randomisierung deaktiviert
Begriffe im Überblick
- Hexadecimal (Hexadezimal): mit dem Präfix
0xgekennzeichnet; praktisch für Hardware-Bitstrukturen und Alignment - Register: temporärer Speicher innerhalb der CPU (CS, DS, SS, IP, SP usw.)
- Segment/Offset: Adressberechnung im Real Mode
(segment * 16 + offset) - BIOS/UEFI: Firmware für Systeminitialisierung und das Laden des Boot-Programms
- Bootloader (GRUB): lädt den Kernel und übergibt Systeminformationen
- Stack/BSS: temporärer Funktionsspeicher bzw. Bereich globaler Variablen, die mit 0 initialisiert sind
- Interrupt/NMI: Mechanismus zur Verarbeitung von Hardware- und Software-Ereignissen
- GDT/IDT: Tabellen zur Definition von Segmenten und Interrupts
- A20-Leitung: Schalter zur Vermeidung des 1-MB-Adress-Wrappings
- Protected Mode/Long Mode: 32-Bit- und 64-Bit-Ausführungsmodi
- Paging/Seitentabellen: Zuordnung zwischen virtuellen und physischen Adressen
Noch keine Kommentare.