Page 1 of 1

Incorrect Stack on IRET in IRQ0 Handler

Posted: Tue Aug 09, 2016 9:05 pm
by jon002
So I am currently trying to re-enable interrupts in 32-bit mode, and I have run into a weird error that should be easily fixed.

I have currently set up my GDT and IDT in 32-bit mode, mapped IRQs 0-15 to IVT entries 32-47, and enabled both the 8259A PIC and 8253 PIT, enabling interrupts with "sti" afterwards.

However, upon the PIT firing IRQ0, the OS generates a General Protection Fault (IR 13). I have been debugging with both GDB and Bochs and I believe the issue is related to an incorrect stack, which should be easily solvable. When an IRQ that does not generate an error code is fired (such as IRQ0) EFLAGS, CS, then EIP is pushed to the stack. If an error code is fired, EFLAGS, CS, EIP, and the 32-bit error code is pushed.

In C, I have been using the following method to update ESP and to read the values from the stack before calling IRET:

Code: Select all

asm("addl $12, %esp);

// ... Handle interrupt here

int eip;
int cs;
int eflags;

asm("movl (%%esp), %0" : "=r"(eip));
asm("movl 4(%%esp), %0" : "=r"(cs));
asm("movl 8(%%esp), %0" : "=r"(eflags));

// ... Print integers here

asm("iretl");
(If an error code is generated, I also get that with movl 16(%%esp)).

I have checked the stack with bochs debugger (I can put a "for (;;) { asm("hlt"); } to halt the system) and I have found that it is not set up correctly.

Because I am still in Ring 0, the processor should use the current stack. But I have found 0x00000008 (my CS value, or at least what I think it is) to be offset 20-42 bytes from ESP instead of 4 bytes when handling an interrupt, and the location of different values in the stack also change frequently. I have looked through my code, and I am unable to figure out why in the world this is happening. It happens in QEMU, Bochs, and VirtualBox.

My GDT is set up correctly (Triple-checked it). IDT is also set up correctly and IRQs 0-15 are correctly mapped to 32-47 IRs. PIT and PIC are initialized correctly. Assembly code should be great for what I want it to do. I have researched the stack, even widely switch the values (from "movl 4(%%esp)" to "movl -4(%%esp)"), or adjusted ESP, but nothing seems to work. I have googled all over this site and the internet (looked as OSDev.org, BrokenThorn.com, even articles from .edu sites) but I cannot make heads or tails out of the problem. If there's a problem with the code, or it's clear for some reason that I don't fully understand the stack, please let me know. Any help possible would be greatly appreciated!

My source code can be found on GitHub here: https://github.com/jaller200/DarkOS. Thanks in advance for any guidance or help!

Re: Incorrect Stack on IRET in IRQ0 Handler

Posted: Tue Aug 09, 2016 10:28 pm
by BrightLight
Very common mistake: the IRQ handler wrapper/dispatcher (whatever you want to call it) must be written in assembly.
Something like this, for example (Intel syntax, I can't do much in gas):

Code: Select all

irq0_dispatcher:
	pusha
	extern irq_c_handler
	call irq0_c_handler    ; your code written in C
	popa
	iret

Re: Incorrect Stack on IRET in IRQ0 Handler

Posted: Wed Aug 10, 2016 12:17 pm
by jon002
omarrx024 wrote:Very common mistake: the IRQ handler wrapper/dispatcher (whatever you want to call it) must be written in assembly.
Something like this, for example (Intel syntax, I can't do much in gas):

Code: Select all

irq0_dispatcher:
	pusha
	extern irq_c_handler
	call irq0_c_handler    ; your code written in C
	popa
	iret
Thanks so much! IRQs are working perfectly now. Rookie mistake - won't happen again.

Also, after you told me that and I fixed the problem, I found this as I knew what I was looking for: http://wiki.osdev.org/Interrupt_Service_Routines. #-o