Return to BSD News archive
Path: sserve!newshost.anu.edu.au!harbinger.cc.monash.edu.au!bunyip.cc.uq.oz.au!munnari.oz.au!comp.vuw.ac.nz!newshost.wcc.govt.nz!aladdin.wcc.govt.nz!zheng From: zheng@aladdin.wcc.govt.nz () Newsgroups: comp.os.386bsd.questions Subject: netbsd-0.9 syscall -- LONG !!! -- Date: 23 Jun 1994 10:54:17 GMT Organization: Wellington City Council, Wellington, New Zealand Lines: 213 Sender: Chuck Zheng (zheng@aladdin.wcc.govt.nz) Message-ID: <2ubpkp$ccg@golem.wcc.govt.nz> NNTP-Posting-Host: aladdin.wcc.govt.nz Hello, I have always been wondering how does a user process trap into kernel via a system call on NetBSD (v0.9). After on and off efforts over several months study of NetBSD-0.9 source codes, Bill Jolitzs' DDJ series and a 386 programmer guide, together advises from serveral poeple on the net, I finally sort of patch the whole picture to a recognizable form, as listed bellow. I still have not found where/how is user process LDT set up exactly. I would like to read your comments and advise. cheers, chuck How does netbsd-0.9 do system call ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - user process issues intersegmental (far procedure) call: [DDJ,Jan91,p38] lcall $0x7, 0x0 # [DDJ,Jan91,p39,Fig8] which saves current code segment selector (value of CS) and PC (EIP) value on stack. control then transfers to the destination specified by the operand: (selector-16bit : offset-32bit) pair (0x7, 0x0). In other words, selector 0x7 is loaded into CS register. [386/486,p187] - Actually, to work around of a bug in GAS, user process issues a macro: LCALL(0x7,0x0) this can be expanded to .byte 0x9a ; .long 0x0; .word 0x7 which equals to lcall $0x7, 0x0 ## info source came from [DDJ,Mar91,p82] - This 16-bit selector 0x7 = 0000 0000 0000 0111 (in binary) [386/486,p59] ----------------A-- Index = 0 | RPL = 3 = user mode/level | TI = 1 = Local Descriptor Table Thus the selector points to the slot zero (the first entry) of the current LDT [386/486,p58]. - First entry for *KERNEL* LDT is set up in file /sys/arch/i386/i386/machdep.c ... #define GCODE_SEL 1 /* Kernel Code Descriptor */ #define GLDT_SEL 3 /* LDT - eventually one per process */ ... /* local descriptor table */ union descriptor ldt[5]; #define LSYS5CALLS_SEL 0 /* forced by intel BCS */ ... init386(first) { ... struct gate_descriptor *gdp; ... lldt(GSEL(GLDT_SEL, SEL_KPL)); ... /* make a call gate to reenter kernel with */ gdp = &ldt[LSYS5CALLS_SEL].gd; x = (int) &IDTVEC(syscall); gdp->gd_looffset = x++; gdp->gd_selector = GSEL(GCODE_SEL,SEL_KPL); gdp->gd_stkcpy = 0; gdp->gd_type = SDT_SYS386CGT; gdp->gd_dpl = SEL_UPL; gdp->gd_p = 1; gdp->gd_hioffset = ((int) &IDTVEC(syscall)) >>16; ... } macros and struct are defined in /sys/arch/i386/include/segments.h: #define SEL_KPL 0 /* kernel priority level */ #define SEL_UPL 3 /* user priority level */ #define GSEL(s,r) (((s)<<3) | r) /* a global selector */ ... #define SDT_SYS386CGT 12 /* system 386 call gate */ lldt() is defined in /sys/arch/i386/include/locore.s #define ENTRY(name) .globl _/**/name; ALIGN_TEXT; _/**/name: ... /* * void lldt(u_short sel) */ ENTRY(lldt) lldt 4(%esp) ret init386() is invoked during system startup by /sys/arch/i386/i386/locore.s. It sets up the first entry of LDT as a call gate descriptor, with selector GSEL(GCODE_SEL,SEL_KPL) points to a kernel code segment and an offset into the segment. [386/486,p112] The offset is the kernel space address for interrupt descriptor table IDT entry: &IDTVEC(syscall) which is defined in /sys/arch/i386/i386/locore.s. (some macros in locore.s and machdep.c interprets IDTVEC). - The setup for kernal LDT is copied/inherited by subsequent user process during fork(). /sys/kern/kern_fork.c reads: int fork1(p1, isvfork, retval) register struct proc *p1; int isvfork, retval[]; { register struct proc *p2; ... /* Allocate new proc. */ MALLOC(p2, struct proc *, sizeof(struct proc), M_PROC, M_WAITOK); ... /* * Make a proc table entry for the new process. * Start by zeroing the section of proc that is zero-initialized, * then copy the section that is copied directly from the parent. */ bzero(&p2->p_startzero, (unsigned) ((caddr_t)&p2->p_endzero - (caddr_t)&p2->p_startzero)); bcopy(&p1->p_startcopy, &p2->p_startcopy, (unsigned) ((caddr_t)&p2->p_endcopy - (caddr_t)&p2->p_startcopy)); ... } struct proc is defined in /usr/src/sys/sys/proc.h. A proc slot has a pointer to struct user: struct user *p_addr; /* kernel virtual addr of u-area (PROC ONLY) */ struct user is defined in /usr/src/sys/sys/user.h. user has a field: struct pcb u_pcb; struct pcb is defined in /sys/arch/i386/include/pcb.h. pcb has a field: struct i386tss pcb_tss; struct i386tss is defined in /sys/arch/i386/include/tss.h. i386tss has a field: int tss_ldt; /* actually 16 bits: top 16 bits must be zero*/ This field holds the selector for LDTR. Since bcopy only copys "struct user *p_addr" from p1 to p2, I guess XXX some routhines overlooked by me must do the actual "copy" of tss_ldt from p1 to p2, or there is a copy-on-write scheme implemented (is that vfork?). Can somebody clarify this further (and point out error I have made above)? There is also a cpu_fork() routine in /sys/arch/i386/i386/vm_machdep.c, which does some copying similar to fork1(): cpu_fork(p1, p2) register struct proc *p1, *p2; { ... /* * Copy pcb and stack from proc p1 to p2. * We do this as cheaply as possible, copying only the active * part of the stack. The stack and pcb need to agree; * this is tricky, as the final pcb is constructed by savectx, * but its frame isn't yet on the stack when the stack is copied. * swtch compensates for this when the child eventually runs. * This should be done differently, with a single call * that copies and updates the pcb+stack, * replacing the bcopy and savectx. */ p2->p_addr->u_pcb = p1->p_addr->u_pcb; offset = mvesp() - (int)kstack; bcopy((caddr_t)kstack + offset, (caddr_t)p2->p_addr + offset, (unsigned) ctob(UPAGES) - offset); p2->p_regs = p1->p_regs; ... } I have not been able to find out who calls this routine. Does anyone knows what is it for and how is it used? Reference ~~~~~~~~~ [DDJ] -- Dr. Dobb's Journal [386/486] -- Microsoft's 80386/80486 Programming Guide. Ross Nelson.