Page 1 of 1

Double Fault after loading IDT and executing sti

Posted: Sun Jan 18, 2009 1:03 pm
by skwee
Hello all!

I have a problem (and probably I did misunderstood something):
I loaded the kernel, kernel loaded GDT and IDT, if after this I execute asm("sti"); I go double fault! Is that normal behavior?

kernel:

Code: Select all

static void k_set_descriptors();

//Main kernel function
extern int k_start(){
	start_textmode();
	
	print_to_screen("Text mode initialized: 80x25 console screen\n");
	print_to_screen("Initializing Global and Interrupt Descriptor Tables...");
	k_set_descriptors();
	print_to_screen("Done!\n");
	
	asm("sti");
	
	/* exceptions test
	{
		int a, b;
		a = 23;
		b = 0;
		
		a = a / b;
	}*/
	
	asm("hlt");
	
	return 0;
}

//Set descriptors
static void k_set_descriptors(){
	gdt_init();
	idt_init();
}
gdt:

Code: Select all

//Selectors
static gdt_descriptor_t selectors[MAX_SELECTORS];

/**
 * Internal function.
 * Set specific gdt selector.
 * @param sel_num Number of selector.
 * @param limit Low limit of selector.
 * @param base_low Low base of selector.
 * @param base_middle Middle base of selector.
 * @param access Selector acces and info.
 * @param granularity Selector info.
 * @param base_high High base of selector.
 */
static void gdt_set_selector(u8int sel_id,
u16int limit, u16int base_low, u8int base_middle, u8int access,
u8int granularity, u8int base_high);

/**
 * Load GDT to CPU.
 * @param ptr GDT Pointer structure.
 */
static void gdt_load(gdt_pointer_t ptr);

//Init gdt
void gdt_init(){
	gdt_pointer_t ptr;
	
	//Null selector
	gdt_set_selector(NULL_SEL_ID, 0, 0, 0, 0, 0, 0);
	
	//Kernel code selector
	gdt_set_selector(KERNEL_CODE_SEL_ID, 0xFFFF, 0, 0, 0x9A, 0xCF, 0);
	
	//Kernel data selector
	gdt_set_selector(KERNEL_DATA_SEL_ID, 0xFFFF, 0, 0, 0x92, 0xCF, 0);
	
	ptr.limit = (sizeof(gdt_descriptor_t) * MAX_SELECTORS) - 1;
	ptr.base = (unsigned long)&selectors;
	
	//Load selectors
	gdt_load(ptr);
}

//Set selector
static void gdt_set_selector(u8int sel_id,
u16int limit, u16int base_low, u8int base_middle, u8int access,
u8int granularity, u8int base_high){
	
	if((sel_id < 0) || (sel_id >= MAX_SELECTORS)){
		//Error here
		///@todo
	}else{
		selectors[sel_id].limit = limit;
		selectors[sel_id].base_low = base_low;
		selectors[sel_id].base_middle = base_middle;
		selectors[sel_id].access = access;
		selectors[sel_id].granularity = granularity;
		selectors[sel_id].base_high = base_high;
	}
}

//Load gdt register
static void gdt_load(gdt_pointer_t ptr){
	asm volatile("lgdt %0" : "=m" (ptr));
}
idt:

Code: Select all

//IDT entries
static idt_descriptor_t entries[MAX_IDT];

/**
 * Set IDT gate.
 * @param idt_num Number of IDT.
 * @param base Address of interrupt handler.
 * @param selector Selector type.
 * @param info IDT info.
 */
static void idt_set_gate(u8int idt_num,
u32int base, u16int selector, u8int info);

/**
 * Load IDT to IDTR.
 * @param ptr Pointer to valid IDT structure.
 */
static void idt_load(idt_pointer_t ptr);

//Initialize IDT
void idt_init(){
	idt_pointer_t ptr;
	
	//Must defined ISRs, all run 32bit and ring0
	idt_set_gate( 0, (u32int) isr0, KERNEL_CODE_SELECTOR, 0x8E);
	idt_set_gate( 1, (u32int) isr1, KERNEL_CODE_SELECTOR, 0x8E);
	idt_set_gate( 2, (u32int) isr2, KERNEL_CODE_SELECTOR, 0x8E);
	idt_set_gate( 3, (u32int) isr3, KERNEL_CODE_SELECTOR, 0x8E);
	idt_set_gate( 4, (u32int) isr4, KERNEL_CODE_SELECTOR, 0x8E);
	idt_set_gate( 5, (u32int) isr5, KERNEL_CODE_SELECTOR, 0x8E);
	idt_set_gate( 6, (u32int) isr6, KERNEL_CODE_SELECTOR, 0x8E);
	idt_set_gate( 7, (u32int) isr7, KERNEL_CODE_SELECTOR, 0x8E);
	idt_set_gate( 8, (u32int) isr8, KERNEL_CODE_SELECTOR, 0x8E);
	idt_set_gate( 9, (u32int) isr9, KERNEL_CODE_SELECTOR, 0x8E);
	idt_set_gate(10, (u32int)isr10, KERNEL_CODE_SELECTOR, 0x8E);
	idt_set_gate(11, (u32int)isr11, KERNEL_CODE_SELECTOR, 0x8E);
	idt_set_gate(12, (u32int)isr12, KERNEL_CODE_SELECTOR, 0x8E);
	idt_set_gate(13, (u32int)isr13, KERNEL_CODE_SELECTOR, 0x8E);
	idt_set_gate(14, (u32int)isr14, KERNEL_CODE_SELECTOR, 0x8E);
	idt_set_gate(15, (u32int)isr15, KERNEL_CODE_SELECTOR, 0x8E);
	idt_set_gate(16, (u32int)isr16, KERNEL_CODE_SELECTOR, 0x8E);
	idt_set_gate(17, (u32int)isr17, KERNEL_CODE_SELECTOR, 0x8E);
	idt_set_gate(18, (u32int)isr18, KERNEL_CODE_SELECTOR, 0x8E);
	idt_set_gate(19, (u32int)isr19, KERNEL_CODE_SELECTOR, 0x8E);
	idt_set_gate(20, (u32int)isr20, KERNEL_CODE_SELECTOR, 0x8E);
	idt_set_gate(21, (u32int)isr21, KERNEL_CODE_SELECTOR, 0x8E);
	idt_set_gate(22, (u32int)isr22, KERNEL_CODE_SELECTOR, 0x8E);
	idt_set_gate(23, (u32int)isr23, KERNEL_CODE_SELECTOR, 0x8E);
	idt_set_gate(24, (u32int)isr24, KERNEL_CODE_SELECTOR, 0x8E);
	idt_set_gate(25, (u32int)isr25, KERNEL_CODE_SELECTOR, 0x8E);
	idt_set_gate(26, (u32int)isr26, KERNEL_CODE_SELECTOR, 0x8E);
	idt_set_gate(27, (u32int)isr27, KERNEL_CODE_SELECTOR, 0x8E);
	idt_set_gate(28, (u32int)isr28, KERNEL_CODE_SELECTOR, 0x8E);
	idt_set_gate(29, (u32int)isr29, KERNEL_CODE_SELECTOR, 0x8E);
	idt_set_gate(30, (u32int)isr30, KERNEL_CODE_SELECTOR, 0x8E);
	idt_set_gate(31, (u32int)isr31, KERNEL_CODE_SELECTOR, 0x8E);
	
	//Rest interrupts are zero for now
	{
		int i;
		for(i = 32; i < MAX_IDT; ++i)
			idt_set_gate(i, 0, 0, 0);
	}
	
	ptr.limit = (sizeof(idt_descriptor_t) * MAX_IDT) - 1;
	ptr.base = (unsigned long)&entries;
	
	//load idt
	idt_load(ptr);
}

//Set IDT gate
static void idt_set_gate(u8int idt_num,
u32int base, u16int selector, u8int info){
	if((idt_num < 0) || (idt_num >= MAX_IDT)){
		//error
		///@todo
	}else{
		entries[idt_num].base_low = base & 0xFFFF;
		entries[idt_num].base_high = (base >> 16) & 0xFFFF;
		entries[idt_num].selector = selector;
		entries[idt_num].zero = 0;
		entries[idt_num].info = info;
	}
}

//Load idt
static void idt_load(idt_pointer_t ptr){
	asm volatile("lidt %0" : "=m" (ptr));
}
All ISR's just call a common handler that pushes all needed registers and then executes a kernel_panic with the error.

Am I doing something wrong?

Re: Double Fault after loading IDT and executing sti

Posted: Sun Jan 18, 2009 1:49 pm
by LordMage
okay, did you set up your pic to remap the hardware interrupts to locations that you specified. also you have to make sure that your interrupt functions don't have an junk code at the front that will mess with your stack. Mostly though, I would say to examine your PIC code to make sure everything got mapped to dummy functions in your idt.

Also, I know you were probably trying to leave out any nonimportant code, but there is a lot of support code required to finally turn on interrupts. are you creating your idt in the boot loader or are you creating it in the kernel. I can see that it looks like you are creating it in the kernel but I don't see your PIC code, your PIT code, etc... I may have just missed it but did you run ldtr??? I have like 6 files to run through before I turn on interrupts. and you must handle the timer or you will show a fault as soon as you turn on interrupts.

hope some of this helped. depending on what compilers and tools you are using and what tutorial you might be following you will be taking care of this different ways so it is hard to give a good suggestion with out more information.

Re: Double Fault after loading IDT and executing sti

Posted: Sun Jan 18, 2009 2:00 pm
by skwee
Thanks for the replay :)
"also you have to make sure that your interrupt functions don't have an junk code at the front that will mess with your stack"
What do you mean? I do have some code that pushes all registers so I can view them later?

Well, No I didn't make PIT and PIC remapping, I just defined 32 basic interrupts (planning to do remapping tomorrow).
What do you mean by "did you run ldtr???"?

Anyway I did search in the forum, but I don't sure if its correct to use STI in Pmode?

Oh and the problem fixed, I recompiled everything and it worked, dunno what was the problem before.

Re: Double Fault after loading IDT and executing sti

Posted: Sun Jan 18, 2009 3:42 pm
by Combuster
if you didn't remap the PIC, then your regular interrupts will call (amongst others) the double fault handler instead of the appropriate IRQ handler.

Re: Double Fault after loading IDT and executing sti

Posted: Sun Jan 18, 2009 5:38 pm
by LordMage
Sorry for the typo's, first, combuster is right, you must remap the PIC or your code will never work.

In a C function there is code put in place by the compiler, I have to assume that the code is slightly different depending on the compiler but the code pushes the calling points address on to the stack so it can return after the function finishes. This is not needed or wanted in a interrupt because it will corrupt the stack that you use for retrieving information about the failure. so you must call a "naked" function that does not have the stack corrupting information at the begining of it.

lidtr and lgdtr are the ASM functions that load your IDT and GDT. without them your gdt and idt aren't actually being used. if you are using GRUB I think you can have GRUB load the idt and gdt that you need and then reference it somewhere in the multiboot format, someone correct me if I am wrong I don't use GRUB and don't know much about multiboot.

your confusion with what I was talking about leads me to beleive that you are using a barebones example or a tutorial that gives more code than explaination. I found Neon's tutorials to be extremely helpful whether you are using the code or not. they describe more the why of things than any other tutorials I have found. they can be found at www.BrokenThorn.com . The code is written for MSVC++ although he is taking a C approach and only using some minor C++ functions.

and lastly, STI is the proper command to reenable interrupts after they have been setup