For some reason, I can't seem to get my timer interrupt to trigger. I'm following James Molloy's tutorial and trying to keep in mind the standard known issues listed on the wiki. It might just be some dumb C bug but I can't seem to find it.
I expect this code to print out a ton of "Tick" messages but instead, it just prints out the following
Quote:
setting up timer
registering interrupt handler
Unhandled Interupt: 0x03
Unhandled Interupt: 0x04
The first two show that I made it past the timer init. The fact that printing works (even for manually triggered interrupts) and that I should be printing "irq hit" if I was getting to irq_handle indicates to me that I'm not managing to get into irq0 like I wanted. I'm not particularlly sure why I'm not getting there though. Any idea what I might be doing wrong?
(Chunks of my code that I think are relevant posted below)
kernel_main
Code:
void kernel_main(void) {
gdt_init();
idt_init();
t_init();
timer_init(100);
asm volatile ("int $0x3");
asm volatile ("int $0x4");
}
The relevant assembly for irq1-15
Code:
.global irq_handler
.type irq_handler, @function
irq_common_stub:
pusha
mov %ds, %ax
push %eax
push %esp
mov $0x10, %ax
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
call irq_handler
add $0x04, %esp
pop %eax
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
popa
add $0x08,%esp
iret
.macro make_iqr n irqn
.global irq\n
.type irq\n, @function
irq\n:
cli
push $0
push $\irqn
jmp irq_common_stub
.endm
make_iqr 0,32
...
make_iqr 15,47
isr_init
Code:
void idt_init() {
idt_ptr.limit = sizeof(idt_entry_t) * 256 - 1;
idt_ptr.base = (uint32_t)&idt_entries;
kmemset((void *)&idt_entries, 0, sizeof(idt_entry_t) * 256);
kmemset((void *)&interrupt_handlers, 0, sizeof(isr_t) * 256);
// Remap the irq tables
outb(0x20, 0x11);
outb(0xA0, 0x11);
outb(0x21, 0x20);
outb(0xA1, 0x28);
outb(0x21, 0x04);
outb(0xA1, 0x02);
outb(0x21, 0x01);
outb(0xA1, 0x01);
outb(0x21, 0x0);
outb(0xA1, 0x0);
idt_set_gate(0, (uint32_t)isr0, 0x08, 0x8E);
...
idt_set_gate(32, (uint32_t)irq0, 0x08, 0x8E);
idt_set_gate(33, (uint32_t)irq1, 0x08, 0x8E);
idt_set_gate(34, (uint32_t)irq2, 0x08, 0x8E);
idt_set_gate(35, (uint32_t)irq3, 0x08, 0x8E);
idt_set_gate(36, (uint32_t)irq4, 0x08, 0x8E);
idt_set_gate(37, (uint32_t)irq5, 0x08, 0x8E);
idt_set_gate(38, (uint32_t)irq6, 0x08, 0x8E);
idt_set_gate(39, (uint32_t)irq7, 0x08, 0x8E);
idt_set_gate(40, (uint32_t)irq8, 0x08, 0x8E);
idt_set_gate(41, (uint32_t)irq9, 0x08, 0x8E);
idt_set_gate(42, (uint32_t)irq10, 0x08, 0x8E);
idt_set_gate(43, (uint32_t)irq11, 0x08, 0x8E);
idt_set_gate(44, (uint32_t)irq12, 0x08, 0x8E);
idt_set_gate(45, (uint32_t)irq13, 0x08, 0x8E);
idt_set_gate(46, (uint32_t)irq14, 0x08, 0x8E);
idt_set_gate(47, (uint32_t)irq15, 0x08, 0x8E);
idt_flush((uint32_t)&idt_ptr);
}
some other irq related functions form the tutorial
Code:
void isr_handler(registers_t *regs) {
if (interrupt_handlers[regs->int_no])
interrupt_handlers[regs->int_no](regs);
else {
term_t *term = t_curterm();
static const char *lookup = "0123456789ABCDEF";
t_print(term, "\n\nUnhandled interrupt: 0x");
t_putchar(term, lookup[(regs->int_no >> 4) & 0xF]);
t_putchar(term, lookup[regs->int_no & 0xF]);
t_print(term, "\n");
}
}
void register_interrupt_handler(uint8_t n, isr_t h) {
t_print(t_curterm(), "registering interrupt handler\n");
interrupt_handlers[n] = h;
}
void irq_handler(registers_t *regs);
void irq_handler(registers_t *regs) {
t_print(t_curterm(), "irq hit\n");
if (regs->int_no >= 40) {
outb(0xA0, 0x20);
}
outb(0x20, 0x20);
if (interrupt_handlers[regs->int_no] != 0)
interrupt_handlers[regs->int_no](regs);
}
and finally the timer code from the tutorial (as I've written/copied it)
Code:
static void timer_callback(registers_t* regs) {
term_t *term = t_curterm();
t_print(term, "Tick\n");
tick++;
}
void timer_init(uint16_t frequency) {
t_print(t_curterm(), "setting up timer\n");
// Firstly, register our timer callback.
register_interrupt_handler(IRQ0, &timer_callback);
// The value we send to the PIT is the value to divide it's input clock
// (1193180 Hz) by, to get our required frequency. Important to note is
// that the divisor must be small enough to fit into 16-bits.
uint32_t divisor = 1193180 / frequency;
// Send the command byte.
outb(0x43, 0x36);
// Divisor has to be sent byte-wise, so split here into upper/lower bytes.
uint8_t l = (uint8_t)(divisor & 0xFF);
uint8_t h = (uint8_t)((divisor >> 8) & 0xFF);
// Send the frequency divisor.
outb(0x40, l);
outb(0x40, h);
}