Exception handler: #GP when the exception has an error code

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
User avatar
finarfin
Member
Member
Posts: 106
Joined: Fri Feb 23, 2007 1:41 am
Location: Italy & Ireland
Contact:

Exception handler: #GP when the exception has an error code

Post by finarfin »

I spent last few days studying exception handling in 64bit modes, how to handle it, and looking at different sources how they does it.

I think i got more or less an idea of how to implement it ad drafted my own handler.

The actual problem is that if i got an exception that has no error code, the handler executes correctly and apparently also return the control back to the kernel (i tested it with some prints), but if i try to call an exception that has an error code (i.e. the page Fault) it start to generate #GP exceptions (like in a loop).

I'm not sure what i'm doing wrong. Here is the actual code:

The exception wrapper:

Code: Select all

[bits  64]
[extern interrupts_handler]

%macro interrupt_service_routine 1
[global interrupt_service_routine_%1]
interrupt_service_routine_%1:
    cli
    push 0	; since we have no error code, to keep things consistent we push a default EC of 0
    push %1 ; pushing the interrupt number for easier identification by the handler
	save_context
	mov rdi, rsp
    cld
    call interrupts_handler
	restore_context
	add rsp, 16
	sti
    iretq
%endmacro

%macro interrupt_service_routine_error_code 1
[global interrupt_service_routine_error_code_%1]
interrupt_service_routine_error_code_%1:
    cli
    push %1 ; In this case the error code is already present on the stack
	save_context
	mov rdi, rsp
    cld
    call interrupts_handler
	restore_context
	add rsp, 16
	sti
    iretq
%endmacro



%macro save_context 0
    push rax
    push rbx
    push rcx
    push rdx
    push rbp
    push rsi
    push rdi
    push r8
    push r9
    push r10
    push r11
    push r12
    push r13
    push r14
    push r15
%endmacro

%macro restore_context 0
    pop r15
    pop r14
    pop r13
    pop r12
    pop r11
    pop r10
    pop r9
    pop r8
    pop rdi
    pop rsi
    pop rbp
    pop rdx
    pop rcx
    pop rbx
    pop rax
%endmacro

interrupt_service_routine 0
interrupt_service_routine 1
interrupt_service_routine 2
interrupt_service_routine 3
interrupt_service_routine 4
interrupt_service_routine 5
interrupt_service_routine 6
interrupt_service_routine 7
interrupt_service_routine_error_code 8
interrupt_service_routine 9
interrupt_service_routine_error_code 10
interrupt_service_routine_error_code 11
interrupt_service_routine_error_code 12
interrupt_service_routine_error_code 13
interrupt_service_routine_error_code 14
interrupt_service_routine 15
interrupt_service_routine 16
interrupt_service_routine_error_code 17
interrupt_service_routine 18
As you can see the only difference is that in case of exception without error code i'm just pushing an extra value on the stack acting as default error code.

The interrupt handler:

Code: Select all

void interrupts_handler(cpu_status_t *status){
	qemu_write_string(exception_names[status->interrupt_number]);
	qemu_write_string("\n");
	if (status->interrupt_number == 14){
		qemu_write_string("Page fault called\n");
		qemu_write_string("Looping...\n");
	} else if (status->interrupt_number == 7){
		qemu_write_string("Device not available\n");
		qemu_write_string("Looping...");
	} else {
		qemu_write_string("Another exception called...");
	}
	qemu_write_string("Arriving here\n");
}
This is how every single exception is initialized:

Code: Select all

	set_idt_entry(0x00, IDT_PRESENT_FLAG | IDT_INTERRUPT_TYPE_FLAG, 0x08, 0, interrupt_service_routine_0);
	set_idt_entry(0x01, IDT_PRESENT_FLAG | IDT_INTERRUPT_TYPE_FLAG, 0x08, 0, interrupt_service_routine_1);
	set_idt_entry(0x02, IDT_PRESENT_FLAG | IDT_INTERRUPT_TYPE_FLAG, 0x08, 0, interrupt_service_routine_2);
	set_idt_entry(0x03, IDT_PRESENT_FLAG | IDT_INTERRUPT_TYPE_FLAG, 0x08, 0, interrupt_service_routine_3);
	set_idt_entry(0x04, IDT_PRESENT_FLAG | IDT_INTERRUPT_TYPE_FLAG, 0x08, 0, interrupt_service_routine_4);
	set_idt_entry(0x05, IDT_PRESENT_FLAG | IDT_INTERRUPT_TYPE_FLAG, 0x08, 0, interrupt_service_routine_5);
	set_idt_entry(0x06, IDT_PRESENT_FLAG | IDT_INTERRUPT_TYPE_FLAG, 0x08, 0, interrupt_service_routine_6);
	set_idt_entry(0x07, IDT_PRESENT_FLAG | IDT_INTERRUPT_TYPE_FLAG, 0x08, 0, interrupt_service_routine_7);
	set_idt_entry(0x08, IDT_PRESENT_FLAG | IDT_INTERRUPT_TYPE_FLAG, 0x08, 0, interrupt_service_routine_error_code_8);
	set_idt_entry(0x09, IDT_PRESENT_FLAG | IDT_INTERRUPT_TYPE_FLAG, 0x08, 0, interrupt_service_routine_9);
	set_idt_entry(0x0A, IDT_PRESENT_FLAG | IDT_INTERRUPT_TYPE_FLAG, 0x08, 0, interrupt_service_routine_error_code_10);
	set_idt_entry(0x0B, IDT_PRESENT_FLAG | IDT_INTERRUPT_TYPE_FLAG, 0x08, 0, interrupt_service_routine_error_code_11);
	set_idt_entry(0x0C, IDT_PRESENT_FLAG | IDT_INTERRUPT_TYPE_FLAG, 0x08, 0, interrupt_service_routine_error_code_12);
	set_idt_entry(0x0D, IDT_PRESENT_FLAG | IDT_INTERRUPT_TYPE_FLAG, 0x08, 0, interrupt_service_routine_error_code_13);
	set_idt_entry(0x0E, IDT_PRESENT_FLAG | IDT_INTERRUPT_TYPE_FLAG, 0x08, 0, interrupt_service_routine_error_code_14);
	set_idt_entry(0x0F, IDT_PRESENT_FLAG | IDT_INTERRUPT_TYPE_FLAG, 0x08, 0, interrupt_service_routine_15);
	set_idt_entry(0x10, IDT_PRESENT_FLAG | IDT_INTERRUPT_TYPE_FLAG, 0x08, 0, interrupt_service_routine_16);
	set_idt_entry(0x11, IDT_PRESENT_FLAG | IDT_INTERRUPT_TYPE_FLAG, 0x08, 0, interrupt_service_routine_error_code_17);
	set_idt_entry(0x12, IDT_PRESENT_FLAG | IDT_INTERRUPT_TYPE_FLAG, 0x08, 0, interrupt_service_routine_18);
To test the interrupts in the main function of the kernel, i'm manually firing an exception with asm("int $XX"), but as said above if the exception has no error code it went through the exception, and printed the text just below, otherwise it go through the exception then it start to fire #GP exceptions.

But looking at the code in other projects i'm doing more or less the same. What i'm doing wrong?

Btw i also have few more questions about interrupt handling in 64 bit mode:
1. I was reading about the RED zone at this stage i'm not sure if i need it or not, btw i have tried to disable/enable it, and i see the exact same behaviour.
2. I saw several projects passing the stack as parameter (at the moment i did the same) but i'm wondering if i can pass just the minimum information needed (in this case error code and interrupt number), in my previous 32 bit OS i did this way. In case i go for this option i just need to put the values in rdi, and rsi ?
3. I saw again several project returning the pointer to the stack once the interrupt is handled, and moving it back to the stack in the asm wrapper, while i saw other not doing it, is that really necessary?

EDIT 1:
I tried to print the value of the error code in the GP Exception and the value is 4 not sure if this helpful!
Elen síla lúmenn' omentielvo
- DreamOS64 - My latest attempt with osdev: https://github.com/dreamos82/Dreamos64
- Osdev Notes - My notes about osdeving! https://github.com/dreamos82/Osdev-Notes
- My old Os Project: https://github.com/dreamos82/DreamOs
Octocontrabass
Member
Member
Posts: 5568
Joined: Mon Mar 25, 2013 7:01 pm

Re: Exception handler: #GP when the exception has an error c

Post by Octocontrabass »

finarfin wrote:To test the interrupts in the main function of the kernel, i'm manually firing an exception with asm("int $XX"),
The INT instruction does not push an error code.
finarfin wrote:1. I was reading about the RED zone at this stage i'm not sure if i need it or not, btw i have tried to disable/enable it, and i see the exact same behaviour.
You need to disable the red zone.
finarfin wrote:2. I saw several projects passing the stack as parameter (at the moment i did the same) but i'm wondering if i can pass just the minimum information needed (in this case error code and interrupt number), in my previous 32 bit OS i did this way. In case i go for this option i just need to put the values in rdi, and rsi ?
You need to know what the CPU was doing in order to handle an exception. You will not be able to tell what the CPU was doing with only an error code and an interrupt number.
finarfin wrote:3. I saw again several project returning the pointer to the stack once the interrupt is handled, and moving it back to the stack in the asm wrapper, while i saw other not doing it, is that really necessary?
Maybe. It depends on how the rest of your kernel works.
User avatar
finarfin
Member
Member
Posts: 106
Joined: Fri Feb 23, 2007 1:41 am
Location: Italy & Ireland
Contact:

Re: Exception handler: #GP when the exception has an error c

Post by finarfin »

Well then i suppose that teh problem is using the INT instruction!

Btw i trioed to raiose a #PF accessing a non mapped memory area, and now it is looping into page faults.
But in this case i suppose it is ok, because, since at the moment i'm not handling the page faultm once i return from the interurpt routine the eip is still pointing to the "offending" instruction, that of course is gonna to fire up the #PF again.

But i suppose this is expected (please correct if i'm wrong).

Thanks for the other answers.
Elen síla lúmenn' omentielvo
- DreamOS64 - My latest attempt with osdev: https://github.com/dreamos82/Dreamos64
- Osdev Notes - My notes about osdeving! https://github.com/dreamos82/Osdev-Notes
- My old Os Project: https://github.com/dreamos82/DreamOs
Post Reply