Help Understanding TSS's

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.
enigma
Posts: 18
Joined: Tue Dec 25, 2007 10:52 am

Help Understanding TSS's

Post 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.
xyzzy
Member
Member
Posts: 391
Joined: Wed Jul 25, 2007 8:45 am
Libera.chat IRC: aejsmith
Location: London, UK
Contact:

Re: Help Understanding TSS's

Post 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.
enigma
Posts: 18
Joined: Tue Dec 25, 2007 10:52 am

Re: Help Understanding TSS's

Post 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?
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: Help Understanding TSS's

Post 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
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.
enigma
Posts: 18
Joined: Tue Dec 25, 2007 10:52 am

Re: Help Understanding TSS's

Post 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?
xyzzy
Member
Member
Posts: 391
Joined: Wed Jul 25, 2007 8:45 am
Libera.chat IRC: aejsmith
Location: London, UK
Contact:

Re: Help Understanding TSS's

Post 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.
enigma
Posts: 18
Joined: Tue Dec 25, 2007 10:52 am

Re: Help Understanding TSS's

Post 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. :-k 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?
egos
Member
Member
Posts: 612
Joined: Fri Nov 16, 2007 1:59 pm

Re: Help Understanding TSS's

Post 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.
If you have seen bad English in my words, tell me what's wrong, please.
User avatar
AJ
Member
Member
Posts: 2646
Joined: Sun Oct 22, 2006 7:01 am
Location: Devon, UK
Contact:

Re: Help Understanding TSS's

Post by AJ »

enigma wrote:Okay then, that makes it a lot easier. :-k 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
enigma
Posts: 18
Joined: Tue Dec 25, 2007 10:52 am

Re: Help Understanding TSS's

Post 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)
egos
Member
Member
Posts: 612
Joined: Fri Nov 16, 2007 1:59 pm

Re: Help Understanding TSS's

Post 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.
If you have seen bad English in my words, tell me what's wrong, please.
enigma
Posts: 18
Joined: Tue Dec 25, 2007 10:52 am

Re: Help Understanding TSS's

Post 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?
pcmattman
Member
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

Post 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.
egos
Member
Member
Posts: 612
Joined: Fri Nov 16, 2007 1:59 pm

Re: Help Understanding TSS's

Post 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.
If you have seen bad English in my words, tell me what's wrong, please.
enigma
Posts: 18
Joined: Tue Dec 25, 2007 10:52 am

Re: Help Understanding TSS's

Post by enigma »

Thanks AlexExtreme, I guess that I was probably putting too much into my TSS and initializing it in an overly-complex way. =D> On to supporting multiple tasks then!
Post Reply