{{Hinweis|Text=Dieser Artikel wird in mehreren Schritten komplett überarbeitet, da der bisherige Inhalt nur das Programmieren eines Bootsektor beinhaltet, jedoch in keinster Weise auf das Booten des eigenen OS eingeht. -- [[Benutzer:ScollPi|ScollPi]] 18:16, 30. Mär. 2010 (CEST)}}
{{Tutorial
|Titel=Eigener Bootloader
|Schwierigkeit=4
|Vorwissen=[[Real Mode]], [[BIOS]]
|Sprache=Assembler
}}
Der Weg zum eigenen Betriebssystem ist lang und steinig. Manch einer versucht als erstes einen eigenen [[Bootloader]] inklusive [[Bootsektor]] zu entwickeln. Klar kann man sich die Arbeit auch sparen, indem man einfach zu [[GRUB]] greift. Dennoch möchte man die Funktionsweise eines Bootloader verstehen können. Dies soll mit diesem Tutorial vermittelt werden.
Dieses Tutorial wird in erster Linie die Vorgehensweise erläutern, die bei vielen [[Dateisystem|Dateisystemen]] identisch sind. Folglich wird hier auch kein kompletter Source-Code eines Bootloader gelistet. Grundkenntnise in [[Assembler]] sind für dieses Tutorial allerdings Voraussetzung, so auch der Umgang mit einem Assembler und einem [[Linker]].
Auch in Assembler sollte man sauber programmieren und nicht scheuen, Strunkturen und Variablen statt Offset-Konstanten zu verwenden, besonders beim [[FAT#Bootsektor | Boot-Parameter-Block]].
==Allgemeines==
Der [[Bootprozess]] wird ja bereits an anderer Stelle beschrieben, jedoch wird dort nicht näher ins Detail gegangen. Dies wird nun an dieser Stelle nun nachgeholt.
Ausgangspunkt ist, dass das [[BIOS]] bereits den ersten Sektor von unserem Datenträger geladen hat und nach einer Überprüfung der Signatur (0x0AA55) die Kontrolle an diesem abgibt. Man beachte, das der erste Sektor bei Festplatten für gewöhnlich der [[Master Boot Record]] ist und auf dem ebenfalls eingegangen wird, bzw. die Vorgehensweise erläutert.
Das erste Problem, auf das man stoßen wird, ist, dass der Binärcode des Bootsektors die Größe eines Sektors (typische 512 Bytes bei Diskette, Festplatten und Flash-Storages) je nach Komplexität des Dateisystem überschreiten könnte, dies aber im Idealfall jedoch nicht sollte. OK, zugegeben, bei der CD mit ihren 2048 Bytes pro Sektor und deren Dateisystem langt dieser. Auch zukünftige Festplatten mit einer physikalischen Sektorgröße von 4096 Bytes mögen dem entgegen wirken. Unser Ziel ist aber keineswegs für jedes Dateisystem einen eigenen Bootloader (ausschließlich dem Bootsektor) zu entwickeln, sondern viel mehr ein universelles Modell auf die Beine zu stellen, bei dem nur der Bootsektor individuell dem Dateisystem vorliegt und der Bootloader (Binärdatei) für alle unterstützten Dateisystemen gleich bleibt.
==Universelles Bootloader-Modell==
Damit wir nicht im Chaos bei der Entwicklung unseres Bootloaders versinken, werden wir diesen unterteilen, nicht nur wegen der Übersicht, sondern weil es zum einen auch partitionierte Datenträger gibt und zum anderem der Bootsektor in seiner Größe begrenzt ist.
* Stage 0: Master-Boot-Record (optional)
* Stage 1: Bootsektor
* Stage 2: Bootloader
Manch einer würde hier die Frage stellen, ob es wirklich sinnvoll ist, auch den MBR zu entwickeln. Wie schon anfangs erwähnt, wollen wir den Bootloader im Detail verstehen können, also machen wir keine halben Sachen. Dennoch, ganz so verkehrt ist die Frage allerdings nicht, denn Disketten haben für gewöhnlich keinen MBR.
===Stage 0: Master-Boot-Record===
Fangen wir mal klein an, oder besser gesagt bei Null, denn der MBR ist wirklich der kleinste Teil des Ganzen. Der Code des MBR macht nichts weiter als in der Partitionstabelle nach einer aktiven Partition zu suchen, dessen Bootsektor zu laden und die Kontrolle an diesen abzugeben.
Doch zuerst das wichtigste, was man über den MBR wissen sollte. Richtig, die Position und Struktur der Partitionstabelle. Aber die können wir hier im Lowlevel-Wiki nachschlagen.
Doch hier erst einmal etwas Starthilfe, ein MBR-Rohling:
:''MBR.asm''
Beachte aber, das man nur aus sogenannten primären Partitionen bootet.
# Was wir als nächstes benötigen, sind die Offsets der FAT, des Hauptverzeichnisses und den Offset an dem die Dateien anfangen.
LBA-STart = Offset, an der die Partition beginnt, also gleich "HiddenSecs".
FAT = LBA-Start + ReservedSectors
ROOT = FAT + NumFats * NumFatSecs16
DATA = ROOT + ((RootEntries - 1) * 32 + BytesPerSector) / BytesPerSector
# Nach dem wir nun den Offset des Hauptverzeichnis haben und wissen, wie groß dieses ist, können wir nun nach dem Bootloader bspw. "bootload.bin" suchen. Sofern sie gefunden wurde, ermitteln wir den Startcluster.
# Nun haben wir den ersten Cluster und können mit dem Laden der Datei anfangen. Dazu müssen wir die Clusternummer in LBA umrechnen. Beachte aber, dass der Datenbereich bei Cluster #2 anfängt.
LBA = DATA + (Cluster - 2) * SecsPerCluster
# Da unsere Datei sicher noch nicht mit einem Cluster komplett geladen wurde, müssen wir in der FAT nach weiteren Clustern zu dieser Datei suchen. Hier unterscheidet sich FAT12 von FAT16 etwas und genau hier weist der Source-Code etwas ab.
# Nach dem auch der letzte Cluster geladen wurde, kann endlich in den Bootloader gesprungen wurden. Damit beginnt also Stage 2.
====FAT32====
:''(Demnächst)''
===Stage 2: Bootloader===
:''(Demnächst)''
----
{{Hinweis|Text=Der folgende Text enthält noch den ursprünglichen Artikel.}}
==Tools==
Das brauchen wir um unser erstes Programm zu realisieren: Einen Editor, einen Assembler, ein paar Konsolentools und eine Testumgebung.
===Der Editor===
''Hauptartikel: [[Editor]]''
Fürs erste reicht jeder beliebige Texteditor. Unter Windows z. B. Notepad. Von Vorteil ist es, wenn der Editor Syntaxhighlighting für Assemblerdateien unterstützt wie z. B. Notepad 2, Ultraedit, Proton ... Für Linux gilt das gleiche. Hier gibt es auch jede Menge brauchbarer Editoren, die unseren Ansprüchen genügen.
===Der Assembler===
Der Assembler ist das Tool, mit dem wir unseren Quelltext in eine dem PC verständliche Sprache übersetzen. Der Assemblerquelltext ist für viele Menschen zwar auch nicht gerade verständlich, der PC kann damit aber auch noch nichts anfangen. Daher übersetzt der Assembler unseren Quelltext in einen von Maschinen ausführbaren Programmcode.
Als Assembler verwenden wir den [[Netwide Assembler]] (kurz NASM). Er ist für das, was wir vorhaben, eine gute Wahl. Außerdem existiert er sowohl für Windows als auch für Linux und steht unter der LGPL. NASM ist ein Sourceforge-Projekt und kann unter http://sourceforge.net/projects/nasm bezogen werden.
===Die Testumgebung===
Zum Testen unseres Images werden wir Bochs verwenden. Bochs ist ein PC–Emulator. Er emuliert einen vollständigen PC inklusive Prozessor, im Gegensatz zu z. B. VM-Ware oder Virtual PC. Diese Programme simulieren auch einen PC, hier wird jedoch das Programm auf dem Hostprozessor ausgeführt, um mehr Geschwindigkeit zu erzielen. Bochs hingegen hat für uns den Vorteil, dass er einen integrierten Debugger besitzt. Er wird uns noch viele nützliche Informationen über unser Programm und dessen Fehler liefern.
Auch Bochs steht unter der LGPL und ist auf Sourceforge.net unter http://bochs.sourceforge.net erhältlich.
==Hello World==
Wie bereits erwähnt wird unser erstes Programm nicht viel anderes machen, als einen Text auszugeben, danach auf eine beliebige Taste zu warten, und dann das System neu zu starten.
Bevor wir anfangen, sind noch ein paar Informationen wichtig. Unser Programm wird im [[Boot Record]] liegen. Daher muss es im ersten Sektor einer Partition oder Diskette liegen. Weiterhin braucht unser Programm eine besondere Kennzeichnung, die Magic Number (0xAA55). Diese muss am Ende des Sektors stehen. Ein Sektor ist übrigens nur 512 Byte groß. Abzüglich der Magic Number bleiben uns noch 510 Byte für unser Programm. Das ist nicht wirklich viel und unter anderem ein Grund, wieso wir das Programm in Assembler schreiben und nicht in einer Hochsprache wie z. B. C. Der nächste wichtige Punkt ist, dass das BIOS unseren Sektor an die absolute Adresse 0x07C00 kopiert und ausführt. Je nach BIOS-Hersteller kann die Zusammensetzung der absoluten Adresse aber unterschiedlich aussehen.
===Assemblerdirektiven===
Nun zu unserem Programm. Das Programm beginnt mit folgenden Zeilen.