Triple fault when attempting to load the task register

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
0xBADC0DE
Posts: 18
Joined: Mon Sep 26, 2016 8:25 am

Triple fault when attempting to load the task register

Post by 0xBADC0DE »

I've finished up writing my shell and filesystem for my operating system, and now have decided to move onto user mode and writing system calls. Though the difficulty I've been having in doing that is before actually entering user mode: trying to load a selector from the GDT into the task register, which results in a triple fault and the CPU resetting.

To setup the GDT, IDT, interrupts and exception handling, I followed http://www.osdever.net/bkerndev/Docs/intro.htm (bran's kernel development tutorial). Now for entering user mode, I was following http://jamesmolloy.co.uk/tutorial_html/ ... 0Mode.html (James Molly), but unfortunately couldn't get that to work. Since that wasn't working, I've been looking at other articles on this site about the GDT and exceptions, and the brokenthorne OS dev series to try and help me out. I've found some other questions similar to mine, but none of the answers there have helped me.

The specific instruction that is causing the processor to triple fault is

Code: Select all

ltr %ax
The code I am using to install the TSS is similar to Jame's Molly's:

Code: Select all

void install_tss(uint32_t i, uint16_t kernel_ss, uint16_t kernel_esp)
{
	gdt_set_descriptor(i, 0x00, 0xFFFFFFFF, 0x89, 0x00);

	memset(&tss_entry, 0, sizeof(tss_entry));

	tss_entry.ss0 = kernel_ss;
	tss_entry.esp0 = kernel_esp;

	tss_entry.cs = 0x0b;
	tss_entry.ss = 0x13;
	tss_entry.es = 0x13;
	tss_entry.ds = 0x13;
	tss_entry.fs = 0x13;
	tss_entry.gs = 0x13;

	asm(
		"mov $0x2b, %ax\n"
		"ltr %ax"
	);
}
I have read the article about the known bugs in that tutorial, and my code doesn't appear to have any of the problems discussed in that article.

This is the function I am using to initialize the GDT:

Code: Select all

void init_gdt()
{
	gdt_ptr.limit = (sizeof(struct gdt_descriptor) * MAX_DESCRIPTORS) - 1;
	gdt_ptr.base  = (uint32_t) &gdt_desc;

	gdt_set_descriptor(0, 0, 0, 0, 0); // Null descriptor
	gdt_set_descriptor(1, 0, 0xFFFFFFFF, 0x9A, 0xCF); // Kernel code descriptor
	gdt_set_descriptor(2, 0, 0xFFFFFFFF, 0x92, 0xCF); // Kernel data descriptor
	gdt_set_descriptor(3, 0, 0xFFFFFFFF, 0xFA, 0xCF); // User code descriptor
	gdt_set_descriptor(4, 0, 0xFFFFFFFF, 0xF2, 0xCF); // User data descriptor

	// Install the TSS to selector 5
	install_tss(5, 0x10, 0x0);

	/* install the GDT */
	install_gdt();
}
I added the flag

Code: Select all

-d int,cpu_reset
to the qemu command when running my OS, and noticed it ended with a triple fault, with ~2 or 3 general protection faults before that. I tried debugging using gdb, but that didn't help since it still just crashed on the

Code: Select all

ltr
instruction and doesn't provide any other information.

I also read somewhere that a triple fault (or something of the sort) is the result of fault exception handling code. I'm pretty sure that my exception handling code is catching exceptions, because when I do

Code: Select all

asm("int $0")
I get a division by zero exception. The IRQs have been remapped and the timer is firing as expected. Should exceptions always result in the exception handler being called (as in my code should be catching exceptions) or are there occasions (like loading some invalid value into the task register) that won't be caught by the exception handler and immediately result in a triple fault/crash?).

My OS can be found at https://github.com/aaron2212/XOS, though I have not pushed the latest code about entering user mode, or rather, even just setting up the task state segment.

I have tried everything I can think of, and searched all of google with avail, so any help is greatly appreciated :D
GhelloWorld
Member
Member
Posts: 27
Joined: Thu Apr 19, 2018 5:31 am

Re: Triple fault when attempting to load the task register

Post by GhelloWorld »

Instead of using 0 and 0xFFFFFFFF as the base for the TSS GDT descriptor you should set the base to the address of the tss and the limit to the same address + size of the tss. This way the CPU can read where the tss is located.
Try changing this line:

Code: Select all

gdt_set_descriptor(i, 0x00, 0xFFFFFFFF, 0x89, 0x00);
To something like this:

Code: Select all

gdt_set_descriptor(i, &tss_entry, $tss_entry + size of the tss, 0x89, 0x00);
Hopefully it will then work :D
0xBADC0DE
Posts: 18
Joined: Mon Sep 26, 2016 8:25 am

Re: Triple fault when attempting to load the task register

Post by 0xBADC0DE »

Thanks so much for the reply @GhelloWorld, but unfortunately that doesn't seem to work :(
MichaelPetch
Member
Member
Posts: 712
Joined: Fri Aug 26, 2016 1:41 pm
Freenode IRC: mpetch

Re: Triple fault when attempting to load the task register

Post by MichaelPetch »

Why don't you update your repository with the code that doesn't work?
0xBADC0DE
Posts: 18
Joined: Mon Sep 26, 2016 8:25 am

Re: Triple fault when attempting to load the task register

Post by 0xBADC0DE »

@MichaelPetch thanks. I have pushed the code to the "user-mode" branch
MichaelPetch
Member
Member
Posts: 712
Joined: Fri Aug 26, 2016 1:41 pm
Freenode IRC: mpetch

Re: Triple fault when attempting to load the task register

Post by MichaelPetch »

It seems you are doing the ltr before you have installed the GDT! You'll need to move the inline assembly that does the LTR after you call install_gdt. I think the TSS descriptor should be set up this way:

Code: Select all

gdt_set_descriptor(i, base, limit-1, 0x89, 0x00);
The limit is relative to the base (don't add base to limit) and you should be subtracting 1 from the limit in the descriptor entry. You also have a problem in your inline assembly (inline assembly is hard to get right) but you can't modify a register without telling the compiler you did so. You can fix the inline assembly by passing the TSS's selector as a 16-bit value and have the compiler pick a register for you. It could look like:

Code: Select all

    asm (
        "ltr %0" :: "r"((uint16_t)0x2b)
    );
0xBADC0DE
Posts: 18
Joined: Mon Sep 26, 2016 8:25 am

Re: Triple fault when attempting to load the task register

Post by 0xBADC0DE »

@MichaelPetch thank you SO much! I don't know how I made such a dumb mistake - face palm!
This solved my problem :D :D
Post Reply