/joh'liks/ n.,adj. 386BSD
PORTING UNIX TO THE 386: A PRACTICAL APPROACH
William & Lynne Jolitz
Here's an oddity we had in the initial port. We first avoided the 386 TSS context switch because it was slow. With the 486 and later, it wasn't so we used it. But we needed at lead one to transit rings.
Initial Task State Load
Even if we don't wish to use the 386's special context switch feature, we must initialize a root task state. Why? Because once we are in a user-mode process, only the TSS (Task State Segment) contains the information on where the stack is in the supervisor (kernel) processor ring.|
Interestingly, the processor will indeed go into user mode, functioning just fine until a trap, interrupt, or system call occurs. Most other processors have dedicated register sets to locate the kernel stack in these cases. But the 386 designers conceptualized ring crossings (user <-> kernel mode) like that of task switches. Thus, we include the supervisor "entry into ring" stack pointer in the TSS.
In the TSS structure (see Listing Ten), we assign a kernel/supervisor stack top well below our current stack to avoid conflicts. We select this as the current task segment, and then use a little trick to fill out the remainder of this large structure by arranging to context switch back to our task segment, using our assembly language stub jmptss. jmptss always saves the task state of the current task and then loads the state yet again. Because the new state must not already be BUSY in order to use this trick, we force it to be AVAILABLE. UNIX kernels use a function, called resume(), to provide for this mechanism. However, jmptss also provides for context switching when we wish to transfer from one process or task to the next one. The general case, when we call jmptss with a new TSS selector, not the current one, will be covered in a later article.
/* [excerpted from i386.c] */
/* make a initial tss so 386 can get interrupt stack on syscall! */
tss.tss_esp0 = (int) &x - 4096;
tss.tss_ss0 = GSEL(GDATA_SEL, SEL_KPL) ;
tss.tss_cr3 = (int) cr3;
printf("ltr "); getchar();
printf("resume() "); getchar();
/* set busy type to avail */
gdt[GPROC0_SEL].sd.sd_type = SDT_SYS386TSS;
/* jump to self to fill out tss, like BSD resume() */
# excerpted from srt.s
/* jmptss(sel)-- Jump to TSS so that we can load/unload context */
.globl _jmptss /* similar to BSD swtch()/resume() */
ljmp 0(%esp) /* ljmp tss */
/* saved pc points here */