Exception handler: #GP when the exception has an error code
Posted: Fri Mar 05, 2021 5:39 pm
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:
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:
This is how every single exception is initialized:
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!
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
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");
}
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);
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!