Autor Thema: Gelöst: Problem mit GDT - Kernel stürtzt ab  (Gelesen 4423 mal)

boom1

  • Beiträge: 6
    • Profil anzeigen
Gespeichert
« am: 21. July 2008, 01:19 »
Hi,

seit letzten Mittwoch hab auch ich vor ein kleines OS zu proggen.
Ich hab mich durch die Tutorials von LowLevel (btw. nette Seite!) durchgewühlt und bin jetzt bei der Global Deskriptor Table hängen geblieben.

Undzwar habe ich folgenden Code:

(alternativer Link)
global loader ; loader für Linker sichtbar machen
extern main   ; main-Funktion des C-Kernels
 
FLAGS    equ 0
MAGIC    equ 0x1BADB002       ; Magicnumber - Erkennungsmerkmal für Grub
CHECKSUM equ -(MAGIC + FLAGS) ; Checksum
 
section .text
align 4
align 4
MultiBootHeader:
dd MAGIC       ; Magic number
dd FLAGS       ; Flags
dd CHECKSUM    ; Checksum
 
loader:
mov esp,0x200000 ; Stack an die 2MB-Grenze platzieren
push eax        ; Multiboot Magicnumber auf den Stack legen
push ebx        ; Adresse der Multiboot-Structure auf den Stack legen
cli

call load_gdt

  call main        ; main-Funktion des C-Kernels aufrufen
 
  cli ; falls der Kernel bis hier her kommt, CPU anhalten
  hlt

load_gdt:
lgdt [gdtable] ; GDT laden
mov eax, cs ; Daten-, Stack und Extrasegment mit Datensegmentdesktriptor laden
shl eax, 4
add eax, gdt
mov ds, ax
mov ss, ax
mov es, ax
mov eax, 0 ; FS und GS mit Null-Deskriptor laden
mov fs, ax
mov gs, ax

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;; == GDT == ;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

gdtable: ; Deskriptortabelle
dw gdt_end-gdt-1 ; Limit
dd gdt ; Basis
gdt:
Null_Desc:
dd 0 ; Nulldeskriptor, bestehend aus Nullen
dd 0 ; Das gleiche. Insgesamt 8 Byte

Code_Desc: ; Code Deskriptor, erstreckt sich über den ganzen Speicher
dw 0xFFFF ; Segmentgrösse: 0xFFFFF (zusammen mit dem zweitletzten Byte)
dw 0 ; Segmentbasisadresse
db 0
db 0x9A
db 0xCF
db 0

Data_Desc: ; Daten Deskriptor, entspricht in etwa dem Code Deskriptor
dw 0xFFFF
dw 0
db 0
db 0x92
db 0xCF
db 0
gdt_end: ; GDT End


Der Kernel (naja ist mir etwas peinlich das schon Kernel zu nennen ;-)) wird von GRUB geladen und ruft diese load_gdt-funktion auf, die eine Standard-GDT laden soll. (Danach ruft er den C-Kernel auf, der in einer anderen Datei liegt, für mein Problem aber unwichtig ist).

Wenn ich den Kernel allerdings wie gewohnt in ein Image schreibe und mit qemu öffne, stürtzt er mir ab und gibt mir einen Triple Fault (was üblich sein soll, wenn was an der GDT faul ist). Ich bin mir ziemlich sicher, dass es am GDT-Teil liegt, da der Kernel ohne die GDT einwandfrei lief.

Die Ausgabe von qemu ist:
EAX=00100316 EBX=0002d8e4 ECX=00008c79 EDX=00000001
ESI=00054527 EDI=00054528 EBP=00067eac ESP=001ffff4
EIP=00100283 EFL=00000002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00cf9300
CS =0008 00000000 ffffffff 00cf9a00
SS =0010 00000000 ffffffff 00cf9300
DS =0316 f053f000 0000ff53 f000ff53
FS =0010 00000000 ffffffff 00cf9300
GS =0010 00000000 ffffffff 00cf9300
LDT=0000 00000000 0000ffff 00008000
TR =0000 00000000 0000ffff 00008000
GDT=     00100296 00000017
IDT=     00000000 000003ff
CR0=60000011 CR2=00000000 CR3=00000000 CR4=00000000
CCS=00100296 CCD=00100316 CCO=ADDL   
FCW=037f FSW=0000 [ST=0] FTW=00 MXCSR=00001f80
FPR0=0000000000000000 0000 FPR1=0000000000000000 0000
FPR2=0000000000000000 0000 FPR3=0000000000000000 0000
FPR4=0000000000000000 0000 FPR5=0000000000000000 0000
FPR6=0000000000000000 0000 FPR7=0000000000000000 0000
XMM00=00000000000000000000000000000000 XMM01=00000000000000000000000000000000
XMM02=00000000000000000000000000000000 XMM03=00000000000000000000000000000000
XMM04=00000000000000000000000000000000 XMM05=00000000000000000000000000000000
XMM06=00000000000000000000000000000000 XMM07=00000000000000000000000000000000
Aborted

Ich benutzte Ubuntu 8.04, den gcc, nasm, und als linker ld.

Könnt ihr mir weiterhelfen?

Danke im Voraus  :wink:
« Letzte Änderung: 21. July 2008, 23:39 von boom1 »

jgraef

  • Beiträge: 67
    • Profil anzeigen
Gespeichert
« Antwort #1 am: 21. July 2008, 09:03 »
Hi,

Was soll das hier machen?
mov  eax, cs   ; Daten-, Stack und Extrasegment mit Datensegmentdesktriptor laden
shl eax, 4
add eax, gdt
mov ds, ax
mov ss, ax
mov es, ax
mov eax, 0 ; FS und GS mit Null-Deskriptor laden
mov fs, ax
mov gs, ax

Du musst einfach DS, ES,FS, GS, SS mit 0x10 und CS mit 0x8 laden. Wobei CS schwieriger ist, da du dafür einen Jump durchfürhen musst. So in etwa
mov ax,0x10
mov ds,ax
mov es,ax
mov fs,ax
mov gs,ax
mov ss,ax
jmp 0x8:.newcs
.newcs:
ret

Außerdem fehlt das ret in load_gdt und er versucht deine GDT auszuführen.

BTW: GDT (IDT usw auch) kannst du auch von deinem C-Teil aus laden (was  einfacher sein könnte). Grub lädt ja beim Start auch eine GDT, die du für den Anfang benutzen kannst. Man sollte trotzdem nachher eine eigene laden, da nicht feststeht welchen Index Code- und Daten-Segment haben.

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #2 am: 21. July 2008, 10:00 »
Zum Fehler hat janosch ja schon alles gesagt. Für die Zukunft wäre es sicher hilfreich, wenn du lernst, die qemu-Ausgaben zu lesen. Interessant sind vor allem EIP=00100283, das dir sagt, welcher Befehl in deinem Code zum Absturz führt (mit objdump -d kernel disassemblieren; bei dir sollte das das mov ds, ax sein), und in deinem Fall DS =0316 f053f000 0000ff53 f000ff53, was irgendwie anders als die anderen Segmentregister und irgendwie kaputt aussieht. ;) Das 0316 ist der Wert des Registers (vergleiche mit EAX, wo es herkommt), dahinter kommen Basis, Limit und irgendwas. Flags und so. Das ist offensichtlich nicht, was du wolltest.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

boom1

  • Beiträge: 6
    • Profil anzeigen
Gespeichert
« Antwort #3 am: 21. July 2008, 13:26 »
Hi,

erstmal vielen dank für die Hilfe, mein Kernel funktioniert jetzt. ;-)
Ich hab allerdings noch ein paar Fragen:

Zitat
Was soll das hier machen?
Code:
mov  eax, cs   ; Daten-, Stack und Extrasegment mit Datensegmentdesktriptor laden
shl eax, 4
add eax, gdt
mov ds, ax
mov ss, ax
mov es, ax
mov eax, 0 ; FS und GS mit Null-Deskriptor laden
mov fs, ax
mov gs, ax

Du musst einfach DS, ES,FS, GS, SS mit 0x10 und CS mit 0x8 laden.
ich habe eigentlich versucht die DS, ES, FS, GS, SS Register mit der Adresse der GDT zu laden, da ich das so verstanden habe und es es auch so in einem Code von jemand anderem gesehen habe.
Wieso muss ich genau den Wert 0x10 laden? Führt das nur dazu, dass die Register neu geladen werden?

Ich hab mir auch überlegt ob ich die ganzen Tabellen im C-Kernel laden soll...
Aber ich denke, dass ich das noch im Assembler Kernel mache.

Mit der Ausgabe von qemu kann ich nicht so viel anfangen...
Z.b. der DS Register:
DS =0316 f053f000 0000ff53 f000ff53
Der DS-Register ist doch 16Bit breit, wofür stehen dann die hinteren drei "Pakete"?


blitzmaster

  • Beiträge: 77
    • Profil anzeigen
Gespeichert
« Antwort #4 am: 21. July 2008, 14:24 »
[...] und in deinem Fall DS =0316 f053f000 0000ff53 f000ff53, was irgendwie anders als die anderen Segmentregister und irgendwie kaputt aussieht. ;) Das 0316 ist der Wert des Registers (vergleiche mit EAX, wo es herkommt), dahinter kommen Basis, Limit und irgendwas. Flags und so. [...]
Nein, das 0x10 ist dein datendeskriptor in der GDT.
Und ich würde dir empfehlen die Tabellen wirklich erst im C-Kernel zu laden, da du dann leichter was dran ändern kannst. (also ich weiß ja net wie dein Design aussieht, aber mein Design sieht es vor, dass ich später noch weitere Segmente in die GDT einbaue..., das geht mit C dann einfach leichter)
A / OS PM - THE operating system of the future

bluecode

  • Beiträge: 1 391
    • Profil anzeigen
    • lightOS
Gespeichert
« Antwort #5 am: 21. July 2008, 15:01 »
Wieso muss ich genau den Wert 0x10 laden? Führt das nur dazu, dass die Register neu geladen werden?
Jeder Wert würde dazu führen, dass die Register neu geladen werden, nur manche Werte sind eben sinnvoll, andere nicht. Wie du schon bemerkt hast braucht man im PMode eine GDT (Global Descriptor Table). Diese enthält Deskriptoren (8Byte jeweils), welche ein (Protected-Mode-)Segment (Vorsicht: Das ist _nicht_ das gleiche wie ein Realmodesegment) beschreiben. Die GDT ist dann ein Array aus solchen Deskriptoren, wobei logischerweise jeder Deskriptor einen Index in dieses Array hat.
Dann gibt es noch Selektoren (2Byte), welche aus einem Index, zwei Bits RPL (Requested Priviledge Level) und ein Bit für die Tabellenauswahl (falls 1 ist es die LDT, falls 0 die GDT, wobei sogut wie keiner mehr eine LDT verwendet).
In ein Segmentregister kommt nun einer dieser Selektoren, welcher wie oben auf einen der Deskriptoren "zeigt". Die Daten innerhalb des Deskriptors werden dann zur Adressberechnung herangezogen, sobald du irgendwas über dieses Register adressierst.

Zitat
Mit der Ausgabe von qemu kann ich nicht so viel anfangen...
Z.b. der DS Register:
DS =0316 f053f000 0000ff53 f000ff53
Der DS-Register ist doch 16Bit breit, wofür stehen dann die hinteren drei "Pakete"?
DS = <Selektor>
Der Rest ist der Deskriptor in irgendeiner Weise kodiert, hab grad kein qemu zur Hand um es genau rauszufinden und evtl. noch die Basisadresse des Deskriptors explizit. Aus dem Datenmüll (Es gibt ja nicht wirklich einen Deskriptor mit dem Index) lässt sich das auch nicht wirklich rauslesen.
DS sollte halt aussehen wie ein legaler Selektor und der Rest in irgendeiner Weise den Deskriptoren in deiner GDT ähneln.
lightOS
"Überlegen sie mal 'nen Augenblick, dann lösen sich die ganzen Widersprüche auf. Die Wut wird noch größer, aber die intellektuelle Verwirrung lässt nach.", Georg Schramm

boom1

  • Beiträge: 6
    • Profil anzeigen
Gespeichert
« Antwort #6 am: 21. July 2008, 23:38 »
Danke für die Antworten, hat mir sehr weiter geholfen.
Ich hab jetzt, wie empfolen die GDT in C realisiert. Funktioniert und find ich so auch irgendwie besser ;-)

 

Einloggen