Lowlevel
Lowlevel => Lowlevel-Coding => Thema gestartet von: oern am 14. February 2012, 18:06
-
Hallo,
ich habe mal wieder angefangen, einen neuen Kernel zu schreiben weil meine alten alle zu kaputt waren.
Jetzt hänge ich bei der IDT / den Interrupts.
Ich habe so ziemlich das gleiche Problem wie manche andere hier auch schon hatten, aber die Fehler die es da waren können es eigentlich nicht sein.
Offenbar wird der Stack, wo die Registerwerte beim Sprung in den Handler drauf sind, kaputt gemacht. Zum Beispiel kommen wenn ich (nach Laden der IDT) die Interrupts einschalte zuerst (laut QEMU-Logs) ein Int 0x21 (wieso der kommt weiß ich nicht, ist doch Tastatur?), danach ein Int 0x20 und dann ein Int 0x0e. Ich habe einen Registerdump auch bei IRQs. Der behauptet aber beim ersten IRQ, dass IRQ 4 (statt 1) und beim zweiten, dass IRQ 3 (statt 0), aufgerufen wurde. Beim zweiten IRQ stimmen auch die Registerwerte vom Stack nicht mehr mit den aus den QEMU-Logs überein (z.B. steht in CS der Wert von EIP, in ESP die EFLAGS etc.). Beim Page Fault ist dann von den Werten her alles in Ordnung. Der Code, der die IDT einrichtet, ist in Assembler geschrieben und schreibt nur die Handleraddressen in die IDT; der Rest (Flags, Segment) steht da schon vorher drinnen, damit so wenig wie möglich zur Laufzeit gemacht wird (mir kommt es unnötig vor, die Flags und das Codesegment zur Laufzeit zu bestimmen, obwohl die schon zur Kompilierzeit feststehen, mache ich bei der GDT genau so).
Außerdem wird der PIC auf die entsprechenden Nummern gemappt. Bootloader ist GRUB und kein eigener.
Alles in allem: Interrupts funktionieren nicht. Wieso nicht? :roll:
Ich könnte noch den Code posten, das mache ich aber erst, falls überhaupt jemand sich das hier durchliest. Ich hoffe, dass die restliche Beschreibung genau genug war, aber sonst sagt ihr das ja.
Grüße,
oern
-
Was heißt denn kaputt? Wenn die Registerwerte nicht stimmen, sehen sie völlig willkürlich aus oder sind sie nur irgendwie verschoben? Deine Interruptstubs solltest du auf jeden Fall mal posten, ohne Code ist das Stochern im Nebel. Wo holst du die falsche IRQ-Nummer her und wie sieht deine C-Struktur aus, die du zum Zugreifen auf die Registerwerte benutzt?
Den Tastatur-IRQ kriegst du vermutlich, weil du im GRUB-Menü/Shell Enter gedrückt hast und GRUB zwar den Make-, aber nicht mehr den Break-Code verarbeitet.
-
Ok, du wolltest ja Code :-D:
idt.S:
#define idt_sys_gate(n) .quad 0x00008e0000080000
#define idt_usr_trap(n) .quad 0x0000ef0000080000
#define idt_no_entry() .quad 0x0000000000000000
#define int_handler(n) \
push $0; \
push $##n; \
jmp common_int_handler;
#define int_handler_error(n) \
nop; \
nop; \
push $##n; \
jmp common_int_handler;
.section .text
.global idt_init
idt_init:
push %edi
push %ecx
mov $handler, %eax
xor %ecx, %ecx
mov $idt, %edi
.loop:
cmp $0x31, %ecx
je .loop_end
mov %ax, (%edi,%ecx,8)
ror $0x10, %eax
mov %ax, 0x06(%edi,%ecx,8)
ror $0x10, %eax
add $0x09, %eax
inc %ecx
jmp .loop
.loop_end:
pop %ecx
pop %edi
lidt idtp
mov $0x11, %al
out %al, $0x20
out %al, $0xa0
mov $0x20, %al
out %al, $0x21
mov $0x28, %al
out %al, $0xa1
mov $0x04, %al
out %al, $0x21
mov $0x02, %al
out %al, $0xa1
mov $0x01, %al
out %al, $0x21
out %al, $0xa1
ret
handler:
int_handler(0)
int_handler(1)
int_handler(2)
int_handler(3)
int_handler(4)
int_handler(5)
int_handler(6)
int_handler(7)
int_handler_error(8)
int_handler(9)
int_handler_error(10)
int_handler_error(11)
int_handler_error(12)
int_handler_error(13)
int_handler_error(14)
int_handler(15)
int_handler(16)
int_handler_error(17)
int_handler(18)
...
int_handler(48)
.extern int_handler
common_int_handler:
push %gs
push %fs
push %es
push %ds
push %ebp
push %edi
push %esi
push %edx
push %ecx
push %ebx
push %eax
mov $0x10, %ax
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
push %esp
call int_handler
mov %eax, %esp
pop %eax
pop %ebx
pop %ecx
pop %edx
pop %esi
pop %edi
pop %ebp
pop %ds
pop %es
pop %fs
pop %gs
add $0x08, %esp
iretl
.section .data
idtp:
.word 0x7ff
.int idt
idt:
idt_sys_gate(0)
idt_sys_gate(1)
...
idt_sys_gate(47)
idt_usr_trap(48)
idt_no_entry()
...
cpu-Struktur:
struct cpu {
unsigned int eax;
unsigned int ebx;
unsigned int ecx;
unsigned int edx;
unsigned int esi;
unsigned int edi;
unsigned int ebp;
unsigned int ds;
unsigned int es;
unsigned int fs;
unsigned int gs;
unsigned int intr;
unsigned int error;
unsigned int eip;
unsigned int cs;
unsigned int eflags;
unsigned int esp;
unsigned int ss;
} __attribute__((packed));
handler.c:
struct cpu *int_handler(struct cpu *cpu)
{
if (cpu->intr <= 0x1f) {
printk("Exception %d\n", cpu->intr);
printk("EAX = 0x%x EBX = 0x%x ECX = 0x%x EDX = 0x%x\n", \
cpu->eax, cpu->ebx, cpu->ecx, cpu->edx);
if (cpu->cs != 0x08) {
printk("ESI = 0x%x EDI = 0x%x EBP = 0x%x ESP = 0x%x\n", \
cpu->esi, cpu->edi, cpu->ebp, cpu->esp);
} else {
printk("ESI = 0x%x EDI = 0x%x EBP = 0x%x\n", \
cpu->esi, cpu->edi, cpu->ebp);
}
printk("CS = 0x%x DS = 0x%x ES = 0x%x FS = 0x%x\n", \
cpu->cs, cpu->es, cpu->fs, cpu->gs);
if (cpu->cs != 0x08) {
printk("GS = 0x%x SS = 0x%x EFL = 0x%x EIP = 0x%x\n", \
cpu->gs, cpu->ss, cpu->eflags, cpu->eip);
} else {
printk("GS = 0x%x EFL = 0x%x EIP = 0x%x\n", \
cpu->gs, cpu->eflags, cpu->eip);
}
printk("ERR = 0x%x\n", cpu->error);
asm volatile("cli; hlt");
} else if (cpu->intr <= 0x2f) {
printk("IRQ %d\n", cpu->intr - 0x20);
printk("EAX = 0x%x EBX = 0x%x ECX = 0x%x EDX = 0x%x\n", \
cpu->eax, cpu->ebx, cpu->ecx, cpu->edx);
if (cpu->cs != 0x08) {
printk("ESI = 0x%x EDI = 0x%x EBP = 0x%x ESP = 0x%x\n", \
cpu->esi, cpu->edi, cpu->ebp, cpu->esp);
} else {
printk("ESI = 0x%x EDI = 0x%x EBP = 0x%x\n", \
cpu->esi, cpu->edi, cpu->ebp);
}
printk("CS = 0x%x DS = 0x%x ES = 0x%x FS = 0x%x\n", \
cpu->cs, cpu->es, cpu->fs, cpu->gs);
if (cpu->cs != 0x08) {
printk("GS = 0x%x SS = 0x%x EFL = 0x%x EIP = 0x%x\n", \
cpu->gs, cpu->ss, cpu->eflags, cpu->eip);
} else {
printk("GS = 0x%x EFL = 0x%x EIP = 0x%x\n", \
cpu->gs, cpu->eflags, cpu->eip);
}
handle_irq(cpu->intr - 0x20);
} else {
cpu = handle_syscall(cpu);
}
return cpu;
}
unsigned int irq_table[0x10] = { 0, 0, 0, 0, 0, 0, 0, 0, \
0, 0, 0, 0, 0, 0, 0, 0 };
void set_irq(unsigned char num, unsigned int handler)
{
irq_table[num] = handler;
}
static void handle_irq(unsigned char num)
{
if (irq_table[num]) {
void (*handler)(void) = (void*)irq_table[num];
handler();
}
outb(0x20, 0x20);
if (num > 7) {
outb(0x20, 0xa0);
}
}
Ich kann auch noch den genauen QEMU-Log und die dazugehörige Ausgabe schreiben.
Und ich meinte eigentlich gar nicht GRUB :oops:, sondern qemu mit -kernel, ich wollte damit nur sagen, dass der Fehler nicht von einem eigenen Bootloader kommen kann.
In dem Moment, in dem das passiert ist schon Paging an (das sollte auch nicht der Fehler sein, der Page Fault kommt an Addresse 0x430, die wirklich nicht gemappt sein sollte) und die neue GDT geladen.
Wie schon gesagt, die Registerwerte sind (teilweise) verschoben, andere kommen scheibar von irgendwo her. Ich habe schon drin, dass er ss und esp nur ausgibt, wenn der Interrupt nicht vom Kernelspace aufgerufen wird.
oern
-
Es geht jetzt!!!
Das Problem war, dass der Assembler aus den unteren Interruptstubs relative Jumps gemacht hat, die nur zwei Bytes verbrauchen, während ich davon ausging, dass alle Jump-Befehle 5 Bytes brauchen. Hier war es aber wichtig, dass alle Stubs gleich groß sind, da ich ja auf den Anfang der Handler immer 9 addiere (zu den 5 Bytes für den Sprung kommen noch die nops, die ich ja schon extra zum Alignment eingebaut hatte, bzw. eben die push-Befehle). Also wurden mir da praktisch ungültige Addressen in die IDT reingeschoben.
Hätte eben doch C nehmen sollen und die Addressen der Stubs jedes mal einzeln nehmen sollen.
Ich habe jetzt als Zwischenlösung einfach die absolute Addresse statt common_int_handler geschrieben, aber das geht natürlich auch nicht mehr, wenn sich der Code verschiebt (zur Zeit ist die idt.o immer ganz am Anfang, andere Dateien stören also nicht).
Grüße,
oern