As indicated by the title, I've installed an IDT with CPU exception handlers and a keyboard interrupt service routine. The CPU exceptions (e.g. int $0x00) work like a charm but I'm having issues with the keyboard interrupt. At this point, I've removed all the code in the keyboard interrupt service routine except for the iret instruction.
In emulators (Virtualbox, Qemu, Bochs) everything works as expected. OS boots, installs IDT, and on a keypress nothing happens (as expected because the ISR for 0x21 consists only of the iret instruction). HOWEVER, on real hardware, this iret instruction triggers a #GF (General Protection Fault)... Only on real hardware...
Because the Protection Fault only gets triggered on real hardware, it's really hard to debug (I'm not very experienced in OSdev). I've deduced / tracked down the following symptoms:
- #GF gets triggerd when the iret instruction occurs of the 0x21 keyboard handler service routine (I've remapped the PIT such that keyboard interrupt is the 0x21 entry in the IDT)
- I have reason to believe the stack gets corrupted for some reason. That would be the only thing that could make sense. But the question is: why?
- If the stack gets corrupted, the iret will pop invalid values and jump to invalid memory, thus causing a #GF
- Here's the kicker: this behaviour is not consistent. Sometimes (after a few reboots), everything works as expected (no #GF). How is that possible?
This is driving me crazy because I can't find any good information on this. It's hard to debug for me because it's happening on real machines only. Tips or help or general advice is greatly appreciated (at this point, anything is appreciated )
Here is the (relavent) code:
Installing keyboard IRQ handler in IDT (C compiled with GCC on Linux)
Code: Select all
void init_keyboard()
{
register_isr_gate(0x21, (void*) isr_keyboard_handler);
}
Code: Select all
global isr_keyboard_handler
isr_keyboard_handler:
; everything left blank
iretd
Code: Select all
void enable_hardware_interrupts()
{
asm volatile(
"movb $0b11111101, %al # // 0 = enabled, 1 = disabled, irq 0 = lsb, irq 8 = msb\n"\
"out %al, $0x21 \n"\
"out %al, $0xA1 \n");
}
Code: Select all
protect_mode:
mov ax, DATA_DESC - NULL_DESC ; 0x10
mov ds, ax ; update data segment
mov ss, ax
mov es, ax
mov fs, ax
; stack will grow downard
mov ebp, 0x7C00
mov esp, 0x7C00
; setup interrupts
call reprogram_pic ; resets bios defaults for hardware interrupts, mapping them to IRD > 32
lidt [idt_descr] ; remembers the base adres and size of idt
call kernel_main ; start C kernel
hlt
Code: Select all
; reprograms the PIC such that it remaps old bios interrupts
; Taken from an early Linux Kernel
reprogram_pic:
mov al,0x11 ; initialization sequence
out 0x20,al ; send it to 8259A-1
dw 0x00eb,0x00eb ; jmp $+2, jmp $+2
out 0xA0,al ; and to 8259A-2
dw 0x00eb,0x00eb
mov al,0x20 ; start of hardware int's master pic (0x20)
out 0x21,al
dw 0x00eb,0x00eb
mov al,0x28 ; start of hardware int's 2 slave pic (0x28)
out 0xA1,al
dw 0x00eb,0x00eb
mov al,0x04 ; 8259-1 is master
out 0x21,al
dw 0x00eb,0x00eb
mov al,0x02 ; 8259-2 is slave
out 0xA1,al
dw 0x00eb,0x00eb
mov al,0x01 ; 8086 mode for both
out 0x21,al
dw 0x00eb,0x00eb
out 0xA1,al
dw 0x00eb,0x00eb
mov al,0xFF ; mask off all interrupts for now
out 0x21,al
dw 0x00eb,0x00eb
out 0xA1,al
ret
Code: Select all
extern void kernel_main()
{
.......
isr_traps_init(); // install CPU exceptions (like divide by 0, #GF, etc)
init_keyboard(); // install keyboard ISR
enable_hardware_interrupts(); // enable hardware keyboard IRQ
asm ("sti"); // enable interrupts
......
Any help or insight is greatly appreciated. I cannot find out why the iret causes a #GF only on real hardware. I'm thinking it may have something to do with the stack, but I can't see what I'm doing wrong.
Best regards