Page 1 of 1

PIT not triggering interupt

Posted: Mon Jul 16, 2018 11:32 pm
by lambdaislove
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
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: Select all

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: Select all

.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: Select all

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: Select all

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: Select all

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);
}

Re: PIT not triggering interupt

Posted: Tue Jul 17, 2018 12:25 am
by lambdaislove
Ah well I made some progress by randomlly trying things out

1) I was following bare-bones initially and thus a) had hardware interrupts disabled it would seem and
2) I wasn't looping over a halt at the end like I should have been. Consequently, I was disabling interrupts too soon to see anything.

the PIT is now triggering my desired function correctly!