Autor Thema: DPF gefolgt von GPF bei der GDT-Initialisierung  (Gelesen 17453 mal)

rizor

  • Beiträge: 521
    • Profil anzeigen
Gespeichert
« am: 18. May 2009, 20:55 »
Nabend zusammen,

mir ist aufgefallen, dass mein GDT nicht richtig war, wodurch das mit dem TSS nicht richtig geklappt hat.
Nun habe ich den GDT ein wenig umgeschrieben, allerdings bekomme ich erst eine DPF und dann einen GPF.
Das Problem tritt auf, wenn ich den GDT update.

Der Code sieht wie folgt aus:
//memset(&GDT , 0 , GDT_SIZE * sizeof(struct gd_table));
gdt_set_gate(1, 0, 0xFFFFF, GDT_SEGMENT | GDT_PRESENT | GDT_CODE , GDT_RING_0);        // Kernel code
gdt_set_gate(2, 0, 0xFFFFF, GDT_SEGMENT | GDT_PRESENT | GDT_DATA , GDT_RING_0);        // Kernel data
gdt_set_gate(3, 0, 0xFFFFF, GDT_SEGMENT | GDT_PRESENT | GDT_CODE , GDT_RING_3);        // User code
gdt_set_gate(4, 0, 0xFFFFF, GDT_SEGMENT | GDT_PRESENT | GDT_DATA , GDT_RING_3);        // User data
puts("set up gates");

gdt_set_gran_bit(5 , (uint32_t)&krn_tss , sizeof(krn_tss) - 1 , GDT_PRESENT | GDT_TSS, GDT_RING_3);
puts("\nset up granularity");

gdt_update();

gdt_update sieht wie folgt aus:
/* Defines the Pointer to a GDT entry */
struct gdt_ptr{
uint16_t limit;
    uint32_t base;
} __attribute__((packed)) gp = {
.limit  = sizeof(struct gd_table)*GDT_SIZE - 1,
.base  = (uint32_t)&GDT,
};

  __asm__("lgdtl %0\n\t"
  "ljmpl $0x08, $1f\n\t"
  "1:\n\t"
  "mov $0x10, %%eax\n\t"
  "mov %%eax, %%ds\n\t"
  "mov %%eax, %%es\n\t"
  "mov %%eax, %%fs\n\t"
  "mov %%eax, %%gs\n\t"
  "mov %%eax, %%ss\n\t" : : "m" (gp) : "eax");

gdt_set_gate und gdt_set_gran_bit sehen wie folgt aus:
void gdt_set_gate(uint8_t num , uint32_t base , uint32_t limit , uint8_t access , uint8_t priv){
if(num < GDT_SIZE){
GDT[num].limit_low = (limit & 0xFFFF);
GDT[num].flags = ((limit >> 16) & 0x0F);
GDT[num].base_low = (base & 0xFFFF);
GDT[num].base_middle = (base >> 16) & 0xFF;
GDT[num].base_high = (base >> 24) & 0xFF;
GDT[num].access = access | ((priv & 3) << 5);
}
else
panic("Tried to set a wrong GDT-Gate.");
}

void gdt_set_gran_bit(uint8_t num , uint32_t base , uint32_t limit , uint8_t access , uint8_t priv){
gdt_set_gate(num , base , limit , access , priv);
GDT[num].flags = ((limit >> 16) & 0x0F) | 0x40;
}

Mein GDT sieht wie folgt aus:
struct gd_table{
uint16_t limit_low;
uint16_t base_low;
uint8_t base_middle;
uint8_t access;
uint8_t flags;
uint8_t base_high;
};

seht ihr den fehler?
Die Definitionen bei gdt_set_gate und gdt_set_gran_bit sind richtig.
Die habe ich kontrolliert.

Danke für eure Hilfe.
« Letzte Änderung: 18. May 2009, 21:08 von rizor »
Programmiertechnik:
Vermeide in Assembler zu programmieren wann immer es geht.

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #1 am: 18. May 2009, 21:33 »
Wo genau tritt der Fehler auf? Beim lgdt? Beim Laden der Segmentregister? Woanders?

Hast du beachtet, dass du die lineare Adresse an lgdt übergeben musst?
Dieser Text wird unter jedem Beitrag angezeigt.

rizor

  • Beiträge: 521
    • Profil anzeigen
Gespeichert
« Antwort #2 am: 18. May 2009, 22:17 »
Stimmt, die letzten beiden Zeilen haben gefehlt.
Das Problem liegt an lgdtl.
Was meinst du mit linearen Adressen?
Die Deskriptoren liegen alle in einem Array, oder was meinst du?
Programmiertechnik:
Vermeide in Assembler zu programmieren wann immer es geht.

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #3 am: 18. May 2009, 22:31 »
Stimmt, die letzten beiden Zeilen haben gefehlt.
Ihr könnt nicht einfach so zwei verschiedene Funktionen vergleichen und zusammenschnipseln. Ich denke (nach scharfem draufgucken^^), dass beide korrekt sind (mit passendem Aufruf/passender Verwendung).


Zitat
Was meinst du mit linearen Adressen?
Die x86-CPU kennt ja mehrere "Stufen" von Adressen/Adressierung:
- physische (durchnummerierung der Speicherzellen auf dem Chip)
- lineare (implementiert durch Segmentierung)
- virtuelle (implementiert durch Paging)

Die lineare Adresse ist gleich der physischen, wenn deine Segmente die Basis 0 haben. Macht man in der Regel so, und es sieht so aus als würdest du das auch tun.

Wenn du Paging aktiviert hast, und dein Kernel Adressraum nicht 1:1 gemappt ist, dann unterscheiden sich aber die virtuelle und die lineare/physische Adresse. Du musst also die Bestimmung der linearen Adresse über die Page Table für Instruktionen wie LGDT irgendwie selbst übernehmen. Da "gp" auf deinem Stack liegt, wäre das also nur relevant, wenn der woanders hingemappt ist.
« Letzte Änderung: 18. May 2009, 22:33 von PorkChicken »
Dieser Text wird unter jedem Beitrag angezeigt.

rizor

  • Beiträge: 521
    • Profil anzeigen
Gespeichert
« Antwort #4 am: 18. May 2009, 22:38 »
Ach das war mit linearer Adressierung gemeint.
Der Speicher ist 1:1 gemappt.
Da kommt es nicht zu Problemen.
Habe vorsichtshalber auch mal paging deaktiviert.
Das Problem tritt trotzdem auf.
Programmiertechnik:
Vermeide in Assembler zu programmieren wann immer es geht.

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #5 am: 18. May 2009, 22:41 »
Hm, also dann schaut noch die Zeile .base  = (uint32_t)&GDT, verdächtig aus. Das & muss da weg. GDT ist bereits die Adresse von dem Array und von dem ersten Eintrag.
Dieser Text wird unter jedem Beitrag angezeigt.

rizor

  • Beiträge: 521
    • Profil anzeigen
Gespeichert
« Antwort #6 am: 18. May 2009, 22:52 »
Stimmt, das hat das Problem allerdings immer noch nicht behoben.
Programmiertechnik:
Vermeide in Assembler zu programmieren wann immer es geht.

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #7 am: 18. May 2009, 23:04 »
Dann geht das raten weiter ;) Ich würde struct gd_table mit einem __attribute__((packed)) versehen.
Dieser Text wird unter jedem Beitrag angezeigt.

rizor

  • Beiträge: 521
    • Profil anzeigen
Gespeichert
« Antwort #8 am: 18. May 2009, 23:12 »
Hat auch nichts gebracht.
Hätte mich auch verwundert, da ich noch keine Optimierungen vom Compiler vornehmen lasse.
Programmiertechnik:
Vermeide in Assembler zu programmieren wann immer es geht.

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #9 am: 19. May 2009, 00:26 »
Nun habe ich den GDT ein wenig umgeschrieben, allerdings bekomme ich erst eine DPF und dann einen GPF.
Was hast du denn geändert? Haben die Selektoren für die Code-/Daten-Segmente vorher funktioniert?
Dieser Text wird unter jedem Beitrag angezeigt.

rizor

  • Beiträge: 521
    • Profil anzeigen
Gespeichert
« Antwort #10 am: 19. May 2009, 12:20 »
Vorher hatte ich nur drei Einträge.
Die wurden so initialisiert:
    gdt_set_gate(0, 0, 0, 0, 0);
    gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF);
    gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92

Da hatte noch alles funktioniert.
Allerdings habe ich dann Probleme mit dem TSS bekommen und ich habe Exception bekommen, die unmöglich waren.

Habe mir mal noch das Logfile von Bochs angeguckt.
Dabei ist mir das aufgefallen.
Vllt hilft das bei der Fehlerfindung.
00032420322i[CPU0 ] CPU is in protected mode (active)
00032420322i[CPU0 ] CS.d_b = 32 bit
00032420322i[CPU0 ] SS.d_b = 32 bit
00032420322i[CPU0 ] EFER   = 0x00000000
00032420322i[CPU0 ] | RAX=0000000080000011  RBX=0000000000000800
00032420322i[CPU0 ] | RCX=0000000000000f20  RDX=00000000300003d5
00032420322i[CPU0 ] | RSP=0000000000109fa8  RBP=0000000000109fa8
00032420322i[CPU0 ] | RSI=0000000000054549  RDI=000000000005454a
00032420322i[CPU0 ] |  R8=0000000000000000   R9=0000000000000000
00032420322i[CPU0 ] | R10=0000000000000000  R11=0000000000000000
00032420322i[CPU0 ] | R12=0000000000000000  R13=0000000000000000
00032420322i[CPU0 ] | R14=0000000000000000  R15=0000000000000000
00032420322i[CPU0 ] | IOPL=0 id vip vif ac vm RF nt of df if tf sf zf AF PF cf
00032420322i[CPU0 ] | SEG selector     base    limit G D
00032420322i[CPU0 ] | SEG sltr(index|ti|rpl)     base    limit G D
00032420322i[CPU0 ] |  CS:0008( 0001| 0|  0) 00000000 000fffff 1 1
00032420322i[CPU0 ] |  DS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00032420322i[CPU0 ] |  SS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00032420322i[CPU0 ] |  ES:0010( 0002| 0|  0) 00000000 000fffff 1 1
00032420322i[CPU0 ] |  FS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00032420322i[CPU0 ] |  GS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00032420322i[CPU0 ] |  MSR_FS_BASE:0000000000000000
00032420322i[CPU0 ] |  MSR_GS_BASE:0000000000000000
00032420322i[CPU0 ] | RIP=000000000010027d (000000000010027d)
00032420322i[CPU0 ] | CR0=0x80000011 CR1=0x0 CR2=0x000000000010a0c0
00032420322i[CPU0 ] | CR3=0x01ffc000 CR4=0x00000000
00032420322i[CPU0 ] (instruction unavailable) page not present
00032420322e[CPU0 ] exception(): 3rd (14) exception with no resolution, shutdown status is 00h, resetting
« Letzte Änderung: 19. May 2009, 14:42 von rizor »
Programmiertechnik:
Vermeide in Assembler zu programmieren wann immer es geht.

rizor

  • Beiträge: 521
    • Profil anzeigen
Gespeichert
« Antwort #11 am: 20. May 2009, 22:52 »
Habe das Problem gelöst.
Habe vergessen einen Teil mit 0xC0 zu verodern.
Allerdings habe ich jetzt ein anderes Problem, dass ich nicht verstehe.

Wenn das System bootet bekomme ich am ende einen DPF, GPF oder eine DBZ.
Wenn ich die Interrupts abschalte, dann startet Qemu neu (wenn ISR, IDT, IRQ und PIC nicht mitgeladen werden), aber wenn alle Module mitgeladen werden, dann kommt es zu keinen Problemen (außer das die Interrupts deaktiviert sind).

Woran kann es liegen, dass ich immer unterschiedliche Exceptions bekomme?
Wenn das System gebootet wurde, läuft es nur noch in einer Endlosschleife, die nichts tut.
Daran sollte es also nicht liegen.
Programmiertechnik:
Vermeide in Assembler zu programmieren wann immer es geht.

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #12 am: 20. May 2009, 23:27 »
Mal eine ganz blöde Frage... GPF sagt mir ja was, aber was sollen denn DPF und DBZ sein? Wenn man zu viele ungeläufige Abkürzungen verwendet, wird man irgendwann nicht mehr verstanden. ;)
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

rizor

  • Beiträge: 521
    • Profil anzeigen
Gespeichert
« Antwort #13 am: 21. May 2009, 09:11 »
Sorry,
Douple Page Fault und
Division by Zero sind die anderen Abkürzungen
Programmiertechnik:
Vermeide in Assembler zu programmieren wann immer es geht.

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #14 am: 21. May 2009, 10:56 »
Du meinst vermutlich Double Fault? Von einem Double Page Fault habe ich nämlich noch nichts gehört - maximal von einem Nested Page Fault, aber das ist eine ganz andere Baustelle. ;)

Hast du mal geschaut, ob du diese Exceptions wirklich alle kriegst oder ob dein Interrupthandling kaputt ist? Also einfach mal qemu -d int benutzen und schauen, ob da wirklich die Exceptions kommen, die dein OS meint, dass kommen.

Und dann könnte es natürlich noch sein, dass die Sache timingabhängig ist - z.B. wenn ein Timer-IRQ früh genug kommt, gibt es einen GPF, ansonsten rennt er in eine kaputte Division. An der Stelle wäre es dann noch nützlich zu wissen, was den Double Fault ausgelöst hast, siehst du auch mit -d int.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

rizor

  • Beiträge: 521
    • Profil anzeigen
Gespeichert
« Antwort #15 am: 21. May 2009, 12:29 »
Ich habe mir ma das logfile angeschaut, nachdem eine Division by Zero kam.
Da gibt es keine Exception.
Hier mal das File, falls ich was übersehe.

SMM: enter
EAX=00000001 EBX=80000070 ECX=00000cf8 EDX=000000b3
ESI=02000000 EDI=0009fb8c EBP=0009fb24 ESP=0009fb14
EIP=000e0ad0 EFL=00000002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0018 00000000 ffffffff 00cf9300
CS =0010 00000000 ffffffff 00cf9b00
SS =0018 00000000 ffffffff 00cf9300
DS =0018 00000000 ffffffff 00cf9300
FS =0000 00000000 00000000 00000000
GS =0000 00000000 00000000 00000000
LDT=0000 00000000 0000ffff 00008200
TR =0000 00000000 0000ffff 00008b00
GDT=     000fb7f7 00000030
IDT=     000f0000 00000000
CR0=60000011 CR2=00000000 CR3=00000000 CR4=00000000
DR0=00000000 DR1=00000000 DR2=00000000 DR3=00000000
DR6=ffff0ff0 DR7=00000400
CCS=0000002d CCD=00000001 CCO=LOGICB 
SMM: after RSM
EAX=00000001 EBX=80000070 ECX=00000cf8 EDX=000000b3
ESI=02000000 EDI=0009fb8c EBP=0009fb24 ESP=0009fb14
EIP=000e0ad0 EFL=00000002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0018 00000000 ffffffff 00c09300
CS =0010 00000000 ffffffff 00c09b00
SS =0018 00000000 ffffffff 00c09300
DS =0018 00000000 ffffffff 00c09300
FS =0000 00000000 00000000 00000000
GS =0000 00000000 00000000 00000000
LDT=0000 00000000 0000ffff 00008200
TR =0000 00000000 0000ffff 00008b00
GDT=     000fb7f7 00000030
IDT=     000f0000 00000000
CR0=60000011 CR2=00000000 CR3=00000000 CR4=00000000
DR0=00000000 DR1=00000000 DR2=00000000 DR3=00000000
DR6=ffff0ff0 DR7=00000400
CCS=00000000 CCD=ffffff9c CCO=EFLAGS 
     0: v=20 e=0000 i=0 cpl=0 IP=0008:00100545 pc=00100545 SP=0010:0010cfe0 EAX=00000017
EAX=00000017 EBX=00000800 ECX=000b8000 EDX=000b03d5
ESI=000579bd EDI=000579be EBP=0010cff8 ESP=0010cfe0
EIP=00100545 EFL=00000202 [-------] 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 =0010 00000000 ffffffff 00cf9300
FS =0010 00000000 ffffffff 00cf9300
GS =0010 00000000 ffffffff 00cf9300
LDT=0000 00000000 0000ffff 00008200
TR =0000 00000000 0000ffff 00008b00
GDT=     0010d060 0000002f
IDT=     0010d0a0 000007ff
CR0=e0000011 CR2=00000000 CR3=01ffc000 CR4=00000000
DR0=00000000 DR1=00000000 DR2=00000000 DR3=00000000
DR6=ffff0ff0 DR7=00000400
CCS=00000010 CCD=0010cfe0 CCO=ADDL   

Was meintest du mit dem Timer-IRQ?
Wie kann der dafür sorgen, dass das System in eine defekte Division läuft?
Programmiertechnik:
Vermeide in Assembler zu programmieren wann immer es geht.

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #16 am: 21. May 2009, 12:59 »
Sehe ich genauso, da war keine Exception. Also macht dein Interrupthandling irgendwas falsch. Das einzige, was du bekommst ist ein Interrupt 0x20, also vermutlich der Timer auf IRQ 0.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

rizor

  • Beiträge: 521
    • Profil anzeigen
Gespeichert
« Antwort #17 am: 21. May 2009, 15:17 »
Hab den Fehler gefunden.
Mein Interrupt-Dispatcher hatte falsch gearbeitet.
Nun bekomme ich aber einen GPF und weiß leider nicht wo der herkommt.
Folgende Module habe ich initialisiert:
Paging, GDT, IDT, Exceptions, IRQ, PIC

Was genau ist dein ein GPF, bzw. was könnte das denn auslösen?
Programmiertechnik:
Vermeide in Assembler zu programmieren wann immer es geht.

MNemo

  • Beiträge: 547
    • Profil anzeigen
Gespeichert
« Antwort #18 am: 21. May 2009, 17:38 »
Also ein #GP kann durch ziemlich viele Dinge ausgelöst werden. Am besten du zeigst mal den asm-code der den #GP auslöst.

Die stelle an der die Exception ausgelöst wird kannst du ja aus cs:(e/r)ip auslesen.
und dann in der ausgabe von:
objdump -d -M intel <kernel.sys>den zugehörigen Code ausfindig machen.
„Wichtig ist nicht, besser zu sein als alle anderen. Wichtig ist, besser zu sein als du gestern warst!“

rizor

  • Beiträge: 521
    • Profil anzeigen
Gespeichert
« Antwort #19 am: 21. May 2009, 18:48 »
wie kann ich das am besten machen mit CS:eip?
habe ich bisher noch nicht gemacht.
Das objdump kann ich wahrscheinlioch mit dem gdb machen, oder?
Programmiertechnik:
Vermeide in Assembler zu programmieren wann immer es geht.

 

Einloggen