Page 1 of 1

TSS troubles

Posted: Wed May 13, 2009 5:32 pm
by NickJohnson
I'm getting very close to being able to run actual loaded programs in usermode, but I've been stuck on one thing for a few days - the TSS. I've read and re-read the documentation, but I can't see what is wrong with my implementation. Perhaps someone could shed more light on the situation.

I have the GDT hard-coded in assembly, but with an open (and allocated) space for the TSS entry. It already has the granularity and permission flags set, because those are static:

Code: Select all

section .data

global gdt
gdt:
align 0x1000
	dd 0x00000000, 0x00000000
	dd 0x0000FFFF, 0x00CF9A00
	dd 0x0000FFFF, 0x00CF9200
	dd 0x0000FFFF, 0x00CFFA00
	dd 0x0000FFFF, 0x00CFF200
	dd 0x00000000, 0x0000E900 ; This will become the TSS
There is a piece of C code run later in the setup that sets the values for the TSS and TSS entry (and has the TSS itself as a global variable):

Code: Select all

struct tss {
	u32int prev_tss;
	u32int esp0;
	u32int ss0;
	u32int unused[15];
	u32int es, cs, ss, ds, fs, gs;
	u32int ldt;
	u16int trap, iomap_base;
} __attribute__ ((packed)) tss;

extern u8int gdt[48];

__attribute__ ((section(".ttext")))
void init_tss() {
	u32int base = (u32int) &tss;
	u16int limit = sizeof(struct tss);

	memclr(&tss, sizeof(struct tss));
	tss.ss0 = 0x13;
	extern u32int stack;
	tss.esp0 = (u32int) &stack;
	tss.cs = 0x0B;
	tss.es = tss.ds = tss.fs = tss.gs = 0x13;

	gdt[40] = (limit >> 8) & 0xFF;
	gdt[41] = (limit) & 0xFF;
	gdt[42] = (base >> 8) & 0xFF;
	gdt[43] = base & 0xFF;
	gdt[44] = (base >> 16) & 0xFF;
	gdt[47] = (base >> 24) & 0xFF;

	extern void tss_flush();
	tss_flush();
}
Btw, memclr() is like memcpy but with all zeroes, which is a bit faster.
EDIT: Also, the reason that init_tss() is in ELF section ".ttext" is because I have the kernel free all memory used by initialization code and data after it is done booting by lumping init code together. It usually only saves ~5 pages of memory, but who knows when that will come in handy? I also know this is not the part of the problem.

And here is the "tss_flush" function called by that C function:

Code: Select all

global tss_flush
tss_flush:
	mov ax, 0x2B
	ltr ax
	ret
This is effectively all of the code that does any interfacing with the TSS. When I switch to usermode and turn on interrupts, Bochs triple faults with error 10, i.e. a bad TSS fault. Unfortuantely, it is not much more descriptive, other than informing me that the fault happened with EIP in user memory and the same instruction that should be the first in the user program, which at least means my loader is working. I also noticed that the system pauses for slightly varying amounts of time before faulting, so the fault is probably caused by the PIT firing in usermode.

Any help would be appreciated!

Re: TSS troubles

Posted: Thu May 14, 2009 3:22 am
by Combuster
tss.ss0 = 0x13;
...
mov ax, 0x2B
ltr ax
The TSS is a kernel structure, as is the kernel stack. i.e. set the DPL and RPL to 0.

Also, doesn't bochs panic or error on anything else? Normally you get a rather specific message when something is wrong with the IDT/TSS (should be seen before the triple fault).

Re: TSS troubles

Posted: Thu May 14, 2009 4:27 am
by NickJohnson
I fixed the permissions, but it still keeps triple faulting. However, I did find some errors immediately before the triple fault. Bochs displayed "interrupt(): SS selector null" three times. Could it be that the layout of my TSS structure is wrong, and I'm not setting the proper information?

Re: TSS troubles

Posted: Thu May 14, 2009 6:12 am
by Combuster
SS=null means something is not setup right and the TSS the processor uses is not the TSS you think you're using.

Have your kernel print the TSS address, then from the debugger type info tss and info gdt and check that these values match. If your tss struct is wrong then those commands will give you some clue to that as well.

Re: TSS troubles

Posted: Thu May 14, 2009 1:55 pm
by NickJohnson
Got it! :mrgreen:

I was using only byte sized addressing to set up the GDT entry, but totally forgot about endianness! Because I was thinking in the arabic numeral (i.e. big-endian) system used in C, I put everything in in the opposite order. Maybe I should have defined a structure for the C code, so it wouldn't get this confusing. Some simple switching around of the indices fixed it anyway:

Code: Select all

	gdt[41] = (limit >> 8) & 0xFF;
	gdt[40] = (limit) & 0xFF;
	gdt[43] = (base >> 8) & 0xFF;
	gdt[42] = base & 0xFF;
	gdt[44] = (base >> 16) & 0xFF;
	gdt[47] = (base >> 24) & 0xFF;
Now the kernel can actually execute programs, although I haven't made any system calls yet, so it's not very exciting looking. Your idea to compare the GDT entry and TSS address tipped me off to the issue.

Although, about that: should I be using a real debugger? Up until now I think I've been getting along fine with printk at runtime and readelf/objdump to analyze the binary.

Re: TSS troubles

Posted: Thu May 14, 2009 4:55 pm
by Combuster
printf debugging can do a lot (and sometimes, that's all you got). Being able to use a debugger is, in most cases, just a big timesaver.