In the xv6 source code for x86 architecture, in the switchuvm function, the TSS is being updated (as well as the page table but that's not my concern here). I've been struggling with the concept of the GDT and TSS and so I want to attempt to lay out what I think is happening in this function.
switchuvm(struct proc *p)
{
...
mycpu()->gdt[SEG_TSS] = SEG16(STS_T32A, &mycpu()->ts,
sizeof(mycpu()->ts)-1, 0); // 1
mycpu()->gdt[SEG_TSS].s = 0; // 2
mycpu()->ts.ss0 = SEG_KDATA << 3; // 3
mycpu()->ts.esp0 = (uint)p->kstack + KSTACKSIZE; // 4
...
mycpu()->ts.iomb = (ushort) 0xFFFF;
ltr(SEG_TSS << 3); // 5
...
}
In line 1, I believe we are setting the segment descriptor in the GDT (which is shared by all cores on a multiprocessing CPU) to point to the new process's TSS. Then, in line 5, the LTR instruction is passed an index into the GDT which points to the TSS segment descriptor. The core then loads the base address and limit into the Task Register so that the core will be able to find the TSS during subsequent interrupts. This only works because these calls are wrapped by a spinlock in the scheduler function which is the only function that calls switchuvm
So while the Global Descriptor Table is:
-Shared by all the cores of the CPU (by virtue of being in the shared kernel Virtual Address Space)
-The first 4 segment descriptors are never changed because paging is used and so all the kernel threads share the same virtual address space (and these are used for user/kernel code/data) and all user threads have the same illusion of a VAS
-The 5th segment of the GDT is in fact changed during every context switch in order to update TR's (which are individual to each core). The reason this works is because the atomicity surrounding updating the GDT (which is in shared kernel VAS) and loading the updated entry into the TR
Is this a correct understanding?
Also, what is line 4 doing? I've thought the kernel stack pointer (i.e. esp) was stored in the context pointer in each proc structure, so I'm confused by what esp0 is referring to.