/joh'liks/ n.,adj. 386BSD

Porting Unix to the 386: A Practical Approach



William & Lynne Jolitz


System calls were handled by a call gate, a 386 peculiar mechanism which allowed for ring crossing into supervisor mode using a special segment descriptor call. We simulated user mode to test prior to running in the kernel.




System Call Handling
To test system calls, we must first enter user mode by generating an outbound ring crossing. The touser() function (see Listing Thirteen) does this by switching to our previously set-up user ring address space found in the LDT and enters user mode in the function named usercode(). (The LDT, by a remarkable coincidence, exactly corresponds to our standalone system's "kernel" address space!) A special kind of stack frame is built that imitates the 386's inbound ring-crossing processing. In other words, we "fake out" the processor into believing it has just come from user mode. This done, we calmly return with an intersegment return, executing in the new mode at the beginning of the function.

Listing 13:
test386(){
 ...
       /* transfer to user mode to test system call */
       printf("touser "); getchar();
       touser (LSEL(LUCODE_SEL,SEL_UPL), LSEL(LUDATA_SEL, SEL_UPL), &usercode);
 ...
# [excerpted from srt.s]

   /* touser (cs,ds,func) */
   .globl   _touser
_touser:
   pushal
   movl   %esp,_myspback
   movl   32+4(%esp),%eax
   movl   32+8(%esp),%edx
   movl   32+12(%esp),%ecx
   # build outer stack frame
   pushl   %edx      # user ss
   pushl   %esp      # user esp
   pushl   %eax      # cs
   pushl   %ecx      # ip
   movw   %dx,%ds
   movw   %dx,%es
   lret   # goto user!

/* code to execute in user mode */
   .globl   _usercode
#define   LCALL(x,y)   .byte 0x9a ; .long y; .word x
_usercode:
   LCALL(0x7,0x0)   /* would be lcall $0x7,0x0 except for assembler bug */
IDTVEC(syscall)
   pushal
   movw   $0x10,%ax
   movw   %ax,%ds
   movw   %ax,%es
   call   _syscall
   movl   _myspback,%esp   /* non-local goto touser() exit */
   popal
   ret
/* back to i386.c */
 ...
/* System call processing */
syscall() {
   printf("syscall\n");
}
usercode() does not tarry in the user ring for long; it immediately calls the system call gate previously set up in the LDT. This call gate regulates entry into the kernel at location IDTVEC (syscall), which in turn calls the C function syscall() to properly enter the kernel.

Normally, at the end of the system call assembly stub we would return to the user ring program, but since we have concluded testing the user ring transition, we instead return to the touserp() function caller via a nonlocal goto. We have carefully preserved the stack frame further up the stack, so we can test other parts of the kernel mechanism in the test386() function.




<<BACK NEXT >>



Copyright 1989, 1990, 2006 TeleMuse Partners, William Jolitz and Lynne Jolitz