Autor Thema: Endlosschleife nach Initialisieren der GDT  (Gelesen 3297 mal)

ChristianF

  • Beiträge: 296
    • Profil anzeigen
    • DeutschOS - Betriebssystem Projekt
Gespeichert
« am: 09. September 2009, 09:05 »
Guten Morgen,
 
und noch ein neuer Thread  :-D
Ich bin momentan dabei, alles rauszuschmeißen und neuzuschreiben, was aus Tutorials übernommen wurde, bzw. den kompletten Kernel neuzuschreiben.
Leider leider hänge ich schon bei der GDT :cry:
 
Und zwar initialisiere ich diese und nach dem Sprung, um die GDT neuzuladen, hängt Bochs in einer endlosschleife. Hier erst einmal die Ausgaben des Debuggers, die wichtig sein sollten:
(0) [0x00100129] 0008:0000000000100129 (unk. ctxt): lgdt ss:[esp+0x1e]        ; 0f0154241e
(0) [0x0010012e] 0008:000000000010012e (unk. ctxt): mov ax, 0x0010            ; 66b81000
(0) [0x00100132] 0008:0000000000100132 (unk. ctxt): mov ds, ax                ; 8ed8
(0) [0x00100134] 0008:0000000000100134 (unk. ctxt): mov es, ax                ; 8ec0
(0) [0x00100136] 0008:0000000000100136 (unk. ctxt): mov fs, ax                ; 8ee0
(0) [0x00100138] 0008:0000000000100138 (unk. ctxt): mov gs, ax                ; 8ee8
(0) [0x0010013a] 0008:000000000010013a (unk. ctxt): mov ss, ax                ; 8ed0
(0) [0x0010013c] 0008:000000000010013c (unk. ctxt): jmp far 0008:00100143     ; ea430110000800
(0) [0x00110142] 0008:0000000000100143 (unk. ctxt): add byte ptr ds:[eax], al ; 0000
(0) [0x00110144] 0008:0000000000100145 (unk. ctxt): add byte ptr ds:[eax], al ; 0000
(0) [0x00110146] 0008:0000000000100147 (unk. ctxt): add byte ptr ds:[eax], al ; 0000
(0) [0x00110148] 0008:0000000000100149 (unk. ctxt): add byte ptr ds:[eax], al ; 0000
(0) [0x0011014a] 0008:000000000010014b (unk. ctxt): add byte ptr ds:[eax], al ; 0000
(0) [0x0011014c] 0008:000000000010014d (unk. ctxt): add byte ptr ds:[eax], al ; 0000
....

Nun zu meinem geschriebenen unrat. Meine Struktur für die Einträge habe ich so angelegt, wie es in den Intel Dokumenten steht. Damit GCC nix optimiert, habe ich das auch noch gepackt:
struct segment_descriptor
{
unsigned short base_low;
unsigned short limit_low;
unsigned char base_mid;
unsigned char access;
unsigned char granularity;
unsigned char base_high;
} __attribute__((packed));
Meine Funktion zum setzen eines Eintrags ist auch nicht unähnlich der, die im Tutorial genutzt wurde, allerdings selbst geschrieben:
void gdt_set_descriptor(unsigned int num, unsigned int base, unsigned int limit, unsigned char access, unsigned char granularity)
{
// sollte nicht passieren ;)
if(num >= GDT_NUM_MAX_ENTRIES)
return;

// basis adresse
kgdt[num].base_low = (base & 0xFFFF);
kgdt[num].base_mid = ((base >> 16) & 0xFF);
kgdt[num].base_high = ((base >> 24)); // sollte man weglassen können: & 0xFF

// limit
kgdt[num].limit_low = (limit & 0xFFFF);
kgdt[num].granularity = ((limit >> 16) & 0x0F);

// der rest
kgdt[num].access = access;
kgdt[num].granularity |= (granularity & 0xF0);
}

Für das Initialisieren der GDT rufe ich einfach oben die aufgeführte Funktion 5 mal auf (0, Kernelcode, Kerneldata, Usercode, Userdata (später 6 mal, da noch die TSS hinzu kommt)).
Nun habe ich die Informationen für das Register GDTR nur lokal in der Funktion abgelegt, da es ja eigentlich nur einmal gebraucht wird. Und da ich auf zusätzliche Funktionen verzichten wollte, habe ich den Wechsel auch gleich in inline assembler geschrieben.  Das ganze sieht dann wie folgt aus:
void gdt_install(void)
{
// gdtr
struct {
unsigned short limit;
unsigned int base;
}__attribute__((packed)) gp = {
.limit = (sizeof(struct segment_descriptor) * GDT_NUM_MAX_ENTRIES) - 1,
.base = (unsigned int)kgdt
};

// NULL-Segment
gdt_set_descriptor(0, 0, 0, 0, 0);
// kernel code
gdt_set_descriptor(1, 0, 0xFFFFFFFF, 0x9A, 0xCF);
// kernel data
gdt_set_descriptor(2, 0, 0xFFFFFFFF, 0x92, 0xCF);
// user code
gdt_set_descriptor(3, 0, 0xFFFFFFFF, 0xFA, 0xCF);
// user data
gdt_set_descriptor(4, 0, 0xFFFFFFFF, 0xF2, 0xCF);
// task state segment
//gdt_set_descriptor(5, &..., sizeof(...), 0x89, 0x4F);

asm("lgdt %0\n\t"
"mov $0x10, %%ax\n\t"
"mov %%ax, %%ds\n\t"
"mov %%ax, %%es\n\t"
"mov %%ax, %%fs\n\t"
"mov %%ax, %%gs\n\t"
"mov %%ax, %%ss\n\t"
"ljmp $0x08,$1f\n\t"
"1:\n\t"
: : "m"(gp) : "ax");
}

 
Da der Code nicht funktioniert, muss ich irgendwo einen Fehler haben, doch wo? Ich sehe hier keine Unstimmigkeiten. Wenn ich nun aus ax eax mache, passiert auch nichts anderes... :(
Was kann ich hier machen?

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #1 am: 09. September 2009, 11:12 »
Hi,

das ist keine Endlosschleife, sondern du siehst an den 0008:0000000000100143, dass er an die falsche Stelle gesprungen zu sein scheint. (Wobei der angezeigte Wert cs:eip ist, aber nicht die physische Adresse.)

Allerdings scheint das ganze doch irgendwie zu funktionieren. (d.h. kein Triple Fault.) Eine Ursache dafür kann sein, dass die Basis deines Segments falsch ist. (Eigentlich müsste dir sowas dein Debugger sagen können.)

Und in der Tat ist der Aufbau deines Deskriptors falsch. base_low und limit_low sind vertauscht. Das heißt du hast in Wirklichkeit Basis 0x0000ffff und Limit 0xf0000 (=3,75GB).
« Letzte Änderung: 09. September 2009, 11:14 von PorkChicken »
Dieser Text wird unter jedem Beitrag angezeigt.

ChristianF

  • Beiträge: 296
    • Profil anzeigen
    • DeutschOS - Betriebssystem Projekt
Gespeichert
« Antwort #2 am: 09. September 2009, 11:32 »
Zu blöd um die Intel docs zu lesen ... :roll:
 
Das war das Problem. Nun scheint es zu funktionieren, ich muss allerdings noch etwas testen und debuggen, ob das Konstrukt so wirklich korrekt arbeitet.

ChristianF

  • Beiträge: 296
    • Profil anzeigen
    • DeutschOS - Betriebssystem Projekt
Gespeichert
« Antwort #3 am: 10. September 2009, 09:14 »
Ich habe um das zu testen, mir von GRUB die Segment Register ausgeben lassen (per "sreg"). Herausgekommen ist dabei folgendes:
cs:s=0x0008, dh=0x00cf9a00, dl=0x0000ffff, valid=1
ds:s=0x0010, dh=0x00cf9200, dl=0x0000ffff, valid=1
ss:s=0x0010, dh=0x00cf9300, dl=0x0000ffff, valid=7
es:s=0x0010, dh=0x00cf9300, dl=0x0000ffff, valid=1
fs:s=0x0010, dh=0x00cf9300, dl=0x0000ffff, valid=1
gs:s=0x0010, dh=0x00cf9300, dl=0x0000ffff, valid=1
ldtr:s=0x0000, dh=0x00008200, dl=0x0000ffff, valid=1
tr:s=0x0000, dh=0x00008b00, dl=0x0000ffff, valid=1
gdtr:base=0x00104180, limit=0x2f
idtr:base=0x00000000, limit=0xffff

dh und dl sind vermutlich das dx register, doch was möchte Bochs damit sagen? Die Selektoren (Code- und Datensegment) scheinen zu stimmen, doch was sagt das dl, das dh und das valid dahinter?
 
Kann es sein, dass da was schief läuft wegen den verkorksten Werten in dh und dl?
« Letzte Änderung: 10. September 2009, 09:31 von ChristianF »

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #4 am: 10. September 2009, 11:02 »
Diese dh und dl haben nichts mit dx zu tun. Das sind die verborgenen Teile des jeweilgen Segmentregisters. Im Prinzip sind das genau die Werte, die in der GDT im Eintrag des geladenen Selektors stehen. Wenn du genau hinschaust erkennst du auch die Felder für Base, Limit, Access und Flags:

cs:s=0x0008, dh=0x00cf9a00, dl=0x0000ffff, valid=1
                  ^^----^^-------^^^^------------- base
                     ^---------------^^^^----- limit
                      ^^------------------ access
                    ^----------------- flags

Die Werte da sehen in Ordnung aus.
« Letzte Änderung: 10. September 2009, 11:05 von PorkChicken »
Dieser Text wird unter jedem Beitrag angezeigt.

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #5 am: 10. September 2009, 11:09 »
info registers in einem aktuellen qemu gibt das übrigens sogar in lesbar aus (die Felder sind der Selektor, base, limit, flags und flags in lesbar). Beispiel aus einem laufenden tyndur:

ES =0023 00000000 ffffffff 00cff300 DPL=3 DS   [-WA]
CS =001b 00000000 ffffffff 00cffa00 DPL=3 CS32 [-R-]
SS =0023 00000000 ffffffff 00cff300 DPL=3 DS   [-WA]
DS =0023 00000000 ffffffff 00cff300 DPL=3 DS   [-WA]
FS =0023 00000000 ffffffff 00cff300 DPL=3 DS   [-WA]
GS =0023 00000000 ffffffff 00cff300 DPL=3 DS   [-WA]
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

VooDoo

  • Beiträge: 4
    • Profil anzeigen
Gespeichert
« Antwort #6 am: 10. September 2009, 23:35 »
kann man den lgdt befehl mit dem inline assembler aufrufen?

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #7 am: 10. September 2009, 23:53 »
Ja, oben in ChristianFs Post siehst du ein Beispiel ;)
Dieser Text wird unter jedem Beitrag angezeigt.

 

Einloggen