* Copyright (c) 1990 The Regents of the University of California.
* This code is derived from software contributed to Berkeley by
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* @(#)locore.s 7.3 (Berkeley) 5/13/91
* PATCHES MAGIC LEVEL PATCH THAT GOT US HERE
* -------------------- ----- ----------------------
* CURRENT PATCH LEVEL: 3 00117
* -------------------- ----- ----------------------
* 06 Aug 92 Pace Willisson Allow VGA memory to be mapped
* 28 Nov 92 Frank MacLachlan Aligned addresses and data
* 25 Mar 93 Kevin Lahey Add syscall counter for vmstat
* locore.s: 4BSD machine support for the Intel 386
* Written by William F. Jolitz, 386BSD Project
#include "machine/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 index of System Base
#define NOP inb $0x84, %al ; inb $0x84, %al
#define ALIGN32 .align 2 /* 2^2 = 4 */
* PTmap is recursive pagemap at top of virtual address space.
* Within PTmap, the page directory can be found (third indirection).
.set PDRPDROFF,0x3F7 # Page dir index of Page dir
.globl _PTmap, _PTD, _PTDpde, _Sysmap
.set _PTDpde,0xFDFF7000+4*PDRPDROFF
* APTmap, APTD is the alternate recursive pagemap.
* It's used when modifying another process's page tables.
.set APDRPDROFF,0x3FE # Page dir index of Page dir
.globl _APTmap, _APTD, _APTDpde
.set _APTDpde,0xFDFF7000+4*APDRPDROFF
* Access to each processes kernel stack is via a region of
* per-process address space (at the beginning), immediatly above
* the user process stack.
.set PPTEOFF,0x400-UPAGES # 0x3FE
.globl _/**/name; _/**/name:
.globl _/**/name; _/**/name:
.globl _cpu,_cold,_boothowto,_bootdev,_cyloffset,_atdevbase,_atdevphys
_cpu: .long 0 # are we 386, 386sx, or 486
_cold: .long 1 # cold till we are not
_atdevbase: .long 0 # location of start of iomem in virtual
_atdevphys: .long 0 # location of device mapping ptes (phys)
.globl _IdlePTD, _KPTphys
movw %ax,0x472 # warm boot
.space 0x500 # skip over warm boot shit
* pass parameters on stack (howto, bootdev, unit, cyloffset)
* note: 0(%esp) is return address of boot
* ( if we want to hold onto /boot, it's physical %esp up to _end)
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
/* flush stupid cache here! (with bcopy (0,0,512*1024) ) */
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
/* find end of kernel image */
/* clear bss and memory for bootstrap pagetables. */
addl $(UPAGES+5)*NBPG,%ecx
* Virtual address space of kernel:
* text | data | bss | page dir | proc0 kernel stack | usr stk map | Sysmap
movl %esi,_IdlePTD-SYSTEM /*physical address of Idle Address space */
movl $ tmpstk-SYSTEM,%esp # bootstrap stack end location
addl $ NBPG,%eax ; /* increment physical address */ \
addl $4,%ebx ; /* next pte */ \
* 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
addl $ UPAGES+4,%ecx # including our early context
movl $ PG_V,%eax # having these bits set,
lea (4*NBPG)(%esi),%ebx # physical address of KPT in proc 0,
movl %ebx,_KPTphys-SYSTEM # in the kernel page table,
movl $0x100-0xa0,%ecx # for this many pte s,
movl $(0xa0000|PG_V|PG_UW),%eax # having these bits set,(perhaps URW?) XXX 06 Aug 92
movl %ebx,_atdevphys-SYSTEM # remember phys addr of ptes
/* map proc 0's kernel stack into user page table page */
movl $ UPAGES,%ecx # for this many pte s,
lea (1*NBPG)(%esi),%eax # physical address in proc 0
movl %edx,_proc0paddr-SYSTEM # remember VA for 0th process init
orl $ PG_V|PG_URKW,%eax # having these bits set,
lea (3*NBPG)(%esi),%ebx # physical address of stack pt in proc 0
* Construct a page table directory
* (of page directory elements - pde's)
/* install a pde for temporary double map of bottom of VA */
lea (4*NBPG)(%esi),%eax # physical address of kernel page table
orl $ PG_V|PG_UW,%eax # pde entry is valid XXX 06 Aug 92
movl %eax,(%esi) # which is where temp maps!
movl $ 3,%ecx # for this many pde s,
lea (SYSPDROFF*4)(%esi), %ebx # offset of pde for kernel
/* install a pde recursively mapping page directory as a page table! */
movl %esi,%eax # phys address of ptd in proc 0
orl $ PG_V|PG_UW,%eax # pde entry is valid XXX 06 Aug 92
movl %eax, PDRPDROFF*4(%esi) # which is where PTmap maps!
/* install a pde to map kernel stack for proc 0 */
lea (3*NBPG)(%esi),%eax # physical address of pt in proc 0
orl $ PG_V,%eax # pde entry is valid
movl %eax,PPDROFF*4(%esi) # which is where kernel stack maps!
/* load base of page directory, and enable mapping */
movl %esi,%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!
begin: /* now running relocated at SYSTEM where the system is linked to run */
movl _atdevphys,%edx # get pte PA
subl _KPTphys,%edx # remove base of ptes, now have phys offset
shll $ PGSHIFT-2,%edx # corresponding to virt offset
addl $ SYSTEM,%edx # add virtual base
/* set up bootstrap stack */
movl $ _kstack+UPAGES*NBPG-4*12,%esp # bootstrap stack end location
xorl %eax,%eax # mark end of frames
lea 7*NBPG(%esi),%esi # skip past stack.
call _init386 # wire 386 chip for unix operation
.globl __ucodesel,__udatasel
# build outer stack frame
pushl $ USRSTACK # user esp
movw %ax,%fs # double map cs to fs
movw %cx,%gs # and ds to gs
pushl $lretmsg1 /* "should never get here!" */
#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 $argv-_icode # gas fucks up again
pushl %eax # dummy out rta
pushl %eax # dummy out rta
.long init+6-_icode # argv[0] = "init" ("/sbin/init" + 6)
.long eicode-_icode # argv[1] follows icode after copyout
.globl _sigcode,_szsigcode
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
.long _szsigcode-_sigcode
* Support routines for GCC
#xorl %edx,%edx /* not needed - cltd sign extends into %edx */
* I/O bus instructions via C
_rtcin: movl 4(%esp),%eax
inb $0x71,%al # Compaq SystemPro
* void bzero(void *base, u_int cnt)
* (ov)bcopy (src,dst,cnt)
* ws@tools.de (Wolfgang Solfrank, TooLs GmbH) +49-228-985800
cmpl %esi,%edi /* potentially overlapping? */
cld /* nope, copy forwards. */
shrl $2,%ecx /* copy by words */
andl $3,%ecx /* any bytes left? */
addl %ecx,%edi /* copy backwards. */
andl $3,%ecx /* any fractional bytes? */
movl 20(%esp),%ecx /* copy remainder by words */
movl $cpyflt, PCB_ONFAULT(%eax) # in case we page/protection violate
/* first, check to see if "write fault" */
shrl $IDXSHIFT, %eax /* fetch pte associated with address */
andb $7, %al /* if we are the one case that won't trap... */
/* ... then simulate the trap! */
call _trapwrite /* trapwrite(addr) */
cmpl $0, %eax /* if not ok, return */
/* otherwise, continue with reference */
movl %edi, %eax /* calculate remainder this pass */
movl %edx, %ecx /* don't depend on ecx here! */
movl %eax,PCB_ONFAULT(%edx)
movl $cpyflt,PCB_ONFAULT(%eax) # in case we page/protection violate
movl %eax,PCB_ONFAULT(%edx)
movl $0,PCB_ONFAULT(%edx)
movl $cpyflt,PCB_ONFAULT(%eax) # in case we page/protection violate
movl %eax,PCB_ONFAULT(%edx)
movl $cpyflt,PCB_ONFAULT(%eax) # in case we page/protection violate
movl %eax,PCB_ONFAULT(%edx)
movl $0,PCB_ONFAULT(%edx)
.byte 0x66,0xf2,0x6d # rep insw
.byte 0x66,0xf2,0x6f # rep outsw
* void lgdt(struct region_descriptor *rdp);
/* reload the descriptor table */
/* flush the prefetch q */
/* reload "stale" selectors */
/* reload code selector by turning return into intersegmental return */
* void lidt(struct region_descriptor *rdp);
inb $0x84,%al # check wristwatch
inb $0x84,%al # check wristwatch
inb $0x84,%al # check wristwatch
inb $0x84,%al # check wristwatch
movl $fusufault,PCB_ONFAULT(%ecx)
movl $0,PCB_ONFAULT(%ecx)
movl $fusufault,PCB_ONFAULT(%ecx) #in case we page/protection violate
movl $0,PCB_ONFAULT(%ecx)
movl $fusufault,PCB_ONFAULT(%ecx) #in case we page/protection violate
movl $0,PCB_ONFAULT(%ecx)
movl %eax,PCB_ONFAULT(%ecx) #in case we page/protection violate
movl $fusufault,PCB_ONFAULT(%ecx) #in case we page/protection violate
shrl $IDXSHIFT, %edx /* fetch pte associated with address */
andb $7, %dl /* if we are the one case that won't trap... */
/* ... then simulate the trap! */
call _trapwrite /* trapwrite(addr) */
cmpl $0, %eax /* if not ok, return */
movl 8(%esp),%eax /* otherwise, continue with reference */
movl %eax,PCB_ONFAULT(%ecx) #in case we page/protection violate
movl $fusufault,PCB_ONFAULT(%ecx) #in case we page/protection violate
shrl $IDXSHIFT, %edx /* calculate pte address */
andb $7, %edx /* if we are the one case that won't trap... */
/* ..., then simulate the trap! */
call _trapwrite /* trapwrite(addr) */
movl _curpcb, %ecx # restore trashed registers
cmpl $0, %eax /* if not ok, return */
movl %eax,PCB_ONFAULT(%ecx) #in case we page/protection violate
movl $fusufault,PCB_ONFAULT(%ecx) #in case we page/protection violate
shrl $IDXSHIFT, %edx /* calculate pte address */
andb $7, %edx /* if we are the one case that won't trap... */
/* ..., then simulate the trap! */
call _trapwrite /* trapwrite(addr) */
movl _curpcb, %ecx # restore trashed registers
cmpl $0, %eax /* if not ok, return */
movl %eax,PCB_ONFAULT(%ecx) #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.
.align 4 /* ..so that profiling doesn't lump Idle with swtch().. */
/* switch to new process. first, save context as needed */
/* if no process to save, don't bother */
movl (%esp),%eax # Hardware registers
/* have we used fp, and need a save? */
andb $ FP_WASUSED|FP_NEEDSSAVE,%al
cmpb $ FP_WASUSED|FP_NEEDSSAVE,%al
movl %cr0,%eax /* insure fp is enabled */
orb $4,%al /* disable it */
xorb $ FP_NEEDSSAVE,%al /* save processed */
movl _CMAP2,%eax # save temporary map PTE
movl %eax,PCB_CMAP2(%ecx) # in our context
movl $0,_curproc # out of process
# movw %ax, PCB_IML(%ecx) # save ipl
/* save is done, now choose a new process or idle */
bsfl %edi,%eax # find a full q
btrl %eax,%edi # clear q full status
jnb 2b # if it was clear, look for another
movl %eax,%ebx # 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),%esi # q empty
btsl %ebx,%edi # nope, set to indicate full
movl %edi,_whichqs # update q status
movl %eax,P_RLINK(%ecx) /* isolate process to run */
/* switch address space */
/* if fp could be used, a dna trap will do a restore */
orb $ FP_NEEDSRESTORE,PCB_FLAGS(%edx)
movl PCB_CMAP2(%edx),%eax # get temporary map
movl %eax,_CMAP2 # reload temporary map PTE
movl %ecx,_curproc # into next process
movl %edx,%eax # return (1);
* struct proc *swtch_to_inactive(p) ; struct proc *p;
* At exit of a process, move off the address space of the
* process and onto a "safe" one. Then, on a temporary stack
* return and run code that disposes of the old state.
* Since this code requires a parameter from the "old" stack,
* pass it back as a return value.
popl %eax # arg, our return value
movl %ecx,%cr3 # good bye address space
movl $tmpstk-4,%esp # temporary stack, compensated for call
jmp %edx # return, execute remainder of cleanup
* savectx(pcb, altreturn)
* Update pcb, saving current processor state and arranging
* for alternate return ala longjmp in swtch if altreturn is true.
/* have we ever used fp, and need to save? */
testb $ FP_WASUSED, PCB_FLAGS(%ecx)
movl _CMAP2, %edx # save temporary map PTE
movl %edx, PCB_CMAP2(%ecx) # in our context
movl %esp, %edx # relocate current sp relative to pcb
subl $_kstack, %edx # (sp is relative to kstack):
addl %edx, %ecx # pcb += sp - kstack;
movl %eax, (%ecx) # write return pc at (relocated) sp@
# this mess deals with replicating register state gcc hides
xorl %eax, %eax # return 0
* addupc(int pc, struct uprof *up, int ticks):
* update profiling information for the user process.
movl 12(%ebp),%edx /* up */
movl 8(%ebp),%eax /* pc */
subl PR_OFF(%edx),%eax /* pc -= up->pr_off */
jl L1 /* if (pc < 0) return */
shrl $1,%eax /* praddr = pc >> 1 */
imull PR_SCALE(%edx),%eax /* praddr *= up->pr_scale */
shrl $15,%eax /* praddr = praddr << 15 */
andl $-2,%eax /* praddr &= ~1 */
cmpl PR_SIZE(%edx),%eax /* if (praddr > up->pr_size) return */
/* addl %eax,%eax /* praddr -> word offset */
addl PR_BASE(%edx),%eax /* praddr += up-> pr_base */
movl 16(%ebp),%ecx /* ticks */
movl $proffault,PCB_ONFAULT(%edx)
addl %ecx,(%eax) /* storage location += ticks */
movl $0,PCB_ONFAULT(%edx)
/* if we get a fault, then kill profiling all together */
movl $0,PCB_ONFAULT(%edx) /* squish the fault handler */
movl $0,PR_SCALE(%ecx) /* up->pr_scale = 0 */
.globl _cyloffset, _curpcb
#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 ; nop ; pushl 1f; call _printf; MSG(msg) ; \
#define MSG(msg) .data; 1: .asciz msg; .text
* Trap and fault vector routines
#define TRAP(a) pushl $(a) ; jmp alltraps
#define BPTTRAP(a) pushl $(a) ; jmp bpttraps
#define BPTTRAP(a) TRAP(a)
pushl $0; BPTTRAP(T_TRCTRAP)
pushl $0; BPTTRAP(T_BPTFLT)
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 */
addl $8,%esp # pop type, code
* This code checks for a kgdb trap, then falls through
* to the regular trap 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
incl _cnt+V_SYSCALL # kml 3/25/93
movw __udatasel,%ax # switch back to user segments
#include "i386/isa/icu.s"