123eb0b74Storek/* 2304a8d6dSbostic * Copyright (c) 1992, 1993 3304a8d6dSbostic * The Regents of the University of California. All rights reserved. 423eb0b74Storek * 523eb0b74Storek * This software was developed by the Computer Systems Engineering group 623eb0b74Storek * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and 723eb0b74Storek * contributed to Berkeley. 823eb0b74Storek * 983548bb1Sbostic * All advertising materials mentioning features or use of this software 1083548bb1Sbostic * must display the following acknowledgement: 1183548bb1Sbostic * This product includes software developed by the University of 128e156d2fStorek * California, Lawrence Berkeley Laboratory. 1383548bb1Sbostic * 1423eb0b74Storek * %sccs.include.redist.c% 1523eb0b74Storek * 16*c073cf18Smckusick * @(#)locore.s 8.4 (Berkeley) 12/10/93 1723eb0b74Storek * 188e156d2fStorek * from: $Header: locore.s,v 1.51 93/04/21 06:19:37 torek Exp $ 1923eb0b74Storek */ 2023eb0b74Storek 2123eb0b74Storek#define LOCORE 2223eb0b74Storek#include "assym.s" 23a159b5e1Sbostic#include <sparc/sparc/intreg.h> 24a159b5e1Sbostic#include <sparc/sparc/timerreg.h> 2523eb0b74Storek#ifdef notyet 268e156d2fStorek#include <sparc/sparc/vaddrs.h> 27a159b5e1Sbostic#include <sparc/dev/zsreg.h> 2823eb0b74Storek#endif 298e156d2fStorek#include <machine/ctlreg.h> 30a159b5e1Sbostic#include <machine/psl.h> 31a159b5e1Sbostic#include <machine/signal.h> 32a159b5e1Sbostic#include <machine/trap.h> 3323eb0b74Storek 3423eb0b74Storek/* 3523eb0b74Storek * GNU assembler does not understand `.empty' directive; Sun assembler 3623eb0b74Storek * gripes about labels without it. To allow cross-compilation using 3723eb0b74Storek * the Sun assembler, and because .empty directives are useful documentation, 388e156d2fStorek * we use this trick. 3923eb0b74Storek */ 4023eb0b74Storek#ifdef SUN_AS 4123eb0b74Storek#define EMPTY .empty 4223eb0b74Storek#else 4323eb0b74Storek#define EMPTY /* .empty */ 4423eb0b74Storek#endif 4523eb0b74Storek 468e156d2fStorek/* use as needed to align things on longword boundaries */ 478e156d2fStorek#define ALIGN .align 4 488e156d2fStorek 4923eb0b74Storek/* 5023eb0b74Storek * CCFSZ (C Compiler Frame SiZe) is the size of a stack frame required if 5123eb0b74Storek * a function is to call C code. It should be just 64, but Sun defined 5223eb0b74Storek * their frame with space to hold arguments 0 through 5 (plus some junk), 5323eb0b74Storek * and varargs routines (such as printf) demand this, and gcc uses this 5423eb0b74Storek * area at times anyway. 5523eb0b74Storek */ 5623eb0b74Storek#define CCFSZ 96 5723eb0b74Storek 5823eb0b74Storek/* 5923eb0b74Storek * A handy macro for maintaining instrumentation counters. 6023eb0b74Storek * Note that this clobbers %o0 and %o1. Normal usage is 6123eb0b74Storek * something like: 6223eb0b74Storek * foointr: 6323eb0b74Storek * TRAP_SETUP(...) ! makes %o registers safe 6423eb0b74Storek * INCR(_cnt+V_FOO) ! count a foo 6523eb0b74Storek */ 6623eb0b74Storek#define INCR(what) \ 6723eb0b74Storek sethi %hi(what), %o0; \ 6823eb0b74Storek ld [%o0 + %lo(what)], %o1; \ 6923eb0b74Storek inc %o1; \ 7023eb0b74Storek st %o1, [%o0 + %lo(what)] 7123eb0b74Storek 7223eb0b74Storek/* 7323eb0b74Storek * Another handy macro: load one register window, given `base' address. 7423eb0b74Storek * This can be either a simple register (e.g., %sp) or include an initial 7523eb0b74Storek * offset (e.g., %g6 + PCB_RW). 7623eb0b74Storek */ 7723eb0b74Storek#define LOADWIN(addr) \ 7823eb0b74Storek ldd [addr], %l0; \ 7923eb0b74Storek ldd [addr + 8], %l2; \ 8023eb0b74Storek ldd [addr + 16], %l4; \ 8123eb0b74Storek ldd [addr + 24], %l6; \ 8223eb0b74Storek ldd [addr + 32], %i0; \ 8323eb0b74Storek ldd [addr + 40], %i2; \ 8423eb0b74Storek ldd [addr + 48], %i4; \ 8523eb0b74Storek ldd [addr + 56], %i6 8623eb0b74Storek 8723eb0b74Storek/* 8823eb0b74Storek * To return from trap we need the two-instruction sequence 8923eb0b74Storek * `jmp %l1; rett %l2', which is defined here for convenience. 9023eb0b74Storek */ 9123eb0b74Storek#define RETT jmp %l1; rett %l2 9223eb0b74Storek 9323eb0b74Storek .data 9423eb0b74Storek/* 9523eb0b74Storek * The interrupt stack. 9623eb0b74Storek * 9723eb0b74Storek * This is the very first thing in the data segment, and therefore has 9823eb0b74Storek * the lowest kernel stack address. We count on this in the interrupt 9923eb0b74Storek * trap-frame setup code, since we may need to switch from the kernel 10023eb0b74Storek * stack to the interrupt stack (iff we are not already on the interrupt 10123eb0b74Storek * stack). One sethi+cmp is all we need since this is so carefully 10223eb0b74Storek * arranged. 10323eb0b74Storek */ 10423eb0b74Storek .globl _intstack 10523eb0b74Storek .globl _eintstack 10623eb0b74Storek_intstack: 10723eb0b74Storek .skip 4 * NBPG ! 16k = 128 128-byte stack frames 10823eb0b74Storek_eintstack: 10923eb0b74Storek 11023eb0b74Storek/* 11123eb0b74Storek * When a process exits and its u. area goes away, we set cpcb to point 11223eb0b74Storek * to this `u.', leaving us with something to use for an interrupt stack, 11323eb0b74Storek * and letting all the register save code have a pcb_uw to examine. 11423eb0b74Storek * This is also carefully arranged (to come just before u0, so that 11523eb0b74Storek * process 0's kernel stack can quietly overrun into it during bootup, if 11623eb0b74Storek * we feel like doing that). 11723eb0b74Storek */ 11823eb0b74Storek .globl _idle_u 11923eb0b74Storek_idle_u: 12023eb0b74Storek .skip UPAGES * NBPG 12123eb0b74Storek 12223eb0b74Storek/* 12323eb0b74Storek * Process 0's u. 12423eb0b74Storek * 12523eb0b74Storek * This must be aligned on an 8 byte boundary. 12623eb0b74Storek */ 12723eb0b74Storek .globl _u0 12823eb0b74Storek_u0: .skip UPAGES * NBPG 12923eb0b74Storekestack0: 13023eb0b74Storek 13123eb0b74Storek#ifdef KGDB 13223eb0b74Storek/* 13323eb0b74Storek * Another item that must be aligned, easiest to put it here. 13423eb0b74Storek */ 13523eb0b74StorekKGDB_STACK_SIZE = 2048 13623eb0b74Storek .globl _kgdb_stack 13723eb0b74Storek_kgdb_stack: 13823eb0b74Storek .skip KGDB_STACK_SIZE ! hope this is enough 13923eb0b74Storek#endif 14023eb0b74Storek 14123eb0b74Storek/* 14223eb0b74Storek * _cpcb points to the current pcb (and hence u. area). 14323eb0b74Storek * Initially this is the special one. 14423eb0b74Storek */ 14523eb0b74Storek .globl _cpcb 14623eb0b74Storek_cpcb: .word _u0 14723eb0b74Storek 14823eb0b74Storek .text 14923eb0b74Storek 15023eb0b74Storek/* 15123eb0b74Storek * The first thing in the real text segment is the trap vector table, 15223eb0b74Storek * which must be aligned on a 4096 byte boundary. The text segment 15323eb0b74Storek * starts beyond page 0 of KERNBASE so that there is a red zone 15423eb0b74Storek * between user and kernel space. Since the boot ROM loads us at 15523eb0b74Storek * 0x4000, it is far easier to start at KERNBASE+0x4000 than to 15623eb0b74Storek * buck the trend. This is four pages in; we can stuff something 15723eb0b74Storek * into the three pages left beneath us later ... like, oh, say, the 15823eb0b74Storek * message buffer (1 page). 15923eb0b74Storek */ 16023eb0b74Storek .globl _msgbuf 16123eb0b74Storekmsgbufsize = NBPG ! 1 page for msg buffer 16223eb0b74Storek_msgbuf = KERNBASE + NBPG 16323eb0b74Storek 16423eb0b74Storek/* 16523eb0b74Storek * The remaining two physical pages are currently unused. We need to 16623eb0b74Storek * map the interrupt enable register very early on in the boot process, 16723eb0b74Storek * so that we can handle NMIs (parity errors) halfway sensibly during 16823eb0b74Storek * boot. We use virtual address f8002000 (`page 2') for this, wasting 16923eb0b74Storek * 4096 bytes of physical memory. 17023eb0b74Storek */ 17123eb0b74StorekIE_reg_addr = _msgbuf + msgbufsize ! this page not used; points to IEreg 17223eb0b74Storek 17323eb0b74Storek/* 17423eb0b74Storek * Each trap has room for four instructions, of which one perforce must 17523eb0b74Storek * be a branch. On entry the hardware has copied pc and npc to %l1 and 17623eb0b74Storek * %l2 respectively. We use two more to read the psr into %l0, and to 17723eb0b74Storek * put the trap type value into %l3 (with a few exceptions below). 17823eb0b74Storek * We could read the trap type field of %tbr later in the code instead, 17923eb0b74Storek * but there is no need, and that would require more instructions 18023eb0b74Storek * (read+mask, vs 1 `mov' here). 18123eb0b74Storek * 18223eb0b74Storek * I used to generate these numbers by address arithmetic, but gas's 18323eb0b74Storek * expression evaluator has about as much sense as your average slug 18423eb0b74Storek * (oddly enough, the code looks about as slimy too). Thus, all the 18523eb0b74Storek * trap numbers are given as arguments to the trap macros. This means 18623eb0b74Storek * there is one line per trap. Sigh. 18723eb0b74Storek * 18823eb0b74Storek * Note that only the local registers may be used, since the trap 18923eb0b74Storek * window is potentially the last window. Its `in' registers are 19023eb0b74Storek * the previous window's outs (as usual), but more important, its 19123eb0b74Storek * `out' registers may be in use as the `topmost' window's `in' registers. 19223eb0b74Storek * The global registers are of course verboten (well, until we save 19323eb0b74Storek * them away). 19423eb0b74Storek * 19523eb0b74Storek * Hardware interrupt vectors can be `linked'---the linkage is to regular 19623eb0b74Storek * C code---or rewired to fast in-window handlers. The latter are good 19723eb0b74Storek * for unbuffered hardware like the Zilog serial chip and the AMD audio 19823eb0b74Storek * chip, where many interrupts can be handled trivially with pseudo-DMA or 19923eb0b74Storek * similar. Only one `fast' interrupt can be used per level, however, and 20023eb0b74Storek * direct and `fast' interrupts are incompatible. Routines in intr.c 20123eb0b74Storek * handle setting these, with optional paranoia. 20223eb0b74Storek */ 20323eb0b74Storek 20423eb0b74Storek /* regular vectored traps */ 20523eb0b74Storek#define VTRAP(type, label) \ 20623eb0b74Storek mov (type), %l3; b label; mov %psr, %l0; nop 20723eb0b74Storek 20823eb0b74Storek /* hardware interrupts (can be linked or made `fast') */ 20923eb0b74Storek#define HARDINT(lev) \ 21023eb0b74Storek mov (lev), %l3; b _sparc_interrupt; mov %psr, %l0; nop 21123eb0b74Storek 21223eb0b74Storek /* software interrupts (may not be made direct, sorry---but you 21323eb0b74Storek should not be using them trivially anyway) */ 21423eb0b74Storek#define SOFTINT(lev, bit) \ 21523eb0b74Storek mov (lev), %l3; mov (bit), %l4; b softintr; mov %psr, %l0 21623eb0b74Storek 21723eb0b74Storek /* traps that just call trap() */ 21823eb0b74Storek#define TRAP(type) VTRAP(type, slowtrap) 21923eb0b74Storek 22023eb0b74Storek /* architecturally undefined traps (cause panic) */ 22123eb0b74Storek#define UTRAP(type) VTRAP(type, slowtrap) 22223eb0b74Storek 22323eb0b74Storek /* software undefined traps (may be replaced) */ 22423eb0b74Storek#define STRAP(type) VTRAP(type, slowtrap) 22523eb0b74Storek 22623eb0b74Storek/* breakpoint acts differently under kgdb */ 22723eb0b74Storek#ifdef KGDB 22823eb0b74Storek#define BPT VTRAP(T_BREAKPOINT, bpt) 22923eb0b74Storek#define BPT_KGDB_EXEC VTRAP(T_KGDB_EXEC, bpt) 23023eb0b74Storek#else 23123eb0b74Storek#define BPT TRAP(T_BREAKPOINT) 23223eb0b74Storek#define BPT_KGDB_EXEC TRAP(T_KGDB_EXEC) 23323eb0b74Storek#endif 23423eb0b74Storek 23523eb0b74Storek/* special high-speed 1-instruction-shaved-off traps (get nothing in %l3) */ 23623eb0b74Storek#ifdef COMPAT_SUNOS 23723eb0b74Storek#define SUN_SYSCALL b sun_syscall; mov %psr, %l0; nop; nop 23823eb0b74Storek#else 23923eb0b74Storek#define SUN_SYSCALL TRAP(T_SUN_SYSCALL) 24023eb0b74Storek#endif 24123eb0b74Storek#define SYSCALL b syscall; mov %psr, %l0; nop; nop 24223eb0b74Storek#define WINDOW_OF b window_of; mov %psr, %l0; nop; nop 24323eb0b74Storek#define WINDOW_UF b window_uf; mov %psr, %l0; nop; nop 24423eb0b74Storek#ifdef notyet 24523eb0b74Storek#define ZS_INTERRUPT b zshard; mov %psr, %l0; nop; nop 24623eb0b74Storek#else 24723eb0b74Storek#define ZS_INTERRUPT HARDINT(12) 24823eb0b74Storek#endif 24923eb0b74Storek 25023eb0b74Storek .globl start 25123eb0b74Storek .globl _trapbase 25223eb0b74Storekstart: 25323eb0b74Storek_trapbase: 25423eb0b74Storek/* trap 0 is special since we cannot receive it */ 25523eb0b74Storek b dostart; nop; nop; nop ! 00 = reset (fake) 25623eb0b74Storek VTRAP(T_TEXTFAULT, memfault) ! 01 = instr. fetch fault 25723eb0b74Storek TRAP(T_ILLINST) ! 02 = illegal instruction 25823eb0b74Storek TRAP(T_PRIVINST) ! 03 = privileged instruction 25923eb0b74Storek TRAP(T_FPDISABLED) ! 04 = fp instr, but EF bit off in psr 26023eb0b74Storek WINDOW_OF ! 05 = window overflow 26123eb0b74Storek WINDOW_UF ! 06 = window underflow 26223eb0b74Storek TRAP(T_ALIGN) ! 07 = address alignment error 26323eb0b74Storek VTRAP(T_FPE, fp_exception) ! 08 = fp exception 26423eb0b74Storek VTRAP(T_DATAFAULT, memfault) ! 09 = data fetch fault 26523eb0b74Storek TRAP(T_TAGOF) ! 0a = tag overflow 26623eb0b74Storek UTRAP(0x0b) 26723eb0b74Storek UTRAP(0x0c) 26823eb0b74Storek UTRAP(0x0d) 26923eb0b74Storek UTRAP(0x0e) 27023eb0b74Storek UTRAP(0x0f) 27123eb0b74Storek UTRAP(0x10) 27223eb0b74Storek SOFTINT(1, IE_L1) ! 11 = level 1 interrupt 27323eb0b74Storek HARDINT(2) ! 12 = level 2 interrupt 27423eb0b74Storek HARDINT(3) ! 13 = level 3 interrupt 27523eb0b74Storek SOFTINT(4, IE_L4) ! 14 = level 4 interrupt 27623eb0b74Storek HARDINT(5) ! 15 = level 5 interrupt 27723eb0b74Storek SOFTINT(6, IE_L6) ! 16 = level 6 interrupt 27823eb0b74Storek HARDINT(7) ! 17 = level 7 interrupt 27923eb0b74Storek HARDINT(8) ! 18 = level 8 interrupt 28023eb0b74Storek HARDINT(9) ! 19 = level 9 interrupt 28123eb0b74Storek HARDINT(10) ! 1a = level 10 interrupt 28223eb0b74Storek HARDINT(11) ! 1b = level 11 interrupt 28323eb0b74Storek ZS_INTERRUPT ! 1c = level 12 (zs) interrupt 28423eb0b74Storek HARDINT(13) ! 1d = level 13 interrupt 28523eb0b74Storek HARDINT(14) ! 1e = level 14 interrupt 28623eb0b74Storek VTRAP(15, nmi) ! 1f = nonmaskable interrupt 28723eb0b74Storek UTRAP(0x20) 28823eb0b74Storek UTRAP(0x21) 28923eb0b74Storek UTRAP(0x22) 29023eb0b74Storek UTRAP(0x23) 29123eb0b74Storek UTRAP(0x24) 29223eb0b74Storek UTRAP(0x25) 29323eb0b74Storek UTRAP(0x26) 29423eb0b74Storek UTRAP(0x27) 29523eb0b74Storek UTRAP(0x28) 29623eb0b74Storek UTRAP(0x29) 29723eb0b74Storek UTRAP(0x2a) 29823eb0b74Storek UTRAP(0x2b) 29923eb0b74Storek UTRAP(0x2c) 30023eb0b74Storek UTRAP(0x2d) 30123eb0b74Storek UTRAP(0x2e) 30223eb0b74Storek UTRAP(0x2f) 30323eb0b74Storek UTRAP(0x30) 30423eb0b74Storek UTRAP(0x31) 30523eb0b74Storek UTRAP(0x32) 30623eb0b74Storek UTRAP(0x33) 30723eb0b74Storek UTRAP(0x34) 30823eb0b74Storek UTRAP(0x35) 30923eb0b74Storek TRAP(T_CPDISABLED) ! 36 = coprocessor instr, EC bit off in psr 31023eb0b74Storek UTRAP(0x37) 31123eb0b74Storek UTRAP(0x38) 31223eb0b74Storek UTRAP(0x39) 31323eb0b74Storek UTRAP(0x3a) 31423eb0b74Storek UTRAP(0x3b) 31523eb0b74Storek UTRAP(0x3c) 31623eb0b74Storek UTRAP(0x3d) 31723eb0b74Storek UTRAP(0x3e) 31823eb0b74Storek UTRAP(0x3f) 31923eb0b74Storek TRAP(T_CPEXCEPTION) ! 40 = coprocessor exception 32023eb0b74Storek UTRAP(0x41) 32123eb0b74Storek UTRAP(0x42) 32223eb0b74Storek UTRAP(0x43) 32323eb0b74Storek UTRAP(0x44) 32423eb0b74Storek UTRAP(0x45) 32523eb0b74Storek UTRAP(0x46) 32623eb0b74Storek UTRAP(0x47) 32723eb0b74Storek UTRAP(0x48) 32823eb0b74Storek UTRAP(0x49) 32923eb0b74Storek UTRAP(0x4a) 33023eb0b74Storek UTRAP(0x4b) 33123eb0b74Storek UTRAP(0x4c) 33223eb0b74Storek UTRAP(0x4d) 33323eb0b74Storek UTRAP(0x4e) 33423eb0b74Storek UTRAP(0x4f) 33523eb0b74Storek UTRAP(0x50) 33623eb0b74Storek UTRAP(0x51) 33723eb0b74Storek UTRAP(0x52) 33823eb0b74Storek UTRAP(0x53) 33923eb0b74Storek UTRAP(0x54) 34023eb0b74Storek UTRAP(0x55) 34123eb0b74Storek UTRAP(0x56) 34223eb0b74Storek UTRAP(0x57) 34323eb0b74Storek UTRAP(0x58) 34423eb0b74Storek UTRAP(0x59) 34523eb0b74Storek UTRAP(0x5a) 34623eb0b74Storek UTRAP(0x5b) 34723eb0b74Storek UTRAP(0x5c) 34823eb0b74Storek UTRAP(0x5d) 34923eb0b74Storek UTRAP(0x5e) 35023eb0b74Storek UTRAP(0x5f) 35123eb0b74Storek UTRAP(0x60) 35223eb0b74Storek UTRAP(0x61) 35323eb0b74Storek UTRAP(0x62) 35423eb0b74Storek UTRAP(0x63) 35523eb0b74Storek UTRAP(0x64) 35623eb0b74Storek UTRAP(0x65) 35723eb0b74Storek UTRAP(0x66) 35823eb0b74Storek UTRAP(0x67) 35923eb0b74Storek UTRAP(0x68) 36023eb0b74Storek UTRAP(0x69) 36123eb0b74Storek UTRAP(0x6a) 36223eb0b74Storek UTRAP(0x6b) 36323eb0b74Storek UTRAP(0x6c) 36423eb0b74Storek UTRAP(0x6d) 36523eb0b74Storek UTRAP(0x6e) 36623eb0b74Storek UTRAP(0x6f) 36723eb0b74Storek UTRAP(0x70) 36823eb0b74Storek UTRAP(0x71) 36923eb0b74Storek UTRAP(0x72) 37023eb0b74Storek UTRAP(0x73) 37123eb0b74Storek UTRAP(0x74) 37223eb0b74Storek UTRAP(0x75) 37323eb0b74Storek UTRAP(0x76) 37423eb0b74Storek UTRAP(0x77) 37523eb0b74Storek UTRAP(0x78) 37623eb0b74Storek UTRAP(0x79) 37723eb0b74Storek UTRAP(0x7a) 37823eb0b74Storek UTRAP(0x7b) 37923eb0b74Storek UTRAP(0x7c) 38023eb0b74Storek UTRAP(0x7d) 38123eb0b74Storek UTRAP(0x7e) 38223eb0b74Storek UTRAP(0x7f) 38323eb0b74Storek SUN_SYSCALL ! 80 = sun syscall 38423eb0b74Storek BPT ! 81 = pseudo breakpoint instruction 38523eb0b74Storek TRAP(T_DIV0) ! 82 = divide by zero 38623eb0b74Storek TRAP(T_FLUSHWIN) ! 83 = flush windows 38723eb0b74Storek TRAP(T_CLEANWIN) ! 84 = provide clean windows 38823eb0b74Storek TRAP(T_RANGECHECK) ! 85 = ??? 38923eb0b74Storek TRAP(T_FIXALIGN) ! 86 = fix up unaligned accesses 39023eb0b74Storek TRAP(T_INTOF) ! 87 = integer overflow 39123eb0b74Storek BPT_KGDB_EXEC ! 88 = enter kernel gdb on kernel startup 39223eb0b74Storek SYSCALL ! 89 = bsd syscall 39323eb0b74Storek STRAP(0x8a) 39423eb0b74Storek STRAP(0x8b) 39523eb0b74Storek STRAP(0x8c) 39623eb0b74Storek STRAP(0x8d) 39723eb0b74Storek STRAP(0x8e) 39823eb0b74Storek STRAP(0x8f) 39923eb0b74Storek STRAP(0x90) 40023eb0b74Storek STRAP(0x91) 40123eb0b74Storek STRAP(0x92) 40223eb0b74Storek STRAP(0x93) 40323eb0b74Storek STRAP(0x94) 40423eb0b74Storek STRAP(0x95) 40523eb0b74Storek STRAP(0x96) 40623eb0b74Storek STRAP(0x97) 40723eb0b74Storek STRAP(0x98) 40823eb0b74Storek STRAP(0x99) 40923eb0b74Storek STRAP(0x9a) 41023eb0b74Storek STRAP(0x9b) 41123eb0b74Storek STRAP(0x9c) 41223eb0b74Storek STRAP(0x9d) 41323eb0b74Storek STRAP(0x9e) 41423eb0b74Storek STRAP(0x9f) 41523eb0b74Storek STRAP(0xa0) 41623eb0b74Storek STRAP(0xa1) 41723eb0b74Storek STRAP(0xa2) 41823eb0b74Storek STRAP(0xa3) 41923eb0b74Storek STRAP(0xa4) 42023eb0b74Storek STRAP(0xa5) 42123eb0b74Storek STRAP(0xa6) 42223eb0b74Storek STRAP(0xa7) 42323eb0b74Storek STRAP(0xa8) 42423eb0b74Storek STRAP(0xa9) 42523eb0b74Storek STRAP(0xaa) 42623eb0b74Storek STRAP(0xab) 42723eb0b74Storek STRAP(0xac) 42823eb0b74Storek STRAP(0xad) 42923eb0b74Storek STRAP(0xae) 43023eb0b74Storek STRAP(0xaf) 43123eb0b74Storek STRAP(0xb0) 43223eb0b74Storek STRAP(0xb1) 43323eb0b74Storek STRAP(0xb2) 43423eb0b74Storek STRAP(0xb3) 43523eb0b74Storek STRAP(0xb4) 43623eb0b74Storek STRAP(0xb5) 43723eb0b74Storek STRAP(0xb6) 43823eb0b74Storek STRAP(0xb7) 43923eb0b74Storek STRAP(0xb8) 44023eb0b74Storek STRAP(0xb9) 44123eb0b74Storek STRAP(0xba) 44223eb0b74Storek STRAP(0xbb) 44323eb0b74Storek STRAP(0xbc) 44423eb0b74Storek STRAP(0xbd) 44523eb0b74Storek STRAP(0xbe) 44623eb0b74Storek STRAP(0xbf) 44723eb0b74Storek STRAP(0xc0) 44823eb0b74Storek STRAP(0xc1) 44923eb0b74Storek STRAP(0xc2) 45023eb0b74Storek STRAP(0xc3) 45123eb0b74Storek STRAP(0xc4) 45223eb0b74Storek STRAP(0xc5) 45323eb0b74Storek STRAP(0xc6) 45423eb0b74Storek STRAP(0xc7) 45523eb0b74Storek STRAP(0xc8) 45623eb0b74Storek STRAP(0xc9) 45723eb0b74Storek STRAP(0xca) 45823eb0b74Storek STRAP(0xcb) 45923eb0b74Storek STRAP(0xcc) 46023eb0b74Storek STRAP(0xcd) 46123eb0b74Storek STRAP(0xce) 46223eb0b74Storek STRAP(0xcf) 46323eb0b74Storek STRAP(0xd0) 46423eb0b74Storek STRAP(0xd1) 46523eb0b74Storek STRAP(0xd2) 46623eb0b74Storek STRAP(0xd3) 46723eb0b74Storek STRAP(0xd4) 46823eb0b74Storek STRAP(0xd5) 46923eb0b74Storek STRAP(0xd6) 47023eb0b74Storek STRAP(0xd7) 47123eb0b74Storek STRAP(0xd8) 47223eb0b74Storek STRAP(0xd9) 47323eb0b74Storek STRAP(0xda) 47423eb0b74Storek STRAP(0xdb) 47523eb0b74Storek STRAP(0xdc) 47623eb0b74Storek STRAP(0xdd) 47723eb0b74Storek STRAP(0xde) 47823eb0b74Storek STRAP(0xdf) 47923eb0b74Storek STRAP(0xe0) 48023eb0b74Storek STRAP(0xe1) 48123eb0b74Storek STRAP(0xe2) 48223eb0b74Storek STRAP(0xe3) 48323eb0b74Storek STRAP(0xe4) 48423eb0b74Storek STRAP(0xe5) 48523eb0b74Storek STRAP(0xe6) 48623eb0b74Storek STRAP(0xe7) 48723eb0b74Storek STRAP(0xe8) 48823eb0b74Storek STRAP(0xe9) 48923eb0b74Storek STRAP(0xea) 49023eb0b74Storek STRAP(0xeb) 49123eb0b74Storek STRAP(0xec) 49223eb0b74Storek STRAP(0xed) 49323eb0b74Storek STRAP(0xee) 49423eb0b74Storek STRAP(0xef) 49523eb0b74Storek STRAP(0xf0) 49623eb0b74Storek STRAP(0xf1) 49723eb0b74Storek STRAP(0xf2) 49823eb0b74Storek STRAP(0xf3) 49923eb0b74Storek STRAP(0xf4) 50023eb0b74Storek STRAP(0xf5) 50123eb0b74Storek STRAP(0xf6) 50223eb0b74Storek STRAP(0xf7) 50323eb0b74Storek STRAP(0xf8) 50423eb0b74Storek STRAP(0xf9) 50523eb0b74Storek STRAP(0xfa) 50623eb0b74Storek STRAP(0xfb) 50723eb0b74Storek STRAP(0xfc) 50823eb0b74Storek STRAP(0xfd) 50923eb0b74Storek STRAP(0xfe) 51023eb0b74Storek STRAP(0xff) 51123eb0b74Storek 51223eb0b74Storek /* the message buffer is always mapped */ 51323eb0b74Storek_msgbufmapped: 51423eb0b74Storek .word 1 51523eb0b74Storek 51623eb0b74Storek#ifdef DEBUG 51723eb0b74Storek/* 51823eb0b74Storek * A hardware red zone is impossible. We simulate one in software by 51923eb0b74Storek * keeping a `red zone' pointer; if %sp becomes less than this, we panic. 52023eb0b74Storek * This is expensive and is only enabled when debugging. 52123eb0b74Storek */ 52223eb0b74Storek#define REDSIZE (8*96) /* some room for bouncing */ 52323eb0b74Storek#define REDSTACK 2048 /* size of `panic: stack overflow' region */ 52423eb0b74Storek .data 52523eb0b74Storek_redzone: 52623eb0b74Storek .word _idle_u + REDSIZE 52723eb0b74Storek_redstack: 52823eb0b74Storek .skip REDSTACK 52923eb0b74Storek .text 53023eb0b74StorekLpanic_red: 53123eb0b74Storek .asciz "stack overflow" 53223eb0b74Storek ALIGN 53323eb0b74Storek 53423eb0b74Storek /* set stack pointer redzone to base+minstack; alters base */ 53523eb0b74Storek#define SET_SP_REDZONE(base, tmp) \ 53623eb0b74Storek add base, REDSIZE, base; \ 53723eb0b74Storek sethi %hi(_redzone), tmp; \ 53823eb0b74Storek st base, [tmp + %lo(_redzone)] 53923eb0b74Storek 54023eb0b74Storek /* variant with a constant */ 54123eb0b74Storek#define SET_SP_REDZONE_CONST(const, tmp1, tmp2) \ 54223eb0b74Storek set (const) + REDSIZE, tmp1; \ 54323eb0b74Storek sethi %hi(_redzone), tmp2; \ 54423eb0b74Storek st tmp1, [tmp2 + %lo(_redzone)] 54523eb0b74Storek 54623eb0b74Storek /* check stack pointer against redzone (uses two temps) */ 54723eb0b74Storek#define CHECK_SP_REDZONE(t1, t2) \ 54823eb0b74Storek sethi %hi(_redzone), t1; \ 54923eb0b74Storek ld [t1 + %lo(_redzone)], t2; \ 55023eb0b74Storek cmp %sp, t2; /* if sp >= t2, not in red zone */ \ 55123eb0b74Storek bgeu 7f; nop; /* and can continue normally */ \ 55223eb0b74Storek /* move to panic stack */ \ 55323eb0b74Storek st %g0, [t1 + %lo(_redzone)]; \ 55423eb0b74Storek set _redstack + REDSTACK - 96, %sp; \ 55523eb0b74Storek /* prevent panic() from lowering ipl */ \ 55623eb0b74Storek sethi %hi(_panicstr), t2; \ 55723eb0b74Storek set Lpanic_red, t2; \ 55823eb0b74Storek st t2, [t1 + %lo(_panicstr)]; \ 55923eb0b74Storek rd %psr, t1; /* t1 = splhigh() */ \ 56023eb0b74Storek or t1, PSR_PIL, t2; \ 56123eb0b74Storek wr t2, 0, %psr; \ 56223eb0b74Storek wr t2, PSR_ET, %psr; /* turn on traps */ \ 56323eb0b74Storek nop; nop; nop; \ 56423eb0b74Storek save %sp, -96, %sp; /* preserve current window */ \ 56523eb0b74Storek sethi %hi(Lpanic_red), %o0; \ 56623eb0b74Storek call _panic; or %o0, %lo(Lpanic_red), %o0; \ 56723eb0b74Storek7: 56823eb0b74Storek 56923eb0b74Storek#else 57023eb0b74Storek 57123eb0b74Storek#define SET_SP_REDZONE(base, tmp) 57223eb0b74Storek#define SET_SP_REDZONE_CONST(const, t1, t2) 57323eb0b74Storek#define CHECK_SP_REDZONE(t1, t2) 57423eb0b74Storek#endif 57523eb0b74Storek 57623eb0b74Storek/* 57723eb0b74Storek * The window code must verify user stack addresses before using them. 57823eb0b74Storek * A user stack pointer is invalid if: 57923eb0b74Storek * - it is not on an 8 byte boundary; 58023eb0b74Storek * - its pages (a register window, being 64 bytes, can occupy 58123eb0b74Storek * two pages) are not readable or writable. 58223eb0b74Storek * We define three separate macros here for testing user stack addresses. 58323eb0b74Storek * 58423eb0b74Storek * PTE_OF_ADDR locates a PTE, branching to a `bad address' 58523eb0b74Storek * handler if the stack pointer points into the hole in the 58623eb0b74Storek * address space (i.e., top 3 bits are not either all 1 or all 0); 58723eb0b74Storek * CMP_PTE_USER_READ compares the located PTE against `user read' mode; 58823eb0b74Storek * CMP_PTE_USER_WRITE compares the located PTE against `user write' mode. 58923eb0b74Storek * The compares give `equal' if read or write is OK. 59023eb0b74Storek * 59123eb0b74Storek * Note that the user stack pointer usually points into high addresses 59223eb0b74Storek * (top 3 bits all 1), so that is what we check first. 59323eb0b74Storek * 59423eb0b74Storek * The code below also assumes that PTE_OF_ADDR is safe in a delay 59523eb0b74Storek * slot; it is, at it merely sets its `pte' register to a temporary value. 59623eb0b74Storek */ 59723eb0b74Storek /* input: addr, output: pte; aux: bad address label */ 59823eb0b74Storek#define PTE_OF_ADDR(addr, pte, bad) \ 59923eb0b74Storek sra addr, PG_VSHIFT, pte; \ 60023eb0b74Storek cmp pte, -1; \ 60123eb0b74Storek be,a 1f; andn addr, 4095, pte; \ 60223eb0b74Storek tst pte; \ 60323eb0b74Storek bne bad; EMPTY; \ 60423eb0b74Storek andn addr, 4095, pte; \ 60523eb0b74Storek1: 60623eb0b74Storek 60723eb0b74Storek /* input: pte; output: condition codes */ 60823eb0b74Storek#define CMP_PTE_USER_READ(pte) \ 60923eb0b74Storek lda [pte] ASI_PTE, pte; \ 61023eb0b74Storek srl pte, PG_PROTSHIFT, pte; \ 61123eb0b74Storek andn pte, (PG_W >> PG_PROTSHIFT), pte; \ 61223eb0b74Storek cmp pte, PG_PROTUREAD 61323eb0b74Storek 61423eb0b74Storek /* input: pte; output: condition codes */ 61523eb0b74Storek#define CMP_PTE_USER_WRITE(pte) \ 61623eb0b74Storek lda [pte] ASI_PTE, pte; \ 61723eb0b74Storek srl pte, PG_PROTSHIFT, pte; \ 61823eb0b74Storek cmp pte, PG_PROTUWRITE 61923eb0b74Storek 62023eb0b74Storek/* 62123eb0b74Storek * The calculations in PTE_OF_ADDR and CMP_PTE_USER_* are rather slow: 62223eb0b74Storek * in particular, according to Gordon Irlam of the University of Adelaide 62323eb0b74Storek * in Australia, these consume at least 18 cycles on an SS1 and 37 on an 62423eb0b74Storek * SS2. Hence, we try to avoid them in the common case. 62523eb0b74Storek * 62623eb0b74Storek * A chunk of 64 bytes is on a single page if and only if: 62723eb0b74Storek * 62823eb0b74Storek * ((base + 64 - 1) & ~4095) == (base & ~4095) 62923eb0b74Storek * 63023eb0b74Storek * Equivalently (and faster to test), the low order bits (base & 4095) must 63123eb0b74Storek * be small enough so that the sum (base + 63) does not carry out into the 63223eb0b74Storek * upper page-address bits, i.e., 63323eb0b74Storek * 63423eb0b74Storek * (base & 4095) < (4096 - 63) 63523eb0b74Storek * 63623eb0b74Storek * so we allow testing that here. This macro is also assumed to be safe 63723eb0b74Storek * in a delay slot (modulo overwriting its temporary). 63823eb0b74Storek */ 63923eb0b74Storek#define SLT_IF_1PAGE_RW(addr, tmp) \ 64023eb0b74Storek and addr, 4095, tmp; \ 64123eb0b74Storek cmp tmp, (4096 - 63) 64223eb0b74Storek 64323eb0b74Storek/* 64423eb0b74Storek * Every trap that enables traps must set up stack space. 64523eb0b74Storek * If the trap is from user mode, this involves switching to the kernel 64623eb0b74Storek * stack for the current process, and we must also set cpcb->pcb_uw 64723eb0b74Storek * so that the window overflow handler can tell user windows from kernel 64823eb0b74Storek * windows. 64923eb0b74Storek * 65023eb0b74Storek * The number of user windows is: 65123eb0b74Storek * 65223eb0b74Storek * cpcb->pcb_uw = (cpcb->pcb_wim - 1 - CWP) % nwindows 65323eb0b74Storek * 65423eb0b74Storek * (where pcb_wim = log2(current %wim) and CWP = low 5 bits of %psr). 65523eb0b74Storek * We compute this expression by table lookup in uwtab[CWP - pcb_wim], 65623eb0b74Storek * which has been set up as: 65723eb0b74Storek * 65823eb0b74Storek * for i in [-nwin+1 .. nwin-1] 65923eb0b74Storek * uwtab[i] = (nwin - 1 - i) % nwin; 66023eb0b74Storek * 66123eb0b74Storek * (If you do not believe this works, try it for yourself.) 66223eb0b74Storek * 66323eb0b74Storek * We also keep one or two more tables: 66423eb0b74Storek * 66523eb0b74Storek * for i in 0..nwin-1 66623eb0b74Storek * wmask[i] = 1 << ((i + 1) % nwindows); 66723eb0b74Storek * 66823eb0b74Storek * wmask[CWP] tells whether a `rett' would return into the invalid window. 66923eb0b74Storek */ 67023eb0b74Storek .data 67123eb0b74Storek .skip 32 ! alignment byte & negative indicies 67223eb0b74Storekuwtab: .skip 32 ! u_char uwtab[-31..31]; 67323eb0b74Storekwmask: .skip 32 ! u_char wmask[0..31]; 67423eb0b74Storek 67523eb0b74Storek .text 67623eb0b74Storek/* 67723eb0b74Storek * Things begin to grow uglier.... 67823eb0b74Storek * 67923eb0b74Storek * Each trap handler may (always) be running in the trap window. 68023eb0b74Storek * If this is the case, it cannot enable further traps until it writes 68123eb0b74Storek * the register windows into the stack (or, if the stack is no good, 68223eb0b74Storek * the current pcb). 68323eb0b74Storek * 68423eb0b74Storek * ASSUMPTIONS: TRAP_SETUP() is called with: 68523eb0b74Storek * %l0 = %psr 68623eb0b74Storek * %l1 = return pc 68723eb0b74Storek * %l2 = return npc 68823eb0b74Storek * %l3 = (some value that must not be altered) 68923eb0b74Storek * which means we have 4 registers to work with. 69023eb0b74Storek * 69123eb0b74Storek * The `stackspace' argument is the number of stack bytes to allocate 69223eb0b74Storek * for register-saving, and must be at least -64 (and typically more, 69323eb0b74Storek * for global registers and %y). 69423eb0b74Storek * 69523eb0b74Storek * Trapframes should use -CCFSZ-80. (80 = sizeof(struct trapframe); 69623eb0b74Storek * see trap.h. This basically means EVERYONE. Interrupt frames could 69723eb0b74Storek * get away with less, but currently do not.) 69823eb0b74Storek * 69923eb0b74Storek * The basic outline here is: 70023eb0b74Storek * 70123eb0b74Storek * if (trap came from kernel mode) { 70223eb0b74Storek * if (we are in the trap window) 70323eb0b74Storek * save it away; 70423eb0b74Storek * %sp = %fp - stackspace; 70523eb0b74Storek * } else { 70623eb0b74Storek * compute the number of user windows; 70723eb0b74Storek * if (we are in the trap window) 70823eb0b74Storek * save it away; 70923eb0b74Storek * %sp = (top of kernel stack) - stackspace; 71023eb0b74Storek * } 71123eb0b74Storek * 71223eb0b74Storek * Again, the number of user windows is: 71323eb0b74Storek * 71423eb0b74Storek * cpcb->pcb_uw = (cpcb->pcb_wim - 1 - CWP) % nwindows 71523eb0b74Storek * 71623eb0b74Storek * (where pcb_wim = log2(current %wim) and CWP is the low 5 bits of %psr), 71723eb0b74Storek * and this is computed as `uwtab[CWP - pcb_wim]'. 71823eb0b74Storek * 71923eb0b74Storek * NOTE: if you change this code, you will have to look carefully 72023eb0b74Storek * at the window overflow and underflow handlers and make sure they 72123eb0b74Storek * have similar changes made as needed. 72223eb0b74Storek */ 72323eb0b74Storek#define CALL_CLEAN_TRAP_WINDOW \ 72423eb0b74Storek sethi %hi(clean_trap_window), %l7; \ 72523eb0b74Storek jmpl %l7 + %lo(clean_trap_window), %l4; \ 72623eb0b74Storek mov %g7, %l7 /* save %g7 in %l7 for clean_trap_window */ 72723eb0b74Storek 72823eb0b74Storek#define TRAP_SETUP(stackspace) \ 72923eb0b74Storek rd %wim, %l4; \ 73023eb0b74Storek mov 1, %l5; \ 73123eb0b74Storek sll %l5, %l0, %l5; \ 73223eb0b74Storek btst PSR_PS, %l0; \ 73323eb0b74Storek bz 1f; \ 73423eb0b74Storek btst %l5, %l4; \ 73523eb0b74Storek /* came from kernel mode; cond codes indicate trap window */ \ 73623eb0b74Storek bz,a 3f; \ 73723eb0b74Storek add %fp, stackspace, %sp; /* want to just set %sp */ \ 73823eb0b74Storek CALL_CLEAN_TRAP_WINDOW; /* but maybe need to clean first */ \ 73923eb0b74Storek b 3f; \ 74023eb0b74Storek add %fp, stackspace, %sp; \ 74123eb0b74Storek1: \ 74223eb0b74Storek /* came from user mode: compute pcb_nw */ \ 74323eb0b74Storek sethi %hi(_cpcb), %l6; \ 74423eb0b74Storek ld [%l6 + %lo(_cpcb)], %l6; \ 74523eb0b74Storek ld [%l6 + PCB_WIM], %l5; \ 74623eb0b74Storek and %l0, 31, %l4; \ 74723eb0b74Storek sub %l4, %l5, %l5; \ 74823eb0b74Storek set uwtab, %l4; \ 74923eb0b74Storek ldub [%l4 + %l5], %l5; \ 75023eb0b74Storek st %l5, [%l6 + PCB_UW]; \ 75123eb0b74Storek /* cond codes still indicate whether in trap window */ \ 75223eb0b74Storek bz,a 2f; \ 75323eb0b74Storek sethi %hi(UPAGES*NBPG+(stackspace)), %l5; \ 75423eb0b74Storek /* yes, in trap window; must clean it */ \ 75523eb0b74Storek CALL_CLEAN_TRAP_WINDOW; \ 75623eb0b74Storek sethi %hi(_cpcb), %l6; \ 75723eb0b74Storek ld [%l6 + %lo(_cpcb)], %l6; \ 75823eb0b74Storek sethi %hi(UPAGES*NBPG+(stackspace)), %l5; \ 75923eb0b74Storek2: \ 76023eb0b74Storek /* trap window is (now) clean: set %sp */ \ 76123eb0b74Storek or %l5, %lo(UPAGES*NBPG+(stackspace)), %l5; \ 76223eb0b74Storek add %l6, %l5, %sp; \ 76323eb0b74Storek SET_SP_REDZONE(%l6, %l5); \ 76423eb0b74Storek3: \ 76523eb0b74Storek CHECK_SP_REDZONE(%l6, %l5) 76623eb0b74Storek 76723eb0b74Storek/* 76823eb0b74Storek * Interrupt setup is almost exactly like trap setup, but we need to 76923eb0b74Storek * go to the interrupt stack if (a) we came from user mode or (b) we 77023eb0b74Storek * came from kernel mode on the kernel stack. 77123eb0b74Storek */ 77223eb0b74Storek#define INTR_SETUP(stackspace) \ 77323eb0b74Storek rd %wim, %l4; \ 77423eb0b74Storek mov 1, %l5; \ 77523eb0b74Storek sll %l5, %l0, %l5; \ 77623eb0b74Storek btst PSR_PS, %l0; \ 77723eb0b74Storek bz 1f; \ 77823eb0b74Storek btst %l5, %l4; \ 77923eb0b74Storek /* came from kernel mode; cond codes still indicate trap window */ \ 78023eb0b74Storek bz,a 0f; \ 78123eb0b74Storek sethi %hi(_eintstack), %l7; \ 78223eb0b74Storek CALL_CLEAN_TRAP_WINDOW; \ 78323eb0b74Storek sethi %hi(_eintstack), %l7; \ 78423eb0b74Storek0: /* now if %fp >= eintstack, we were on the kernel stack */ \ 78523eb0b74Storek cmp %fp, %l7; \ 78623eb0b74Storek bge,a 3f; \ 78723eb0b74Storek add %l7, stackspace, %sp; /* so switch to intstack */ \ 78823eb0b74Storek b 4f; \ 78923eb0b74Storek add %fp, stackspace, %sp; /* else stay on intstack */ \ 79023eb0b74Storek1: \ 79123eb0b74Storek /* came from user mode: compute pcb_nw */ \ 79223eb0b74Storek sethi %hi(_cpcb), %l6; \ 79323eb0b74Storek ld [%l6 + %lo(_cpcb)], %l6; \ 79423eb0b74Storek ld [%l6 + PCB_WIM], %l5; \ 79523eb0b74Storek and %l0, 31, %l4; \ 79623eb0b74Storek sub %l4, %l5, %l5; \ 79723eb0b74Storek set uwtab, %l4; \ 79823eb0b74Storek ldub [%l4 + %l5], %l5; \ 79923eb0b74Storek st %l5, [%l6 + PCB_UW]; \ 80023eb0b74Storek /* cond codes still indicate whether in trap window */ \ 80123eb0b74Storek bz,a 2f; \ 80223eb0b74Storek sethi %hi(_eintstack), %l7; \ 80323eb0b74Storek /* yes, in trap window; must save regs */ \ 80423eb0b74Storek CALL_CLEAN_TRAP_WINDOW; \ 80523eb0b74Storek sethi %hi(_eintstack), %l7; \ 80623eb0b74Storek2: \ 80723eb0b74Storek add %l7, stackspace, %sp; \ 80823eb0b74Storek3: \ 80923eb0b74Storek SET_SP_REDZONE_CONST(_intstack, %l6, %l5); \ 81023eb0b74Storek4: \ 81123eb0b74Storek CHECK_SP_REDZONE(%l6, %l5) 81223eb0b74Storek 81323eb0b74Storek/* 81423eb0b74Storek * Handler for making the trap window shiny clean. 81523eb0b74Storek * 81623eb0b74Storek * On entry: 81723eb0b74Storek * cpcb->pcb_nw = number of user windows 81823eb0b74Storek * %l0 = %psr 81923eb0b74Storek * %l1 must not be clobbered 82023eb0b74Storek * %l2 must not be clobbered 82123eb0b74Storek * %l3 must not be clobbered 82223eb0b74Storek * %l4 = address for `return' 82323eb0b74Storek * %l7 = saved %g7 (we put this in a delay slot above, to save work) 82423eb0b74Storek * 82523eb0b74Storek * On return: 82623eb0b74Storek * %wim has changed, along with cpcb->pcb_wim 82723eb0b74Storek * %g7 has been restored 82823eb0b74Storek * 82923eb0b74Storek * Normally, we push only one window. 83023eb0b74Storek */ 83123eb0b74Storekclean_trap_window: 83223eb0b74Storek mov %g5, %l5 ! save %g5 83323eb0b74Storek mov %g6, %l6 ! ... and %g6 83423eb0b74Storek/* mov %g7, %l7 ! ... and %g7 (already done for us) */ 83523eb0b74Storek sethi %hi(_cpcb), %g6 ! get current pcb 83623eb0b74Storek ld [%g6 + %lo(_cpcb)], %g6 83723eb0b74Storek 83823eb0b74Storek /* Figure out whether it is a user window (cpcb->pcb_uw > 0). */ 83923eb0b74Storek ld [%g6 + PCB_UW], %g7 84023eb0b74Storek deccc %g7 84123eb0b74Storek bge ctw_user 84223eb0b74Storek save %g0, %g0, %g0 ! in any case, enter window to save 84323eb0b74Storek 84423eb0b74Storek /* The window to be pushed is a kernel window. */ 84523eb0b74Storek std %l0, [%sp + (0*8)] 84623eb0b74Storekctw_merge: 84723eb0b74Storek std %l2, [%sp + (1*8)] 84823eb0b74Storek std %l4, [%sp + (2*8)] 84923eb0b74Storek std %l6, [%sp + (3*8)] 85023eb0b74Storek std %i0, [%sp + (4*8)] 85123eb0b74Storek std %i2, [%sp + (5*8)] 85223eb0b74Storek std %i4, [%sp + (6*8)] 85323eb0b74Storek std %i6, [%sp + (7*8)] 85423eb0b74Storek 85523eb0b74Storek /* Set up new window invalid mask, and update cpcb->pcb_wim. */ 85623eb0b74Storek rd %psr, %g7 ! g7 = (junk << 5) + new_cwp 85723eb0b74Storek mov 1, %g5 ! g5 = 1 << new_cwp; 85823eb0b74Storek sll %g5, %g7, %g5 85923eb0b74Storek wr %g5, 0, %wim ! setwim(g5); 86023eb0b74Storek and %g7, 31, %g7 ! cpcb->pcb_wim = g7 & 31; 86123eb0b74Storek st %g7, [%g6 + PCB_WIM] 86223eb0b74Storek nop 86323eb0b74Storek restore ! back to trap window 86423eb0b74Storek 86523eb0b74Storek mov %l5, %g5 ! restore g5 86623eb0b74Storek mov %l6, %g6 ! ... and g6 86723eb0b74Storek jmp %l4 + 8 ! return to caller 86823eb0b74Storek mov %l7, %g7 ! ... and g7 86923eb0b74Storek /* NOTREACHED */ 87023eb0b74Storek 87123eb0b74Storekctw_user: 87223eb0b74Storek /* 87323eb0b74Storek * The window to be pushed is a user window. 87423eb0b74Storek * We must verify the stack pointer (alignment & permissions). 87523eb0b74Storek * See comments above definition of PTE_OF_ADDR. 87623eb0b74Storek */ 87723eb0b74Storek st %g7, [%g6 + PCB_UW] ! cpcb->pcb_uw--; 87823eb0b74Storek btst 7, %sp ! if not aligned, 87923eb0b74Storek bne ctw_invalid ! choke on it 88023eb0b74Storek EMPTY 88123eb0b74Storek PTE_OF_ADDR(%sp, %g7, ctw_invalid) 88223eb0b74Storek CMP_PTE_USER_WRITE(%g7) ! likewise if not writable 88323eb0b74Storek bne ctw_invalid 88423eb0b74Storek EMPTY 88523eb0b74Storek SLT_IF_1PAGE_RW(%sp, %g7) 88623eb0b74Storek bl,a ctw_merge ! all ok if only 1 88723eb0b74Storek std %l0, [%sp] 88823eb0b74Storek add %sp, 7*8, %g5 ! check last addr too 88923eb0b74Storek PTE_OF_ADDR(%g5, %g7, ctw_invalid) 89023eb0b74Storek CMP_PTE_USER_WRITE(%g7) 89123eb0b74Storek be,a ctw_merge ! all ok: store <l0,l1> and merge 89223eb0b74Storek std %l0, [%sp] 89323eb0b74Storek 89423eb0b74Storek /* 89523eb0b74Storek * The window we wanted to push could not be pushed. 89623eb0b74Storek * Instead, save ALL user windows into the pcb. 89723eb0b74Storek * We will notice later that we did this, when we 89823eb0b74Storek * get ready to return from our trap or syscall. 89923eb0b74Storek * 90023eb0b74Storek * The code here is run rarely and need not be optimal. 90123eb0b74Storek */ 90223eb0b74Storekctw_invalid: 90323eb0b74Storek /* 90423eb0b74Storek * Reread cpcb->pcb_uw. We decremented this earlier, 90523eb0b74Storek * so it is off by one. 90623eb0b74Storek */ 90723eb0b74Storek ld [%g6 + PCB_UW], %g7 ! (number of user windows) - 1 90823eb0b74Storek add %g6, PCB_RW, %g5 90923eb0b74Storek 91023eb0b74Storek /* save g7+1 windows, starting with the current one */ 91123eb0b74Storek1: ! do { 91223eb0b74Storek std %l0, [%g5 + (0*8)] ! rw->rw_local[0] = l0; 91323eb0b74Storek std %l2, [%g5 + (1*8)] ! ... 91423eb0b74Storek std %l4, [%g5 + (2*8)] 91523eb0b74Storek std %l6, [%g5 + (3*8)] 91623eb0b74Storek std %i0, [%g5 + (4*8)] 91723eb0b74Storek std %i2, [%g5 + (5*8)] 91823eb0b74Storek std %i4, [%g5 + (6*8)] 91923eb0b74Storek std %i6, [%g5 + (7*8)] 92023eb0b74Storek deccc %g7 ! if (n > 0) save(), rw++; 92123eb0b74Storek bge,a 1b ! } while (--n >= 0); 92223eb0b74Storek save %g5, 64, %g5 92323eb0b74Storek 92423eb0b74Storek /* stash sp for bottommost window */ 92523eb0b74Storek st %sp, [%g5 + 64 + (7*8)] 92623eb0b74Storek 92723eb0b74Storek /* set up new wim */ 92823eb0b74Storek rd %psr, %g7 ! g7 = (junk << 5) + new_cwp; 92923eb0b74Storek mov 1, %g5 ! g5 = 1 << new_cwp; 93023eb0b74Storek sll %g5, %g7, %g5 93123eb0b74Storek wr %g5, 0, %wim ! wim = g5; 93223eb0b74Storek and %g7, 31, %g7 93323eb0b74Storek st %g7, [%g6 + PCB_WIM] ! cpcb->pcb_wim = new_cwp; 93423eb0b74Storek 93523eb0b74Storek /* fix up pcb fields */ 93623eb0b74Storek ld [%g6 + PCB_UW], %g7 ! n = cpcb->pcb_uw; 93723eb0b74Storek add %g7, 1, %g5 93823eb0b74Storek st %g5, [%g6 + PCB_NSAVED] ! cpcb->pcb_nsaved = n + 1; 93923eb0b74Storek st %g0, [%g6 + PCB_UW] ! cpcb->pcb_uw = 0; 94023eb0b74Storek 94123eb0b74Storek /* return to trap window */ 94223eb0b74Storek1: deccc %g7 ! do { 94323eb0b74Storek bge 1b ! restore(); 94423eb0b74Storek restore ! } while (--n >= 0); 94523eb0b74Storek 94623eb0b74Storek mov %l5, %g5 ! restore g5, g6, & g7, and return 94723eb0b74Storek mov %l6, %g6 94823eb0b74Storek jmp %l4 + 8 94923eb0b74Storek mov %l7, %g7 95023eb0b74Storek /* NOTREACHED */ 95123eb0b74Storek 95223eb0b74Storek 95323eb0b74Storek/* 95423eb0b74Storek * Each memory access (text or data) fault, from user or kernel mode, 95523eb0b74Storek * comes here. We read the error register and figure out what has 95623eb0b74Storek * happened. 95723eb0b74Storek * 95823eb0b74Storek * This cannot be done from C code since we must not enable traps (and 95923eb0b74Storek * hence may not use the `save' instruction) until we have decided that 96023eb0b74Storek * the error is or is not an asynchronous one that showed up after a 96123eb0b74Storek * synchronous error, but which must be handled before the sync err. 96223eb0b74Storek * 96323eb0b74Storek * Most memory faults are user mode text or data faults, which can cause 96423eb0b74Storek * signal delivery or ptracing, for which we must build a full trapframe. 96523eb0b74Storek * It does not seem worthwhile to work to avoid this in the other cases, 96623eb0b74Storek * so we store all the %g registers on the stack immediately. 96723eb0b74Storek * 96823eb0b74Storek * On entry: 96923eb0b74Storek * %l0 = %psr 97023eb0b74Storek * %l1 = return pc 97123eb0b74Storek * %l2 = return npc 97223eb0b74Storek * %l3 = T_TEXTFAULT or T_DATAFAULT 97323eb0b74Storek * 97423eb0b74Storek * Internal: 97523eb0b74Storek * %l4 = %y, until we call mem_access_fault (then onto trapframe) 97623eb0b74Storek * %l5 = IE_reg_addr, if async mem error 97723eb0b74Storek * 97823eb0b74Storek * We know about the layout of the error registers here. 97923eb0b74Storek * addr reg 98023eb0b74Storek * ---- --- 98123eb0b74Storek * a AC_SYNC_ERR 98223eb0b74Storek * a+4 AC_SYNC_VA 98323eb0b74Storek * a+8 AC_ASYNC_ERR 98423eb0b74Storek * a+12 AC_ASYNC_VA 98523eb0b74Storek */ 98623eb0b74Storekmemfault: 98723eb0b74Storek TRAP_SETUP(-CCFSZ-80) 98823eb0b74Storek 98923eb0b74Storek INCR(_cnt+V_FAULTS) ! cnt.v_faults++ (clobbers %o0,%o1) 99023eb0b74Storek 99123eb0b74Storek st %g1, [%sp + CCFSZ + 20] ! save g1 99223eb0b74Storek rd %y, %l4 ! save y 99323eb0b74Storek 99423eb0b74Storek#if AC_SYNC_ERR + 4 != AC_SYNC_VA || \ 99523eb0b74Storek AC_SYNC_ERR + 8 != AC_ASYNC_ERR || AC_SYNC_ERR + 12 != AC_ASYNC_VA 99623eb0b74Storek help help help ! I, I, I wanna be a lifeguard 99723eb0b74Storek#endif 99823eb0b74Storek set AC_SYNC_ERR, %o0 99923eb0b74Storek std %g2, [%sp + CCFSZ + 24] ! save g2, g3 100023eb0b74Storek lda [%o0] ASI_CONTROL, %o1 ! sync err reg 100123eb0b74Storek inc 4, %o0 100223eb0b74Storek std %g4, [%sp + CCFSZ + 32] ! (sneak g4,g5 in here) 100323eb0b74Storek lda [%o0] ASI_CONTROL, %o2 ! sync virt addr 100423eb0b74Storek btst SER_MEMERR, %o1 ! memory error? 100523eb0b74Storek std %g6, [%sp + CCFSZ + 40] 100623eb0b74Storek bz,a normal_mem_fault ! no, just a regular fault 100723eb0b74Storek wr %l0, PSR_ET, %psr ! (and reenable traps) 100823eb0b74Storek 100923eb0b74Storek /* 101023eb0b74Storek * We got a synchronous memory error. It could be one that 101123eb0b74Storek * happened because there were two stores in a row, and the 101223eb0b74Storek * first went into the write buffer, and the second caused this 101323eb0b74Storek * synchronous trap; so there could now be a pending async error. 101423eb0b74Storek * This is in fact the case iff the two va's differ. 101523eb0b74Storek */ 101623eb0b74Storek inc 4, %o0 101723eb0b74Storek lda [%o0] ASI_CONTROL, %o3 ! async err reg 101823eb0b74Storek inc 4, %o0 101923eb0b74Storek lda [%o0] ASI_CONTROL, %o4 ! async virt addr 102023eb0b74Storek cmp %o2, %o4 102123eb0b74Storek be,a 1f ! no, not an async err 102223eb0b74Storek wr %l0, PSR_ET, %psr ! (and reenable traps) 102323eb0b74Storek 102423eb0b74Storek /* 102523eb0b74Storek * Handle the async error; ignore the sync error for now 102623eb0b74Storek * (we may end up getting it again, but so what?). 102723eb0b74Storek * This code is essentially the same as that at `nmi' below, 102823eb0b74Storek * but the register usage is different and we cannot merge. 102923eb0b74Storek */ 103023eb0b74Storek sethi %hi(IE_reg_addr), %l5 ! ienab_bic(IE_ALLIE); 103123eb0b74Storek ldub [%l5 + %lo(IE_reg_addr)], %o0 103223eb0b74Storek andn %o0, IE_ALLIE, %o0 103323eb0b74Storek stb %o0, [%l5 + %lo(IE_reg_addr)] 103423eb0b74Storek 103523eb0b74Storek /* 103623eb0b74Storek * Now reenable traps and call C code. 103723eb0b74Storek * %o1 through %o4 still hold the error reg contents. 103823eb0b74Storek * If memerr() returns, return from the trap. 103923eb0b74Storek */ 104023eb0b74Storek wr %l0, PSR_ET, %psr 104123eb0b74Storek call _memerr ! memerr(0, ser, sva, aer, ava) 104223eb0b74Storek clr %o0 104323eb0b74Storek 104423eb0b74Storek ld [%sp + CCFSZ + 20], %g1 ! restore g1 through g7 104523eb0b74Storek wr %l0, 0, %psr ! and disable traps, 3 instr delay 104623eb0b74Storek ldd [%sp + CCFSZ + 24], %g2 104723eb0b74Storek ldd [%sp + CCFSZ + 32], %g4 104823eb0b74Storek ldd [%sp + CCFSZ + 40], %g6 104923eb0b74Storek /* now safe to set IE_ALLIE again */ 105023eb0b74Storek ldub [%l5 + %lo(IE_reg_addr)], %o1 105123eb0b74Storek or %o1, IE_ALLIE, %o1 105223eb0b74Storek stb %o1, [%l5 + %lo(IE_reg_addr)] 105323eb0b74Storek b return_from_trap 105423eb0b74Storek wr %l4, 0, %y ! restore y 105523eb0b74Storek 105623eb0b74Storek /* 105723eb0b74Storek * Trap was a synchronous memory error. 105823eb0b74Storek * %o1 through %o4 still hold the error reg contents. 105923eb0b74Storek */ 106023eb0b74Storek1: 106123eb0b74Storek call _memerr ! memerr(1, ser, sva, aer, ava) 106223eb0b74Storek mov 1, %o0 106323eb0b74Storek 106423eb0b74Storek ld [%sp + CCFSZ + 20], %g1 ! restore g1 through g7 106523eb0b74Storek ldd [%sp + CCFSZ + 24], %g2 106623eb0b74Storek ldd [%sp + CCFSZ + 32], %g4 106723eb0b74Storek ldd [%sp + CCFSZ + 40], %g6 106823eb0b74Storek wr %l4, 0, %y ! restore y 106923eb0b74Storek b return_from_trap 107023eb0b74Storek wr %l0, 0, %psr 107123eb0b74Storek /* NOTREACHED */ 107223eb0b74Storek 107323eb0b74Storeknormal_mem_fault: 107423eb0b74Storek /* 107523eb0b74Storek * Trap was some other error; call C code to deal with it. 107623eb0b74Storek * Must finish trap frame (psr,pc,npc,%y,%o0..%o7) in case 107723eb0b74Storek * we decide to deliver a signal or ptrace the process. 107823eb0b74Storek * %g1..%g7 were already set up above. 107923eb0b74Storek */ 108023eb0b74Storek std %l0, [%sp + CCFSZ + 0] ! set tf.tf_psr, tf.tf_pc 108123eb0b74Storek mov %l3, %o0 ! (argument: type) 108223eb0b74Storek st %l2, [%sp + CCFSZ + 8] ! set tf.tf_npc 108323eb0b74Storek st %l4, [%sp + CCFSZ + 12] ! set tf.tf_y 108423eb0b74Storek mov %l1, %o3 ! (argument: pc) 108523eb0b74Storek std %i0, [%sp + CCFSZ + 48] ! tf.tf_out[0], etc 108623eb0b74Storek std %i2, [%sp + CCFSZ + 56] 108723eb0b74Storek mov %l0, %o4 ! (argument: psr) 108823eb0b74Storek std %i4, [%sp + CCFSZ + 64] 108923eb0b74Storek std %i6, [%sp + CCFSZ + 72] 109023eb0b74Storek call _mem_access_fault ! mem_access_fault(type, ser, sva, 109123eb0b74Storek ! pc, psr, &tf); 109223eb0b74Storek add %sp, CCFSZ, %o5 ! (argument: &tf) 109323eb0b74Storek 109423eb0b74Storek ldd [%sp + CCFSZ + 0], %l0 ! load new values 109523eb0b74Storek ldd [%sp + CCFSZ + 8], %l2 109623eb0b74Storek wr %l3, 0, %y 109723eb0b74Storek ld [%sp + CCFSZ + 20], %g1 109823eb0b74Storek ldd [%sp + CCFSZ + 24], %g2 109923eb0b74Storek ldd [%sp + CCFSZ + 32], %g4 110023eb0b74Storek ldd [%sp + CCFSZ + 40], %g6 110123eb0b74Storek ldd [%sp + CCFSZ + 48], %i0 110223eb0b74Storek ldd [%sp + CCFSZ + 56], %i2 110323eb0b74Storek ldd [%sp + CCFSZ + 64], %i4 110423eb0b74Storek ldd [%sp + CCFSZ + 72], %i6 110523eb0b74Storek 110623eb0b74Storek b return_from_trap ! go return 110723eb0b74Storek wr %l0, 0, %psr ! (but first disable traps again) 110823eb0b74Storek 110923eb0b74Storek 111023eb0b74Storek/* 111123eb0b74Storek * fp_exception has to check to see if we are trying to save 111223eb0b74Storek * the FP state, and if so, continue to save the FP state. 111323eb0b74Storek * 111423eb0b74Storek * We do not even bother checking to see if we were in kernel mode, 111523eb0b74Storek * since users have no access to the special_fp_store instruction. 111623eb0b74Storek * 111723eb0b74Storek * This whole idea was stolen from Sprite. 111823eb0b74Storek */ 111923eb0b74Storekfp_exception: 112023eb0b74Storek set special_fp_store, %l4 ! see if we came from the special one 112123eb0b74Storek cmp %l1, %l4 ! pc == special_fp_store? 112223eb0b74Storek bne slowtrap ! no, go handle per usual 112323eb0b74Storek EMPTY 112423eb0b74Storek sethi %hi(savefpcont), %l4 ! yes, "return" to the special code 112523eb0b74Storek or %lo(savefpcont), %l4, %l4 112623eb0b74Storek jmp %l4 112723eb0b74Storek rett %l4 + 4 112823eb0b74Storek 112923eb0b74Storek/* 113023eb0b74Storek * slowtrap() builds a trap frame and calls trap(). 113123eb0b74Storek * This is called `slowtrap' because it *is*.... 113223eb0b74Storek * We have to build a full frame for ptrace(), for instance. 113323eb0b74Storek * 113423eb0b74Storek * Registers: 113523eb0b74Storek * %l0 = %psr 113623eb0b74Storek * %l1 = return pc 113723eb0b74Storek * %l2 = return npc 113823eb0b74Storek * %l3 = trap code 113923eb0b74Storek */ 114023eb0b74Storekslowtrap: 114123eb0b74Storek TRAP_SETUP(-CCFSZ-80) 114223eb0b74Storek /* 114323eb0b74Storek * Phew, ready to enable traps and call C code. 114423eb0b74Storek */ 114523eb0b74Storek mov %l3, %o0 ! put type in %o0 for later 114623eb0b74StorekLslowtrap_reenter: 114723eb0b74Storek wr %l0, PSR_ET, %psr ! traps on again 114823eb0b74Storek std %l0, [%sp + CCFSZ] ! tf.tf_psr = psr; tf.tf_pc = ret_pc; 114923eb0b74Storek rd %y, %l3 115023eb0b74Storek std %l2, [%sp + CCFSZ + 8] ! tf.tf_npc = return_npc; tf.tf_y = %y; 115123eb0b74Storek st %g1, [%sp + CCFSZ + 20] 115223eb0b74Storek std %g2, [%sp + CCFSZ + 24] 115323eb0b74Storek std %g4, [%sp + CCFSZ + 32] 115423eb0b74Storek std %g6, [%sp + CCFSZ + 40] 115523eb0b74Storek std %i0, [%sp + CCFSZ + 48] 115623eb0b74Storek mov %l0, %o1 ! (psr) 115723eb0b74Storek std %i2, [%sp + CCFSZ + 56] 115823eb0b74Storek mov %l1, %o2 ! (pc) 115923eb0b74Storek std %i4, [%sp + CCFSZ + 64] 116023eb0b74Storek add %sp, CCFSZ, %o3 ! (&tf) 116123eb0b74Storek call _trap ! trap(type, psr, pc, &tf) 116223eb0b74Storek std %i6, [%sp + CCFSZ + 72] 116323eb0b74Storek 116423eb0b74Storek ldd [%sp + CCFSZ], %l0 ! load new values 116523eb0b74Storek ldd [%sp + CCFSZ + 8], %l2 116623eb0b74Storek wr %l3, 0, %y 116723eb0b74Storek ld [%sp + CCFSZ + 20], %g1 116823eb0b74Storek ldd [%sp + CCFSZ + 24], %g2 116923eb0b74Storek ldd [%sp + CCFSZ + 32], %g4 117023eb0b74Storek ldd [%sp + CCFSZ + 40], %g6 117123eb0b74Storek ldd [%sp + CCFSZ + 48], %i0 117223eb0b74Storek ldd [%sp + CCFSZ + 56], %i2 117323eb0b74Storek ldd [%sp + CCFSZ + 64], %i4 117423eb0b74Storek ldd [%sp + CCFSZ + 72], %i6 117523eb0b74Storek b return_from_trap 117623eb0b74Storek wr %l0, 0, %psr 117723eb0b74Storek 117823eb0b74Storek/* 117923eb0b74Storek * Do a `software' trap by re-entering the trap code, possibly first 118023eb0b74Storek * switching from interrupt stack to kernel stack. This is used for 118123eb0b74Storek * scheduling and signal ASTs (which generally occur from softclock or 118223eb0b74Storek * tty or net interrupts) and register window saves (which might occur 118323eb0b74Storek * from anywhere). 118423eb0b74Storek * 118523eb0b74Storek * The current window is the trap window, and it is by definition clean. 118623eb0b74Storek * We enter with the trap type in %o0. All we have to do is jump to 118723eb0b74Storek * Lslowtrap_reenter above, but maybe after switching stacks.... 118823eb0b74Storek */ 118923eb0b74Storeksofttrap: 119023eb0b74Storek sethi %hi(_eintstack), %l7 119123eb0b74Storek cmp %sp, %l7 119223eb0b74Storek bge Lslowtrap_reenter 119323eb0b74Storek EMPTY 119423eb0b74Storek sethi %hi(_cpcb), %l6 119523eb0b74Storek ld [%l6 + %lo(_cpcb)], %l6 119623eb0b74Storek set UPAGES*NBPG - CCFSZ - 80, %l5 119723eb0b74Storek add %l6, %l5, %l7 119823eb0b74Storek SET_SP_REDZONE(%l6, %l5) 119923eb0b74Storek b Lslowtrap_reenter 120023eb0b74Storek mov %l7, %sp 120123eb0b74Storek 120223eb0b74Storek#ifdef KGDB 120323eb0b74Storek/* 120423eb0b74Storek * bpt is entered on all breakpoint traps. 120523eb0b74Storek * If this is a kernel breakpoint, we do not want to call trap(). 120623eb0b74Storek * Among other reasons, this way we can set breakpoints in trap(). 120723eb0b74Storek */ 120823eb0b74Storekbpt: 120923eb0b74Storek btst PSR_PS, %l0 ! breakpoint from kernel? 121023eb0b74Storek bz slowtrap ! no, go do regular trap 121123eb0b74Storek nop 121223eb0b74Storek 121323eb0b74Storek /* 121423eb0b74Storek * Build a trap frame for kgdb_trap_glue to copy. 121523eb0b74Storek * Enable traps but set ipl high so that we will not 121623eb0b74Storek * see interrupts from within breakpoints. 121723eb0b74Storek */ 121823eb0b74Storek TRAP_SETUP(-CCFSZ-80) 121923eb0b74Storek or %l0, PSR_PIL, %l4 ! splhigh() 122023eb0b74Storek wr %l4, 0, %psr ! the manual claims that this 122123eb0b74Storek wr %l4, PSR_ET, %psr ! song and dance is necessary 122223eb0b74Storek std %l0, [%sp + CCFSZ + 0] ! tf.tf_psr, tf.tf_pc 122323eb0b74Storek mov %l3, %o0 ! trap type arg for kgdb_trap_glue 122423eb0b74Storek rd %y, %l3 122523eb0b74Storek std %l2, [%sp + CCFSZ + 8] ! tf.tf_npc, tf.tf_y 122623eb0b74Storek rd %wim, %l3 122723eb0b74Storek st %l3, [%sp + CCFSZ + 16] ! tf.tf_wim (a kgdb-only r/o field) 122823eb0b74Storek st %g1, [%sp + CCFSZ + 20] ! tf.tf_global[1] 122923eb0b74Storek std %g2, [%sp + CCFSZ + 24] ! etc 123023eb0b74Storek std %g4, [%sp + CCFSZ + 32] 123123eb0b74Storek std %g6, [%sp + CCFSZ + 40] 123223eb0b74Storek std %i0, [%sp + CCFSZ + 48] ! tf.tf_in[0..1] 123323eb0b74Storek std %i2, [%sp + CCFSZ + 56] ! etc 123423eb0b74Storek std %i4, [%sp + CCFSZ + 64] 123523eb0b74Storek std %i6, [%sp + CCFSZ + 72] 123623eb0b74Storek 123723eb0b74Storek /* 123823eb0b74Storek * Now call kgdb_trap_glue(); if it returns, call trap(). 123923eb0b74Storek */ 124023eb0b74Storek mov %o0, %l3 ! gotta save trap type 124123eb0b74Storek call _kgdb_trap_glue ! kgdb_trap_glue(type, &trapframe) 124223eb0b74Storek add %sp, CCFSZ, %o1 ! (&trapframe) 124323eb0b74Storek 124423eb0b74Storek /* 124523eb0b74Storek * Use slowtrap to call trap---but first erase our tracks 124623eb0b74Storek * (put the registers back the way they were). 124723eb0b74Storek */ 124823eb0b74Storek mov %l3, %o0 ! slowtrap will need trap type 124923eb0b74Storek ld [%sp + CCFSZ + 12], %l3 125023eb0b74Storek wr %l3, 0, %y 125123eb0b74Storek ld [%sp + CCFSZ + 20], %g1 125223eb0b74Storek ldd [%sp + CCFSZ + 24], %g2 125323eb0b74Storek ldd [%sp + CCFSZ + 32], %g4 125423eb0b74Storek b Lslowtrap_reenter 125523eb0b74Storek ldd [%sp + CCFSZ + 40], %g6 125623eb0b74Storek 125723eb0b74Storek/* 125823eb0b74Storek * Enter kernel breakpoint. Write all the windows (not including the 125923eb0b74Storek * current window) into the stack, so that backtrace works. Copy the 126023eb0b74Storek * supplied trap frame to the kgdb stack and switch stacks. 126123eb0b74Storek * 126223eb0b74Storek * kgdb_trap_glue(type, tf0) 126323eb0b74Storek * int type; 126423eb0b74Storek * struct trapframe *tf0; 126523eb0b74Storek */ 126623eb0b74Storek .globl _kgdb_trap_glue 126723eb0b74Storek_kgdb_trap_glue: 126823eb0b74Storek save %sp, -CCFSZ, %sp 126923eb0b74Storek 127023eb0b74Storek call _write_all_windows 127123eb0b74Storek mov %sp, %l4 ! %l4 = current %sp 127223eb0b74Storek 127323eb0b74Storek /* copy trapframe to top of kgdb stack */ 127423eb0b74Storek set _kgdb_stack + KGDB_STACK_SIZE - 80, %l0 127523eb0b74Storek ! %l0 = tfcopy -> end_of_kgdb_stack 127623eb0b74Storek mov 80, %l1 127723eb0b74Storek1: ldd [%i1], %l2 127823eb0b74Storek inc 8, %i1 127923eb0b74Storek deccc 8, %l1 128023eb0b74Storek std %l2, [%l0] 128123eb0b74Storek bg 1b 128223eb0b74Storek inc 8, %l0 128323eb0b74Storek 128423eb0b74Storek#ifdef DEBUG 128523eb0b74Storek /* save old red zone and then turn it off */ 128623eb0b74Storek sethi %hi(_redzone), %l7 128723eb0b74Storek ld [%l7 + %lo(_redzone)], %l6 128823eb0b74Storek st %g0, [%l7 + %lo(_redzone)] 128923eb0b74Storek#endif 129023eb0b74Storek /* switch to kgdb stack */ 129123eb0b74Storek add %l0, -CCFSZ-80, %sp 129223eb0b74Storek 129323eb0b74Storek /* if (kgdb_trap(type, tfcopy)) kgdb_rett(tfcopy); */ 129423eb0b74Storek mov %i0, %o0 129523eb0b74Storek call _kgdb_trap 129623eb0b74Storek add %l0, -80, %o1 129723eb0b74Storek tst %o0 129823eb0b74Storek bnz,a kgdb_rett 129923eb0b74Storek add %l0, -80, %g1 130023eb0b74Storek 130123eb0b74Storek /* 130223eb0b74Storek * kgdb_trap() did not handle the trap at all so the stack is 130323eb0b74Storek * still intact. A simple `restore' will put everything back, 130423eb0b74Storek * after we reset the stack pointer. 130523eb0b74Storek */ 130623eb0b74Storek mov %l4, %sp 130723eb0b74Storek#ifdef DEBUG 130823eb0b74Storek st %l6, [%l7 + %lo(_redzone)] ! restore red zone 130923eb0b74Storek#endif 131023eb0b74Storek ret 131123eb0b74Storek restore 131223eb0b74Storek 131323eb0b74Storek/* 131423eb0b74Storek * Return from kgdb trap. This is sort of special. 131523eb0b74Storek * 131623eb0b74Storek * We know that kgdb_trap_glue wrote the window above it, so that we will 131723eb0b74Storek * be able to (and are sure to have to) load it up. We also know that we 131823eb0b74Storek * came from kernel land and can assume that the %fp (%i6) we load here 131923eb0b74Storek * is proper. We must also be sure not to lower ipl (it is at splhigh()) 132023eb0b74Storek * until we have traps disabled, due to the SPARC taking traps at the 132123eb0b74Storek * new ipl before noticing that PSR_ET has been turned off. We are on 132223eb0b74Storek * the kgdb stack, so this could be disastrous. 132323eb0b74Storek * 132423eb0b74Storek * Note that the trapframe argument in %g1 points into the current stack 132523eb0b74Storek * frame (current window). We abandon this window when we move %g1->tf_psr 132623eb0b74Storek * into %psr, but we will not have loaded the new %sp yet, so again traps 132723eb0b74Storek * must be disabled. 132823eb0b74Storek */ 132923eb0b74Storekkgdb_rett: 133023eb0b74Storek rd %psr, %g4 ! turn off traps 133123eb0b74Storek wr %g4, PSR_ET, %psr 133223eb0b74Storek /* use the three-instruction delay to do something useful */ 133323eb0b74Storek ld [%g1], %g2 ! pick up new %psr 133423eb0b74Storek ld [%g1 + 12], %g3 ! set %y 133523eb0b74Storek wr %g3, 0, %y 133623eb0b74Storek#ifdef DEBUG 133723eb0b74Storek st %l6, [%l7 + %lo(_redzone)] ! and restore red zone 133823eb0b74Storek#endif 133923eb0b74Storek wr %g0, 0, %wim ! enable window changes 134023eb0b74Storek nop; nop; nop 134123eb0b74Storek /* now safe to set the new psr (changes CWP, leaves traps disabled) */ 134223eb0b74Storek wr %g2, 0, %psr ! set rett psr (including cond codes) 134323eb0b74Storek /* 3 instruction delay before we can use the new window */ 134423eb0b74Storek/*1*/ ldd [%g1 + 24], %g2 ! set new %g2, %g3 134523eb0b74Storek/*2*/ ldd [%g1 + 32], %g4 ! set new %g4, %g5 134623eb0b74Storek/*3*/ ldd [%g1 + 40], %g6 ! set new %g6, %g7 134723eb0b74Storek 134823eb0b74Storek /* now we can use the new window */ 134923eb0b74Storek mov %g1, %l4 135023eb0b74Storek ld [%l4 + 4], %l1 ! get new pc 135123eb0b74Storek ld [%l4 + 8], %l2 ! get new npc 135223eb0b74Storek ld [%l4 + 20], %g1 ! set new %g1 135323eb0b74Storek 135423eb0b74Storek /* set up returnee's out registers, including its %sp */ 135523eb0b74Storek ldd [%l4 + 48], %i0 135623eb0b74Storek ldd [%l4 + 56], %i2 135723eb0b74Storek ldd [%l4 + 64], %i4 135823eb0b74Storek ldd [%l4 + 72], %i6 135923eb0b74Storek 136023eb0b74Storek /* load returnee's window, making the window above it be invalid */ 136123eb0b74Storek restore 136223eb0b74Storek restore %g0, 1, %l1 ! move to inval window and set %l1 = 1 136323eb0b74Storek rd %psr, %l0 136423eb0b74Storek sll %l1, %l0, %l1 136523eb0b74Storek wr %l1, 0, %wim ! %wim = 1 << (%psr & 31) 136623eb0b74Storek sethi %hi(_cpcb), %l1 136723eb0b74Storek ld [%l1 + %lo(_cpcb)], %l1 136823eb0b74Storek and %l0, 31, %l0 ! CWP = %psr & 31; 136923eb0b74Storek st %l0, [%l1 + PCB_WIM] ! cpcb->pcb_wim = CWP; 137023eb0b74Storek save %g0, %g0, %g0 ! back to window to reload 137123eb0b74Storek LOADWIN(%sp) 137223eb0b74Storek save %g0, %g0, %g0 ! back to trap window 137323eb0b74Storek /* note, we have not altered condition codes; safe to just rett */ 137423eb0b74Storek RETT 137523eb0b74Storek#endif 137623eb0b74Storek 137723eb0b74Storek/* 137823eb0b74Storek * syscall() builds a trap frame and calls syscall(). 137923eb0b74Storek * sun_syscall is same but delivers sun system call number 138023eb0b74Storek * XXX should not have to save&reload ALL the registers just for 138123eb0b74Storek * ptrace... 138223eb0b74Storek */ 138323eb0b74Storek#ifdef COMPAT_SUNOS 138423eb0b74Storeksun_syscall: 138523eb0b74Storek TRAP_SETUP(-CCFSZ-80) 138623eb0b74Storek b sys_merge 138723eb0b74Storek mov 1, %o3 ! third arg to syscall: sun compat 138823eb0b74Storeksyscall: 138923eb0b74Storek TRAP_SETUP(-CCFSZ-80) 139023eb0b74Storek clr %o3 ! third arg to syscall: native bsd 139123eb0b74Storeksys_merge: 139223eb0b74Storek#else 139323eb0b74Storeksyscall: 139423eb0b74Storek TRAP_SETUP(-CCFSZ-80) 139523eb0b74Storek#endif 139623eb0b74Storek wr %l0, PSR_ET, %psr 139723eb0b74Storek std %l0, [%sp + CCFSZ + 0] ! tf_psr, tf_pc 139823eb0b74Storek rd %y, %l3 139923eb0b74Storek std %l2, [%sp + CCFSZ + 8] ! tf_npc, tf_y 140023eb0b74Storek st %g1, [%sp + CCFSZ + 20] ! tf_g[1] 140123eb0b74Storek std %g2, [%sp + CCFSZ + 24] ! tf_g[2], tf_g[3] 140223eb0b74Storek std %g4, [%sp + CCFSZ + 32] ! etc 140323eb0b74Storek std %g6, [%sp + CCFSZ + 40] 140423eb0b74Storek mov %g1, %o0 ! (code) 140523eb0b74Storek std %i0, [%sp + CCFSZ + 48] 140623eb0b74Storek add %sp, CCFSZ, %o1 ! (&tf) 140723eb0b74Storek std %i2, [%sp + CCFSZ + 56] 140823eb0b74Storek mov %l1, %o2 ! (pc) 140923eb0b74Storek std %i4, [%sp + CCFSZ + 64] 141023eb0b74Storek call _syscall ! syscall(code, &tf, pc, suncompat) 141123eb0b74Storek std %i6, [%sp + CCFSZ + 72] 141223eb0b74Storek ! now load em all up again, sigh 141323eb0b74Storek ldd [%sp + CCFSZ + 0], %l0 ! new %psr, new pc 141423eb0b74Storek ldd [%sp + CCFSZ + 8], %l2 ! new npc, new %y 141523eb0b74Storek wr %l3, 0, %y 1416*c073cf18Smckusick /* see `dostart' for the reason for this label */ 1417*c073cf18Smckusickinit_syscall_ret: 141823eb0b74Storek ld [%sp + CCFSZ + 20], %g1 141923eb0b74Storek ldd [%sp + CCFSZ + 24], %g2 142023eb0b74Storek ldd [%sp + CCFSZ + 32], %g4 142123eb0b74Storek ldd [%sp + CCFSZ + 40], %g6 142223eb0b74Storek ldd [%sp + CCFSZ + 48], %i0 142323eb0b74Storek ldd [%sp + CCFSZ + 56], %i2 142423eb0b74Storek ldd [%sp + CCFSZ + 64], %i4 142523eb0b74Storek ldd [%sp + CCFSZ + 72], %i6 142623eb0b74Storek b return_from_trap 142723eb0b74Storek wr %l0, 0, %psr 142823eb0b74Storek 142923eb0b74Storek/* 143023eb0b74Storek * Interrupts. Software interrupts must be cleared from the software 143123eb0b74Storek * interrupt enable register. Rather than calling ienab_bic for each, 143223eb0b74Storek * we do them in-line before enabling traps. 143323eb0b74Storek * 143423eb0b74Storek * After preliminary setup work, the interrupt is passed to each 143523eb0b74Storek * registered handler in turn. These are expected to return nonzero if 143623eb0b74Storek * they took care of the interrupt. If a handler claims the interrupt, 143723eb0b74Storek * we exit (hardware interrupts are latched in the requestor so we'll 143823eb0b74Storek * just take another interrupt in the unlikely event of simultaneous 143923eb0b74Storek * interrupts from two different devices at the same level). If we go 144023eb0b74Storek * through all the registered handlers and no one claims it, we report a 144123eb0b74Storek * stray interrupt. This is more or less done as: 144223eb0b74Storek * 144323eb0b74Storek * for (ih = intrhand[intlev]; ih; ih = ih->ih_next) 144423eb0b74Storek * if ((*ih->ih_fun)(ih->ih_arg ? ih->ih_arg : &frame)) 144523eb0b74Storek * return; 144623eb0b74Storek * strayintr(&frame); 144723eb0b74Storek * 144823eb0b74Storek * Software interrupts are almost the same with three exceptions: 144923eb0b74Storek * (1) we clear the interrupt from the software interrupt enable 145023eb0b74Storek * register before calling any handler (we have to clear it first 145123eb0b74Storek * to avoid an interrupt-losing race), 145223eb0b74Storek * (2) we always call all the registered handlers (there is no way 145323eb0b74Storek * to tell if the single bit in the software interrupt register 145423eb0b74Storek * represents one or many requests) 145523eb0b74Storek * (3) we never announce a stray interrupt (because of (1), another 145623eb0b74Storek * interrupt request can come in while we're in the handler. If 145723eb0b74Storek * the handler deal with everything for both the original & the 145823eb0b74Storek * new request, we'll erroneously report a stray interrupt when 145923eb0b74Storek * we take the software interrupt for the new request. 146023eb0b74Storek * 146123eb0b74Storek * Inputs: 146223eb0b74Storek * %l0 = %psr 146323eb0b74Storek * %l1 = return pc 146423eb0b74Storek * %l2 = return npc 146523eb0b74Storek * %l3 = interrupt level 146623eb0b74Storek * (software interrupt only) %l4 = bits to clear in interrupt register 146723eb0b74Storek * 146823eb0b74Storek * Internal: 146923eb0b74Storek * %l4, %l5: local variables 147023eb0b74Storek * %l6 = %y 147123eb0b74Storek * %l7 = %g1 147223eb0b74Storek * %g2..%g7 go to stack 147323eb0b74Storek * 147423eb0b74Storek * An interrupt frame is built in the space for a full trapframe; 147523eb0b74Storek * this contains the psr, pc, npc, and interrupt level. 147623eb0b74Storek */ 147723eb0b74Storek .comm _intrhand, 15 * 8 ! intrhand[0..14]; 0 => error 147823eb0b74Storeksoftintr: 147923eb0b74Storek sethi %hi(IE_reg_addr), %l6 148023eb0b74Storek ldub [%l6 + %lo(IE_reg_addr)], %l5 148123eb0b74Storek andn %l5, %l4, %l5 148223eb0b74Storek stb %l5, [%l6 + %lo(IE_reg_addr)] 148323eb0b74Storek INTR_SETUP(-CCFSZ-80) 148423eb0b74Storek std %g2, [%sp + CCFSZ + 24] ! save registers 148523eb0b74Storek INCR(_cnt+V_INTR) ! cnt.v_intr++; (clobbers %o0,%o1) 148623eb0b74Storek mov %g1, %l7 148723eb0b74Storek rd %y, %l6 148823eb0b74Storek std %g4, [%sp + CCFSZ + 32] 148923eb0b74Storek andn %l0, PSR_PIL, %l4 ! %l4 = psr & ~PSR_PIL | 149023eb0b74Storek sll %l3, 8, %l5 ! intlev << IPLSHIFT 149123eb0b74Storek std %g6, [%sp + CCFSZ + 40] 149223eb0b74Storek or %l5, %l4, %l4 ! ; 149323eb0b74Storek wr %l4, 0, %psr ! the manual claims this 149423eb0b74Storek wr %l4, PSR_ET, %psr ! song and dance is necessary 149523eb0b74Storek std %l0, [%sp + CCFSZ + 0] ! set up intrframe/clockframe 149623eb0b74Storek sll %l3, 2, %l5 149723eb0b74Storek set _intrcnt, %l4 ! intrcnt[intlev]++; 149823eb0b74Storek ld [%l4 + %l5], %o0 149923eb0b74Storek std %l2, [%sp + CCFSZ + 8] 150023eb0b74Storek inc %o0 150123eb0b74Storek st %o0, [%l4 + %l5] 150223eb0b74Storek set _intrhand, %l4 ! %l4 = intrhand[intlev]; 150323eb0b74Storek ld [%l4 + %l5], %l4 150423eb0b74Storek b 3f 150523eb0b74Storek st %fp, [%sp + CCFSZ + 16] 150623eb0b74Storek 150723eb0b74Storek1: ld [%l4], %o1 150823eb0b74Storek ld [%l4 + 4], %o0 150923eb0b74Storek tst %o0 151023eb0b74Storek bz,a 2f 151123eb0b74Storek add %sp, CCFSZ, %o0 151223eb0b74Storek2: jmpl %o1, %o7 ! (void)(*ih->ih_fun)(...) 151323eb0b74Storek ld [%l4 + 8], %l4 ! and ih = ih->ih_next 151423eb0b74Storek3: tst %l4 ! while ih != NULL 151523eb0b74Storek bnz 1b 151623eb0b74Storek nop 151723eb0b74Storek mov %l7, %g1 151823eb0b74Storek wr %l6, 0, %y 151923eb0b74Storek ldd [%sp + CCFSZ + 24], %g2 152023eb0b74Storek ldd [%sp + CCFSZ + 32], %g4 152123eb0b74Storek ldd [%sp + CCFSZ + 40], %g6 152223eb0b74Storek b return_from_trap 152323eb0b74Storek wr %l0, 0, %psr 152423eb0b74Storek 152523eb0b74Storek /* 152623eb0b74Storek * _sparc_interrupt is exported for paranoia checking (see intr.c). 152723eb0b74Storek */ 152823eb0b74Storek .globl _sparc_interrupt 152923eb0b74Storek_sparc_interrupt: 153023eb0b74Storek INTR_SETUP(-CCFSZ-80) 153123eb0b74Storek std %g2, [%sp + CCFSZ + 24] ! save registers 153223eb0b74Storek INCR(_cnt+V_INTR) ! cnt.v_intr++; (clobbers %o0,%o1) 153323eb0b74Storek mov %g1, %l7 153423eb0b74Storek rd %y, %l6 153523eb0b74Storek std %g4, [%sp + CCFSZ + 32] 153623eb0b74Storek andn %l0, PSR_PIL, %l4 ! %l4 = psr & ~PSR_PIL | 153723eb0b74Storek sll %l3, 8, %l5 ! intlev << IPLSHIFT 153823eb0b74Storek std %g6, [%sp + CCFSZ + 40] 153923eb0b74Storek or %l5, %l4, %l4 ! ; 154023eb0b74Storek wr %l4, 0, %psr ! the manual claims this 154123eb0b74Storek wr %l4, PSR_ET, %psr ! song and dance is necessary 154223eb0b74Storek std %l0, [%sp + CCFSZ + 0] ! set up intrframe/clockframe 154323eb0b74Storek sll %l3, 2, %l5 154423eb0b74Storek set _intrcnt, %l4 ! intrcnt[intlev]++; 154523eb0b74Storek ld [%l4 + %l5], %o0 154623eb0b74Storek std %l2, [%sp + CCFSZ + 8] ! set up intrframe/clockframe 154723eb0b74Storek inc %o0 154823eb0b74Storek st %o0, [%l4 + %l5] 154923eb0b74Storek set _intrhand, %l4 ! %l4 = intrhand[intlev]; 155023eb0b74Storek ld [%l4 + %l5], %l4 155123eb0b74Storek b 3f 155223eb0b74Storek st %fp, [%sp + CCFSZ + 16] 155323eb0b74Storek 155423eb0b74Storek1: ld [%l4], %o1 155523eb0b74Storek ld [%l4 + 4], %o0 155623eb0b74Storek tst %o0 155723eb0b74Storek bz,a 2f 155823eb0b74Storek add %sp, CCFSZ, %o0 155923eb0b74Storek2: jmpl %o1, %o7 ! handled = (*ih->ih_fun)(...) 156023eb0b74Storek ld [%l4 + 8], %l4 ! and ih = ih->ih_next 156123eb0b74Storek tst %o0 156223eb0b74Storek bnz 4f ! if (handled) break 156323eb0b74Storek nop 156423eb0b74Storek3: tst %l4 156523eb0b74Storek bnz 1b ! while (ih) 156623eb0b74Storek nop 156723eb0b74Storek call _strayintr ! strayintr(&intrframe) 156823eb0b74Storek add %sp, CCFSZ, %o0 156923eb0b74Storek /* all done: restore registers and go return */ 157023eb0b74Storek4: mov %l7, %g1 157123eb0b74Storek wr %l6, 0, %y 157223eb0b74Storek ldd [%sp + CCFSZ + 24], %g2 157323eb0b74Storek ldd [%sp + CCFSZ + 32], %g4 157423eb0b74Storek ldd [%sp + CCFSZ + 40], %g6 157523eb0b74Storek b return_from_trap 157623eb0b74Storek wr %l0, 0, %psr 157723eb0b74Storek 157823eb0b74Storek#ifdef notyet 157923eb0b74Storek/* 158023eb0b74Storek * Level 12 (ZS serial) interrupt. Handle it quickly, schedule a 158123eb0b74Storek * software interrupt, and get out. Do the software interrupt directly 158223eb0b74Storek * if we would just take it on the way out. 158323eb0b74Storek * 158423eb0b74Storek * Input: 158523eb0b74Storek * %l0 = %psr 158623eb0b74Storek * %l1 = return pc 158723eb0b74Storek * %l2 = return npc 158823eb0b74Storek * Internal: 158923eb0b74Storek * %l3 = zs device 159023eb0b74Storek * %l4, %l5 = temporary 159123eb0b74Storek * %l6 = rr3 (or temporary data) + 0x100 => need soft int 159223eb0b74Storek * %l7 = zs soft status 159323eb0b74Storek */ 159423eb0b74Storekzshard: 159523eb0b74Storek#endif /* notyet */ 159623eb0b74Storek 159723eb0b74Storek/* 159823eb0b74Storek * Level 15 interrupt. An async memory error has occurred; 159923eb0b74Storek * take care of it (typically by panicking, but hey...). 160023eb0b74Storek * %l0 = %psr 160123eb0b74Storek * %l1 = return pc 160223eb0b74Storek * %l2 = return npc 160323eb0b74Storek * %l3 = 15 * 4 (why? just because!) 160423eb0b74Storek * 160523eb0b74Storek * Internal: 160623eb0b74Storek * %l4 = %y 160723eb0b74Storek * %l5 = %g1 160823eb0b74Storek * %l6 = %g6 160923eb0b74Storek * %l7 = %g7 161023eb0b74Storek * g2, g3, g4, g5 go to stack 161123eb0b74Storek * 161223eb0b74Storek * This code is almost the same as that in mem_access_fault, 161323eb0b74Storek * except that we already know the problem is not a `normal' fault, 161423eb0b74Storek * and that we must be extra-careful with interrupt enables. 161523eb0b74Storek */ 161623eb0b74Storeknmi: 161723eb0b74Storek INTR_SETUP(-CCFSZ-80) 161823eb0b74Storek INCR(_cnt+V_INTR) ! cnt.v_intr++; (clobbers %o0,%o1) 161923eb0b74Storek /* 162023eb0b74Storek * Level 15 interrupts are nonmaskable, so with traps off, 162123eb0b74Storek * disable all interrupts to prevent recursion. 162223eb0b74Storek */ 162323eb0b74Storek sethi %hi(IE_reg_addr), %o0 162423eb0b74Storek ldub [%o0 + %lo(IE_reg_addr)], %o1 162523eb0b74Storek andn %o1, IE_ALLIE, %o1 162623eb0b74Storek stb %o1, [%o0 + %lo(IE_reg_addr)] 162723eb0b74Storek wr %l0, PSR_ET, %psr ! okay, turn traps on again 162823eb0b74Storek 162923eb0b74Storek std %g2, [%sp + CCFSZ + 0] ! save g2, g3 163023eb0b74Storek rd %y, %l4 ! save y 163123eb0b74Storek 163223eb0b74Storek ! must read the sync error register too. 163323eb0b74Storek set AC_SYNC_ERR, %o0 163423eb0b74Storek lda [%o0] ASI_CONTROL, %o1 ! sync err reg 163523eb0b74Storek inc 4, %o0 163623eb0b74Storek lda [%o0] ASI_CONTROL, %o2 ! sync virt addr 163723eb0b74Storek std %g4, [%sp + CCFSZ + 8] ! save g4,g5 163823eb0b74Storek mov %g1, %l5 ! save g1,g6,g7 163923eb0b74Storek mov %g6, %l6 164023eb0b74Storek mov %g7, %l7 164123eb0b74Storek inc 4, %o0 164223eb0b74Storek lda [%o0] ASI_CONTROL, %o3 ! async err reg 164323eb0b74Storek inc 4, %o0 164423eb0b74Storek lda [%o0] ASI_CONTROL, %o4 ! async virt addr 164523eb0b74Storek 164623eb0b74Storek ! and call C code 164723eb0b74Storek call _memerr ! memerr(0, ser, sva, aer, ava) 164823eb0b74Storek clr %o0 164923eb0b74Storek 165023eb0b74Storek mov %l5, %g1 ! restore g1 through g7 165123eb0b74Storek ldd [%sp + CCFSZ + 0], %g2 165223eb0b74Storek ldd [%sp + CCFSZ + 8], %g4 165323eb0b74Storek wr %l0, 0, %psr ! re-disable traps 165423eb0b74Storek mov %l6, %g6 165523eb0b74Storek mov %l7, %g7 165623eb0b74Storek 165723eb0b74Storek ! set IE_ALLIE again (safe, we disabled traps again above) 165823eb0b74Storek sethi %hi(IE_reg_addr), %o0 165923eb0b74Storek ldub [%o0 + %lo(IE_reg_addr)], %o1 166023eb0b74Storek or %o1, IE_ALLIE, %o1 166123eb0b74Storek stb %o1, [%o0 + %lo(IE_reg_addr)] 166223eb0b74Storek b return_from_trap 166323eb0b74Storek wr %l4, 0, %y ! restore y 166423eb0b74Storek 166523eb0b74Storek 166623eb0b74Storek/* 166723eb0b74Storek * Window overflow trap handler. 166823eb0b74Storek * %l0 = %psr 166923eb0b74Storek * %l1 = return pc 167023eb0b74Storek * %l2 = return npc 167123eb0b74Storek */ 167223eb0b74Storekwindow_of: 167323eb0b74Storek#ifdef TRIVIAL_WINDOW_OVERFLOW_HANDLER 167423eb0b74Storek /* a trivial version that assumes %sp is ok */ 167523eb0b74Storek /* (for testing only!) */ 167623eb0b74Storek save %g0, %g0, %g0 167723eb0b74Storek std %l0, [%sp + (0*8)] 167823eb0b74Storek rd %psr, %l0 167923eb0b74Storek mov 1, %l1 168023eb0b74Storek sll %l1, %l0, %l0 168123eb0b74Storek wr %l0, 0, %wim 168223eb0b74Storek std %l2, [%sp + (1*8)] 168323eb0b74Storek std %l4, [%sp + (2*8)] 168423eb0b74Storek std %l6, [%sp + (3*8)] 168523eb0b74Storek std %i0, [%sp + (4*8)] 168623eb0b74Storek std %i2, [%sp + (5*8)] 168723eb0b74Storek std %i4, [%sp + (6*8)] 168823eb0b74Storek std %i6, [%sp + (7*8)] 168923eb0b74Storek restore 169023eb0b74Storek RETT 169123eb0b74Storek#else 169223eb0b74Storek /* 169323eb0b74Storek * This is similar to TRAP_SETUP, but we do not want to spend 169423eb0b74Storek * a lot of time, so we have separate paths for kernel and user. 169523eb0b74Storek * We also know for sure that the window has overflowed. 169623eb0b74Storek */ 169723eb0b74Storek btst PSR_PS, %l0 169823eb0b74Storek bz winof_user 169923eb0b74Storek sethi %hi(clean_trap_window), %l7 170023eb0b74Storek 170123eb0b74Storek /* 170223eb0b74Storek * Overflow from kernel mode. Call clean_trap_window to 170323eb0b74Storek * do the dirty work, then just return, since we know prev 170423eb0b74Storek * window is valid. clean_trap_windows might dump all *user* 170523eb0b74Storek * windows into the pcb, but we do not care: there is at 170623eb0b74Storek * least one kernel window (a trap or interrupt frame!) 170723eb0b74Storek * above us. 170823eb0b74Storek */ 170923eb0b74Storek jmpl %l7 + %lo(clean_trap_window), %l4 171023eb0b74Storek mov %g7, %l7 ! for clean_trap_window 171123eb0b74Storek 171223eb0b74Storek wr %l0, 0, %psr ! put back the @%*! cond. codes 171323eb0b74Storek nop ! (let them settle in) 171423eb0b74Storek RETT 171523eb0b74Storek 171623eb0b74Storekwinof_user: 171723eb0b74Storek /* 171823eb0b74Storek * Overflow from user mode. 171923eb0b74Storek * If clean_trap_window dumps the registers into the pcb, 172023eb0b74Storek * rft_user will need to call trap(), so we need space for 172123eb0b74Storek * a trap frame. We also have to compute pcb_nw. 172223eb0b74Storek * 172323eb0b74Storek * SHOULD EXPAND IN LINE TO AVOID BUILDING TRAP FRAME ON 172423eb0b74Storek * `EASY' SAVES 172523eb0b74Storek */ 172623eb0b74Storek sethi %hi(_cpcb), %l6 172723eb0b74Storek ld [%l6 + %lo(_cpcb)], %l6 172823eb0b74Storek ld [%l6 + PCB_WIM], %l5 172923eb0b74Storek and %l0, 31, %l3 173023eb0b74Storek sub %l3, %l5, %l5 /* l5 = CWP - pcb_wim */ 173123eb0b74Storek set uwtab, %l4 173223eb0b74Storek ldub [%l4 + %l5], %l5 /* l5 = uwtab[l5] */ 173323eb0b74Storek st %l5, [%l6 + PCB_UW] 173423eb0b74Storek jmpl %l7 + %lo(clean_trap_window), %l4 173523eb0b74Storek mov %g7, %l7 ! for clean_trap_window 173623eb0b74Storek sethi %hi(_cpcb), %l6 173723eb0b74Storek ld [%l6 + %lo(_cpcb)], %l6 173823eb0b74Storek set UPAGES*NBPG-CCFSZ-80, %l5 173923eb0b74Storek add %l6, %l5, %sp /* over to kernel stack */ 174023eb0b74Storek CHECK_SP_REDZONE(%l6, %l5) 174123eb0b74Storek 174223eb0b74Storek /* 174323eb0b74Storek * Copy return_from_trap far enough to allow us 174423eb0b74Storek * to jump directly to rft_user_or_recover_pcb_windows 174523eb0b74Storek * (since we know that is where we are headed). 174623eb0b74Storek */ 174723eb0b74Storek! and %l0, 31, %l3 ! still set (clean_trap_window 174823eb0b74Storek ! leaves this register alone) 174923eb0b74Storek set wmask, %l6 175023eb0b74Storek ldub [%l6 + %l3], %l5 ! %l5 = 1 << ((CWP + 1) % nwindows) 175123eb0b74Storek b rft_user_or_recover_pcb_windows 175223eb0b74Storek rd %wim, %l4 ! (read %wim first) 175323eb0b74Storek#endif /* end `real' version of window overflow trap handler */ 175423eb0b74Storek 175523eb0b74Storek/* 175623eb0b74Storek * Window underflow trap handler. 175723eb0b74Storek * %l0 = %psr 175823eb0b74Storek * %l1 = return pc 175923eb0b74Storek * %l2 = return npc 176023eb0b74Storek * 176123eb0b74Storek * A picture: 176223eb0b74Storek * 176323eb0b74Storek * T R I X 176423eb0b74Storek * 0 0 0 1 0 0 0 (%wim) 176523eb0b74Storek * [bit numbers increase towards the right; 176623eb0b74Storek * `restore' moves right & `save' moves left] 176723eb0b74Storek * 176823eb0b74Storek * T is the current (Trap) window, R is the window that attempted 176923eb0b74Storek * a `Restore' instruction, I is the Invalid window, and X is the 177023eb0b74Storek * window we want to make invalid before we return. 177123eb0b74Storek * 177223eb0b74Storek * Since window R is valid, we cannot use rft_user to restore stuff 177323eb0b74Storek * for us. We have to duplicate its logic. YUCK. 177423eb0b74Storek * 177523eb0b74Storek * Incidentally, TRIX are for kids. Silly rabbit! 177623eb0b74Storek */ 177723eb0b74Storekwindow_uf: 177823eb0b74Storek#ifdef TRIVIAL_WINDOW_UNDERFLOW_HANDLER 177923eb0b74Storek wr %g0, 0, %wim ! allow us to enter I 178023eb0b74Storek restore ! to R 178123eb0b74Storek nop 178223eb0b74Storek nop 178323eb0b74Storek restore ! to I 178423eb0b74Storek restore %g0, 1, %l1 ! to X 178523eb0b74Storek rd %psr, %l0 178623eb0b74Storek sll %l1, %l0, %l0 178723eb0b74Storek wr %l0, 0, %wim 178823eb0b74Storek save %g0, %g0, %g0 ! back to I 178923eb0b74Storek LOADWIN(%sp) 179023eb0b74Storek save %g0, %g0, %g0 ! back to R 179123eb0b74Storek save %g0, %g0, %g0 ! back to T 179223eb0b74Storek RETT 179323eb0b74Storek#else 179423eb0b74Storek wr %g0, 0, %wim ! allow us to enter I 179523eb0b74Storek btst PSR_PS, %l0 179623eb0b74Storek restore ! enter window R 179723eb0b74Storek bz winuf_user 179823eb0b74Storek restore ! enter window I 179923eb0b74Storek 180023eb0b74Storek /* 180123eb0b74Storek * Underflow from kernel mode. Just recover the 180223eb0b74Storek * registers and go (except that we have to update 180323eb0b74Storek * the blasted user pcb fields). 180423eb0b74Storek */ 180523eb0b74Storek restore %g0, 1, %l1 ! enter window X, then set %l1 to 1 180623eb0b74Storek rd %psr, %l0 ! cwp = %psr & 31; 180723eb0b74Storek and %l0, 31, %l0 180823eb0b74Storek sll %l1, %l0, %l1 ! wim = 1 << cwp; 180923eb0b74Storek wr %l1, 0, %wim ! setwim(wim); 181023eb0b74Storek sethi %hi(_cpcb), %l1 181123eb0b74Storek ld [%l1 + %lo(_cpcb)], %l1 181223eb0b74Storek st %l0, [%l1 + PCB_WIM] ! cpcb->pcb_wim = cwp; 181323eb0b74Storek save %g0, %g0, %g0 ! back to window I 181423eb0b74Storek LOADWIN(%sp) 181523eb0b74Storek save %g0, %g0, %g0 ! back to R 181623eb0b74Storek save %g0, %g0, %g0 ! and then to T 181723eb0b74Storek wr %l0, 0, %psr ! fix those cond codes.... 181823eb0b74Storek nop ! (let them settle in) 181923eb0b74Storek RETT 182023eb0b74Storek 182123eb0b74Storekwinuf_user: 182223eb0b74Storek /* 182323eb0b74Storek * Underflow from user mode. 182423eb0b74Storek * 182523eb0b74Storek * We cannot use rft_user (as noted above) because 182623eb0b74Storek * we must re-execute the `restore' instruction. 182723eb0b74Storek * Since it could be, e.g., `restore %l0,0,%l0', 182823eb0b74Storek * it is not okay to touch R's registers either. 182923eb0b74Storek * 183023eb0b74Storek * We are now in window I. 183123eb0b74Storek */ 183223eb0b74Storek btst 7, %sp ! if unaligned, it is invalid 183323eb0b74Storek bne winuf_invalid 183423eb0b74Storek EMPTY 183523eb0b74Storek 183623eb0b74Storek PTE_OF_ADDR(%sp, %l7, winuf_invalid) 183723eb0b74Storek CMP_PTE_USER_READ(%l7) ! if first page not readable, 183823eb0b74Storek bne winuf_invalid ! it is invalid 183923eb0b74Storek EMPTY 184023eb0b74Storek SLT_IF_1PAGE_RW(%sp, %l7) ! first page is readable 184123eb0b74Storek bl,a winuf_ok ! if only one page, enter window X 184223eb0b74Storek restore %g0, 1, %l1 ! and goto ok, & set %l1 to 1 184323eb0b74Storek add %sp, 7*8, %l5 184423eb0b74Storek PTE_OF_ADDR(%l5, %l7, winuf_invalid) 184523eb0b74Storek CMP_PTE_USER_READ(%l7) ! check second page too 184623eb0b74Storek be,a winuf_ok ! enter window X and goto ok 184723eb0b74Storek restore %g0, 1, %l1 ! (and then set %l1 to 1) 184823eb0b74Storek 184923eb0b74Storekwinuf_invalid: 185023eb0b74Storek /* 185123eb0b74Storek * We were unable to restore the window because %sp 185223eb0b74Storek * is invalid or paged out. Return to the trap window 185323eb0b74Storek * and call trap(T_WINUF). This will save R to the user 185423eb0b74Storek * stack, then load both R and I into the pcb rw[] area, 185523eb0b74Storek * and return with pcb_nsaved set to -1 for success, 0 for 185623eb0b74Storek * failure. `Failure' indicates that someone goofed with the 185723eb0b74Storek * trap registers (e.g., signals), so that we need to return 185823eb0b74Storek * from the trap as from a syscall (probably to a signal handler) 185923eb0b74Storek * and let it retry the restore instruction later. Note that 186023eb0b74Storek * window R will have been pushed out to user space, and thus 186123eb0b74Storek * be the invalid window, by the time we get back here. (We 186223eb0b74Storek * continue to label it R anyway.) We must also set %wim again, 186323eb0b74Storek * and set pcb_uw to 1, before enabling traps. (Window R is the 186423eb0b74Storek * only window, and it is a user window). 186523eb0b74Storek */ 186623eb0b74Storek save %g0, %g0, %g0 ! back to R 186723eb0b74Storek#if 0 /* this gives `as' mild heartburn */ 186823eb0b74Storek save %g0, 1, %l4 ! back to T, then %l4 = 1 186923eb0b74Storek#else 187023eb0b74Storek save %g0, %g0, %g0 ! back to T 187123eb0b74Storek mov 1, %l4 ! and set %l4 = 1 187223eb0b74Storek#endif 187323eb0b74Storek sethi %hi(_cpcb), %l6 187423eb0b74Storek ld [%l6 + %lo(_cpcb)], %l6 187523eb0b74Storek st %l4, [%l6 + PCB_UW] ! pcb_uw = 1 187623eb0b74Storek ld [%l6 + PCB_WIM], %l5 ! get log2(%wim) 187723eb0b74Storek sll %l4, %l5, %l4 ! %l4 = old %wim 187823eb0b74Storek wr %l4, 0, %wim ! window I is now invalid again 187923eb0b74Storek set UPAGES*NBPG-CCFSZ-80, %l5 188023eb0b74Storek add %l6, %l5, %sp ! get onto kernel stack 188123eb0b74Storek CHECK_SP_REDZONE(%l6, %l5) 188223eb0b74Storek 188323eb0b74Storek /* 188423eb0b74Storek * Okay, call trap(T_WINUF, psr, pc, &tf). 188523eb0b74Storek * See `slowtrap' above for operation. 188623eb0b74Storek */ 188723eb0b74Storek wr %l0, PSR_ET, %psr 188823eb0b74Storek std %l0, [%sp + CCFSZ + 0] ! tf.tf_psr, tf.tf_pc 188923eb0b74Storek rd %y, %l3 189023eb0b74Storek std %l2, [%sp + CCFSZ + 8] ! tf.tf_npc, tf.tf_y 189123eb0b74Storek mov T_WINUF, %o0 189223eb0b74Storek st %g1, [%sp + CCFSZ + 20] ! tf.tf_global[1] 189323eb0b74Storek mov %l0, %o1 189423eb0b74Storek std %g2, [%sp + CCFSZ + 24] ! etc 189523eb0b74Storek mov %l1, %o2 189623eb0b74Storek std %g4, [%sp + CCFSZ + 32] 189723eb0b74Storek add %sp, CCFSZ, %o3 189823eb0b74Storek std %g6, [%sp + CCFSZ + 40] 189923eb0b74Storek std %i0, [%sp + CCFSZ + 48] ! tf.tf_out[0], etc 190023eb0b74Storek std %i2, [%sp + CCFSZ + 56] 190123eb0b74Storek std %i4, [%sp + CCFSZ + 64] 190223eb0b74Storek call _trap ! trap(T_WINUF, pc, psr, &tf) 190323eb0b74Storek std %i6, [%sp + CCFSZ + 72] ! tf.tf_out[6] 190423eb0b74Storek 190523eb0b74Storek ldd [%sp + CCFSZ + 0], %l0 ! new psr, pc 190623eb0b74Storek ldd [%sp + CCFSZ + 8], %l2 ! new npc, %y 190723eb0b74Storek wr %l3, 0, %y 190823eb0b74Storek ld [%sp + CCFSZ + 20], %g1 190923eb0b74Storek ldd [%sp + CCFSZ + 24], %g2 191023eb0b74Storek ldd [%sp + CCFSZ + 32], %g4 191123eb0b74Storek ldd [%sp + CCFSZ + 40], %g6 191223eb0b74Storek ldd [%sp + CCFSZ + 48], %i0 ! %o0 for window R, etc 191323eb0b74Storek ldd [%sp + CCFSZ + 56], %i2 191423eb0b74Storek ldd [%sp + CCFSZ + 64], %i4 191523eb0b74Storek wr %l0, 0, %psr ! disable traps: test must be atomic 191623eb0b74Storek ldd [%sp + CCFSZ + 72], %i6 191723eb0b74Storek sethi %hi(_cpcb), %l6 191823eb0b74Storek ld [%l6 + %lo(_cpcb)], %l6 191923eb0b74Storek ld [%l6 + PCB_NSAVED], %l7 ! if nsaved is -1, we have our regs 192023eb0b74Storek tst %l7 192123eb0b74Storek bl,a 1f ! got them 192223eb0b74Storek wr %g0, 0, %wim ! allow us to enter windows R, I 192323eb0b74Storek b,a return_from_trap 192423eb0b74Storek 192523eb0b74Storek /* 192623eb0b74Storek * Got 'em. Load 'em up. 192723eb0b74Storek */ 192823eb0b74Storek1: 192923eb0b74Storek mov %g6, %l3 ! save %g6; set %g6 = cpcb 193023eb0b74Storek mov %l6, %g6 193123eb0b74Storek st %g0, [%g6 + PCB_NSAVED] ! and clear magic flag 193223eb0b74Storek restore ! from T to R 193323eb0b74Storek restore ! from R to I 193423eb0b74Storek restore %g0, 1, %l1 ! from I to X, then %l1 = 1 193523eb0b74Storek rd %psr, %l0 ! cwp = %psr; 193623eb0b74Storek sll %l1, %l0, %l1 193723eb0b74Storek wr %l1, 0, %wim ! make window X invalid 193823eb0b74Storek and %l0, 31, %l0 193923eb0b74Storek st %l0, [%g6 + PCB_WIM] ! cpcb->pcb_wim = cwp; 194023eb0b74Storek nop ! unnecessary? old wim was 0... 194123eb0b74Storek save %g0, %g0, %g0 ! back to I 194223eb0b74Storek LOADWIN(%g6 + PCB_RW + 64) ! load from rw[1] 194323eb0b74Storek save %g0, %g0, %g0 ! back to R 194423eb0b74Storek LOADWIN(%g6 + PCB_RW) ! load from rw[0] 194523eb0b74Storek save %g0, %g0, %g0 ! back to T 194623eb0b74Storek wr %l0, 0, %psr ! restore condition codes 194723eb0b74Storek mov %l3, %g6 ! fix %g6 194823eb0b74Storek RETT 194923eb0b74Storek 195023eb0b74Storek /* 195123eb0b74Storek * Restoring from user stack, but everything has checked out 195223eb0b74Storek * as good. We are now in window X, and %l1 = 1. Window R 195323eb0b74Storek * is still valid and holds user values. 195423eb0b74Storek */ 195523eb0b74Storekwinuf_ok: 195623eb0b74Storek rd %psr, %l0 195723eb0b74Storek sll %l1, %l0, %l1 195823eb0b74Storek wr %l1, 0, %wim ! make this one invalid 195923eb0b74Storek sethi %hi(_cpcb), %l2 196023eb0b74Storek ld [%l2 + %lo(_cpcb)], %l2 196123eb0b74Storek and %l0, 31, %l0 196223eb0b74Storek st %l0, [%l2 + PCB_WIM] ! cpcb->pcb_wim = cwp; 196323eb0b74Storek save %g0, %g0, %g0 ! back to I 196423eb0b74Storek LOADWIN(%sp) 196523eb0b74Storek save %g0, %g0, %g0 ! back to R 196623eb0b74Storek save %g0, %g0, %g0 ! back to T 196723eb0b74Storek wr %l0, 0, %psr ! restore condition codes 196823eb0b74Storek nop ! it takes three to tangle 196923eb0b74Storek RETT 197023eb0b74Storek#endif /* end `real' version of window underflow trap handler */ 197123eb0b74Storek 197223eb0b74Storek/* 197323eb0b74Storek * Various return-from-trap routines (see return_from_trap). 197423eb0b74Storek */ 197523eb0b74Storek 197623eb0b74Storek/* 197723eb0b74Storek * Return from trap, to kernel. 197823eb0b74Storek * %l0 = %psr 197923eb0b74Storek * %l1 = return pc 198023eb0b74Storek * %l2 = return npc 198123eb0b74Storek * %l4 = %wim 198223eb0b74Storek * %l5 = bit for previous window 198323eb0b74Storek */ 198423eb0b74Storekrft_kernel: 198523eb0b74Storek btst %l5, %l4 ! if (wim & l5) 198623eb0b74Storek bnz 1f ! goto reload; 198723eb0b74Storek wr %l0, 0, %psr ! but first put !@#*% cond codes back 198823eb0b74Storek 198923eb0b74Storek /* previous window is valid; just rett */ 199023eb0b74Storek nop ! wait for cond codes to settle in 199123eb0b74Storek RETT 199223eb0b74Storek 199323eb0b74Storek /* 199423eb0b74Storek * Previous window is invalid. 199523eb0b74Storek * Update %wim and then reload l0..i7 from frame. 199623eb0b74Storek * 199723eb0b74Storek * T I X 199823eb0b74Storek * 0 0 1 0 0 (%wim) 199923eb0b74Storek * [see picture in window_uf handler] 200023eb0b74Storek * 200123eb0b74Storek * T is the current (Trap) window, I is the Invalid window, 200223eb0b74Storek * and X is the window we want to make invalid. Window X 200323eb0b74Storek * currently has no useful values. 200423eb0b74Storek */ 200523eb0b74Storek1: 200623eb0b74Storek wr %g0, 0, %wim ! allow us to enter window I 200723eb0b74Storek nop; nop; nop ! (it takes a while) 200823eb0b74Storek restore ! enter window I 200923eb0b74Storek restore %g0, 1, %l1 ! enter window X, then %l1 = 1 201023eb0b74Storek rd %psr, %l0 ! CWP = %psr & 31; 201123eb0b74Storek and %l0, 31, %l0 201223eb0b74Storek sll %l1, %l0, %l1 ! wim = 1 << CWP; 201323eb0b74Storek wr %l1, 0, %wim ! setwim(wim); 201423eb0b74Storek sethi %hi(_cpcb), %l1 201523eb0b74Storek ld [%l1 + %lo(_cpcb)], %l1 201623eb0b74Storek st %l0, [%l1 + PCB_WIM] ! cpcb->pcb_wim = l0 & 31; 201723eb0b74Storek save %g0, %g0, %g0 ! back to window I 201823eb0b74Storek LOADWIN(%sp) 201923eb0b74Storek save %g0, %g0, %g0 ! back to window T 202023eb0b74Storek /* 202123eb0b74Storek * Note that the condition codes are still set from 202223eb0b74Storek * the code at rft_kernel; we can simply return. 202323eb0b74Storek */ 202423eb0b74Storek RETT 202523eb0b74Storek 202623eb0b74Storek/* 202723eb0b74Storek * Return from trap, to user. Checks for scheduling trap (`ast') first; 202823eb0b74Storek * will re-enter trap() if set. Note that we may have to switch from 202923eb0b74Storek * the interrupt stack to the kernel stack in this case. 203023eb0b74Storek * %l0 = %psr 203123eb0b74Storek * %l1 = return pc 203223eb0b74Storek * %l2 = return npc 203323eb0b74Storek * %l4 = %wim 203423eb0b74Storek * %l5 = bit for previous window 203523eb0b74Storek * %l6 = cpcb 203623eb0b74Storek * If returning to a valid window, just set psr and return. 203723eb0b74Storek */ 203823eb0b74Storekrft_user: 203923eb0b74Storek! sethi %hi(_want_ast), %l7 ! (done below) 204023eb0b74Storek ld [%l7 + %lo(_want_ast)], %l7 204123eb0b74Storek tst %l7 ! want AST trap? 204223eb0b74Storek bne,a softtrap ! yes, re-enter trap with type T_AST 204323eb0b74Storek mov T_AST, %o0 204423eb0b74Storek 204523eb0b74Storek btst %l5, %l4 ! if (wim & l5) 204623eb0b74Storek bnz 1f ! goto reload; 204723eb0b74Storek wr %l0, 0, %psr ! restore cond codes 204823eb0b74Storek nop ! (three instruction delay) 204923eb0b74Storek RETT 205023eb0b74Storek 205123eb0b74Storek /* 205223eb0b74Storek * Previous window is invalid. 205323eb0b74Storek * Before we try to load it, we must verify its stack pointer. 205423eb0b74Storek * This is much like the underflow handler, but a bit easier 205523eb0b74Storek * since we can use our own local registers. 205623eb0b74Storek */ 205723eb0b74Storek1: 205823eb0b74Storek btst 7, %fp ! if unaligned, address is invalid 205923eb0b74Storek bne rft_invalid 206023eb0b74Storek EMPTY 206123eb0b74Storek 206223eb0b74Storek PTE_OF_ADDR(%fp, %l7, rft_invalid) 206323eb0b74Storek CMP_PTE_USER_READ(%l7) ! try first page 206423eb0b74Storek bne rft_invalid ! no good 206523eb0b74Storek EMPTY 206623eb0b74Storek SLT_IF_1PAGE_RW(%fp, %l7) 206723eb0b74Storek bl,a rft_user_ok ! only 1 page: ok 206823eb0b74Storek wr %g0, 0, %wim 206923eb0b74Storek add %fp, 7*8, %l5 207023eb0b74Storek PTE_OF_ADDR(%l5, %l7, rft_invalid) 207123eb0b74Storek CMP_PTE_USER_READ(%l7) ! check 2nd page too 207223eb0b74Storek be,a rft_user_ok 207323eb0b74Storek wr %g0, 0, %wim 207423eb0b74Storek 207523eb0b74Storek /* 207623eb0b74Storek * The window we wanted to pull could not be pulled. Instead, 207723eb0b74Storek * re-enter trap with type T_RWRET. This will pull the window 207823eb0b74Storek * into cpcb->pcb_rw[0] and set cpcb->pcb_nsaved to -1, which we 207923eb0b74Storek * will detect when we try to return again. 208023eb0b74Storek */ 208123eb0b74Storekrft_invalid: 208223eb0b74Storek b softtrap 208323eb0b74Storek mov T_RWRET, %o0 208423eb0b74Storek 208523eb0b74Storek /* 208623eb0b74Storek * The window we want to pull can be pulled directly. 208723eb0b74Storek */ 208823eb0b74Storekrft_user_ok: 208923eb0b74Storek! wr %g0, 0, %wim ! allow us to get into it 209023eb0b74Storek wr %l0, 0, %psr ! fix up the cond codes now 209123eb0b74Storek nop; nop; nop 209223eb0b74Storek restore ! enter window I 209323eb0b74Storek restore %g0, 1, %l1 ! enter window X, then %l1 = 1 209423eb0b74Storek rd %psr, %l0 ! l0 = (junk << 5) + CWP; 209523eb0b74Storek sll %l1, %l0, %l1 ! %wim = 1 << CWP; 209623eb0b74Storek wr %l1, 0, %wim 209723eb0b74Storek sethi %hi(_cpcb), %l1 209823eb0b74Storek ld [%l1 + %lo(_cpcb)], %l1 209923eb0b74Storek and %l0, 31, %l0 210023eb0b74Storek st %l0, [%l1 + PCB_WIM] ! cpcb->pcb_wim = l0 & 31; 210123eb0b74Storek save %g0, %g0, %g0 ! back to window I 210223eb0b74Storek LOADWIN(%sp) ! suck hard 210323eb0b74Storek save %g0, %g0, %g0 ! back to window T 210423eb0b74Storek RETT 210523eb0b74Storek 210623eb0b74Storek/* 210723eb0b74Storek * Return from trap. Entered after a 210823eb0b74Storek * wr %l0, 0, %psr 210923eb0b74Storek * which disables traps so that we can rett; registers are: 211023eb0b74Storek * 211123eb0b74Storek * %l0 = %psr 211223eb0b74Storek * %l1 = return pc 211323eb0b74Storek * %l2 = return npc 211423eb0b74Storek * 211523eb0b74Storek * (%l3..%l7 anything). 211623eb0b74Storek * 211723eb0b74Storek * If we are returning to user code, we must: 211823eb0b74Storek * 1. Check for register windows in the pcb that belong on the stack. 211923eb0b74Storek * If there are any, reenter trap with type T_WINOF. 212023eb0b74Storek * 2. Make sure the register windows will not underflow. This is 212123eb0b74Storek * much easier in kernel mode.... 212223eb0b74Storek */ 212323eb0b74Storekreturn_from_trap: 212423eb0b74Storek! wr %l0, 0, %psr ! disable traps so we can rett 212523eb0b74Storek! (someone else did this already) 212623eb0b74Storek and %l0, 31, %l5 212723eb0b74Storek set wmask, %l6 212823eb0b74Storek ldub [%l6 + %l5], %l5 ! %l5 = 1 << ((CWP + 1) % nwindows) 212923eb0b74Storek btst PSR_PS, %l0 ! returning to userland? 213023eb0b74Storek bnz rft_kernel ! no, go return to kernel 213123eb0b74Storek rd %wim, %l4 ! (read %wim in any case) 213223eb0b74Storek 213323eb0b74Storekrft_user_or_recover_pcb_windows: 213423eb0b74Storek /* 213523eb0b74Storek * (entered with %l4=%wim, %l5=wmask[cwp]; %l0..%l2 as usual) 213623eb0b74Storek * 213723eb0b74Storek * check cpcb->pcb_nsaved: 213823eb0b74Storek * if 0, do a `normal' return to user (see rft_user); 213923eb0b74Storek * if > 0, cpcb->pcb_rw[] holds registers to be copied to stack; 214023eb0b74Storek * if -1, cpcb->pcb_rw[0] holds user registers for rett window 214123eb0b74Storek * from an earlier T_RWRET pseudo-trap. 214223eb0b74Storek */ 214323eb0b74Storek sethi %hi(_cpcb), %l6 214423eb0b74Storek ld [%l6 + %lo(_cpcb)], %l6 214523eb0b74Storek ld [%l6 + PCB_NSAVED], %l7 214623eb0b74Storek tst %l7 214723eb0b74Storek bz,a rft_user 214823eb0b74Storek sethi %hi(_want_ast), %l7 ! first instr of rft_user 214923eb0b74Storek 215023eb0b74Storek bg,a softtrap ! if (pcb_nsaved > 0) 215123eb0b74Storek mov T_WINOF, %o0 ! trap(T_WINOF); 215223eb0b74Storek 215323eb0b74Storek /* 215423eb0b74Storek * To get here, we must have tried to return from a previous 215523eb0b74Storek * trap and discovered that it would cause a window underflow. 215623eb0b74Storek * We then must have tried to pull the registers out of the 215723eb0b74Storek * user stack (from the address in %fp==%i6) and discovered 215823eb0b74Storek * that it was either unaligned or not loaded in memory, and 215923eb0b74Storek * therefore we ran a trap(T_RWRET), which loaded one set of 216023eb0b74Storek * registers into cpcb->pcb_pcb_rw[0] (if it had killed the 216123eb0b74Storek * process due to a bad stack, we would not be here). 216223eb0b74Storek * 216323eb0b74Storek * We want to load pcb_rw[0] into the previous window, which 216423eb0b74Storek * we know is currently invalid. In other words, we want 216523eb0b74Storek * %wim to be 1 << ((cwp + 2) % nwindows). 216623eb0b74Storek */ 216723eb0b74Storek wr %g0, 0, %wim ! enable restores 216823eb0b74Storek mov %g6, %l3 ! save g6 in l3 216923eb0b74Storek mov %l6, %g6 ! set g6 = &u 217023eb0b74Storek st %g0, [%g6 + PCB_NSAVED] ! clear cpcb->pcb_nsaved 217123eb0b74Storek restore ! enter window I 217223eb0b74Storek restore %g0, 1, %l1 ! enter window X, then %l1 = 1 217323eb0b74Storek rd %psr, %l0 217423eb0b74Storek sll %l1, %l0, %l1 ! %wim = 1 << CWP; 217523eb0b74Storek wr %l1, 0, %wim 217623eb0b74Storek and %l0, 31, %l0 217723eb0b74Storek st %l0, [%g6 + PCB_WIM] ! cpcb->pcb_wim = CWP; 217823eb0b74Storek nop ! unnecessary? old wim was 0... 217923eb0b74Storek save %g0, %g0, %g0 ! back to window I 218023eb0b74Storek LOADWIN(%g6 + PCB_RW) 218123eb0b74Storek save %g0, %g0, %g0 ! back to window T (trap window) 218223eb0b74Storek wr %l0, 0, %psr ! cond codes, cond codes everywhere 218323eb0b74Storek mov %l3, %g6 ! restore g6 218423eb0b74Storek RETT 218523eb0b74Storek 218623eb0b74Storek! exported end marker for kernel gdb 218723eb0b74Storek .globl _endtrapcode 218823eb0b74Storek_endtrapcode: 218923eb0b74Storek 219023eb0b74Storek/* 219123eb0b74Storek * init_tables(nwin) int nwin; 219223eb0b74Storek * 219323eb0b74Storek * Set up the uwtab and wmask tables. 219423eb0b74Storek * We know nwin > 1. 219523eb0b74Storek */ 219623eb0b74Storekinit_tables: 219723eb0b74Storek /* 219823eb0b74Storek * for (i = -nwin, j = nwin - 2; ++i < 0; j--) 219923eb0b74Storek * uwtab[i] = j; 220023eb0b74Storek * (loop runs at least once) 220123eb0b74Storek */ 220223eb0b74Storek set uwtab, %o3 220323eb0b74Storek sub %g0, %o0, %o1 ! i = -nwin + 1 220423eb0b74Storek inc %o1 220523eb0b74Storek add %o0, -2, %o2 ! j = nwin - 2; 220623eb0b74Storek0: 220723eb0b74Storek stb %o2, [%o3 + %o1] ! uwtab[i] = j; 220823eb0b74Storek1: 220923eb0b74Storek inccc %o1 ! ++i < 0? 221023eb0b74Storek bl 0b ! yes, continue loop 221123eb0b74Storek dec %o2 ! in any case, j-- 221223eb0b74Storek 221323eb0b74Storek /* 221423eb0b74Storek * (i now equals 0) 221523eb0b74Storek * for (j = nwin - 1; i < nwin; i++, j--) 221623eb0b74Storek * uwtab[i] = j; 221723eb0b74Storek * (loop runs at least twice) 221823eb0b74Storek */ 221923eb0b74Storek sub %o0, 1, %o2 ! j = nwin - 1 222023eb0b74Storek0: 222123eb0b74Storek stb %o2, [%o3 + %o1] ! uwtab[i] = j 222223eb0b74Storek inc %o1 ! i++ 222323eb0b74Storek1: 222423eb0b74Storek cmp %o1, %o0 ! i < nwin? 222523eb0b74Storek bl 0b ! yes, continue 222623eb0b74Storek dec %o2 ! in any case, j-- 222723eb0b74Storek 222823eb0b74Storek /* 222923eb0b74Storek * We observe that, for i in 0..nwin-2, (i+1)%nwin == i+1; 223023eb0b74Storek * for i==nwin-1, (i+1)%nwin == 0. 223123eb0b74Storek * To avoid adding 1, we run i from 1 to nwin and set 223223eb0b74Storek * wmask[i-1]. 223323eb0b74Storek * 223423eb0b74Storek * for (i = j = 1; i < nwin; i++) { 223523eb0b74Storek * j <<= 1; (j now == 1 << i) 223623eb0b74Storek * wmask[i - 1] = j; 223723eb0b74Storek * } 223823eb0b74Storek * (loop runs at least once) 223923eb0b74Storek */ 224023eb0b74Storek set wmask - 1, %o3 224123eb0b74Storek mov 1, %o1 ! i = 1; 224223eb0b74Storek mov 2, %o2 ! j = 2; 224323eb0b74Storek0: 224423eb0b74Storek stb %o2, [%o3 + %o1] ! (wmask - 1)[i] = j; 224523eb0b74Storek inc %o1 ! i++ 224623eb0b74Storek cmp %o1, %o0 ! i < nwin? 224723eb0b74Storek bl,a 0b ! yes, continue 224823eb0b74Storek sll %o2, 1, %o2 ! (and j <<= 1) 224923eb0b74Storek 225023eb0b74Storek /* 225123eb0b74Storek * Now i==nwin, so we want wmask[i-1] = 1. 225223eb0b74Storek */ 225323eb0b74Storek mov 1, %o2 ! j = 1; 225423eb0b74Storek retl 225523eb0b74Storek stb %o2, [%o3 + %o1] ! (wmask - 1)[i] = j; 225623eb0b74Storek 225723eb0b74Storekdostart: 22588e156d2fStorek rd %psr, %l0 ! paranoia: make sure ... 22598e156d2fStorek andn %l0, PSR_ET, %l0 ! we have traps off 22608e156d2fStorek wr %l0, 0, %psr ! so that we can fiddle safely 22618e156d2fStorek nop; nop; nop 22628e156d2fStorek 226323eb0b74Storek /* 226423eb0b74Storek * Startup. 226523eb0b74Storek * 226623eb0b74Storek * We have been loaded in low RAM, at some address which 226723eb0b74Storek * is page aligned (0x4000 actually) rather than where we 226823eb0b74Storek * want to run (0xf8004000). Until we get everything set, 226923eb0b74Storek * we have to be sure to use only pc-relative addressing. 227023eb0b74Storek */ 22718e156d2fStorek 227223eb0b74Storek wr %g0, 0, %wim ! make sure we can set psr 227323eb0b74Storek mov %o0, %g7 ! save prom vector pointer 227423eb0b74Storek nop; nop 227523eb0b74Storek wr %g0, PSR_S|PSR_PS|PSR_PIL, %psr ! set initial psr 227623eb0b74Storek set AC_CONTEXT, %g1 ! paranoia: set context to kernel 227723eb0b74Storek stba %g0, [%g1] ASI_CONTROL 227823eb0b74Storek wr %g0, 2, %wim ! set initial %wim (w1 invalid) 227923eb0b74Storek mov 1, %g1 ! set pcb_wim (log2(%wim) = 1) 2280*c073cf18Smckusick sethi %hi(_u0 - KERNBASE + PCB_WIM), %g2 2281*c073cf18Smckusick st %g1, [%g2 + %lo(_u0 - KERNBASE + PCB_WIM)] 228223eb0b74Storek 228323eb0b74Storek /* 228423eb0b74Storek * Step 1: double map low RAM (addresses [0.._end-start-1]) 228523eb0b74Storek * to KERNBASE (addresses [KERNBASE.._end-1]). None of these 228623eb0b74Storek * are `bad' aliases (since they are all on segment boundaries) 228723eb0b74Storek * so we do not have to worry about cache aliasing. 228823eb0b74Storek * 228923eb0b74Storek * We map in another couple of segments just to have some 229023eb0b74Storek * more memory (512K, actually) guaranteed available for 229123eb0b74Storek * bootstrap code (pmap_bootstrap needs memory to hold MMU 229223eb0b74Storek * and context data structures). 229323eb0b74Storek */ 229423eb0b74Storek clr %l0 ! lowva 229523eb0b74Storek set KERNBASE, %l1 ! highva 229623eb0b74Storek set _end + (2 << 18), %l2 ! last va that must be remapped 229723eb0b74Storek set 1 << 18, %l3 ! segment size in bytes 229823eb0b74Storek0: 229923eb0b74Storek lduba [%l0] ASI_SEGMAP, %l4 ! segmap[highva] = segmap[lowva]; 230023eb0b74Storek stba %l4, [%l1] ASI_SEGMAP 230123eb0b74Storek add %l3, %l1, %l1 ! highva += segsiz; 230223eb0b74Storek cmp %l1, %l2 ! done? 230323eb0b74Storek bl 0b ! no, loop 230423eb0b74Storek add %l3, %l0, %l0 ! (and lowva += segsz) 230523eb0b74Storek 230623eb0b74Storek /* 230723eb0b74Storek * Now map the interrupt enable register and clear any interrupts, 230823eb0b74Storek * enabling NMIs. Note that we will not take NMIs until we change 230923eb0b74Storek * %tbr. 231023eb0b74Storek */ 231123eb0b74Storek set IE_reg_addr, %l0 231223eb0b74Storek set IE_REG_PTE, %l1 231323eb0b74Storek sta %l1, [%l0] ASI_PTE 231423eb0b74Storek mov IE_ALLIE, %l1 231523eb0b74Storek nop; nop ! paranoia 231623eb0b74Storek stb %l1, [%l0] 231723eb0b74Storek 231823eb0b74Storek /* 231923eb0b74Storek * All set, fix pc and npc. Once we are where we should be, 232023eb0b74Storek * we can give ourselves a stack and enable traps. 232123eb0b74Storek */ 232223eb0b74Storek set 1f, %l0 232323eb0b74Storek jmp %l0 232423eb0b74Storek nop 232523eb0b74Storek1: 232623eb0b74Storek set USRSTACK - CCFSZ, %fp ! as if called from user code 232723eb0b74Storek set estack0 - CCFSZ - 80, %sp ! via syscall(boot_me_up) or somesuch 232823eb0b74Storek rd %psr, %l0 232923eb0b74Storek wr %l0, PSR_ET, %psr 233023eb0b74Storek 233123eb0b74Storek /* 233223eb0b74Storek * Step 2: clear BSS. This may just be paranoia; the boot 233323eb0b74Storek * loader might already do it for us; but what the hell. 233423eb0b74Storek */ 233523eb0b74Storek set _edata, %o0 ! bzero(edata, end - edata) 233623eb0b74Storek set _end, %o1 233723eb0b74Storek call _bzero 233823eb0b74Storek sub %o1, %o0, %o1 233923eb0b74Storek 234023eb0b74Storek /* 234123eb0b74Storek * Stash prom vectors now, after bzero, as it lives in bss 234223eb0b74Storek * (which we just zeroed). 234323eb0b74Storek * This depends on the fact that bzero does not use %g7. 234423eb0b74Storek */ 234523eb0b74Storek sethi %hi(_promvec), %l0 234623eb0b74Storek st %g7, [%l0 + %lo(_promvec)] 234723eb0b74Storek 234823eb0b74Storek /* 234923eb0b74Storek * Step 3: compute number of windows and set up tables. 235023eb0b74Storek * We could do some of this later. 235123eb0b74Storek */ 235223eb0b74Storek save %sp, -64, %sp 235323eb0b74Storek rd %psr, %g1 235423eb0b74Storek restore 235523eb0b74Storek and %g1, 31, %g1 ! want just the CWP bits 235623eb0b74Storek add %g1, 1, %o0 ! compute nwindows 235723eb0b74Storek sethi %hi(_nwindows), %o1 ! may as well tell everyone 235823eb0b74Storek call init_tables 235923eb0b74Storek st %o0, [%o1 + %lo(_nwindows)] 236023eb0b74Storek 236123eb0b74Storek /* 236223eb0b74Storek * Step 4: change the trap base register, now that our trap handlers 236323eb0b74Storek * will function (they need the tables we just set up). 236423eb0b74Storek */ 236523eb0b74Storek set _trapbase, %l0 236623eb0b74Storek wr %l0, 0, %tbr 236723eb0b74Storek nop ! paranoia 236823eb0b74Storek 236923eb0b74Storek /* 2370*c073cf18Smckusick * Ready to run C code; finish bootstrap. 237123eb0b74Storek */ 237223eb0b74Storek call _bootstrap 237323eb0b74Storek nop 2374*c073cf18Smckusick 2375*c073cf18Smckusick /* 2376*c073cf18Smckusick * Call main. This returns to us after loading /sbin/init into 2377*c073cf18Smckusick * user space. (If the exec fails, main() does not return.) 2378*c073cf18Smckusick */ 237923eb0b74Storek call _main 2380*c073cf18Smckusick clr %o0 ! our frame arg is ignored 238123eb0b74Storek 238223eb0b74Storek /* 2383*c073cf18Smckusick * Here we finish up as in syscall, but simplified. We need to 2384*c073cf18Smckusick * fiddle pc and npc a bit, as execve() / setregs() have only set 2385*c073cf18Smckusick * npc, in anticipation that trap.c will advance past the trap 2386*c073cf18Smckusick * instruction; but we bypass that, so we must do it manually. 238723eb0b74Storek */ 2388*c073cf18Smckusick mov PSR_S, %l0 ! user psr (no need to load it) 2389*c073cf18Smckusick ld [%sp + CCFSZ + 8], %l1 ! pc = npc from execve 2390*c073cf18Smckusick b init_syscall_ret 2391*c073cf18Smckusick add %l1, 4, %l2 ! npc = pc+4 239223eb0b74Storek 239323eb0b74Storek/* 239423eb0b74Storek * The following code is copied to the top of the user stack when each 239523eb0b74Storek * process is exec'ed, and signals are `trampolined' off it. 239623eb0b74Storek * 239723eb0b74Storek * When this code is run, the stack looks like: 239823eb0b74Storek * [%sp] 64 bytes to which registers can be dumped 239923eb0b74Storek * [%sp + 64] signal number (goes in %o0) 240023eb0b74Storek * [%sp + 64 + 4] signal code (goes in %o1) 240123eb0b74Storek * [%sp + 64 + 8] placeholder 240223eb0b74Storek * [%sp + 64 + 12] argument for %o3, currently unsupported (always 0) 240323eb0b74Storek * [%sp + 64 + 16] first word of saved state (sigcontext) 240423eb0b74Storek * . 240523eb0b74Storek * . 240623eb0b74Storek * . 240723eb0b74Storek * [%sp + NNN] last word of saved state 240823eb0b74Storek * (followed by previous stack contents or top of signal stack). 240923eb0b74Storek * The address of the function to call is in %g1; the old %g1 and %o0 2410a961f703Storek * have already been saved in the sigcontext. We are running in a clean 2411a961f703Storek * window, all previous windows now being saved to the stack. 241223eb0b74Storek * 241323eb0b74Storek * Note that [%sp + 64 + 8] == %sp + 64 + 16. The copy at %sp+64+8 241423eb0b74Storek * will eventually be removed, with a hole left in its place, if things 241523eb0b74Storek * work out. 241623eb0b74Storek */ 241723eb0b74Storek .globl _sigcode 241823eb0b74Storek .globl _esigcode 241923eb0b74Storek_sigcode: 242023eb0b74Storek /* 2421a961f703Storek * XXX the `save' and `restore' below are unnecessary: should 2422a961f703Storek * replace with simple arithmetic on %sp 2423a961f703Storek * 2424a961f703Storek * Make room on the stack for 32 %f registers + %fsr. This comes 2425a961f703Storek * out to 33*4 or 132 bytes, but this must be aligned to a multiple 2426a961f703Storek * of 8, or 136 bytes. 242723eb0b74Storek */ 242823eb0b74Storek save %sp, -CCFSZ - 136, %sp 242923eb0b74Storek mov %g2, %l2 ! save globals in %l registers 243023eb0b74Storek mov %g3, %l3 243123eb0b74Storek mov %g4, %l4 243223eb0b74Storek mov %g5, %l5 243323eb0b74Storek mov %g6, %l6 243423eb0b74Storek mov %g7, %l7 243523eb0b74Storek /* 243623eb0b74Storek * Saving the fpu registers is expensive, so do it iff the fsr 243723eb0b74Storek * stored in the sigcontext shows that the fpu is enabled. 243823eb0b74Storek */ 243923eb0b74Storek ld [%fp + 64 + 16 + SC_PSR_OFFSET], %l0 244023eb0b74Storek sethi %hi(PSR_EF), %l1 ! FPU enable bit is too high for andcc 244123eb0b74Storek andcc %l0, %l1, %l0 ! %l0 = fpu enable bit 244223eb0b74Storek be 1f ! if not set, skip the saves 244323eb0b74Storek rd %y, %l1 ! in any case, save %y 244423eb0b74Storek 244523eb0b74Storek ! fpu is enabled, oh well 244623eb0b74Storek st %fsr, [%sp + CCFSZ + 0] 244723eb0b74Storek std %f0, [%sp + CCFSZ + 8] 244823eb0b74Storek std %f2, [%sp + CCFSZ + 16] 244923eb0b74Storek std %f4, [%sp + CCFSZ + 24] 245023eb0b74Storek std %f6, [%sp + CCFSZ + 32] 245123eb0b74Storek std %f8, [%sp + CCFSZ + 40] 245223eb0b74Storek std %f10, [%sp + CCFSZ + 48] 245323eb0b74Storek std %f12, [%sp + CCFSZ + 56] 245423eb0b74Storek std %f14, [%sp + CCFSZ + 64] 245523eb0b74Storek std %f16, [%sp + CCFSZ + 72] 245623eb0b74Storek std %f18, [%sp + CCFSZ + 80] 245723eb0b74Storek std %f20, [%sp + CCFSZ + 88] 245823eb0b74Storek std %f22, [%sp + CCFSZ + 96] 245923eb0b74Storek std %f24, [%sp + CCFSZ + 104] 246023eb0b74Storek std %f26, [%sp + CCFSZ + 112] 246123eb0b74Storek std %f28, [%sp + CCFSZ + 120] 246223eb0b74Storek std %f30, [%sp + CCFSZ + 128] 246323eb0b74Storek 246423eb0b74Storek1: 246523eb0b74Storek ldd [%fp + 64], %o0 ! sig, code 246623eb0b74Storek ld [%fp + 76], %o3 ! arg3 246723eb0b74Storek call %g1 ! (*sa->sa_handler)(sig,code,scp,arg3) 246823eb0b74Storek add %fp, 64 + 16, %o2 ! scp 246923eb0b74Storek 247023eb0b74Storek /* 247123eb0b74Storek * Now that the handler has returned, re-establish all the state 247223eb0b74Storek * we just saved above, then do a sigreturn. 247323eb0b74Storek */ 247423eb0b74Storek tst %l0 ! reload fpu registers? 247523eb0b74Storek be 1f ! if not, skip the loads 247623eb0b74Storek wr %l1, %g0, %y ! in any case, restore %y 247723eb0b74Storek 247823eb0b74Storek ld [%sp + CCFSZ + 0], %fsr 247923eb0b74Storek ldd [%sp + CCFSZ + 8], %f0 248023eb0b74Storek ldd [%sp + CCFSZ + 16], %f2 248123eb0b74Storek ldd [%sp + CCFSZ + 24], %f4 248223eb0b74Storek ldd [%sp + CCFSZ + 32], %f6 248323eb0b74Storek ldd [%sp + CCFSZ + 40], %f8 248423eb0b74Storek ldd [%sp + CCFSZ + 48], %f10 248523eb0b74Storek ldd [%sp + CCFSZ + 56], %f12 248623eb0b74Storek ldd [%sp + CCFSZ + 64], %f14 248723eb0b74Storek ldd [%sp + CCFSZ + 72], %f16 248823eb0b74Storek ldd [%sp + CCFSZ + 80], %f18 248923eb0b74Storek ldd [%sp + CCFSZ + 88], %f20 249023eb0b74Storek ldd [%sp + CCFSZ + 96], %f22 249123eb0b74Storek ldd [%sp + CCFSZ + 104], %f24 249223eb0b74Storek ldd [%sp + CCFSZ + 112], %f26 249323eb0b74Storek ldd [%sp + CCFSZ + 120], %f28 249423eb0b74Storek ldd [%sp + CCFSZ + 128], %f30 249523eb0b74Storek 249623eb0b74Storek1: 249723eb0b74Storek mov %l2, %g2 249823eb0b74Storek mov %l3, %g3 249923eb0b74Storek mov %l4, %g4 250023eb0b74Storek mov %l5, %g5 250123eb0b74Storek mov %l6, %g6 250223eb0b74Storek mov %l7, %g7 250323eb0b74Storek 250423eb0b74Storek restore %g0, SYS_sigreturn, %g1 ! get registers back & set syscall # 250523eb0b74Storek add %sp, 64 + 16, %o0 ! compute scp 250623eb0b74Storek t ST_SYSCALL ! sigreturn(scp) 250723eb0b74Storek ! sigreturn does not return unless it fails 250823eb0b74Storek mov SYS_exit, %g1 ! exit(errno) 250923eb0b74Storek t ST_SYSCALL 251023eb0b74Storek_esigcode: 251123eb0b74Storek 251223eb0b74Storek/* 251323eb0b74Storek * Primitives 251423eb0b74Storek */ 251523eb0b74Storek 251623eb0b74Storek#ifdef GPROF 251723eb0b74Storek .globl mcount 251823eb0b74Storek#define ENTRY(x) \ 251923eb0b74Storek .globl _##x; _##x: \ 252023eb0b74Storek save %sp, -CCFSZ, %sp; \ 252123eb0b74Storek call mcount; \ 252223eb0b74Storek nop; \ 252323eb0b74Storek restore 252423eb0b74Storek#else 252523eb0b74Storek#define ENTRY(x) .globl _##x; _##x: 252623eb0b74Storek#endif 252723eb0b74Storek#define ALTENTRY(x) .globl _##x; _##x: 252823eb0b74Storek 252923eb0b74Storek/* 253023eb0b74Storek * copyinstr(fromaddr, toaddr, maxlength, &lencopied) 253123eb0b74Storek * 253223eb0b74Storek * Copy a null terminated string from the user address space into 253323eb0b74Storek * the kernel address space. 253423eb0b74Storek */ 253523eb0b74StorekENTRY(copyinstr) 253623eb0b74Storek ! %o0 = fromaddr, %o1 = toaddr, %o2 = maxlen, %o3 = &lencopied 253723eb0b74Storek#ifdef DIAGNOSTIC 253823eb0b74Storek tst %o2 ! kernel should never give maxlen <= 0 253923eb0b74Storek ble 1f 254023eb0b74Storek EMPTY 254123eb0b74Storek#endif 254223eb0b74Storek set KERNBASE, %o4 254323eb0b74Storek cmp %o0, %o4 ! fromaddr < KERNBASE? 254423eb0b74Storek blu,a Lcsdocopy ! yes, go do it 254523eb0b74Storek sethi %hi(_cpcb), %o4 ! (first instr of copy) 254623eb0b74Storek 254723eb0b74Storek b Lcsdone ! no, return EFAULT 254823eb0b74Storek mov EFAULT, %o0 254923eb0b74Storek 255023eb0b74Storek1: 255123eb0b74Storek sethi %hi(2f), %o0 255223eb0b74Storek call _panic 255323eb0b74Storek or %lo(2f), %o0, %o0 255423eb0b74Storek2: .asciz "copyinstr" 255523eb0b74Storek ALIGN 255623eb0b74Storek 255723eb0b74Storek/* 255823eb0b74Storek * copyoutstr(fromaddr, toaddr, maxlength, &lencopied) 255923eb0b74Storek * 256023eb0b74Storek * Copy a null terminated string from the kernel 256123eb0b74Storek * address space to the user address space. 256223eb0b74Storek */ 256323eb0b74StorekENTRY(copyoutstr) 256423eb0b74Storek ! %o0 = fromaddr, %o1 = toaddr, %o2 = maxlen, %o3 = &lencopied 256523eb0b74Storek#ifdef DIAGNOSTIC 256623eb0b74Storek tst %o2 256723eb0b74Storek ble 1f 256823eb0b74Storek EMPTY 256923eb0b74Storek#endif 257023eb0b74Storek set KERNBASE, %o4 257123eb0b74Storek cmp %o1, %o4 ! toaddr < KERNBASE? 257223eb0b74Storek blu,a Lcsdocopy ! yes, go do it 257323eb0b74Storek sethi %hi(_cpcb), %o4 ! (first instr of copy) 257423eb0b74Storek 257523eb0b74Storek b Lcsdone ! no, return EFAULT 257623eb0b74Storek mov EFAULT, %o0 257723eb0b74Storek 257823eb0b74Storek1: 257923eb0b74Storek sethi %hi(2f), %o0 258023eb0b74Storek call _panic 258123eb0b74Storek or %lo(2f), %o0, %o0 258223eb0b74Storek2: .asciz "copyoutstr" 258323eb0b74Storek ALIGN 258423eb0b74Storek 258523eb0b74StorekLcsdocopy: 258623eb0b74Storek! sethi %hi(_cpcb), %o4 ! (done earlier) 258723eb0b74Storek ld [%o4 + %lo(_cpcb)], %o4 ! catch faults 258823eb0b74Storek set Lcsfault, %o5 258923eb0b74Storek st %o5, [%o4 + PCB_ONFAULT] 259023eb0b74Storek 259123eb0b74Storek mov %o1, %o5 ! save = toaddr; 259223eb0b74Storek! XXX should do this in bigger chunks when possible 259323eb0b74Storek0: ! loop: 259423eb0b74Storek ldsb [%o0], %g1 ! c = *fromaddr; 259523eb0b74Storek tst %g1 259623eb0b74Storek stb %g1, [%o1] ! *toaddr++ = c; 259723eb0b74Storek be 1f ! if (c == NULL) 259823eb0b74Storek inc %o1 ! goto ok; 259923eb0b74Storek deccc %o2 ! if (--len > 0) { 260023eb0b74Storek bg 0b ! fromaddr++; 260123eb0b74Storek inc %o0 ! goto loop; 260223eb0b74Storek ! } 260323eb0b74Storek ! 260423eb0b74Storek b Lcsdone ! error = ENAMETOOLONG; 260523eb0b74Storek mov ENAMETOOLONG, %o0 ! goto done; 260623eb0b74Storek1: ! ok: 260723eb0b74Storek clr %o0 ! error = 0; 260823eb0b74StorekLcsdone: ! done: 260923eb0b74Storek sub %o1, %o5, %o1 ! len = to - save; 261023eb0b74Storek tst %o3 ! if (lencopied) 261123eb0b74Storek bnz,a 3f 261223eb0b74Storek st %o1, [%o3] ! *lencopied = len; 261323eb0b74Storek3: 261423eb0b74Storek retl ! cpcb->pcb_onfault = 0; 261523eb0b74Storek st %g0, [%o4 + PCB_ONFAULT]! return (error); 261623eb0b74Storek 261723eb0b74StorekLcsfault: 261823eb0b74Storek b Lcsdone ! error = EFAULT; 261923eb0b74Storek mov EFAULT, %o0 ! goto ret; 262023eb0b74Storek 262123eb0b74Storek/* 262223eb0b74Storek * copystr(fromaddr, toaddr, maxlength, &lencopied) 262323eb0b74Storek * 262423eb0b74Storek * Copy a null terminated string from one point to another in 262523eb0b74Storek * the kernel address space. (This is a leaf procedure, but 262623eb0b74Storek * it does not seem that way to the C compiler.) 262723eb0b74Storek */ 262823eb0b74StorekENTRY(copystr) 262923eb0b74Storek#ifdef DIAGNOSTIC 263023eb0b74Storek tst %o2 ! if (maxlength <= 0) 263123eb0b74Storek ble 4f ! panic(...); 263223eb0b74Storek EMPTY 263323eb0b74Storek#endif 263423eb0b74Storek mov %o1, %o5 ! to0 = to; 263523eb0b74Storek0: ! loop: 263623eb0b74Storek ldsb [%o0], %o4 ! c = *from; 263723eb0b74Storek tst %o4 263823eb0b74Storek stb %o4, [%o1] ! *to++ = c; 263923eb0b74Storek be 1f ! if (c == 0) 264023eb0b74Storek inc %o1 ! goto ok; 264123eb0b74Storek deccc %o2 ! if (--len > 0) { 264223eb0b74Storek bg,a 0b ! from++; 264323eb0b74Storek inc %o0 ! goto loop; 264423eb0b74Storek b 2f ! } 264523eb0b74Storek mov ENAMETOOLONG, %o0 ! ret = ENAMETOOLONG; goto done; 264623eb0b74Storek1: ! ok: 264723eb0b74Storek clr %o0 ! ret = 0; 264823eb0b74Storek2: 264923eb0b74Storek sub %o1, %o5, %o1 ! len = to - to0; 265023eb0b74Storek tst %o3 ! if (lencopied) 265123eb0b74Storek bnz,a 3f 265223eb0b74Storek st %o1, [%o3] ! *lencopied = len; 265323eb0b74Storek3: 265423eb0b74Storek retl 265523eb0b74Storek nop 265623eb0b74Storek#ifdef DIAGNOSTIC 265723eb0b74Storek4: 265823eb0b74Storek sethi %hi(5f), %o0 265923eb0b74Storek call _panic 266023eb0b74Storek or %lo(5f), %o0, %o0 266123eb0b74Storek5: 266223eb0b74Storek .asciz "copystr" 266323eb0b74Storek ALIGN 266423eb0b74Storek#endif 266523eb0b74Storek 266623eb0b74Storek/* 266723eb0b74Storek * Copyin(src, dst, len) 266823eb0b74Storek * 266923eb0b74Storek * Copy specified amount of data from user space into the kernel. 267023eb0b74Storek */ 267123eb0b74StorekENTRY(copyin) 267223eb0b74Storek set KERNBASE, %o3 267323eb0b74Storek cmp %o0, %o3 ! src < KERNBASE? 267423eb0b74Storek blu,a Ldocopy ! yes, can try it 267523eb0b74Storek sethi %hi(_cpcb), %o3 267623eb0b74Storek 267723eb0b74Storek /* source address points into kernel space: return EFAULT */ 267823eb0b74Storek retl 267923eb0b74Storek mov EFAULT, %o0 268023eb0b74Storek 268123eb0b74Storek/* 268223eb0b74Storek * Copyout(src, dst, len) 268323eb0b74Storek * 268423eb0b74Storek * Copy specified amount of data from kernel to user space. 268523eb0b74Storek * Just like copyin, except that the `dst' addresses are user space 268623eb0b74Storek * rather than the `src' addresses. 268723eb0b74Storek */ 268823eb0b74StorekENTRY(copyout) 268923eb0b74Storek set KERNBASE, %o3 269023eb0b74Storek cmp %o1, %o3 ! dst < KERBASE? 269123eb0b74Storek blu,a Ldocopy 269223eb0b74Storek sethi %hi(_cpcb), %o3 269323eb0b74Storek 269423eb0b74Storek /* destination address points into kernel space: return EFAULT */ 269523eb0b74Storek retl 269623eb0b74Storek mov EFAULT, %o0 269723eb0b74Storek 269823eb0b74Storek /* 269923eb0b74Storek * ******NOTE****** this depends on bcopy() not using %g7 270023eb0b74Storek */ 270123eb0b74StorekLdocopy: 270223eb0b74Storek! sethi %hi(_cpcb), %o3 270323eb0b74Storek ld [%o3 + %lo(_cpcb)], %o3 270423eb0b74Storek set Lcopyfault, %o4 270523eb0b74Storek mov %o7, %g7 ! save return address 270623eb0b74Storek call _bcopy ! bcopy(src, dst, len) 270723eb0b74Storek st %o4, [%o3 + PCB_ONFAULT] 270823eb0b74Storek 270923eb0b74Storek sethi %hi(_cpcb), %o3 271023eb0b74Storek ld [%o3 + %lo(_cpcb)], %o3 271123eb0b74Storek st %g0, [%o3 + PCB_ONFAULT] 271223eb0b74Storek jmp %g7 + 8 271323eb0b74Storek clr %o0 ! return 0 271423eb0b74Storek 271523eb0b74Storek! Copyin or copyout fault. Clear cpcb->pcb_onfault and return EFAULT. 271623eb0b74Storek! Note that although we were in bcopy, there is no state to clean up; 271723eb0b74Storek! the only special thing is that we have to return to [g7 + 8] rather than 271823eb0b74Storek! [o7 + 8]. 271923eb0b74StorekLcopyfault: 272023eb0b74Storek sethi %hi(_cpcb), %o3 272123eb0b74Storek ld [%o3 + %lo(_cpcb)], %o3 272223eb0b74Storek st %g0, [%o3 + PCB_ONFAULT] 272323eb0b74Storek jmp %g7 + 8 272423eb0b74Storek mov EFAULT, %o0 272523eb0b74Storek 272623eb0b74Storek 272723eb0b74Storek/* 272823eb0b74Storek * Write all user windows presently in the CPU back to the user's stack. 272923eb0b74Storek * We just do `save' instructions until pcb_uw == 0. 273023eb0b74Storek * 273123eb0b74Storek * p = cpcb; 273223eb0b74Storek * nsaves = 0; 273323eb0b74Storek * while (p->pcb_uw > 0) 273423eb0b74Storek * save(), nsaves++; 273523eb0b74Storek * while (--nsaves >= 0) 273623eb0b74Storek * restore(); 273723eb0b74Storek */ 273823eb0b74StorekENTRY(write_user_windows) 273923eb0b74Storek sethi %hi(_cpcb), %g6 274023eb0b74Storek ld [%g6 + %lo(_cpcb)], %g6 274123eb0b74Storek b 2f 274223eb0b74Storek clr %g5 274323eb0b74Storek1: 274423eb0b74Storek save %sp, -64, %sp 274523eb0b74Storek2: 274623eb0b74Storek ld [%g6 + PCB_UW], %g7 274723eb0b74Storek tst %g7 274823eb0b74Storek bg,a 1b 274923eb0b74Storek inc %g5 275023eb0b74Storek3: 275123eb0b74Storek deccc %g5 275223eb0b74Storek bge,a 3b 275323eb0b74Storek restore 275423eb0b74Storek retl 275523eb0b74Storek nop 275623eb0b74Storek 275723eb0b74Storek 275823eb0b74Storek .comm _want_resched,4 275923eb0b74Storek/* 276023eb0b74Storek * Masterpaddr is the p->p_addr of the last process on the processor. 276123eb0b74Storek * XXX masterpaddr is almost the same as cpcb 276223eb0b74Storek * XXX should delete this entirely 276323eb0b74Storek */ 276423eb0b74Storek .comm _masterpaddr, 4 276523eb0b74Storek 276623eb0b74Storek/* 276723eb0b74Storek * Switch statistics (for later tweaking): 276823eb0b74Storek * nswitchdiff = p1 => p2 (i.e., chose different process) 2769cb228b87Sbostic * nswitchexit = number of calls to switchexit() 277023eb0b74Storek * _cnt.v_swtch = total calls to swtch+swtchexit 277123eb0b74Storek */ 277223eb0b74Storek .comm _nswitchdiff, 4 277323eb0b74Storek .comm _nswitchexit, 4 277423eb0b74Storek 277523eb0b74Storek/* 2776cb228b87Sbostic * REGISTER USAGE IN cpu_switch AND switchexit: 277723eb0b74Storek * This is split into two phases, more or less 277823eb0b74Storek * `before we locate a new proc' and `after'. 277923eb0b74Storek * Some values are the same in both phases. 278023eb0b74Storek * Note that the %o0-registers are not preserved across 278123eb0b74Storek * the psr change when entering a new process, since this 278223eb0b74Storek * usually changes the CWP field (hence heavy usage of %g's). 278323eb0b74Storek * 278423eb0b74Storek * %g1 = oldpsr (excluding ipl bits) 278523eb0b74Storek * %g2 = %hi(_whichqs); newpsr 278623eb0b74Storek * %g3 = p 278723eb0b74Storek * %g4 = lastproc 278823eb0b74Storek * %g5 = <free>; newpcb 278923eb0b74Storek * %g6 = %hi(_cpcb) 279023eb0b74Storek * %g7 = %hi(_curproc) 279123eb0b74Storek * %o0 = tmp 1 279223eb0b74Storek * %o1 = tmp 2 279323eb0b74Storek * %o2 = tmp 3 279423eb0b74Storek * %o3 = tmp 4; whichqs; vm 279523eb0b74Storek * %o4 = tmp 4; which; sswap 279623eb0b74Storek * %o5 = tmp 5; q; <free> 279723eb0b74Storek */ 279823eb0b74Storek 279923eb0b74Storek/* 2800cb228b87Sbostic * switchexit is called only from cpu_exit() before the current process 280123eb0b74Storek * has freed its kernel stack; we must free it. (curproc is already NULL.) 280223eb0b74Storek * 280323eb0b74Storek * We lay the process to rest by changing to the `idle' kernel stack, 280423eb0b74Storek * and note that the `last loaded process' is nonexistent. 280523eb0b74Storek */ 2806cb228b87SbosticENTRY(switchexit) 280723eb0b74Storek mov %o0, %g2 ! save the 280823eb0b74Storek mov %o1, %g3 ! ... three parameters 280923eb0b74Storek mov %o2, %g4 ! ... to kmem_free 281023eb0b74Storek 281123eb0b74Storek /* 281223eb0b74Storek * Change pcb to idle u. area, i.e., set %sp to top of stack 281323eb0b74Storek * and %psr to PSR_S|PSR_ET, and set cpcb to point to _idle_u. 281423eb0b74Storek * Once we have left the old stack, we can call kmem_free to 281523eb0b74Storek * destroy it. Call it any sooner and the register windows 281623eb0b74Storek * go bye-bye. 281723eb0b74Storek */ 281823eb0b74Storek set _idle_u, %g5 281923eb0b74Storek sethi %hi(_cpcb), %g6 282023eb0b74Storek mov 1, %g7 282123eb0b74Storek wr %g0, PSR_S, %psr ! change to window 0, traps off 282223eb0b74Storek wr %g0, 2, %wim ! and make window 1 the trap window 282323eb0b74Storek st %g5, [%g6 + %lo(_cpcb)] ! cpcb = &idle_u 282423eb0b74Storek st %g7, [%g5 + PCB_WIM] ! idle_u.pcb_wim = log2(2) = 1 282523eb0b74Storek set _idle_u + UPAGES * NBPG - CCFSZ, %sp ! set new %sp 282623eb0b74Storek#ifdef DEBUG 282723eb0b74Storek set _idle_u, %l6 282823eb0b74Storek SET_SP_REDZONE(%l6, %l5) 282923eb0b74Storek#endif 283023eb0b74Storek wr %g0, PSR_S|PSR_ET, %psr ! and then enable traps 283123eb0b74Storek mov %g2, %o0 ! now ready to call kmem_free 283223eb0b74Storek mov %g3, %o1 283323eb0b74Storek call _kmem_free 283423eb0b74Storek mov %g4, %o2 283523eb0b74Storek 283623eb0b74Storek /* 2837cb228b87Sbostic * Now fall through to `the last switch'. %g6 was set to 283823eb0b74Storek * %hi(_cpcb), but may have been clobbered in kmem_free, 283923eb0b74Storek * so all the registers described below will be set here. 284023eb0b74Storek * 284123eb0b74Storek * REGISTER USAGE AT THIS POINT: 284223eb0b74Storek * %g1 = oldpsr (excluding ipl bits) 284323eb0b74Storek * %g2 = %hi(_whichqs) 284423eb0b74Storek * %g4 = lastproc 284523eb0b74Storek * %g6 = %hi(_cpcb) 284623eb0b74Storek * %g7 = %hi(_curproc) 284723eb0b74Storek * %o0 = tmp 1 284823eb0b74Storek * %o1 = tmp 2 284923eb0b74Storek * %o3 = whichqs 285023eb0b74Storek */ 285123eb0b74Storek 285223eb0b74Storek INCR(_nswitchexit) ! nswitchexit++; 285323eb0b74Storek INCR(_cnt+V_SWTCH) ! cnt.v_switch++; 285423eb0b74Storek 285523eb0b74Storek mov PSR_S|PSR_ET, %g1 ! oldpsr = PSR_S | PSR_ET; 285623eb0b74Storek sethi %hi(_whichqs), %g2 285723eb0b74Storek clr %g4 ! lastproc = NULL; 285823eb0b74Storek sethi %hi(_cpcb), %g6 285923eb0b74Storek sethi %hi(_curproc), %g7 286023eb0b74Storek /* FALLTHROUGH */ 286123eb0b74Storek 286223eb0b74Storek/* 2863cb228b87Sbostic * When no processes are on the runq, switch 286423eb0b74Storek * idles here watiing for something to come ready. 286523eb0b74Storek * The registers are set up as noted above. 286623eb0b74Storek */ 286723eb0b74Storek .globl idle 286823eb0b74Storekidle: 286923eb0b74Storek st %g0, [%g7 + %lo(_curproc)] ! curproc = NULL; 287023eb0b74Storek wr %g1, 0, %psr ! (void) spl0(); 287123eb0b74Storek1: ! spin reading _whichqs until nonzero 287223eb0b74Storek ld [%g2 + %lo(_whichqs)], %o3 287323eb0b74Storek tst %o3 287423eb0b74Storek bnz,a Lsw_scan 287523eb0b74Storek wr %g1, PIL_CLOCK << 8, %psr ! (void) splclock(); 287623eb0b74Storek b,a 1b 287723eb0b74Storek 287823eb0b74StorekLsw_panic_rq: 287923eb0b74Storek sethi %hi(1f), %o0 288023eb0b74Storek call _panic 288123eb0b74Storek or %lo(1f), %o0, %o0 288223eb0b74StorekLsw_panic_wchan: 288323eb0b74Storek sethi %hi(2f), %o0 288423eb0b74Storek call _panic 288523eb0b74Storek or %lo(2f), %o0, %o0 288623eb0b74StorekLsw_panic_srun: 288723eb0b74Storek sethi %hi(3f), %o0 288823eb0b74Storek call _panic 288923eb0b74Storek or %lo(3f), %o0, %o0 2890cb228b87Sbostic1: .asciz "switch rq" 2891cb228b87Sbostic2: .asciz "switch wchan" 2892cb228b87Sbostic3: .asciz "switch SRUN" 289323eb0b74Storek ALIGN 289423eb0b74Storek 289523eb0b74Storek/* 2896cb228b87Sbostic * cpu_switch() picks a process to run and runs it, saving the current 289723eb0b74Storek * one away. On the assumption that (since most workstations are 289823eb0b74Storek * single user machines) the chances are quite good that the new 289923eb0b74Storek * process will turn out to be the current process, we defer saving 290023eb0b74Storek * it here until we have found someone to load. If that someone 290123eb0b74Storek * is the current process we avoid both store and load. 290223eb0b74Storek * 2903cb228b87Sbostic * cpu_switch() is always entered at splstatclock or splhigh. 290423eb0b74Storek * 290523eb0b74Storek * IT MIGHT BE WORTH SAVING BEFORE ENTERING idle TO AVOID HAVING TO 290623eb0b74Storek * SAVE LATER WHEN SOMEONE ELSE IS READY ... MUST MEASURE! 290723eb0b74Storek */ 290823eb0b74Storek .globl _runtime 290923eb0b74Storek .globl _time 2910cb228b87SbosticENTRY(cpu_switch) 291123eb0b74Storek /* 291223eb0b74Storek * REGISTER USAGE AT THIS POINT: 291323eb0b74Storek * %g1 = oldpsr (excluding ipl bits) 291423eb0b74Storek * %g2 = %hi(_whichqs) 291523eb0b74Storek * %g3 = p 291623eb0b74Storek * %g4 = lastproc 291723eb0b74Storek * %g5 = tmp 0 291823eb0b74Storek * %g6 = %hi(_cpcb) 291923eb0b74Storek * %g7 = %hi(_curproc) 292023eb0b74Storek * %o0 = tmp 1 292123eb0b74Storek * %o1 = tmp 2 292223eb0b74Storek * %o2 = tmp 3 292323eb0b74Storek * %o3 = tmp 4, then at Lsw_scan, whichqs 292423eb0b74Storek * %o4 = tmp 5, then at Lsw_scan, which 292523eb0b74Storek * %o5 = tmp 6, then at Lsw_scan, q 292623eb0b74Storek */ 292723eb0b74Storek sethi %hi(_whichqs), %g2 ! set up addr regs 292823eb0b74Storek sethi %hi(_cpcb), %g6 292923eb0b74Storek ld [%g6 + %lo(_cpcb)], %o0 293023eb0b74Storek std %o6, [%o0 + PCB_SP] ! cpcb->pcb_<sp,pc> = <sp,pc>; 293123eb0b74Storek rd %psr, %g1 ! oldpsr = %psr; 293223eb0b74Storek sethi %hi(_curproc), %g7 293323eb0b74Storek ld [%g7 + %lo(_curproc)], %g4 ! lastproc = curproc; 293423eb0b74Storek st %g1, [%o0 + PCB_PSR] ! cpcb->pcb_psr = oldpsr; 293523eb0b74Storek andn %g1, PSR_PIL, %g1 ! oldpsr &= ~PSR_PIL; 293623eb0b74Storek 293723eb0b74Storek /* 293823eb0b74Storek * In all the fiddling we did to get this far, the thing we are 293923eb0b74Storek * waiting for might have come ready, so let interrupts in briefly 294023eb0b74Storek * before checking for other processes. Note that we still have 294123eb0b74Storek * curproc set---we have to fix this or we can get in trouble with 294223eb0b74Storek * the run queues below. 294323eb0b74Storek */ 294423eb0b74Storek st %g0, [%g7 + %lo(_curproc)] ! curproc = NULL; 294523eb0b74Storek wr %g1, 0, %psr ! (void) spl0(); 294623eb0b74Storek nop; nop; nop ! paranoia 294723eb0b74Storek wr %g1, PIL_CLOCK <<8 , %psr ! (void) splclock(); 294823eb0b74Storek 294923eb0b74StorekLsw_scan: 295023eb0b74Storek nop; nop; nop ! paranoia 295123eb0b74Storek /* 295223eb0b74Storek * We're about to run a (possibly) new process. Set runtime 295323eb0b74Storek * to indicate its start time. 295423eb0b74Storek */ 295523eb0b74Storek sethi %hi(_time), %o0 295623eb0b74Storek ldd [%o0 + %lo(_time)], %o2 295723eb0b74Storek sethi %hi(_runtime), %o0 295823eb0b74Storek std %o2, [%o0 + %lo(_runtime)] 295923eb0b74Storek 296023eb0b74Storek ld [%g2 + %lo(_whichqs)], %o3 296123eb0b74Storek 296223eb0b74Storek /* 296323eb0b74Storek * Optimized inline expansion of `which = ffs(whichqs) - 1'; 296423eb0b74Storek * branches to idle if ffs(whichqs) was 0. 296523eb0b74Storek */ 296623eb0b74Storek set ffstab, %o2 296723eb0b74Storek andcc %o3, 0xff, %o1 ! byte 0 zero? 296823eb0b74Storek bz,a 1f ! yes, try byte 1 296923eb0b74Storek srl %o3, 8, %o0 297023eb0b74Storek b 2f ! ffs = ffstab[byte0]; which = ffs - 1; 297123eb0b74Storek ldsb [%o2 + %o1], %o0 297223eb0b74Storek1: andcc %o0, 0xff, %o1 ! byte 1 zero? 297323eb0b74Storek bz,a 1f ! yes, try byte 2 297423eb0b74Storek srl %o0, 8, %o0 297523eb0b74Storek ldsb [%o2 + %o1], %o0 ! which = ffstab[byte1] + 7; 297623eb0b74Storek b 3f 297723eb0b74Storek add %o0, 7, %o4 297823eb0b74Storek1: andcc %o0, 0xff, %o1 ! byte 2 zero? 297923eb0b74Storek bz,a 1f ! yes, try byte 3 298023eb0b74Storek srl %o0, 8, %o0 298123eb0b74Storek ldsb [%o2 + %o1], %o0 ! which = ffstab[byte2] + 15; 298223eb0b74Storek b 3f 298323eb0b74Storek add %o0, 15, %o4 298423eb0b74Storek1: ldsb [%o2 + %o0], %o0 ! ffs = ffstab[byte3] + 24 298523eb0b74Storek addcc %o0, 24, %o0 ! (note that ffstab[0] == -24) 298623eb0b74Storek bz idle ! if answer was 0, go idle 298723eb0b74Storek EMPTY 298823eb0b74Storek2: sub %o0, 1, %o4 ! which = ffs(whichqs) - 1 298923eb0b74Storek3: /* end optimized inline expansion */ 299023eb0b74Storek 299123eb0b74Storek /* 299223eb0b74Storek * We found a nonempty run queue. Take its first process. 299323eb0b74Storek */ 299423eb0b74Storek set _qs, %o5 ! q = &qs[which]; 299523eb0b74Storek sll %o4, 3, %o0 299623eb0b74Storek add %o0, %o5, %o5 299723eb0b74Storek ld [%o5], %g3 ! p = q->ph_link; 299823eb0b74Storek cmp %g3, %o5 ! if (p == q) 2999cb228b87Sbostic be Lsw_panic_rq ! panic("switch rq"); 300023eb0b74Storek EMPTY 3001cb228b87Sbostic ld [%g3], %o0 ! tmp0 = p->p_forw; 300223eb0b74Storek st %o0, [%o5] ! q->ph_link = tmp0; 3003cb228b87Sbostic st %o5, [%o0 + 4] ! tmp0->p_back = q; 300423eb0b74Storek cmp %o0, %o5 ! if (tmp0 == q) 300523eb0b74Storek bne 1f 300623eb0b74Storek EMPTY 300723eb0b74Storek mov 1, %o1 ! whichqs &= ~(1 << which); 300823eb0b74Storek sll %o1, %o4, %o1 300923eb0b74Storek andn %o3, %o1, %o3 301023eb0b74Storek st %o3, [%g2 + %lo(_whichqs)] 301123eb0b74Storek1: 301223eb0b74Storek /* 301323eb0b74Storek * PHASE TWO: NEW REGISTER USAGE: 301423eb0b74Storek * %g1 = oldpsr (excluding ipl bits) 301523eb0b74Storek * %g2 = newpsr 301623eb0b74Storek * %g3 = p 301723eb0b74Storek * %g4 = lastproc 301823eb0b74Storek * %g5 = newpcb 301923eb0b74Storek * %g6 = %hi(_cpcb) 302023eb0b74Storek * %g7 = %hi(_curproc) 302123eb0b74Storek * %o0 = tmp 1 302223eb0b74Storek * %o1 = tmp 2 302323eb0b74Storek * %o2 = tmp 3 302423eb0b74Storek * %o3 = vm 302523eb0b74Storek * %o4 = sswap 302623eb0b74Storek * %o5 = <free> 302723eb0b74Storek */ 302823eb0b74Storek 302923eb0b74Storek /* firewalls */ 303023eb0b74Storek ld [%g3 + P_WCHAN], %o0 ! if (p->p_wchan) 303123eb0b74Storek tst %o0 3032cb228b87Sbostic bne Lsw_panic_wchan ! panic("switch wchan"); 303323eb0b74Storek EMPTY 303423eb0b74Storek ldsb [%g3 + P_STAT], %o0 ! if (p->p_stat != SRUN) 303523eb0b74Storek cmp %o0, SRUN 3036cb228b87Sbostic bne Lsw_panic_srun ! panic("switch SRUN"); 303723eb0b74Storek EMPTY 303823eb0b74Storek 303923eb0b74Storek /* 304023eb0b74Storek * Committed to running process p. 304123eb0b74Storek * It may be the same as the one we were running before. 304223eb0b74Storek */ 304323eb0b74Storek sethi %hi(_want_resched), %o0 304423eb0b74Storek st %g0, [%o0 + %lo(_want_resched)] ! want_resched = 0; 304523eb0b74Storek ld [%g3 + P_ADDR], %g5 ! newpcb = p->p_addr; 3046cb228b87Sbostic st %g0, [%g3 + 4] ! p->p_back = NULL; 304723eb0b74Storek ld [%g5 + PCB_PSR], %g2 ! newpsr = newpcb->pcb_psr; 304823eb0b74Storek st %g3, [%g7 + %lo(_curproc)] ! curproc = p; 304923eb0b74Storek 305023eb0b74Storek cmp %g3, %g4 ! p == lastproc? 305123eb0b74Storek be,a Lsw_sameproc ! yes, go return 0 305223eb0b74Storek wr %g2, 0, %psr ! (after restoring ipl) 305323eb0b74Storek 305423eb0b74Storek /* 305523eb0b74Storek * Not the old process. Save the old process, if any; 305623eb0b74Storek * then load p. 305723eb0b74Storek */ 305823eb0b74Storek tst %g4 305923eb0b74Storek be,a Lsw_load ! if no old process, go load 306023eb0b74Storek wr %g1, (PIL_CLOCK << 8) | PSR_ET, %psr 306123eb0b74Storek 306223eb0b74Storek INCR(_nswitchdiff) ! clobbers %o0,%o1 306323eb0b74Storek /* 306423eb0b74Storek * save: write back all windows (including the current one). 306523eb0b74Storek * XXX crude; knows nwindows <= 8 306623eb0b74Storek */ 306723eb0b74Storek#define SAVE save %sp, -64, %sp 306823eb0b74Storek SAVE; SAVE; SAVE; SAVE; SAVE; SAVE; SAVE /* 7 of each: */ 306923eb0b74Storek restore; restore; restore; restore; restore; restore; restore 307023eb0b74Storek 307123eb0b74Storek /* 307223eb0b74Storek * Load the new process. To load, we must change stacks and 307323eb0b74Storek * alter cpcb and %wim, hence we must disable traps. %psr is 307423eb0b74Storek * currently equal to oldpsr (%g1) ^ (PIL_CLOCK << 8); 307523eb0b74Storek * this means that PSR_ET is on. Likewise, PSR_ET is on 307623eb0b74Storek * in newpsr (%g2), although we do not know newpsr's ipl. 307723eb0b74Storek * 307823eb0b74Storek * We also must load up the `in' and `local' registers. 307923eb0b74Storek */ 308023eb0b74Storek wr %g1, (PIL_CLOCK << 8) | PSR_ET, %psr 308123eb0b74StorekLsw_load: 308223eb0b74Storek! wr %g1, (PIL_CLOCK << 8) | PSR_ET, %psr ! done above 308323eb0b74Storek /* compute new wim */ 308423eb0b74Storek ld [%g5 + PCB_WIM], %o0 308523eb0b74Storek mov 1, %o1 308623eb0b74Storek sll %o1, %o0, %o0 308723eb0b74Storek wr %o0, 0, %wim ! %wim = 1 << newpcb->pcb_wim; 308823eb0b74Storek /* now must not change %psr for 3 more instrs */ 308923eb0b74Storek/*1*/ set PSR_EF|PSR_EC, %o0 309023eb0b74Storek/*2*/ andn %g2, %o0, %g2 ! newpsr &= ~(PSR_EF|PSR_EC); 309123eb0b74Storek/*3*/ nop 309223eb0b74Storek /* set new psr, but with traps disabled */ 309323eb0b74Storek wr %g2, PSR_ET, %psr ! %psr = newpsr ^ PSR_ET; 309423eb0b74Storek /* set new cpcb */ 309523eb0b74Storek st %g5, [%g6 + %lo(_cpcb)] ! cpcb = newpcb; 309623eb0b74Storek /* XXX update masterpaddr too */ 309723eb0b74Storek sethi %hi(_masterpaddr), %g7 309823eb0b74Storek st %g5, [%g7 + %lo(_masterpaddr)] 309923eb0b74Storek ldd [%g5 + PCB_SP], %o6 ! <sp,pc> = newpcb->pcb_<sp,pc> 310023eb0b74Storek /* load window */ 310123eb0b74Storek ldd [%sp + (0*8)], %l0 310223eb0b74Storek ldd [%sp + (1*8)], %l2 310323eb0b74Storek ldd [%sp + (2*8)], %l4 310423eb0b74Storek ldd [%sp + (3*8)], %l6 310523eb0b74Storek ldd [%sp + (4*8)], %i0 310623eb0b74Storek ldd [%sp + (5*8)], %i2 310723eb0b74Storek ldd [%sp + (6*8)], %i4 310823eb0b74Storek ldd [%sp + (7*8)], %i6 310923eb0b74Storek#ifdef DEBUG 311023eb0b74Storek mov %g5, %o0 311123eb0b74Storek SET_SP_REDZONE(%o0, %o1) 311223eb0b74Storek CHECK_SP_REDZONE(%o0, %o1) 311323eb0b74Storek#endif 311423eb0b74Storek /* finally, enable traps */ 311523eb0b74Storek wr %g2, 0, %psr ! psr = newpsr; 311623eb0b74Storek 311723eb0b74Storek /* 311823eb0b74Storek * Now running p. Make sure it has a context so that it 311923eb0b74Storek * can talk about user space stuff. (Its pcb_uw is currently 312023eb0b74Storek * zero so it is safe to have interrupts going here.) 312123eb0b74Storek */ 312223eb0b74Storek ld [%g3 + P_VMSPACE], %o3 ! vm = p->p_vmspace; 312323eb0b74Storek ld [%o3 + VM_PMAP_CTX], %o0! if (vm->vm_pmap.pm_ctx != NULL) 312423eb0b74Storek tst %o0 312523eb0b74Storek bnz,a Lsw_havectx ! goto havecontext; 312623eb0b74Storek ld [%o3 + VM_PMAP_CTXNUM], %o0 312723eb0b74Storek 312823eb0b74Storek /* p does not have a context: call ctx_alloc to get one */ 312923eb0b74Storek save %sp, -CCFSZ, %sp 313023eb0b74Storek call _ctx_alloc ! ctx_alloc(&vm->vm_pmap); 313123eb0b74Storek add %i3, VM_PMAP, %o0 313223eb0b74Storek ret 313323eb0b74Storek restore 313423eb0b74Storek 313523eb0b74Storek /* p does have a context: just switch to it */ 313623eb0b74StorekLsw_havectx: 313723eb0b74Storek! ld [%o3 + VM_PMAP_CTXNUM], %o0 ! (done in delay slot) 313823eb0b74Storek set AC_CONTEXT, %o1 313923eb0b74Storek stba %o0, [%o1] ASI_CONTROL ! setcontext(vm->vm_pmap.pm_ctxnum); 314023eb0b74Storek retl 314123eb0b74Storek nop 314223eb0b74Storek 314323eb0b74StorekLsw_sameproc: 314423eb0b74Storek /* 314523eb0b74Storek * We are resuming the process that was running at the 3146cb228b87Sbostic * call to switch(). Just set psr ipl and return. 314723eb0b74Storek */ 314823eb0b74Storek! wr %g2, 0 %psr ! %psr = newpsr; (done earlier) 314923eb0b74Storek nop 315023eb0b74Storek retl 315123eb0b74Storek nop 315223eb0b74Storek 315323eb0b74Storek 315423eb0b74Storek/* 315523eb0b74Storek * Snapshot the current process so that stack frames are up to date. 315623eb0b74Storek * This is called from two places: 315723eb0b74Storek * - just before a crash dump, for the stack update; 315823eb0b74Storek * - in cpu_fork(), before copying the kernel stack. 315923eb0b74Storek * In the latter case the pcb and stack will be copied to the child, 3160cb228b87Sbostic * and the child will be made runnable. Eventually switch() will run 316123eb0b74Storek * it. When it does, we want its pcb_pc set so that we can appear 316223eb0b74Storek * to return 1 from cpu_fork(), so we store the current sp and psr 316323eb0b74Storek * in the given pcb, and set its pcb_pc to our return-1 code (offset 316423eb0b74Storek * by -8 due to call/ret conventions). This is not useful in the crash 316523eb0b74Storek * dump code but it is easiest to do it anyway. 316623eb0b74Storek */ 316723eb0b74StorekENTRY(snapshot) 316823eb0b74Storek st %o6, [%o0 + PCB_SP] ! save sp 316923eb0b74Storek set 1f - 8, %o1 ! set child-return pc 317023eb0b74Storek st %o1, [%o0 + PCB_PC] 317123eb0b74Storek rd %psr, %o1 ! save psr 317223eb0b74Storek st %o1, [%o0 + PCB_PSR] 317323eb0b74Storek 317423eb0b74Storek /* 3175cb228b87Sbostic * Just like switch(); same XXX comments apply. 317623eb0b74Storek * 7 of each. Minor tweak: the 7th restore is 317723eb0b74Storek * done after a ret. 317823eb0b74Storek */ 317923eb0b74Storek SAVE; SAVE; SAVE; SAVE; SAVE; SAVE; SAVE 318023eb0b74Storek restore; restore; restore; restore; restore; restore; ret; restore 318123eb0b74Storek 3182cb228b87Sbostic1: /* this is reached only after a child gets chosen in switch() */ 318323eb0b74Storek mov 1, %i0 ! return 1 from cpu_fork 318423eb0b74Storek ret 318523eb0b74Storek restore 318623eb0b74Storek 318723eb0b74Storek/* 318823eb0b74Storek * {fu,su}{,i}{byte,word} 318923eb0b74Storek */ 319023eb0b74StorekALTENTRY(fuiword) 319123eb0b74StorekENTRY(fuword) 319223eb0b74Storek set KERNBASE, %o2 319323eb0b74Storek cmp %o0, %o2 ! if addr >= KERNBASE... 319423eb0b74Storek bgeu Lfsbadaddr 319523eb0b74Storek EMPTY 319623eb0b74Storek btst 3, %o0 ! or has low bits set... 319723eb0b74Storek bnz Lfsbadaddr ! go return -1 319823eb0b74Storek EMPTY 319923eb0b74Storek sethi %hi(_cpcb), %o2 ! cpcb->pcb_onfault = Lfserr; 320023eb0b74Storek ld [%o2 + %lo(_cpcb)], %o2 320123eb0b74Storek set Lfserr, %o3 320223eb0b74Storek st %o3, [%o2 + PCB_ONFAULT] 320323eb0b74Storek ld [%o0], %o0 ! fetch the word 320423eb0b74Storek retl ! phew, made it, return the word 320523eb0b74Storek st %g0, [%o2 + PCB_ONFAULT]! but first clear onfault 320623eb0b74Storek 320723eb0b74StorekLfserr: 320823eb0b74Storek st %g0, [%o2 + PCB_ONFAULT]! error in r/w, clear pcb_onfault 320923eb0b74StorekLfsbadaddr: 321023eb0b74Storek retl ! and return error indicator 321123eb0b74Storek mov -1, %o0 321223eb0b74Storek 321323eb0b74Storek /* 321423eb0b74Storek * This is just like Lfserr, but it's a global label that allows 321523eb0b74Storek * mem_access_fault() to check to see that we don't want to try to 321623eb0b74Storek * page in the fault. It's used by fuswintr() etc. 321723eb0b74Storek */ 321823eb0b74Storek .globl _Lfsbail 321923eb0b74Storek_Lfsbail: 322023eb0b74Storek st %g0, [%o2 + PCB_ONFAULT]! error in r/w, clear pcb_onfault 322123eb0b74Storek retl ! and return error indicator 322223eb0b74Storek mov -1, %o0 322323eb0b74Storek 322423eb0b74Storek /* 322523eb0b74Storek * Like fusword but callable from interrupt context. 322623eb0b74Storek * Fails if data isn't resident. 322723eb0b74Storek */ 322823eb0b74StorekENTRY(fuswintr) 322923eb0b74Storek set KERNBASE, %o2 323023eb0b74Storek cmp %o0, %o2 ! if addr >= KERNBASE 323123eb0b74Storek bgeu Lfsbadaddr ! return error 323223eb0b74Storek EMPTY 323323eb0b74Storek sethi %hi(_cpcb), %o2 ! cpcb->pcb_onfault = _Lfsbail; 323423eb0b74Storek ld [%o2 + %lo(_cpcb)], %o2 323523eb0b74Storek set _Lfsbail, %o3 323623eb0b74Storek st %o3, [%o2 + PCB_ONFAULT] 323723eb0b74Storek lduh [%o0], %o0 ! fetch the halfword 323823eb0b74Storek retl ! made it 323923eb0b74Storek st %g0, [%o2 + PCB_ONFAULT]! but first clear onfault 324023eb0b74Storek 324123eb0b74StorekENTRY(fusword) 324223eb0b74Storek set KERNBASE, %o2 324323eb0b74Storek cmp %o0, %o2 ! if addr >= KERNBASE 324423eb0b74Storek bgeu Lfsbadaddr ! return error 324523eb0b74Storek EMPTY 324623eb0b74Storek sethi %hi(_cpcb), %o2 ! cpcb->pcb_onfault = Lfserr; 324723eb0b74Storek ld [%o2 + %lo(_cpcb)], %o2 324823eb0b74Storek set Lfserr, %o3 324923eb0b74Storek st %o3, [%o2 + PCB_ONFAULT] 325023eb0b74Storek lduh [%o0], %o0 ! fetch the halfword 325123eb0b74Storek retl ! made it 325223eb0b74Storek st %g0, [%o2 + PCB_ONFAULT]! but first clear onfault 325323eb0b74Storek 325423eb0b74StorekALTENTRY(fuibyte) 325523eb0b74StorekENTRY(fubyte) 325623eb0b74Storek set KERNBASE, %o2 325723eb0b74Storek cmp %o0, %o2 ! if addr >= KERNBASE 325823eb0b74Storek bgeu Lfsbadaddr ! return error 325923eb0b74Storek EMPTY 326023eb0b74Storek sethi %hi(_cpcb), %o2 ! cpcb->pcb_onfault = Lfserr; 326123eb0b74Storek ld [%o2 + %lo(_cpcb)], %o2 326223eb0b74Storek set Lfserr, %o3 326323eb0b74Storek st %o3, [%o2 + PCB_ONFAULT] 326423eb0b74Storek ldub [%o0], %o0 ! fetch the byte 326523eb0b74Storek retl ! made it 326623eb0b74Storek st %g0, [%o2 + PCB_ONFAULT]! but first clear onfault 326723eb0b74Storek 326823eb0b74StorekALTENTRY(suiword) 326923eb0b74StorekENTRY(suword) 327023eb0b74Storek set KERNBASE, %o2 327123eb0b74Storek cmp %o0, %o2 ! if addr >= KERNBASE ... 327223eb0b74Storek bgeu Lfsbadaddr 327323eb0b74Storek EMPTY 327423eb0b74Storek btst 3, %o0 ! or has low bits set ... 327523eb0b74Storek bnz Lfsbadaddr ! go return error 327623eb0b74Storek EMPTY 327723eb0b74Storek sethi %hi(_cpcb), %o2 ! cpcb->pcb_onfault = Lfserr; 327823eb0b74Storek ld [%o2 + %lo(_cpcb)], %o2 327923eb0b74Storek set Lfserr, %o3 328023eb0b74Storek st %o3, [%o2 + PCB_ONFAULT] 328123eb0b74Storek st %o1, [%o0] ! store the word 328223eb0b74Storek st %g0, [%o2 + PCB_ONFAULT]! made it, clear onfault 328323eb0b74Storek retl ! and return 0 328423eb0b74Storek clr %o0 328523eb0b74Storek 328623eb0b74StorekENTRY(suswintr) 328723eb0b74Storek set KERNBASE, %o2 328823eb0b74Storek cmp %o0, %o2 ! if addr >= KERNBASE 328923eb0b74Storek bgeu Lfsbadaddr ! go return error 329023eb0b74Storek EMPTY 329123eb0b74Storek sethi %hi(_cpcb), %o2 ! cpcb->pcb_onfault = _Lfsbail; 329223eb0b74Storek ld [%o2 + %lo(_cpcb)], %o2 329323eb0b74Storek set _Lfsbail, %o3 329423eb0b74Storek st %o3, [%o2 + PCB_ONFAULT] 329523eb0b74Storek sth %o1, [%o0] ! store the halfword 329623eb0b74Storek st %g0, [%o2 + PCB_ONFAULT]! made it, clear onfault 329723eb0b74Storek retl ! and return 0 329823eb0b74Storek clr %o0 329923eb0b74Storek 330023eb0b74StorekENTRY(susword) 330123eb0b74Storek set KERNBASE, %o2 330223eb0b74Storek cmp %o0, %o2 ! if addr >= KERNBASE 330323eb0b74Storek bgeu Lfsbadaddr ! go return error 330423eb0b74Storek EMPTY 330523eb0b74Storek sethi %hi(_cpcb), %o2 ! cpcb->pcb_onfault = Lfserr; 330623eb0b74Storek ld [%o2 + %lo(_cpcb)], %o2 330723eb0b74Storek set Lfserr, %o3 330823eb0b74Storek st %o3, [%o2 + PCB_ONFAULT] 330923eb0b74Storek sth %o1, [%o0] ! store the halfword 331023eb0b74Storek st %g0, [%o2 + PCB_ONFAULT]! made it, clear onfault 331123eb0b74Storek retl ! and return 0 331223eb0b74Storek clr %o0 331323eb0b74Storek 331423eb0b74StorekALTENTRY(suibyte) 331523eb0b74StorekENTRY(subyte) 331623eb0b74Storek set KERNBASE, %o2 331723eb0b74Storek cmp %o0, %o2 ! if addr >= KERNBASE 331823eb0b74Storek bgeu Lfsbadaddr ! go return error 331923eb0b74Storek EMPTY 332023eb0b74Storek sethi %hi(_cpcb), %o2 ! cpcb->pcb_onfault = Lfserr; 332123eb0b74Storek ld [%o2 + %lo(_cpcb)], %o2 332223eb0b74Storek set Lfserr, %o3 332323eb0b74Storek st %o3, [%o2 + PCB_ONFAULT] 332423eb0b74Storek stb %o1, [%o0] ! store the byte 332523eb0b74Storek st %g0, [%o2 + PCB_ONFAULT]! made it, clear onfault 332623eb0b74Storek retl ! and return 0 332723eb0b74Storek clr %o0 332823eb0b74Storek 332923eb0b74Storek/* probeget and probeset are meant to be used during autoconfiguration */ 333023eb0b74Storek 333123eb0b74Storek/* 333223eb0b74Storek * probeget(addr, size) caddr_t addr; int size; 333323eb0b74Storek * 333423eb0b74Storek * Read or write a (byte,word,longword) from the given address. 333523eb0b74Storek * Like {fu,su}{byte,halfword,word} but our caller is supposed 333623eb0b74Storek * to know what he is doing... the address can be anywhere. 333723eb0b74Storek * 333823eb0b74Storek * We optimize for space, rather than time, here. 333923eb0b74Storek */ 334023eb0b74StorekENTRY(probeget) 334123eb0b74Storek ! %o0 = addr, %o1 = (1,2,4) 334223eb0b74Storek set KERNBASE, %o2 334323eb0b74Storek cmp %o0, %o2 ! if addr < KERNBASE 334423eb0b74Storek blu Lfsbadaddr ! go return error 334523eb0b74Storek EMPTY 334623eb0b74Storek sethi %hi(_cpcb), %o2 334723eb0b74Storek ld [%o2 + %lo(_cpcb)], %o2 ! cpcb->pcb_onfault = Lfserr; 334823eb0b74Storek set Lfserr, %o5 334923eb0b74Storek st %o5, [%o2 + PCB_ONFAULT] 335023eb0b74Storek btst 1, %o1 335123eb0b74Storek bnz,a 0f ! if (len & 1) 335223eb0b74Storek ldub [%o0], %o0 ! value = *(char *)addr; 335323eb0b74Storek0: btst 2, %o1 335423eb0b74Storek bnz,a 0f ! if (len & 2) 335523eb0b74Storek lduh [%o0], %o0 ! value = *(short *)addr; 335623eb0b74Storek0: btst 4, %o1 335723eb0b74Storek bnz,a 0f ! if (len & 4) 335823eb0b74Storek ld [%o0], %o0 ! value = *(int *)addr; 335923eb0b74Storek0: retl ! made it, clear onfault and return 336023eb0b74Storek st %g0, [%o2 + PCB_ONFAULT] 336123eb0b74Storek 336223eb0b74Storek/* 336323eb0b74Storek * probeset(addr, size, val) caddr_t addr; int size, val; 336423eb0b74Storek * 336523eb0b74Storek * As above, but we return 0 on success. 336623eb0b74Storek */ 336723eb0b74StorekENTRY(probeset) 336823eb0b74Storek ! %o0 = addr, %o1 = (1,2,4), %o2 = val 336923eb0b74Storek set KERNBASE, %o2 337023eb0b74Storek cmp %o0, %o2 ! if addr < KERNBASE 337123eb0b74Storek blu Lfsbadaddr ! go return error 337223eb0b74Storek EMPTY 337323eb0b74Storek sethi %hi(_cpcb), %o2 337423eb0b74Storek ld [%o2 + %lo(_cpcb)], %o2 ! cpcb->pcb_onfault = Lfserr; 337523eb0b74Storek set Lfserr, %o5 337623eb0b74Storek st %o5, [%o2 + PCB_ONFAULT] 337723eb0b74Storek btst 1, %o1 337823eb0b74Storek bnz,a 0f ! if (len & 1) 337923eb0b74Storek stb %o2, [%o0] ! *(char *)addr = value; 338023eb0b74Storek0: btst 2, %o1 338123eb0b74Storek bnz,a 0f ! if (len & 2) 338223eb0b74Storek sth %o2, [%o0] ! *(short *)addr = value; 338323eb0b74Storek0: btst 4, %o1 338423eb0b74Storek bnz,a 0f ! if (len & 4) 338523eb0b74Storek st %o2, [%o0] ! *(int *)addr = value; 338623eb0b74Storek0: clr %o0 ! made it, clear onfault and return 0 338723eb0b74Storek retl 338823eb0b74Storek st %g0, [%o2 + PCB_ONFAULT] 338923eb0b74Storek 339023eb0b74Storek/* 339123eb0b74Storek * Insert entry into doubly-linked queue. 339223eb0b74Storek * We could just do this in C, but gcc does not do leaves well (yet). 339323eb0b74Storek */ 339423eb0b74StorekENTRY(_insque) 339523eb0b74Storek ! %o0 = e = what to insert; %o1 = after = entry to insert after 339623eb0b74Storek st %o1, [%o0 + 4] ! e->prev = after; 339723eb0b74Storek ld [%o1], %o2 ! tmp = after->next; 339823eb0b74Storek st %o2, [%o0] ! e->next = tmp; 339923eb0b74Storek st %o0, [%o1] ! after->next = e; 340023eb0b74Storek retl 340123eb0b74Storek st %o0, [%o2 + 4] ! tmp->prev = e; 340223eb0b74Storek 340323eb0b74Storek 340423eb0b74Storek/* 340523eb0b74Storek * Remove entry from doubly-linked queue. 340623eb0b74Storek */ 340723eb0b74StorekENTRY(_remque) 340823eb0b74Storek ! %o0 = e = what to remove 340923eb0b74Storek ld [%o0], %o1 ! n = e->next; 341023eb0b74Storek ld [%o0 + 4], %o2 ! p = e->prev; 341123eb0b74Storek st %o2, [%o1 + 4] ! n->prev = p; 341223eb0b74Storek retl 341323eb0b74Storek st %o1, [%o2] ! p->next = n; 341423eb0b74Storek 341523eb0b74Storek/* 341623eb0b74Storek * copywords(src, dst, nbytes) 341723eb0b74Storek * 341823eb0b74Storek * Copy `nbytes' bytes from src to dst, both of which are word-aligned; 341923eb0b74Storek * nbytes is a multiple of four. It may, however, be zero, in which case 342023eb0b74Storek * nothing is to be copied. 342123eb0b74Storek */ 342223eb0b74StorekENTRY(copywords) 342323eb0b74Storek ! %o0 = src, %o1 = dst, %o2 = nbytes 342423eb0b74Storek b 1f 342523eb0b74Storek deccc 4, %o2 342623eb0b74Storek0: 342723eb0b74Storek st %o3, [%o1 + %o2] 342823eb0b74Storek deccc 4, %o2 ! while ((n -= 4) >= 0) 342923eb0b74Storek1: 343023eb0b74Storek bge,a 0b ! *(int *)(dst+n) = *(int *)(src+n); 343123eb0b74Storek ld [%o0 + %o2], %o3 343223eb0b74Storek retl 343323eb0b74Storek nop 343423eb0b74Storek 343523eb0b74Storek/* 343623eb0b74Storek * qcopy(src, dst, nbytes) 343723eb0b74Storek * 343823eb0b74Storek * (q for `quad' or `quick', as opposed to b for byte/block copy) 343923eb0b74Storek * 344023eb0b74Storek * Just like copywords, but everything is multiples of 8. 344123eb0b74Storek */ 344223eb0b74StorekENTRY(qcopy) 344323eb0b74Storek b 1f 344423eb0b74Storek deccc 8, %o2 344523eb0b74Storek0: 344623eb0b74Storek std %o4, [%o1 + %o2] 344723eb0b74Storek deccc 8, %o2 344823eb0b74Storek1: 344923eb0b74Storek bge,a 0b 345023eb0b74Storek ldd [%o0 + %o2], %o4 345123eb0b74Storek retl 345223eb0b74Storek nop 345323eb0b74Storek 345423eb0b74Storek/* 345523eb0b74Storek * qzero(addr, nbytes) 345623eb0b74Storek * 345723eb0b74Storek * Zeroes `nbytes' bytes of a quad-aligned virtual address, 345823eb0b74Storek * where nbytes is itself a multiple of 8. 345923eb0b74Storek */ 346023eb0b74StorekENTRY(qzero) 346123eb0b74Storek ! %o0 = addr, %o1 = len (in bytes) 346223eb0b74Storek clr %g1 346323eb0b74Storek0: 346423eb0b74Storek deccc 8, %o1 ! while ((n =- 8) >= 0) 346523eb0b74Storek bge,a 0b 346623eb0b74Storek std %g0, [%o0 + %o1] ! *(quad *)(addr + n) = 0; 346723eb0b74Storek retl 346823eb0b74Storek nop 346923eb0b74Storek 347023eb0b74Storek/* 347123eb0b74Storek * bzero(addr, len) 347223eb0b74Storek * 347323eb0b74Storek * We should unroll the loop, but at the moment this would 347423eb0b74Storek * gain nothing since the `std' instructions are what limits us. 347523eb0b74Storek */ 347623eb0b74StorekALTENTRY(blkclr) 347723eb0b74StorekENTRY(bzero) 347823eb0b74Storek ! %o0 = addr, %o1 = len 347923eb0b74Storek 348023eb0b74Storek ! Optimize a common case: addr and len are both multiples of 8. 348123eb0b74Storek or %o0, %o1, %o2 348223eb0b74Storek btst 7, %o2 ! ((addr | len) & 7) != 0? 348323eb0b74Storek bnz 1f ! if so, cannot optimize 348423eb0b74Storek clr %g1 ! in any case, we want g1=0 348523eb0b74Storek 348623eb0b74Storek /* `Good' operands, can just store doubles. */ 348723eb0b74Storek0: 348823eb0b74Storek deccc 8, %o1 ! while ((len -= 8) >= 0) 348923eb0b74Storek bge,a 0b 349023eb0b74Storek std %g0, [%o0 + %o1] ! *(quad *)(addr + len) = 0; 349123eb0b74Storek retl 349223eb0b74Storek nop 349323eb0b74Storek 349423eb0b74Storek /* 349523eb0b74Storek * Either the address is unaligned, or the count is not a 349623eb0b74Storek * multiple of 8, or both. We will have to align the address 349723eb0b74Storek * in order to use anything `better' than stb. 349823eb0b74Storek */ 349923eb0b74Storek1: 350023eb0b74Storek cmp %o1, 15 ! len >= 15? 350123eb0b74Storek bge,a Lstd ! yes, use std 350223eb0b74Storek btst 1, %o0 ! (but first check alignment) 350323eb0b74Storek 350423eb0b74Storek ! not enough to bother: do byte-at-a-time loop. 350523eb0b74Storek2: 350623eb0b74Storek deccc %o1 ! while (--len >= 0) 350723eb0b74Storek bge,a 2b 350823eb0b74Storek stb %g0, [%o0 + %o1] ! addr[len] = 0; 350923eb0b74Storek retl 351023eb0b74Storek nop 351123eb0b74Storek 351223eb0b74StorekLstd: 351323eb0b74Storek /* 351423eb0b74Storek * There are at least 15 bytes to zero. 351523eb0b74Storek * We may have to zero some initial stuff to align 351623eb0b74Storek * the address. 351723eb0b74Storek */ 351823eb0b74Storek bz,a 1f ! if (addr & 1) { 351923eb0b74Storek btst 2, %o0 352023eb0b74Storek stb %g0, [%o0] ! *addr = 0; 352123eb0b74Storek inc %o0 ! addr++; 352223eb0b74Storek dec %o1 ! len--; 352323eb0b74Storek btst 2, %o0 ! } 352423eb0b74Storek1: 352523eb0b74Storek bz,a 1f ! if (addr & 2) { 352623eb0b74Storek btst 4, %o0 352723eb0b74Storek sth %g0, [%o0] ! *(short *)addr = 0; 352823eb0b74Storek inc 2, %o0 ! addr += 2; 352923eb0b74Storek dec 2, %o1 ! len -= 2; 353023eb0b74Storek btst 4, %o0 ! } 353123eb0b74Storek1: 353223eb0b74Storek bz 1f ! if (addr & 4) { 353323eb0b74Storek dec 8, %o1 353423eb0b74Storek st %g0, [%o0] ! *(int *)addr = 0; 353523eb0b74Storek inc 4, %o0 ! addr += 4; 353623eb0b74Storek dec 4, %o1 ! len -= 4; 353723eb0b74Storek ! } 353823eb0b74Storek /* 353923eb0b74Storek * Address is double word aligned; len is 8 less than 354023eb0b74Storek * the number of bytes remaining (i.e., len is 0 if 354123eb0b74Storek * the remaining count is 8, 1 if it is 9, etc.). 354223eb0b74Storek */ 354323eb0b74Storek1: 354423eb0b74Storek std %g0, [%o0] ! do { 354523eb0b74Storek2: ! *(quad *)addr = 0; 354623eb0b74Storek inc 8, %o0 ! addr += 8; 354723eb0b74Storek deccc 8, %o1 ! } while ((len -= 8) >= 0); 354823eb0b74Storek bge,a 2b 354923eb0b74Storek std %g0, [%o0] 355023eb0b74Storek 355123eb0b74Storek /* 355223eb0b74Storek * Len is in [-8..-1] where -8 => done, -7 => 1 byte to zero, 355323eb0b74Storek * -6 => two bytes, etc. Mop up this remainder, if any. 355423eb0b74Storek */ 355523eb0b74Storek btst 4, %o1 355623eb0b74Storek bz 1f ! if (len & 4) { 355723eb0b74Storek btst 2, %o1 355823eb0b74Storek st %g0, [%o0] ! *(int *)addr = 0; 355923eb0b74Storek inc 4, %o0 ! addr += 4; 356023eb0b74Storek1: 356123eb0b74Storek bz 1f ! if (len & 2) { 356223eb0b74Storek btst 1, %o1 356323eb0b74Storek sth %g0, [%o0] ! *(short *)addr = 0; 356423eb0b74Storek inc 2, %o0 ! addr += 2; 356523eb0b74Storek1: 356623eb0b74Storek bnz,a 1f ! if (len & 1) 356723eb0b74Storek stb %g0, [%o0] ! *addr = 0; 356823eb0b74Storek1: 356923eb0b74Storek retl 357023eb0b74Storek nop 357123eb0b74Storek 357223eb0b74Storek/* 357323eb0b74Storek * kernel bcopy/memcpy 357423eb0b74Storek * Assumes regions do not overlap; has no useful return value. 357523eb0b74Storek * 357623eb0b74Storek * Must not use %g7 (see copyin/copyout above). 357723eb0b74Storek */ 357823eb0b74Storek 357923eb0b74Storek#define BCOPY_SMALL 32 /* if < 32, copy by bytes */ 358023eb0b74Storek 358123eb0b74StorekENTRY(memcpy) 358223eb0b74Storek /* 358323eb0b74Storek * Swap args for bcopy. Gcc generates calls to memcpy for 358423eb0b74Storek * structure assignments. 358523eb0b74Storek */ 358623eb0b74Storek mov %o0, %o3 358723eb0b74Storek mov %o1, %o0 358823eb0b74Storek mov %o3, %o1 358923eb0b74StorekENTRY(bcopy) 359023eb0b74Storek cmp %o2, BCOPY_SMALL 359123eb0b74StorekLbcopy_start: 359223eb0b74Storek bge,a Lbcopy_fancy ! if >= this many, go be fancy. 359323eb0b74Storek btst 7, %o0 ! (part of being fancy) 359423eb0b74Storek 359523eb0b74Storek /* 359623eb0b74Storek * Not much to copy, just do it a byte at a time. 359723eb0b74Storek */ 359823eb0b74Storek deccc %o2 ! while (--len >= 0) 359923eb0b74Storek bl 1f 360023eb0b74Storek EMPTY 360123eb0b74Storek0: 360223eb0b74Storek inc %o0 360323eb0b74Storek ldsb [%o0 - 1], %o4 ! (++dst)[-1] = *src++; 360423eb0b74Storek stb %o4, [%o1] 360523eb0b74Storek deccc %o2 360623eb0b74Storek bge 0b 360723eb0b74Storek inc %o1 360823eb0b74Storek1: 360923eb0b74Storek retl 361023eb0b74Storek nop 361123eb0b74Storek /* NOTREACHED */ 361223eb0b74Storek 361323eb0b74Storek /* 361423eb0b74Storek * Plenty of data to copy, so try to do it optimally. 361523eb0b74Storek */ 361623eb0b74StorekLbcopy_fancy: 361723eb0b74Storek ! check for common case first: everything lines up. 361823eb0b74Storek! btst 7, %o0 ! done already 361923eb0b74Storek bne 1f 362023eb0b74Storek EMPTY 362123eb0b74Storek btst 7, %o1 362223eb0b74Storek be,a Lbcopy_doubles 362323eb0b74Storek dec 8, %o2 ! if all lined up, len -= 8, goto bcopy_doubes 362423eb0b74Storek 362523eb0b74Storek ! If the low bits match, we can make these line up. 362623eb0b74Storek1: 362723eb0b74Storek xor %o0, %o1, %o3 ! t = src ^ dst; 362823eb0b74Storek btst 1, %o3 ! if (t & 1) { 362923eb0b74Storek be,a 1f 363023eb0b74Storek btst 1, %o0 ! [delay slot: if (src & 1)] 363123eb0b74Storek 363223eb0b74Storek ! low bits do not match, must copy by bytes. 363323eb0b74Storek0: 363423eb0b74Storek ldsb [%o0], %o4 ! do { 363523eb0b74Storek inc %o0 ! (++dst)[-1] = *src++; 363623eb0b74Storek inc %o1 363723eb0b74Storek deccc %o2 363823eb0b74Storek bnz 0b ! } while (--len != 0); 363923eb0b74Storek stb %o4, [%o1 - 1] 364023eb0b74Storek retl 364123eb0b74Storek nop 364223eb0b74Storek /* NOTREACHED */ 364323eb0b74Storek 364423eb0b74Storek ! lowest bit matches, so we can copy by words, if nothing else 364523eb0b74Storek1: 364623eb0b74Storek be,a 1f ! if (src & 1) { 364723eb0b74Storek btst 2, %o3 ! [delay slot: if (t & 2)] 364823eb0b74Storek 364923eb0b74Storek ! although low bits match, both are 1: must copy 1 byte to align 365023eb0b74Storek ldsb [%o0], %o4 ! *dst++ = *src++; 365123eb0b74Storek stb %o4, [%o1] 365223eb0b74Storek inc %o0 365323eb0b74Storek inc %o1 365423eb0b74Storek dec %o2 ! len--; 365523eb0b74Storek btst 2, %o3 ! } [if (t & 2)] 365623eb0b74Storek1: 365723eb0b74Storek be,a 1f ! if (t & 2) { 365823eb0b74Storek btst 2, %o0 ! [delay slot: if (src & 2)] 365923eb0b74Storek dec 2, %o2 ! len -= 2; 366023eb0b74Storek0: 366123eb0b74Storek ldsh [%o0], %o4 ! do { 366223eb0b74Storek sth %o4, [%o1] ! *(short *)dst = *(short *)src; 366323eb0b74Storek inc 2, %o0 ! dst += 2, src += 2; 366423eb0b74Storek deccc 2, %o2 ! } while ((len -= 2) >= 0); 366523eb0b74Storek bge 0b 366623eb0b74Storek inc 2, %o1 366723eb0b74Storek b Lbcopy_mopb ! goto mop_up_byte; 366823eb0b74Storek btst 1, %o2 ! } [delay slot: if (len & 1)] 366923eb0b74Storek /* NOTREACHED */ 367023eb0b74Storek 367123eb0b74Storek ! low two bits match, so we can copy by longwords 367223eb0b74Storek1: 367323eb0b74Storek be,a 1f ! if (src & 2) { 367423eb0b74Storek btst 4, %o3 ! [delay slot: if (t & 4)] 367523eb0b74Storek 367623eb0b74Storek ! although low 2 bits match, they are 10: must copy one short to align 367723eb0b74Storek ldsh [%o0], %o4 ! (*short *)dst = *(short *)src; 367823eb0b74Storek sth %o4, [%o1] 367923eb0b74Storek inc 2, %o0 ! dst += 2; 368023eb0b74Storek inc 2, %o1 ! src += 2; 368123eb0b74Storek dec 2, %o2 ! len -= 2; 368223eb0b74Storek btst 4, %o3 ! } [if (t & 4)] 368323eb0b74Storek1: 368423eb0b74Storek be,a 1f ! if (t & 4) { 368523eb0b74Storek btst 4, %o0 ! [delay slot: if (src & 4)] 368623eb0b74Storek dec 4, %o2 ! len -= 4; 368723eb0b74Storek0: 368823eb0b74Storek ld [%o0], %o4 ! do { 368923eb0b74Storek st %o4, [%o1] ! *(int *)dst = *(int *)src; 369023eb0b74Storek inc 4, %o0 ! dst += 4, src += 4; 369123eb0b74Storek deccc 4, %o2 ! } while ((len -= 4) >= 0); 369223eb0b74Storek bge 0b 369323eb0b74Storek inc 4, %o1 369423eb0b74Storek b Lbcopy_mopw ! goto mop_up_word_and_byte; 369523eb0b74Storek btst 2, %o2 ! } [delay slot: if (len & 2)] 369623eb0b74Storek /* NOTREACHED */ 369723eb0b74Storek 369823eb0b74Storek ! low three bits match, so we can copy by doublewords 369923eb0b74Storek1: 370023eb0b74Storek be 1f ! if (src & 4) { 370123eb0b74Storek dec 8, %o2 ! [delay slot: len -= 8] 370223eb0b74Storek ld [%o0], %o4 ! *(int *)dst = *(int *)src; 370323eb0b74Storek st %o4, [%o1] 370423eb0b74Storek inc 4, %o0 ! dst += 4, src += 4, len -= 4; 370523eb0b74Storek inc 4, %o1 370623eb0b74Storek dec 4, %o2 ! } 370723eb0b74Storek1: 370823eb0b74StorekLbcopy_doubles: 370923eb0b74Storek ldd [%o0], %o4 ! do { 371023eb0b74Storek std %o4, [%o1] ! *(double *)dst = *(double *)src; 371123eb0b74Storek inc 8, %o0 ! dst += 8, src += 8; 371223eb0b74Storek deccc 8, %o2 ! } while ((len -= 8) >= 0); 371323eb0b74Storek bge Lbcopy_doubles 371423eb0b74Storek inc 8, %o1 371523eb0b74Storek 371623eb0b74Storek ! check for a usual case again (save work) 371723eb0b74Storek btst 7, %o2 ! if ((len & 7) == 0) 371823eb0b74Storek be Lbcopy_done ! goto bcopy_done; 371923eb0b74Storek 372023eb0b74Storek btst 4, %o2 ! if ((len & 4)) == 0) 372123eb0b74Storek be,a Lbcopy_mopw ! goto mop_up_word_and_byte; 372223eb0b74Storek btst 2, %o2 ! [delay slot: if (len & 2)] 372323eb0b74Storek ld [%o0], %o4 ! *(int *)dst = *(int *)src; 372423eb0b74Storek st %o4, [%o1] 372523eb0b74Storek inc 4, %o0 ! dst += 4; 372623eb0b74Storek inc 4, %o1 ! src += 4; 372723eb0b74Storek btst 2, %o2 ! } [if (len & 2)] 372823eb0b74Storek 372923eb0b74Storek1: 373023eb0b74Storek ! mop up trailing word (if present) and byte (if present). 373123eb0b74StorekLbcopy_mopw: 373223eb0b74Storek be Lbcopy_mopb ! no word, go mop up byte 373323eb0b74Storek btst 1, %o2 ! [delay slot: if (len & 1)] 373423eb0b74Storek ldsh [%o0], %o4 ! *(short *)dst = *(short *)src; 373523eb0b74Storek be Lbcopy_done ! if ((len & 1) == 0) goto done; 373623eb0b74Storek sth %o4, [%o1] 373723eb0b74Storek ldsb [%o0 + 2], %o4 ! dst[2] = src[2]; 373823eb0b74Storek retl 373923eb0b74Storek stb %o4, [%o1 + 2] 374023eb0b74Storek /* NOTREACHED */ 374123eb0b74Storek 374223eb0b74Storek ! mop up trailing byte (if present). 374323eb0b74StorekLbcopy_mopb: 374423eb0b74Storek bne,a 1f 374523eb0b74Storek ldsb [%o0], %o4 374623eb0b74Storek 374723eb0b74StorekLbcopy_done: 374823eb0b74Storek retl 374923eb0b74Storek nop 375023eb0b74Storek 375123eb0b74Storek1: 375223eb0b74Storek retl 375323eb0b74Storek stb %o4,[%o1] 375423eb0b74Storek/* 375523eb0b74Storek * ovbcopy(src, dst, len): like bcopy, but regions may overlap. 375623eb0b74Storek */ 375723eb0b74StorekENTRY(ovbcopy) 375823eb0b74Storek cmp %o0, %o1 ! src < dst? 375923eb0b74Storek bgeu Lbcopy_start ! no, go copy forwards as via bcopy 376023eb0b74Storek cmp %o2, BCOPY_SMALL! (check length for doublecopy first) 376123eb0b74Storek 376223eb0b74Storek /* 376323eb0b74Storek * Since src comes before dst, and the regions might overlap, 376423eb0b74Storek * we have to do the copy starting at the end and working backwards. 376523eb0b74Storek */ 376623eb0b74Storek add %o2, %o0, %o0 ! src += len 376723eb0b74Storek add %o2, %o1, %o1 ! dst += len 376823eb0b74Storek bge,a Lback_fancy ! if len >= BCOPY_SMALL, go be fancy 376923eb0b74Storek btst 3, %o0 377023eb0b74Storek 377123eb0b74Storek /* 377223eb0b74Storek * Not much to copy, just do it a byte at a time. 377323eb0b74Storek */ 377423eb0b74Storek deccc %o2 ! while (--len >= 0) 377523eb0b74Storek bl 1f 377623eb0b74Storek EMPTY 377723eb0b74Storek0: 377823eb0b74Storek dec %o0 ! *--dst = *--src; 377923eb0b74Storek ldsb [%o0], %o4 378023eb0b74Storek dec %o1 378123eb0b74Storek deccc %o2 378223eb0b74Storek bge 0b 378323eb0b74Storek stb %o4, [%o1] 378423eb0b74Storek1: 378523eb0b74Storek retl 378623eb0b74Storek nop 378723eb0b74Storek 378823eb0b74Storek /* 378923eb0b74Storek * Plenty to copy, try to be optimal. 379023eb0b74Storek * We only bother with word/halfword/byte copies here. 379123eb0b74Storek */ 379223eb0b74StorekLback_fancy: 379323eb0b74Storek! btst 3, %o0 ! done already 379423eb0b74Storek bnz 1f ! if ((src & 3) == 0 && 379523eb0b74Storek btst 3, %o1 ! (dst & 3) == 0) 379623eb0b74Storek bz,a Lback_words ! goto words; 379723eb0b74Storek dec 4, %o2 ! (done early for word copy) 379823eb0b74Storek 379923eb0b74Storek1: 380023eb0b74Storek /* 380123eb0b74Storek * See if the low bits match. 380223eb0b74Storek */ 380323eb0b74Storek xor %o0, %o1, %o3 ! t = src ^ dst; 380423eb0b74Storek btst 1, %o3 380523eb0b74Storek bz,a 3f ! if (t & 1) == 0, can do better 380623eb0b74Storek btst 1, %o0 380723eb0b74Storek 380823eb0b74Storek /* 380923eb0b74Storek * Nope; gotta do byte copy. 381023eb0b74Storek */ 381123eb0b74Storek2: 381223eb0b74Storek dec %o0 ! do { 381323eb0b74Storek ldsb [%o0], %o4 ! *--dst = *--src; 381423eb0b74Storek dec %o1 381523eb0b74Storek deccc %o2 ! } while (--len != 0); 381623eb0b74Storek bnz 2b 381723eb0b74Storek stb %o4, [%o1] 381823eb0b74Storek retl 381923eb0b74Storek nop 382023eb0b74Storek 382123eb0b74Storek3: 382223eb0b74Storek /* 382323eb0b74Storek * Can do halfword or word copy, but might have to copy 1 byte first. 382423eb0b74Storek */ 382523eb0b74Storek! btst 1, %o0 ! done earlier 382623eb0b74Storek bz,a 4f ! if (src & 1) { /* copy 1 byte */ 382723eb0b74Storek btst 2, %o3 ! (done early) 382823eb0b74Storek dec %o0 ! *--dst = *--src; 382923eb0b74Storek ldsb [%o0], %o4 383023eb0b74Storek dec %o1 383123eb0b74Storek stb %o4, [%o1] 383223eb0b74Storek dec %o2 ! len--; 383323eb0b74Storek btst 2, %o3 ! } 383423eb0b74Storek 383523eb0b74Storek4: 383623eb0b74Storek /* 383723eb0b74Storek * See if we can do a word copy ((t&2) == 0). 383823eb0b74Storek */ 383923eb0b74Storek! btst 2, %o3 ! done earlier 384023eb0b74Storek bz,a 6f ! if (t & 2) == 0, can do word copy 384123eb0b74Storek btst 2, %o0 ! (src&2, done early) 384223eb0b74Storek 384323eb0b74Storek /* 384423eb0b74Storek * Gotta do halfword copy. 384523eb0b74Storek */ 384623eb0b74Storek dec 2, %o2 ! len -= 2; 384723eb0b74Storek5: 384823eb0b74Storek dec 2, %o0 ! do { 384923eb0b74Storek ldsh [%o0], %o4 ! src -= 2; 385023eb0b74Storek dec 2, %o1 ! dst -= 2; 385123eb0b74Storek deccc 2, %o0 ! *(short *)dst = *(short *)src; 385223eb0b74Storek bge 5b ! } while ((len -= 2) >= 0); 385323eb0b74Storek sth %o4, [%o1] 385423eb0b74Storek b Lback_mopb ! goto mop_up_byte; 385523eb0b74Storek btst 1, %o2 ! (len&1, done early) 385623eb0b74Storek 385723eb0b74Storek6: 385823eb0b74Storek /* 385923eb0b74Storek * We can do word copies, but we might have to copy 386023eb0b74Storek * one halfword first. 386123eb0b74Storek */ 386223eb0b74Storek! btst 2, %o0 ! done already 386323eb0b74Storek bz 7f ! if (src & 2) { 386423eb0b74Storek dec 4, %o2 ! (len -= 4, done early) 386523eb0b74Storek dec 2, %o0 ! src -= 2, dst -= 2; 386623eb0b74Storek ldsh [%o0], %o4 ! *(short *)dst = *(short *)src; 386723eb0b74Storek dec 2, %o1 386823eb0b74Storek sth %o4, [%o1] 386923eb0b74Storek dec 2, %o2 ! len -= 2; 387023eb0b74Storek ! } 387123eb0b74Storek 387223eb0b74Storek7: 387323eb0b74StorekLback_words: 387423eb0b74Storek /* 387523eb0b74Storek * Do word copies (backwards), then mop up trailing halfword 387623eb0b74Storek * and byte if any. 387723eb0b74Storek */ 387823eb0b74Storek! dec 4, %o2 ! len -= 4, done already 387923eb0b74Storek0: ! do { 388023eb0b74Storek dec 4, %o0 ! src -= 4; 388123eb0b74Storek dec 4, %o1 ! src -= 4; 388223eb0b74Storek ld [%o0], %o4 ! *(int *)dst = *(int *)src; 388323eb0b74Storek deccc 4, %o2 ! } while ((len -= 4) >= 0); 388423eb0b74Storek bge 0b 388523eb0b74Storek st %o4, [%o1] 388623eb0b74Storek 388723eb0b74Storek /* 388823eb0b74Storek * Check for trailing shortword. 388923eb0b74Storek */ 389023eb0b74Storek btst 2, %o2 ! if (len & 2) { 389123eb0b74Storek bz,a 1f 389223eb0b74Storek btst 1, %o2 ! (len&1, done early) 389323eb0b74Storek dec 2, %o0 ! src -= 2, dst -= 2; 389423eb0b74Storek ldsh [%o0], %o4 ! *(short *)dst = *(short *)src; 389523eb0b74Storek dec 2, %o1 389623eb0b74Storek sth %o4, [%o1] ! } 389723eb0b74Storek btst 1, %o2 389823eb0b74Storek 389923eb0b74Storek /* 390023eb0b74Storek * Check for trailing byte. 390123eb0b74Storek */ 390223eb0b74Storek1: 390323eb0b74StorekLback_mopb: 390423eb0b74Storek! btst 1, %o2 ! (done already) 390523eb0b74Storek bnz,a 1f ! if (len & 1) { 390623eb0b74Storek ldsb [%o0 - 1], %o4 ! b = src[-1]; 390723eb0b74Storek retl 390823eb0b74Storek nop 390923eb0b74Storek1: 391023eb0b74Storek retl ! dst[-1] = b; 391123eb0b74Storek stb %o4, [%o1 - 1] ! } 391223eb0b74Storek 391323eb0b74Storek 391423eb0b74Storek/* 391523eb0b74Storek * savefpstate(f) struct fpstate *f; 391623eb0b74Storek * 391723eb0b74Storek * Store the current FPU state. The first `st %fsr' may cause a trap; 391823eb0b74Storek * our trap handler knows how to recover (by `returning' to savefpcont). 391923eb0b74Storek */ 392023eb0b74StorekENTRY(savefpstate) 392123eb0b74Storek rd %psr, %o1 ! enable FP before we begin 392223eb0b74Storek set PSR_EF, %o2 392323eb0b74Storek or %o1, %o2, %o1 392423eb0b74Storek wr %o1, 0, %psr 392523eb0b74Storek /* do some setup work while we wait for PSR_EF to turn on */ 392623eb0b74Storek set FSR_QNE, %o5 ! QNE = 0x2000, too big for immediate 392723eb0b74Storek clr %o3 ! qsize = 0; 392823eb0b74Storek nop ! (still waiting for PSR_EF) 392923eb0b74Storekspecial_fp_store: 393023eb0b74Storek st %fsr, [%o0 + FS_FSR] ! f->fs_fsr = getfsr(); 393123eb0b74Storek /* 393223eb0b74Storek * Even if the preceding instruction did not trap, the queue 393323eb0b74Storek * is not necessarily empty: this state save might be happening 393423eb0b74Storek * because user code tried to store %fsr and took the FPU 393523eb0b74Storek * from `exception pending' mode to `exception' mode. 393623eb0b74Storek * So we still have to check the blasted QNE bit. 393723eb0b74Storek * With any luck it will usually not be set. 393823eb0b74Storek */ 393923eb0b74Storek ld [%o0 + FS_FSR], %o4 ! if (f->fs_fsr & QNE) 394023eb0b74Storek btst %o5, %o4 394123eb0b74Storek bnz Lfp_storeq ! goto storeq; 394223eb0b74Storek std %f0, [%o0 + FS_REGS + (4*0)] ! f->fs_f0 = etc; 394323eb0b74StorekLfp_finish: 394423eb0b74Storek st %o3, [%o0 + FS_QSIZE] ! f->fs_qsize = qsize; 394523eb0b74Storek std %f2, [%o0 + FS_REGS + (4*2)] 394623eb0b74Storek std %f4, [%o0 + FS_REGS + (4*4)] 394723eb0b74Storek std %f6, [%o0 + FS_REGS + (4*6)] 394823eb0b74Storek std %f8, [%o0 + FS_REGS + (4*8)] 394923eb0b74Storek std %f10, [%o0 + FS_REGS + (4*10)] 395023eb0b74Storek std %f12, [%o0 + FS_REGS + (4*12)] 395123eb0b74Storek std %f14, [%o0 + FS_REGS + (4*14)] 395223eb0b74Storek std %f16, [%o0 + FS_REGS + (4*16)] 395323eb0b74Storek std %f18, [%o0 + FS_REGS + (4*18)] 395423eb0b74Storek std %f20, [%o0 + FS_REGS + (4*20)] 395523eb0b74Storek std %f22, [%o0 + FS_REGS + (4*22)] 395623eb0b74Storek std %f24, [%o0 + FS_REGS + (4*24)] 395723eb0b74Storek std %f26, [%o0 + FS_REGS + (4*26)] 395823eb0b74Storek std %f28, [%o0 + FS_REGS + (4*28)] 395923eb0b74Storek retl 396023eb0b74Storek std %f30, [%o0 + FS_REGS + (4*30)] 396123eb0b74Storek 396223eb0b74Storek/* 396323eb0b74Storek * Store the (now known nonempty) FP queue. 396423eb0b74Storek * We have to reread the fsr each time in order to get the new QNE bit. 396523eb0b74Storek */ 396623eb0b74StorekLfp_storeq: 396723eb0b74Storek add %o0, FS_QUEUE, %o1 ! q = &f->fs_queue[0]; 396823eb0b74Storek1: 396923eb0b74Storek std %fq, [%o1 + %o3] ! q[qsize++] = fsr_qfront(); 397023eb0b74Storek st %fsr, [%o0 + FS_FSR] ! reread fsr 397123eb0b74Storek ld [%o0 + FS_FSR], %o4 ! if fsr & QNE, loop 397223eb0b74Storek btst %o5, %o4 397323eb0b74Storek bnz 1b 397423eb0b74Storek inc 8, %o3 397523eb0b74Storek b Lfp_finish ! set qsize and finish storing fregs 397623eb0b74Storek srl %o3, 3, %o3 ! (but first fix qsize) 397723eb0b74Storek 397823eb0b74Storek/* 397923eb0b74Storek * The fsr store trapped. Do it again; this time it will not trap. 398023eb0b74Storek * We could just have the trap handler return to the `st %fsr', but 398123eb0b74Storek * if for some reason it *does* trap, that would lock us into a tight 398223eb0b74Storek * loop. This way we panic instead. Whoopee. 398323eb0b74Storek */ 398423eb0b74Storeksavefpcont: 398523eb0b74Storek b special_fp_store + 4 ! continue 398623eb0b74Storek st %fsr, [%o0 + FS_FSR] ! but first finish the %fsr store 398723eb0b74Storek 398823eb0b74Storek/* 398923eb0b74Storek * Load FPU state. 399023eb0b74Storek */ 399123eb0b74StorekENTRY(loadfpstate) 399223eb0b74Storek rd %psr, %o1 ! enable FP before we begin 399323eb0b74Storek set PSR_EF, %o2 399423eb0b74Storek or %o1, %o2, %o1 399523eb0b74Storek wr %o1, 0, %psr 399623eb0b74Storek nop; nop; nop ! paranoia 399723eb0b74Storek ldd [%o0 + FS_REGS + (4*0)], %f0 399823eb0b74Storek ldd [%o0 + FS_REGS + (4*2)], %f2 399923eb0b74Storek ldd [%o0 + FS_REGS + (4*4)], %f4 400023eb0b74Storek ldd [%o0 + FS_REGS + (4*6)], %f6 400123eb0b74Storek ldd [%o0 + FS_REGS + (4*8)], %f8 400223eb0b74Storek ldd [%o0 + FS_REGS + (4*10)], %f10 400323eb0b74Storek ldd [%o0 + FS_REGS + (4*12)], %f12 400423eb0b74Storek ldd [%o0 + FS_REGS + (4*14)], %f14 400523eb0b74Storek ldd [%o0 + FS_REGS + (4*16)], %f16 400623eb0b74Storek ldd [%o0 + FS_REGS + (4*18)], %f18 400723eb0b74Storek ldd [%o0 + FS_REGS + (4*20)], %f20 400823eb0b74Storek ldd [%o0 + FS_REGS + (4*22)], %f22 400923eb0b74Storek ldd [%o0 + FS_REGS + (4*24)], %f24 401023eb0b74Storek ldd [%o0 + FS_REGS + (4*26)], %f26 401123eb0b74Storek ldd [%o0 + FS_REGS + (4*28)], %f28 401223eb0b74Storek ldd [%o0 + FS_REGS + (4*30)], %f30 401323eb0b74Storek retl 401423eb0b74Storek ld [%o0 + FS_FSR], %fsr ! setfsr(f->fs_fsr); 401523eb0b74Storek 401623eb0b74Storek/* 401723eb0b74Storek * ienab_bis(bis) int bis; 401823eb0b74Storek * ienab_bic(bic) int bic; 401923eb0b74Storek * 402023eb0b74Storek * Set and clear bits in the interrupt register. 402123eb0b74Storek * Since there are no read-modify-write instructions for this, 402223eb0b74Storek * and one of the interrupts is nonmaskable, we must disable traps. 402323eb0b74Storek * 402423eb0b74Storek * NB: ___main is defined here for gcc-2 idiocy. Ignore it. 402523eb0b74Storek */ 402623eb0b74StorekENTRY(ienab_bis) 402723eb0b74Storek ! %o0 = bits to set 402823eb0b74Storek rd %psr, %o2 402923eb0b74Storek wr %o2, PSR_ET, %psr ! disable traps 403023eb0b74Storek nop; nop ! 3-instr delay until ET turns off 403123eb0b74Storek sethi %hi(IE_reg_addr), %o3 403223eb0b74Storek ldub [%o3 + %lo(IE_reg_addr)], %o4 403323eb0b74Storek or %o4, %o0, %o4 ! *IE_reg_addr |= bis; 403423eb0b74Storek stb %o4, [%o3 + %lo(IE_reg_addr)] 403523eb0b74Storek wr %o2, 0, %psr ! reenable traps 403623eb0b74Storek nop 403723eb0b74Storek .globl ___main 403823eb0b74Storek___main: 403923eb0b74Storek retl 404023eb0b74Storek nop 404123eb0b74Storek 404223eb0b74StorekENTRY(ienab_bic) 404323eb0b74Storek ! %o0 = bits to clear 404423eb0b74Storek rd %psr, %o2 404523eb0b74Storek wr %o2, PSR_ET, %psr ! disable traps 404623eb0b74Storek nop; nop 404723eb0b74Storek sethi %hi(IE_reg_addr), %o3 404823eb0b74Storek ldub [%o3 + %lo(IE_reg_addr)], %o4 404923eb0b74Storek andn %o4, %o0, %o4 ! *IE_reg_addr &=~ bic; 405023eb0b74Storek stb %o4, [%o3 + %lo(IE_reg_addr)] 405123eb0b74Storek wr %o2, 0, %psr ! reenable traps 405223eb0b74Storek nop 405323eb0b74Storek retl 405423eb0b74Storek nop 405523eb0b74Storek 405623eb0b74Storek/* 405723eb0b74Storek * ffs(), using table lookup. 405823eb0b74Storek * The process switch code shares the table, so we just put the 405923eb0b74Storek * whole thing here. 406023eb0b74Storek */ 406123eb0b74Storekffstab: 406223eb0b74Storek .byte -24,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* 00-0f */ 406323eb0b74Storek .byte 5,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* 10-1f */ 406423eb0b74Storek .byte 6,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* 20-2f */ 406523eb0b74Storek .byte 5,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* 30-3f */ 406623eb0b74Storek .byte 7,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* 40-4f */ 406723eb0b74Storek .byte 5,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* 50-5f */ 406823eb0b74Storek .byte 6,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* 60-6f */ 406923eb0b74Storek .byte 5,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* 70-7f */ 407023eb0b74Storek .byte 8,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* 80-8f */ 407123eb0b74Storek .byte 5,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* 10-9f */ 407223eb0b74Storek .byte 6,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* a0-af */ 407323eb0b74Storek .byte 5,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* b0-bf */ 407423eb0b74Storek .byte 7,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* c0-cf */ 407523eb0b74Storek .byte 5,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* d0-df */ 407623eb0b74Storek .byte 6,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* e0-ef */ 407723eb0b74Storek .byte 5,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* f0-ff */ 407823eb0b74Storek 407923eb0b74Storek/* 408023eb0b74Storek * We use a table lookup on each byte. 408123eb0b74Storek * 408223eb0b74Storek * In each section below, %o1 is the current byte (0, 1, 2, or 3). 408323eb0b74Storek * The last byte is handled specially: for the first three, 408423eb0b74Storek * if that byte is nonzero, we return the table value 408523eb0b74Storek * (plus 0, 8, or 16 for the byte number), but for the last 408623eb0b74Storek * one, we just return the table value plus 24. This means 408723eb0b74Storek * that ffstab[0] must be -24 so that ffs(0) will return 0. 408823eb0b74Storek */ 408923eb0b74StorekENTRY(ffs) 409023eb0b74Storek set ffstab, %o2 409123eb0b74Storek andcc %o0, 0xff, %o1 ! get low byte 409223eb0b74Storek bz,a 1f ! try again if 0 409323eb0b74Storek srl %o0, 8, %o0 ! delay slot, get ready for next byte 409423eb0b74Storek 409523eb0b74Storek retl ! return ffstab[%o1] 409623eb0b74Storek ldsb [%o2 + %o1], %o0 409723eb0b74Storek 409823eb0b74Storek1: 409923eb0b74Storek andcc %o0, 0xff, %o1 ! byte 1 like byte 0... 410023eb0b74Storek bz,a 2f 410123eb0b74Storek srl %o0, 8, %o0 ! (use delay to prepare for byte 2) 410223eb0b74Storek 410323eb0b74Storek ldsb [%o2 + %o1], %o0 410423eb0b74Storek retl ! return ffstab[%o1] + 8 410523eb0b74Storek add %o0, 8, %o0 410623eb0b74Storek 410723eb0b74Storek2: 410823eb0b74Storek andcc %o0, 0xff, %o1 410923eb0b74Storek bz,a 3f 411023eb0b74Storek srl %o0, 8, %o0 ! (prepare for byte 3) 411123eb0b74Storek 411223eb0b74Storek ldsb [%o2 + %o1], %o0 411323eb0b74Storek retl ! return ffstab[%o1] + 16 411423eb0b74Storek add %o0, 16, %o0 411523eb0b74Storek 411623eb0b74Storek3: ! just return ffstab[%o0] + 24 411723eb0b74Storek ldsb [%o2 + %o0], %o0 411823eb0b74Storek retl 411923eb0b74Storek add %o0, 24, %o0 412023eb0b74Storek 412123eb0b74Storek/* 412223eb0b74Storek * Here is a very good random number generator. This implementation is 412323eb0b74Storek * based on ``Two Fast Implementations of the "Minimal Standard" Random 412423eb0b74Storek * Number Generator", David G. Carta, Communications of the ACM, Jan 1990, 412523eb0b74Storek * Vol 33 No 1. 412623eb0b74Storek */ 412723eb0b74Storek .data 412823eb0b74Storekrandseed: 412923eb0b74Storek .word 1 413023eb0b74Storek .text 413123eb0b74StorekENTRY(random) 413223eb0b74Storek sethi %hi(16807), %o1 413323eb0b74Storek wr %o1, %lo(16807), %y 413423eb0b74Storek sethi %hi(randseed), %g1 413523eb0b74Storek ld [%g1 + %lo(randseed)], %o0 413623eb0b74Storek andcc %g0, 0, %o2 413723eb0b74Storek mulscc %o2, %o0, %o2 413823eb0b74Storek mulscc %o2, %o0, %o2 413923eb0b74Storek mulscc %o2, %o0, %o2 414023eb0b74Storek mulscc %o2, %o0, %o2 414123eb0b74Storek mulscc %o2, %o0, %o2 414223eb0b74Storek mulscc %o2, %o0, %o2 414323eb0b74Storek mulscc %o2, %o0, %o2 414423eb0b74Storek mulscc %o2, %o0, %o2 414523eb0b74Storek mulscc %o2, %o0, %o2 414623eb0b74Storek mulscc %o2, %o0, %o2 414723eb0b74Storek mulscc %o2, %o0, %o2 414823eb0b74Storek mulscc %o2, %o0, %o2 414923eb0b74Storek mulscc %o2, %o0, %o2 415023eb0b74Storek mulscc %o2, %o0, %o2 415123eb0b74Storek mulscc %o2, %o0, %o2 415223eb0b74Storek mulscc %o2, %g0, %o2 415323eb0b74Storek rd %y, %o3 415423eb0b74Storek srl %o2, 16, %o1 415523eb0b74Storek set 0xffff, %o4 415623eb0b74Storek and %o4, %o2, %o0 415723eb0b74Storek sll %o0, 15, %o0 415823eb0b74Storek srl %o3, 17, %o3 415923eb0b74Storek or %o3, %o0, %o0 416023eb0b74Storek addcc %o0, %o1, %o0 4161266cc840Smckusick bneg 1f 416223eb0b74Storek sethi %hi(0x7fffffff), %o1 416323eb0b74Storek retl 416423eb0b74Storek st %o0, [%g1 + %lo(randseed)] 416523eb0b74Storek1: 416623eb0b74Storek or %o1, %lo(0x7fffffff), %o1 416723eb0b74Storek add %o0, 1, %o0 416823eb0b74Storek and %o1, %o0, %o0 416923eb0b74Storek retl 417023eb0b74Storek st %o0, [%g1 + %lo(randseed)] 417123eb0b74Storek 417223eb0b74Storek/* 417323eb0b74Storek * void microtime(struct timeval *tv) 417423eb0b74Storek * 417523eb0b74Storek * LBL's sparc bsd 'microtime': We don't need to spl (so this routine 417623eb0b74Storek * can be a leaf routine) and we don't keep a 'last' timeval (there 417723eb0b74Storek * can't be two calls to this routine in a microsecond). This seems to 417823eb0b74Storek * be about 20 times faster than the Sun code on an SS-2. - vj 417923eb0b74Storek * 418023eb0b74Storek * Read time values from slowest-changing to fastest-changing, 418123eb0b74Storek * then re-read out to slowest. If the values read before 418223eb0b74Storek * the innermost match those read after, the innermost value 418323eb0b74Storek * is consistent with the outer values. If not, it may not 418423eb0b74Storek * be and we must retry. Typically this loop runs only once; 418523eb0b74Storek * occasionally it runs twice, and only rarely does it run longer. 418623eb0b74Storek */ 418723eb0b74StorekENTRY(microtime) 418823eb0b74Storek sethi %hi(_time), %g2 418923eb0b74Storek sethi %hi(TIMERREG_VA), %g3 419023eb0b74Storek1: 419123eb0b74Storek ldd [%g2+%lo(_time)], %o2 ! time.tv_sec & time.tv_usec 419223eb0b74Storek ld [%g3+%lo(TIMERREG_VA)], %o4 ! usec counter 419323eb0b74Storek ldd [%g2+%lo(_time)], %g4 ! see if time values changed 419423eb0b74Storek cmp %g4, %o2 419523eb0b74Storek bne 1b ! if time.tv_sec changed 419623eb0b74Storek cmp %g5, %o3 419723eb0b74Storek bne 1b ! if time.tv_usec changed 419823eb0b74Storek tst %o4 419923eb0b74Storek 420023eb0b74Storek bpos 2f ! reached limit? 420123eb0b74Storek srl %o4, TMR_SHIFT, %o4 ! convert counter to usec 420223eb0b74Storek sethi %hi(_tick), %g4 ! bump usec by 1 tick 420323eb0b74Storek ld [%g4+%lo(_tick)], %o1 420423eb0b74Storek set TMR_MASK, %g5 420523eb0b74Storek add %o1, %o3, %o3 420623eb0b74Storek and %o4, %g5, %o4 420723eb0b74Storek2: 420823eb0b74Storek add %o4, %o3, %o3 420923eb0b74Storek set 1000000, %g5 ! normalize usec value 421023eb0b74Storek cmp %o3, %g5 421123eb0b74Storek bl,a 3f 421223eb0b74Storek st %o2, [%o0] ! (should be able to std here) 421323eb0b74Storek add %o2, 1, %o2 ! overflow 421423eb0b74Storek sub %o3, %g5, %o3 421523eb0b74Storek st %o2, [%o0] ! (should be able to std here) 421623eb0b74Storek3: 421723eb0b74Storek retl 421823eb0b74Storek st %o3, [%o0+4] 421923eb0b74Storek 422023eb0b74Storek/* 422123eb0b74Storek * This procedure exists to make stdarg functions work correctly. 422223eb0b74Storek * We write the caller's `in' registers into his caller's `arg dump' 422323eb0b74Storek * area. That arg-dump area immediately precedes the argument extension 422423eb0b74Storek * area, resulting in a single contiguous block of memory. 422523eb0b74Storek * 422623eb0b74Storek * This is really the wrong way to do it: the arguments should be written 422723eb0b74Storek * to storage local to the stdarg function, and the stdarg `pick up 422823eb0b74Storek * the next argument' code should pick it up from whichever region is 422923eb0b74Storek * `active' at that point. 423023eb0b74Storek */ 423123eb0b74Storek .globl ___builtin_saveregs 423223eb0b74Storek___builtin_saveregs: 423323eb0b74Storek ! not profiled -- this should be done inline anyway 423423eb0b74Storek ! bleah! the arg dump area is unaligned! cannot std w/o reg/reg moves 423523eb0b74Storek st %i0, [%fp + 0x44] ! fr->fr_argd[0] 423623eb0b74Storek st %i1, [%fp + 0x48] ! fr->fr_argd[1] 423723eb0b74Storek st %i2, [%fp + 0x4c] ! fr->fr_argd[2] 423823eb0b74Storek st %i3, [%fp + 0x50] ! fr->fr_argd[3] 423923eb0b74Storek st %i4, [%fp + 0x54] ! fr->fr_argd[4] 424023eb0b74Storek retl 424123eb0b74Storek st %i5, [%fp + 0x58] ! fr->fr_argd[5] 424223eb0b74Storek 424323eb0b74Storek#ifdef KGDB 424423eb0b74Storek/* 424523eb0b74Storek * Write all windows (user or otherwise), except the current one. 424623eb0b74Storek * 424723eb0b74Storek * THIS COULD BE DONE IN USER CODE 424823eb0b74Storek */ 424923eb0b74StorekENTRY(write_all_windows) 425023eb0b74Storek /* 425123eb0b74Storek * g2 = g1 = nwindows - 1; 425223eb0b74Storek * while (--g1 > 0) save(); 425323eb0b74Storek * while (--g2 > 0) restore(); 425423eb0b74Storek */ 425523eb0b74Storek sethi %hi(_nwindows), %g1 425623eb0b74Storek ld [%g1 + %lo(_nwindows)], %g1 425723eb0b74Storek dec %g1 425823eb0b74Storek mov %g1, %g2 425923eb0b74Storek 426023eb0b74Storek1: deccc %g1 426123eb0b74Storek bg,a 1b 426223eb0b74Storek save %sp, -64, %sp 426323eb0b74Storek 426423eb0b74Storek2: deccc %g2 426523eb0b74Storek bg,a 2b 426623eb0b74Storek restore 426723eb0b74Storek 426823eb0b74Storek retl 426923eb0b74Storek nop 427023eb0b74Storek#endif /* KGDB */ 427123eb0b74Storek 427223eb0b74Storek .data 427323eb0b74Storek .globl _cold 427423eb0b74Storek_cold: 427523eb0b74Storek .word 1 ! cold start flag 427623eb0b74Storek 427723eb0b74Storek .globl _proc0paddr 427823eb0b74Storek_proc0paddr: 427923eb0b74Storek .word _u0 ! KVA of proc0 uarea 428023eb0b74Storek 428123eb0b74Storek/* interrupt counters XXX THESE BELONG ELSEWHERE (if anywhere) */ 428223eb0b74Storek .globl _intrcnt, _eintrcnt, _intrnames, _eintrnames 428323eb0b74Storek_intrnames: 428423eb0b74Storek .asciz "spur" 428523eb0b74Storek .asciz "lev1" 428623eb0b74Storek .asciz "lev2" 428723eb0b74Storek .asciz "lev3" 428823eb0b74Storek .asciz "lev4" 428923eb0b74Storek .asciz "lev5" 429023eb0b74Storek .asciz "lev6" 429123eb0b74Storek .asciz "lev7" 429223eb0b74Storek .asciz "lev8" 429323eb0b74Storek .asciz "lev9" 429423eb0b74Storek .asciz "clock" 429523eb0b74Storek .asciz "lev11" 429623eb0b74Storek .asciz "lev12" 429723eb0b74Storek .asciz "lev13" 429823eb0b74Storek .asciz "prof" 429923eb0b74Storek_eintrnames: 430023eb0b74Storek ALIGN 430123eb0b74Storek_intrcnt: 430223eb0b74Storek .skip 4*15 430323eb0b74Storek_eintrcnt: 430423eb0b74Storek 430523eb0b74Storek .comm _nwindows, 4 430623eb0b74Storek .comm _promvec, 4 430723eb0b74Storek .comm _curproc, 4 430823eb0b74Storek .comm _qs, 32 * 8 430923eb0b74Storek .comm _whichqs, 4 4310