Page 1 of 1

Only one PIT IRQ

Posted: Sat Oct 26, 2013 7:56 pm
by gsingh2011
I just set up IRQs and the PIT, but I only see one IRQ from the PIT. After the IRQ, I see that the IF bit is not set in EFLAGS. Shouldn't iret set the IF bit back to one? I tried adding sti before iret, but that didn't work either. Here's my IRQ code:

Code: Select all

; First parameter is IRQ number, second is IDT entry number
%macro IRQ 2
    global irq%1
    irq%1:
        cli
        push 0
        push %2
        jmp irq_common_stub
%endmacro

IRQ 0, 32
IRQ 1, 33
IRQ 2, 34
IRQ 3, 35
IRQ 4, 36
IRQ 5, 37
IRQ 6, 38
IRQ 7, 39
IRQ 8, 40
IRQ 9, 41
IRQ 10, 42
IRQ 11, 43
IRQ 12, 44
IRQ 13, 45
IRQ 14, 46
IRQ 15, 47

extern irq_handler ; Defined in irq.c

irq_common_stub:
    pusha              ; Push all general purpose registers

    push ds            ; Push all data segment registers
    push es
    push fs
    push gs

    mov ax, 0x10       ; Load the kernel data segment descriptor
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax

    call irq_handler   ; Handle the IRQ

    pop gs             ; Restore all regs modified in this function
    pop fs
    pop es
    pop ds

    popa
    add esp, 8         ; Cleans up the stack
    iret               ; Return from interrupt
On the screen I see the output from one clock tick, meaning the IRQ happens once. How can I re-enable interrupts after each IRQ?

Re: Only one PIT IRQ

Posted: Sat Oct 26, 2013 8:25 pm
by piranha
You need to send an ACK back to the PICs (clicky).

Yes, IRET should re-set the flag back to what it was previously, but you need to tell the PICs that its okay to send interrupts again.

Re: Only one PIT IRQ

Posted: Sat Oct 26, 2013 8:36 pm
by gsingh2011
I forgot to post the source for irq_handler(). Is this what you're talking about?

Code: Select all

#define MASTER_PIC_COMMAND_PORT 0x20
#define SLAVE_PIC_COMMAND_PORT  0xA0

void irq_handler(interrupt_stack_frame_t frame) {
    if (frame.interrupt_number > 40) {
        // Reset slave
        outb(SLAVE_PIC_COMMAND_PORT, 0x20);
    }

    // Reset master
    outb(MASTER_PIC_COMMAND_PORT, 0x20);

    if (interrupt_handlers[frame.interrupt_number] != NULL) {
        interrupt_handler *handler = interrupt_handlers[frame.interrupt_number];
        handler(frame);
    }
}

Re: Only one PIT IRQ

Posted: Mon Oct 28, 2013 10:51 am
by gsingh2011
Does anyone have any ideas on how to debug this? Is there any way to query the PIC to see if it's properly reset?

Re: Only one PIT IRQ

Posted: Mon Oct 28, 2013 2:28 pm
by xenos
The best way is to use Bochs and to enable debug messages for the PIC. Then it tells you in its log file everything the PIC does, including its reset.

Re: Only one PIT IRQ

Posted: Mon Oct 28, 2013 3:01 pm
by gsingh2011
Thanks for the help. Your response prompted me to see if there was similar functionality in QEMU, and I found that I could type 'info pic' at the QEMU console to get some useful information.

Before the PIT interrupt (IRQ0), IRR = 00 and ISR = 00. After the interrupt IRR = 01 and ISR = 00.

Wikipedia states:
The IRR specifies which interrupts are pending acknowledgement, and is typically a symbolic register which can not be directly accessed. The ISR register specifies which interrupts have been acknowledged, but are still waiting for an End Of Interrupt (EOI).
So what does the fact that ISR still equals 0 mean? That the IRQ hasn't been acknowledged or has been acknowledged and is waiting for an EOI?

Re: Only one PIT IRQ

Posted: Tue Oct 29, 2013 12:02 am
by xenos
The first - it means that the CPU hasn't acknowledged the IRQ yet, i.e., it has not started processing the interrupt. As soon as your IRQ handler starts it will acknowledge the IRQ (this is done automatically, you don't need code for that), after that you should have IRR=0 and ISR=1 (since the IRQ is now "in service"). Finally you need to send EOI, so this will clear the ISR.

Re: Only one PIT IRQ

Posted: Tue Oct 29, 2013 10:58 pm
by gsingh2011
I noticed that if I add a delay after the reset it actually works fine. For example,

Code: Select all

#define MASTER_PIC_COMMAND_PORT 0x20
#define SLAVE_PIC_COMMAND_PORT  0xA0

void irq_handler(interrupt_stack_frame_t frame) {
    if (frame.interrupt_number > 40) {
        // Reset slave
        outb(SLAVE_PIC_COMMAND_PORT, 0x20);
    }

    // Reset master
    outb(MASTER_PIC_COMMAND_PORT, 0x20);
    int i = 10000000;
    while (i--);

    if (interrupt_handlers[frame.interrupt_number] != NULL) {
        interrupt_handler *handler = interrupt_handlers[frame.interrupt_number];
        handler(frame);
    }
}
Notice the while loop. If I remove that I only see one PIT tick, otherwise I see the PIT continuously ticking. Obviously, this isn't a proper solution to the problem, but it might help diagnose the problem. Any ideas?

Re: Only one PIT IRQ

Posted: Wed Oct 30, 2013 3:36 pm
by Nable
> 0x20
This is not reset, it's an EOI (end of interrupt [handling]) command. You should send it at the _end_ of the handler.
http://wiki.osdev.org/PIC#End_of_Interrupt

Re: Only one PIT IRQ

Posted: Wed Oct 30, 2013 3:56 pm
by gsingh2011
I had already tried that, but it doesn't work. With the while loop (to cause a delay), it works, but again, that's not a solution to the problem.

I've tried to go through different scenarios of how getting interrupts at different states of the IRR and ISR registers could cause problems, but I haven't found anything problematic.

Re: Only one PIT IRQ

Posted: Wed Oct 30, 2013 4:17 pm
by Nable
> I had already tried that, but it doesn't work.
Could you clarify what do you mean under `that', please? Moving EOI to the end of interrupt handler from it's beginning?

> With the while loop (to cause a delay), it works
It seems to me that with this delay you are waiting until interrupt fires for the second time after the early EOI.
I didn't try debugging full version of your code, so that's just an assumption, sorry.

Re: Only one PIT IRQ

Posted: Wed Oct 30, 2013 4:51 pm
by gsingh2011
Yea, I meant move the EOI command to the end of the IRQ handler. In other words (I've changed the command sending interface around a bit, but it does the same thing),

Code: Select all

void irq_handler(interrupt_stack_frame_t frame) {
    ASSERT_INTERRUPTS_DISABLED();

    if (interrupt_handlers[frame.interrupt_number] != NULL) {
        interrupt_handler *handler = interrupt_handlers[frame.interrupt_number];
        handler(frame);
    }

    if (frame.interrupt_number > 40) {
        // Reset slave
        PIC_send_command(&slave, 0x20);
    }

    // Reset master
    PIC_send_command(&master, 0x20);
    int i = 10000000;
    while (i--);
}
When irq_handler starts and right before the EOI is sent, IRR = 0 and ISR = 1. When the EOI is sent, IRR = 0 and ISR = 0. After the while loop delay, IRR = 1 and and ISR = 0. So yes, the while loop essentially waits for the next interrupt before letting the IRQ handler finish. But I don't understand why it doesn't work without this.

Re: Only one PIT IRQ

Posted: Fri Nov 01, 2013 7:32 pm
by gsingh2011
I have no idea what to do and I'm completely stuck on this issue. I've pushed all my code here: https://github.com/gsingh93/edos/tree/pic.

Can someone take a look? Important files are irq.c, irq_asm.s, and pit.c. Any help is appreciated.

Re: Only one PIT IRQ

Posted: Fri Nov 01, 2013 8:02 pm
by Brendan
Hi,
gsingh2011 wrote:Can someone take a look? Important files are irq.c, irq_asm.s, and pit.c. Any help is appreciated.
I'd assume that the problem has nothing to do with IRQs, the PIC or the PIT; and that the code simply crashes and/or goes into an infinite loop after initialising things.

I'm also curious about the "return 0;" at the end of "kmain()" - why is it returning at all, and what is it returning to?

As far as I can tell, it returns to the code in "loader.s", which does CLI and then goes into an infinite loop...


Cheers,

Brendan

Re: Only one PIT IRQ

Posted: Fri Nov 01, 2013 8:25 pm
by gsingh2011
I'm an idiot. A complete idiot. Thank you for the help. I take it that the reason the delay fixed the issue was there was always an interrupt to service, so the timer_callback was always called, and the code never got a chance to return and halt.