Page 1 of 1

ISR GPF Loop, IRQs Fine...

Posted: Wed May 28, 2008 9:17 am
by dega512
My title pretty much gives away my problem. Whenever my general ISR handler is called, it ends up causing a GPF (then when when it is called for the GPF, it causes a GPF, and thus the GPF cycle starts). I looked around the forums and I noticed other people had inifite GPF loops and an eventual triple fault/video corruption (probably from some sort of stack overrun would be my guess), but none of the problems were exactly like mine (a GPF is caused at the end of the ISR handler).

Here is my asm handler for ISRs:

Code: Select all

; This macro creates a 'stub' for an ISR that does not
; push its own error code, a dummy error code (0) is used instead.
; NOTE: The general handler expects the interrupt number
; and error code on the stack.
%macro ISR_STUB_NOERRORCODE 1
	[GLOBAL isr%1]
	isr%1:
		cli					; disable interrupts
		push 0				; push a dummy error code
		push dword %1		; push the interrupt number
		jmp common_stub_isr	; use the common ISR handler to handle this ISR
%endmacro

; This macro creates a 'stub' for an ISR that pushes its own error code.
; NOTE: The general handler expects the interrupt number
; and error code on the stack.
%macro ISR_STUB 1
	[GLOBAL isr%1]
	isr%1:
		cli					; disable interrupts
		push dword %1		; push the interrupt number
		jmp common_stub_isr	; use the common ISR handler to handle this ISR
%endmacro

...

; set up the ISR/IRQ stubs
ISR_STUB_NOERRORCODE	0
ISR_STUB_NOERRORCODE	1

...

; a C function that writes a string to the console
[EXTERN debugconsole_writeline]

fmt: db 'AD', 0  ; Acronym for 'Assembly-Done', I print this out for debugging...

; Common ISR stub that saves all needed information to the stack then calls
; the C handler.
common_stub_isr:
	pusha                    ; pushes edi, esi, ebp, esp, ebx, edx, ecx, eax
	
	xor eax, eax
	mov ax, ds               ; save the data segment descriptor
	push eax                 ; ...

	; The kernel data segment is the 3rd gdt entry (index 2),
	; each of which is 8 bytes.  Thus its offfset is (2 * 8) = 16
	; which is 0x10 in hex.
	;
	; We need to update all of the segment registers to reflect this.
	; 
	; For now, these registers are always 0x10 (this is setup when
	; the GDT is created, and hasn't been touched since; which
	; technically makes this code redundant).
	mov ax, 0x10
	mov ds, ax
	mov es, ax
	mov fs, ax
	mov gs, ax
	
	call IsrHandler	; call C code
	
	; print a notification to the screen saying we got this far
	; NOTE: This does result in 'AD' being printed to the screen.
	push fmt
	call debugconsole_writeline
	add esp, 4
	
	pop eax		; reload the original segment descriptors
	mov ds, ax
	mov es, ax
	mov fs, ax
	mov gs, ax
	
	; print a notification to the screen saying we got this far
	; Starting here, AD is no longer printed (which is why
	; I have come to believe the code above is the culprit)
	push fmt
	call debugconsole_writeline
	add esp, 4
	
	popa		; pops edi, esi, ebp, esp, eb, edx, ec, eax
	add esp, 8	; cleans up the pushed error code and ISR number
	
	; print a notification to the screen saying we got this far
	push fmt
	call debugconsole_writeline
	add esp, 4
	
	sti		; enable interrupts
	
	iret		; pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP

I would expect three sets of the string 'AD' to be printed on the screen, but that isn't the case, I only get one.

So my guess is that my problem lies in:

Code: Select all

	pop eax		; reload the original data segment descriptor
	mov ds, ax
	mov es, ax
	mov fs, ax
	mov gs, ax
Although to me I don't see why this could cause an error.... Also, an interesting thing to note is that my general IRQ handler uses almost identical code (the difference in them is that they call different C functions, and the IRQ function sends the EOI signal to the PICs), yet it doesn't have this problem....

I would greatly appreciate it if someone could send me in the right direction as far as where to hunt some bugs!

Thanks!

Posted: Wed May 28, 2008 1:33 pm
by Combuster
I invite you to grab bochs' debugger and find the exact instruction that is causing your problem. You may find the bug in the process as well.

Posted: Sat May 31, 2008 1:19 pm
by dega512
Ok after some debugging with Qemu and GDB (I gave up on compiling Bochs with debugging support, I got way too many errors; but I have to say I love that I now have a debugger of some sort :) ) I've discovered that my GPF occurs at:

Code: Select all

...

call IsrHandler   ; call C code 

...

pop eax      ; reload the original segment descriptors   <-- value of EAX after this is 0x10
mov ds, ax  <-- GPF
mov es, ax
mov fs, ax
mov gs, ax 
What I don't understand is that there is code above it that is nearly identical (shown below) that doesn't cause a GPF....

Code: Select all

; The kernel data segment is the 3rd gdt entry (index 2),
; each of which is 8 bytes.  Thus its offfset is (2 * 8) = 16
; which is 0x10 in hex.
;
; We need to update all of the segment registers to reflect this.
;
; For now, these registers are always 0x10 (this is setup when
; the GDT is created, and hasn't been touched since; which
; technically makes this code redundant).
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax 
My flags byte for my IDT entries are 10001110 (Present, Ring Level 0) and have the kernel code selector 0x8, so I don't think my GPF has to do with the privileges of my code (especially since the almost identical code doesn't cause a GPF).

Why might the "mov ds, ax" that I marked cause a GPF?

Posted: Sat May 31, 2008 1:53 pm
by suthers
Do you know what the content of your ax register is when you move it to ds, It looks like you probably have a problem with your stack frame and that your popping the wrong value into ax....
Make sure you have a pop for every push (I'm re-doing my isr right now so I'm experiencing similar problems... )
Jules

Posted: Sat May 31, 2008 3:15 pm
by jnc100
In theory, as you've pushed eax before the C function and pop it immediately afterwards, then there shouldn't be a problem unless the C function messes up you stack. One way it could do this is if the function IsrHandler expects arguments and using a calling convention where the callee does stack clean-up.

As a side issue, you don't need to do an sti before an iret (look up interrupt and trap gates in the intel manuals).

Regards,
John.

Posted: Sat May 31, 2008 3:46 pm
by suthers
To jnc100:
That's exactly what I was thinking, I suspect that one of the functions he called messed up his stack...
Jules