/* locore.s 1.21.1.1 87/11/24 */
#include "../tahoe/mtpr.h"
#include "../tahoe/trap.h"
#include "../tahoe/psl.h"
#include "../tahoe/pte.h"
#include "../tahoe/mem.h"
#include "../tahoe/SYS.h"
#include "../tahoemath/fp.h"
.set HIGH,0x1f # mask for total disable
.set NISP,3 # number of interrupt stack pages
.set SYSTEM,0xC0000000 # virtual address of system start
.set PPAGES,0x100000 # possible pages in P0,P1, etc.
/* ACBL for non-negative '_add' */
#define ACBL(_limit,_add,_index,_displ) \
/* _ACBL for negative '_add' */
#define _ACBL(_limit,_add,_index,_displ) \
#define MOVC3(_srcaddr,_dstaddr,_len) \
/* keep address of psl if coming from user mode */
#define CHECK_SFE(_delta) \
bitl $PSL_CURMOD,_delta(sp); \
moval _delta(sp),_user_psl; \
* User structure is UPAGES at top of user space.
.set _u,SYSTEM - UPAGES*NBPG
* Restart stack. Used on power recovery or panic.
* Takes a core-dump and then halts.
* Power failure storage block and
* macros for saving and restoring.
#define POWERFAIL(id,longs) \
pwfl_/**/id: .space longs*4
POWERFAIL(r0, 14) # r0-r13
POWERFAIL(SCBB, 1) # system control block base
POWERFAIL(SBR, 1) # system pte base
POWERFAIL(SLR, 1) # system pte length
POWERFAIL(P0BR, 1) # p0 pte base
POWERFAIL(P0LR, 1) # p0 pte length
POWERFAIL(P1BR, 1) # p1 pte base
POWERFAIL(P1LR, 1) # p1 pte length
POWERFAIL(P2BR, 1) # p2 pte base
POWERFAIL(P2LR, 1) # p2 pte length
POWERFAIL(IPL, 1) # interrupt priority level
POWERFAIL(DCK, 1) # data cache key
POWERFAIL(CCK, 1) # code cache key
POWERFAIL(PCBB, 1) # process control block base
POWERFAIL(ISP, 1) # interrupt stack pointer
POWERFAIL(KSP, 1) # kernel mode stack pointer
POWERFAIL(USP, 1) # user mode stack pointer
POWERFAIL(MME, 1) # memory management enable
POWERFAIL(PSL, 1) # processor status longword
* Save current state in power fail storage block.
movpsl pwfl_PSL # Keeps all flags, etc. \
storer $0x3fff,pwfl_r0 # Saves r0-r13 \
moval 0(sp),pwfl_sp # Saves sp (=r14) \
mfpr $SBR,pwfl_SBR # Save all re_loadable registers \
* Restore state saved in power fail block and
* jmp to location specified after (possibly)
* enabling memory management.
#define RESTOREpwfl(loc) \
loadr $0x3fff,pwfl_r0 # Restore r0-r13 \
movl pwfl_sp,sp # Restore sp (=r14) \
mtpr pwfl_SBR,$SBR # Restore all re_loadable registers \
bicpsw $0xff # Restore PSW. \
bispsw pwfl_PSL+2 # Set original bits back (just in case..) \
# now go to mapped mode \
# Have to change PC to system addresses \
mtpr $1,$PACC # Thoroughly clean up caches. \
mtpr pwfl_MME,$MME # Restore MME. Last thing to be done. \
* Called by auto-restart.
* May be called manually.
_Xdoadump: # CP comes here after power fail
RESTOREpwfl(*0f) # restore state
#define _rsstkmap _Sysmap+12 # powerfail storage, scb, rsstk, int stack
tstl dumpflag # dump only once!
andl2 $~PG_PROT,_rsstkmap
orl2 $PG_KW,_rsstkmap # Make dump stack r/w
* Interrupt vector routines
clrl _waittime; pushab 1f; callf $8,_panic; 1: .asciz msg
pushab 1f; callf $(n+2)*4,_printf; MSG(msg)
#define MSG(msg) .data; 1: .asciz msg; .text
* r0-r2 are saved across all faults and interrupts.
* Routines below and those hidden in vbglue.s (device
* interrupts) invoke the PUSHR/POPR macros to execute
* this. Also, certain stack frame offset calculations
* (such as in hardclock) understand this, using the
* REGSPC definition (and FPSPC defined below).
* Finally, many routines, including those expanded
* inline depend on this! Should probably save all
* live C compiler temp registers to eliminate potentially
* grievous problems caused by incorrect register save masks.
#define PUSHR pushl r0; pushl r1; pushl r2;
#define POPR movl (sp)+,r2; movl (sp)+,r1; movl (sp)+,r0;
* Floating point state is saved across faults and
* interrupts. The state occupies 4 longwords on
* precision indicator (single = 0/double = 1)
* double representation of accumulator
* save accumulator status flag (pcb_savacc)
#define SAVE_FPSTAT(_delta) \
bitl $PSL_DBL,_delta(sp); \
moval 0(sp),_u+PCB_SAVACC; \
andl3 $(EXPMASK|SIGNBIT),(r1),-(sp); \
cmpl $0x80000000,(sp)+; \
nofault: .space 4 # bus error non-local goto label
incl _intrcnt+I_BUSERR # keep stats...
andl3 24(sp),$ERRCD,r0 # grab pushed MER value
cmpl r0,$APE # address parity error?
1: cmpl r0,$VBE # versabus error?
movl (sp)+,r0 # restore r0 and...
bitl $PSL_CURMOD,4*4+3*4(sp) # check if happened in user mode?
jeql 3f # yes, then shift stack up for trap...
movl 12(sp),16(sp) # sorry, no space for which-buss...
movl $T_BUSERR,0(sp) # push trap type code and...
jbr alltraps # ...merge with all other traps
3: # kernel mode, check to see if...
tstl nofault # ...doing peek/poke?
jeql 4f # nofault set? if so, jump to it...
movl nofault,4*4+2*4(sp) # ...setup for non-local goto
pushab 7*4(sp) # address of bus error parameters
movab 8(sp),sp # remove bus error parameters
SCBVEC(powfail): # We should be on interrupt stack now.
SAVEpwfl() # save machine state
moval _Xdoadump-SYSTEM,_scb+SCB_DOADUMP
incl _cnt+V_INTR # add to statistics
#include "../net/netisr.h"
bbc $NETISR_IMP,_netisr,1f;
andl2 $~(1<<NETISR_IMP),_netisr
bbc $NETISR_IP,_netisr,1f
andl2 $~(1<<NETISR_IP),_netisr
bbc $NETISR_NS,_netisr,1f
andl2 $~(1<<NETISR_NS),_netisr
bbc $NETISR_RAW,_netisr,1f
andl2 $~(1<<NETISR_RAW),_netisr
pushl $CPCONS; callf $8,_cnrint;
pushl $CPCONS; callf $8,_cnxint;
pushl $CPREMOT; callf $8,_cnrint;
pushl $CPREMOT; callf $8,_cnxint;
#define PUSHPCPSL pushl 4+FPSPC+REGSPC(sp); pushl 4+FPSPC+REGSPC(sp);
PUSHPCPSL # push pc and psl
callf $12,_hardclock # hardclock(pc,psl)
incl _cnt+V_INTR ## temp so not to break vmstat -= HZ
PUSHPCPSL # push pc and psl
callf $12,_softclock # softclock(pc,psl)
* Stray VERSAbus interrupt catch routines
#define PJ .align 2; callf $4,_Xvstray
PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ
PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ
PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ
PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ
PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ
PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ
PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ
PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ
PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ
PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ
PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ
PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ
bbc $0,_cold,2f # system running?
bbc $1,_cold,1f # doing autoconfig?
jbr 3f # random interrupt, ignore
mfpr $IPL,r12 # ...setup br and cvec
subl3 $_catcher+7,-8(fp),r11; shar $3,r11,r11
subl3 $_catcher+7,-8(fp),r0; shar $3,r0,r0
addl3 $SCB_DEVBASE,r0,-(sp);
PRINTF(2, "stray intr ipl %x vec %x\n")
3: moval 0f,-8(fp); ret # pop callf frame...
* Trap and fault vector routines
#define TRAP(a) pushl $T_/**/a; jbr alltraps
* Ast delivery (profiling and/or reschedule)
jeql align_excp # Can't emulate for kernel mode !
jbr non_aligned # Only emulated for user mode.
SCBVEC(fpm): # Floating Point Emulation
incl _cnt+V_FPE # count emulation traps
moval 8(sp),sp # Pop operand
tstl (sp) # Stack= PSL, PC, return_code
jneq _Xarithtrap # If not OK, emulate F.P. exception
movab 4(sp),sp # Else remove return_code and
#define REST_STACK movab 4(sp), sp; REST_FPSTAT; movab 4(sp), sp
REST_STACK # pop type, code, and fp stuff
mtpr $HIGH,$IPL ## dont go to a higher IPL (GROT)
REST_STACK # pop type, code, and fp stuff
mtpr $HIGH,$IPL ## dont go to a higher IPL (GROT)
* Mbmap and Usrptmap are enlarged by CLSIZE entries
* as they are managed by resource maps starting with index 1 or CLSIZE.
#define vaddr(x) ((((x)-_Sysmap)/4)*NBPG+SYSTEM)
#define SYSMAP(mname, vname, npte) \
_/**/mname: .globl _/**/mname; \
.set _/**/vname,vaddr(_/**/mname)
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(CMAP1 ,CADDR1 ,1 )
SYSMAP(CMAP2 ,CADDR2 ,1 )
SYSMAP(alignmap ,alignutl ,1 ) /* XXX */
SYSMAP(msgbufmap,msgbuf ,MSGBUFPTECNT )
SYSMAP(Mbmap ,mbutl ,NMBCLUSTERS*CLSIZE+CLSIZE )
SYSMAP(camap ,cabase ,16*CLSIZE )
SYSMAP(profmap ,profbase ,600*CLSIZE )
* Enlarge kmempt as needed for bounce buffers allocated
SYSMAP(_vdmap ,_vdbase ,NVD*(MAXPHYS/NBPG+CLSIZE) )
SYSMAP(_cymap ,_cybase ,NCY*(MAXPHYS/NBPG+CLSIZE) )
SYSMAP(ecamap ,calimit ,0 )
SYSMAP(ekmempt ,kmemlimit ,0 )
SYSMAP(VMEMbeg ,vmembeg ,0 )
SYSMAP(VMEMmap ,vmem ,VBIOSIZE )
SYSMAP(VMEMmap1 ,vmem1 ,0 )
SYSMAP(_acemap1 ,_acemem ,NACE*32 )
SYSMAP(VMEMend ,vmemend ,0 )
SYSMAP(VBmap ,vbbase ,CLSIZE )
SYSMAP(_vdbmap ,_vdbbase ,NVD*(MAXPHYS/NBPG+CLSIZE) )
SYSMAP(_cybmap ,_cybbase ,NCY*(MAXPHYS/NBPG+CLSIZE) )
SYSMAP(eVBmap ,vbend ,0 )
SYSMAP(Usrptmap ,usrpt ,USRPTSIZE+CLSIZE )
.set _Syssize,(eSysmap-_Sysmap)/4
* IPL 0x1f; MME 0; scbb, pcbb, sbr, slr, isp, ksp not set
/* set system control block base and system page table params */
mtpr $_Sysmap-SYSTEM,$SBR
/* double map the kernel into the virtual user addresses of phys mem */
mtpr $_Sysmap,$P1BR # against Murphy
movl $_intstack-SYSTEM+NISP*NBPG,sp # still physical
mtpr $_intstack+NISP*NBPG,$ISP
1: pushl $1; pushl r7; callf $12,_badaddr; tstl r0; bneq 9f
ACBL($MAXMEM*1024-1,$64*1024,r7,1b)
/* clear memory from kernel bss and pages for proc 0 u. and page table */
movab _edata,r6; andl2 $~SYSTEM,r6
movab _end,r5; andl2 $~SYSTEM,r5
1: clrl (r6); ACBL(r5,$4,r6,1b) # clear just bss
bbc $6,r11,0f # check RB_KDB
andl3 $~SYSTEM,r9,r5 # skip symbol & string tables
0: orl3 $SYSTEM,r5,r9 # convert to virtual address
addl2 $NBPG-1,r9 # roundup to next page
addl2 $(UPAGES*NBPG)+NBPG+NBPG,r5
1: clrl (r6); ACBL(r5,$4,r6,1b)
/* trap(), syscall(), and fpemulate() save r0-r12 in the entry mask */
orw2 $0x01ffc,_panic # for debugging (no r0|r1)
callf $4,_fixctlrmask # setup for autoconfig
/* initialize system page table: scb and int stack writeable */
shrl $PGSHIFT,r1,r1 # r1-page number of eintstack
/* make 1st page processor storage read/only, 2nd read/write */
orl3 $PG_V|PG_KR,r2,_Sysmap[r2]; incl r2;
orl3 $PG_V|PG_KW,r2,_Sysmap[r2]; incl r2;
/* other parts of the system are read/write for kernel */
1: orl3 $PG_V|PG_KW,r2,_Sysmap[r2]; # data:kernel write+phys=virtual
/* make rsstk read-only as red zone for interrupt stack */
andl2 $~PG_PROT,_rsstkmap
orl2 $PG_V|PG_KR,_rsstkmap
/* make kernel text space read-only */
1: orl3 $PG_V|PG_KR,r2,_Sysmap[r2]
/* make kernel data, bss, read-write */
1: orl3 $PG_V|PG_KW,r2,_Sysmap[r2]
/* go to mapped mode, have to change both pc and sp to system addresses */
mtpr $1,$PADC # needed by HW parity&ECC logic
mtpr $1,$PACC # just in case
/* disable any interrupts */
/* setup context for proc[0] == scheduler */
andl3 $~SYSTEM,r9,r6 # convert to physical
andl2 $~(NBPG-1),r6 # make page boundary
/* setup page table for proc[0] */
shrl $PGSHIFT,r6,r3 # r3 = btoc(r6)
orl3 $PG_V|PG_KW,r3,_Usrptmap # init first upt entry
movab _usrpt,r0 # r0 - first user page
mtpr r0,$P0BR # no p0 for proc[0]
mtpr r0,$P1BR # no p1 either
/* setup mapping for UPAGES of _u */
moval -NBPG(r1),r1 # r1 = virtual add of next (downward) _u page
subl2 $4,r0 # r0 = pte address
orl3 $PG_V|PG_URKW,r3,(r0)
/* initialize (slightly) the pcb */
movab UPAGES*NBPG(r1),PCB_KSP(r1) # KSP starts at end of _u
movl r1,PCB_USP(r1) # USP starts just below _u
movl $CLSIZE,PCB_SZPT(r1) # init u.u_pcb.pcb_szpt
movl r9,PCB_R9(r1) # r9 obtained from boot
movl r10,PCB_R10(r1) # r10 obtained from boot
movl r11,PCB_R11(r1) # r11 obtained from CP on boot
movab 1f,PCB_PC(r1) # initial pc
clrl PCB_PSL(r1) # kernel mode, ipl=0
mtpr r3,$PCBB # first pcbb (physical)
rei # Actually next instruction:
/* put signal trampoline code in u. area */
/* save boot device in global _bootdev */
/* save reboot flags in global _boothowto */
/* save end of symbol & string table in global _bootesym */
subl3 $NBPG-1,r9,_bootesym
/* calculate firstaddr, and call main() */
addl2 $UPAGES+1,(sp) # first physical unused page
/* proc[1] == /etc/init now running here in kernel mode; run icode */
pushl $PSL_CURMOD # User mode PSL
pushl $0 # PC = 0 (virtual now)
* Mask for saving/restoring registers on entry to
* a user signal handler. Space for the registers
* is reserved in sendsig, so beware if you want
#define SIGREGS (R0|R1|R2|R3|R4|R5)
storer $SIGREGS,16(sp) # save volatile registers
calls $4*3+4,*12(sp) # params pushed by sendsig for handler
loadr $SIGREGS,4(sp) # restore volatile registers
movab 24(sp),fp # use parameter list set up in sendsig
kcall $SYS_sigreturn # cleanup mask and onsigstack
halt # sigreturn does not return!
* Icode is copied out to process 1 to exec /etc/init.
* If the exec fails, process 1 exits.
argv: .long init+5-_icode
* see if access addr with a len type instruction causes a machine check
* len is length of access (1=byte, 2=short, 4=long)
* r0 = 0 means good(exists); r0 =1 means does not exist.
1: bbc $1,r4,1f; tstw (r3)
1: bbc $2,r4,1f; tstl (r3)
2: movl r2,_scb+SCB_BUSERR
9: # catch buss error (if it comes)
halt # address parity error
movl $1,r0 # Anything else = bad address
movab 8(sp),sp # discard buss error trash
movab 2b,(sp) # new program counter on stack.
* see if access tape master controller addr causes a bus error
* r0 = 0: no error; r0 = 1: timeout error.
clrl nofault # made it w/o bus error
* fetch word and catch any bus error
mtpr $0x18,$IPL # not reentrant
1: movl $-1,r0 # bus error
* write word and catch any bus error
mtpr $0x18,$IPL # not reentrant
1: movl $-1,r0 # bus error
* Copy a potentially overlapping block of memory.
* ovbcopy(src, dst, count)
* caddr_t src, dst; unsigned count;
bgtru 1f # normal forward case
beql 2f # equal, nothing to do
addl2 r2,r0 # may be overlapping
subl2 r2,r0 # normal forward case
addl2 r2,r1 # overlapping, must do backwards
* Copy a null terminated string from the user address space into
* the kernel address space.
* copyinstr(fromaddr, toaddr, maxlength, &lencopied)
movl 12(fp),r5 # r5 = max length
movl 8(fp),r4 # r4 = kernel address
movl 4(fp),r0 # r0 = user address
andl3 $(NBPG*CLSIZE-1),r0,r2 # r2 = bytes on first page
subl3 r2,$(NBPG*CLSIZE),r2
cmpl r5,r2 # r2 = min(bytes on page, length left);
prober $1,(r0),r2 # bytes accessible?
subl2 r2,r5 # update bytes left count
movl r2,r3 # r3 = saved count
movblk # copy in next piece
movl $(NBPG*CLSIZE),r2 # check next page
tstl r5 # run out of space?
movl $ENOENT,r0 # set error code and return
tstl 16(fp) # return length?
subl3 r5,12(fp),r5 # actual len = maxlen - unused pages
subl2 r2,r5 # - unused on this page
addl3 $1,r5,*16(fp) # + the null byte
subl3 r2,r3,r2 # calc char cnt
incl r2 # add on null byte
* Copy a null terminated string from the kernel
* address space to the user address space.
* copyoutstr(fromaddr, toaddr, maxlength, &lencopied)
movl 12(fp),r5 # r5 = max length
movl 4(fp),r0 # r0 = kernel address
movl 8(fp),r4 # r4 = user address
andl3 $(NBPG*CLSIZE-1),r4,r2 # r2 = bytes on first page
subl3 r2,$(NBPG*CLSIZE),r2
cmpl r5,r2 # r2 = min(bytes on page, length left);
probew $1,(r4),r2 # bytes accessible?
subl2 r2,r5 # update bytes left count
movl r2,r3 # r3 = saved count
* This is a workaround for a microcode bug that causes
* a trap type 9 when cmps3/movs3 touches the last byte
* on a valid page immediately followed by an invalid page.
beql 9f # cannot handle case of r2 == 0!
cmps3 # check for null up to last byte
cmpl $1,r2 # get to last byte on page?
tstb (r0) # last byte on page null?
incl r0 # not null, so bump pointer
movblk # copy out next piece
movl $(NBPG*CLSIZE),r2 # check next page
tstl r5 # run out of space?
movl $ENOENT,r0 # set error code and return
clrl *$0 # this should never execute, if it does
movl $EFAULT,r0 # save me a core dump (mkm - 9/87)
* Copy a null terminated string from one point to another in
* the kernel address space.
* copystr(fromaddr, toaddr, maxlength, &lencopied)
movl 12(fp),r3 # r3 = max length
movl 4(fp),r0 # r0 = src address
movl 8(fp),r4 # r4 = dest address
clrl r5 # r5 = bytes left
movl r3,r2 # r2 = max bytes to copy
movl $ENOENT,r0 # set error code and return
* Copy a block of data from the user address space into
* the kernel address space.
* copyin(fromaddr, toaddr, count)
movl 12(fp),r0 # copy length
movl 4(fp),r1 # copy user address
cmpl $(CLSIZE*NBPG),r0 # probing one page or less ?
prober $1,(r1),$(CLSIZE*NBPG) # bytes accessible ?
addl2 $(CLSIZE*NBPG),r1 # incr user address ptr
_ACBL($(CLSIZE*NBPG+1),$(-CLSIZE*NBPG),r0,1b) # reduce count and loop
prober $1,(r1),r0 # bytes accessible ?
MOVC3(4(fp),8(fp),12(fp))
* Copy a block of data from the kernel
* address space to the user address space.
* copyout(fromaddr, toaddr, count)
movl 12(fp),r0 # get count
movl 8(fp),r1 # get user address
cmpl $(CLSIZE*NBPG),r0 # can do in one probew?
probew $1,(r1),$(CLSIZE*NBPG) # bytes accessible?
addl2 $(CLSIZE*NBPG),r1 # increment user address
_ACBL($(CLSIZE*NBPG+1),$(-CLSIZE*NBPG),r0,1b) # reduce count and loop
probew $1,(r1),r0 # bytes accessible?
MOVC3(4(fp),8(fp),12(fp))
movl (fp),(r0); addl2 $4,r0 # save fp
movl -8(fp),(r0) # save pc
movl (r0),newfp; addl2 $4,r0 # must save parameters in memory
movl (r0),newpc # as all regs may be clobbered.
cmpl fp,newfp # are we there yet?
moval 1b,-8(fp) # redirect return pc to us!
beql 3f # did we miss our frame?
movl newpc,r0 # all done, just return
jmp (r0) # to setjmp `ret'
* setjmp that saves all registers as the call frame may not
* be available to recover them in the usual manner by longjmp.
* Called before swapping out the u. area, restored by resume()
storer $0x1ff8,(r2); addl2 $40,r2 # r3-r12
movl (fp),(r2); addl2 $4,r2 # fp
movab 8(fp),(r2); addl2 $4,r2 # sp
* C library -- reset, setexit
* will generate a "return" from
* by restoring r2 - r12, fp
* The returned value is x; on the original
* call the returned value is 0.
movl 4(fp),r0 # returned value
* The following primitives use the fancy TAHOE instructions.
* _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
* setrq(p), using fancy TAHOE instructions.
* Call should be made at spl8(), and p->p_stat should be SRUN
tstl P_RLINK(r0) ## firewall: p->p_rlink must be 0
movzbl P_PRI(r0),r1 # put on queue which is p->p_pri / 4
insque (r0),*4(r2) # at end of queue
orl2 r1,_whichqs # mark queue non-empty
* remrq(p), using fancy TAHOE instructions
* Call should be made at spl8().
pushab rem3 # it wasn't recorded to be on its q
bneq rem2 # q not empty yet
andl2 r1,_whichqs # mark queue empty
clrl P_RLINK(r0) ## for firewall checking
* Masterpaddr is the p->p_addr of the running process on the master
* processor. When a multiprocessor system, the slave processors will have
* an array of slavepaddr's.
* When no processes are on the runq, swtch branches to idle
* to wait for something to come ready.
mtpr $0,$IPL # must allow interrupts here
tstl _whichqs # look for non-empty queue
* swtch(), using fancy tahoe instructions
movl (fp),fp # prepare for rei
movl (sp),4(sp) # saved pc
sw1: ffs _whichqs,r0 # look for non-empty queue
blss idle # if none, idle
mtpr $0x18,$IPL # lock out all so _whichqs==_qs
bbc r0,_whichqs,sw1 # proc moved via interrupt
movl (r1),r2 # r2 = p = highest pri process
bvs badsw # make sure something was there
andl2 r1,_whichqs # no more procs in this queue
tstl P_WCHAN(r2) ## firewalls
cmpl r0,_masterpaddr # resume of current proc is easy
shal $PGSHIFT,r0,r0 # r0 = pcbb(p)
shal $PGSHIFT,4(fp),r0 # r0 = pcbb(pf)
movl (fp),fp # prepare for rei
movl (sp)+,4(sp) # saved pc
mtpr $0x18,$IPL # no interrupts, please
movl _CMAP2,_u+PCB_CMAP2 # yech
REST_ACC # restore original accumulator
movl _u+PCB_CMAP2,_CMAP2 # yech
movl _u+U_PROCP,r2 # r2 = u.u_procp
tstl P_CKEY(r2) # does proc have code key?
callf $4,_getcodekey # no, give him one
movl _u+U_PROCP,r2 # r2 = u.u_procp
tstl P_DKEY(r2) # does proc have data key?
callf $4,_getdatakey # no, give him one
movl _u+U_PROCP,r2 # r2 = u.u_procp
mtpr P_CKEY(r2),$CCK # set code cache key
mtpr P_DKEY(r2),$DCK # set data cache key
res1: # longjmp to saved context
loadr $0x3ff8,(r2); addl2 $44,r2 # restore r3-r13 (r13=fp)
movl (r2),r1; addl2 $4,r2 # fetch previous sp ...
movab (sp),r0 # ... and current sp and
cmpl r1,r0 # check for credibility,
bgequ 1f # if further up stack ...
pushab 2f; callf $8,_panic # ... panic
1: # sp ok, complete return
movl (r2),(sp) # return address
movl $PSL_PRVMOD,4(sp) # kernel mode, ipl 0
prober $1,(r1),$4 # check access
beql fserr # page unreadable
bitl $1,r1 # check byte alignment
bneq 2f # odd, do byte-word-byte
bitl $2,r1 # check word alignment
bneq 1f # odd, do in 2 words
movl (r1),r0 # move longword
movw (r1),r0 # move two words
movzwl 2(r1),r1 # orw2 sign extends
movb (r1),r0 # move byte-word-byte
movzwl 1(r1),r2 # orw2 sign extends
movzbl 3(r1),r1 # orb2 sign extends
probew $1,(r0),$4 # check access
beql fserr # page unwritable
bitl $1,r0 # check byte alignment
bneq 1f # odd byte boundary
bitl $2,r0 # check word alignment
beql 2f # longword aligned
movw 8(fp),(r0) # move two words
* Copy 1 relocation unit (NBPG bytes)
* from user virtual address to physical address
orl3 $PG_V|PG_KW,8(fp),_CMAP2
mtpr $_CADDR2,$TBIS # invalidate entry for copy
MOVC3(4(fp),$_CADDR2,$NBPG)
* Clear a page of memory. The page frame is specified.
orl3 $PG_V|PG_KW,4(fp),_CMAP1 # Maps to virtual addr CADDR1
movl $255,r0 # r0 = limit
clrl r1 # r1 = index of cleared long
* Check user mode read/write access.
* useracc(addr, count, mode)
* caddr_t addr; int count, mode;
movl $1,r2 # r2 = 'user mode' for probew/probew
tstl 12(fp) # test for read access ?
cmpl $(CLSIZE*NBPG),r1 # can we do it in one probe ?
probew r2,(r0),$(CLSIZE*NBPG)
_ACBL($(CLSIZE*NBPG+1),$(-CLSIZE*NBPG),r1,uaw1)
prober r2,(r0),$(CLSIZE*NBPG)
_ACBL($(CLSIZE*NBPG+1),$(-CLSIZE*NBPG),r1,uar1)
* Check kernel mode read/write access.
* kernacc(addr, count, mode)
* caddr_t addr; int count, mode;
clrl r2 # r2 = 0 means kernel mode probe.
jbr probes # Dijkstra would get gastric distress here.
* addupc - increment some histogram counter
* in the profiling buffer
* addupc(pc, prof, delta)
* long pc; short delta; struct uprof *prof;
* struct uprof { # profile arguments
* short *r_base; # buffer base
* unsigned pr_size; # buffer size
* unsigned pr_off; # pc offset
* unsigned pr_scale; # pc scaling
movl 8(fp),r2 # r2 points to structure
subl3 8(r2),4(fp),r0 # r0 = PC - lowpc
jlss 9f # PC < lowpc , out of range !
shrl $1,r0,r0 # the unit is words
shrl $1,12(r2),r1 # ditto for scale
cmpl r1,4(r2) # Check buffer overflow
probew $1,*0(r2)[r1],$2 # counter accessible?
shrl $1,r1,r1 # make r1 word index
* scanc(size, cp, table, mask)
addl3 4(fp),r0,r2 # end = &cp[size]
movl 12(fp),r1 # r1 = table
movb 19(fp),r4 # r4 = mask
jbr 0f # just like Fortran...
bitb r4,(r1)[r3] # if (table[*cp] & mask)
0: aoblss r2,r0,1b # } while (++cp < end);
subl3 r0,r2,r0; ret # return (end - cp);
addl3 8(fp),r0,r1 # r1 = end = &cp[size];
movb 7(fp),r2 # r2 = mask
cmpb (r0),r2 # if (*cp != mask)
0: aoblss r1,r0,1b # while (++cp < end);
subl3 r0,r1,r0; ret # return (end - cp);
addl3 8(fp),r0,r1 # r1 = end = &cp[size]
movb 7(fp),r2 # r2 = mask
cmpb (r0),r2 # if (*cp == mask)
0: aoblss r1,r0,1b # while (++cp < end);
subl3 r0,r1,r0; ret # return (end - cp);
#include "../tahoealign/align.h"
* There's an intimate relationship between this piece of code
* and the alignment emulation code (especially the layout
* of local variables in alignment.c! Don't change unless
* you update both this, alignment.h and alignment.c !!
orb2 $EMULATEALIGN,_u+U_EOSYS
incl _cnt+V_ALIGN # count emulated alignment traps
SAVE_FPSTAT(4) # Also zeroes out ret_exception !
mfpr $USP,-(sp) # user sp
callf $4,_alignment # call w/o parms so regs may be modified
* We return here after a successful emulation or an exception.
* The registers have been restored and we must not alter them
* before returning to the user.
2: mtpr (sp)+,$USP # restore user sp
tstl 8(sp) # Any exception ?
bneq got_excp # Yes, reflect it back to user.
moval 8(sp),sp # pop 2 zeroes pushed above
xorb2 $EMULATEALIGN,_u+U_EOSYS
bitl $PSL_T,4(sp) # check for trace bit set
got_excp: # decode exception
casel 8(sp),$ILL_ADDRMOD,$ALIGNMENT
brw alignment # default - shouldn't come here at all !
ill_addrmod: # No other parameters. Set up stack as
moval 8(sp),sp # the HW would do it in a real case.
jbr align_excp # NB: going to _Xalignflt would cause loop
* Must restore accumulator w/o modifying sp and w/o using
* registers. Solution: copy things needed by REST_FPSTAT.
pushl 20(sp) # The flags longword
pushl 20(sp) # ret_exception ignored by REST_FPSTAT
REST_FPSTAT # Back where we were with the sp !
movl (sp),16(sp) # code for illegal access
movl 4(sp),20(sp) # original virtual address
moval 16(sp),sp # Just like the HW would set it up
arithmetic: # same trickery as above
pushl 20(sp) # The flags longword
pushl 20(sp) # ret_exception ignored by REST_FPSTAT
REST_FPSTAT # Back where we were with the sp !
movl (sp),20(sp) # code for arithmetic exception
moval 20(sp),sp # Just like the HW would set it up