Autor Thema: IDT funktioniert nicht  (Gelesen 23438 mal)

Pk3

  • Beiträge: 52
    • Profil anzeigen
    • Xyross
Gespeichert
« am: 22. August 2009, 10:56 »
Hi, hab mal wieder ein Problem :roll:, ich kann in meinem C Kernel (PM)
keine Interrupts ausführen. Wenn ich z.B. int 16h ausführe,
bleibt mein Kernel stehen, passiert nichts mehr.

Hier mal der Code.

char IDT[256 * 8];
void LoadIDT();

void begin() {
LoadIDT();
asm("sti\n\tint $0x16");
v_print("Test");
}

void LoadIDT() {
struct {
unsigned int limit;
unsigned int base;
}  __attribute__((packed)) idt_ptr = {
.limit  = 256*8 - 1,
.base  = (unsigned int)IDT,

};

asm("lidt %0" : : "m" (idt_ptr));
}

Weiß jemand warum das nicht geht?

Edit:
Bochs meldet dazu:

00004421957-i-@000013f9-[CPU0 ] >> int 0x16 : CD16
00004421957-e-@000013f9-[CPU0 ] exception(): 3rd (13) exception with no resolution, shutdown status is 00h, resetting
« Letzte Änderung: 22. August 2009, 11:00 von Pk3 »

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #1 am: 22. August 2009, 11:24 »
Wird die IDT auch irgendwo mit sinnvollen Werten gefüllt?
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

Pk3

  • Beiträge: 52
    • Profil anzeigen
    • Xyross
Gespeichert
« Antwort #2 am: 22. August 2009, 11:27 »
Also das ist der gesamte Code den ich gepostet habe.
Wie und mit welchen Werten fülle ich sie?

rizor

  • Beiträge: 521
    • Profil anzeigen
Gespeichert
« Antwort #3 am: 22. August 2009, 12:06 »
Du musst zu jedem Interrupt, auf den du reagieren möchtest, das Gate definieren.
Das kannst du dir im Wiki anschauen.
Da steht genau drin was du in die Einträge schreiben kannst.
Du musst halt definieren, wer den Interrupt auslösen darf und was für eine Art von Interrupt-Gate das ist.
Programmiertechnik:
Vermeide in Assembler zu programmieren wann immer es geht.

Pk3

  • Beiträge: 52
    • Profil anzeigen
    • Xyross
Gespeichert
« Antwort #4 am: 22. August 2009, 12:15 »
http://lowlevel.brainsware.org/wiki/index.php/IDT

Da steht aber nicht, wie man eine IDT füllt, sondern nur die Werte.
Kannst du mir vielleicht bitte ein Beispiel geben  :-)?

rizor

  • Beiträge: 521
    • Profil anzeigen
Gespeichert
« Antwort #5 am: 22. August 2009, 12:36 »
Es gibt bestimmte Flags, dir du an einem Gate setzen kannst:

#define IDT_TASK_GATE 0x5
#define IDT_INT_GATE_16 0x6
#define IDT_TRAP_GATE_16 0x7
#define IDT_INT_GATE_32 0xe
#define IDT_TRAP_GATE_32 0xf
#define IDT_SYS_SEGMENT 0x10
#define IDT_RING_0 0x0
#define IDT_RING_1 0x20
#define IDT_RING_2 0x40
#define IDT_RING_3 0x60
#define IDT_PRESENT 0x80

#define IDT_STD_GATE IDT_PRESENT | IDT_RING_0 | IDT_INT_GATE_32
#define IDT_SYSCALL_GATE IDT_PRESENT | IDT_RING_3 | IDT_INT_GATE_32

Das sind bestimme Flags.
STD_GATE beschreibt bei mir einfach die normalen Interrupts.
Die sollten am besten nicht aus dem Userspace ausgelöst werden können.
Die Syscalls hingegen schon.

Du kannst dir die ganzen Gates definieren wie du willst.
Programmiertechnik:
Vermeide in Assembler zu programmieren wann immer es geht.

Pk3

  • Beiträge: 52
    • Profil anzeigen
    • Xyross
Gespeichert
« Antwort #6 am: 22. August 2009, 12:43 »
Ich hab das jetzt so verstanden:

void LoadIDT() {
unsigned int i=0;
while(i < 256) {
IDT[i] = IDT_STD_GATE;
i++;
}

struct {
unsigned int limit;
unsigned int base;
}  __attribute__((packed)) idt_ptr = {
.limit  = 256*8 - 1,
.base  = (unsigned int)IDT,

};

asm("lidt %0" : : "m" (idt_ptr));
}

Ist das so richtig?

rizor

  • Beiträge: 521
    • Profil anzeigen
Gespeichert
« Antwort #7 am: 22. August 2009, 12:51 »
Das kannst du so machen.
Allerdings brauchst du nicht jedes gate definieren.
Es ist effektiver und sicher wenn du nicht jeden Eintrag definierst.
Am besten ist es, wenn du nur die Gates definierst, die du auch wirklich brauchst.
Den Rest kannst du mit NULL auffüllen.

So könnte eine Methode aussehen:
void idt_set_gate(uint16_t num , uint32_t base , uint16_t sel , uint8_t attr){
if(num < IDT_SIZE){
if(IDT[num].offset_low != NULL)
panic("Tried to write into a set IDT-Entry" , NO_CPU_STATE);

IDT[num].offset_low = (base & 0xFFFF);
IDT[num].offset_high = (base >> 16) & 0xFFFF;
IDT[num].selector = sel;
IDT[num].zero = 0;
IDT[num].attribute = attr;
}
else
panic("Tried to set a wrong interrupt-gate" , NO_CPU_STATE);
}

Bei mir sehen die Gates wie folgt aus:
typedef struct{
uint16_t offset_low;
uint16_t selector;
uint8_t zero;
uint8_t attribute;
uint16_t offset_high;
}id_table;

Der Call sieht dann für die Division by zero Exception so aus:
idt_set_gate(0, (unsigned)intr_0, 0x08, IDT_STD_GATE);
« Letzte Änderung: 22. August 2009, 12:55 von rizor »
Programmiertechnik:
Vermeide in Assembler zu programmieren wann immer es geht.

Pk3

  • Beiträge: 52
    • Profil anzeigen
    • Xyross
Gespeichert
« Antwort #8 am: 22. August 2009, 12:56 »
Ah ok, also so geht es auch?

Weil sobald ich das hier mache, funktioniert wieder nix mehr:

asm("sti\n\ttaste:\n\tmov $0x1, %ah\n\tint $0x16\n\tjz taste\n\tmov 0x0, %ah\n\tint $0x16");
Bochs sagt wieder:

00004424009-i-@0000141d-[CPU0 ] >> int 0x16 : CD16
00004424009-e-@0000141d-[CPU0 ] exception(): 3rd (13) exception with no resolution, shutdown status is 00h, resetting

 :-(

Edit

Dein Code geht bei mir nicht, kommen unmengen an Fehlern beim
komplimieren, liegt vielleicht daran das du einen anderen Compiler
als ich verwendest.
« Letzte Änderung: 22. August 2009, 13:03 von Pk3 »

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #9 am: 22. August 2009, 13:04 »
void LoadIDT() {
unsigned int i=0;
while(i < 256) {
IDT[i] = IDT_STD_GATE;
i++;
}
Das ist Blödsinn. Die entscheidende Information lässt du weg, nämlich was passieren soll, wenn der Interrupt aufgerufen werden soll. Es sei denn, du hast abweichend von rizor deinen Interrupthandler noch in IDT_STD_GATE reinkodiert.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

rizor

  • Beiträge: 521
    • Profil anzeigen
Gespeichert
« Antwort #10 am: 22. August 2009, 13:05 »
Machst du das noch nach deiner Methode?

Sorry, mir ist eben aufgefallen, dass du die Gates falsch initialisierst.
Du musst dir so einen Struct aufbauen, da die Gates nicht nur char* sind.
Das STD_GATE ist an sich nur das Attribut des Gates.
Du musst natürlich auch noch eine Methode angeben, die bei dem Gate angesrungen werden soll.
Ich habe mir dafür einfach für jedes Interrupt eine Asm-Methode geschrieben, die dann die Interrupt-Nummer speichert.

Wenn ein Interrupt ausgelöst wird, schaut die CPU in der IDT nach und sucht nach dem passenden Gate.
Wenn dieses gefunden wurde,springt die CPU die dort eingetragene Methode an.
Programmiertechnik:
Vermeide in Assembler zu programmieren wann immer es geht.

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #11 am: 22. August 2009, 13:07 »
Dein Code geht bei mir nicht, kommen unmengen an Fehlern beim
komplimieren, liegt vielleicht daran das du einen anderen Compiler
als ich verwendest.
Er benutzt die Datentypen aus stdint.h, die du vermutlich nicht definiert hast.

Womit wir wieder bei einer Grundregel wären: Beherrsche die Sprache, mit der du ein OS entwickeln willst. Ein OS ist schwierig genug, dass man nicht auch noch mit der Sprache kämpfen müssen sollte. Und noch viel wichtiger: Verstehe Beispielcode anstatt ihn zu kopieren. Dann hättest du nämlich kein Problem damit, ihn auf deinen Code anzupassen.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

Pk3

  • Beiträge: 52
    • Profil anzeigen
    • Xyross
Gespeichert
« Antwort #12 am: 22. August 2009, 13:23 »
typedef struct {
uint16_t  offset_low;
uint16_t selector;
uint8_t zero;
uint8_t attribute;
uint16_t offset_high;
}id_table;

id_table IDT[256];

void begin() {
idt_set_gate(0, (unsigned)intr_0, 0x16, IDT_STD_GATE);
}

void idt_set_gate(uint16_t num, uint32_t base, uint16_t sel, uint8_t attr) {
IDT[num].offset_low = (base & 0xFFFF);
IDT[num].offset_high = (base >> 16) & 0xFFFF;
IDT[num].selector = sel;
IDT[num].zero = 0;
IDT[num].attribute = attr;

struct {
unsigned int limit;
unsigned int base;
}  __attribute__((packed)) idt_ptr = {
.limit  = 256*8 - 1,
.base  = (unsigned int)IDT,

};

asm("lidt %0" : : "m" (idt_ptr));
}

So?
« Letzte Änderung: 22. August 2009, 13:24 von Pk3 »

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #13 am: 22. August 2009, 13:45 »
Ich glaube, es bewegt sich langsam in die richtige Richtung. 0x16 sollte aber num sein, nicht sel. sel ist der Selektor für dein Codesegment, also vermutlich 0x08.

Außerdem brauchst du die IDT nicht bei jedem Eintrag, den du neu setzt, laden. Normalerweise setzt du einfach erst alle Einträge und lädst dann hinterher die IDT, die du aufgebaut hast.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

Pk3

  • Beiträge: 52
    • Profil anzeigen
    • Xyross
Gespeichert
« Antwort #14 am: 22. August 2009, 14:00 »
Was muss ich einbinden damit (unsigned)intr_0 funktioniert?
Und ich bin mir nicht sicher ob 0x08 mein Codesegment ist....

rizor

  • Beiträge: 521
    • Profil anzeigen
Gespeichert
« Antwort #15 am: 22. August 2009, 14:13 »
Liegt deine Methode am Ende nicht im Kernel-Space?

Du kannst dir einfach eine ASM-Methode schreiben, die die interrupt-nummer und ggf. den errorcode auf den Stack pusht.
Danach kannst du einen handler aufrufen, der den letzten CPU-Status speichert und dann am Ende den Interrupt behandelt.
Programmiertechnik:
Vermeide in Assembler zu programmieren wann immer es geht.

Pk3

  • Beiträge: 52
    • Profil anzeigen
    • Xyross
Gespeichert
« Antwort #16 am: 22. August 2009, 14:16 »
Das Problem ist nur noch hier:

idt_set_gate(0x16, 0, 0x08, IDT_STD_GATE);
Beim 2. Parameter (uint32_t base) weiß ich nicht, was da hin soll.

Ja, ist im Kernelspace.

rizor

  • Beiträge: 521
    • Profil anzeigen
Gespeichert
« Antwort #17 am: 22. August 2009, 14:19 »
Das ist genau deine Methode, die angesprungen werden soll, wenn der Interrupt ausgelöst wird.
Bei mir intr_0
Programmiertechnik:
Vermeide in Assembler zu programmieren wann immer es geht.

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #18 am: 22. August 2009, 14:21 »
Wobei für Interrupt 0x16 vielleicht der Name intr_0x16 oder intr_22 oder so angebracht ist.
Dieser Text wird unter jedem Beitrag angezeigt.

rizor

  • Beiträge: 521
    • Profil anzeigen
Gespeichert
« Antwort #19 am: 22. August 2009, 14:23 »
Stimmt, hab mich nur verlesen.
Dachte, dass es der 0. Interrupt ist
Programmiertechnik:
Vermeide in Assembler zu programmieren wann immer es geht.

 

Einloggen