Page 1 of 2
How to write a TSS (in assembly)
Posted: Thu Feb 18, 2021 7:50 pm
by austanss
How do I write a TSS?
I've seen the article on the OSDev wiki, but it doesn't go into enough detail for me.
I've also seen some code from someone else, but it is hard to understand for me. Also, it's written in C and generated at runtime. I'm not a fan of that.
I also don't understand the meaning behind the values in the TSS. What are the RSP values, and why are there multiple. What is an IST, and why is there so damn many of them?
Thank you in advance for any insight.
Re: How to write a TSS (in assembly)
Posted: Thu Feb 18, 2021 8:11 pm
by Octocontrabass
rizxt wrote:How do I write a TSS?
You put the TSS values in memory according to the TSS structure, set up a TSS descriptor, and load the TSS descriptor into the task register.
rizxt wrote:Also, it's written in C and generated at runtime. I'm not a fan of that.
Which part don't you like, and why?
rizxt wrote:I also don't understand the meaning behind the values in the TSS. What are the RSP values, and why are there multiple. What is an IST, and why is there so damn many of them?
Have you checked the Intel SDM or AMD APM yet? Make sure you're looking at the 64-bit TSS, it's very different from the others.
Re: How to write a TSS (in assembly)
Posted: Thu Feb 18, 2021 8:24 pm
by austanss
Octocontrabass wrote:
rizxt wrote:Also, it's written in C and generated at runtime. I'm not a fan of that.
Which part don't you like, and why?
Written in C. I prefer writing data structures in assembly.
rizxt wrote:I also don't understand the meaning behind the values in the TSS. What are the RSP values, and why are there multiple. What is an IST, and why is there so damn many of them?
Have you checked the Intel SDM or AMD APM yet? Make sure you're looking at the 64-bit TSS, it's very different from the others.
OF course! I forgot!
Re: How to write a TSS (in assembly)
Posted: Thu Feb 18, 2021 8:51 pm
by Octocontrabass
rizxt wrote:Written in C. I prefer writing data structures in assembly.
Hmm... It's true that a lot of x86-specific data structures are difficult to represent in C, but I still think it's easier to manipulate those structures in C.
Re: How to write a TSS (in assembly)
Posted: Thu Feb 18, 2021 9:39 pm
by austanss
How do I write the GDT segment descriptor for the TSS? I can't find much info in the Intel/AMD manuals.
Re: How to write a TSS (in assembly)
Posted: Thu Feb 18, 2021 10:03 pm
by Octocontrabass
Intel describes it in Volume 3A, section 7.2.3 "TSS Descriptor in 64-bit mode". AMD describes it in Volume 2, section 4.8.3 "System Descriptors". Both have diagrams showing the layout of individual bits.
It should look familiar since you've already set up other descriptors in your GDT.
Re: How to write a TSS (in assembly)
Posted: Thu Feb 18, 2021 10:10 pm
by austanss
I don't quite understand the diagram.
Re: How to write a TSS (in assembly)
Posted: Thu Feb 18, 2021 10:22 pm
by Octocontrabass
Very strange, aren't they? You basically have to read them backwards, since bit 0 is in the bottom right corner. Each row is four bytes.
Re: How to write a TSS (in assembly)
Posted: Thu Feb 18, 2021 10:29 pm
by austanss
I don't understand how with my current GDT/TSS (link in signature), how I can configure that segment. That's what I am lost on.
Re: How to write a TSS (in assembly)
Posted: Thu Feb 18, 2021 10:44 pm
by Octocontrabass
You add the descriptor to your GDT? I'm not sure how you wrote your GDT in the first place without understanding how to add a descriptor to it.
Re: How to write a TSS (in assembly)
Posted: Fri Feb 19, 2021 2:14 am
by xeyes
Also, it's written in C and generated at runtime. I'm not a fan of that.
The easiest way is to solve this particular problem would be to hand translate that C code to assembly or use gcc -S and copy paste as needed.
Re: How to write a TSS (in assembly)
Posted: Fri Feb 19, 2021 10:24 am
by kzinti
If you have decided to write your OS using only assembly and you aren't sure how to convert a small snippet of code from C to ASM that simply adds new entries to what you already have... You might want to take a pause and question why you are making your life so hard.
Doing it in C is rather simple... Maybe this will help you (you do need two entries in long mode):
Code: Select all
const uintptr_t tss_base = (uintptr_t)tss;
const uintptr_t tss_limit = sizeof(*tss) - 1;
// TSS - low
gdt[5].limit = tss_limit; // Limit (15:0)
gdt[5].base = (uint16_t)tss_base; // Base (15:0)
gdt[5].flags1 = (uint16_t)(0xE900 + ((tss_base >> 16) & 0xFF)); // P + DPL 3 + TSS + base (23:16)
gdt[5].flags2 = (uint16_t)((tss_base >> 16) & 0xFF00); // Base (31:24)
// TSS - high
gdt[6].limit = (uint16_t)(tss_base >> 32); // Base (47:32)
gdt[6].base = (uint16_t)(tss_base >> 48); // Base (63:32)
gdt[6].flags1 = 0x0000;
gdt[6].flags2 = 0x0000;
Definition of the TSS structure (long mode):
Code: Select all
struct Tss64
{
uint32_t reserved0;
uint64_t rsp0; // rsp when entering ring 0
uint64_t rsp1; // rsp when entering ring 1
uint64_t rsp2; // rsp when entering ring 2
uint64_t reserved1;
// The next 7 entries are the "Interrupt stack Table"
// Here we can define stack pointers to use when handling interrupts.
// Which one to use is defined in the Interrupt Descriptor Table.
uint64_t ist1;
uint64_t ist2;
uint64_t ist3;
uint64_t ist4;
uint64_t ist5;
uint64_t ist6;
uint64_t ist7;
uint64_t reserved2;
uint16_t reserved3;
uint16_t iomap;
} __attribute__((packed));
Finally don't forget to initialize the TSS before using it:
Code: Select all
void InitTss()
{
memset(tss, 0, sizeof(*tss);
tss->iomap = 0xdfff; // For now, point beyond the TSS limit (no iomap)
}
You will also want to set "rsp0" in the TSS before returning to user mode. This way the CPU will now where to store the kernel stack when the user program makes system calls (or interrupts occur).
Re: How to write a TSS (in assembly)
Posted: Fri Feb 19, 2021 10:35 am
by nexos
rizxt wrote:Also, it's written in C and generated at runtime. I'm not a fan of that.
Generating a GDT at runtime is your only option. Doing it in C is another good thing as well. The problem with a static GDT comes in at SMP time. You always need one CPU per TSS at least. If you did it statically, you would limit yourself to a fixed number of CPUs, which, isn't optimal at all.
Re: How to write a TSS (in assembly)
Posted: Fri Feb 19, 2021 11:00 am
by bzt
rizxt wrote:Also, it's written in C and generated at runtime. I'm not a fan of that.
Then don't do it. You can easily generate the TSS from Assembly as well, it's just bytes in the memory after all.
Code: Select all
tss_seg:
.long 0 // reserved
.quad 0 // rsp0
.quad 0 // rsp1
.quad 0 // rsp2
.long 0 // reserved
...etc.
And if your linker places that "tss_seg" at a fixed address, you can also generate the GDT statically, for example:
Code: Select all
gdt64:
.word gdt64_end - gdt64_start - 1
.quad gdt64_start
gdt64_start:
.quad 0x0000000000000000 /* null descriptor */
.quad 0x002098000000ffff /* 08 core CS */
.quad 0x008092000000ffff /* 10 core DS */
.quad 0x0080f2000000ffff /* 18 user DS */
.quad 0x0020f8000000ffff /* 20 user CS */
.quad 0x8000890000000068 /* 28 tss, you should use your static tss_seg address here */
.quad 0x00000000ffffffff
gdt64_end:
nexos wrote:You always need one CPU per TSS at least. If you did it statically, you would limit yourself to a fixed number of CPUs, which, isn't optimal at all.
Not necessarily, you could create 255 TSS segments statically (APIC can't have more cores anyway, as APIC ID is a byte and 255 is reserved for broadcast address.) It isn't more complicated than to create 256 IDT entries statically. And with x2APIC dynamically generated or not, the limit of GDT won't be enough, so there you must use the "all cores have different CR3" trick anyway if you want to support all possible cores.
Cheers,
bzt
Re: How to write a TSS (in assembly)
Posted: Fri Feb 19, 2021 3:10 pm
by thewrongchristian
kzinti wrote:
Definition of the TSS structure (long mode):
Code: Select all
struct Tss64
{
uint32_t reserved0;
uint64_t rsp0; // rsp when entering ring 0
uint64_t rsp1; // rsp when entering ring 1
...
} __attribute__((packed));
Oh god, really? I'd have thought the 64-bit pointers would have been 64-bit aligned at least, given that AMD had a free hand to define this. Why would they put a reserved, 32-bit structure at the beginning, and basically force C users to have to pack their structures?
I can sort of perhaps see that it aligns with the 32-bit TSS, and its offsets for ESP[0,1,2], but why did they have to? They literally had no compatibility baggage to worry about.
I think working with anything x86 must rot the brain.