Page 1 of 2
Help Understanding TSS's
Posted: Sun Sep 14, 2008 12:06 am
by enigma
Hello, I've just finished a simple memory manager for my kernel, and I wanted to try and tackle scheduling and task management next. The problem is that I don't know where to start. I've read the Intel Manual regarding task management and I'm stuck on TSS's. I understand the need for them (ring switching and what-not) and the TSS descriptor, but I'm confused about the actual implementation of a TSS. Any help would be appreciated.
Re: Help Understanding TSS's
Posted: Sun Sep 14, 2008 1:24 am
by xyzzy
If you're using software multitasking then all you need to do is set one up and change the ESP0 value in it at every task switch. It's pretty simple to set up. You need a GDT segment with the present bit set, and bit 0 and bit 3 set in the Type field. The base of the segment should be the base (virtual) address of your TSS, and the limit should be the size of the TSS. Once the GDT is loaded, all you need to do is set SS0 in the TSS to your kernel data segment, and then use the LTR instruction to load the TSS segment:
Code: Select all
__asm__ volatile("ltr %%ax" :: "a"(<TSS descriptor>));
Then, as said before, on every task switch change the ESP0 field in the TSS to the top of the new task's kernel stack.
Re: Help Understanding TSS's
Posted: Sun Sep 14, 2008 8:30 pm
by enigma
AlexExtreme wrote: The base of the segment should be the base (virtual) address of your TSS, and the limit should be the size of the TSS. Once the GDT is loaded, all you need to do is set SS0 in the TSS to your kernel data segment, and then use the LTR instruction to load the TSS segment:
Code: Select all
__asm__ volatile("ltr %%ax" :: "a"(<TSS descriptor>));
Then, as said before, on every task switch change the ESP0 field in the TSS to the top of the new task's kernel stack.
Okay, the Intel manuals say that a TSS is 104 bytes long, so is that what I should plug into the segment limit, or is it the size of the task space? Correct me if I'm wrong, but there should be one TSS per CPU core, right? As a last question (I won't implement it immediately, I intend to do a rewrite once I understand the basic concepts) how would I go about writing a multi threading kernel?
Re: Help Understanding TSS's
Posted: Sun Sep 14, 2008 9:13 pm
by Brendan
Hi,
enigma wrote:Okay, the Intel manuals say that a TSS is 104 bytes long, so is that what I should plug into the segment limit, or is it the size of the task space?
For the TSS descriptor in the GDT you'd want to use "limit - 1" or 103 (unless you use the space at the end of the TSS for an I/O permission bitmap or for virtual 80x86 interrupt handling).
enigma wrote:Correct me if I'm wrong, but there should be one TSS per CPU core, right?
Usually you'd need one TSS per CPU, but it's possible to use the same TSS for all CPUs if all CPL=0/kernel stacks are at the same linear address (different physical pages mapped at the same linear address, so that switching address spaces also makes the CPU to use a different physical page for the kernel stack).
enigma wrote:As a last question (I won't implement it immediately, I intend to do a rewrite once I understand the basic concepts) how would I go about writing a multi threading kernel?
Implement code to do one task switch (e.g. "switchToTask(newTask)"), then build on it...
Cheers,
Brendan
Re: Help Understanding TSS's
Posted: Mon Sep 15, 2008 1:57 pm
by enigma
Alright, so basically, I initialize a 104 byte region of memory to use as a TSS, fill in the appropriate values for all of the stacks and what not, then plug in an entry into the GDT with the base region pointing to the start of the TSS, and the limit set to 103, right?
I'm still a bit confused about performing software task switching with one TSS and more than one task. Would I have a structure similar to:
Code: Select all
typedef struct
{
unsigned short prev_task;
unsigned int esp0;
unsigned short ss0;
unsigned int esp1;
unsigned short ss1;
unsigned int esp2;
unsigned short ss2;
unsigned int cr3;
unsigned int eip;
unsigned int eflags;
unsigned int eax;
unsigned int ecx;
unsigned int edx;
unsigned int ebx;
unsigned int esp;
unsigned int ebp;
unsigned int esi;
unsigned int edi;
unsigned short es;
unsigned short cs;
unsigned short ss;
unsigned short ds;
unsigned short fs;
unsigned short gs;
unsigned short ldt_seg_selector;
unsigned short io_map_base;
} tss_t
And then just keep one of these structures for every task running, switching the address in the GDT for every task switch? Or am I just totally off the mark?
Re: Help Understanding TSS's
Posted: Mon Sep 15, 2008 2:00 pm
by xyzzy
For software task switching you just need one TSS per CPU, no need to have one for each task. Upon a task switch, all that's necessary is to change the appropriate values in the CPU's TSS - usually just ESP0 and SS0, and depending on whether you require it, the I/O permission map.
Re: Help Understanding TSS's
Posted: Mon Sep 15, 2008 2:39 pm
by enigma
AlexExtreme wrote:For software task switching you just need one TSS per CPU, no need to have one for each task. Upon a task switch, all that's necessary is to change the appropriate values in the CPU's TSS - usually just ESP0 and SS0, and depending on whether you require it, the I/O permission map.
Okay then, that makes it a lot easier.
And cr3, ESP2, and SS2 if it were a seperate program running in userspace, correct?
Add-on:
Assuming that you intend to resume where you left off after switching, wouldn't you need to also at least store EAX, EBX... for each process?
Re: Help Understanding TSS's
Posted: Tue Sep 16, 2008 12:15 am
by egos
enigma wrote:And cr3, ESP2, and SS2 if it were a seperate program running in userspace, correct?
No. The pair SS2:ESP2 usually is not used at all. CR3 and SS:ESP are stored in special data structure(s) of process and/or thread. The general purpose registers may be stored in this structure(s) too. Or they may be stored in the kernel stack. But the thread data structure is better place for storing FPU context.
Re: Help Understanding TSS's
Posted: Tue Sep 16, 2008 3:14 am
by AJ
enigma wrote:Okay then, that makes it a lot easier.
And cr3, ESP2, and SS2 if it were a seperate program running in userspace, correct?
SS2 and ESP2 are a bit of an artifact from the days of protection by segmentation. In the IDT, you have a DPL field, so that the interrupt can be handled in ring 0, 1, 2 or 3. SS2 and ESP2 would be used to load a ring 2 privilege stack if the DPL of your IVT entry was 2. This is now considered "legacy" and you will almost certainly handle all interrupts in ring 0, making the fields SS2, ESP2, SS1 and ESP1 close to obsolete.
Cheers,
Adam
Re: Help Understanding TSS's
Posted: Tue Sep 16, 2008 9:30 pm
by enigma
egos wrote:CR3 and SS:ESP are stored in special data structure(s) of process and/or thread. The general purpose registers may be stored in this structure(s) too. Or they may be stored in the kernel stack. But the thread data structure is better place for storing FPU context.
If I went the structure approach, all I woud need to do is keep tabs of CR3, SS, ESP, and the general registers? In which case I'd just update the eip, ss, esp, etc., in the scheduling function, before switching to the next task, just update the registers of the one I was just running right? (My apologies for thinking out loud, but I want to be absolutely certain that I'm doing this correctly)
Re: Help Understanding TSS's
Posted: Wed Sep 17, 2008 1:46 am
by egos
enigma wrote:If I went the structure approach, all I woud need to do is keep tabs of CR3, SS, ESP, and the general registers?
Reserve place for FPU context. You must set TS bit in CR0 (if this needs to be done), when task switching has happen, and change FPU context (if this needs to be done), when FPU exception has occurred. SS field is required only if multiple kernel stack segments are used.
enigma wrote:In which case I'd just update the eip, ss, esp, etc., in the scheduling function, before switching to the next task, just update the registers of the one I was just running right?
EIP has is stored in one stack context, when the function is calling, and has is restored from other stack context, when the function is returning control (if task switching has happen). ESP is changing during the task switching by hand. And TSS.ESP0 field is updating by hand too.
Re: Help Understanding TSS's
Posted: Sun Sep 21, 2008 1:16 am
by enigma
Just curious, since I'm going with software based multitasking, is it even necessary to have a TSS in the GDT? I'm pretty sure that Linux uses 4 total:
Kernel Code
Kernel Data
User Code
User Data
Should I even use TSS's at all, or is it one of those must-haves?
Re: Help Understanding TSS's
Posted: Sun Sep 21, 2008 1:19 am
by pcmattman
A TSS is a must-have if you're switching between ring 3 and ring 0 code. Generally that's what you would do for userland tasks, so in that case you'd want a TSS. I believe Linux has at least a fifth entry for the TSS, but don't quote me on that.
Really, it depends on what you're doing.
Re: Help Understanding TSS's
Posted: Sun Sep 21, 2008 11:26 pm
by egos
If you use traditional way for giving control to the kernel (i.e. with gate using), you must have TSS descriptor in GDT. Nevertheless, it is possible to initialize TR at initial stage from one GDT and then to use other GDT in which the TSS descriptor is absent. This relates also to the LDTR.
Re: Help Understanding TSS's
Posted: Thu Sep 25, 2008 9:55 pm
by enigma
Thanks AlexExtreme, I guess that I was probably putting too much into my TSS and initializing it in an overly-complex way.
On to supporting multiple tasks then!