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!