The questions arose when I am reading xv6 (x86 version) about how registers of user process are saved. The situation is that while a common user program is running, a hardware interrupt occurs like timer. From my reading of xv6 code and its book, my understanding is as follows:
Since the processor will transfer from user privilege level (3) to kernel privilege level (0), this is a privilege change, so the user process's %ss
and %esp
should be saved. It should be saved to the stack for privilege level 0 because user process's stack is unreliable and insecure. But how does the process know where the stack for privilege level 0 (kernel stack) is? I think it is set to TSS by the following code in function switchuvm
(please correct me now if I am wrong, so that I know where my question originates):
1869 pushcli(); 1870 mycpu()−>gdt[SEG_TSS] = SEG16(STS_T32A, &mycpu()−>ts, 1871 sizeof(mycpu()−>ts)−1, 0); 1872 mycpu()−>gdt[SEG_TSS].s = 0; 1873 mycpu()−>ts.ss0 = SEG_KDATA << 3; 1874 mycpu()−>ts.esp0 = (uint)p−>kstack + KSTACKSIZE; 1875 // setting IOPL=0 in eflags *and* iomb beyond the tss segment limit 1876 // forbids I/O instructions (e.g., inb and outb) from user space 1877 mycpu()−>ts.iomb = (ushort) 0xFFFF; 1878 ltr(SEG_TSS << 3); 1879 lcr3(V2P(p−>pgdir)); // switch to process’s address space 1880 popcli();
When an interrupt occurs, the processor saves the old %ss
and %esp
somewhere (this is what looks strange to me, because nowhere in the online document I read talks about this. But let's ignore this), reads the address of kernel stack from TSS set above and let %ss
and %esp
point to it, and pushes the saved old %ss
and %esp
to the new kernel stack. Continuing, the processor will save %eflags
, %cs
and %eip
of the user process to the kernel stack.
I copy the diagram of the kernel stack in vx6's book below for better illustration.
As the title of the diagram confirms, it is the "kernel stack" that saves user stack, and the address of kernel stack comes only from the TSS, set in switchuvm
function, to the best of my knowledge.
According to the online document here: http://www.logix.cz/michal/doc/i386/chp07-05.htm, x86 task switching occurs only in four cases. But xv6 only load task register at line 1878 and all the descriptors in xv6's IDT are either trap or interrupt gate, i.e., none of the four cases happen. I think merely loading TR does not trigger a task switching (right?). However, I think mechanisms/data structures of TR or TSS in i386 is for task switching (multitasking). If there is no task switching, TSS should have not been used at all (right?).
Then here comes my questions:
- Is my claim that task switching does not occur in xv6 when an interrupt occurs while a user program is running, as well as how TSS gets involved, correct?
- If task switching does not occur, why does TSS get involved to provide the address of kernel stack? Is this usage of TSS unrelated to task switching described somewhere in any x86 documentation?
Thank you.
PS: I'm working on an intel i386 compatible machine.