Help Understanding TSS's
Help Understanding TSS's
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.
-
- Member
- Posts: 391
- Joined: Wed Jul 25, 2007 8:45 am
- Libera.chat IRC: aejsmith
- Location: London, UK
- Contact:
Re: Help Understanding TSS's
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:
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.
Code: Select all
__asm__ volatile("ltr %%ax" :: "a"(<TSS descriptor>));
Re: Help Understanding TSS's
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?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: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.Code: Select all
__asm__ volatile("ltr %%ax" :: "a"(<TSS descriptor>));
Re: Help Understanding TSS's
Hi,
Cheers,
Brendan
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: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?
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:Correct me if I'm wrong, but there should be one TSS per CPU core, right?
Implement code to do one task switch (e.g. "switchToTask(newTask)"), then build on it...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?
Cheers,
Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
Re: Help Understanding TSS's
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:
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?
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
-
- Member
- Posts: 391
- Joined: Wed Jul 25, 2007 8:45 am
- Libera.chat IRC: aejsmith
- Location: London, UK
- Contact:
Re: Help Understanding TSS's
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
Okay then, that makes it a lot easier. And cr3, ESP2, and SS2 if it were a seperate program running in userspace, correct?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.
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
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.enigma wrote:And cr3, ESP2, and SS2 if it were a seperate program running in userspace, correct?
If you have seen bad English in my words, tell me what's wrong, please.
Re: Help Understanding TSS's
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.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?
Cheers,
Adam
Re: Help Understanding TSS's
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)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.
Re: Help Understanding TSS's
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:If I went the structure approach, all I woud need to do is keep tabs of CR3, SS, ESP, and the general registers?
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.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?
If you have seen bad English in my words, tell me what's wrong, please.
Re: Help Understanding TSS's
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?
Kernel Code
Kernel Data
User Code
User Data
Should I even use TSS's at all, or is it one of those must-haves?
-
- Member
- Posts: 2566
- Joined: Sun Jan 14, 2007 9:15 pm
- Libera.chat IRC: miselin
- Location: Sydney, Australia (I come from a land down under!)
- Contact:
Re: Help Understanding TSS's
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.
Really, it depends on what you're doing.
Re: Help Understanding TSS's
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.
If you have seen bad English in my words, tell me what's wrong, please.
Re: Help Understanding TSS's
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!