Autor Thema: Problem mit Interrupts  (Gelesen 2510 mal)

oern

  • Beiträge: 44
    • Profil anzeigen
Gespeichert
« 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

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #1 am: 14. February 2012, 21:21 »
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.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

oern

  • Beiträge: 44
    • Profil anzeigen
Gespeichert
« Antwort #2 am: 15. February 2012, 16:43 »
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

oern

  • Beiträge: 44
    • Profil anzeigen
Gespeichert
« Antwort #3 am: 16. February 2012, 16:55 »
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

 

Einloggen