I'm trying to make a simple OS and I'm currently working on interrupts and the IDT.
So I implemented the IDT and a default exception handler that currently doe's nothing.
And when I run my OS in bochs I see that the OS triple faults right when it is jumping to the kernel.
bochs logs:
fetch_raw_descriptor: GDT: index (bf) 17 > limit (17)
interrupt(): gate descriptor is not valid sys seg (vector=0x0d)
interrupt(): gate descriptor is not valid sys seg (vector=0x08)
Exception 0x0d is General Protection Fault so My guess is that it has something to do with the GDT so here is my code for the GDT and how I load it:
LoadGDT:
lgdt [g_GDTDesc]
ret
g_GDT:
; NULL descrytpor
dq 0
; 32 code segment
dw 0FFFFh ; limit
dw 0 ; base
db 0 ; base
db 10011010b ; access
db 11001111b ; granularity
db 0 ; base high
; 32 data segment
dw 0FFFFh ; limit
dw 0 ; base
db 0 ; base
db 10010010b ; access
db 11001111b ; granularity
db 0 ; base high
g_GDTDesc:
dw g_GDTDesc - g_GDT - 1 ; size of gdt
dd g_GDT ; address of gdt
But if it is because the IDT then I followed this tutorial from osDev wiki.
idt.c:
#define INTERRUPT_GATE_FLAGS 0x8e
typedef struct {
u16 isr_low; // lower 16 bit of ISR's address
u16 kernel_cs; // gdt segment selector
u8 reserved; // set to zero
u8 flags; // attribute flags
u16 isr_high; // higher 16 bit of ISR's address
} PACKED idt_entry_t;
typedef struct {
u16 limit;
u32 ptr;
} PACKED idtr_t;
__attribute__((aligned(0x10))) static idt_entry_t idt[256];
static idtr_t idtr;
void exception_handler(){
__asm__ volatile("cli; hlt"); // currently doe's nothing
}
void IDT_set_descriptor(u8 interrupt, void* isr, u8 flags){
idt_entry_t* descriptor = &idt[interrupt];
descriptor->isr_low = (u32)isr & 0xFFFF;
descriptor->kernel_cs = 0x08;
descriptor->reserved = 0;
descriptor->flags = flags;
descriptor->isr_high = ((u32)isr >> 16) & 0xFFFF;
}
extern void* isr_stub_table[];
void IDT_init(){
idtr.ptr = (u32)&idt[0];
idtr.limit = (u16)sizeof(idt) - 1;
for(u8 interrupt=0; interrupt<32; interrupt++){
IDT_set_descriptor(interrupt, isr_stub_table[interrupt], INTERRUPT_GATE_FLAGS);
}
__asm__ volatile("lidt %0": :"m"(idtr));
STI();
}
idt.asm:
; exceptions with error code
%macro isr_err_stub 1
isr_stub_%+%1:
call exception_handler
iret
%endmacro
; exceptions without error code
%macro isr_no_err_stub 1
isr_stub_%+%1:
call exception_handler
iret
%endmacro
extern exception_handler
isr_no_err_stub 0
isr_no_err_stub 1
isr_no_err_stub 2
isr_no_err_stub 3
......
isr_no_err_stub 29
isr_err_stub 30
isr_no_err_stub 31
; defining the isr_stub_table
global isr_stub_table
isr_stub_table:
%assign i 0
%rep 32
dd isr_stub_%+i
%assign i i+1
%endrep
As Michael Petch pointed out, turned out my code had two main issues.
The first is that in my boot loader I loaded only one sector even though my kernel is bigger than that.
The second is that in link.ldt
, my linking script, I had:
.entry : { __entry_start = .; *(.entry) }
.text : { __text_start = .; *(.text) }
that means that the section .entry
will be linked before the .text
sectoin.
But in kernel_entry.asm
which is a file that calls the main kernel function I started with .text
, so my kernel wasn't linked properly.