Maschinensprache i8086/ Bootloader

Aus Wikibooks

Theorie:  EinleitungMaschinenspracheAssemblerZahlensystemeRAM-AdressenBWSDebugCPU-RegisterEinfache BefehleStringbefehleInterruptsI/O-Ports
Versuch:  BWS1BWS2Hallo WeltBootsektorMBR
Nützlich: BefehlslistePAUSEFilter
Analyse:  Bootloader


Übersicht über den Ablauf des Bootvorgangs[Bearbeiten]

Wenn das BIOS mit dem Selbsttest fertig ist, liest es die im CMOS_RAM gespeicherten Angaben zur Boot-Reihenfolge (Boot-Sequenz). Aktuelle Mainboards können von Festplatte, DVD-Laufwerk, USB-Speicherstick oder aber ein Netzwerk booten. In der vorgegebenen Reihenfolge wird jeder Massenspeicher überprüft, ob er betriebsbereit ist und ob der erste Sektor des Speichers gelesen werden kann. Wenn nicht, wird der nächste Massenspeicher geprüft.

Der weitere Ablauf hängt davon ab, ob das Boot-Laufwerk eine Partitionstabelle hat oder nicht. Festplatten können eine oder mehrere Partitionen haben. Weil in mehreren Partitionen ein Betriebssystem installiert sein kann, muss eine der Partitionen in der Partitionstabelle als „aktiv“ gekennzeichnet sein.

Auch USB-Speichersticks können in Partitionen unterteilt sein. Seit dem „Creators Update“ 2017 kann sogar der Diskmanager von Windows 10 auf einem USB-Speicherstick mehrere Partitionen anlegen. Windows kennzeichnet den USB-Stick mit dem Festplattensymbol, während USB-Sticks im Superfloppy-Format (ohne Partitionstabelle) mit Wechseldatenträgersymbol markiert werden.

Das BIOS liest also den ersten Sektor vom Boot-Laufwerks ein und speichert dessen Inhalt im Arbeitsspeicher im Adressbereich von 07C00 bis 07DFF. Wenn der gelesene Sektor mit 55 AA endet, also die Speicherplätze ab 07DFE den Code 55 AA enthalten, betrachtet das BIOS den eingelesenen Sektor als korrekt und springt zur Adresse 07C00. Hiermit gibt das BIOS den weiteren Ablauf „aus der Hand“ und überlässt die weitere Steuerung dem soeben geladenen Programm.

Wenn das eingelesene Startprogramm von einem CD/DVD-Laufwerk oder einem nicht partitionierten USB-Stick stammt, nennt man es „Bootsektor“. Es enthält die Befehle (im Binärcode des Prozessors) zum Starten des Betriebssystems.

Wenn das eingelesene Programm von einem Massenspeicher mit Partitionstabelle stammt, ist es komplizierter. Das vom ersten Sektor des Speichers eingelesene Programm nennt man Master Boot Record (MBR). Der MBR enthält am Anfang den Bootloader und am Ende die Partitionstabelle, gefolgt von 55 AA. Erst nach Auswertung der Partitionstabelle steht fest, von welcher Partition anschließend der Bootsektor geladen und gestartet werden muss. Weil das Betriebssystem den Bootsektor ab Adresse 07C00 erwartet, muss dieser Bereich erst freigemacht werden, denn er ist noch durch den MBR belegt. Deshalb wird der Bereich von 07C00 bis 07DFF (512 Byte) nach 00600 bis 07FF verschoben, und nach dem Verschieben wird dann das Programm ab 0061D fortgesetzt.

Im weiteren Programmverlauf wird geprüft, ob eine gültige Partitionstabelle vorhanden ist. Die aktive Partition wird ausgewählt und deren Bootsektor in den vorsorglich freigemachten Bereich von 07C00 bis 07DFF eingelesen. Wenn die letzten eingelesenen Bytes 55 AA sind, führt das MBR-Programm als letzten Befehl einen Sprung nach 07C00 zum Beginn des Bootsektors aus.

Programmcode[Bearbeiten]

; Dieser Sektor wurde vom BIOS nach 07C00 eingelesen und muß jetzt 
; nach 00600 verschoben werden.
  0000 FA           CLI                 ; Alle INTR sperren
  0001 33C0         XOR     AX,AX       ; AX = 0
  0003 8ED0         MOV     SS,AX
  0005 BC007C       MOV     SP,7C00     ; Stack unterhalb von 07C00
  0008 8BF4         MOV     SI,SP       ; ab 07C00 umkopieren
  000A 50           PUSH    AX
  000B 07           POP     ES          ; ES = 0
  000C 50           PUSH    AX
  000D 1F           POP     DS          ; DS = 0
  000E FB           STI                 ; Alle INTR zulassen
  000F FC           CLD                 ; Richtung aufwärts für Stringbefehle
  0010 BF0006       MOV     DI,0600     ; nach 00600
  0013 B90001       MOV     CX,0100     ; 256 Words = 512 Byte
  0016 F2           REPNZ
  0017 A5           MOVSW
  0018 EA1D060000   JMP     0000:061D   ; weiter ab Adresse 061D

Die Partitionstabelle wird durchsucht, ob eine aktive Partition vorhanden ist.

  061D BEBE07       MOV     SI,07BE     ; Beginn der Partitionstabelle
  0620 B304         MOV     BL,04       ; max. Anzahl Partitionen
  0622 803C80       CMP     BYTE PTR [SI],80
  0625 740E         JZ      0635        ; Aktive Partition gefunden
  0627 803C00       CMP     BYTE PTR [SI],00
  062A 751C         JNZ     0648        ; ungültige Partitionstabelle
  062C 83C610       ADD     SI,+10 
  062F FECB         DEC     BL
  0631 75EF         JNZ     0622        ; nächsten Eintrag prüfen

; Keine aktive Partition gefunden, ROM-BASIC wird gestartet
  0633 CD18         INT     18          ; zum ROM-BASIC oder IRET
; Aktive Partition wurde gefunden
  0635 8B14         MOV     DX,[SI]     ; DH = Kopf der aktiven Part.
  0637 8B4C02       MOV     CX,[SI+02]  ; CX = Sektor und Zylinder
  063A 8BEE         MOV     BP,SI
  063C 83C610       ADD     SI,+10
  063F FECB         DEC     BL 
  0641 741A         JZ      065D        ; Alle Einträge wurden geprüft
  0643 803C00       CMP     BYTE PTR [SI],00 
  0646 74F4         JZ      063C

; Fehler! Mehr als eine Partition ist als aktiv gesetzt!
  0648 BE8B06       MOV     SI,068B	; "Ungültige Partitionstabelle"
  064B AC           LODSB               ; Fehlertext ausgeben
  064C 3C00         CMP     AL,00
  064E 740B         JZ      065B        ; Am Textende zur Endlosschleife
  0650 56           PUSH    SI
  0651 BB0700       MOV     BX,0007
  0654 B40E         MOV     AH,0E       ; Zeichen in AL 
  0656 CD10         INT     10          ; auf Bildschirm ausgeben
  0658 5E           POP     SI
  0659 EBF0         JMP     064B

  065B EBFE         JMP     065B        ; Endlosschleife bei Fehler

Der Bootsektor der aktiven Partition wird eingelesen

  065D BF0500       MOV     DI,0005
  0660 BB007C       MOV     BX,7C00
  0663 B80102       MOV     AX,0201
  0666 57           PUSH    DI     
  0667 CD13         INT     13          ; Startsektor einlesen ab 07C00
  0669 5F           POP     DI     
  066A 730C         JNB     0678   
  066C 33C0         XOR     AX,AX  
  066E CD13         INT     13     
  0670 4F           DEC     DI     
  0671 75ED         JNZ     0660   
  0673 BEA706       MOV     SI,06A7     ; "Fehler beim Laden des Betriebs..."
  0676 EBD3         JMP     064B        ; Fehlertext ausgeben und Ende

  0678 BECD06       MOV     SI,06CD     ; "Betriebssystem fehlt"
  067B BFFE7D       MOV     DI,7DFE
  067E 813D55AA     CMP     WORD PTR [DI],AA55 ; 55AA am Sektorende?
  0682 75C7         JNZ     064B        ; kein gültiger Bootsektor
; Bootsektor ist gültig
  0684 8BF5         MOV     SI,BP
  0686 EA007C0000   JMP     0000:7C00   ; Sprung zum eben eingelesenen Bootsektor

Hexadezimales Listing[Bearbeiten]

-d 68b 6af
Nahtlos hinter dem JMP-Befehl beginnt der Text der Fehlermeldungen, dann folgen Nullen. Statt der Nullen können auch zufällige Bytes enthalten sein.

0680                                   55 6E 67 81 6C            Ungül
0690  74 69 67 65 20 50 61 72 74 69 74 69 6F 6E 73 74 tige Partitionst
06A0  61 62 65 6C 6C 65 00 46 65 68 6C 65 72 20 62 65 abelle.Fehler be
06B0  69 6D 20 4C 61 64 65 6E 20 64 65 73 20 42 65 74 im Laden des Bet
06C0  72 69 65 62 73 73 79 73 74 65 6D 73 00 42 65 74 riebssystems.Bet
06D0  72 69 65 62 73 73 79 73 74 65 6D 20 66 65 68 6C riebssystem fehl
06E0  74 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 t...............
06F0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0710  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0710  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0720  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0730  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0740  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0750  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0760  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0770  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0780  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0790  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
07A0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
07B0  00 00 00 00 00 00 00 00 00 00 00 00 00 00

Ab Adresse 7BE beginnt die Partitionstabelle. Die Festplatte in diesem Beispiel hat 1023 Spuren, 18 Köpfe und 63 Sektoren pro Spur. Es ist nur eine Partition vorhanden, welche die gesamte Festplatte einnimmt. Jeder der vier Einträge der Partitionstabelle ist 10h = 16d Bytes lang:

07BE                                            80    Aktive Partition
07BF                                               01 Kopf Part.-Anfang
07C0  01 00                                           mit Sektor 1 und Zylinder 0 beginnt die Partition
07C2        06                                        Partitionstyp 06 = BIGDOS (siehe Tabelle) 
07C3           0F                                     Mit Kopf 0F hex = 15 dez endet die Partition
07C4              FF FE                               mit Sektor 63 und Zylinder 1022 endet die Partition
                                                        1022 = 11 1111 1110, Sekt 63 = 11 1111
                                                        Kodierung 1111 1110 11         11 1111 = FE FF hex
07C6                    3F 00 00 00                   Entfernung des 1. Sektors vom MBR = 0000003F = 63 dez (= Sektoren pro Spur)
07CA                                D1 BB 0F 00       Anzahl Sektoren in der Partition
                                                        = 000FBBD1 hex = 1031121
                                                        = (1023 * 16 * 63) -63

Ab Adresse 7CE beginnt der zweite (leere) Eintrag der Partitionstabelle

07CE                                            00 00               ..
07D0  00 00 00 00 00 00 00 00 00 00 00 00 00 00       ..............

Ab Adresse 7DE beginnt der dritte (leere) Eintrag der Partitionstabelle

07DE                                            00 00               ..
07E0  00 00 00 00 00 00 00 00 00 00 00 00 00 00       ..............

Ab Adresse 7EE beginnt der vierte (leere) Eintrag der Partitionstabelle

07EE                                            00 00               ..
07F0  00 00 00 00 00 00 00 00 00 00 00 00 00 00       ..............

Die beiden letzten Byte enthalten ein Kennzeichen

07FE                                            55 AA               U.