Autor Thema: Fehler beim laden von Disk nach Wechsel in Protected Mode  (Gelesen 6769 mal)

JustJim

  • Beiträge: 20
    • Profil anzeigen
Gespeichert
 :|Hey hey! Ich brauch ein wenig Hilfe.
Und zwar hab ich einen Boot-Sektor geschrieben, der zunächst einen Text ausgibt, dann den Kernel lädt und dies ebenso mit einer MSG quittiert und dann in den 32-Bit-Protected übergeht und zum Kernel springt.
Kompiliert habe ich die asm mit nasm und die .c's mit gcc und ld ohne 32 flag (ich sitze an nem 64 bit linux). War natürlich zum scheitern verurteilt.
Teilweise spuckt er Fehler aus, dass die Sektoren nicht richtig geladen werden können, teilweise landet er in nem Loop bei dem alles immer wieder schnell durchläuft.
Nachdem ich über gcc und ld mit 32 flag compiliert habe, landet er auch im Endlosmodus.

Seltsamerweise klappt es mit 64Bit-Komp des Kerlen.c's wenn ich am Ende des Boot-Sektors folgendes einbinde:
times 1000-($-$$)db 0
times 1000 dw 0
schreibe. Aber auch nur mit genau dem Kernel :( Verändere ich die Kernel.C, dann klappts wieder nicht.
Der letzte schritt sollte bei mir eig sein, ein Label in asm für die Main-Funk im Kernel anzulegen, damit der Bootloader immer zur main springt.
Aber das kann ich im moment vergessen.

Ich hänge mal die asm' und kernel.c an.
Hoffe ich könnt mir ein wenig helfen.
for(i=0; i<=length_of_ife; i++)
{
  learn(All);
}

JustJim

  • Beiträge: 20
    • Profil anzeigen
Gespeichert
« Antwort #1 am: 24. November 2013, 13:11 »
Da ich nur max 4 Dateien laden kann, hier der Rest ^^. Und, achja, ich nutze Qemu zu testen, da Bochs bei mir ständig Die'd, wenn ichs starte.
Um zu teste, verbinde ich nach nasm, gcc und ld die boot_sect.bin und kernel.bin zu einer os-image mit cat
Danke trotzdem im Vorraus!
« Letzte Änderung: 24. November 2013, 13:13 von JustJim »
for(i=0; i<=length_of_ife; i++)
{
  learn(All);
}

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #2 am: 24. November 2013, 15:07 »
Schau dir mal mit einem Hexeditor an, an welchen Offsets welche Bestandteile auf der virtuellen Diskette auftauchen und arbeite im Hauptprogramm lieber mit LBA-Adressen, die du dann in der Ladefunktion in CHS-Adressen umrechnest. Disketten können verschiedene Geometrien haben, Festplatten ebenfalls.

Es sollte möglich sein, eine simple Prüfsumme (z.B. Addition aller Bytes und Abschneiden der Überträge) über den Kernel zu erzeugen und im Kernel selbst abzuspeichern. Dann kann deine Ladefunktion wenigstens grob prüfen, ob der Kernel korrekt geladen wurde. Wie groß ist dein Kernel und wieviele Sektoren lädst du? Wie stellst du sicher, dass du immer den gesamten Kernel lädst?

Du setzt beim Start des Bootsektors die Segmentregister nicht. Die physische Adresse 0x7c00 ist zwar definiert, aber da die RM-Segmente überlappen, gibt es mehrere mögliche seg:off-Adressen. Passe auf, dass du deinen Kernel nicht an die falsche physische Adresse lädst (das ist BIOS-abhängig!).

Dein Kernel hat eine main()-Funktion und die endet. Das darf nicht passieren, denn danach springt der Kernel sonstwo hin. Außerdem solltest du per Linkerscript sicherstellen, dass der Anfang von main() auch tatsächlich mit dem ersten Byte des Kernels übereinstimmt - denn das ist nicht garantiert.

Und als Nachtrag empfehle ich dir, deinen Kernel als Multiboot-Kernel zu entwickeln und ihn von einem Multiboot-kompatiblem Bootloader starten zu lassen. Das vereinfacht vieles; OS-Entwicklung ist schwer genug, da muss man sich nicht unbedingt gleich am Anfang mit historischen Problemen prügeln. Alternativ solltest du überlegen, ob du deinen Bootsektor wirklich selbst in den PM springen lassen willst, denn:
- dein Kernel kann nicht größer werden als ca. 550 KB
- dein Kernel kann nach dem Start keine Module/Treiber vom Bootlaufwerk laden
- ein richtiges Dateisystem für Kernel (und Module) im Bootsektor zu implementieren, ist schwierig
- die ganze Vorgehensweise setzt ein BIOS voraus, UEFI wird das nicht ewig anbieten

Plag dich nicht mit dem alten Kram rum, wenn du dafür keinen guten Grund hast ("ich will aber" ist meist keiner). Und beim nächsten Mal lade bitte nicht alle Dateien einzeln mit der falschen Endung als Anhänge hoch. :-)

PS: Es kann gut sein, dass der gesamte PM-Kram falsch ist. Den habe ich mir nicht angeschaut.

JustJim

  • Beiträge: 20
    • Profil anzeigen
Gespeichert
« Antwort #3 am: 24. November 2013, 15:21 »
Nun ja, ich will aber, ist kein grund, aber ich will erstmal das grundlegenste über OS-Dev lernen, und dazu gehört ein Bootloader und das Bootstrapping.
Da ich noch relativ neu bei der Sache bin, eröffnen sich bei deiner Antwort natürlich noch weitere Fragen.

Was sind LBA-Adressen und was sind CHS-Adressen?
Beim Start des Bootsektors habe ich doch bp und sp gesetzt?

Bei load_kernel wird der Offset des Kernels über die Konstante KERNEL_OFFSET, die Anzahl der Sektoren über dh (hier 5) und die Disk über dl (hier zunächst von dl in [BOOT_DRIVE], dannach wieder zurück zu dl) übergeben.

Das Problem beim laden hatte ich zuvor auch schon, wo der Boot-Sektor nur aus Lade Daten und gebe sie aus bestand.

Da wurd nach dem padding und der magischen Zahl 55aa (bzw aa55) jeweils 256 mal 0xdada und 256 mal 0xface in den code geschrieben.
Beim laden habe ich immer das Problem: wenn ich mehr Sektoren lade, als ich vorher beschriftet habe, dann kriege ich einen Fehler.
Laut dem Buch, welches ich dafür benutze sollte ich eig 15 Sektoren laden. Soviele wurden aber mit Boot_Sektor und Kernel garnicht beschrieben.

Wie gesagt, Ich wollte als nächstes die main() über assembler labeln, damit auch garantiert dahingesprungen wird.

Ich wollte nach dem Fiasko und der Frustration auch Grub verwenden. Nur da hab ich keinen Einstieg, da ich jetzt in dem Thema "voll drin bin" und weil ich absolut keine Ahnung habe, wie ich Grub in Qemu zum laufen kriege.

Zum Punkt: Bootsektor selber in den PM springen lassen:
Kann ich denn auch den Kernel nach dem laden in den PM-Mode springen lassen? Oder wie meinst du das?

Ich kann auch nicht sagen ob der PM-Kram 100% richtig ist. Hab es über das Buch gemacht (die stellen dabei Aufgaben und nach den Aufgaben entwickelt man es praktisch) hab aber auch schon das komplett nochmal abgeglichen. Hab das Flat-Mode für die GDT verwendet (zwei überlappende, ein Data-Seg ein Code-Seg).
for(i=0; i<=length_of_ife; i++)
{
  learn(All);
}

JustJim

  • Beiträge: 20
    • Profil anzeigen
Gespeichert
« Antwort #4 am: 24. November 2013, 17:27 »
Okay das klingt jetzt vll seeeehr seltsam. Ich habe Bochs unter Ubuntu durch Anleitung zum laufen gekriegt (install von bochs-x, install von bochs-sdl, veränderung der bochsrk) und jetzt läuft mein boot vollständig!
Woran kann das liegen? Ist da irgendetwas was Bochs richtiger macht?
for(i=0; i<=length_of_ife; i++)
{
  learn(All);
}

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #5 am: 24. November 2013, 17:38 »
Nun ja, ich will aber, ist kein grund, aber ich will erstmal das grundlegenste über OS-Dev lernen, und dazu gehört ein Bootloader und das Bootstrapping.
Eben nicht. Du lernst viel über die zugrundeliegende Hardwareplattform, aber nur sehr wenig über Betriebssystementwicklung. Die PC-Plattform gibt es seit 1984 (IBM PC/AT) - und die stinkt inzwischen an einigen Stellen (und ist zudem noch zu ihrem Vorgänger kompatibel).

Es gibt da einiges zu beachten, was eigentlich total irrelevant wäre, wenn da nicht die Geschichte und so. Das fängt schon mit einer zuverlässigen Methode an, den RAM anzusprechen (Stichwort A20) oder überhaupt rauszufinden, wieviel RAM überhaupt verbaut ist (dazu brauchst du BIOS-Interrupts - und die gehen im PM nicht).

Ein Bootloader kann dir das abnehmen und gibt dir gleichzeitig sehr viel Flexibilität, damit du dich auf das konzentrieren kannst, was du eigentlich willst: Ein Betriebssystem entwickeln. Darum meinte ich, dass "ich will aber" kein guter Grund ist. Nur sehr, sehr viel sinnlose Arbeit und viele Kompatiblitätsprobleme. :-)

Was sind LBA-Adressen und was sind CHS-Adressen?
LBA: Logical Block Addressing. Die Sektoren sind durchnummeriert. Festplatten ab 2 GB können das zuverlässig.
CHS: Cylinder/Head/Sector. Jeder Sektor hat Zylinder-, Kopf- und Sektornummer. Bei Diskettenlaufwerken kommst du da nicht drum herum, ansonsten will man sich damit nicht mehr ohne Grund herumärgern (es gibt für Festplatten diverse Translation-Mechanismen, jeweils mit verschiedenen Grenzen und Fehlermöglichkeiten).

Beim Start des Bootsektors habe ich doch bp und sp gesetzt?
Das sind so Dinge, die man nicht will. Adressen im Real Mode bestehen aus Segment:Offset, für den Stack SS:SP. Es geht konkret um CS, DS und SS.

Laut dem Buch, welches ich dafür benutze sollte ich eig 15 Sektoren laden.
Da liegt das Problem. Leg das Buch weg, mache einen Zeitsprung über 20 Jahre und betritt danach die wunderbare Welt der Gegenwart. Dann gehst du in das Wiki zu dem OS-Dev für Einsteiger-Tutorial und spielst das durch. Dann geht es weiter.

Wie gesagt, Ich wollte als nächstes die main() über assembler labeln, damit auch garantiert dahingesprungen wird.
Das wird so nicht ohne weiteres funktionieren. Dein Kernel ist eine Datei, die du in den Speicher lädst, aber du musst auch wissen, an welche Stelle (relativ zum Dateianfang) du springen musst, damit es losgeht. Sowas steht normalerweise in einem Header, aber du kannst den Linker auch überreden, eine flat binary mit main() ganz am Anfang zu erzeugen. Musst du halt nur machen.

Ich wollte nach dem Fiasko und der Frustration auch Grub verwenden. Nur da hab ich keinen Einstieg, da ich jetzt in dem Thema "voll drin bin" und weil ich absolut keine Ahnung habe, wie ich Grub in Qemu zum laufen kriege.
Brauchst du für die ersten Schritte nicht, weil Qemu Linux- und Multibootkernel selbst laden und starten kann ("qemu -kernel mein_multiboot_kernel.elf"). Ich persönlich mag Grub nicht und nutze lieber Syslinux.

Kann ich denn auch den Kernel nach dem laden in den PM-Mode springen lassen? Oder wie meinst du das?
Es gibt sogar Kernel, die nie in den PM springen (DOS zum Beispiel). Üblicherweise lädt der Bootsektor nur einen 2nd stage, welcher dann das Dateisystem des Bootlaufwerks lesen kann und damit den Kernel sowie eventuell nötige Treiber in den RAM lädt und danach erst den Kernel startet. Ein guter 2nd stage hat die Komplexität eines kleinen Betriebssystems.

Ist da irgendetwas was Bochs richtiger macht?
Nicht richtiger, sondern anders. Du willst vermutlich garnicht wissen, was für Seltsamkeiten verschiedene BIOS-Hersteller einbauen.

PS: Doppelpostings sind böse. Du kannst alte Beiträge auch bearbeiten.

PPS: Übrigens herzlich Willkommen hier im Forum! :-D

Gruß,
Svenska

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #6 am: 24. November 2013, 17:41 »
Beim laden habe ich immer das Problem: wenn ich mehr Sektoren lade, als ich vorher beschriftet habe, dann kriege ich einen Fehler.

Das solltest du nicht tun. Dann macht es auch Sinn, wenn es in Qemu nicht funktioniert, aber in Bochs schon. Wenn du das Diskettenimage auf die richtige Größe bringst, solltest du damit keine Probleme haben.

Ich wollte nach dem Fiasko und der Frustration auch Grub verwenden. Nur da hab ich keinen Einstieg, da ich jetzt in dem Thema "voll drin bin" und weil ich absolut keine Ahnung habe, wie ich Grub in Qemu zum laufen kriege.
Du kannst ja mal unsere Tutorialreihe durchspielen. In Teil 4 wird dann auch erklärt, wie man einen Multibootkernel erstellt und ihn in QEMU ohne GRUB lädt.

Zum Punkt: Bootsektor selber in den PM springen lassen:
Kann ich denn auch den Kernel nach dem laden in den PM-Mode springen lassen? Oder wie meinst du das?
Das kann man tun. Macht die Sache aber in der Regel nicht weniger kompliziert.
Dieser Text wird unter jedem Beitrag angezeigt.

JustJim

  • Beiträge: 20
    • Profil anzeigen
Gespeichert
« Antwort #7 am: 24. November 2013, 18:14 »
Oh vielen Dank für dein Willkommen haha. Naja laut Hex-Editor lädt der Boot-Sect den Kernel tatsächlich direkt nach die aa55.
Gut ja das Problem mit den Sektoren laden ist klar, wenn keine mehr da sind, kann auch nichts geladen werden.
Fands halt nur seltsam, dass es laut Buch klappen sollte.
Kann mir jetzt aber auch denken: Wenn das Programm von einem realen Datenträger käme, wäre auch darüberhinaus 0-Werte, die man laden kann. Eine Datei hört einfach auf :D.
Naja zumindest läuft es jetzt, und da ich jetzt weiss wie es läuft, warum es läuft und kann mich jetzt den schönen Dingen widmen :).
Trotzdem, vielen Dank!
Achja. Ich hatte vorher nochmal alles überprüft gehabt (vor dem Wechsel zu Bochs).
Er sprang tatsächlich in den PM und gab die Nachricht "Started in 32-Bit Protected Mode" aus.
Sobald ich den Befehl "call KERNEL_OFFSET" (was hier 0x1000 ist, so wie es über gcc und ld auch kompiliert und geladen wurde) wieder eingebunden habe, spielte alles verrückt
for(i=0; i<=length_of_ife; i++)
{
  learn(All);
}

 

Einloggen