Nullplan pretty much covered it. Your idt.asm could be fixed up to look like
Code: Select all
%macro isr_err_stub 1
isr_stub_%+%1:
push %1
pusha
push gs
push fs
push es
push ds
mov eax, 0x10 ; Set Segment selectors to kernel data selectors
mov ds, eax
mov es, eax
mov fs, eax
mov gs, eax
cld
mov ebx, esp ; EBX=Pointer to ISR state
and esp, -16 ; Align stack on 16 byte boundary
sub esp, 12 ; One param to handler. Must subtract additional 12 bytes
; so that at point of calling handler the stack
; is still on 16 byte boundary
push ebx ; Param1 - Pointer to ISR state
call exception_handler
mov esp, ebx ; EBX is preserved, restore stack pointer
pop ds
pop es
pop fs
pop gs
popa
add esp, 8
iret
%endmacro
%macro isr_no_err_stub 1
isr_stub_%+%1:
push 0
push %1
pusha
push gs
push fs
push es
push ds
mov eax, 0x10 ; Set Segment selectors to kernel data selectors
mov ds, eax
mov es, eax
mov fs, eax
mov gs, eax
cld
mov ebx, esp ; EBX=Pointer to ISR state
and esp, -16 ; Align stack on 16 byte boundary
sub esp, 12 ; One param to handler. Must subtract additional 12 bytes
; so that at point of calling handler the stack
; is still on 16 byte boundary
push ebx ; Param1 - Pointer to ISR state
call exception_handler
mov esp, ebx ; EBX is preserved, restore stack pointer
pop ds
pop es
pop fs
pop gs
popa
add esp, 8
iret
%endmacro
%macro irq_stub 1
isr_stub_%+%1:
push 0
push %1
pusha
push gs
push fs
push es
push ds
mov eax, 0x10 ; Set Segment selectors to kernel data selectors
mov ds, eax
mov es, eax
mov fs, eax
mov gs, eax
cld
mov ebx, esp ; EBX=Pointer to ISR state
and esp, -16 ; Align stack on 16 byte boundary
sub esp, 12 ; One param to handler. Must subtract additional 12 bytes
; so that at point of calling handler the stack
; is still on 16 byte boundary
push ebx ; Param1 - Pointer to ISR state
call irq_handler
mov esp, ebx ; EBX is preserved, restore stack pointer
pop ds
pop es
pop fs
pop gs
popa
add esp, 8
iret
%endmacro
extern exception_handler
isr_no_err_stub 0
isr_no_err_stub 1
isr_no_err_stub 2
isr_no_err_stub 3
isr_no_err_stub 4
isr_no_err_stub 5
isr_no_err_stub 6
isr_no_err_stub 7
isr_err_stub 8
isr_no_err_stub 9
isr_err_stub 10
isr_err_stub 11
isr_err_stub 12
isr_err_stub 13
isr_err_stub 14
isr_no_err_stub 15
isr_no_err_stub 16
isr_err_stub 17
isr_no_err_stub 18
isr_no_err_stub 19
isr_no_err_stub 20
isr_no_err_stub 21
isr_no_err_stub 22
isr_no_err_stub 23
isr_no_err_stub 24
isr_no_err_stub 25
isr_no_err_stub 26
isr_no_err_stub 27
isr_no_err_stub 28
isr_no_err_stub 29
isr_err_stub 30
isr_no_err_stub 31
extern irq_handler
%assign i 32
%rep 16
irq_stub i
%assign i i+1
%endrep
; for performance i think
global isr_stub_table
isr_stub_table:
%assign i 0
%rep 48
dd isr_stub_%+i
%assign i i+1
%endrep
The lone parameter passed to the handlers is a pointer to the stack state which includes the value of the interrupt no, error code, all the segment and general purpose registers etc. your idt.h would be amended to look like:
Code: Select all
#ifndef LIB_IDT_H
#define LIB_IDT_H
#include <stdint.h>
#define IDT_SIZE 256
typedef struct registers
{
uint32_t ds, es, fs, gs; // Data segment selector
uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax; // Pushed by pusha.
uint32_t int_no, err_code; // Interrupt number and error code (if applicable)
uint32_t eip, cs, eflags, useresp, ss; // Pushed by the processor automatically.
} registers_t;
typedef void (*isr_t)(registers_t *);
typedef struct {
uint16_t offset_low; // lower 16 bits of handler func addr
uint16_t selector; // kernel segment selector
uint8_t reserved; // this musts be zero
uint8_t attributes; // type and attributes
uint16_t offset_high; // higher 16 bits of handler func addr
} __attribute__((packed)) idt_entry_t;
typedef struct {
uint16_t limit;
uint32_t base;
} __attribute__((packed)) idtr_t;
extern idt_entry_t idt[IDT_SIZE];
extern idtr_t idtr;
void set_idt_entry(uint8_t index, uintptr_t handler_addr);
void idt_init();
void exception_handler(registers_t *state);
void irq_handler(registers_t *state);
#endif
Your idt.c would look like:
Code: Select all
#include <stdbool.h>
#include <stdint.h>
#include <lib/panic.h>
#include <lib/idt.h>
#include <drivers/pic.h>
#include <drivers/vga.h>
#include <drivers/keyboard.h>
__attribute__((aligned(0x10))) idt_entry_t idt[IDT_SIZE];
idtr_t idtr;
void set_idt_entry(uint8_t index, uintptr_t handler_addr) {
idt[index].offset_low = handler_addr & 0xffff;
idt[index].selector = 0x08; // kernel code segment
idt[index].reserved = 0;
idt[index].attributes = 0x8e; // interrupt gate
idt[index].offset_high = (handler_addr >> 16) & 0xffff;
}
extern uintptr_t isr_stub_table[];
void idt_init() {
idtr.base = (uint32_t) (uint8_t*) &idt[0];
idtr.limit = (uint16_t) sizeof(idt) - 1;
for (uint8_t i = 0; i < 48; i++) {
idt_set_entry(i, isr_stub_table[i]);
}
__asm__ volatile ("lidt %0" : : "m"(idtr)); // load idt
__asm__ volatile ("sti"); // enable interrupts
}
void exception_handler(registers_t *state) {
panic(state->int_no, state->err_code);
}
void irq_handler(registers_t *state) {
if (state->int_no == 33) keyboard_input();
pic_end_int(state->int_no);
}
There is a bit of a bug in your EOI function. It should look like:
Code: Select all
void pic_end_int(uint8_t irq) {
if (irq >= 40) outb(PIC2_CMD, 0x20);
outb(PIC1_CMD, 0x20);
}