Page 1 of 1

PIT does not call IRQ Routine

Posted: Mon Dec 28, 2020 3:09 pm
by micheben
Hey,

I need some help with correctly setting up the PIT and I'm hitting my head against this wall since several days now.

I compile my kernel with a gcc cross compiler (i686-elf-gcc) and I'm using the qemu emulator. I used the "Meaty Skeleton" as a starting point.

The Interrupt itself is working correctly. I've added

Code: Select all

__asm__ __volatile__ ("int $0x20");
in my kernel_main to test it.

Here is what I do to set up the timer (all in GNU Assembly dialect):
First (bevor setting up the IDT) I remap the IRQ Ports:

Code: Select all

.global PIC_remap

PIC_remap:
    # tell both PIC that a reinit will come now
    mov $0x11, %al
    outb %al, $0x20
    outb %al, $0xA0

    # tell both PICs their Interrupt Number Offset
    mov $0x20, %al
    outb %al, $0x21
    mov $0x28, %al
    outb %al, $0xA1

    # tell Master PIC that there is a slave on port 2 (0000 0100)
    mov $0x04, %al
    outb %al, $0x21

    # tell Slave PIC its cascade identity (0000 0010)
    mov $0x02, %al
    outb %al, $0xA1

    # activate 8086/88 (MCS-80/85) mode on both PIC
    mov $0x01, %al
    outb %al, $0x21
    outb %al, $0xA1

    # unmask/activate all IRQs
    mov $0x0, %al
    outb %al, $0x21
    outb %al, $0xA1
As I understood it, this should map IRQ0 (i.e. the PIT) to ISR32 (or ISR 0x20).

Then, I initialize the PIT (partly C, partly Assembly):

Code: Select all

// in irq.c
extern void send_divisior_to_PID(uint32_t);
void init_irq0_timer(uint32_t frequency) {
	// 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_divisior_to_PID(divisor);
}

// in irq_asm.S
.global send_divisior_to_PID
send_divisior_to_PID:
    cli

    mov $0x34, %al                  #channel 0, lobyte/hibyte, rate generator
    outb %al, $0x43

    mov 4(%esp), %eax                   # set EDA to 1st argument - the divisior
    outb %al,$0x40                       #Set low byte of PIT reload value

    mov %ah,%al                         #ax = high 8 bits of reload value
    outb %al,$0x40                       #Set high byte of PIT reload value

    # unmask/activate all IRQs
    mov $0x0, %al
    outb %al, $0x21
    outb %al, $0xA1

// in boot.S
...
	push $100             # 100Hz, i.e. 100 times per Second or every 10 ms
	call init_irq0_timer
	add $0x04, %esp   # Stack Cleanup
...
My Plan was to use a simplified version of https://wiki.osdev.org/Programmable_Interval_Timer (partly inspired by James Molloy's Tutorial).

However, the interrupt is not called by the timer.

I added

Code: Select all

static uint32_t i = 0;
...
	for (;;) {
		++i;
		if (i == 0)
			printf("overflow\n");
	}
at the end of my current kernel main to ensure that my kernel is actually still running...

Re: PIT does not call IRQ Routine

Posted: Mon Dec 28, 2020 7:43 pm
by xeyes
code seems all right

Have you tried to:

run with qemu -d int to observe interrupt delivery to the CPU (only need to look at the first part, <counter>: v=<vector>)

install a catch all handler on all vectors just in case? or better yet different handlers on each one.

lastly, you didn't forget to unmask interrupts on the CPU (sti) right?

Re: PIT does not call IRQ Routine

Posted: Tue Dec 29, 2020 2:37 am
by micheben
Thanks for your help.

It was actually a series of errors:

Previously, I missed a

Code: Select all

0x
at the IDT Setup, but i found that error while going through my code. I also added a

Code: Select all

cli
inside the PIT Setup at the same instance (found it on the osdev wiki example), and my

Code: Select all

sti
was to early after that change.

PS: I actually thought that "manual" interrupts (i.e.

Code: Select all

int $0x20
) only works, if interrupts are enabled, so I didn't think about checking this flag again.