Well you allocate one, then you load one into the task register, and then you set esp0 to whatever you want your ESP to be when you enter your kernel.
Look into the Intel manual. It tells everything you need to know about those. You can skip all the crazy stuff about actually using them for something other than ESP0. For all practical purposes, that's the only field you need to care about.
Basicly, you need an entry in your GDT (or LDT if you bother with those.. I don't).. and then you need a structure like this:
Code:
typedef volatile struct __tss_struct {
unsigned short link;
unsigned short link_h;
unsigned long esp0;
unsigned short ss0;
unsigned short ss0_h;
unsigned long esp1;
unsigned short ss1;
unsigned short ss1_h;
unsigned long esp2;
unsigned short ss2;
unsigned short ss2_h;
unsigned long cr3;
unsigned long eip;
unsigned long eflags;
unsigned long eax;
unsigned long ecx;
unsigned long edx;
unsigned long ebx;
unsigned long esp;
unsigned long ebp;
unsigned long esi;
unsigned long edi;
unsigned short es;
unsigned short es_h;
unsigned short cs;
unsigned short cs_h;
unsigned short ss;
unsigned short ss_h;
unsigned short ds;
unsigned short ds_h;
unsigned short fs;
unsigned short fs_h;
unsigned short gs;
unsigned short gs_h;
unsigned short ldt;
unsigned short ldt_h;
unsigned short trap;
unsigned short iomap;
} tss_struct;
Then you allocate one of those, and then you put a descriptor in your GDT and load the task registers with code something like this:
Code:
void tss_load(unsigned long cpu_num) {
unsigned long tss_addr;
tss_addr = (unsigned long) tss_table + cpu_num;
/* build descriptor */
gdt[5 + cpu_num] =
/* base */
((unsigned long long) (tss_addr & 0x00ffffff) << 16)
+ ((unsigned long long) (tss_addr & 0xff000000) << 32)
/* attributes, 32-bit TSS, present, */
+ 0x0000890000000000LL
/* limit, it's less than 2^16 anyhow, so no need for th eupper nibble */
+ (sizeof(tss_struct)) - 1;
asm volatile("ltr %%ax": : "a" ((5 + cpu_num)<<3));
}
And once you've done that, you can keep the pointer to the tss somewhere, if you allocated it dynamically (I just allocate them statically as a table, so I just need the address of the table) and then you just put your kernel datasegment descriptor into ss0 like this:
Code:
tss->ss0 = 0x10;
and if you care about the IO bitmap, you could do something with those, but I don't bother with them, so I just:
Code:
/* set to point beyond the TSS limit */
tss->iomap = (unsigned short) sizeof(tss_struct);
And then the only thing from there on you need to do is put into esp0 the value you want in your ESP when you enter your kernel from outside ring0.
And that's about it.
Pretty uninteresting structure.
Like I said, the internal manual knows the details.