Maschinensprache i8086/ Bootloader
Theorie:
Einleitung – Maschinensprache –
Assembler – Zahlensysteme –
RAM-Adressen – BWS – Debug –
CPU-Register – Einfache Befehle – Stringbefehle – Interrupts –
I/O-Ports
Versuch:
BWS1 – BWS2 –
Hallo Welt – Bootsektor – MBR
Nützlich:
Befehlsliste – PAUSE – Filter
Analyse:
Bootloader
Übersicht über den Ablauf des Bootvorgangs
BearbeitenWenn 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.