Lowlevel
Lowlevel => Lowlevel-Coding => Thema gestartet von: rizor 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.
-
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?
-
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?
-
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).
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.
-
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.
-
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.
-
Stimmt, das hat das Problem allerdings immer noch nicht behoben.
-
Dann geht das raten weiter ;) Ich würde struct gd_table mit einem __attribute__((packed)) versehen.
-
Hat auch nichts gebracht.
Hätte mich auch verwundert, da ich noch keine Optimierungen vom Compiler vornehmen lasse.
-
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?
-
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
-
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.
-
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. ;)
-
Sorry,
Douple Page Fault und
Division by Zero sind die anderen Abkürzungen
-
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.
-
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?
-
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.
-
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?
-
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.
-
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?
-
wie kann ich das am besten machen mit CS:eip?
du suchst in der log-datei von qemu(wieder mit '-d int') nach der ersten Exception und
guckst im darauf folgenden register dump.
Das objdump kann ich wahrscheinlioch mit dem gdb machen, oder?
em… wieso, hast du kein 'objdump'? das ist iirc sogar bei mingw dabei. mit dem gdb kenn ich mich nicht aus.
-
Mir ist grad aufgefallen, dass es eine Funktion bei Linux ist.
Wusste ich vorher nicht.
Ich schaue dann gleich mal nach.
-
Wie lese ich denn das cs:eip?
Da stehen folgende Werte:
CS=0008 00000000 ffffffff 00cf9a00
EIP=00100347
-
Wie lese ich denn das cs:eip?
Da stehen folgende Werte:
CS=0008 00000000 ffffffff 00cf9a00
EIP=00100347
cs:eip = 0x08:0x00100347(was da sonst noch steht ist base,limit und type des segments)
aber cs ist eigentlich egal. wichtig ist die 0x00100347, danach musst du in der ausgabe von 'objdump' suchen.
-
Hab die Fehlerhafte Stelle gefunden.
Es ist der iret.
Der Handler sieht wie folgt aus:
10031c: 60 pusha
10031d: 1e push ds
10031e: 06 push es
10031f: 0f a0 push fs
100321: 0f a8 push gs
100323: fc cld
100324: 66 b8 10 00 mov ax,0x10
100328: 8e d8 mov ds,eax
10032a: 8e c0 mov es,eax
10032c: 54 push esp
10032d: e8 62 0c 00 00 call 100f94 <int_dispatcher>
100332: 81 c4 04 00 00 00 add esp,0x4
100338: 89 c4 mov esp,eax
10033a: 0f a9 pop gs
10033c: 0f a1 pop fs
10033e: 07 pop es
10033f: 1f pop ds
100340: 61 popa
100341: 81 c4 08 00 00 00 add esp,0x8
100347: cf iret
-
dann ist vermutlich der Stack kaputt.
tritt der fehler auch noch auf wenn dein int_dispatcher einfach das gleiche esp returned wie es als parameter bekommt?
wessen iret ist es denn? der erste timer-interrupt?
was sagt denn der error code?
check_exception old: 0xffffffff new 0xd
1: v=0d e=???? […]
-
Es ist der erste Timer-IRQ.
An sich müsste es lso der gleiche ESP sein.
-
Es ist der erste Timer-IRQ.
An sich müsste es lso der gleiche ESP sein.
d.h. bei dir funktionieren wahrscheinlich noch keine Interrupts richtig.?
wie sieht den dein int_0x20 stub aus?
int_0x20:
push 0
push 0x20
jmp 0x10031c
was für ein error code wird denn der exception mitgegeben?
bzw. poste mal den kompletten register-dump der zu der exception gehört
-
genau so sieht der stub aus.
Der Error-Code ist 0, der mit dem GPF gegeben wird.
Woran kann es liegen, dass der iret Fehler auslöst?
-
Iret kann aus vielen verschiedenen gründen ein #GP auslösen
ErrorCode: 0
1. Null Selektor für Code oder Stacksegment
2. Adressierung außerhalb der Segment Grenzen
ErrorCode: Selector {nicht wirklich relevant}
fehlerhafte/inkompatible privileglevel, selectoren außerhalb der GDT, und andere
Edit: Bist du dir sicher das es ein #GP(0x0d) ist. Alle möglichen Ursachen scheine für mich mit dem ErrorCode 0 ausgeschlossen zu sein.
Edit2:
Wenn du willst kann ich mir das auch mal angucken(bräuchte nur ein image, zeit hab ich). Ich wüsste sonst echt nicht woran es liegt.
-
Ja, da bin ich mir sicher.
Es erzählt mir zumindest das logile von qemu.
Der Link zu meinem Image:
http://rizor.bplaced.net/rizor-OS-0.1-Alpha.img
Danke für deine Hilfe.
-
Mir ist etwas aufgefallen.
Wieso wird in tyndur 0x28 ins TR geschrieben?
Muss im TR nicht eher die Adresse des TSS stehen?
-
Nein, da steht ein Selektor, der eben auf einen GDT-Eintrag verweist. TR ist im weitesten Sinn auch nur ein Segmentregister.
-
Okay.
Dann mal eine andere Frage:
Ich mache es momentan so, dass ich den GDT initialisiere und sobald ich Multitasking aktiviere den TSS setze.
Könnte es sein, dass ich damit irgendwelche Probleme erzeuge?
-
Wenn du es richtig machst nicht.
-
An sich sollte es passen.
Habe nur eine Frage:
Wieso wird in esp0 eine 0 geschrieben (habe ich mir nur ab geguckt)?
Sollte da nicht eigentlich der stackpointer stehen?
So sieht es momentan aus:
memset(&krn_tss , 0 , sizeof(tss_t));
krn_tss.ss0 = SYSTEM_DATA_SELECTOR;
krn_tss.esp0 = 0x0;
krn_tss.ss = 0x10;
krn_tss.ds = 0x10;
krn_tss.es = 0x10;
krn_tss.fs = 0x10;
krn_tss.gs = 0x10;
-
Da sollte ein Stackpointer stehen, ja. Wo hast du denn abgeschrieben? Sicher, dass der Wert nicht später noch auf was vernünftiges gesetzt wird?
-
dein int_dispatcher returned nicht esp, sondern die interrupt nummer
-
Das war aus einem Tut, weiß leider nicht mehr welches.
Habe da jetzt den aktuellen ESP-Wert eingetragen.
Das sollte jetzt stimmen, oder?
@MNemo:
Bist du dir da sicher?
Habe mir mal den Stack aufgeschrieben.
Ich sehe da keinen Fehler?
Was sollte deiner Meinung nach denn wegfallen in dem Code?
-
Das Problem hat sich gelöst.
Manchmal bin ich wie vernagelt...
Habe anstatt esp + 8, esp + 4 gemacht.
Stimmt das mit dem esp0 denn, wenn ich den aktuellen esp nehme?