I am trying to setup GDT/IDT/Paging. Below code is what i do,
global start
global CODE_SEG
global isr_asm
global DATA_SEG
global loadGDT
global isr_wrapper
extern main
extern isr
section .text
bits 32
start:
mov esp, stack_top
call main
hlt
align 4
isr_wrapper:
cli
pushad
call isr
popad
iret
loadGDT:
; Set our own GDT, can't rely GDT register being valid after bootloader
; transfers control to our entry point
lgdt [gdt_descriptor] ; Load GDT Register with GDT record
mov eax, DATA_SEG
mov ds, eax ; Reload all the data descriptors with Data selector (2nd argument)
mov es, eax
mov gs, eax
mov fs, eax
mov ss, eax
jmp CODE_SEG:.setcs
; Do the FAR JMP to next instruction to set CS with Code selector, and
; set the EIP (instruction pointer) to offset of setcs
.setcs:
section .bss
align 4096
stack_bottom:
resb 4096 * 4 ; Reserve this many bytes
stack_top:
section .rodata
gdt_start:
dq 0x0000000000000000
gdt_code:
dq 0x00CF9A000000FFFF
gdt_data:
dq 0x00CF92000000FFFF
gdt_end:
; GDT descriptor
gdt_descriptor:
dw gdt_end - gdt_start - 1 ; size (16 bit), always one less of its true size
dd gdt_start ; address (32 bit)
; define some constants for later use
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start
And my main is,
typedef unsigned short uint16_t;
typedef unsigned char uint8_t;
typedef unsigned int uint32_t;
typedef unsigned int uintptr_t;
extern int CODE_SEG;
extern int gdt_descriptor;
extern int DATA_SEG;
extern void loadGDT(void);
typedef struct {
uint16_t isr_low; // The lower 16 bits of the ISR's address
uint16_t kernel_cs; // The GDT segment selector that the CPU will load into CS before calling the ISR
uint8_t reserved; // Set to zero
uint8_t attributes; // Type and attributes; see the IDT page
uint16_t isr_high; // The higher 16 bits of the ISR's address
} __attribute__((packed)) idt_entry_t;
__attribute__((aligned(0x10)))
static idt_entry_t idt[256]; // Create an array of IDT entries; aligned for performance
typedef struct {
uint16_t limit;
uint32_t base;
} __attribute__((packed)) idtr_t;
static idtr_t idtr;
static inline void lcr3(unsigned int val)
{
asm volatile("movl %0,%%cr3" : : "r" (val));
}
// 8MB mapping
int pagetable[2][1024] __attribute__((aligned(4096)));
int pagedirectory[2] __attribute__((aligned(4096)));
void idt_set_descriptor(uint8_t vector, void* isr, uint8_t flags) {
idt_entry_t* descriptor = &idt[vector];
descriptor->isr_low = (uint32_t)isr & 0xFFFF;
descriptor->kernel_cs = CODE_SEG; // this value can be whatever offset your kernel code selector is in your GDT
descriptor->attributes = flags;
descriptor->isr_high = (uint32_t)isr >> 16;
descriptor->reserved = 0;
}
static inline void halt(void)
{
asm volatile("hlt" : : );
}
extern void isr_wrapper(void);
void isr(void)
{
}
void idt_init() {
idtr.base = (uintptr_t)&idt[0];
idtr.limit = (uint16_t)sizeof(idt_entry_t) * 256 - 1;
for (uint16_t vector = 0; vector < 256; vector++) {
idt_set_descriptor(vector, isr_wrapper, 0x8E);
}
idt_set_descriptor(0x00, isr, 0x8F);
idt_set_descriptor(0x08, isr, 0x8F);
__asm__ volatile ("lidt %0" : : "m"(idtr)); // load the new IDT
}
int main(void)
{
int i;
int j;
int addr=0;
// Create your page table here
for(i=0;i<2;i++){
for(j=0;j<1024;j++){
pagetable[i][j]=addr|0b11;
addr +=4096;
}
pagedirectory[i]=(unsigned int )(&pagetable[i])|0b11;
}
lcr3((unsigned int)(&pagedirectory));
loadGDT();
idt_init();
__asm__ volatile ("sti"); // set the interrupt flag
while(1) {
halt();
}
}
My make:qemu-system-x86_64 -cdrom hello.iso -vga std -s -serial file:serial.log -d int,cpu_reset -no-shutdown -no-reboot
As soon as i enable irq i am getting double fault, general protection fault, which is not served and results in triple fault and cpu rebooting..
Below is the dump from qemu,
0: v=08 e=0000 i=0 cpl=0 IP=0008:00000000001014b2 pc=00000000001014b2 SP=0010:0000000000106ff0 env->regs[R_EAX]=0000000000000008
EAX=00000008 EBX=0010da60 ECX=0000008f EDX=00000010
ESI=00000000 EDI=00000000 EBP=00106ff8 ESP=00106ff0
EIP=001014b2 EFL=00000202 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
CS =0008 00000000 ffffffff 00cf9a00 DPL=0 CS32 [-R-]
SS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
DS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
FS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
GS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT= 00100018 00000017
IDT= 0010b010 000007ff
CR0=00000011 CR2=00000000 CR3=00108000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=00000000 CCD=00106fe0 CCO=EFLAGS
EFER=0000000000000000
check_exception old: 0xffffffff new 0xd
1: v=0d e=e2c0 i=0 cpl=0 IP=0008:00000000001014b2 pc=00000000001014b2 SP=0010:0000000000106ff0 env->regs[R_EAX]=0000000000000008
EAX=00000008 EBX=0010da60 ECX=0000008f EDX=00000010
ESI=00000000 EDI=00000000 EBP=00106ff8 ESP=00106ff0
EIP=001014b2 EFL=00000202 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
CS =0008 00000000 ffffffff 00cf9a00 DPL=0 CS32 [-R-]
SS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
DS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
FS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
GS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT= 00100018 00000017
IDT= 0010b010 000007ff
CR0=00000011 CR2=00000000 CR3=00108000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=00000000 CCD=00106fe0 CCO=EFLAGS
EFER=0000000000000000
check_exception old: 0xd new 0xd
2: v=08 e=0000 i=0 cpl=0 IP=0008:00000000001014b2 pc=00000000001014b2 SP=0010:0000000000106ff0 env->regs[R_EAX]=0000000000000008
EAX=00000008 EBX=0010da60 ECX=0000008f EDX=00000010
ESI=00000000 EDI=00000000 EBP=00106ff8 ESP=00106ff0
EIP=001014b2 EFL=00000202 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
CS =0008 00000000 ffffffff 00cf9a00 DPL=0 CS32 [-R-]
SS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
DS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
FS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
GS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT= 00100018 00000017
IDT= 0010b010 000007ff
CR0=00000011 CR2=00000000 CR3=00108000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=00000000 CCD=00106fe0 CCO=EFLAGS
EFER=0000000000000000
check_exception old: 0x8 new 0xd
Triple fault
How is the segment selector getting value 0xe2c0? What am i doing wrong here, if someone can comment i am thankful to you, i am trying to debug this for days now..
1: v=0d e=e2c0 i=0 cpl=0 IP=0008:00000000001014b2 pc=00000000001014b2
I tried remote debugging with gdb and as soon as sti is executed, it gets SIGRESET as soon as triple fault, i dont understand why. tried breakpoint in isr_wrapper and its not hit..
If i dont add "sti" in other words if interrupts are not enabled then there is no fault from qemu. I have setup IDT/GDT/paging right to my knowledge; what am I missing here?
I have tried wiki os dev, intel software developers manual, everywhere in web, but with no hope..
.setcslabel, so the processor will just start executing random instructions instead of returning fromloadGDT.