I'm encountering a double fault in my kernel when loading the Interrupt Descriptor Table (IDT). The interrupt handler outputs the following error:
Got Signal: Double Fault (4144)
Since a double fault should always have an error code of 0, the non-zero value 4144 suggests there may be issues with my ISR routines.
Relevant Code
kinit.S
[extern kmain]
[extern kernel_stack]
[global _start]
KERNEL_STACK_SIZE equ 4096
DATA_SEG equ 0x10
section .kinit
_start:
mov ax, DATA_SEG
mov ds, ax
mov es, ax
mov ss, ax
lea ebp, [kernel_stack + KERNEL_STACK_SIZE]
mov esp, ebp
cld
call kmain
jmp $
kmain.c
#include <stddef.h>
#include <stdint.h>
#include "idt.h"
#include "vga_screen.h"
#define KERNEL_STACK_SIZE 4096
__attribute__((aligned(16))) uint8_t kernel_stack[KERNEL_STACK_SIZE];
void kmain() {
clear_screen();
init_idt();
for (;;);
}
isr.S
[extern irq_handler]
[global isr0]
[global isr1]
[global isr2]
[global isr3]
[global isr4]
[global isr5]
[global isr6]
[global isr7]
[global isr8]
[global isr9]
[global isr10]
[global isr11]
[global isr12]
[global isr13]
[global isr14]
[global isr15]
[global isr16]
[global isr17]
[global isr18]
[global isr19]
[global isr20]
[global isr21]
[global isr22]
[global isr23]
[global isr24]
[global isr25]
[global isr26]
[global isr27]
[global isr28]
[global isr29]
[global isr30]
[global isr31]
isr_common_stub:
pusha
mov ax, ds
push eax
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
cld
lea eax, [esp]
push eax
call irq_handler
add esp, 4
pop eax
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
popa
add esp, 8
iret
%macro isr_with_err 1
isr%+%1:
push %1
jmp isr_common_stub
%endmacro
%macro isr_no_err 1
isr%+%1:
push 0
push %1
jmp isr_common_stub
%endmacro
; Division Error
isr_no_err 0
; Debug
isr_no_err 1
; Non-Maskable Interrupt
isr_no_err 2
; Breakpoint
isr_no_err 3
; Overflow
isr_no_err 4
; Bound Range Exceeded
isr_no_err 5
; Invalid Opcode
isr_no_err 6
; Device Not Available
isr_no_err 7
; Double Fault
isr_with_err 8
; Coprocessor Segment Overrun (Deprecated)
isr_no_err 9
; Invalid TSS
isr_with_err 10
; Segment Not Present
isr_with_err 11
; Stack-Segment Fault
isr_with_err 12
; General Protection Fault
isr_with_err 13
; Page Fault
isr_with_err 14
; Reserved
isr_no_err 15
; x87 Floating-Point Exception
isr_no_err 16
; Alignment Check
isr_with_err 17
; Machine Check
isr_no_err 18
; SIMD Floating-Point Exception
isr_no_err 19
; Virtualization Exception
isr_no_err 20
; Control Protection Exception
isr_with_err 21
; Reserved
isr_no_err 22
; Reserved
isr_no_err 23
; Reserved
isr_no_err 24
; Reserved
isr_no_err 25
; Reserved
isr_no_err 26
; Reserved
isr_no_err 27
; Hypervisor Injection Exception
isr_no_err 28
; VMM Communication Exception
isr_with_err 29
; Security Exception
isr_with_err 30
; Reserved
isr_no_err 31
idt.c
#include "idt.h"
#include <stdint.h>
#include "irq_handler.h"
#define CODE_SEG 0x8
__attribute__((aligned(16))) idt_entry_t idt[IDT_ENTRIES_COUNT];
idt_register_t idt_reg;
idt_entry_t make_idt_entry(uintptr_t irq_handler_addr) {
return (idt_entry_t){.low_addr = irq_handler_addr & 0xFFFF,
.selector = CODE_SEG,
.always0 = 0,
.flags = 0x8e,
.high_addr = irq_handler_addr >> 16};
}
void load_idt() {
idt_reg.base = (uintptr_t)&idt;
idt_reg.limit = IDT_ENTRIES_COUNT * sizeof(idt_entry_t) - 1;
__asm__ __volatile__("lidtl (%0)" : : "r"(&idt_reg));
__asm__ __volatile__("sti");
}
void init_idt() {
install_irq_handlers();
load_idt();
}
the interrupt frame struct
typedef struct {
uint32_t user_ds;
uint32_t edi;
uint32_t esi;
uint32_t ebp;
uint32_t ignored_val;
uint32_t ebx;
uint32_t edx;
uint32_t ecx;
uint32_t eax;
uint32_t int_num;
uint32_t err_code;
uint32_t eip;
uint32_t cs;
uint32_t eflags;
} interrupt_frame_t;
For a minimal reproducible example, please refer to my GitHub repository: SafeMemoryZone/ssos
Question
Why am I receiving a double fault with a non-zero error code when loading the IDT, and how can I fix potential issues in my ISR routines?