* Copyright (c) 1990 The Regents of the University of California.
* This code is derived from software contributed to Berkeley by
* @(#)locore.s 5.6 (Berkeley) %G%
* locore.s: 4BSD machine support for the Intel 386
* Written by William F. Jolitz, 386BSD Project
#include "../i386/trap.h"
* Note: This version greatly munged to avoid various assembler errors
* that may be fixed in newer versions of gas. Perhaps newer versions
* will have more pleasant appearance.
.set SYSTEM,0xFE000000 # virtual address of system start
/*note: gas copys sign bit (e.g. arithmetic >>), can't do SYSTEM>>22! */
.set SYSPDROFF,0x3F8 # Page dir
/* IBM "compatible" nop - sensitive macro on "fast" 386 machines */
#define NOP jmp 7f ; nop ; 7: jmp 7f ; nop ; 7:
* User structure is UPAGES at top of user space.
.globl _/**/name; _/**/name:
.globl _/**/name; _/**/name:
* Mbmap and Usrptmap are enlarged by CLSIZE entries
* as they are managed by resource maps starting with index 1 or CLSIZE.
#define SYSMAP(mname, vname, npte) \
_/**/mname: .globl _/**/mname; \
.set _/**/vname,ptes*NBPG+SYSTEM; \
#define ZSYSMAP(mname, vname, npte) \
_/**/mname: .globl _/**/mname; \
.set _/**/vname,ptes*NBPG+SYSTEM; \
# assumed to start at data mod 4096
SYSMAP(Sysmap,Sysbase,SYSPTSIZE)
SYSMAP(Forkmap,forkutl,UPAGES)
SYSMAP(Xswapmap,xswaputl,UPAGES)
SYSMAP(Xswap2map,xswap2utl,UPAGES)
SYSMAP(Swapmap,swaputl,UPAGES)
SYSMAP(Pushmap,pushutl,UPAGES)
SYSMAP(Vfmap,vfutl,UPAGES)
SYSMAP(alignmap,alignutl,1) /* XXX */
SYSMAP(msgbufmap,msgbuf,MSGBUFPTECNT)
/* SYSMAP(EMCmap,EMCbase,1) */
SYSMAP(Npxmap,npxutl,UPAGES)
SYSMAP(Swtchmap,Swtchbase,UPAGES)
.set mbxxx,(NMBCLUSTERS*MCLBYTES)
.set mbyyy,(mbxxx>>PGSHIFT)
.set mbpgs,(mbyyy+CLSIZE)
SYSMAP(Mbmap,mbutl,mbpgs)
* XXX: NEED way to compute kmem size from maxusers,
SYSMAP(kmempt,kmembase,300*CLSIZE)
SYSMAP(profmap,profbase,600*CLSIZE)
.set atmemsz,0x100000-0xa0000
.set atpgs,(atmemsz>>PGSHIFT)
SYSMAP(ATDevmem,atdevbase,atpgs)
SYSMAP(Usriomap,usrio,USRIOSIZE+CLSIZE) /* for PHYSIO */
ZSYSMAP(ekmempt,kmemlimit,0)
SYSMAP(Usrptmap,usrpt,USRPTSIZE+CLSIZE)
# .set _Syssize,(eSysmap-_Sysmap)/4
/* align on next page boundary */
# . = . + NBPG - 1 & -NBPG /* align to page boundry-does not work*/
# .space (PGSIZE - ((eSysmap-_Sysmap) % PGSIZE)) % PGSIZE
.globl _cpu, _boothowto, _bootdev, _cyloffset, _Maxmem
_cpu: .long 0 # are we 386, 386sx, or 486
start: # This is assumed to be location zero!
movw %ax,0x472 # warm boot
.space 0x500 # skip over warm boot shit
/* enable a20! yecchh!! */
/* pass parameters on stack (howto, bootdev, unit, cyloffset) */
movl %eax,_boothowto-SYSTEM
movl %eax,_bootdev-SYSTEM
movl %eax, _cyloffset-SYSTEM
xorl %eax,%eax # start with base memory at 0x0
#movl $ 0xA0000/NBPG,%ecx # look every 4K up to 640K
movl $ 0xA0,%ecx # look every 4K up to 640K
1: movl 0(%eax),%ebx # save location to check
movl $0xa55a5aa5,0(%eax) # write test pattern
cmpl $0xa55a5aa5,0(%eax) # does not check yet for rollover
movl %ebx,0(%eax) # restore memory
movl $0x100000,%eax # next, talley remaining memory
#movl $((0xFFF000-0x100000)/NBPG),%ecx
1: movl 0(%eax),%ebx # save location to check
movl $0xa55a5aa5,0(%eax) # write test pattern
cmpl $0xa55a5aa5,0(%eax) # does not check yet for rollover
movl %ebx,0(%eax) # restore memory
addl $(UPAGES*NBPG)+NBPG+NBPG+NBPG,%ecx
# txt+data+proc zero pt+u.
# shrl $2,%ecx # convert to long word count
/* pass parameters on stack (howto, bootdev, unit, cyloffset) */
movl %eax,_boothowto-SYSTEM
movl %eax,_bootdev-SYSTEM
movl %eax, _cyloffset-SYSTEM
* N.B. don't bother with making kernel text RO, as 386
* ignores R/W AND U/S bits on kernel access (only v works) !
* First step - build page tables
movl %esi,%ecx # this much memory,
shrl $ PGSHIFT,%ecx # for this many pte s
movl $ PG_V,%eax # having these bits set,
movl $_Sysmap-SYSTEM,%ebx # in the kernel page table,
# fill in kernel page table.
addl $ NBPG,%eax # increment physical address
/* temporary double map virt == real */
movl $1024,%ecx # for this many pte s,
movl $ PG_V,%eax # having these bits set,
movl $_Sysmap+4096-SYSTEM,%ebx # in the temporary page table,
# fill in kernel page table.
addl $ NBPG,%eax # increment physical address
movl $atpgs,%ecx # for this many pte s,
movl $(IOPHYSmem|PG_V),%eax # having these bits set, (perhaps URW?)
movl $_ATDevmem-SYSTEM,%ebx # in the temporary page table,
# fill in kernel page table.
addl $ NBPG,%eax # increment physical address
/* map proc 0's page table (P1 region) */
movl $_Usrptmap-SYSTEM,%ebx # get pt map address
lea (0*NBPG)(%esi),%eax # physical address of pt in proc 0
orl $ PG_V,%eax # having these bits set,
movl $ UPAGES,%ecx # for this many pte s,
lea (2*NBPG)(%esi),%eax # physical address of _u in proc 0
orl $ PG_V|PG_URKW,%eax # having these bits set,
lea (0*NBPG)(%esi),%ebx # physical address of stack pt in proc 0
# fill in proc 0 stack page table.
addl $ NBPG,%eax # increment physical address
/*# map proc 0's page directory*/
lea (1*NBPG)(%esi),%eax # physical address of ptd in proc 0
movl %eax,%edi # remember ptd physical address
orl $ PG_V|PG_URKW,%eax # having these bits set,
lea (0*NBPG)(%esi),%ebx # physical address of stack pt in proc 0
* Construct a page table directory
* (of page directory elements - pde's)
movl $_Sysmap-SYSTEM,%eax # physical address of kernel page table
orl $ PG_V,%eax # pde entry is valid
movl $ Npdes,%ecx # for this many pde s,
movl %edi,%ebx # phys address of ptd in proc 0
addl $(SYSPDROFF*4), %ebx # offset of pde for kernel
addl $ NBPG,%eax # increment physical address
# install a pde for temporary double map
movl $_Sysmap+4096-SYSTEM,%eax # physical address of temp page table
# movl $_Sysmap-SYSTEM,%eax # physical address of temp page table
orl $ PG_V,%eax # pde entry is valid
movl %edi,%ebx # phys address of ptd in proc 0
movl %eax,0(%ebx) # which is where temp maps!
# install a pde to map _u for proc 0
lea (0*NBPG)(%esi),%eax # physical address of pt in proc 0
orl $ PG_V,%eax # pde entry is valid
movl %edi,%ebx # phys address of ptd in proc 0
addl $(UPDROFF*4), %ebx # offset of pde for kernel
movl %eax,0(%ebx) # which is where _u maps!
movl %edi,%eax # phys address of ptd in proc 0
movl %eax,%cr3 # load ptd addr into mmu
movl %cr0,%eax # get control word
orl $0x80000001,%eax # and let s page!
pushl $begin # jump to high mem!
ret # jmp $begin does not work
movl $_Sysbase,%eax # kernel stack just below system
xorl %eax,%eax # mark end of frames
movl _Crtat,%eax # initialize Crt video ram address
call _init386 # wire 386 chip for unix operation
/* initialize (slightly) the pcb */
movl $_u,%eax # proc0 u-area
movl %ecx,PCB_P0BR(%eax) # p0br: SVA of text/data user PT
movl %ecx,PCB_P0LR(%eax) # p0lr: 0 (doesn t really exist)
movl $_usrpt+NBPG,%ecx # addr of end of PT
subl $ P1PAGES*4,%ecx # backwards size of P1 region
movl %ecx,PCB_P1BR(%eax) # p1br: P1PAGES from end of PT
movl $ P1PAGES-UPAGES,PCB_P1LR(%eax) # p1lr: vax style
movl $ CLSIZE,PCB_SZPT(%eax) # page table size
# fnsave PCB_SAVEFPU(%eax)
addl $(UPAGES*NBPG)+NBPG+NBPG+NBPG,%eax
pushl $20 # install signal trampoline code
.globl __ucodesel,__udatasel
# build outer stack frame
movw %ax,%fs # double map cs to fs
movw %cx,%gs # and ds to gs
lidt xaxa # invalidate interrupt descriptor
movl $0,%esp # hardware "freeze" fault
/* gas fucks up offset -- */
#define LCALL(x,y) .byte 0x9a ; .long y; .word x
* Icode is copied out to process 1 to exec /etc/init.
* If the exec fails, process 1 exits.
pushl %eax # dummy out rta
pushl %eax # dummy out rta
movl 12(%esp),%eax # unsure if call will dec stack 1st
xorl %eax,%eax # smaller movl $103,%eax
movb $103,%al # sigreturn()
LCALL(0x7,0) # enter kernel with args on stack
# NOTE: does not (yet) handle overlapped copies
movl $cpyflt,_nofault # in case we page/protection violate
movl $cpyflt,_nofault # in case we page/protection violate
.byte 0x66,0xf2,0x6d # rep insw
.byte 0x66,0xf2,0x6f # rep outsw
movl $fusufault,_nofault # in case we page/protection violate
movl $fusufault,_nofault # in case we page/protection violate
movl $fusufault,_nofault # in case we page/protection violate
movl $fusufault,_nofault # in case we page/protection violate
movl $fusufault,_nofault # in case we page/protection violate
movl $fusufault,_nofault # in case we page/protection violate
movl %ebx, 0(%eax) # save ebx
movl %esp, 4(%eax) # save esp
movl %ebp, 8(%eax) # save ebp
movl %esi,12(%eax) # save esi
movl %edi,16(%eax) # save edi
movl (%esp),%edx # get rta
movl %edx,20(%eax) # save eip
xorl %eax,%eax # return (0);
movl 0(%eax),%ebx # restore ebx
movl 4(%eax),%esp # restore esp
movl 8(%eax),%ebp # restore ebp
movl 12(%eax),%esi # restore esi
movl 16(%eax),%edi # restore edi
movl 20(%eax),%edx # get rta
movl %edx,(%esp) # put in return frame
xorl %eax,%eax # return (1);
* The following primitives manipulate the run queues.
* _whichqs tells which of the 32 queues _qs
* have processes in them. Setrq puts processes into queues, Remrq
* removes them from queues. The running process is on no queue,
* other processes are on a queue related to p->p_pri, divided by 4
* actually to shrink the 0-127 range of priorities into the 32 available
.globl _whichqs,_qs,_cnt,_panic
* Call should be made at spl6(), and p->p_stat should be SRUN
cmpl $0,P_RLINK(%eax) # should not be on q already
btsl %edx,_whichqs # set q full bit
addl $_qs,%edx # locate q hdr
movl %edx,P_LINK(%eax) # link process on tail of q
* Call should be made at spl6().
btrl %edx,_whichqs # clear full bit, panic if clear already
movl P_LINK(%eax),%ecx # unlink process
cmpl P_LINK(%ecx),%ecx # q still has something?
shrl $3,%edx # yes, set bit as still full
movl $0,P_RLINK(%eax) # zap reverse link to indicate off list
* When no processes are on the runq, Swtch branches to idle
* to wait for something to come ready.
bsfl _whichqs,%eax # find a full q
btrl %eax,_whichqs # clear q full status
jnb sw1 # if it was clear, look for another
pushl %eax # save which one we are using
addl $_qs,%eax # select q
cmpl P_LINK(%eax),%eax # linked to self? (e.g. not on list)
movl P_LINK(%eax),%ecx # unlink from front of process q
cmpl P_LINK(%ecx),%eax # q empty
btsl %edx,_whichqs # nope, indicate full
movl _Swtchbase+PCB_CR3,%edx
# pushal; pushl %edx ; pushl P_CR3(%ecx); pushl $l2; call _pg; popl %eax ; popl %eax; popl %eax ; popal ; .data ; l2: .asciz "s %x %x " ; .text
/* switch to new process. first, save context as needed */
movl (%esp),%eax # Hardware registers
movl _CMAP2,%eax # save temporary map PTE
movl %eax,PCB_CMAP2(%ecx) # in our context
movl %edx,%cr3 # context switch
movl PCB_CMAP2(%ecx),%eax # get temporary map
movl %eax,_CMAP2 # reload temporary map PTE
cmpl $0,PCB_SSWAP(%ecx) # do an alternate return?
jne res3 # yes, go reload regs
xorl %eax,%eax # inline restore context
xchgl PCB_SSWAP(%ecx),%eax # addr of saved context, clear it
#pushal; pushl 20(%eax); pushl $l2; call _printf; popl %eax ; popl %eax; popal ; .data ; l2: .asciz "s %x\n" ; .text
movl 0(%eax),%ebx # restore ebx
movl 4(%eax),%esp # restore esp
movl 8(%eax),%ebp # restore ebp
movl 12(%eax),%esi # restore esi
movl 16(%eax),%edi # restore edi
movl 20(%eax),%edx # get rta
movl %edx,(%esp) # put in return frame
xorl %eax,%eax # return (1);
* current just used to fillout u. tss so fork can fake a return to swtch
* [ all thats really needed is esp and eip ]
#define IDTVEC(name) .align 4; .globl _X/**/name; _X/**/name:
/*#define PANIC(msg) xorl %eax,%eax; movl %eax,_waittime; pushl 1f; \
call _panic; 1: .asciz msg*/
#define PRINTF(n,msg) pushal ; pushl 1f; call _printf; MSG(msg) ; popl %eax ; popal
#define MSG(msg) .data; 1: .asciz msg; .text
* Trap and fault vector routines
#define TRAP(a) pushl $ a; jmp alltraps
pushl $0; TRAP(T_TRCTRAP)
pushl $0; TRAP(T_PRIVINFLT)
/*PANIC("Double Fault");*/
pushl $0; TRAP(T_FPOPFLT)
/*PANIC("TSS not valid");*/
pushl $0; TRAP(T_RESERVED)
pushl $0; TRAP(T_ARITHTRAP)
/* 17 - 31 reserved for future exp */
cmpl $0xfc000000,12*4(%esp) # is it a user pc
cmpw $0x1f,13*4(%esp) # is it a user cs
.data ; lx: .asciz "t user cs %x?" ; .text
addl $8,%esp # pop type, code
* Call gate entry for syscall
pushfl # only for stupid carry bit and more stupid wait3 cc kludge
pushal # only need eax,ecx,edx - trap resaves others
movw $0x10,%ax # switch to kernel segments
cmpw $0x1f,10*4(%esp) # is user cs what it should be?
.data ; lz: .asciz "s user cs %x?" ; .text
movw __udatasel,%ax # switch back to user segments
lret # back we go, we hope!