NetBSD/sys/arch/sparc/sparc/locore.s
pk 8de1fd22bc Add sun4m support. Many, many changes..
Note on multi-architecture kernels: most architecture dependent traps are
forked off directly by having a separate trap table for each (sun4,sun4c,sun4m)
This reduces the number of tests on `cputyp' significantly, and it's easier
on the mind. There are still some entry points that need to test `cputyp',
most notably microtime().. yuck.
1996-03-31 23:19:22 +00:00

5786 lines
147 KiB
ArmAsm

/*
* Copyright (c) 1996 Paul Kranenburg
* Copyright (c) 1996
* The President and Fellows of Harvard University. All rights reserved.
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* This software was developed by the Computer Systems Engineering group
* at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
* contributed to Berkeley.
*
* All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Lawrence Berkeley Laboratory.
* This product includes software developed by Harvard University.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* This product includes software developed by Harvard University.
* This product includes software developed by Paul Kranenburg.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)locore.s 8.4 (Berkeley) 12/10/93
*/
#include "assym.h"
#include <machine/param.h>
#include <sparc/sparc/intreg.h>
#include <sparc/sparc/timerreg.h>
#include <sparc/sparc/vaddrs.h>
#ifdef notyet
#include <sparc/dev/zsreg.h>
#endif
#include <machine/ctlreg.h>
#include <machine/psl.h>
#include <machine/signal.h>
#include <machine/trap.h>
#ifdef COMPAT_SVR4
#include <compat/svr4/svr4_syscall.h>
#endif
/*
* GNU assembler does not understand `.empty' directive; Sun assembler
* gripes about labels without it. To allow cross-compilation using
* the Sun assembler, and because .empty directives are useful documentation,
* we use this trick.
*/
#ifdef SUN_AS
#define EMPTY .empty
#else
#define EMPTY /* .empty */
#endif
/* use as needed to align things on longword boundaries */
#define _ALIGN .align 4
/*
* CCFSZ (C Compiler Frame SiZe) is the size of a stack frame required if
* a function is to call C code. It should be just 64, but Sun defined
* their frame with space to hold arguments 0 through 5 (plus some junk),
* and varargs routines (such as printf) demand this, and gcc uses this
* area at times anyway.
*/
#define CCFSZ 96
/*
* A handy macro for maintaining instrumentation counters.
* Note that this clobbers %o0 and %o1. Normal usage is
* something like:
* foointr:
* TRAP_SETUP(...) ! makes %o registers safe
* INCR(_cnt+V_FOO) ! count a foo
*/
#define INCR(what) \
sethi %hi(what), %o0; \
ld [%o0 + %lo(what)], %o1; \
inc %o1; \
st %o1, [%o0 + %lo(what)]
/*
* Another handy macro: load one register window, given `base' address.
* This can be either a simple register (e.g., %sp) or include an initial
* offset (e.g., %g6 + PCB_RW).
*/
#define LOADWIN(addr) \
ldd [addr], %l0; \
ldd [addr + 8], %l2; \
ldd [addr + 16], %l4; \
ldd [addr + 24], %l6; \
ldd [addr + 32], %i0; \
ldd [addr + 40], %i2; \
ldd [addr + 48], %i4; \
ldd [addr + 56], %i6
/*
* To return from trap we need the two-instruction sequence
* `jmp %l1; rett %l2', which is defined here for convenience.
*/
#define RETT jmp %l1; rett %l2
.data
/*
* The interrupt stack.
*
* This is the very first thing in the data segment, and therefore has
* the lowest kernel stack address. We count on this in the interrupt
* trap-frame setup code, since we may need to switch from the kernel
* stack to the interrupt stack (iff we are not already on the interrupt
* stack). One sethi+cmp is all we need since this is so carefully
* arranged.
*/
.globl _intstack
.globl _eintstack
_intstack:
.skip 128 * 128 ! 16k = 128 128-byte stack frames
_eintstack:
/*
* When a process exits and its u. area goes away, we set cpcb to point
* to this `u.', leaving us with something to use for an interrupt stack,
* and letting all the register save code have a pcb_uw to examine.
* This is also carefully arranged (to come just before u0, so that
* process 0's kernel stack can quietly overrun into it during bootup, if
* we feel like doing that).
*/
.globl _idle_u
_idle_u:
.skip USPACE
/*
* Process 0's u.
*
* This must be aligned on an 8 byte boundary.
*/
.globl _u0
_u0: .skip USPACE
estack0:
#ifdef KGDB
/*
* Another item that must be aligned, easiest to put it here.
*/
KGDB_STACK_SIZE = 2048
.globl _kgdb_stack
_kgdb_stack:
.skip KGDB_STACK_SIZE ! hope this is enough
#endif
/*
* _cpcb points to the current pcb (and hence u. area).
* Initially this is the special one.
*/
.globl _cpcb
_cpcb: .word _u0
/*
* _cputyp is the current cpu type, used to distinguish between
* the many variations of different sun4* machines. It contains
* the value CPU_SUN4, CPU_SUN4C, or CPU_SUN4M.
*/
.globl _cputyp
_cputyp:
.word 1
/*
* _cpumod is the current cpu model, used to distinguish between variants
* in the Sun4 and Sun4M families. See /sys/arch/sparc/include/param.h for
* possible values.
*/
.globl _cpumod
_cpumod:
.word 1
/*
* _mmumod is the current mmu model, used to distinguish between the
* various implementations of the SRMMU in the sun4m family of machines.
* See /sys/arch/sparc/include/param.h for possible values.
*/
.globl _mmumod
_mmumod:
.word 0
#if defined(SUN4C) || defined(SUN4M)
_cputypval:
.asciz "sun4c"
.ascii " "
_cputypvar:
.asciz "compatible"
_cputypvallen = _cputypvar - _cputypval
_ALIGN
#endif
/*
* There variables are pointed to by the cpp symbols PGSHIFT, NBPG,
* and PGOFSET.
*/
.globl _pgshift, _nbpg, _pgofset
_pgshift:
.word 0
_nbpg:
.word 0
_pgofset:
.word 0
.globl _trapbase
_trapbase:
.word 0
#if defined(SUN4M)
_mapme:
.asciz "0 0 f8000000 15c6a0 map-pages"
#endif
#if !defined(SUN4M)
sun4m_notsup:
.asciz "cr .( NetBSD/sparc: this kernel does not support the sun4m) cr"
#endif
#if !defined(SUN4C)
sun4c_notsup:
.asciz "cr .( NetBSD/sparc: this kernel does not support the sun4c) cr"
#endif
#if !defined(SUN4)
sun4_notsup:
! the extra characters at the end are to ensure the zs fifo drains
! before we halt. Sick, eh?
.asciz "NetBSD/sparc: this kernel does not support the sun4\n\r \b"
#endif
_ALIGN
.text
/*
* The first thing in the real text segment is the trap vector table,
* which must be aligned on a 4096 byte boundary. The text segment
* starts beyond page 0 of KERNBASE so that there is a red zone
* between user and kernel space. Since the boot ROM loads us at
* 0x4000, it is far easier to start at KERNBASE+0x4000 than to
* buck the trend. This is two or four pages in (depending on if
* pagesize is 8192 or 4096). We place two items in this area:
* the message buffer (phys addr 0) and the IE_reg (phys addr 0x2000).
* because the message buffer is in our "red zone" between user and
* kernel space we remap it in configure() to another location and
* invalidate the mapping at KERNBASE.
*/
.globl _msgbuf
_msgbuf = KERNBASE
/*
* We need to map the interrupt enable register very early on in the
* boot process, so that we can handle NMIs (parity errors) halfway
* sensibly during boot. We use virtual address f8002000 (`page 2')
* for this, wasting a page of physical memory.
*
* This doesn't work for the Sun4M, which can have 5 or more pages of
* registers. Thus we use a reserved piece of the virtual address space, set
* up in bootstrap().
*/
IE_reg_addr = KERNBASE + 8192 ! this page not used; points to IEreg
/*
* Each trap has room for four instructions, of which one perforce must
* be a branch. On entry the hardware has copied pc and npc to %l1 and
* %l2 respectively. We use two more to read the psr into %l0, and to
* put the trap type value into %l3 (with a few exceptions below).
* We could read the trap type field of %tbr later in the code instead,
* but there is no need, and that would require more instructions
* (read+mask, vs 1 `mov' here).
*
* I used to generate these numbers by address arithmetic, but gas's
* expression evaluator has about as much sense as your average slug
* (oddly enough, the code looks about as slimy too). Thus, all the
* trap numbers are given as arguments to the trap macros. This means
* there is one line per trap. Sigh.
*
* Note that only the local registers may be used, since the trap
* window is potentially the last window. Its `in' registers are
* the previous window's outs (as usual), but more important, its
* `out' registers may be in use as the `topmost' window's `in' registers.
* The global registers are of course verboten (well, until we save
* them away).
*
* Hardware interrupt vectors can be `linked'---the linkage is to regular
* C code---or rewired to fast in-window handlers. The latter are good
* for unbuffered hardware like the Zilog serial chip and the AMD audio
* chip, where many interrupts can be handled trivially with pseudo-DMA or
* similar. Only one `fast' interrupt can be used per level, however, and
* direct and `fast' interrupts are incompatible. Routines in intr.c
* handle setting these, with optional paranoia.
*/
/* regular vectored traps */
#define VTRAP(type, label) \
mov (type), %l3; b label; mov %psr, %l0; nop
/* hardware interrupts (can be linked or made `fast') */
#define HARDINT44C(lev) \
mov (lev), %l3; b _sparc_interrupt44c; mov %psr, %l0; nop
/* hardware interrupts (can be linked or made `fast') */
#define HARDINT4M(lev) \
mov (lev), %l3; b _sparc_interrupt4m; mov %psr, %l0; nop
/* software interrupts (may not be made direct, sorry---but you
should not be using them trivially anyway) */
#define SOFTINT44C(lev, bit) \
mov (lev), %l3; mov (bit), %l4; b softintr_sun44c; mov %psr, %l0
/* There's no SOFTINT4M(): both hard and soft vector the same way */
/* traps that just call trap() */
#define TRAP(type) VTRAP(type, slowtrap)
/* architecturally undefined traps (cause panic) */
#define UTRAP(type) VTRAP(type, slowtrap)
/* software undefined traps (may be replaced) */
#define STRAP(type) VTRAP(type, slowtrap)
/* breakpoint acts differently under kgdb */
#ifdef KGDB
#define BPT VTRAP(T_BREAKPOINT, bpt)
#define BPT_KGDB_EXEC VTRAP(T_KGDB_EXEC, bpt)
#else
#define BPT TRAP(T_BREAKPOINT)
#define BPT_KGDB_EXEC TRAP(T_KGDB_EXEC)
#endif
/* special high-speed 1-instruction-shaved-off traps (get nothing in %l3) */
#define SYSCALL b syscall; mov %psr, %l0; nop; nop
#define WINDOW_OF b window_of; mov %psr, %l0; nop; nop
#define WINDOW_UF b window_uf; mov %psr, %l0; nop; nop
#ifdef notyet
#define ZS_INTERRUPT b zshard; mov %psr, %l0; nop; nop
#else
#define ZS_INTERRUPT44C HARDINT44C(12)
#define ZS_INTERRUPT4M HARDINT4M(12)
#endif
.globl start
start:
/*
* Put sun4 traptable first, since it needs the most stringent aligment (8192)
*/
#if defined(SUN4)
trapbase_sun4:
/* trap 0 is special since we cannot receive it */
b dostart; nop; nop; nop ! 00 = reset (fake)
VTRAP(T_TEXTFAULT, memfault_sun4) ! 01 = instr. fetch fault
TRAP(T_ILLINST) ! 02 = illegal instruction
TRAP(T_PRIVINST) ! 03 = privileged instruction
TRAP(T_FPDISABLED) ! 04 = fp instr, but EF bit off in psr
WINDOW_OF ! 05 = window overflow
WINDOW_UF ! 06 = window underflow
TRAP(T_ALIGN) ! 07 = address alignment error
VTRAP(T_FPE, fp_exception) ! 08 = fp exception
VTRAP(T_DATAFAULT, memfault_sun4) ! 09 = data fetch fault
TRAP(T_TAGOF) ! 0a = tag overflow
UTRAP(0x0b)
UTRAP(0x0c)
UTRAP(0x0d)
UTRAP(0x0e)
UTRAP(0x0f)
UTRAP(0x10)
SOFTINT44C(1, IE_L1) ! 11 = level 1 interrupt
HARDINT44C(2) ! 12 = level 2 interrupt
HARDINT44C(3) ! 13 = level 3 interrupt
SOFTINT44C(4, IE_L4) ! 14 = level 4 interrupt
HARDINT44C(5) ! 15 = level 5 interrupt
SOFTINT44C(6, IE_L6) ! 16 = level 6 interrupt
HARDINT44C(7) ! 17 = level 7 interrupt
HARDINT44C(8) ! 18 = level 8 interrupt
HARDINT44C(9) ! 19 = level 9 interrupt
HARDINT44C(10) ! 1a = level 10 interrupt
HARDINT44C(11) ! 1b = level 11 interrupt
ZS_INTERRUPT44C ! 1c = level 12 (zs) interrupt
HARDINT44C(13) ! 1d = level 13 interrupt
HARDINT44C(14) ! 1e = level 14 interrupt
VTRAP(15, nmi_sun4) ! 1f = nonmaskable interrupt
UTRAP(0x20)
UTRAP(0x21)
UTRAP(0x22)
UTRAP(0x23)
TRAP(T_CPDISABLED) ! 24 = coprocessor instr, EC bit off in psr
UTRAP(0x25)
UTRAP(0x26)
UTRAP(0x27)
TRAP(T_CPEXCEPTION) ! 28 = coprocessor exception
UTRAP(0x29)
UTRAP(0x2a)
UTRAP(0x2b)
UTRAP(0x2c)
UTRAP(0x2d)
UTRAP(0x2e)
UTRAP(0x2f)
UTRAP(0x30)
UTRAP(0x31)
UTRAP(0x32)
UTRAP(0x33)
UTRAP(0x34)
UTRAP(0x35)
UTRAP(0x36)
UTRAP(0x37)
UTRAP(0x38)
UTRAP(0x39)
UTRAP(0x3a)
UTRAP(0x3b)
UTRAP(0x3c)
UTRAP(0x3d)
UTRAP(0x3e)
UTRAP(0x3f)
UTRAP(0x40)
UTRAP(0x41)
UTRAP(0x42)
UTRAP(0x43)
UTRAP(0x44)
UTRAP(0x45)
UTRAP(0x46)
UTRAP(0x47)
UTRAP(0x48)
UTRAP(0x49)
UTRAP(0x4a)
UTRAP(0x4b)
UTRAP(0x4c)
UTRAP(0x4d)
UTRAP(0x4e)
UTRAP(0x4f)
UTRAP(0x50)
UTRAP(0x51)
UTRAP(0x52)
UTRAP(0x53)
UTRAP(0x54)
UTRAP(0x55)
UTRAP(0x56)
UTRAP(0x57)
UTRAP(0x58)
UTRAP(0x59)
UTRAP(0x5a)
UTRAP(0x5b)
UTRAP(0x5c)
UTRAP(0x5d)
UTRAP(0x5e)
UTRAP(0x5f)
UTRAP(0x60)
UTRAP(0x61)
UTRAP(0x62)
UTRAP(0x63)
UTRAP(0x64)
UTRAP(0x65)
UTRAP(0x66)
UTRAP(0x67)
UTRAP(0x68)
UTRAP(0x69)
UTRAP(0x6a)
UTRAP(0x6b)
UTRAP(0x6c)
UTRAP(0x6d)
UTRAP(0x6e)
UTRAP(0x6f)
UTRAP(0x70)
UTRAP(0x71)
UTRAP(0x72)
UTRAP(0x73)
UTRAP(0x74)
UTRAP(0x75)
UTRAP(0x76)
UTRAP(0x77)
UTRAP(0x78)
UTRAP(0x79)
UTRAP(0x7a)
UTRAP(0x7b)
UTRAP(0x7c)
UTRAP(0x7d)
UTRAP(0x7e)
UTRAP(0x7f)
SYSCALL ! 80 = sun syscall
BPT ! 81 = pseudo breakpoint instruction
TRAP(T_DIV0) ! 82 = divide by zero
TRAP(T_FLUSHWIN) ! 83 = flush windows
TRAP(T_CLEANWIN) ! 84 = provide clean windows
TRAP(T_RANGECHECK) ! 85 = ???
TRAP(T_FIXALIGN) ! 86 = fix up unaligned accesses
TRAP(T_INTOF) ! 87 = integer overflow
SYSCALL ! 88 = svr4 syscall
SYSCALL ! 89 = bsd syscall
BPT_KGDB_EXEC ! 8a = enter kernel gdb on kernel startup
STRAP(0x8b)
STRAP(0x8c)
STRAP(0x8d)
STRAP(0x8e)
STRAP(0x8f)
STRAP(0x90)
STRAP(0x91)
STRAP(0x92)
STRAP(0x93)
STRAP(0x94)
STRAP(0x95)
STRAP(0x96)
STRAP(0x97)
STRAP(0x98)
STRAP(0x99)
STRAP(0x9a)
STRAP(0x9b)
STRAP(0x9c)
STRAP(0x9d)
STRAP(0x9e)
STRAP(0x9f)
STRAP(0xa0)
STRAP(0xa1)
STRAP(0xa2)
STRAP(0xa3)
STRAP(0xa4)
STRAP(0xa5)
STRAP(0xa6)
STRAP(0xa7)
STRAP(0xa8)
STRAP(0xa9)
STRAP(0xaa)
STRAP(0xab)
STRAP(0xac)
STRAP(0xad)
STRAP(0xae)
STRAP(0xaf)
STRAP(0xb0)
STRAP(0xb1)
STRAP(0xb2)
STRAP(0xb3)
STRAP(0xb4)
STRAP(0xb5)
STRAP(0xb6)
STRAP(0xb7)
STRAP(0xb8)
STRAP(0xb9)
STRAP(0xba)
STRAP(0xbb)
STRAP(0xbc)
STRAP(0xbd)
STRAP(0xbe)
STRAP(0xbf)
STRAP(0xc0)
STRAP(0xc1)
STRAP(0xc2)
STRAP(0xc3)
STRAP(0xc4)
STRAP(0xc5)
STRAP(0xc6)
STRAP(0xc7)
STRAP(0xc8)
STRAP(0xc9)
STRAP(0xca)
STRAP(0xcb)
STRAP(0xcc)
STRAP(0xcd)
STRAP(0xce)
STRAP(0xcf)
STRAP(0xd0)
STRAP(0xd1)
STRAP(0xd2)
STRAP(0xd3)
STRAP(0xd4)
STRAP(0xd5)
STRAP(0xd6)
STRAP(0xd7)
STRAP(0xd8)
STRAP(0xd9)
STRAP(0xda)
STRAP(0xdb)
STRAP(0xdc)
STRAP(0xdd)
STRAP(0xde)
STRAP(0xdf)
STRAP(0xe0)
STRAP(0xe1)
STRAP(0xe2)
STRAP(0xe3)
STRAP(0xe4)
STRAP(0xe5)
STRAP(0xe6)
STRAP(0xe7)
STRAP(0xe8)
STRAP(0xe9)
STRAP(0xea)
STRAP(0xeb)
STRAP(0xec)
STRAP(0xed)
STRAP(0xee)
STRAP(0xef)
STRAP(0xf0)
STRAP(0xf1)
STRAP(0xf2)
STRAP(0xf3)
STRAP(0xf4)
STRAP(0xf5)
STRAP(0xf6)
STRAP(0xf7)
STRAP(0xf8)
STRAP(0xf9)
STRAP(0xfa)
STRAP(0xfb)
STRAP(0xfc)
STRAP(0xfd)
STRAP(0xfe)
STRAP(0xff)
#endif
#if defined(SUN4C)
trapbase_sun4c:
/* trap 0 is special since we cannot receive it */
b dostart; nop; nop; nop ! 00 = reset (fake)
VTRAP(T_TEXTFAULT, memfault_sun4c) ! 01 = instr. fetch fault
TRAP(T_ILLINST) ! 02 = illegal instruction
TRAP(T_PRIVINST) ! 03 = privileged instruction
TRAP(T_FPDISABLED) ! 04 = fp instr, but EF bit off in psr
WINDOW_OF ! 05 = window overflow
WINDOW_UF ! 06 = window underflow
TRAP(T_ALIGN) ! 07 = address alignment error
VTRAP(T_FPE, fp_exception) ! 08 = fp exception
VTRAP(T_DATAFAULT, memfault_sun4c) ! 09 = data fetch fault
TRAP(T_TAGOF) ! 0a = tag overflow
UTRAP(0x0b)
UTRAP(0x0c)
UTRAP(0x0d)
UTRAP(0x0e)
UTRAP(0x0f)
UTRAP(0x10)
SOFTINT44C(1, IE_L1) ! 11 = level 1 interrupt
HARDINT44C(2) ! 12 = level 2 interrupt
HARDINT44C(3) ! 13 = level 3 interrupt
SOFTINT44C(4, IE_L4) ! 14 = level 4 interrupt
HARDINT44C(5) ! 15 = level 5 interrupt
SOFTINT44C(6, IE_L6) ! 16 = level 6 interrupt
HARDINT44C(7) ! 17 = level 7 interrupt
HARDINT44C(8) ! 18 = level 8 interrupt
HARDINT44C(9) ! 19 = level 9 interrupt
HARDINT44C(10) ! 1a = level 10 interrupt
HARDINT44C(11) ! 1b = level 11 interrupt
ZS_INTERRUPT44C ! 1c = level 12 (zs) interrupt
HARDINT44C(13) ! 1d = level 13 interrupt
HARDINT44C(14) ! 1e = level 14 interrupt
VTRAP(15, nmi_sun4c) ! 1f = nonmaskable interrupt
UTRAP(0x20)
UTRAP(0x21)
UTRAP(0x22)
UTRAP(0x23)
TRAP(T_CPDISABLED) ! 24 = coprocessor instr, EC bit off in psr
UTRAP(0x25)
UTRAP(0x26)
UTRAP(0x27)
TRAP(T_CPEXCEPTION) ! 28 = coprocessor exception
UTRAP(0x29)
UTRAP(0x2a)
UTRAP(0x2b)
UTRAP(0x2c)
UTRAP(0x2d)
UTRAP(0x2e)
UTRAP(0x2f)
UTRAP(0x30)
UTRAP(0x31)
UTRAP(0x32)
UTRAP(0x33)
UTRAP(0x34)
UTRAP(0x35)
UTRAP(0x36)
UTRAP(0x37)
UTRAP(0x38)
UTRAP(0x39)
UTRAP(0x3a)
UTRAP(0x3b)
UTRAP(0x3c)
UTRAP(0x3d)
UTRAP(0x3e)
UTRAP(0x3f)
UTRAP(0x40)
UTRAP(0x41)
UTRAP(0x42)
UTRAP(0x43)
UTRAP(0x44)
UTRAP(0x45)
UTRAP(0x46)
UTRAP(0x47)
UTRAP(0x48)
UTRAP(0x49)
UTRAP(0x4a)
UTRAP(0x4b)
UTRAP(0x4c)
UTRAP(0x4d)
UTRAP(0x4e)
UTRAP(0x4f)
UTRAP(0x50)
UTRAP(0x51)
UTRAP(0x52)
UTRAP(0x53)
UTRAP(0x54)
UTRAP(0x55)
UTRAP(0x56)
UTRAP(0x57)
UTRAP(0x58)
UTRAP(0x59)
UTRAP(0x5a)
UTRAP(0x5b)
UTRAP(0x5c)
UTRAP(0x5d)
UTRAP(0x5e)
UTRAP(0x5f)
UTRAP(0x60)
UTRAP(0x61)
UTRAP(0x62)
UTRAP(0x63)
UTRAP(0x64)
UTRAP(0x65)
UTRAP(0x66)
UTRAP(0x67)
UTRAP(0x68)
UTRAP(0x69)
UTRAP(0x6a)
UTRAP(0x6b)
UTRAP(0x6c)
UTRAP(0x6d)
UTRAP(0x6e)
UTRAP(0x6f)
UTRAP(0x70)
UTRAP(0x71)
UTRAP(0x72)
UTRAP(0x73)
UTRAP(0x74)
UTRAP(0x75)
UTRAP(0x76)
UTRAP(0x77)
UTRAP(0x78)
UTRAP(0x79)
UTRAP(0x7a)
UTRAP(0x7b)
UTRAP(0x7c)
UTRAP(0x7d)
UTRAP(0x7e)
UTRAP(0x7f)
SYSCALL ! 80 = sun syscall
BPT ! 81 = pseudo breakpoint instruction
TRAP(T_DIV0) ! 82 = divide by zero
TRAP(T_FLUSHWIN) ! 83 = flush windows
TRAP(T_CLEANWIN) ! 84 = provide clean windows
TRAP(T_RANGECHECK) ! 85 = ???
TRAP(T_FIXALIGN) ! 86 = fix up unaligned accesses
TRAP(T_INTOF) ! 87 = integer overflow
SYSCALL ! 88 = svr4 syscall
SYSCALL ! 89 = bsd syscall
BPT_KGDB_EXEC ! 8a = enter kernel gdb on kernel startup
STRAP(0x8b)
STRAP(0x8c)
STRAP(0x8d)
STRAP(0x8e)
STRAP(0x8f)
STRAP(0x90)
STRAP(0x91)
STRAP(0x92)
STRAP(0x93)
STRAP(0x94)
STRAP(0x95)
STRAP(0x96)
STRAP(0x97)
STRAP(0x98)
STRAP(0x99)
STRAP(0x9a)
STRAP(0x9b)
STRAP(0x9c)
STRAP(0x9d)
STRAP(0x9e)
STRAP(0x9f)
STRAP(0xa0)
STRAP(0xa1)
STRAP(0xa2)
STRAP(0xa3)
STRAP(0xa4)
STRAP(0xa5)
STRAP(0xa6)
STRAP(0xa7)
STRAP(0xa8)
STRAP(0xa9)
STRAP(0xaa)
STRAP(0xab)
STRAP(0xac)
STRAP(0xad)
STRAP(0xae)
STRAP(0xaf)
STRAP(0xb0)
STRAP(0xb1)
STRAP(0xb2)
STRAP(0xb3)
STRAP(0xb4)
STRAP(0xb5)
STRAP(0xb6)
STRAP(0xb7)
STRAP(0xb8)
STRAP(0xb9)
STRAP(0xba)
STRAP(0xbb)
STRAP(0xbc)
STRAP(0xbd)
STRAP(0xbe)
STRAP(0xbf)
STRAP(0xc0)
STRAP(0xc1)
STRAP(0xc2)
STRAP(0xc3)
STRAP(0xc4)
STRAP(0xc5)
STRAP(0xc6)
STRAP(0xc7)
STRAP(0xc8)
STRAP(0xc9)
STRAP(0xca)
STRAP(0xcb)
STRAP(0xcc)
STRAP(0xcd)
STRAP(0xce)
STRAP(0xcf)
STRAP(0xd0)
STRAP(0xd1)
STRAP(0xd2)
STRAP(0xd3)
STRAP(0xd4)
STRAP(0xd5)
STRAP(0xd6)
STRAP(0xd7)
STRAP(0xd8)
STRAP(0xd9)
STRAP(0xda)
STRAP(0xdb)
STRAP(0xdc)
STRAP(0xdd)
STRAP(0xde)
STRAP(0xdf)
STRAP(0xe0)
STRAP(0xe1)
STRAP(0xe2)
STRAP(0xe3)
STRAP(0xe4)
STRAP(0xe5)
STRAP(0xe6)
STRAP(0xe7)
STRAP(0xe8)
STRAP(0xe9)
STRAP(0xea)
STRAP(0xeb)
STRAP(0xec)
STRAP(0xed)
STRAP(0xee)
STRAP(0xef)
STRAP(0xf0)
STRAP(0xf1)
STRAP(0xf2)
STRAP(0xf3)
STRAP(0xf4)
STRAP(0xf5)
STRAP(0xf6)
STRAP(0xf7)
STRAP(0xf8)
STRAP(0xf9)
STRAP(0xfa)
STRAP(0xfb)
STRAP(0xfc)
STRAP(0xfd)
STRAP(0xfe)
STRAP(0xff)
#endif
#if defined(SUN4M)
trapbase_sun4m:
/* trap 0 is special since we cannot receive it */
b dostart; nop; nop; nop ! 00 = reset (fake)
VTRAP(T_TEXTFAULT, memfault_sun4m) ! 01 = instr. fetch fault
TRAP(T_ILLINST) ! 02 = illegal instruction
TRAP(T_PRIVINST) ! 03 = privileged instruction
TRAP(T_FPDISABLED) ! 04 = fp instr, but EF bit off in psr
WINDOW_OF ! 05 = window overflow
WINDOW_UF ! 06 = window underflow
TRAP(T_ALIGN) ! 07 = address alignment error
VTRAP(T_FPE, fp_exception) ! 08 = fp exception
VTRAP(T_DATAFAULT, memfault_sun4m) ! 09 = data fetch fault
TRAP(T_TAGOF) ! 0a = tag overflow
UTRAP(0x0b)
UTRAP(0x0c)
UTRAP(0x0d)
UTRAP(0x0e)
UTRAP(0x0f)
UTRAP(0x10)
HARDINT4M(1) ! 11 = level 1 interrupt
HARDINT4M(2) ! 12 = level 2 interrupt
HARDINT4M(3) ! 13 = level 3 interrupt
HARDINT4M(4) ! 14 = level 4 interrupt
HARDINT4M(5) ! 15 = level 5 interrupt
HARDINT4M(6) ! 16 = level 6 interrupt
HARDINT4M(7) ! 17 = level 7 interrupt
HARDINT4M(8) ! 18 = level 8 interrupt
HARDINT4M(9) ! 19 = level 9 interrupt
HARDINT4M(10) ! 1a = level 10 interrupt
HARDINT4M(11) ! 1b = level 11 interrupt
ZS_INTERRUPT4M ! 1c = level 12 (zs) interrupt
HARDINT4M(13) ! 1d = level 13 interrupt
HARDINT4M(14) ! 1e = level 14 interrupt
VTRAP(15, nmi_sun4m) ! 1f = nonmaskable interrupt
UTRAP(0x20)
UTRAP(0x21)
UTRAP(0x22)
UTRAP(0x23)
TRAP(T_CPDISABLED) ! 24 = coprocessor instr, EC bit off in psr
UTRAP(0x25)
UTRAP(0x26)
UTRAP(0x27)
TRAP(T_CPEXCEPTION) ! 28 = coprocessor exception
UTRAP(0x29)
UTRAP(0x2a)
VTRAP(T_STOREBUFFAULT, memfault_sun4m) ! 2b = SuperSPARC store buffer fault
UTRAP(0x2c)
UTRAP(0x2d)
UTRAP(0x2e)
UTRAP(0x2f)
UTRAP(0x30)
UTRAP(0x31)
UTRAP(0x32)
UTRAP(0x33)
UTRAP(0x34)
UTRAP(0x35)
UTRAP(0x36)
UTRAP(0x37)
UTRAP(0x38)
UTRAP(0x39)
UTRAP(0x3a)
UTRAP(0x3b)
UTRAP(0x3c)
UTRAP(0x3d)
UTRAP(0x3e)
UTRAP(0x3f)
UTRAP(0x40)
UTRAP(0x41)
UTRAP(0x42)
UTRAP(0x43)
UTRAP(0x44)
UTRAP(0x45)
UTRAP(0x46)
UTRAP(0x47)
UTRAP(0x48)
UTRAP(0x49)
UTRAP(0x4a)
UTRAP(0x4b)
UTRAP(0x4c)
UTRAP(0x4d)
UTRAP(0x4e)
UTRAP(0x4f)
UTRAP(0x50)
UTRAP(0x51)
UTRAP(0x52)
UTRAP(0x53)
UTRAP(0x54)
UTRAP(0x55)
UTRAP(0x56)
UTRAP(0x57)
UTRAP(0x58)
UTRAP(0x59)
UTRAP(0x5a)
UTRAP(0x5b)
UTRAP(0x5c)
UTRAP(0x5d)
UTRAP(0x5e)
UTRAP(0x5f)
UTRAP(0x60)
UTRAP(0x61)
UTRAP(0x62)
UTRAP(0x63)
UTRAP(0x64)
UTRAP(0x65)
UTRAP(0x66)
UTRAP(0x67)
UTRAP(0x68)
UTRAP(0x69)
UTRAP(0x6a)
UTRAP(0x6b)
UTRAP(0x6c)
UTRAP(0x6d)
UTRAP(0x6e)
UTRAP(0x6f)
UTRAP(0x70)
UTRAP(0x71)
UTRAP(0x72)
UTRAP(0x73)
UTRAP(0x74)
UTRAP(0x75)
UTRAP(0x76)
UTRAP(0x77)
UTRAP(0x78)
UTRAP(0x79)
UTRAP(0x7a)
UTRAP(0x7b)
UTRAP(0x7c)
UTRAP(0x7d)
UTRAP(0x7e)
UTRAP(0x7f)
SYSCALL ! 80 = sun syscall
BPT ! 81 = pseudo breakpoint instruction
TRAP(T_DIV0) ! 82 = divide by zero
TRAP(T_FLUSHWIN) ! 83 = flush windows
TRAP(T_CLEANWIN) ! 84 = provide clean windows
TRAP(T_RANGECHECK) ! 85 = ???
TRAP(T_FIXALIGN) ! 86 = fix up unaligned accesses
TRAP(T_INTOF) ! 87 = integer overflow
SYSCALL ! 88 = svr4 syscall
SYSCALL ! 89 = bsd syscall
BPT_KGDB_EXEC ! 8a = enter kernel gdb on kernel startup
STRAP(0x8b)
STRAP(0x8c)
STRAP(0x8d)
STRAP(0x8e)
STRAP(0x8f)
STRAP(0x90)
STRAP(0x91)
STRAP(0x92)
STRAP(0x93)
STRAP(0x94)
STRAP(0x95)
STRAP(0x96)
STRAP(0x97)
STRAP(0x98)
STRAP(0x99)
STRAP(0x9a)
STRAP(0x9b)
STRAP(0x9c)
STRAP(0x9d)
STRAP(0x9e)
STRAP(0x9f)
STRAP(0xa0)
STRAP(0xa1)
STRAP(0xa2)
STRAP(0xa3)
STRAP(0xa4)
STRAP(0xa5)
STRAP(0xa6)
STRAP(0xa7)
STRAP(0xa8)
STRAP(0xa9)
STRAP(0xaa)
STRAP(0xab)
STRAP(0xac)
STRAP(0xad)
STRAP(0xae)
STRAP(0xaf)
STRAP(0xb0)
STRAP(0xb1)
STRAP(0xb2)
STRAP(0xb3)
STRAP(0xb4)
STRAP(0xb5)
STRAP(0xb6)
STRAP(0xb7)
STRAP(0xb8)
STRAP(0xb9)
STRAP(0xba)
STRAP(0xbb)
STRAP(0xbc)
STRAP(0xbd)
STRAP(0xbe)
STRAP(0xbf)
STRAP(0xc0)
STRAP(0xc1)
STRAP(0xc2)
STRAP(0xc3)
STRAP(0xc4)
STRAP(0xc5)
STRAP(0xc6)
STRAP(0xc7)
STRAP(0xc8)
STRAP(0xc9)
STRAP(0xca)
STRAP(0xcb)
STRAP(0xcc)
STRAP(0xcd)
STRAP(0xce)
STRAP(0xcf)
STRAP(0xd0)
STRAP(0xd1)
STRAP(0xd2)
STRAP(0xd3)
STRAP(0xd4)
STRAP(0xd5)
STRAP(0xd6)
STRAP(0xd7)
STRAP(0xd8)
STRAP(0xd9)
STRAP(0xda)
STRAP(0xdb)
STRAP(0xdc)
STRAP(0xdd)
STRAP(0xde)
STRAP(0xdf)
STRAP(0xe0)
STRAP(0xe1)
STRAP(0xe2)
STRAP(0xe3)
STRAP(0xe4)
STRAP(0xe5)
STRAP(0xe6)
STRAP(0xe7)
STRAP(0xe8)
STRAP(0xe9)
STRAP(0xea)
STRAP(0xeb)
STRAP(0xec)
STRAP(0xed)
STRAP(0xee)
STRAP(0xef)
STRAP(0xf0)
STRAP(0xf1)
STRAP(0xf2)
STRAP(0xf3)
STRAP(0xf4)
STRAP(0xf5)
STRAP(0xf6)
STRAP(0xf7)
STRAP(0xf8)
STRAP(0xf9)
STRAP(0xfa)
STRAP(0xfb)
STRAP(0xfc)
STRAP(0xfd)
STRAP(0xfe)
STRAP(0xff)
#endif
/*
* Pad the trap table to max page size.
* Trap table size is 0x100 * 4instr * 4byte/instr = 4096 bytes;
* need to .skip 4096 to pad to page size iff. the number of trap tables
* defined above is odd.
*/
#if defined(SUN4) + defined(SUN4C) + defined(SUN4M) - 2 == 0
.skip 4096
#endif
#ifdef DEBUG
/*
* A hardware red zone is impossible. We simulate one in software by
* keeping a `red zone' pointer; if %sp becomes less than this, we panic.
* This is expensive and is only enabled when debugging.
*/
#define REDSIZE (8*96) /* some room for bouncing */
#define REDSTACK 2048 /* size of `panic: stack overflow' region */
.data
_redzone:
.word _idle_u + REDSIZE
_redstack:
.skip REDSTACK
.text
Lpanic_red:
.asciz "stack overflow"
_ALIGN
/* set stack pointer redzone to base+minstack; alters base */
#define SET_SP_REDZONE(base, tmp) \
add base, REDSIZE, base; \
sethi %hi(_redzone), tmp; \
st base, [tmp + %lo(_redzone)]
/* variant with a constant */
#define SET_SP_REDZONE_CONST(const, tmp1, tmp2) \
set (const) + REDSIZE, tmp1; \
sethi %hi(_redzone), tmp2; \
st tmp1, [tmp2 + %lo(_redzone)]
/* check stack pointer against redzone (uses two temps) */
#define CHECK_SP_REDZONE(t1, t2) \
sethi %hi(_redzone), t1; \
ld [t1 + %lo(_redzone)], t2; \
cmp %sp, t2; /* if sp >= t2, not in red zone */ \
bgeu 7f; nop; /* and can continue normally */ \
/* move to panic stack */ \
st %g0, [t1 + %lo(_redzone)]; \
set _redstack + REDSTACK - 96, %sp; \
/* prevent panic() from lowering ipl */ \
sethi %hi(_panicstr), t2; \
set Lpanic_red, t2; \
st t2, [t1 + %lo(_panicstr)]; \
rd %psr, t1; /* t1 = splhigh() */ \
or t1, PSR_PIL, t2; \
wr t2, 0, %psr; \
wr t2, PSR_ET, %psr; /* turn on traps */ \
nop; nop; nop; \
save %sp, -CCFSZ, %sp; /* preserve current window */ \
sethi %hi(Lpanic_red), %o0; \
call _panic; or %o0, %lo(Lpanic_red), %o0; \
7:
#else
#define SET_SP_REDZONE(base, tmp)
#define SET_SP_REDZONE_CONST(const, t1, t2)
#define CHECK_SP_REDZONE(t1, t2)
#endif
/*
* The window code must verify user stack addresses before using them.
* A user stack pointer is invalid if:
* - it is not on an 8 byte boundary;
* - its pages (a register window, being 64 bytes, can occupy
* two pages) are not readable or writable.
* We define three separate macros here for testing user stack addresses.
*
* PTE_OF_ADDR locates a PTE, branching to a `bad address'
* handler if the stack pointer points into the hole in the
* address space (i.e., top 3 bits are not either all 1 or all 0);
* CMP_PTE_USER_READ compares the located PTE against `user read' mode;
* CMP_PTE_USER_WRITE compares the located PTE against `user write' mode.
* The compares give `equal' if read or write is OK.
*
* Note that the user stack pointer usually points into high addresses
* (top 3 bits all 1), so that is what we check first.
*
* The code below also assumes that PTE_OF_ADDR is safe in a delay
* slot; it is, at it merely sets its `pte' register to a temporary value.
*/
#if defined(SUN4) || defined(SUN4C)
/* input: addr, output: pte; aux: bad address label */
#define PTE_OF_ADDR4_4C(addr, pte, bad, page_offset) \
sra addr, PG_VSHIFT, pte; \
cmp pte, -1; \
be,a 1f; andn addr, page_offset, pte; \
tst pte; \
bne bad; EMPTY; \
andn addr, page_offset, pte; \
1:
/* input: pte; output: condition codes */
#define CMP_PTE_USER_READ4_4C(pte) \
lda [pte] ASI_PTE, pte; \
srl pte, PG_PROTSHIFT, pte; \
andn pte, (PG_W >> PG_PROTSHIFT), pte; \
cmp pte, PG_PROTUREAD
/* input: pte; output: condition codes */
#define CMP_PTE_USER_WRITE4_4C(pte) \
lda [pte] ASI_PTE, pte; \
srl pte, PG_PROTSHIFT, pte; \
cmp pte, PG_PROTUWRITE
#endif
/*
* The Sun4M does not have the memory hole that the 4C does. Thus all
* we need to do here is clear the page offset from addr.
*/
#if defined(SUN4M)
#define PTE_OF_ADDR4M(addr, pte, bad, page_offset) \
andn addr, page_offset, pte
#define CMP_PTE_USER_READ4M(pte) \
or pte, ASI_SRMMUFP_L3, pte; \
lda [pte] ASI_SRMMUFP, pte; \
and pte, 0x1f, pte; \
cmp pte, (SRMMU_TEPTE | PPROT_RX_RX)
#define CMP_PTE_USER_WRITE4M(pte) \
or pte, ASI_SRMMUFP_L3, pte; \
lda [pte] ASI_SRMMUFP, pte; \
and pte, 0x1f, pte; \
cmp pte, (SRMMU_TEPTE | PPROT_RWX_RWX)
#endif /* 4m */
#if defined(SUN4M) && !(defined(SUN4C) || defined(SUN4))
#define PTE_OF_ADDR PTE_OF_ADDR4M
#define CMP_PTE_USER_WRITE(pte,tmp) CMP_PTE_USER_WRITE4M(pte)
#define CMP_PTE_USER_READ(pte,tmp) CMP_PTE_USER_READ4M(pte)
#elif (defined(SUN4C) || defined(SUN4)) && !defined(SUN4M)
#define PTE_OF_ADDR PTE_OF_ADDR4_4C
#define CMP_PTE_USER_WRITE(pte,tmp) CMP_PTE_USER_WRITE4_4C(pte)
#define CMP_PTE_USER_READ(pte,tmp) CMP_PTE_USER_READ4_4C(pte)
#else /* both defined, ugh */
#define PTE_OF_ADDR(addr, pte, bad, page_offset) \
sethi %hi(_cputyp), pte; \
ld [pte + %lo(_cputyp)], pte; \
cmp pte, CPU_SUN4M; \
bne 2f; nop; \
PTE_OF_ADDR4M(addr, pte, bad, page_offset); \
b,a 3f; \
2: \
PTE_OF_ADDR4_4C(addr, pte, bad, page_offset); \
3:
#define CMP_PTE_USER_READ(pte, tmp) \
sethi %hi(_cputyp), tmp; \
ld [tmp + %lo(_cputyp)], tmp; \
cmp tmp, CPU_SUN4M; \
bne 1f; nop; \
CMP_PTE_USER_READ4M(pte); \
b,a 2f; \
1: \
CMP_PTE_USER_READ4_4C(pte); \
2:
#define CMP_PTE_USER_WRITE(pte, tmp) \
sethi %hi(_cputyp), tmp; \
ld [tmp + %lo(_cputyp)], tmp; \
cmp tmp, CPU_SUN4M; \
bne 1f; nop; \
CMP_PTE_USER_WRITE4M(pte); \
b,a 2f; \
1: \
CMP_PTE_USER_WRITE4_4C(pte); \
2:
#endif
/*
* The calculations in PTE_OF_ADDR and CMP_PTE_USER_* are rather slow:
* in particular, according to Gordon Irlam of the University of Adelaide
* in Australia, these consume at least 18 cycles on an SS1 and 37 on an
* SS2. Hence, we try to avoid them in the common case.
*
* A chunk of 64 bytes is on a single page if and only if:
*
* ((base + 64 - 1) & ~(NBPG-1)) == (base & ~(NBPG-1))
*
* Equivalently (and faster to test), the low order bits (base & 4095) must
* be small enough so that the sum (base + 63) does not carry out into the
* upper page-address bits, i.e.,
*
* (base & (NBPG-1)) < (NBPG - 63)
*
* so we allow testing that here. This macro is also assumed to be safe
* in a delay slot (modulo overwriting its temporary).
*/
#define SLT_IF_1PAGE_RW(addr, tmp, page_offset) \
and addr, page_offset, tmp; \
sub page_offset, 62, page_offset; \
cmp tmp, page_offset
/*
* Every trap that enables traps must set up stack space.
* If the trap is from user mode, this involves switching to the kernel
* stack for the current process, and we must also set cpcb->pcb_uw
* so that the window overflow handler can tell user windows from kernel
* windows.
*
* The number of user windows is:
*
* cpcb->pcb_uw = (cpcb->pcb_wim - 1 - CWP) % nwindows
*
* (where pcb_wim = log2(current %wim) and CWP = low 5 bits of %psr).
* We compute this expression by table lookup in uwtab[CWP - pcb_wim],
* which has been set up as:
*
* for i in [-nwin+1 .. nwin-1]
* uwtab[i] = (nwin - 1 - i) % nwin;
*
* (If you do not believe this works, try it for yourself.)
*
* We also keep one or two more tables:
*
* for i in 0..nwin-1
* wmask[i] = 1 << ((i + 1) % nwindows);
*
* wmask[CWP] tells whether a `rett' would return into the invalid window.
*/
.data
.skip 32 ! alignment byte & negative indicies
uwtab: .skip 32 ! u_char uwtab[-31..31];
wmask: .skip 32 ! u_char wmask[0..31];
.text
/*
* Things begin to grow uglier....
*
* Each trap handler may (always) be running in the trap window.
* If this is the case, it cannot enable further traps until it writes
* the register windows into the stack (or, if the stack is no good,
* the current pcb).
*
* ASSUMPTIONS: TRAP_SETUP() is called with:
* %l0 = %psr
* %l1 = return pc
* %l2 = return npc
* %l3 = (some value that must not be altered)
* which means we have 4 registers to work with.
*
* The `stackspace' argument is the number of stack bytes to allocate
* for register-saving, and must be at least -64 (and typically more,
* for global registers and %y).
*
* Trapframes should use -CCFSZ-80. (80 = sizeof(struct trapframe);
* see trap.h. This basically means EVERYONE. Interrupt frames could
* get away with less, but currently do not.)
*
* The basic outline here is:
*
* if (trap came from kernel mode) {
* if (we are in the trap window)
* save it away;
* %sp = %fp - stackspace;
* } else {
* compute the number of user windows;
* if (we are in the trap window)
* save it away;
* %sp = (top of kernel stack) - stackspace;
* }
*
* Again, the number of user windows is:
*
* cpcb->pcb_uw = (cpcb->pcb_wim - 1 - CWP) % nwindows
*
* (where pcb_wim = log2(current %wim) and CWP is the low 5 bits of %psr),
* and this is computed as `uwtab[CWP - pcb_wim]'.
*
* NOTE: if you change this code, you will have to look carefully
* at the window overflow and underflow handlers and make sure they
* have similar changes made as needed.
*/
#define CALL_CLEAN_TRAP_WINDOW \
sethi %hi(clean_trap_window), %l7; \
jmpl %l7 + %lo(clean_trap_window), %l4; \
mov %g7, %l7 /* save %g7 in %l7 for clean_trap_window */
#define TRAP_SETUP(stackspace) \
rd %wim, %l4; \
mov 1, %l5; \
sll %l5, %l0, %l5; \
btst PSR_PS, %l0; \
bz 1f; \
btst %l5, %l4; \
/* came from kernel mode; cond codes indicate trap window */ \
bz,a 3f; \
add %fp, stackspace, %sp; /* want to just set %sp */ \
CALL_CLEAN_TRAP_WINDOW; /* but maybe need to clean first */ \
b 3f; \
add %fp, stackspace, %sp; \
1: \
/* came from user mode: compute pcb_nw */ \
sethi %hi(_cpcb), %l6; \
ld [%l6 + %lo(_cpcb)], %l6; \
ld [%l6 + PCB_WIM], %l5; \
and %l0, 31, %l4; \
sub %l4, %l5, %l5; \
set uwtab, %l4; \
ldub [%l4 + %l5], %l5; \
st %l5, [%l6 + PCB_UW]; \
/* cond codes still indicate whether in trap window */ \
bz,a 2f; \
sethi %hi(USPACE+(stackspace)), %l5; \
/* yes, in trap window; must clean it */ \
CALL_CLEAN_TRAP_WINDOW; \
sethi %hi(_cpcb), %l6; \
ld [%l6 + %lo(_cpcb)], %l6; \
sethi %hi(USPACE+(stackspace)), %l5; \
2: \
/* trap window is (now) clean: set %sp */ \
or %l5, %lo(USPACE+(stackspace)), %l5; \
add %l6, %l5, %sp; \
SET_SP_REDZONE(%l6, %l5); \
3: \
CHECK_SP_REDZONE(%l6, %l5)
/*
* Interrupt setup is almost exactly like trap setup, but we need to
* go to the interrupt stack if (a) we came from user mode or (b) we
* came from kernel mode on the kernel stack.
*/
#define INTR_SETUP(stackspace) \
rd %wim, %l4; \
mov 1, %l5; \
sll %l5, %l0, %l5; \
btst PSR_PS, %l0; \
bz 1f; \
btst %l5, %l4; \
/* came from kernel mode; cond codes still indicate trap window */ \
bz,a 0f; \
sethi %hi(_eintstack), %l7; \
CALL_CLEAN_TRAP_WINDOW; \
sethi %hi(_eintstack), %l7; \
0: /* now if %fp >= eintstack, we were on the kernel stack */ \
cmp %fp, %l7; \
bge,a 3f; \
add %l7, stackspace, %sp; /* so switch to intstack */ \
b 4f; \
add %fp, stackspace, %sp; /* else stay on intstack */ \
1: \
/* came from user mode: compute pcb_nw */ \
sethi %hi(_cpcb), %l6; \
ld [%l6 + %lo(_cpcb)], %l6; \
ld [%l6 + PCB_WIM], %l5; \
and %l0, 31, %l4; \
sub %l4, %l5, %l5; \
set uwtab, %l4; \
ldub [%l4 + %l5], %l5; \
st %l5, [%l6 + PCB_UW]; \
/* cond codes still indicate whether in trap window */ \
bz,a 2f; \
sethi %hi(_eintstack), %l7; \
/* yes, in trap window; must save regs */ \
CALL_CLEAN_TRAP_WINDOW; \
sethi %hi(_eintstack), %l7; \
2: \
add %l7, stackspace, %sp; \
3: \
SET_SP_REDZONE_CONST(_intstack, %l6, %l5); \
4: \
CHECK_SP_REDZONE(%l6, %l5)
/*
* Handler for making the trap window shiny clean.
*
* On entry:
* cpcb->pcb_nw = number of user windows
* %l0 = %psr
* %l1 must not be clobbered
* %l2 must not be clobbered
* %l3 must not be clobbered
* %l4 = address for `return'
* %l7 = saved %g7 (we put this in a delay slot above, to save work)
*
* On return:
* %wim has changed, along with cpcb->pcb_wim
* %g7 has been restored
*
* Normally, we push only one window.
*/
clean_trap_window:
mov %g5, %l5 ! save %g5
mov %g6, %l6 ! ... and %g6
/* mov %g7, %l7 ! ... and %g7 (already done for us) */
sethi %hi(_cpcb), %g6 ! get current pcb
ld [%g6 + %lo(_cpcb)], %g6
/* Figure out whether it is a user window (cpcb->pcb_uw > 0). */
ld [%g6 + PCB_UW], %g7
deccc %g7
bge ctw_user
save %g0, %g0, %g0 ! in any case, enter window to save
/* The window to be pushed is a kernel window. */
std %l0, [%sp + (0*8)]
ctw_merge:
std %l2, [%sp + (1*8)]
std %l4, [%sp + (2*8)]
std %l6, [%sp + (3*8)]
std %i0, [%sp + (4*8)]
std %i2, [%sp + (5*8)]
std %i4, [%sp + (6*8)]
std %i6, [%sp + (7*8)]
/* Set up new window invalid mask, and update cpcb->pcb_wim. */
rd %psr, %g7 ! g7 = (junk << 5) + new_cwp
mov 1, %g5 ! g5 = 1 << new_cwp;
sll %g5, %g7, %g5
wr %g5, 0, %wim ! setwim(g5);
and %g7, 31, %g7 ! cpcb->pcb_wim = g7 & 31;
sethi %hi(_cpcb), %g6 ! re-get current pcb
ld [%g6 + %lo(_cpcb)], %g6
st %g7, [%g6 + PCB_WIM]
nop
restore ! back to trap window
mov %l5, %g5 ! restore g5
mov %l6, %g6 ! ... and g6
jmp %l4 + 8 ! return to caller
mov %l7, %g7 ! ... and g7
/* NOTREACHED */
ctw_user:
/*
* The window to be pushed is a user window.
* We must verify the stack pointer (alignment & permissions).
* See comments above definition of PTE_OF_ADDR.
*/
st %g7, [%g6 + PCB_UW] ! cpcb->pcb_uw--;
btst 7, %sp ! if not aligned,
bne ctw_invalid ! choke on it
EMPTY
sethi %hi(_pgofset), %g6 ! trash %g6=curpcb
ld [%g6 + %lo(_pgofset)], %g6
PTE_OF_ADDR(%sp, %g7, ctw_invalid, %g6)
CMP_PTE_USER_WRITE(%g7, %g5) ! likewise if not writable
bne ctw_invalid
EMPTY
/* Note side-effect of SLT_IF_1PAGE_RW: decrements %g6 by 62 */
SLT_IF_1PAGE_RW(%sp, %g7, %g6)
bl,a ctw_merge ! all ok if only 1
std %l0, [%sp]
add %sp, 7*8, %g5 ! check last addr too
add %g6, 62, %g6 ! restore %g6 to `pgofset'
PTE_OF_ADDR(%g5, %g7, ctw_invalid, %g6)
CMP_PTE_USER_WRITE(%g7, %g6)
be,a ctw_merge ! all ok: store <l0,l1> and merge
std %l0, [%sp]
/*
* The window we wanted to push could not be pushed.
* Instead, save ALL user windows into the pcb.
* We will notice later that we did this, when we
* get ready to return from our trap or syscall.
*
* The code here is run rarely and need not be optimal.
*/
ctw_invalid:
/*
* Reread cpcb->pcb_uw. We decremented this earlier,
* so it is off by one.
*/
sethi %hi(_cpcb), %g6 ! re-get current pcb
ld [%g6 + %lo(_cpcb)], %g6
ld [%g6 + PCB_UW], %g7 ! (number of user windows) - 1
add %g6, PCB_RW, %g5
/* save g7+1 windows, starting with the current one */
1: ! do {
std %l0, [%g5 + (0*8)] ! rw->rw_local[0] = l0;
std %l2, [%g5 + (1*8)] ! ...
std %l4, [%g5 + (2*8)]
std %l6, [%g5 + (3*8)]
std %i0, [%g5 + (4*8)]
std %i2, [%g5 + (5*8)]
std %i4, [%g5 + (6*8)]
std %i6, [%g5 + (7*8)]
deccc %g7 ! if (n > 0) save(), rw++;
bge,a 1b ! } while (--n >= 0);
save %g5, 64, %g5
/* stash sp for bottommost window */
st %sp, [%g5 + 64 + (7*8)]
/* set up new wim */
rd %psr, %g7 ! g7 = (junk << 5) + new_cwp;
mov 1, %g5 ! g5 = 1 << new_cwp;
sll %g5, %g7, %g5
wr %g5, 0, %wim ! wim = g5;
and %g7, 31, %g7
st %g7, [%g6 + PCB_WIM] ! cpcb->pcb_wim = new_cwp;
/* fix up pcb fields */
ld [%g6 + PCB_UW], %g7 ! n = cpcb->pcb_uw;
add %g7, 1, %g5
st %g5, [%g6 + PCB_NSAVED] ! cpcb->pcb_nsaved = n + 1;
st %g0, [%g6 + PCB_UW] ! cpcb->pcb_uw = 0;
/* return to trap window */
1: deccc %g7 ! do {
bge 1b ! restore();
restore ! } while (--n >= 0);
mov %l5, %g5 ! restore g5, g6, & g7, and return
mov %l6, %g6
jmp %l4 + 8
mov %l7, %g7
/* NOTREACHED */
/*
* Each memory access (text or data) fault, from user or kernel mode,
* comes here. We read the error register and figure out what has
* happened.
*
* This cannot be done from C code since we must not enable traps (and
* hence may not use the `save' instruction) until we have decided that
* the error is or is not an asynchronous one that showed up after a
* synchronous error, but which must be handled before the sync err.
*
* Most memory faults are user mode text or data faults, which can cause
* signal delivery or ptracing, for which we must build a full trapframe.
* It does not seem worthwhile to work to avoid this in the other cases,
* so we store all the %g registers on the stack immediately.
*
* On entry:
* %l0 = %psr
* %l1 = return pc
* %l2 = return npc
* %l3 = T_TEXTFAULT or T_DATAFAULT
*
* Internal:
* %l4 = %y, until we call mem_access_fault (then onto trapframe)
* %l5 = IE_reg_addr, if async mem error
*
*/
#if defined(SUN4)
memfault_sun4:
TRAP_SETUP(-CCFSZ-80)
INCR(_cnt+V_FAULTS) ! cnt.v_faults++ (clobbers %o0,%o1)
st %g1, [%sp + CCFSZ + 20] ! save g1
rd %y, %l4 ! save y
/*
* registers:
* memerr.ctrl = memory error control reg., error if 0x80 set
* memerr.vaddr = address of memory error
* buserr = basically just like sun4c sync error reg but
* no SER_WRITE bit (have to figure out from code).
*/
set _par_err_reg, %o0 ! memerr ctrl addr -- XXX mapped?
ld [%o0], %o0 ! get it
std %g2, [%sp + CCFSZ + 24] ! save g2, g3
ld [%o0], %o1 ! memerr ctrl register
inc 4, %o0 ! now VA of memerr vaddr register
std %g4, [%sp + CCFSZ + 32] ! (sneak g4,g5 in here)
ld [%o0], %o2 ! memerr virt addr
st %g0, [%o0] ! NOTE: this clears latching!!!
btst ME_REG_IERR, %o1 ! memory error?
! XXX this value may not be correct
! as I got some parity errors and the
! correct bits were not on?
std %g6, [%sp + CCFSZ + 40]
bz,a 0f ! no, just a regular fault
wr %l0, PSR_ET, %psr ! (and reenable traps)
/* memory error = death for now XXX */
clr %o3
clr %o4
call _memerr ! (0, ser, sva, 0, 0)
clr %o0
call _callrom
nop
0:
/*
* have to make SUN4 emulate SUN4C. 4C code expects
* SER in %o1 and the offending VA in %o2, everything else is ok.
* (must figure out if SER_WRITE should be set)
*/
set AC_BUS_ERR, %o0 ! bus error register
cmp %l3, T_TEXTFAULT ! text fault always on PC
be normal_mem_fault ! go
lduba [%o0] ASI_CONTROL, %o1 ! get its value
#define STORE_BIT 21 /* bit that indicates a store instruction for sparc */
ld [%l1], %o3 ! offending instruction in %o3 [l1=pc]
srl %o3, STORE_BIT, %o3 ! get load/store bit (wont fit simm13)
btst 1, %o3 ! test for store operation
bz normal_mem_fault ! if (z) is a load (so branch)
sethi %hi(SER_WRITE), %o5 ! damn SER_WRITE wont fit simm13
! or %lo(SER_WRITE), %o5, %o5! not necessary since %lo is zero
or %o5, %o1, %o1 ! set SER_WRITE
#if defined(SUN4C) || defined(SUN4M)
ba,a normal_mem_fault
!!nop ! XXX make efficient later
#endif /* SUN4C || SUN4M */
#endif /* SUN4 */
memfault_sun4c:
#if defined(SUN4C)
TRAP_SETUP(-CCFSZ-80)
INCR(_cnt+V_FAULTS) ! cnt.v_faults++ (clobbers %o0,%o1)
st %g1, [%sp + CCFSZ + 20] ! save g1
rd %y, %l4 ! save y
/*
* We know about the layout of the error registers here.
* addr reg
* ---- ---
* a AC_SYNC_ERR
* a+4 AC_SYNC_VA
* a+8 AC_ASYNC_ERR
* a+12 AC_ASYNC_VA
*/
#if AC_SYNC_ERR + 4 != AC_SYNC_VA || \
AC_SYNC_ERR + 8 != AC_ASYNC_ERR || AC_SYNC_ERR + 12 != AC_ASYNC_VA
help help help ! I, I, I wanna be a lifeguard
#endif
set AC_SYNC_ERR, %o0
std %g2, [%sp + CCFSZ + 24] ! save g2, g3
lda [%o0] ASI_CONTROL, %o1 ! sync err reg
inc 4, %o0
std %g4, [%sp + CCFSZ + 32] ! (sneak g4,g5 in here)
lda [%o0] ASI_CONTROL, %o2 ! sync virt addr
btst SER_MEMERR, %o1 ! memory error?
std %g6, [%sp + CCFSZ + 40]
bz,a normal_mem_fault ! no, just a regular fault
wr %l0, PSR_ET, %psr ! (and reenable traps)
/*
* We got a synchronous memory error. It could be one that
* happened because there were two stores in a row, and the
* first went into the write buffer, and the second caused this
* synchronous trap; so there could now be a pending async error.
* This is in fact the case iff the two va's differ.
*/
inc 4, %o0
lda [%o0] ASI_CONTROL, %o3 ! async err reg
inc 4, %o0
lda [%o0] ASI_CONTROL, %o4 ! async virt addr
cmp %o2, %o4
be,a 1f ! no, not an async err
wr %l0, PSR_ET, %psr ! (and reenable traps)
/*
* Handle the async error; ignore the sync error for now
* (we may end up getting it again, but so what?).
* This code is essentially the same as that at `nmi' below,
* but the register usage is different and we cannot merge.
*/
sethi %hi(IE_reg_addr), %l5 ! ienab_bic(IE_ALLIE);
ldub [%l5 + %lo(IE_reg_addr)], %o0
andn %o0, IE_ALLIE, %o0
stb %o0, [%l5 + %lo(IE_reg_addr)]
/*
* Now reenable traps and call C code.
* %o1 through %o4 still hold the error reg contents.
* If memerr() returns, return from the trap.
*/
wr %l0, PSR_ET, %psr
call _memerr ! memerr(0, ser, sva, aer, ava)
clr %o0
ld [%sp + CCFSZ + 20], %g1 ! restore g1 through g7
wr %l0, 0, %psr ! and disable traps, 3 instr delay
ldd [%sp + CCFSZ + 24], %g2
ldd [%sp + CCFSZ + 32], %g4
ldd [%sp + CCFSZ + 40], %g6
/* now safe to set IE_ALLIE again */
ldub [%l5 + %lo(IE_reg_addr)], %o1
or %o1, IE_ALLIE, %o1
stb %o1, [%l5 + %lo(IE_reg_addr)]
b return_from_trap
wr %l4, 0, %y ! restore y
/*
* Trap was a synchronous memory error.
* %o1 through %o4 still hold the error reg contents.
*/
1:
call _memerr ! memerr(1, ser, sva, aer, ava)
mov 1, %o0
ld [%sp + CCFSZ + 20], %g1 ! restore g1 through g7
ldd [%sp + CCFSZ + 24], %g2
ldd [%sp + CCFSZ + 32], %g4
ldd [%sp + CCFSZ + 40], %g6
wr %l4, 0, %y ! restore y
b return_from_trap
wr %l0, 0, %psr
/* NOTREACHED */
#endif /* SUN4C */
#if defined(SUN4M)
memfault_sun4m:
TRAP_SETUP(-CCFSZ-80)
INCR(_cnt+V_FAULTS) ! cnt.v_faults++ (clobbers %o0,%o1)
st %g1, [%sp + CCFSZ + 20] ! save g1
rd %y, %l4 ! save y
set SRMMU_SFADDR, %o0
std %g2, [%sp + CCFSZ + 24] ! save g2, g3
lda [%o0] ASI_SRMMU, %o2 ! sync virt addr; must be read first
set SRMMU_SFSTAT, %o0
lda [%o0] ASI_SRMMU, %o1 ! get sync fault status register
std %g4, [%sp + CCFSZ + 32] ! (sneak g4,g5 in here)
/* Now test for a HyperSPARC. If we have one, get the async status */
sethi %hi(_mmumod), %o3 ! get MMU model
ld [%o3 + %lo(_mmumod)], %o3
cmp %o3, SUN4M_MMU_HS ! is it hypersparc?
std %g6, [%sp + CCFSZ + 40] ! sneak in g6, g7
be 1f ! yup, skip ahead
clr %o3 ! clear %o3 and %o4, not hypersparc
b 2f
clr %o4
1:
set SRMMU_AFSTAT, %o3 ! must read status before fault on HS
lda [%o3] ASI_SRMMU, %o3 ! get async fault status
set SRMMU_AFADDR, %o4
lda [%o4] ASI_SRMMU, %o4 ! get async fault address
2:
wr %l0, PSR_ET, %psr ! reenable traps
/* Finish stackframe, call C trap handler */
std %l0, [%sp + CCFSZ + 0] ! set tf.tf_psr, tf.tf_pc
mov %l3, %o0 ! (argument: type)
st %l2, [%sp + CCFSZ + 8] ! set tf.tf_npc
st %l4, [%sp + CCFSZ + 12] ! set tf.tf_y
std %i0, [%sp + CCFSZ + 48] ! tf.tf_out[0], etc
std %i2, [%sp + CCFSZ + 56]
std %i4, [%sp + CCFSZ + 64]
std %i6, [%sp + CCFSZ + 72]
call _mem_access_fault4m ! mem_access_fault(type, sfsr, sfva,
! afsr, afva, &tf);
add %sp, CCFSZ, %o5 ! (argument: &tf)
ldd [%sp + CCFSZ + 0], %l0 ! load new values
ldd [%sp + CCFSZ + 8], %l2
wr %l3, 0, %y
ld [%sp + CCFSZ + 20], %g1
ldd [%sp + CCFSZ + 24], %g2
ldd [%sp + CCFSZ + 32], %g4
ldd [%sp + CCFSZ + 40], %g6
ldd [%sp + CCFSZ + 48], %i0
ldd [%sp + CCFSZ + 56], %i2
ldd [%sp + CCFSZ + 64], %i4
ldd [%sp + CCFSZ + 72], %i6
b return_from_trap ! go return
wr %l0, 0, %psr ! (but first disable traps again)
#endif /* SUN4M */
normal_mem_fault:
/*
* Trap was some other error; call C code to deal with it.
* Must finish trap frame (psr,pc,npc,%y,%o0..%o7) in case
* we decide to deliver a signal or ptrace the process.
* %g1..%g7 were already set up above.
*/
std %l0, [%sp + CCFSZ + 0] ! set tf.tf_psr, tf.tf_pc
mov %l3, %o0 ! (argument: type)
st %l2, [%sp + CCFSZ + 8] ! set tf.tf_npc
st %l4, [%sp + CCFSZ + 12] ! set tf.tf_y
mov %l1, %o3 ! (argument: pc)
std %i0, [%sp + CCFSZ + 48] ! tf.tf_out[0], etc
std %i2, [%sp + CCFSZ + 56]
mov %l0, %o4 ! (argument: psr)
std %i4, [%sp + CCFSZ + 64]
std %i6, [%sp + CCFSZ + 72]
call _mem_access_fault ! mem_access_fault(type, ser, sva,
! pc, psr, &tf);
add %sp, CCFSZ, %o5 ! (argument: &tf)
ldd [%sp + CCFSZ + 0], %l0 ! load new values
ldd [%sp + CCFSZ + 8], %l2
wr %l3, 0, %y
ld [%sp + CCFSZ + 20], %g1
ldd [%sp + CCFSZ + 24], %g2
ldd [%sp + CCFSZ + 32], %g4
ldd [%sp + CCFSZ + 40], %g6
ldd [%sp + CCFSZ + 48], %i0
ldd [%sp + CCFSZ + 56], %i2
ldd [%sp + CCFSZ + 64], %i4
ldd [%sp + CCFSZ + 72], %i6
b return_from_trap ! go return
wr %l0, 0, %psr ! (but first disable traps again)
/*
* fp_exception has to check to see if we are trying to save
* the FP state, and if so, continue to save the FP state.
*
* We do not even bother checking to see if we were in kernel mode,
* since users have no access to the special_fp_store instruction.
*
* This whole idea was stolen from Sprite.
*/
fp_exception:
set special_fp_store, %l4 ! see if we came from the special one
cmp %l1, %l4 ! pc == special_fp_store?
bne slowtrap ! no, go handle per usual
EMPTY
sethi %hi(savefpcont), %l4 ! yes, "return" to the special code
or %lo(savefpcont), %l4, %l4
jmp %l4
rett %l4 + 4
/*
* slowtrap() builds a trap frame and calls trap().
* This is called `slowtrap' because it *is*....
* We have to build a full frame for ptrace(), for instance.
*
* Registers:
* %l0 = %psr
* %l1 = return pc
* %l2 = return npc
* %l3 = trap code
*/
slowtrap:
TRAP_SETUP(-CCFSZ-80)
/*
* Phew, ready to enable traps and call C code.
*/
mov %l3, %o0 ! put type in %o0 for later
Lslowtrap_reenter:
wr %l0, PSR_ET, %psr ! traps on again
std %l0, [%sp + CCFSZ] ! tf.tf_psr = psr; tf.tf_pc = ret_pc;
rd %y, %l3
std %l2, [%sp + CCFSZ + 8] ! tf.tf_npc = return_npc; tf.tf_y = %y;
st %g1, [%sp + CCFSZ + 20]
std %g2, [%sp + CCFSZ + 24]
std %g4, [%sp + CCFSZ + 32]
std %g6, [%sp + CCFSZ + 40]
std %i0, [%sp + CCFSZ + 48]
mov %l0, %o1 ! (psr)
std %i2, [%sp + CCFSZ + 56]
mov %l1, %o2 ! (pc)
std %i4, [%sp + CCFSZ + 64]
add %sp, CCFSZ, %o3 ! (&tf)
call _trap ! trap(type, psr, pc, &tf)
std %i6, [%sp + CCFSZ + 72]
ldd [%sp + CCFSZ], %l0 ! load new values
ldd [%sp + CCFSZ + 8], %l2
wr %l3, 0, %y
ld [%sp + CCFSZ + 20], %g1
ldd [%sp + CCFSZ + 24], %g2
ldd [%sp + CCFSZ + 32], %g4
ldd [%sp + CCFSZ + 40], %g6
ldd [%sp + CCFSZ + 48], %i0
ldd [%sp + CCFSZ + 56], %i2
ldd [%sp + CCFSZ + 64], %i4
ldd [%sp + CCFSZ + 72], %i6
b return_from_trap
wr %l0, 0, %psr
/*
* Do a `software' trap by re-entering the trap code, possibly first
* switching from interrupt stack to kernel stack. This is used for
* scheduling and signal ASTs (which generally occur from softclock or
* tty or net interrupts) and register window saves (which might occur
* from anywhere).
*
* The current window is the trap window, and it is by definition clean.
* We enter with the trap type in %o0. All we have to do is jump to
* Lslowtrap_reenter above, but maybe after switching stacks....
*/
softtrap:
sethi %hi(_eintstack), %l7
cmp %sp, %l7
bge Lslowtrap_reenter
EMPTY
sethi %hi(_cpcb), %l6
ld [%l6 + %lo(_cpcb)], %l6
set USPACE-CCFSZ-80, %l5
add %l6, %l5, %l7
SET_SP_REDZONE(%l6, %l5)
b Lslowtrap_reenter
mov %l7, %sp
#ifdef KGDB
/*
* bpt is entered on all breakpoint traps.
* If this is a kernel breakpoint, we do not want to call trap().
* Among other reasons, this way we can set breakpoints in trap().
*/
bpt:
btst PSR_PS, %l0 ! breakpoint from kernel?
bz slowtrap ! no, go do regular trap
nop
/*
* Build a trap frame for kgdb_trap_glue to copy.
* Enable traps but set ipl high so that we will not
* see interrupts from within breakpoints.
*/
TRAP_SETUP(-CCFSZ-80)
or %l0, PSR_PIL, %l4 ! splhigh()
wr %l4, 0, %psr ! the manual claims that this
wr %l4, PSR_ET, %psr ! song and dance is necessary
std %l0, [%sp + CCFSZ + 0] ! tf.tf_psr, tf.tf_pc
mov %l3, %o0 ! trap type arg for kgdb_trap_glue
rd %y, %l3
std %l2, [%sp + CCFSZ + 8] ! tf.tf_npc, tf.tf_y
rd %wim, %l3
st %l3, [%sp + CCFSZ + 16] ! tf.tf_wim (a kgdb-only r/o field)
st %g1, [%sp + CCFSZ + 20] ! tf.tf_global[1]
std %g2, [%sp + CCFSZ + 24] ! etc
std %g4, [%sp + CCFSZ + 32]
std %g6, [%sp + CCFSZ + 40]
std %i0, [%sp + CCFSZ + 48] ! tf.tf_in[0..1]
std %i2, [%sp + CCFSZ + 56] ! etc
std %i4, [%sp + CCFSZ + 64]
std %i6, [%sp + CCFSZ + 72]
/*
* Now call kgdb_trap_glue(); if it returns, call trap().
*/
mov %o0, %l3 ! gotta save trap type
call _kgdb_trap_glue ! kgdb_trap_glue(type, &trapframe)
add %sp, CCFSZ, %o1 ! (&trapframe)
/*
* Use slowtrap to call trap---but first erase our tracks
* (put the registers back the way they were).
*/
mov %l3, %o0 ! slowtrap will need trap type
ld [%sp + CCFSZ + 12], %l3
wr %l3, 0, %y
ld [%sp + CCFSZ + 20], %g1
ldd [%sp + CCFSZ + 24], %g2
ldd [%sp + CCFSZ + 32], %g4
b Lslowtrap_reenter
ldd [%sp + CCFSZ + 40], %g6
/*
* Enter kernel breakpoint. Write all the windows (not including the
* current window) into the stack, so that backtrace works. Copy the
* supplied trap frame to the kgdb stack and switch stacks.
*
* kgdb_trap_glue(type, tf0)
* int type;
* struct trapframe *tf0;
*/
.globl _kgdb_trap_glue
_kgdb_trap_glue:
save %sp, -CCFSZ, %sp
call _write_all_windows
mov %sp, %l4 ! %l4 = current %sp
/* copy trapframe to top of kgdb stack */
set _kgdb_stack + KGDB_STACK_SIZE - 80, %l0
! %l0 = tfcopy -> end_of_kgdb_stack
mov 80, %l1
1: ldd [%i1], %l2
inc 8, %i1
deccc 8, %l1
std %l2, [%l0]
bg 1b
inc 8, %l0
#ifdef DEBUG
/* save old red zone and then turn it off */
sethi %hi(_redzone), %l7
ld [%l7 + %lo(_redzone)], %l6
st %g0, [%l7 + %lo(_redzone)]
#endif
/* switch to kgdb stack */
add %l0, -CCFSZ-80, %sp
/* if (kgdb_trap(type, tfcopy)) kgdb_rett(tfcopy); */
mov %i0, %o0
call _kgdb_trap
add %l0, -80, %o1
tst %o0
bnz,a kgdb_rett
add %l0, -80, %g1
/*
* kgdb_trap() did not handle the trap at all so the stack is
* still intact. A simple `restore' will put everything back,
* after we reset the stack pointer.
*/
mov %l4, %sp
#ifdef DEBUG
st %l6, [%l7 + %lo(_redzone)] ! restore red zone
#endif
ret
restore
/*
* Return from kgdb trap. This is sort of special.
*
* We know that kgdb_trap_glue wrote the window above it, so that we will
* be able to (and are sure to have to) load it up. We also know that we
* came from kernel land and can assume that the %fp (%i6) we load here
* is proper. We must also be sure not to lower ipl (it is at splhigh())
* until we have traps disabled, due to the SPARC taking traps at the
* new ipl before noticing that PSR_ET has been turned off. We are on
* the kgdb stack, so this could be disastrous.
*
* Note that the trapframe argument in %g1 points into the current stack
* frame (current window). We abandon this window when we move %g1->tf_psr
* into %psr, but we will not have loaded the new %sp yet, so again traps
* must be disabled.
*/
kgdb_rett:
rd %psr, %g4 ! turn off traps
wr %g4, PSR_ET, %psr
/* use the three-instruction delay to do something useful */
ld [%g1], %g2 ! pick up new %psr
ld [%g1 + 12], %g3 ! set %y
wr %g3, 0, %y
#ifdef DEBUG
st %l6, [%l7 + %lo(_redzone)] ! and restore red zone
#endif
wr %g0, 0, %wim ! enable window changes
nop; nop; nop
/* now safe to set the new psr (changes CWP, leaves traps disabled) */
wr %g2, 0, %psr ! set rett psr (including cond codes)
/* 3 instruction delay before we can use the new window */
/*1*/ ldd [%g1 + 24], %g2 ! set new %g2, %g3
/*2*/ ldd [%g1 + 32], %g4 ! set new %g4, %g5
/*3*/ ldd [%g1 + 40], %g6 ! set new %g6, %g7
/* now we can use the new window */
mov %g1, %l4
ld [%l4 + 4], %l1 ! get new pc
ld [%l4 + 8], %l2 ! get new npc
ld [%l4 + 20], %g1 ! set new %g1
/* set up returnee's out registers, including its %sp */
ldd [%l4 + 48], %i0
ldd [%l4 + 56], %i2
ldd [%l4 + 64], %i4
ldd [%l4 + 72], %i6
/* load returnee's window, making the window above it be invalid */
restore
restore %g0, 1, %l1 ! move to inval window and set %l1 = 1
rd %psr, %l0
sll %l1, %l0, %l1
wr %l1, 0, %wim ! %wim = 1 << (%psr & 31)
sethi %hi(_cpcb), %l1
ld [%l1 + %lo(_cpcb)], %l1
and %l0, 31, %l0 ! CWP = %psr & 31;
st %l0, [%l1 + PCB_WIM] ! cpcb->pcb_wim = CWP;
save %g0, %g0, %g0 ! back to window to reload
LOADWIN(%sp)
save %g0, %g0, %g0 ! back to trap window
/* note, we have not altered condition codes; safe to just rett */
RETT
#endif
/*
* syscall() builds a trap frame and calls syscall().
* sun_syscall is same but delivers sun system call number
* XXX should not have to save&reload ALL the registers just for
* ptrace...
*/
syscall:
TRAP_SETUP(-CCFSZ-80)
wr %l0, PSR_ET, %psr
std %l0, [%sp + CCFSZ + 0] ! tf_psr, tf_pc
rd %y, %l3
std %l2, [%sp + CCFSZ + 8] ! tf_npc, tf_y
st %g1, [%sp + CCFSZ + 20] ! tf_g[1]
std %g2, [%sp + CCFSZ + 24] ! tf_g[2], tf_g[3]
std %g4, [%sp + CCFSZ + 32] ! etc
std %g6, [%sp + CCFSZ + 40]
mov %g1, %o0 ! (code)
std %i0, [%sp + CCFSZ + 48]
add %sp, CCFSZ, %o1 ! (&tf)
std %i2, [%sp + CCFSZ + 56]
mov %l1, %o2 ! (pc)
std %i4, [%sp + CCFSZ + 64]
call _syscall ! syscall(code, &tf, pc, suncompat)
std %i6, [%sp + CCFSZ + 72]
! now load em all up again, sigh
ldd [%sp + CCFSZ + 0], %l0 ! new %psr, new pc
ldd [%sp + CCFSZ + 8], %l2 ! new npc, new %y
wr %l3, 0, %y
/* see `proc_trampoline' for the reason for this label */
return_from_syscall:
ld [%sp + CCFSZ + 20], %g1
ldd [%sp + CCFSZ + 24], %g2
ldd [%sp + CCFSZ + 32], %g4
ldd [%sp + CCFSZ + 40], %g6
ldd [%sp + CCFSZ + 48], %i0
ldd [%sp + CCFSZ + 56], %i2
ldd [%sp + CCFSZ + 64], %i4
ldd [%sp + CCFSZ + 72], %i6
b return_from_trap
wr %l0, 0, %psr
/*
* Interrupts. Software interrupts must be cleared from the software
* interrupt enable register. Rather than calling ienab_bic for each,
* we do them in-line before enabling traps.
*
* After preliminary setup work, the interrupt is passed to each
* registered handler in turn. These are expected to return nonzero if
* they took care of the interrupt. If a handler claims the interrupt,
* we exit (hardware interrupts are latched in the requestor so we'll
* just take another interrupt in the unlikely event of simultaneous
* interrupts from two different devices at the same level). If we go
* through all the registered handlers and no one claims it, we report a
* stray interrupt. This is more or less done as:
*
* for (ih = intrhand[intlev]; ih; ih = ih->ih_next)
* if ((*ih->ih_fun)(ih->ih_arg ? ih->ih_arg : &frame))
* return;
* strayintr(&frame);
*
* Software interrupts are almost the same with three exceptions:
* (1) we clear the interrupt from the software interrupt enable
* register before calling any handler (we have to clear it first
* to avoid an interrupt-losing race),
* (2) we always call all the registered handlers (there is no way
* to tell if the single bit in the software interrupt register
* represents one or many requests)
* (3) we never announce a stray interrupt (because of (1), another
* interrupt request can come in while we're in the handler. If
* the handler deals with everything for both the original & the
* new request, we'll erroneously report a stray interrupt when
* we take the software interrupt for the new request.
*
* Inputs:
* %l0 = %psr
* %l1 = return pc
* %l2 = return npc
* %l3 = interrupt level
* (software interrupt only) %l4 = bits to clear in interrupt register
*
* Internal:
* %l4, %l5: local variables
* %l6 = %y
* %l7 = %g1
* %g2..%g7 go to stack
*
* An interrupt frame is built in the space for a full trapframe;
* this contains the psr, pc, npc, and interrupt level.
*/
.comm _intrhand, 15 * 8 ! intrhand[0..14]; 0 => error
softintr_sun44c:
sethi %hi(IE_reg_addr), %l6
ldub [%l6 + %lo(IE_reg_addr)], %l5
andn %l5, %l4, %l5
stb %l5, [%l6 + %lo(IE_reg_addr)]
softintr_common:
INTR_SETUP(-CCFSZ-80)
std %g2, [%sp + CCFSZ + 24] ! save registers
INCR(_cnt+V_INTR) ! cnt.v_intr++; (clobbers %o0,%o1)
mov %g1, %l7
rd %y, %l6
std %g4, [%sp + CCFSZ + 32]
andn %l0, PSR_PIL, %l4 ! %l4 = psr & ~PSR_PIL |
sll %l3, 8, %l5 ! intlev << IPLSHIFT
std %g6, [%sp + CCFSZ + 40]
or %l5, %l4, %l4 ! ;
wr %l4, 0, %psr ! the manual claims this
wr %l4, PSR_ET, %psr ! song and dance is necessary
std %l0, [%sp + CCFSZ + 0] ! set up intrframe/clockframe
sll %l3, 2, %l5
set _intrcnt, %l4 ! intrcnt[intlev]++;
ld [%l4 + %l5], %o0
std %l2, [%sp + CCFSZ + 8]
inc %o0
st %o0, [%l4 + %l5]
set _intrhand, %l4 ! %l4 = intrhand[intlev];
ld [%l4 + %l5], %l4
b 3f
st %fp, [%sp + CCFSZ + 16]
1: ld [%l4], %o1
ld [%l4 + 4], %o0
tst %o0
bz,a 2f
add %sp, CCFSZ, %o0
2: jmpl %o1, %o7 ! (void)(*ih->ih_fun)(...)
ld [%l4 + 8], %l4 ! and ih = ih->ih_next
3: tst %l4 ! while ih != NULL
bnz 1b
nop
mov %l7, %g1
wr %l6, 0, %y
ldd [%sp + CCFSZ + 24], %g2
ldd [%sp + CCFSZ + 32], %g4
ldd [%sp + CCFSZ + 40], %g6
b return_from_trap
wr %l0, 0, %psr
/*
* _sparc_interrupt{44c,4m} is exported for paranoia checking
* (see intr.c).
*/
#if defined(SUN4M)
.globl _sparc_interrupt4m
_sparc_interrupt4m:
mov 1, %l4
sethi %hi(ICR_PI_PEND), %l5
ld [%l5 + %lo(ICR_PI_PEND)], %l5
sll %l4, %l3, %l4
andcc %l5, %l4, %g0
bne _sparc_interrupt_common
nop
! a soft interrupt; clear bit in interrupt-pending register
! XXX - this is CPU0's register set.
sethi %hi(ICR_PI_CLR), %l6
sll %l4, 16, %l5
st %l5, [%l6 + %lo(ICR_PI_CLR)]
b,a softintr_common
#endif
.globl _sparc_interrupt44c
_sparc_interrupt44c:
_sparc_interrupt_common:
INTR_SETUP(-CCFSZ-80)
std %g2, [%sp + CCFSZ + 24] ! save registers
INCR(_cnt+V_INTR) ! cnt.v_intr++; (clobbers %o0,%o1)
mov %g1, %l7
rd %y, %l6
std %g4, [%sp + CCFSZ + 32]
andn %l0, PSR_PIL, %l4 ! %l4 = psr & ~PSR_PIL |
sll %l3, 8, %l5 ! intlev << IPLSHIFT
std %g6, [%sp + CCFSZ + 40]
or %l5, %l4, %l4 ! ;
wr %l4, 0, %psr ! the manual claims this
wr %l4, PSR_ET, %psr ! song and dance is necessary
std %l0, [%sp + CCFSZ + 0] ! set up intrframe/clockframe
sll %l3, 2, %l5
set _intrcnt, %l4 ! intrcnt[intlev]++;
ld [%l4 + %l5], %o0
std %l2, [%sp + CCFSZ + 8] ! set up intrframe/clockframe
inc %o0
st %o0, [%l4 + %l5]
set _intrhand, %l4 ! %l4 = intrhand[intlev];
ld [%l4 + %l5], %l4
b 3f
st %fp, [%sp + CCFSZ + 16]
1: ld [%l4], %o1
ld [%l4 + 4], %o0
tst %o0
bz,a 2f
add %sp, CCFSZ, %o0
2: jmpl %o1, %o7 ! handled = (*ih->ih_fun)(...)
ld [%l4 + 8], %l4 ! and ih = ih->ih_next
tst %o0
bnz 4f ! if (handled) break
nop
3: tst %l4
bnz 1b ! while (ih)
nop
call _strayintr ! strayintr(&intrframe)
add %sp, CCFSZ, %o0
/* all done: restore registers and go return */
4: mov %l7, %g1
wr %l6, 0, %y
ldd [%sp + CCFSZ + 24], %g2
ldd [%sp + CCFSZ + 32], %g4
ldd [%sp + CCFSZ + 40], %g6
b return_from_trap
wr %l0, 0, %psr
#ifdef notyet
/*
* Level 12 (ZS serial) interrupt. Handle it quickly, schedule a
* software interrupt, and get out. Do the software interrupt directly
* if we would just take it on the way out.
*
* Input:
* %l0 = %psr
* %l1 = return pc
* %l2 = return npc
* Internal:
* %l3 = zs device
* %l4, %l5 = temporary
* %l6 = rr3 (or temporary data) + 0x100 => need soft int
* %l7 = zs soft status
*/
zshard:
#endif /* notyet */
/*
* Level 15 interrupt. An async memory error has occurred;
* take care of it (typically by panicking, but hey...).
* %l0 = %psr
* %l1 = return pc
* %l2 = return npc
* %l3 = 15 * 4 (why? just because!)
*
* Internal:
* %l4 = %y
* %l5 = %g1
* %l6 = %g6
* %l7 = %g7
* g2, g3, g4, g5 go to stack
*
* This code is almost the same as that in mem_access_fault,
* except that we already know the problem is not a `normal' fault,
* and that we must be extra-careful with interrupt enables.
*/
#if defined(SUN4)
nmi_sun4:
INTR_SETUP(-CCFSZ-80)
INCR(_cnt+V_INTR) ! cnt.v_intr++; (clobbers %o0,%o1)
/*
* Level 15 interrupts are nonmaskable, so with traps off,
* disable all interrupts to prevent recursion.
*/
sethi %hi(IE_reg_addr), %o0
ldub [%o0 + %lo(IE_reg_addr)], %o1
andn %o0, IE_ALLIE, %o1
stb %o1, [%o0 + %lo(IE_reg_addr)]
wr %l0, PSR_ET, %psr ! okay, turn traps on again
std %g2, [%sp + CCFSZ + 0] ! save g2, g3
rd %y, %l4 ! save y
std %g4, [%sp + CCFSZ + 8] ! save g4, g5
mov %g1, %l5 ! save g1, g6, g7
mov %g6, %l6
mov %g7, %l7
#if defined(SUN4C) || defined(SUN4M)
b,a nmi_common
#endif /* SUN4C || SUN4M */
#endif
#if defined(SUN4C)
nmi_sun4c:
INTR_SETUP(-CCFSZ-80)
INCR(_cnt+V_INTR) ! cnt.v_intr++; (clobbers %o0,%o1)
/*
* Level 15 interrupts are nonmaskable, so with traps off,
* disable all interrupts to prevent recursion.
*/
sethi %hi(IE_reg_addr), %o0
ldub [%o0 + %lo(IE_reg_addr)], %o1
andn %o0, IE_ALLIE, %o1
stb %o1, [%o0 + %lo(IE_reg_addr)]
wr %l0, PSR_ET, %psr ! okay, turn traps on again
std %g2, [%sp + CCFSZ + 0] ! save g2, g3
rd %y, %l4 ! save y
! must read the sync error register too.
set AC_SYNC_ERR, %o0
lda [%o0] ASI_CONTROL, %o1 ! sync err reg
inc 4, %o0
lda [%o0] ASI_CONTROL, %o2 ! sync virt addr
std %g4, [%sp + CCFSZ + 8] ! save g4,g5
mov %g1, %l5 ! save g1,g6,g7
mov %g6, %l6
mov %g7, %l7
inc 4, %o0
lda [%o0] ASI_CONTROL, %o3 ! async err reg
inc 4, %o0
lda [%o0] ASI_CONTROL, %o4 ! async virt addr
#if defined(SUN4M)
!!b,a nmi_common
#endif /* SUN4M */
#endif /* SUN4C */
nmi_common:
! and call C code
call _memerr ! memerr(0, ser, sva, aer, ava)
clr %o0
mov %l5, %g1 ! restore g1 through g7
ldd [%sp + CCFSZ + 0], %g2
ldd [%sp + CCFSZ + 8], %g4
wr %l0, 0, %psr ! re-disable traps
mov %l6, %g6
mov %l7, %g7
! set IE_ALLIE again (safe, we disabled traps again above)
sethi %hi(IE_reg_addr), %o0
ldub [%o0 + %lo(IE_reg_addr)], %o1
or %o1, IE_ALLIE, %o1
stb %o1, [%o0 + %lo(IE_reg_addr)]
b return_from_trap
wr %l4, 0, %y ! restore y
#if defined(SUN4M)
nmi_sun4m:
INTR_SETUP(-CCFSZ-80)
INCR(_cnt+V_INTR) ! cnt.v_intr++; (clobbers %o0,%o1)
/*
* Level 15 interrupts are nonmaskable, so with traps off,
* disable all interrupts to prevent recursion.
*/
sethi %hi(ICR_SI_SET), %o0
set SINTR_MA, %o1
st %o1, [%o0 + %lo(ICR_SI_SET)]
/* Now clear the NMI */
sethi %hi(ICR_PI_CLR), %o0
set PINTR_IC, %o1
st %o1, [%o0 + %lo(ICR_PI_CLR)]
wr %l0, PSR_ET, %psr ! okay, turn traps on again
std %g2, [%sp + CCFSZ + 0] ! save g2, g3
rd %y, %l4 ! save y
! now read sync error registers
set SRMMU_SFADDR, %o0
lda [%o0] ASI_SRMMU, %o2 ! sync virt addr
set SRMMU_SFSTAT, %o0
lda [%o0] ASI_SRMMU, %o1 ! sync err reg
std %g4, [%sp + CCFSZ + 8] ! save g4,g5
/* Now test for a HyperSPARC. If we have one, get the async status */
sethi %hi(_mmumod), %o3 ! get MMU model
ld [%o3 + %lo(_mmumod)], %o3
cmp %o3, SUN4M_MMU_HS ! is it hypersparc?
be 1f ! yup, skip ahead
clr %o3 ! clear %o3 and %o4, not hypersparc
b 2f
clr %o4
1:
set SRMMU_AFSTAT, %o3 ! read status first on hypersparc
lda [%o3] ASI_SRMMU, %o3 ! get async fault status
set SRMMU_AFADDR, %o4
lda [%o4] ASI_SRMMU, %o4 ! get async fault address
2:
/* Finish stackframe, call C trap handler */
mov %g1, %l5 ! save g1,g6,g7
mov %g6, %l6
mov %g7, %l7
clr %o5
call _memerr4m ! memerr4m(0, sfsr, sfva, afsr, afva)
clr %o0
mov %l5, %g1 ! restore g1 through g7
ldd [%sp + CCFSZ + 0], %g2
ldd [%sp + CCFSZ + 8], %g4
wr %l0, 0, %psr ! re-disable traps
mov %l6, %g6
mov %l7, %g7
! enable interrupts again (safe, we disabled traps again above)
sethi %hi(ICR_SI_CLR), %o0
set SINTR_MA, %o1
st %o1, [%o0 + %lo(ICR_SI_CLR)]
b return_from_trap
wr %l4, 0, %y ! restore y
#endif /* SUN4M */
#ifdef GPROF
.globl window_of, winof_user
.globl window_uf, winuf_user, winuf_ok, winuf_invalid
.globl return_from_trap, rft_kernel, rft_user, rft_invalid
.globl softtrap, slowtrap
.globl clean_trap_window, syscall
#endif
/*
* Window overflow trap handler.
* %l0 = %psr
* %l1 = return pc
* %l2 = return npc
*/
window_of:
#ifdef TRIVIAL_WINDOW_OVERFLOW_HANDLER
/* a trivial version that assumes %sp is ok */
/* (for testing only!) */
save %g0, %g0, %g0
std %l0, [%sp + (0*8)]
rd %psr, %l0
mov 1, %l1
sll %l1, %l0, %l0
wr %l0, 0, %wim
std %l2, [%sp + (1*8)]
std %l4, [%sp + (2*8)]
std %l6, [%sp + (3*8)]
std %i0, [%sp + (4*8)]
std %i2, [%sp + (5*8)]
std %i4, [%sp + (6*8)]
std %i6, [%sp + (7*8)]
restore
RETT
#else
/*
* This is similar to TRAP_SETUP, but we do not want to spend
* a lot of time, so we have separate paths for kernel and user.
* We also know for sure that the window has overflowed.
*/
btst PSR_PS, %l0
bz winof_user
sethi %hi(clean_trap_window), %l7
/*
* Overflow from kernel mode. Call clean_trap_window to
* do the dirty work, then just return, since we know prev
* window is valid. clean_trap_windows might dump all *user*
* windows into the pcb, but we do not care: there is at
* least one kernel window (a trap or interrupt frame!)
* above us.
*/
jmpl %l7 + %lo(clean_trap_window), %l4
mov %g7, %l7 ! for clean_trap_window
wr %l0, 0, %psr ! put back the @%*! cond. codes
nop ! (let them settle in)
RETT
winof_user:
/*
* Overflow from user mode.
* If clean_trap_window dumps the registers into the pcb,
* rft_user will need to call trap(), so we need space for
* a trap frame. We also have to compute pcb_nw.
*
* SHOULD EXPAND IN LINE TO AVOID BUILDING TRAP FRAME ON
* `EASY' SAVES
*/
sethi %hi(_cpcb), %l6
ld [%l6 + %lo(_cpcb)], %l6
ld [%l6 + PCB_WIM], %l5
and %l0, 31, %l3
sub %l3, %l5, %l5 /* l5 = CWP - pcb_wim */
set uwtab, %l4
ldub [%l4 + %l5], %l5 /* l5 = uwtab[l5] */
st %l5, [%l6 + PCB_UW]
jmpl %l7 + %lo(clean_trap_window), %l4
mov %g7, %l7 ! for clean_trap_window
sethi %hi(_cpcb), %l6
ld [%l6 + %lo(_cpcb)], %l6
set USPACE-CCFSZ-80, %l5
add %l6, %l5, %sp /* over to kernel stack */
CHECK_SP_REDZONE(%l6, %l5)
/*
* Copy return_from_trap far enough to allow us
* to jump directly to rft_user_or_recover_pcb_windows
* (since we know that is where we are headed).
*/
! and %l0, 31, %l3 ! still set (clean_trap_window
! leaves this register alone)
set wmask, %l6
ldub [%l6 + %l3], %l5 ! %l5 = 1 << ((CWP + 1) % nwindows)
b rft_user_or_recover_pcb_windows
rd %wim, %l4 ! (read %wim first)
#endif /* end `real' version of window overflow trap handler */
/*
* Window underflow trap handler.
* %l0 = %psr
* %l1 = return pc
* %l2 = return npc
*
* A picture:
*
* T R I X
* 0 0 0 1 0 0 0 (%wim)
* [bit numbers increase towards the right;
* `restore' moves right & `save' moves left]
*
* T is the current (Trap) window, R is the window that attempted
* a `Restore' instruction, I is the Invalid window, and X is the
* window we want to make invalid before we return.
*
* Since window R is valid, we cannot use rft_user to restore stuff
* for us. We have to duplicate its logic. YUCK.
*
* Incidentally, TRIX are for kids. Silly rabbit!
*/
window_uf:
#ifdef TRIVIAL_WINDOW_UNDERFLOW_HANDLER
wr %g0, 0, %wim ! allow us to enter I
restore ! to R
nop
nop
restore ! to I
restore %g0, 1, %l1 ! to X
rd %psr, %l0
sll %l1, %l0, %l0
wr %l0, 0, %wim
save %g0, %g0, %g0 ! back to I
LOADWIN(%sp)
save %g0, %g0, %g0 ! back to R
save %g0, %g0, %g0 ! back to T
RETT
#else
wr %g0, 0, %wim ! allow us to enter I
btst PSR_PS, %l0
restore ! enter window R
bz winuf_user
restore ! enter window I
/*
* Underflow from kernel mode. Just recover the
* registers and go (except that we have to update
* the blasted user pcb fields).
*/
restore %g0, 1, %l1 ! enter window X, then set %l1 to 1
rd %psr, %l0 ! cwp = %psr & 31;
and %l0, 31, %l0
sll %l1, %l0, %l1 ! wim = 1 << cwp;
wr %l1, 0, %wim ! setwim(wim);
sethi %hi(_cpcb), %l1
ld [%l1 + %lo(_cpcb)], %l1
st %l0, [%l1 + PCB_WIM] ! cpcb->pcb_wim = cwp;
save %g0, %g0, %g0 ! back to window I
LOADWIN(%sp)
save %g0, %g0, %g0 ! back to R
save %g0, %g0, %g0 ! and then to T
wr %l0, 0, %psr ! fix those cond codes....
nop ! (let them settle in)
RETT
winuf_user:
/*
* Underflow from user mode.
*
* We cannot use rft_user (as noted above) because
* we must re-execute the `restore' instruction.
* Since it could be, e.g., `restore %l0,0,%l0',
* it is not okay to touch R's registers either.
*
* We are now in window I.
*/
btst 7, %sp ! if unaligned, it is invalid
bne winuf_invalid
EMPTY
sethi %hi(_pgofset), %l4
ld [%l4 + %lo(_pgofset)], %l4
PTE_OF_ADDR(%sp, %l7, winuf_invalid, %l4)
CMP_PTE_USER_READ(%l7, %l5) ! if first page not readable,
bne winuf_invalid ! it is invalid
EMPTY
SLT_IF_1PAGE_RW(%sp, %l7, %l4) ! first page is readable
bl,a winuf_ok ! if only one page, enter window X
restore %g0, 1, %l1 ! and goto ok, & set %l1 to 1
add %sp, 7*8, %l5
add %l4, 62, %l4
PTE_OF_ADDR(%l5, %l7, winuf_invalid, %l4)
CMP_PTE_USER_READ(%l7, %l5) ! check second page too
be,a winuf_ok ! enter window X and goto ok
restore %g0, 1, %l1 ! (and then set %l1 to 1)
winuf_invalid:
/*
* We were unable to restore the window because %sp
* is invalid or paged out. Return to the trap window
* and call trap(T_WINUF). This will save R to the user
* stack, then load both R and I into the pcb rw[] area,
* and return with pcb_nsaved set to -1 for success, 0 for
* failure. `Failure' indicates that someone goofed with the
* trap registers (e.g., signals), so that we need to return
* from the trap as from a syscall (probably to a signal handler)
* and let it retry the restore instruction later. Note that
* window R will have been pushed out to user space, and thus
* be the invalid window, by the time we get back here. (We
* continue to label it R anyway.) We must also set %wim again,
* and set pcb_uw to 1, before enabling traps. (Window R is the
* only window, and it is a user window).
*/
save %g0, %g0, %g0 ! back to R
save %g0, 1, %l4 ! back to T, then %l4 = 1
sethi %hi(_cpcb), %l6
ld [%l6 + %lo(_cpcb)], %l6
st %l4, [%l6 + PCB_UW] ! pcb_uw = 1
ld [%l6 + PCB_WIM], %l5 ! get log2(%wim)
sll %l4, %l5, %l4 ! %l4 = old %wim
wr %l4, 0, %wim ! window I is now invalid again
set USPACE-CCFSZ-80, %l5
add %l6, %l5, %sp ! get onto kernel stack
CHECK_SP_REDZONE(%l6, %l5)
/*
* Okay, call trap(T_WINUF, psr, pc, &tf).
* See `slowtrap' above for operation.
*/
wr %l0, PSR_ET, %psr
std %l0, [%sp + CCFSZ + 0] ! tf.tf_psr, tf.tf_pc
rd %y, %l3
std %l2, [%sp + CCFSZ + 8] ! tf.tf_npc, tf.tf_y
mov T_WINUF, %o0
st %g1, [%sp + CCFSZ + 20] ! tf.tf_global[1]
mov %l0, %o1
std %g2, [%sp + CCFSZ + 24] ! etc
mov %l1, %o2
std %g4, [%sp + CCFSZ + 32]
add %sp, CCFSZ, %o3
std %g6, [%sp + CCFSZ + 40]
std %i0, [%sp + CCFSZ + 48] ! tf.tf_out[0], etc
std %i2, [%sp + CCFSZ + 56]
std %i4, [%sp + CCFSZ + 64]
call _trap ! trap(T_WINUF, pc, psr, &tf)
std %i6, [%sp + CCFSZ + 72] ! tf.tf_out[6]
ldd [%sp + CCFSZ + 0], %l0 ! new psr, pc
ldd [%sp + CCFSZ + 8], %l2 ! new npc, %y
wr %l3, 0, %y
ld [%sp + CCFSZ + 20], %g1
ldd [%sp + CCFSZ + 24], %g2
ldd [%sp + CCFSZ + 32], %g4
ldd [%sp + CCFSZ + 40], %g6
ldd [%sp + CCFSZ + 48], %i0 ! %o0 for window R, etc
ldd [%sp + CCFSZ + 56], %i2
ldd [%sp + CCFSZ + 64], %i4
wr %l0, 0, %psr ! disable traps: test must be atomic
ldd [%sp + CCFSZ + 72], %i6
sethi %hi(_cpcb), %l6
ld [%l6 + %lo(_cpcb)], %l6
ld [%l6 + PCB_NSAVED], %l7 ! if nsaved is -1, we have our regs
tst %l7
bl,a 1f ! got them
wr %g0, 0, %wim ! allow us to enter windows R, I
b,a return_from_trap
/*
* Got 'em. Load 'em up.
*/
1:
mov %g6, %l3 ! save %g6; set %g6 = cpcb
mov %l6, %g6
st %g0, [%g6 + PCB_NSAVED] ! and clear magic flag
restore ! from T to R
restore ! from R to I
restore %g0, 1, %l1 ! from I to X, then %l1 = 1
rd %psr, %l0 ! cwp = %psr;
sll %l1, %l0, %l1
wr %l1, 0, %wim ! make window X invalid
and %l0, 31, %l0
st %l0, [%g6 + PCB_WIM] ! cpcb->pcb_wim = cwp;
nop ! unnecessary? old wim was 0...
save %g0, %g0, %g0 ! back to I
LOADWIN(%g6 + PCB_RW + 64) ! load from rw[1]
save %g0, %g0, %g0 ! back to R
LOADWIN(%g6 + PCB_RW) ! load from rw[0]
save %g0, %g0, %g0 ! back to T
wr %l0, 0, %psr ! restore condition codes
mov %l3, %g6 ! fix %g6
RETT
/*
* Restoring from user stack, but everything has checked out
* as good. We are now in window X, and %l1 = 1. Window R
* is still valid and holds user values.
*/
winuf_ok:
rd %psr, %l0
sll %l1, %l0, %l1
wr %l1, 0, %wim ! make this one invalid
sethi %hi(_cpcb), %l2
ld [%l2 + %lo(_cpcb)], %l2
and %l0, 31, %l0
st %l0, [%l2 + PCB_WIM] ! cpcb->pcb_wim = cwp;
save %g0, %g0, %g0 ! back to I
LOADWIN(%sp)
save %g0, %g0, %g0 ! back to R
save %g0, %g0, %g0 ! back to T
wr %l0, 0, %psr ! restore condition codes
nop ! it takes three to tangle
RETT
#endif /* end `real' version of window underflow trap handler */
/*
* Various return-from-trap routines (see return_from_trap).
*/
/*
* Return from trap, to kernel.
* %l0 = %psr
* %l1 = return pc
* %l2 = return npc
* %l4 = %wim
* %l5 = bit for previous window
*/
rft_kernel:
btst %l5, %l4 ! if (wim & l5)
bnz 1f ! goto reload;
wr %l0, 0, %psr ! but first put !@#*% cond codes back
/* previous window is valid; just rett */
nop ! wait for cond codes to settle in
RETT
/*
* Previous window is invalid.
* Update %wim and then reload l0..i7 from frame.
*
* T I X
* 0 0 1 0 0 (%wim)
* [see picture in window_uf handler]
*
* T is the current (Trap) window, I is the Invalid window,
* and X is the window we want to make invalid. Window X
* currently has no useful values.
*/
1:
wr %g0, 0, %wim ! allow us to enter window I
nop; nop; nop ! (it takes a while)
restore ! enter window I
restore %g0, 1, %l1 ! enter window X, then %l1 = 1
rd %psr, %l0 ! CWP = %psr & 31;
and %l0, 31, %l0
sll %l1, %l0, %l1 ! wim = 1 << CWP;
wr %l1, 0, %wim ! setwim(wim);
sethi %hi(_cpcb), %l1
ld [%l1 + %lo(_cpcb)], %l1
st %l0, [%l1 + PCB_WIM] ! cpcb->pcb_wim = l0 & 31;
save %g0, %g0, %g0 ! back to window I
LOADWIN(%sp)
save %g0, %g0, %g0 ! back to window T
/*
* Note that the condition codes are still set from
* the code at rft_kernel; we can simply return.
*/
RETT
/*
* Return from trap, to user. Checks for scheduling trap (`ast') first;
* will re-enter trap() if set. Note that we may have to switch from
* the interrupt stack to the kernel stack in this case.
* %l0 = %psr
* %l1 = return pc
* %l2 = return npc
* %l4 = %wim
* %l5 = bit for previous window
* %l6 = cpcb
* If returning to a valid window, just set psr and return.
*/
rft_user:
! sethi %hi(_want_ast), %l7 ! (done below)
ld [%l7 + %lo(_want_ast)], %l7
tst %l7 ! want AST trap?
bne,a softtrap ! yes, re-enter trap with type T_AST
mov T_AST, %o0
btst %l5, %l4 ! if (wim & l5)
bnz 1f ! goto reload;
wr %l0, 0, %psr ! restore cond codes
nop ! (three instruction delay)
RETT
/*
* Previous window is invalid.
* Before we try to load it, we must verify its stack pointer.
* This is much like the underflow handler, but a bit easier
* since we can use our own local registers.
*/
1:
btst 7, %fp ! if unaligned, address is invalid
bne rft_invalid
EMPTY
sethi %hi(_pgofset), %l3
ld [%l3 + %lo(_pgofset)], %l3
PTE_OF_ADDR(%fp, %l7, rft_invalid, %l3)
CMP_PTE_USER_READ(%l7, %l5) ! try first page
bne rft_invalid ! no good
EMPTY
SLT_IF_1PAGE_RW(%fp, %l7, %l3)
bl,a rft_user_ok ! only 1 page: ok
wr %g0, 0, %wim
add %fp, 7*8, %l5
add %l3, 62, %l3
PTE_OF_ADDR(%l5, %l7, rft_invalid, %l3)
CMP_PTE_USER_READ(%l7, %l5) ! check 2nd page too
be,a rft_user_ok
wr %g0, 0, %wim
/*
* The window we wanted to pull could not be pulled. Instead,
* re-enter trap with type T_RWRET. This will pull the window
* into cpcb->pcb_rw[0] and set cpcb->pcb_nsaved to -1, which we
* will detect when we try to return again.
*/
rft_invalid:
b softtrap
mov T_RWRET, %o0
/*
* The window we want to pull can be pulled directly.
*/
rft_user_ok:
! wr %g0, 0, %wim ! allow us to get into it
wr %l0, 0, %psr ! fix up the cond codes now
nop; nop; nop
restore ! enter window I
restore %g0, 1, %l1 ! enter window X, then %l1 = 1
rd %psr, %l0 ! l0 = (junk << 5) + CWP;
sll %l1, %l0, %l1 ! %wim = 1 << CWP;
wr %l1, 0, %wim
sethi %hi(_cpcb), %l1
ld [%l1 + %lo(_cpcb)], %l1
and %l0, 31, %l0
st %l0, [%l1 + PCB_WIM] ! cpcb->pcb_wim = l0 & 31;
save %g0, %g0, %g0 ! back to window I
LOADWIN(%sp) ! suck hard
save %g0, %g0, %g0 ! back to window T
RETT
/*
* Return from trap. Entered after a
* wr %l0, 0, %psr
* which disables traps so that we can rett; registers are:
*
* %l0 = %psr
* %l1 = return pc
* %l2 = return npc
*
* (%l3..%l7 anything).
*
* If we are returning to user code, we must:
* 1. Check for register windows in the pcb that belong on the stack.
* If there are any, reenter trap with type T_WINOF.
* 2. Make sure the register windows will not underflow. This is
* much easier in kernel mode....
*/
return_from_trap:
! wr %l0, 0, %psr ! disable traps so we can rett
! (someone else did this already)
and %l0, 31, %l5
set wmask, %l6
ldub [%l6 + %l5], %l5 ! %l5 = 1 << ((CWP + 1) % nwindows)
btst PSR_PS, %l0 ! returning to userland?
bnz rft_kernel ! no, go return to kernel
rd %wim, %l4 ! (read %wim in any case)
rft_user_or_recover_pcb_windows:
/*
* (entered with %l4=%wim, %l5=wmask[cwp]; %l0..%l2 as usual)
*
* check cpcb->pcb_nsaved:
* if 0, do a `normal' return to user (see rft_user);
* if > 0, cpcb->pcb_rw[] holds registers to be copied to stack;
* if -1, cpcb->pcb_rw[0] holds user registers for rett window
* from an earlier T_RWRET pseudo-trap.
*/
sethi %hi(_cpcb), %l6
ld [%l6 + %lo(_cpcb)], %l6
ld [%l6 + PCB_NSAVED], %l7
tst %l7
bz,a rft_user
sethi %hi(_want_ast), %l7 ! first instr of rft_user
bg,a softtrap ! if (pcb_nsaved > 0)
mov T_WINOF, %o0 ! trap(T_WINOF);
/*
* To get here, we must have tried to return from a previous
* trap and discovered that it would cause a window underflow.
* We then must have tried to pull the registers out of the
* user stack (from the address in %fp==%i6) and discovered
* that it was either unaligned or not loaded in memory, and
* therefore we ran a trap(T_RWRET), which loaded one set of
* registers into cpcb->pcb_pcb_rw[0] (if it had killed the
* process due to a bad stack, we would not be here).
*
* We want to load pcb_rw[0] into the previous window, which
* we know is currently invalid. In other words, we want
* %wim to be 1 << ((cwp + 2) % nwindows).
*/
wr %g0, 0, %wim ! enable restores
mov %g6, %l3 ! save g6 in l3
mov %l6, %g6 ! set g6 = &u
st %g0, [%g6 + PCB_NSAVED] ! clear cpcb->pcb_nsaved
restore ! enter window I
restore %g0, 1, %l1 ! enter window X, then %l1 = 1
rd %psr, %l0
sll %l1, %l0, %l1 ! %wim = 1 << CWP;
wr %l1, 0, %wim
and %l0, 31, %l0
st %l0, [%g6 + PCB_WIM] ! cpcb->pcb_wim = CWP;
nop ! unnecessary? old wim was 0...
save %g0, %g0, %g0 ! back to window I
LOADWIN(%g6 + PCB_RW)
save %g0, %g0, %g0 ! back to window T (trap window)
wr %l0, 0, %psr ! cond codes, cond codes everywhere
mov %l3, %g6 ! restore g6
RETT
! exported end marker for kernel gdb
.globl _endtrapcode
_endtrapcode:
/*
* init_tables(nwin) int nwin;
*
* Set up the uwtab and wmask tables.
* We know nwin > 1.
*/
init_tables:
/*
* for (i = -nwin, j = nwin - 2; ++i < 0; j--)
* uwtab[i] = j;
* (loop runs at least once)
*/
set uwtab, %o3
sub %g0, %o0, %o1 ! i = -nwin + 1
inc %o1
add %o0, -2, %o2 ! j = nwin - 2;
0:
stb %o2, [%o3 + %o1] ! uwtab[i] = j;
1:
inccc %o1 ! ++i < 0?
bl 0b ! yes, continue loop
dec %o2 ! in any case, j--
/*
* (i now equals 0)
* for (j = nwin - 1; i < nwin; i++, j--)
* uwtab[i] = j;
* (loop runs at least twice)
*/
sub %o0, 1, %o2 ! j = nwin - 1
0:
stb %o2, [%o3 + %o1] ! uwtab[i] = j
inc %o1 ! i++
1:
cmp %o1, %o0 ! i < nwin?
bl 0b ! yes, continue
dec %o2 ! in any case, j--
/*
* We observe that, for i in 0..nwin-2, (i+1)%nwin == i+1;
* for i==nwin-1, (i+1)%nwin == 0.
* To avoid adding 1, we run i from 1 to nwin and set
* wmask[i-1].
*
* for (i = j = 1; i < nwin; i++) {
* j <<= 1; (j now == 1 << i)
* wmask[i - 1] = j;
* }
* (loop runs at least once)
*/
set wmask - 1, %o3
mov 1, %o1 ! i = 1;
mov 2, %o2 ! j = 2;
0:
stb %o2, [%o3 + %o1] ! (wmask - 1)[i] = j;
inc %o1 ! i++
cmp %o1, %o0 ! i < nwin?
bl,a 0b ! yes, continue
sll %o2, 1, %o2 ! (and j <<= 1)
/*
* Now i==nwin, so we want wmask[i-1] = 1.
*/
mov 1, %o2 ! j = 1;
retl
stb %o2, [%o3 + %o1] ! (wmask - 1)[i] = j;
#ifdef SUN4
/*
* getidprom(struct idprom *, sizeof(struct idprom))
*/
.global _getidprom
_getidprom:
set AC_IDPROM, %o2
1: lduba [%o2] ASI_CONTROL, %o3
stb %o3, [%o0]
inc %o0
inc %o2
dec %o1
cmp %o1, 0
bne 1b
nop
retl
nop
#endif
dostart:
/*
* Startup.
*
* We have been loaded in low RAM, at some address which
* is page aligned (0x4000 actually) rather than where we
* want to run (0xf8004000). Until we get everything set,
* we have to be sure to use only pc-relative addressing.
*/
#ifdef DDB
/*
* First, check for DDB arguments. The loader passes `_esym' in %o4.
* A DDB magic number is passed in %o5 to allow for bootloaders
* that know nothing about DDB symbol loading conventions.
* Note: we don't touch %o1-%o3; SunOS bootloaders seem to use them
* for their own mirky business.
*/
set 0x44444230, %l3
cmp %o5, %l3 ! chk magic
bne 1f
tst %o4 ! do we have the symbols?
bz 1f
nop
sethi %hi(_esym - KERNBASE), %l3 ! store _esym
st %o4, [%l3 + %lo(_esym - KERNBASE)]
1:
#endif
/*
* Sun4 passes in the `load address'. Although possible, its highly
* unlikely that OpenBoot would place the prom vector there.
*/
set 0x4000, %g7
cmp %o0, %g7
be is_sun4
nop
#if defined(SUN4C) || defined(SUN4M)
mov %o0, %g7 ! save prom vector pointer
/*
* are we on a sun4c or a sun4m?
*/
ld [%g7 + PV_NODEOPS], %o4 ! node = pv->pv_nodeops->no_nextnode(0)
ld [%o4 + NO_NEXTNODE], %o4
call %o4
mov 0, %o0 ! node
mov %o0, %l0
set _cputypvar-KERNBASE, %o1 ! name = "compatible"
set _cputypval-KERNBASE, %o2 ! buffer ptr (assume buffer long enough)
ld [%g7 + PV_NODEOPS], %o4 ! (void)pv->pv_nodeops->no_getprop(...)
ld [%o4 + NO_GETPROP], %o4
call %o4
nop
set _cputypval-KERNBASE, %o2 ! buffer ptr
ldub [%o2 + 4], %o0 ! which is it... "sun4c", "sun4m", "sun4d"?
cmp %o0, 'c'
be is_sun4c
nop
cmp %o0, 'm'
be is_sun4m
nop
#endif /* SUN4C || SUN4M */
! ``on a sun4d?! hell no!''
ld [%g7 + PV_HALT], %o1 ! by this kernel, then halt
call %o1
nop
is_sun4m:
#if defined(SUN4M)
set trapbase_sun4m, %g6
mov SUN4CM_PGSHIFT, %g5
b start_havetype
mov CPU_SUN4M, %g4
#else
set sun4m_notsup-KERNBASE, %o0
ld [%g7 + PV_EVAL], %o1
call %o1 ! print a message saying that the
nop ! sun4m architecture is not supported
ld [%g7 + PV_HALT], %o1 ! by this kernel, then halt
call %o1
nop
/*NOTREACHED*/
#endif
is_sun4c:
#if defined(SUN4C)
set trapbase_sun4c, %g6
mov SUN4CM_PGSHIFT, %g5
set AC_CONTEXT, %g1 ! paranoia: set context to kernel
stba %g0, [%g1] ASI_CONTROL
b start_havetype
mov CPU_SUN4C, %g4 ! XXX CPU_SUN4
#else
set sun4c_notsup-KERNBASE, %o0
ld [%g7 + PV_ROMVEC_VERS], %o1
cmp %o1, 0
bne 1f
nop
! stupid version 0 rom interface is pv_eval(int length, char *string)
mov %o0, %o1
2: ldub [%o0], %o4
bne 2b
inc %o0
dec %o0
sub %o0, %o1, %o0
1: ld [%g7 + PV_EVAL], %o2
call %o2 ! print a message saying that the
nop ! sun4c architecture is not supported
ld [%g7 + PV_HALT], %o1 ! by this kernel, then halt
call %o1
nop
/*NOTREACHED*/
#endif
is_sun4:
#if defined(SUN4)
set trapbase_sun4, %g6
mov SUN4_PGSHIFT, %g5
set AC_CONTEXT, %g1 ! paranoia: set context to kernel
stba %g0, [%g1] ASI_CONTROL
b start_havetype
mov CPU_SUN4, %g4
#else
set PROM_BASE, %g7
set sun4_notsup-KERNBASE, %o0
ld [%g7 + OLDMON_PRINTF], %o1
call %o1 ! print a message saying that the
nop ! sun4 architecture is not supported
ld [%g7 + OLDMON_HALT], %o1 ! by this kernel, then halt
call %o1
nop
/*NOTREACHED*/
#endif
start_havetype:
/*
* Step 1: double map low RAM (addresses [0.._end-start-1])
* to KERNBASE (addresses [KERNBASE.._end-1]). None of these
* are `bad' aliases (since they are all on segment boundaries)
* so we do not have to worry about cache aliasing.
*
* We map in another couple of segments just to have some
* more memory (512K, actually) guaranteed available for
* bootstrap code (pmap_bootstrap needs memory to hold MMU
* and context data structures). Note: this is only relevant
* for 2-level MMU sun4/sun4c machines.
*/
clr %l0 ! lowva
set KERNBASE, %l1 ! highva
set _end + (2 << 18), %l2 ! last va that must be remapped
#ifdef DDB
sethi %hi(_esym - KERNBASE), %o1
ld [%o1+%lo(_esym - KERNBASE)], %o1
tst %o1
bz 1f
nop
set (2 << 18), %l2
add %l2, %o1, %l2 ! last va that must be remapped
1:
#endif
/*
* Need different initial mapping functions for different
* types of machines.
*/
#if defined(SUN4C)
cmp %g4, CPU_SUN4C
bne 1f
set 1 << 18, %l3 ! segment size in bytes
0:
lduba [%l0] ASI_SEGMAP, %l4 ! segmap[highva] = segmap[lowva];
stba %l4, [%l1] ASI_SEGMAP
add %l3, %l1, %l1 ! highva += segsiz;
cmp %l1, %l2 ! done?
blu 0b ! no, loop
add %l3, %l0, %l0 ! (and lowva += segsz)
/*
* Now map the interrupt enable register and clear any interrupts,
* enabling NMIs. Note that we will not take NMIs until we change
* %tbr.
*/
set IE_reg_addr, %l0
set IE_REG_PTE_PG, %l1
set INT_ENABLE_REG_PHYSADR, %l2
srl %l2, %g5, %l2
or %l2, %l1, %l1
sta %l1, [%l0] ASI_PTE
mov IE_ALLIE, %l1
nop; nop ! paranoia
stb %l1, [%l0]
b startmap_done
nop
1:
#endif /* SUN4C */
#if defined(SUN4)
cmp %g4, CPU_SUN4
bne 2f
#if defined(MMU_3L)
set AC_IDPROM+1, %l3
lduba [%l3] ASI_CONTROL, %l3
cmp %l3, 0x24 ! XXX - SUN4_400
bne no_3mmu
add %l0, 2, %l0 ! get to proper half-word in RG space
add %l1, 2, %l1
lduha [%l0] ASI_REGMAP, %l4 ! regmap[highva] = regmap[lowva];
stha %l4, [%l1] ASI_REGMAP
b,a remap_done
no_3mmu:
#endif
set 1 << 18, %l3 ! segment size in bytes
0:
lduha [%l0] ASI_SEGMAP, %l4 ! segmap[highva] = segmap[lowva];
stha %l4, [%l1] ASI_SEGMAP
add %l3, %l1, %l1 ! highva += segsiz;
cmp %l1, %l2 ! done?
blu 0b ! no, loop
add %l3, %l0, %l0 ! (and lowva += segsz)
remap_done:
/*
* Now map the interrupt enable register and clear any interrupts,
* enabling NMIs. Note that we will not take NMIs until we change
* %tbr.
*/
set IE_reg_addr, %l0
set IE_REG_PTE_PG, %l1
set INT_ENABLE_REG_PHYSADR, %l2
srl %l2, %g5, %l2
or %l2, %l1, %l1
sta %l1, [%l0] ASI_PTE
mov IE_ALLIE, %l1
nop; nop ! paranoia
stb %l1, [%l0]
b,a startmap_done
2:
#endif /* SUN4 */
#if defined(SUN4M)
cmp %g4, CPU_SUN4M ! skip for sun4m!
bne 3f
/*
* The OBP guarantees us a 16MB mapping using a level 1 PTE at
* 0x0. All we have to do is copy the entry. Also, we must
* check to see if we have a TI Viking in non-mbus mode, and
* if so do appropriate flipping and turning off traps before
* we dork with MMU passthrough. -grrr
*/
sethi %hi(0x40000000), %o1 ! TI version bit
rd %psr, %o0
andcc %o0, %o1, %g0
be remap_notvik ! is non-TI normal MBUS module
lda [%g0] ASI_SRMMU, %o0 ! load MMU
andcc %o0, 0x800, %g0
bne remap_notvik ! It is a viking MBUS module
nop
/*
* Ok, we have a non-Mbus TI Viking, a MicroSparc.
* In this scenerio, in order to play with the MMU
* passthrough safely, we need turn off traps, flip
* the AC bit on in the mmu status register, do our
* passthroughs, then restore the mmu reg and %psr
*/
rd %psr, %o4 ! saved here till done
andn %o4, 0x20, %o5
wr %o5, 0x0, %psr
nop; nop; nop;
set SRMMU_CXTPTR, %o0
lda [%o0] ASI_SRMMU, %o0 ! get context table ptr
sll %o0, 4, %o0 ! make physical
lda [%g0] ASI_SRMMU, %o3 ! hold mmu-sreg here
/* 0x8000 is AC bit in Viking mmu-ctl reg */
set 0x8000, %o2
or %o3, %o2, %o2
sta %o2, [%g0] ASI_SRMMU ! AC bit on
lda [%o0] ASI_BYPASS, %o1
srl %o1, 4, %o1
sll %o1, 8, %o1 ! get phys addr of l1 entry
lda [%o1] ASI_BYPASS, %l4
srl %l1, 22, %o2 ! note: 22 == RGSHIFT - 2
add %o1, %o2, %o1
sta %l4, [%o1] ASI_BYPASS
sta %o3, [%g0] ASI_SRMMU ! restore mmu-sreg
wr %o4, 0x0, %psr ! restore psr
b,a startmap_done
/*
* The following is generic and should work on all
* Mbus based SRMMU's.
*/
remap_notvik:
set SRMMU_CXTPTR, %o0
lda [%o0] ASI_SRMMU, %o0 ! get context table ptr
sll %o0, 4, %o0 ! make physical
lda [%o0] ASI_BYPASS, %o1
srl %o1, 4, %o1
sll %o1, 8, %o1 ! get phys addr of l1 entry
lda [%o1] ASI_BYPASS, %l4
srl %l1, 22, %o2 ! note: 22 == RGSHIFT - 2
add %o1, %o2, %o1
sta %l4, [%o1] ASI_BYPASS
!b,a startmap_done
3:
#endif /* SUN4M */
! botch! We should blow up.
startmap_done:
/*
* All set, fix pc and npc. Once we are where we should be,
* we can give ourselves a stack and enable traps.
*/
set 1f, %g1
jmp %g1
nop
1:
sethi %hi(_cputyp), %o0 ! what type of cpu we are on
st %g4, [%o0 + %lo(_cputyp)]
sethi %hi(_pgshift), %o0 ! pgshift = log2(nbpg)
st %g5, [%o0 + %lo(_pgshift)]
mov 1, %o0 ! nbpg = 1 << pgshift
sll %o0, %g5, %g5
sethi %hi(_nbpg), %o0 ! nbpg = bytes in a page
st %g5, [%o0 + %lo(_nbpg)]
sub %g5, 1, %g5
sethi %hi(_pgofset), %o0 ! page offset = bytes in a page - 1
st %g5, [%o0 + %lo(_pgofset)]
rd %psr, %g3 ! paranoia: make sure ...
andn %g3, PSR_ET, %g3 ! we have traps off
wr %g3, 0, %psr ! so that we can fiddle safely
nop; nop; nop
wr %g0, 0, %wim ! make sure we can set psr
nop; nop; nop
wr %g0, PSR_S|PSR_PS|PSR_PIL, %psr ! set initial psr
nop; nop; nop
wr %g0, 2, %wim ! set initial %wim (w1 invalid)
mov 1, %g1 ! set pcb_wim (log2(%wim) = 1)
sethi %hi(_u0 + PCB_WIM), %g2
st %g1, [%g2 + %lo(_u0 + PCB_WIM)]
set USRSTACK - CCFSZ, %fp ! as if called from user code
set estack0 - CCFSZ - 80, %sp ! via syscall(boot_me_up) or somesuch
rd %psr, %l0
wr %l0, PSR_ET, %psr
nop; nop; nop
/* Export actual trapbase */
sethi %hi(_trapbase), %o0
st %g6, [%o0+%lo(_trapbase)]
/*
* Step 2: clear BSS. This may just be paranoia; the boot
* loader might already do it for us; but what the hell.
*/
set _edata, %o0 ! bzero(edata, end - edata)
set _end, %o1
call _bzero
sub %o1, %o0, %o1
/*
* Stash prom vectors now, after bzero, as it lives in bss
* (which we just zeroed).
* This depends on the fact that bzero does not use %g7.
*/
sethi %hi(_promvec), %l0
st %g7, [%l0 + %lo(_promvec)]
/*
* Step 3: compute number of windows and set up tables.
* We could do some of this later.
*/
save %sp, -64, %sp
rd %psr, %g1
restore
and %g1, 31, %g1 ! want just the CWP bits
add %g1, 1, %o0 ! compute nwindows
sethi %hi(_nwindows), %o1 ! may as well tell everyone
call init_tables
st %o0, [%o1 + %lo(_nwindows)]
#if defined(SUN4)
/*
* Some sun4 models have fewer than 8 windows. For extra
* speed, we do not need to save/restore those windows
* The save/restore code has 7 "save"'s followed by 7
* "restore"'s -- we "nop" out the last "save" and first
* "restore"
*/
cmp %o0, 8
be 1f
noplab: nop
set noplab, %l0
ld [%l0], %l1
set wb1, %l0
st %l1, [%l0 + 6*4]
st %l1, [%l0 + 7*4]
1:
#endif
/*
* Step 4: change the trap base register, now that our trap handlers
* will function (they need the tables we just set up).
* This depends on the fact that bzero does not use %g6.
*/
wr %g6, 0, %tbr
nop; nop; nop ! paranoia
/*
* Ready to run C code; finish bootstrap.
*/
call _bootstrap
nop
/*
* Call main. This returns to us after loading /sbin/init into
* user space. (If the exec fails, main() does not return.)
*/
call _main
clr %o0 ! our frame arg is ignored
/*NOTREACHED*/
/*
* The following code is copied to the top of the user stack when each
* process is exec'ed, and signals are `trampolined' off it.
*
* When this code is run, the stack looks like:
* [%sp] 64 bytes to which registers can be dumped
* [%sp + 64] signal number (goes in %o0)
* [%sp + 64 + 4] signal code (goes in %o1)
* [%sp + 64 + 8] placeholder
* [%sp + 64 + 12] argument for %o3, currently unsupported (always 0)
* [%sp + 64 + 16] first word of saved state (sigcontext)
* .
* .
* .
* [%sp + NNN] last word of saved state
* (followed by previous stack contents or top of signal stack).
* The address of the function to call is in %g1; the old %g1 and %o0
* have already been saved in the sigcontext. We are running in a clean
* window, all previous windows now being saved to the stack.
*
* Note that [%sp + 64 + 8] == %sp + 64 + 16. The copy at %sp+64+8
* will eventually be removed, with a hole left in its place, if things
* work out.
*/
.globl _sigcode
.globl _esigcode
_sigcode:
/*
* XXX the `save' and `restore' below are unnecessary: should
* replace with simple arithmetic on %sp
*
* Make room on the stack for 32 %f registers + %fsr. This comes
* out to 33*4 or 132 bytes, but this must be aligned to a multiple
* of 8, or 136 bytes.
*/
save %sp, -CCFSZ - 136, %sp
mov %g2, %l2 ! save globals in %l registers
mov %g3, %l3
mov %g4, %l4
mov %g5, %l5
mov %g6, %l6
mov %g7, %l7
/*
* Saving the fpu registers is expensive, so do it iff the fsr
* stored in the sigcontext shows that the fpu is enabled.
*/
ld [%fp + 64 + 16 + SC_PSR_OFFSET], %l0
sethi %hi(PSR_EF), %l1 ! FPU enable bit is too high for andcc
andcc %l0, %l1, %l0 ! %l0 = fpu enable bit
be 1f ! if not set, skip the saves
rd %y, %l1 ! in any case, save %y
! fpu is enabled, oh well
st %fsr, [%sp + CCFSZ + 0]
std %f0, [%sp + CCFSZ + 8]
std %f2, [%sp + CCFSZ + 16]
std %f4, [%sp + CCFSZ + 24]
std %f6, [%sp + CCFSZ + 32]
std %f8, [%sp + CCFSZ + 40]
std %f10, [%sp + CCFSZ + 48]
std %f12, [%sp + CCFSZ + 56]
std %f14, [%sp + CCFSZ + 64]
std %f16, [%sp + CCFSZ + 72]
std %f18, [%sp + CCFSZ + 80]
std %f20, [%sp + CCFSZ + 88]
std %f22, [%sp + CCFSZ + 96]
std %f24, [%sp + CCFSZ + 104]
std %f26, [%sp + CCFSZ + 112]
std %f28, [%sp + CCFSZ + 120]
std %f30, [%sp + CCFSZ + 128]
1:
ldd [%fp + 64], %o0 ! sig, code
ld [%fp + 76], %o3 ! arg3
call %g1 ! (*sa->sa_handler)(sig,code,scp,arg3)
add %fp, 64 + 16, %o2 ! scp
/*
* Now that the handler has returned, re-establish all the state
* we just saved above, then do a sigreturn.
*/
tst %l0 ! reload fpu registers?
be 1f ! if not, skip the loads
wr %l1, %g0, %y ! in any case, restore %y
ld [%sp + CCFSZ + 0], %fsr
ldd [%sp + CCFSZ + 8], %f0
ldd [%sp + CCFSZ + 16], %f2
ldd [%sp + CCFSZ + 24], %f4
ldd [%sp + CCFSZ + 32], %f6
ldd [%sp + CCFSZ + 40], %f8
ldd [%sp + CCFSZ + 48], %f10
ldd [%sp + CCFSZ + 56], %f12
ldd [%sp + CCFSZ + 64], %f14
ldd [%sp + CCFSZ + 72], %f16
ldd [%sp + CCFSZ + 80], %f18
ldd [%sp + CCFSZ + 88], %f20
ldd [%sp + CCFSZ + 96], %f22
ldd [%sp + CCFSZ + 104], %f24
ldd [%sp + CCFSZ + 112], %f26
ldd [%sp + CCFSZ + 120], %f28
ldd [%sp + CCFSZ + 128], %f30
1:
mov %l2, %g2
mov %l3, %g3
mov %l4, %g4
mov %l5, %g5
mov %l6, %g6
mov %l7, %g7
restore %g0, SYS_sigreturn, %g1 ! get registers back & set syscall #
add %sp, 64 + 16, %o0 ! compute scp
t ST_SYSCALL ! sigreturn(scp)
! sigreturn does not return unless it fails
mov SYS_exit, %g1 ! exit(errno)
t ST_SYSCALL
_esigcode:
#ifdef COMPAT_SVR4
/*
* The following code is copied to the top of the user stack when each
* process is exec'ed, and signals are `trampolined' off it.
*
* When this code is run, the stack looks like:
* [%sp] 64 bytes to which registers can be dumped
* [%sp + 64] signal number (goes in %o0)
* [%sp + 64 + 4] pointer to saved siginfo
* [%sp + 64 + 8] pointer to saved context
* [%sp + 64 + 12] address of the user's handler
* [%sp + 64 + 16] first word of saved state (context)
* .
* .
* .
* [%sp + NNN] last word of saved state (siginfo)
* (followed by previous stack contents or top of signal stack).
* The address of the function to call is in %g1; the old %g1 and %o0
* have already been saved in the sigcontext. We are running in a clean
* window, all previous windows now being saved to the stack.
*
* Note that [%sp + 64 + 8] == %sp + 64 + 16. The copy at %sp+64+8
* will eventually be removed, with a hole left in its place, if things
* work out.
*/
.globl _svr4_sigcode
.globl _svr4_esigcode
_svr4_sigcode:
/*
* XXX the `save' and `restore' below are unnecessary: should
* replace with simple arithmetic on %sp
*
* Make room on the stack for 32 %f registers + %fsr. This comes
* out to 33*4 or 132 bytes, but this must be aligned to a multiple
* of 8, or 136 bytes.
*/
save %sp, -CCFSZ - 136, %sp
mov %g2, %l2 ! save globals in %l registers
mov %g3, %l3
mov %g4, %l4
mov %g5, %l5
mov %g6, %l6
mov %g7, %l7
/*
* Saving the fpu registers is expensive, so do it iff the fsr
* stored in the sigcontext shows that the fpu is enabled.
*/
ld [%fp + 64 + 16 + SC_PSR_OFFSET], %l0
sethi %hi(PSR_EF), %l1 ! FPU enable bit is too high for andcc
andcc %l0, %l1, %l0 ! %l0 = fpu enable bit
be 1f ! if not set, skip the saves
rd %y, %l1 ! in any case, save %y
! fpu is enabled, oh well
st %fsr, [%sp + CCFSZ + 0]
std %f0, [%sp + CCFSZ + 8]
std %f2, [%sp + CCFSZ + 16]
std %f4, [%sp + CCFSZ + 24]
std %f6, [%sp + CCFSZ + 32]
std %f8, [%sp + CCFSZ + 40]
std %f10, [%sp + CCFSZ + 48]
std %f12, [%sp + CCFSZ + 56]
std %f14, [%sp + CCFSZ + 64]
std %f16, [%sp + CCFSZ + 72]
std %f18, [%sp + CCFSZ + 80]
std %f20, [%sp + CCFSZ + 88]
std %f22, [%sp + CCFSZ + 96]
std %f24, [%sp + CCFSZ + 104]
std %f26, [%sp + CCFSZ + 112]
std %f28, [%sp + CCFSZ + 120]
std %f30, [%sp + CCFSZ + 128]
1:
ldd [%fp + 64], %o0 ! sig, siginfo
ld [%fp + 72], %o2 ! uctx
call %g1 ! (*sa->sa_handler)(sig,siginfo,uctx)
nop
/*
* Now that the handler has returned, re-establish all the state
* we just saved above, then do a sigreturn.
*/
tst %l0 ! reload fpu registers?
be 1f ! if not, skip the loads
wr %l1, %g0, %y ! in any case, restore %y
ld [%sp + CCFSZ + 0], %fsr
ldd [%sp + CCFSZ + 8], %f0
ldd [%sp + CCFSZ + 16], %f2
ldd [%sp + CCFSZ + 24], %f4
ldd [%sp + CCFSZ + 32], %f6
ldd [%sp + CCFSZ + 40], %f8
ldd [%sp + CCFSZ + 48], %f10
ldd [%sp + CCFSZ + 56], %f12
ldd [%sp + CCFSZ + 64], %f14
ldd [%sp + CCFSZ + 72], %f16
ldd [%sp + CCFSZ + 80], %f18
ldd [%sp + CCFSZ + 88], %f20
ldd [%sp + CCFSZ + 96], %f22
ldd [%sp + CCFSZ + 104], %f24
ldd [%sp + CCFSZ + 112], %f26
ldd [%sp + CCFSZ + 120], %f28
ldd [%sp + CCFSZ + 128], %f30
1:
mov %l2, %g2
mov %l3, %g3
mov %l4, %g4
mov %l5, %g5
mov %l6, %g6
mov %l7, %g7
restore %g0, SVR4_SYS_context, %g1 ! get registers & set syscall #
mov 1, %o0
add %sp, 64 + 16, %o1 ! compute ucontextp
t ST_SYSCALL ! svr4_context(1, ucontextp)
! setcontext does not return unless it fails
mov SYS_exit, %g1 ! exit(errno)
t ST_SYSCALL
_svr4_esigcode:
#endif
/*
* Primitives
*/
#ifdef GPROF
.globl mcount
#define ENTRY(x) \
.globl _/**/x; _/**/x: ; \
save %sp, -CCFSZ, %sp; \
call mcount; \
nop; \
restore
#else
#define ENTRY(x) .globl _/**/x; _/**/x:
#endif
#define ALTENTRY(x) .globl _/**/x; _/**/x:
/*
* getfp() - get stack frame pointer
*/
ENTRY(getfp)
retl
mov %fp, %o0
/*
* copyinstr(fromaddr, toaddr, maxlength, &lencopied)
*
* Copy a null terminated string from the user address space into
* the kernel address space.
*/
ENTRY(copyinstr)
! %o0 = fromaddr, %o1 = toaddr, %o2 = maxlen, %o3 = &lencopied
#ifdef DIAGNOSTIC
tst %o2 ! kernel should never give maxlen <= 0
ble 1f
EMPTY
#endif
set KERNBASE, %o4
cmp %o0, %o4 ! fromaddr < KERNBASE?
blu,a Lcsdocopy ! yes, go do it
sethi %hi(_cpcb), %o4 ! (first instr of copy)
b Lcsdone ! no, return EFAULT
mov EFAULT, %o0
1:
sethi %hi(2f), %o0
call _panic
or %lo(2f), %o0, %o0
2: .asciz "copyinstr"
_ALIGN
/*
* copyoutstr(fromaddr, toaddr, maxlength, &lencopied)
*
* Copy a null terminated string from the kernel
* address space to the user address space.
*/
ENTRY(copyoutstr)
! %o0 = fromaddr, %o1 = toaddr, %o2 = maxlen, %o3 = &lencopied
#ifdef DIAGNOSTIC
tst %o2
ble 1f
EMPTY
#endif
set KERNBASE, %o4
cmp %o1, %o4 ! toaddr < KERNBASE?
blu,a Lcsdocopy ! yes, go do it
sethi %hi(_cpcb), %o4 ! (first instr of copy)
b Lcsdone ! no, return EFAULT
mov EFAULT, %o0
1:
sethi %hi(2f), %o0
call _panic
or %lo(2f), %o0, %o0
2: .asciz "copyoutstr"
_ALIGN
Lcsdocopy:
! sethi %hi(_cpcb), %o4 ! (done earlier)
ld [%o4 + %lo(_cpcb)], %o4 ! catch faults
set Lcsfault, %o5
st %o5, [%o4 + PCB_ONFAULT]
mov %o1, %o5 ! save = toaddr;
! XXX should do this in bigger chunks when possible
0: ! loop:
ldsb [%o0], %g1 ! c = *fromaddr;
tst %g1
stb %g1, [%o1] ! *toaddr++ = c;
be 1f ! if (c == NULL)
inc %o1 ! goto ok;
deccc %o2 ! if (--len > 0) {
bg 0b ! fromaddr++;
inc %o0 ! goto loop;
! }
!
b Lcsdone ! error = ENAMETOOLONG;
mov ENAMETOOLONG, %o0 ! goto done;
1: ! ok:
clr %o0 ! error = 0;
Lcsdone: ! done:
sub %o1, %o5, %o1 ! len = to - save;
tst %o3 ! if (lencopied)
bnz,a 3f
st %o1, [%o3] ! *lencopied = len;
3:
retl ! cpcb->pcb_onfault = 0;
st %g0, [%o4 + PCB_ONFAULT]! return (error);
Lcsfault:
b Lcsdone ! error = EFAULT;
mov EFAULT, %o0 ! goto ret;
/*
* copystr(fromaddr, toaddr, maxlength, &lencopied)
*
* Copy a null terminated string from one point to another in
* the kernel address space. (This is a leaf procedure, but
* it does not seem that way to the C compiler.)
*/
ENTRY(copystr)
#ifdef DIAGNOSTIC
tst %o2 ! if (maxlength <= 0)
ble 4f ! panic(...);
EMPTY
#endif
mov %o1, %o5 ! to0 = to;
0: ! loop:
ldsb [%o0], %o4 ! c = *from;
tst %o4
stb %o4, [%o1] ! *to++ = c;
be 1f ! if (c == 0)
inc %o1 ! goto ok;
deccc %o2 ! if (--len > 0) {
bg,a 0b ! from++;
inc %o0 ! goto loop;
b 2f ! }
mov ENAMETOOLONG, %o0 ! ret = ENAMETOOLONG; goto done;
1: ! ok:
clr %o0 ! ret = 0;
2:
sub %o1, %o5, %o1 ! len = to - to0;
tst %o3 ! if (lencopied)
bnz,a 3f
st %o1, [%o3] ! *lencopied = len;
3:
retl
nop
#ifdef DIAGNOSTIC
4:
sethi %hi(5f), %o0
call _panic
or %lo(5f), %o0, %o0
5:
.asciz "copystr"
_ALIGN
#endif
/*
* Copyin(src, dst, len)
*
* Copy specified amount of data from user space into the kernel.
*/
ENTRY(copyin)
set KERNBASE, %o3
cmp %o0, %o3 ! src < KERNBASE?
blu,a Ldocopy ! yes, can try it
sethi %hi(_cpcb), %o3
/* source address points into kernel space: return EFAULT */
retl
mov EFAULT, %o0
/*
* Copyout(src, dst, len)
*
* Copy specified amount of data from kernel to user space.
* Just like copyin, except that the `dst' addresses are user space
* rather than the `src' addresses.
*/
ENTRY(copyout)
set KERNBASE, %o3
cmp %o1, %o3 ! dst < KERBASE?
blu,a Ldocopy
sethi %hi(_cpcb), %o3
/* destination address points into kernel space: return EFAULT */
retl
mov EFAULT, %o0
/*
* ******NOTE****** this depends on bcopy() not using %g7
*/
Ldocopy:
! sethi %hi(_cpcb), %o3
ld [%o3 + %lo(_cpcb)], %o3
set Lcopyfault, %o4
mov %o7, %g7 ! save return address
call _bcopy ! bcopy(src, dst, len)
st %o4, [%o3 + PCB_ONFAULT]
sethi %hi(_cpcb), %o3
ld [%o3 + %lo(_cpcb)], %o3
st %g0, [%o3 + PCB_ONFAULT]
jmp %g7 + 8
clr %o0 ! return 0
! Copyin or copyout fault. Clear cpcb->pcb_onfault and return EFAULT.
! Note that although we were in bcopy, there is no state to clean up;
! the only special thing is that we have to return to [g7 + 8] rather than
! [o7 + 8].
Lcopyfault:
sethi %hi(_cpcb), %o3
ld [%o3 + %lo(_cpcb)], %o3
st %g0, [%o3 + PCB_ONFAULT]
jmp %g7 + 8
mov EFAULT, %o0
/*
* Write all user windows presently in the CPU back to the user's stack.
* We just do `save' instructions until pcb_uw == 0.
*
* p = cpcb;
* nsaves = 0;
* while (p->pcb_uw > 0)
* save(), nsaves++;
* while (--nsaves >= 0)
* restore();
*/
ENTRY(write_user_windows)
sethi %hi(_cpcb), %g6
ld [%g6 + %lo(_cpcb)], %g6
b 2f
clr %g5
1:
save %sp, -64, %sp
2:
ld [%g6 + PCB_UW], %g7
tst %g7
bg,a 1b
inc %g5
3:
deccc %g5
bge,a 3b
restore
retl
nop
.comm _want_resched,4
/*
* Masterpaddr is the p->p_addr of the last process on the processor.
* XXX masterpaddr is almost the same as cpcb
* XXX should delete this entirely
*/
.comm _masterpaddr, 4
/*
* Switch statistics (for later tweaking):
* nswitchdiff = p1 => p2 (i.e., chose different process)
* nswitchexit = number of calls to switchexit()
* _cnt.v_swtch = total calls to swtch+swtchexit
*/
.comm _nswitchdiff, 4
.comm _nswitchexit, 4
/*
* REGISTER USAGE IN cpu_switch AND switchexit:
* This is split into two phases, more or less
* `before we locate a new proc' and `after'.
* Some values are the same in both phases.
* Note that the %o0-registers are not preserved across
* the psr change when entering a new process, since this
* usually changes the CWP field (hence heavy usage of %g's).
*
* %g1 = oldpsr (excluding ipl bits)
* %g2 = %hi(_whichqs); newpsr
* %g3 = p
* %g4 = lastproc
* %g5 = <free>; newpcb
* %g6 = %hi(_cpcb)
* %g7 = %hi(_curproc)
* %o0 = tmp 1
* %o1 = tmp 2
* %o2 = tmp 3
* %o3 = tmp 4; whichqs; vm
* %o4 = tmp 4; which; sswap
* %o5 = tmp 5; q; <free>
*/
/*
* switchexit is called only from cpu_exit() before the current process
* has freed its kernel stack; we must free it. (curproc is already NULL.)
*
* We lay the process to rest by changing to the `idle' kernel stack,
* and note that the `last loaded process' is nonexistent.
*/
ENTRY(switchexit)
mov %o0, %g2 ! save the
mov %o1, %g3 ! ... three parameters
mov %o2, %g4 ! ... to kmem_free
/*
* Change pcb to idle u. area, i.e., set %sp to top of stack
* and %psr to PSR_S|PSR_ET, and set cpcb to point to _idle_u.
* Once we have left the old stack, we can call kmem_free to
* destroy it. Call it any sooner and the register windows
* go bye-bye.
*/
set _idle_u, %g5
sethi %hi(_cpcb), %g6
mov 1, %g7
wr %g0, PSR_S, %psr ! change to window 0, traps off
wr %g0, 2, %wim ! and make window 1 the trap window
st %g5, [%g6 + %lo(_cpcb)] ! cpcb = &idle_u
st %g7, [%g5 + PCB_WIM] ! idle_u.pcb_wim = log2(2) = 1
set _idle_u + USPACE-CCFSZ, %sp ! set new %sp
#ifdef DEBUG
set _idle_u, %l6
SET_SP_REDZONE(%l6, %l5)
#endif
wr %g0, PSR_S|PSR_ET, %psr ! and then enable traps
mov %g2, %o0 ! now ready to call kmem_free
mov %g3, %o1
call _kmem_free
mov %g4, %o2
/*
* Now fall through to `the last switch'. %g6 was set to
* %hi(_cpcb), but may have been clobbered in kmem_free,
* so all the registers described below will be set here.
*
* REGISTER USAGE AT THIS POINT:
* %g1 = oldpsr (excluding ipl bits)
* %g2 = %hi(_whichqs)
* %g4 = lastproc
* %g6 = %hi(_cpcb)
* %g7 = %hi(_curproc)
* %o0 = tmp 1
* %o1 = tmp 2
* %o3 = whichqs
*/
INCR(_nswitchexit) ! nswitchexit++;
INCR(_cnt+V_SWTCH) ! cnt.v_switch++;
mov PSR_S|PSR_ET, %g1 ! oldpsr = PSR_S | PSR_ET;
sethi %hi(_whichqs), %g2
clr %g4 ! lastproc = NULL;
sethi %hi(_cpcb), %g6
sethi %hi(_curproc), %g7
/* FALLTHROUGH */
/*
* When no processes are on the runq, switch
* idles here waiting for something to come ready.
* The registers are set up as noted above.
*/
.globl idle
idle:
st %g0, [%g7 + %lo(_curproc)] ! curproc = NULL;
wr %g1, 0, %psr ! (void) spl0();
1: ! spin reading _whichqs until nonzero
ld [%g2 + %lo(_whichqs)], %o3
tst %o3
bnz,a Lsw_scan
wr %g1, PIL_CLOCK << 8, %psr ! (void) splclock();
b,a 1b
Lsw_panic_rq:
sethi %hi(1f), %o0
call _panic
or %lo(1f), %o0, %o0
Lsw_panic_wchan:
sethi %hi(2f), %o0
call _panic
or %lo(2f), %o0, %o0
Lsw_panic_srun:
sethi %hi(3f), %o0
call _panic
or %lo(3f), %o0, %o0
1: .asciz "switch rq"
2: .asciz "switch wchan"
3: .asciz "switch SRUN"
_ALIGN
/*
* cpu_switch() picks a process to run and runs it, saving the current
* one away. On the assumption that (since most workstations are
* single user machines) the chances are quite good that the new
* process will turn out to be the current process, we defer saving
* it here until we have found someone to load. If that someone
* is the current process we avoid both store and load.
*
* cpu_switch() is always entered at splstatclock or splhigh.
*
* IT MIGHT BE WORTH SAVING BEFORE ENTERING idle TO AVOID HAVING TO
* SAVE LATER WHEN SOMEONE ELSE IS READY ... MUST MEASURE!
*/
.globl _runtime
.globl _time
ENTRY(cpu_switch)
/*
* REGISTER USAGE AT THIS POINT:
* %g1 = oldpsr (excluding ipl bits)
* %g2 = %hi(_whichqs)
* %g3 = p
* %g4 = lastproc
* %g5 = tmp 0
* %g6 = %hi(_cpcb)
* %g7 = %hi(_curproc)
* %o0 = tmp 1
* %o1 = tmp 2
* %o2 = tmp 3
* %o3 = tmp 4, then at Lsw_scan, whichqs
* %o4 = tmp 5, then at Lsw_scan, which
* %o5 = tmp 6, then at Lsw_scan, q
*/
sethi %hi(_whichqs), %g2 ! set up addr regs
sethi %hi(_cpcb), %g6
ld [%g6 + %lo(_cpcb)], %o0
std %o6, [%o0 + PCB_SP] ! cpcb->pcb_<sp,pc> = <sp,pc>;
rd %psr, %g1 ! oldpsr = %psr;
sethi %hi(_curproc), %g7
ld [%g7 + %lo(_curproc)], %g4 ! lastproc = curproc;
st %g1, [%o0 + PCB_PSR] ! cpcb->pcb_psr = oldpsr;
andn %g1, PSR_PIL, %g1 ! oldpsr &= ~PSR_PIL;
/*
* In all the fiddling we did to get this far, the thing we are
* waiting for might have come ready, so let interrupts in briefly
* before checking for other processes. Note that we still have
* curproc set---we have to fix this or we can get in trouble with
* the run queues below.
*/
st %g0, [%g7 + %lo(_curproc)] ! curproc = NULL;
wr %g1, 0, %psr ! (void) spl0();
nop; nop; nop ! paranoia
wr %g1, PIL_CLOCK << 8 , %psr ! (void) splclock();
Lsw_scan:
nop; nop; nop ! paranoia
/*
* We're about to run a (possibly) new process. Set runtime
* to indicate its start time.
*/
sethi %hi(_time), %o0
ldd [%o0 + %lo(_time)], %o2
sethi %hi(_runtime), %o0
std %o2, [%o0 + %lo(_runtime)]
ld [%g2 + %lo(_whichqs)], %o3
/*
* Optimized inline expansion of `which = ffs(whichqs) - 1';
* branches to idle if ffs(whichqs) was 0.
*/
set ffstab, %o2
andcc %o3, 0xff, %o1 ! byte 0 zero?
bz,a 1f ! yes, try byte 1
srl %o3, 8, %o0
b 2f ! ffs = ffstab[byte0]; which = ffs - 1;
ldsb [%o2 + %o1], %o0
1: andcc %o0, 0xff, %o1 ! byte 1 zero?
bz,a 1f ! yes, try byte 2
srl %o0, 8, %o0
ldsb [%o2 + %o1], %o0 ! which = ffstab[byte1] + 7;
b 3f
add %o0, 7, %o4
1: andcc %o0, 0xff, %o1 ! byte 2 zero?
bz,a 1f ! yes, try byte 3
srl %o0, 8, %o0
ldsb [%o2 + %o1], %o0 ! which = ffstab[byte2] + 15;
b 3f
add %o0, 15, %o4
1: ldsb [%o2 + %o0], %o0 ! ffs = ffstab[byte3] + 24
addcc %o0, 24, %o0 ! (note that ffstab[0] == -24)
bz idle ! if answer was 0, go idle
EMPTY
2: sub %o0, 1, %o4 ! which = ffs(whichqs) - 1
3: /* end optimized inline expansion */
/*
* We found a nonempty run queue. Take its first process.
*/
set _qs, %o5 ! q = &qs[which];
sll %o4, 3, %o0
add %o0, %o5, %o5
ld [%o5], %g3 ! p = q->ph_link;
cmp %g3, %o5 ! if (p == q)
be Lsw_panic_rq ! panic("switch rq");
EMPTY
ld [%g3], %o0 ! tmp0 = p->p_forw;
st %o0, [%o5] ! q->ph_link = tmp0;
st %o5, [%o0 + 4] ! tmp0->p_back = q;
cmp %o0, %o5 ! if (tmp0 == q)
bne 1f
EMPTY
mov 1, %o1 ! whichqs &= ~(1 << which);
sll %o1, %o4, %o1
andn %o3, %o1, %o3
st %o3, [%g2 + %lo(_whichqs)]
1:
/*
* PHASE TWO: NEW REGISTER USAGE:
* %g1 = oldpsr (excluding ipl bits)
* %g2 = newpsr
* %g3 = p
* %g4 = lastproc
* %g5 = newpcb
* %g6 = %hi(_cpcb)
* %g7 = %hi(_curproc)
* %o0 = tmp 1
* %o1 = tmp 2
* %o2 = tmp 3
* %o3 = vm
* %o4 = sswap
* %o5 = <free>
*/
/* firewalls */
ld [%g3 + P_WCHAN], %o0 ! if (p->p_wchan)
tst %o0
bne Lsw_panic_wchan ! panic("switch wchan");
EMPTY
ldsb [%g3 + P_STAT], %o0 ! if (p->p_stat != SRUN)
cmp %o0, SRUN
bne Lsw_panic_srun ! panic("switch SRUN");
EMPTY
/*
* Committed to running process p.
* It may be the same as the one we were running before.
*/
sethi %hi(_want_resched), %o0
st %g0, [%o0 + %lo(_want_resched)] ! want_resched = 0;
ld [%g3 + P_ADDR], %g5 ! newpcb = p->p_addr;
st %g0, [%g3 + 4] ! p->p_back = NULL;
ld [%g5 + PCB_PSR], %g2 ! newpsr = newpcb->pcb_psr;
st %g3, [%g7 + %lo(_curproc)] ! curproc = p;
cmp %g3, %g4 ! p == lastproc?
be,a Lsw_sameproc ! yes, go return 0
wr %g2, 0, %psr ! (after restoring ipl)
/*
* Not the old process. Save the old process, if any;
* then load p.
*/
tst %g4
be,a Lsw_load ! if no old process, go load
wr %g1, (PIL_CLOCK << 8) | PSR_ET, %psr
INCR(_nswitchdiff) ! clobbers %o0,%o1
/*
* save: write back all windows (including the current one).
* XXX crude; knows nwindows <= 8
*/
#define SAVE save %sp, -64, %sp
wb1: SAVE; SAVE; SAVE; SAVE; SAVE; SAVE; SAVE /* 7 of each: */
restore; restore; restore; restore; restore; restore; restore
/*
* Load the new process. To load, we must change stacks and
* alter cpcb and %wim, hence we must disable traps. %psr is
* currently equal to oldpsr (%g1) ^ (PIL_CLOCK << 8);
* this means that PSR_ET is on. Likewise, PSR_ET is on
* in newpsr (%g2), although we do not know newpsr's ipl.
*
* We also must load up the `in' and `local' registers.
*/
wr %g1, (PIL_CLOCK << 8) | PSR_ET, %psr
Lsw_load:
! wr %g1, (PIL_CLOCK << 8) | PSR_ET, %psr ! done above
/* compute new wim */
ld [%g5 + PCB_WIM], %o0
mov 1, %o1
sll %o1, %o0, %o0
wr %o0, 0, %wim ! %wim = 1 << newpcb->pcb_wim;
/* now must not change %psr for 3 more instrs */
/*1*/ set PSR_EF|PSR_EC, %o0
/*2*/ andn %g2, %o0, %g2 ! newpsr &= ~(PSR_EF|PSR_EC);
/*3*/ nop
/* set new psr, but with traps disabled */
wr %g2, PSR_ET, %psr ! %psr = newpsr ^ PSR_ET;
/* set new cpcb */
st %g5, [%g6 + %lo(_cpcb)] ! cpcb = newpcb;
/* XXX update masterpaddr too */
sethi %hi(_masterpaddr), %g7
st %g5, [%g7 + %lo(_masterpaddr)]
ldd [%g5 + PCB_SP], %o6 ! <sp,pc> = newpcb->pcb_<sp,pc>
/* load window */
ldd [%sp + (0*8)], %l0
ldd [%sp + (1*8)], %l2
ldd [%sp + (2*8)], %l4
ldd [%sp + (3*8)], %l6
ldd [%sp + (4*8)], %i0
ldd [%sp + (5*8)], %i2
ldd [%sp + (6*8)], %i4
ldd [%sp + (7*8)], %i6
#ifdef DEBUG
mov %g5, %o0
SET_SP_REDZONE(%o0, %o1)
CHECK_SP_REDZONE(%o0, %o1)
#endif
/* finally, enable traps */
wr %g2, 0, %psr ! psr = newpsr;
/*
* Now running p. Make sure it has a context so that it
* can talk about user space stuff. (Its pcb_uw is currently
* zero so it is safe to have interrupts going here.)
*/
ld [%g3 + P_VMSPACE], %o3 ! vm = p->p_vmspace;
ld [%o3 + VM_PMAP_CTX], %o0! if (vm->vm_pmap.pm_ctx != NULL)
tst %o0
bnz,a Lsw_havectx ! goto havecontext;
ld [%o3 + VM_PMAP_CTXNUM], %o0 ! load context number
/* p does not have a context: call ctx_alloc to get one */
save %sp, -CCFSZ, %sp
call _ctx_alloc ! ctx_alloc(&vm->vm_pmap);
add %i3, VM_PMAP, %o0
ret
restore
/* p does have a context: just switch to it */
Lsw_havectx:
! context is in %o0
#if (defined(SUN4) || defined(SUN4C)) && defined(SUN4M)
sethi %hi(_cputyp), %o1 ! what cpu are we running on?
ld [%o1 + %lo(_cputyp)], %o1
cmp %o1, CPU_SUN4M
be 1f
nop
#endif
#if defined(SUN4) || defined(SUN4C)
set AC_CONTEXT, %o1
retl
stba %o0, [%o1] ASI_CONTROL ! setcontext(vm->vm_pmap.pm_ctxnum);
#endif
1:
#if defined(SUN4M)
set SRMMU_CXR, %o1
retl
sta %o0, [%o1] ASI_SRMMU ! setcontext(vm->vm_pmap.pm_ctxnum);
#endif
Lsw_sameproc:
/*
* We are resuming the process that was running at the
* call to switch(). Just set psr ipl and return.
*/
! wr %g2, 0 %psr ! %psr = newpsr; (done earlier)
nop
retl
nop
/*
* Snapshot the current process so that stack frames are up to date.
* Only used just before a crash dump.
*/
ENTRY(snapshot)
st %o6, [%o0 + PCB_SP] ! save sp
rd %psr, %o1 ! save psr
st %o1, [%o0 + PCB_PSR]
/*
* Just like switch(); same XXX comments apply.
* 7 of each. Minor tweak: the 7th restore is
* done after a ret.
*/
SAVE; SAVE; SAVE; SAVE; SAVE; SAVE; SAVE
restore; restore; restore; restore; restore; restore; ret; restore
/*
* cpu_set_kpc() and cpu_fork() arrange for proc_trampoline() to run
* after after a process gets chosen in switch(). The stack frame will
* contain a function pointer in %l0, and an argument to pass to it in %l2.
*
* If the function *(%l0) returns, we arrange for an immediate return
* to user mode. This happens in two known cases: after execve(2) of init,
* and when returning a child to user mode after a fork(2).
*/
ENTRY(proc_trampoline)
call %l0 ! re-use current frame
mov %l1, %o0
/*
* Here we finish up as in syscall, but simplified. We need to
* fiddle pc and npc a bit, as execve() / setregs() /cpu_set_kpc()
* have only set npc, in anticipation that trap.c will advance past
* the trap instruction; but we bypass that, so we must do it manually.
*/
mov PSR_S, %l0 ! user psr (no need to load it)
!?wr %g0, 2, %wim ! %wim = 2
ld [%sp + CCFSZ + 8], %l1 ! pc = tf->tf_npc from execve/fork
b return_from_syscall
add %l1, 4, %l2 ! npc = pc+4
/*
* {fu,su}{,i}{byte,word}
*/
ALTENTRY(fuiword)
ENTRY(fuword)
set KERNBASE, %o2
cmp %o0, %o2 ! if addr >= KERNBASE...
bgeu Lfsbadaddr
EMPTY
btst 3, %o0 ! or has low bits set...
bnz Lfsbadaddr ! go return -1
EMPTY
sethi %hi(_cpcb), %o2 ! cpcb->pcb_onfault = Lfserr;
ld [%o2 + %lo(_cpcb)], %o2
set Lfserr, %o3
st %o3, [%o2 + PCB_ONFAULT]
ld [%o0], %o0 ! fetch the word
retl ! phew, made it, return the word
st %g0, [%o2 + PCB_ONFAULT]! but first clear onfault
Lfserr:
st %g0, [%o2 + PCB_ONFAULT]! error in r/w, clear pcb_onfault
Lfsbadaddr:
retl ! and return error indicator
mov -1, %o0
/*
* This is just like Lfserr, but it's a global label that allows
* mem_access_fault() to check to see that we don't want to try to
* page in the fault. It's used by fuswintr() etc.
*/
.globl _Lfsbail
_Lfsbail:
st %g0, [%o2 + PCB_ONFAULT]! error in r/w, clear pcb_onfault
retl ! and return error indicator
mov -1, %o0
/*
* Like fusword but callable from interrupt context.
* Fails if data isn't resident.
*/
ENTRY(fuswintr)
set KERNBASE, %o2
cmp %o0, %o2 ! if addr >= KERNBASE
bgeu Lfsbadaddr ! return error
EMPTY
sethi %hi(_cpcb), %o2 ! cpcb->pcb_onfault = _Lfsbail;
ld [%o2 + %lo(_cpcb)], %o2
set _Lfsbail, %o3
st %o3, [%o2 + PCB_ONFAULT]
lduh [%o0], %o0 ! fetch the halfword
retl ! made it
st %g0, [%o2 + PCB_ONFAULT]! but first clear onfault
ENTRY(fusword)
set KERNBASE, %o2
cmp %o0, %o2 ! if addr >= KERNBASE
bgeu Lfsbadaddr ! return error
EMPTY
sethi %hi(_cpcb), %o2 ! cpcb->pcb_onfault = Lfserr;
ld [%o2 + %lo(_cpcb)], %o2
set Lfserr, %o3
st %o3, [%o2 + PCB_ONFAULT]
lduh [%o0], %o0 ! fetch the halfword
retl ! made it
st %g0, [%o2 + PCB_ONFAULT]! but first clear onfault
ALTENTRY(fuibyte)
ENTRY(fubyte)
set KERNBASE, %o2
cmp %o0, %o2 ! if addr >= KERNBASE
bgeu Lfsbadaddr ! return error
EMPTY
sethi %hi(_cpcb), %o2 ! cpcb->pcb_onfault = Lfserr;
ld [%o2 + %lo(_cpcb)], %o2
set Lfserr, %o3
st %o3, [%o2 + PCB_ONFAULT]
ldub [%o0], %o0 ! fetch the byte
retl ! made it
st %g0, [%o2 + PCB_ONFAULT]! but first clear onfault
ALTENTRY(suiword)
ENTRY(suword)
set KERNBASE, %o2
cmp %o0, %o2 ! if addr >= KERNBASE ...
bgeu Lfsbadaddr
EMPTY
btst 3, %o0 ! or has low bits set ...
bnz Lfsbadaddr ! go return error
EMPTY
sethi %hi(_cpcb), %o2 ! cpcb->pcb_onfault = Lfserr;
ld [%o2 + %lo(_cpcb)], %o2
set Lfserr, %o3
st %o3, [%o2 + PCB_ONFAULT]
st %o1, [%o0] ! store the word
st %g0, [%o2 + PCB_ONFAULT]! made it, clear onfault
retl ! and return 0
clr %o0
ENTRY(suswintr)
set KERNBASE, %o2
cmp %o0, %o2 ! if addr >= KERNBASE
bgeu Lfsbadaddr ! go return error
EMPTY
sethi %hi(_cpcb), %o2 ! cpcb->pcb_onfault = _Lfsbail;
ld [%o2 + %lo(_cpcb)], %o2
set _Lfsbail, %o3
st %o3, [%o2 + PCB_ONFAULT]
sth %o1, [%o0] ! store the halfword
st %g0, [%o2 + PCB_ONFAULT]! made it, clear onfault
retl ! and return 0
clr %o0
ENTRY(susword)
set KERNBASE, %o2
cmp %o0, %o2 ! if addr >= KERNBASE
bgeu Lfsbadaddr ! go return error
EMPTY
sethi %hi(_cpcb), %o2 ! cpcb->pcb_onfault = Lfserr;
ld [%o2 + %lo(_cpcb)], %o2
set Lfserr, %o3
st %o3, [%o2 + PCB_ONFAULT]
sth %o1, [%o0] ! store the halfword
st %g0, [%o2 + PCB_ONFAULT]! made it, clear onfault
retl ! and return 0
clr %o0
ALTENTRY(suibyte)
ENTRY(subyte)
set KERNBASE, %o2
cmp %o0, %o2 ! if addr >= KERNBASE
bgeu Lfsbadaddr ! go return error
EMPTY
sethi %hi(_cpcb), %o2 ! cpcb->pcb_onfault = Lfserr;
ld [%o2 + %lo(_cpcb)], %o2
set Lfserr, %o3
st %o3, [%o2 + PCB_ONFAULT]
stb %o1, [%o0] ! store the byte
st %g0, [%o2 + PCB_ONFAULT]! made it, clear onfault
retl ! and return 0
clr %o0
/* probeget and probeset are meant to be used during autoconfiguration */
/*
* probeget(addr, size) caddr_t addr; int size;
*
* Read or write a (byte,word,longword) from the given address.
* Like {fu,su}{byte,halfword,word} but our caller is supposed
* to know what he is doing... the address can be anywhere.
*
* We optimize for space, rather than time, here.
*/
ENTRY(probeget)
! %o0 = addr, %o1 = (1,2,4)
sethi %hi(_cpcb), %o2
ld [%o2 + %lo(_cpcb)], %o2 ! cpcb->pcb_onfault = Lfserr;
set Lfserr, %o5
st %o5, [%o2 + PCB_ONFAULT]
btst 1, %o1
bnz,a 0f ! if (len & 1)
ldub [%o0], %o0 ! value = *(char *)addr;
0: btst 2, %o1
bnz,a 0f ! if (len & 2)
lduh [%o0], %o0 ! value = *(short *)addr;
0: btst 4, %o1
bnz,a 0f ! if (len & 4)
ld [%o0], %o0 ! value = *(int *)addr;
0: retl ! made it, clear onfault and return
st %g0, [%o2 + PCB_ONFAULT]
/*
* probeset(addr, size, val) caddr_t addr; int size, val;
*
* As above, but we return 0 on success.
*/
ENTRY(probeset)
! %o0 = addr, %o1 = (1,2,4), %o2 = val
sethi %hi(_cpcb), %o3
ld [%o3 + %lo(_cpcb)], %o3 ! cpcb->pcb_onfault = Lfserr;
set Lfserr, %o5
st %o5, [%o3 + PCB_ONFAULT]
btst 1, %o1
bnz,a 0f ! if (len & 1)
stb %o2, [%o0] ! *(char *)addr = value;
0: btst 2, %o1
bnz,a 0f ! if (len & 2)
sth %o2, [%o0] ! *(short *)addr = value;
0: btst 4, %o1
bnz,a 0f ! if (len & 4)
st %o2, [%o0] ! *(int *)addr = value;
0: clr %o0 ! made it, clear onfault and return 0
retl
st %g0, [%o3 + PCB_ONFAULT]
/*
* int xldcontrolb(caddr_t, pcb)
* %o0 %o1
*
* read a byte from the specified address in ASI_CONTROL space.
*/
ENTRY(xldcontrolb)
!sethi %hi(_cpcb), %o2
!ld [%o2 + %lo(_cpcb)], %o2 ! cpcb->pcb_onfault = Lfsbail;
or %o1, %g0, %o2 ! %o2 = %o1
set _Lfsbail, %o5
st %o5, [%o2 + PCB_ONFAULT]
lduba [%o0] ASI_CONTROL, %o0 ! read
0: retl
st %g0, [%o2 + PCB_ONFAULT]
/*
* Insert entry into doubly-linked queue.
* We could just do this in C, but gcc does not do leaves well (yet).
*/
ENTRY(_insque)
! %o0 = e = what to insert; %o1 = after = entry to insert after
st %o1, [%o0 + 4] ! e->prev = after;
ld [%o1], %o2 ! tmp = after->next;
st %o2, [%o0] ! e->next = tmp;
st %o0, [%o1] ! after->next = e;
retl
st %o0, [%o2 + 4] ! tmp->prev = e;
/*
* Remove entry from doubly-linked queue.
*/
ENTRY(_remque)
! %o0 = e = what to remove
ld [%o0], %o1 ! n = e->next;
ld [%o0 + 4], %o2 ! p = e->prev;
st %o2, [%o1 + 4] ! n->prev = p;
retl
st %o1, [%o2] ! p->next = n;
/*
* copywords(src, dst, nbytes)
*
* Copy `nbytes' bytes from src to dst, both of which are word-aligned;
* nbytes is a multiple of four. It may, however, be zero, in which case
* nothing is to be copied.
*/
ENTRY(copywords)
! %o0 = src, %o1 = dst, %o2 = nbytes
b 1f
deccc 4, %o2
0:
st %o3, [%o1 + %o2]
deccc 4, %o2 ! while ((n -= 4) >= 0)
1:
bge,a 0b ! *(int *)(dst+n) = *(int *)(src+n);
ld [%o0 + %o2], %o3
retl
nop
/*
* qcopy(src, dst, nbytes)
*
* (q for `quad' or `quick', as opposed to b for byte/block copy)
*
* Just like copywords, but everything is multiples of 8.
*/
ENTRY(qcopy)
b 1f
deccc 8, %o2
0:
std %o4, [%o1 + %o2]
deccc 8, %o2
1:
bge,a 0b
ldd [%o0 + %o2], %o4
retl
nop
/*
* qzero(addr, nbytes)
*
* Zeroes `nbytes' bytes of a quad-aligned virtual address,
* where nbytes is itself a multiple of 8.
*/
ENTRY(qzero)
! %o0 = addr, %o1 = len (in bytes)
clr %g1
0:
deccc 8, %o1 ! while ((n =- 8) >= 0)
bge,a 0b
std %g0, [%o0 + %o1] ! *(quad *)(addr + n) = 0;
retl
nop
/*
* kernel bcopy/memcpy
* Assumes regions do not overlap; has no useful return value.
*
* Must not use %g7 (see copyin/copyout above).
*/
#define BCOPY_SMALL 32 /* if < 32, copy by bytes */
ENTRY(memcpy)
/*
* Swap args for bcopy. Gcc generates calls to memcpy for
* structure assignments.
*/
mov %o0, %o3
mov %o1, %o0
mov %o3, %o1
ENTRY(bcopy)
cmp %o2, BCOPY_SMALL
Lbcopy_start:
bge,a Lbcopy_fancy ! if >= this many, go be fancy.
btst 7, %o0 ! (part of being fancy)
/*
* Not much to copy, just do it a byte at a time.
*/
deccc %o2 ! while (--len >= 0)
bl 1f
EMPTY
0:
inc %o0
ldsb [%o0 - 1], %o4 ! (++dst)[-1] = *src++;
stb %o4, [%o1]
deccc %o2
bge 0b
inc %o1
1:
retl
nop
/* NOTREACHED */
/*
* Plenty of data to copy, so try to do it optimally.
*/
Lbcopy_fancy:
! check for common case first: everything lines up.
! btst 7, %o0 ! done already
bne 1f
EMPTY
btst 7, %o1
be,a Lbcopy_doubles
dec 8, %o2 ! if all lined up, len -= 8, goto bcopy_doubes
! If the low bits match, we can make these line up.
1:
xor %o0, %o1, %o3 ! t = src ^ dst;
btst 1, %o3 ! if (t & 1) {
be,a 1f
btst 1, %o0 ! [delay slot: if (src & 1)]
! low bits do not match, must copy by bytes.
0:
ldsb [%o0], %o4 ! do {
inc %o0 ! (++dst)[-1] = *src++;
inc %o1
deccc %o2
bnz 0b ! } while (--len != 0);
stb %o4, [%o1 - 1]
retl
nop
/* NOTREACHED */
! lowest bit matches, so we can copy by words, if nothing else
1:
be,a 1f ! if (src & 1) {
btst 2, %o3 ! [delay slot: if (t & 2)]
! although low bits match, both are 1: must copy 1 byte to align
ldsb [%o0], %o4 ! *dst++ = *src++;
stb %o4, [%o1]
inc %o0
inc %o1
dec %o2 ! len--;
btst 2, %o3 ! } [if (t & 2)]
1:
be,a 1f ! if (t & 2) {
btst 2, %o0 ! [delay slot: if (src & 2)]
dec 2, %o2 ! len -= 2;
0:
ldsh [%o0], %o4 ! do {
sth %o4, [%o1] ! *(short *)dst = *(short *)src;
inc 2, %o0 ! dst += 2, src += 2;
deccc 2, %o2 ! } while ((len -= 2) >= 0);
bge 0b
inc 2, %o1
b Lbcopy_mopb ! goto mop_up_byte;
btst 1, %o2 ! } [delay slot: if (len & 1)]
/* NOTREACHED */
! low two bits match, so we can copy by longwords
1:
be,a 1f ! if (src & 2) {
btst 4, %o3 ! [delay slot: if (t & 4)]
! although low 2 bits match, they are 10: must copy one short to align
ldsh [%o0], %o4 ! (*short *)dst = *(short *)src;
sth %o4, [%o1]
inc 2, %o0 ! dst += 2;
inc 2, %o1 ! src += 2;
dec 2, %o2 ! len -= 2;
btst 4, %o3 ! } [if (t & 4)]
1:
be,a 1f ! if (t & 4) {
btst 4, %o0 ! [delay slot: if (src & 4)]
dec 4, %o2 ! len -= 4;
0:
ld [%o0], %o4 ! do {
st %o4, [%o1] ! *(int *)dst = *(int *)src;
inc 4, %o0 ! dst += 4, src += 4;
deccc 4, %o2 ! } while ((len -= 4) >= 0);
bge 0b
inc 4, %o1
b Lbcopy_mopw ! goto mop_up_word_and_byte;
btst 2, %o2 ! } [delay slot: if (len & 2)]
/* NOTREACHED */
! low three bits match, so we can copy by doublewords
1:
be 1f ! if (src & 4) {
dec 8, %o2 ! [delay slot: len -= 8]
ld [%o0], %o4 ! *(int *)dst = *(int *)src;
st %o4, [%o1]
inc 4, %o0 ! dst += 4, src += 4, len -= 4;
inc 4, %o1
dec 4, %o2 ! }
1:
Lbcopy_doubles:
ldd [%o0], %o4 ! do {
std %o4, [%o1] ! *(double *)dst = *(double *)src;
inc 8, %o0 ! dst += 8, src += 8;
deccc 8, %o2 ! } while ((len -= 8) >= 0);
bge Lbcopy_doubles
inc 8, %o1
! check for a usual case again (save work)
btst 7, %o2 ! if ((len & 7) == 0)
be Lbcopy_done ! goto bcopy_done;
btst 4, %o2 ! if ((len & 4)) == 0)
be,a Lbcopy_mopw ! goto mop_up_word_and_byte;
btst 2, %o2 ! [delay slot: if (len & 2)]
ld [%o0], %o4 ! *(int *)dst = *(int *)src;
st %o4, [%o1]
inc 4, %o0 ! dst += 4;
inc 4, %o1 ! src += 4;
btst 2, %o2 ! } [if (len & 2)]
1:
! mop up trailing word (if present) and byte (if present).
Lbcopy_mopw:
be Lbcopy_mopb ! no word, go mop up byte
btst 1, %o2 ! [delay slot: if (len & 1)]
ldsh [%o0], %o4 ! *(short *)dst = *(short *)src;
be Lbcopy_done ! if ((len & 1) == 0) goto done;
sth %o4, [%o1]
ldsb [%o0 + 2], %o4 ! dst[2] = src[2];
retl
stb %o4, [%o1 + 2]
/* NOTREACHED */
! mop up trailing byte (if present).
Lbcopy_mopb:
bne,a 1f
ldsb [%o0], %o4
Lbcopy_done:
retl
nop
1:
retl
stb %o4,[%o1]
/*
* ovbcopy(src, dst, len): like bcopy, but regions may overlap.
*/
ENTRY(ovbcopy)
cmp %o0, %o1 ! src < dst?
bgeu Lbcopy_start ! no, go copy forwards as via bcopy
cmp %o2, BCOPY_SMALL! (check length for doublecopy first)
/*
* Since src comes before dst, and the regions might overlap,
* we have to do the copy starting at the end and working backwards.
*/
add %o2, %o0, %o0 ! src += len
add %o2, %o1, %o1 ! dst += len
bge,a Lback_fancy ! if len >= BCOPY_SMALL, go be fancy
btst 3, %o0
/*
* Not much to copy, just do it a byte at a time.
*/
deccc %o2 ! while (--len >= 0)
bl 1f
EMPTY
0:
dec %o0 ! *--dst = *--src;
ldsb [%o0], %o4
dec %o1
deccc %o2
bge 0b
stb %o4, [%o1]
1:
retl
nop
/*
* Plenty to copy, try to be optimal.
* We only bother with word/halfword/byte copies here.
*/
Lback_fancy:
! btst 3, %o0 ! done already
bnz 1f ! if ((src & 3) == 0 &&
btst 3, %o1 ! (dst & 3) == 0)
bz,a Lback_words ! goto words;
dec 4, %o2 ! (done early for word copy)
1:
/*
* See if the low bits match.
*/
xor %o0, %o1, %o3 ! t = src ^ dst;
btst 1, %o3
bz,a 3f ! if (t & 1) == 0, can do better
btst 1, %o0
/*
* Nope; gotta do byte copy.
*/
2:
dec %o0 ! do {
ldsb [%o0], %o4 ! *--dst = *--src;
dec %o1
deccc %o2 ! } while (--len != 0);
bnz 2b
stb %o4, [%o1]
retl
nop
3:
/*
* Can do halfword or word copy, but might have to copy 1 byte first.
*/
! btst 1, %o0 ! done earlier
bz,a 4f ! if (src & 1) { /* copy 1 byte */
btst 2, %o3 ! (done early)
dec %o0 ! *--dst = *--src;
ldsb [%o0], %o4
dec %o1
stb %o4, [%o1]
dec %o2 ! len--;
btst 2, %o3 ! }
4:
/*
* See if we can do a word copy ((t&2) == 0).
*/
! btst 2, %o3 ! done earlier
bz,a 6f ! if (t & 2) == 0, can do word copy
btst 2, %o0 ! (src&2, done early)
/*
* Gotta do halfword copy.
*/
dec 2, %o2 ! len -= 2;
5:
dec 2, %o0 ! do {
ldsh [%o0], %o4 ! src -= 2;
dec 2, %o1 ! dst -= 2;
deccc 2, %o0 ! *(short *)dst = *(short *)src;
bge 5b ! } while ((len -= 2) >= 0);
sth %o4, [%o1]
b Lback_mopb ! goto mop_up_byte;
btst 1, %o2 ! (len&1, done early)
6:
/*
* We can do word copies, but we might have to copy
* one halfword first.
*/
! btst 2, %o0 ! done already
bz 7f ! if (src & 2) {
dec 4, %o2 ! (len -= 4, done early)
dec 2, %o0 ! src -= 2, dst -= 2;
ldsh [%o0], %o4 ! *(short *)dst = *(short *)src;
dec 2, %o1
sth %o4, [%o1]
dec 2, %o2 ! len -= 2;
! }
7:
Lback_words:
/*
* Do word copies (backwards), then mop up trailing halfword
* and byte if any.
*/
! dec 4, %o2 ! len -= 4, done already
0: ! do {
dec 4, %o0 ! src -= 4;
dec 4, %o1 ! src -= 4;
ld [%o0], %o4 ! *(int *)dst = *(int *)src;
deccc 4, %o2 ! } while ((len -= 4) >= 0);
bge 0b
st %o4, [%o1]
/*
* Check for trailing shortword.
*/
btst 2, %o2 ! if (len & 2) {
bz,a 1f
btst 1, %o2 ! (len&1, done early)
dec 2, %o0 ! src -= 2, dst -= 2;
ldsh [%o0], %o4 ! *(short *)dst = *(short *)src;
dec 2, %o1
sth %o4, [%o1] ! }
btst 1, %o2
/*
* Check for trailing byte.
*/
1:
Lback_mopb:
! btst 1, %o2 ! (done already)
bnz,a 1f ! if (len & 1) {
ldsb [%o0 - 1], %o4 ! b = src[-1];
retl
nop
1:
retl ! dst[-1] = b;
stb %o4, [%o1 - 1] ! }
/*
* savefpstate(f) struct fpstate *f;
*
* Store the current FPU state. The first `st %fsr' may cause a trap;
* our trap handler knows how to recover (by `returning' to savefpcont).
*/
ENTRY(savefpstate)
rd %psr, %o1 ! enable FP before we begin
set PSR_EF, %o2
or %o1, %o2, %o1
wr %o1, 0, %psr
/* do some setup work while we wait for PSR_EF to turn on */
set FSR_QNE, %o5 ! QNE = 0x2000, too big for immediate
clr %o3 ! qsize = 0;
nop ! (still waiting for PSR_EF)
special_fp_store:
st %fsr, [%o0 + FS_FSR] ! f->fs_fsr = getfsr();
/*
* Even if the preceding instruction did not trap, the queue
* is not necessarily empty: this state save might be happening
* because user code tried to store %fsr and took the FPU
* from `exception pending' mode to `exception' mode.
* So we still have to check the blasted QNE bit.
* With any luck it will usually not be set.
*/
ld [%o0 + FS_FSR], %o4 ! if (f->fs_fsr & QNE)
btst %o5, %o4
bnz Lfp_storeq ! goto storeq;
std %f0, [%o0 + FS_REGS + (4*0)] ! f->fs_f0 = etc;
Lfp_finish:
st %o3, [%o0 + FS_QSIZE] ! f->fs_qsize = qsize;
std %f2, [%o0 + FS_REGS + (4*2)]
std %f4, [%o0 + FS_REGS + (4*4)]
std %f6, [%o0 + FS_REGS + (4*6)]
std %f8, [%o0 + FS_REGS + (4*8)]
std %f10, [%o0 + FS_REGS + (4*10)]
std %f12, [%o0 + FS_REGS + (4*12)]
std %f14, [%o0 + FS_REGS + (4*14)]
std %f16, [%o0 + FS_REGS + (4*16)]
std %f18, [%o0 + FS_REGS + (4*18)]
std %f20, [%o0 + FS_REGS + (4*20)]
std %f22, [%o0 + FS_REGS + (4*22)]
std %f24, [%o0 + FS_REGS + (4*24)]
std %f26, [%o0 + FS_REGS + (4*26)]
std %f28, [%o0 + FS_REGS + (4*28)]
retl
std %f30, [%o0 + FS_REGS + (4*30)]
/*
* Store the (now known nonempty) FP queue.
* We have to reread the fsr each time in order to get the new QNE bit.
*/
Lfp_storeq:
add %o0, FS_QUEUE, %o1 ! q = &f->fs_queue[0];
1:
std %fq, [%o1 + %o3] ! q[qsize++] = fsr_qfront();
st %fsr, [%o0 + FS_FSR] ! reread fsr
ld [%o0 + FS_FSR], %o4 ! if fsr & QNE, loop
btst %o5, %o4
bnz 1b
inc 8, %o3
b Lfp_finish ! set qsize and finish storing fregs
srl %o3, 3, %o3 ! (but first fix qsize)
/*
* The fsr store trapped. Do it again; this time it will not trap.
* We could just have the trap handler return to the `st %fsr', but
* if for some reason it *does* trap, that would lock us into a tight
* loop. This way we panic instead. Whoopee.
*/
savefpcont:
b special_fp_store + 4 ! continue
st %fsr, [%o0 + FS_FSR] ! but first finish the %fsr store
/*
* Load FPU state.
*/
ENTRY(loadfpstate)
rd %psr, %o1 ! enable FP before we begin
set PSR_EF, %o2
or %o1, %o2, %o1
wr %o1, 0, %psr
nop; nop; nop ! paranoia
ldd [%o0 + FS_REGS + (4*0)], %f0
ldd [%o0 + FS_REGS + (4*2)], %f2
ldd [%o0 + FS_REGS + (4*4)], %f4
ldd [%o0 + FS_REGS + (4*6)], %f6
ldd [%o0 + FS_REGS + (4*8)], %f8
ldd [%o0 + FS_REGS + (4*10)], %f10
ldd [%o0 + FS_REGS + (4*12)], %f12
ldd [%o0 + FS_REGS + (4*14)], %f14
ldd [%o0 + FS_REGS + (4*16)], %f16
ldd [%o0 + FS_REGS + (4*18)], %f18
ldd [%o0 + FS_REGS + (4*20)], %f20
ldd [%o0 + FS_REGS + (4*22)], %f22
ldd [%o0 + FS_REGS + (4*24)], %f24
ldd [%o0 + FS_REGS + (4*26)], %f26
ldd [%o0 + FS_REGS + (4*28)], %f28
ldd [%o0 + FS_REGS + (4*30)], %f30
retl
ld [%o0 + FS_FSR], %fsr ! setfsr(f->fs_fsr);
/*
* ienab_bis(bis) int bis;
* ienab_bic(bic) int bic;
*
* Set and clear bits in the interrupt register.
*/
#if defined(SUN4M) && (defined(SUN4) || defined(SUN4C))
ENTRY(ienab_bis)
sethi %hi(_cputyp), %o1
ld [%o1 + %lo(_cputyp)], %o1
cmp %o1, CPU_SUN4M
be,a _ienab_bis_4m
nop
b,a _ienab_bis_4c
ENTRY(ienab_bic)
sethi %hi(_cputyp), %o1
ld [%o1 + %lo(_cputyp)], %o1
cmp %o1, CPU_SUN4M
be,a _ienab_bic_4m
nop
b,a _ienab_bic_4c
#endif
#if defined(SUN4) || defined(SUN4C)
/*
* Since there are no read-modify-write instructions for this,
* and one of the interrupts is nonmaskable, we must disable traps.
*/
#if defined(SUN4M)
ENTRY(ienab_bis_4c)
#else
ENTRY(ienab_bis)
#endif
! %o0 = bits to set
rd %psr, %o2
wr %o2, PSR_ET, %psr ! disable traps
nop; nop ! 3-instr delay until ET turns off
sethi %hi(IE_reg_addr), %o3
ldub [%o3 + %lo(IE_reg_addr)], %o4
or %o4, %o0, %o4 ! *IE_reg_addr |= bis;
stb %o4, [%o3 + %lo(IE_reg_addr)]
wr %o2, 0, %psr ! reenable traps
nop
retl
nop
#if defined(SUN4M)
ENTRY(ienab_bic_4c)
#else
ENTRY(ienab_bic)
#endif
! %o0 = bits to clear
rd %psr, %o2
wr %o2, PSR_ET, %psr ! disable traps
nop; nop
sethi %hi(IE_reg_addr), %o3
ldub [%o3 + %lo(IE_reg_addr)], %o4
andn %o4, %o0, %o4 ! *IE_reg_addr &=~ bic;
stb %o4, [%o3 + %lo(IE_reg_addr)]
wr %o2, 0, %psr ! reenable traps
nop
retl
nop
#endif
#if defined(SUN4M)
/*
* sun4m has separate registers for clearing/setting the interrupt mask.
*/
#if defined(SUN4) || defined(SUN4C)
ENTRY(ienab_bis_4m)
#else
ENTRY(ienab_bis)
#endif
set ICR_SI_SET, %o1
retl
st %o0, [%o1]
#if defined(SUN4) || defined(SUN4C)
ENTRY(ienab_bic_4m)
#else
ENTRY(ienab_bic)
#endif
set ICR_SI_CLR, %o1
retl
st %o0, [%o1]
/*
* raise(cpu, level)
*/
ENTRY(raise)
! *(ICR_PI_SET + cpu*_MAXNBPG) = PINTR_SINTRLEV(level)
sethi %hi(1 << 16), %o2
sll %o2, %o1, %o2
set ICR_PI_SET, %o1
set _MAXNBPG, %o3
1:
subcc %o0, 1, %o0
bpos,a 1b
add %o1, %o3, %o1
retl
st %o2, [%o1]
#endif /* SUN4M */
/*
* ffs(), using table lookup.
* The process switch code shares the table, so we just put the
* whole thing here.
*/
ffstab:
.byte -24,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* 00-0f */
.byte 5,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* 10-1f */
.byte 6,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* 20-2f */
.byte 5,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* 30-3f */
.byte 7,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* 40-4f */
.byte 5,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* 50-5f */
.byte 6,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* 60-6f */
.byte 5,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* 70-7f */
.byte 8,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* 80-8f */
.byte 5,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* 10-9f */
.byte 6,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* a0-af */
.byte 5,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* b0-bf */
.byte 7,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* c0-cf */
.byte 5,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* d0-df */
.byte 6,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* e0-ef */
.byte 5,1,2,1,3,1,2,1,4,1,2,1,3,1,2,1 /* f0-ff */
/*
* We use a table lookup on each byte.
*
* In each section below, %o1 is the current byte (0, 1, 2, or 3).
* The last byte is handled specially: for the first three,
* if that byte is nonzero, we return the table value
* (plus 0, 8, or 16 for the byte number), but for the last
* one, we just return the table value plus 24. This means
* that ffstab[0] must be -24 so that ffs(0) will return 0.
*/
ENTRY(ffs)
set ffstab, %o2
andcc %o0, 0xff, %o1 ! get low byte
bz,a 1f ! try again if 0
srl %o0, 8, %o0 ! delay slot, get ready for next byte
retl ! return ffstab[%o1]
ldsb [%o2 + %o1], %o0
1:
andcc %o0, 0xff, %o1 ! byte 1 like byte 0...
bz,a 2f
srl %o0, 8, %o0 ! (use delay to prepare for byte 2)
ldsb [%o2 + %o1], %o0
retl ! return ffstab[%o1] + 8
add %o0, 8, %o0
2:
andcc %o0, 0xff, %o1
bz,a 3f
srl %o0, 8, %o0 ! (prepare for byte 3)
ldsb [%o2 + %o1], %o0
retl ! return ffstab[%o1] + 16
add %o0, 16, %o0
3: ! just return ffstab[%o0] + 24
ldsb [%o2 + %o0], %o0
retl
add %o0, 24, %o0
/*
* Here is a very good random number generator. This implementation is
* based on ``Two Fast Implementations of the "Minimal Standard" Random
* Number Generator", David G. Carta, Communications of the ACM, Jan 1990,
* Vol 33 No 1.
*/
.data
randseed:
.word 1
.text
ENTRY(random)
sethi %hi(16807), %o1
wr %o1, %lo(16807), %y
sethi %hi(randseed), %g1
ld [%g1 + %lo(randseed)], %o0
andcc %g0, 0, %o2
mulscc %o2, %o0, %o2
mulscc %o2, %o0, %o2
mulscc %o2, %o0, %o2
mulscc %o2, %o0, %o2
mulscc %o2, %o0, %o2
mulscc %o2, %o0, %o2
mulscc %o2, %o0, %o2
mulscc %o2, %o0, %o2
mulscc %o2, %o0, %o2
mulscc %o2, %o0, %o2
mulscc %o2, %o0, %o2
mulscc %o2, %o0, %o2
mulscc %o2, %o0, %o2
mulscc %o2, %o0, %o2
mulscc %o2, %o0, %o2
mulscc %o2, %g0, %o2
rd %y, %o3
srl %o2, 16, %o1
set 0xffff, %o4
and %o4, %o2, %o0
sll %o0, 15, %o0
srl %o3, 17, %o3
or %o3, %o0, %o0
addcc %o0, %o1, %o0
bneg 1f
sethi %hi(0x7fffffff), %o1
retl
st %o0, [%g1 + %lo(randseed)]
1:
or %o1, %lo(0x7fffffff), %o1
add %o0, 1, %o0
and %o1, %o0, %o0
retl
st %o0, [%g1 + %lo(randseed)]
/*
* void lo_microtime(struct timeval *tv)
*
* LBL's sparc bsd 'microtime': We don't need to spl (so this routine
* can be a leaf routine) and we don't keep a 'last' timeval (there
* can't be two calls to this routine in a microsecond). This seems to
* be about 20 times faster than the Sun code on an SS-2. - vj
*
* Read time values from slowest-changing to fastest-changing,
* then re-read out to slowest. If the values read before
* the innermost match those read after, the innermost value
* is consistent with the outer values. If not, it may not
* be and we must retry. Typically this loop runs only once;
* occasionally it runs twice, and only rarely does it run longer.
*/
#if defined(SUN4)
ENTRY(lo_microtime)
#else
ENTRY(microtime)
#endif
sethi %hi(_time), %g2
sethi %hi(TIMERREG_VA), %g3
/* blech, sun4m has microsecond counter at a different location */
#if (defined(SUN4) || defined(SUN4C)) && !defined(SUN4M)
#define r_aardvark %lo(TIMERREG_VA)
#elif !(defined(SUN4) || defined(SUN4C)) && defined(SUN4M)
#define r_aardvark %lo(TIMERREG_VA) + 4
#else
sethi %hi(_cputyp), %g4
ld [%g4 + %lo(_cputyp)], %g4
or %g3, %lo(TIMERREG_VA), %g3
cmp %g4, CPU_SUN4M
be,a 1f
add %g3, 4, %g3
1:
#define r_aardvark 0
#endif
2:
ldd [%g2+%lo(_time)], %o2 ! time.tv_sec & time.tv_usec
ld [%g3+r_aardvark], %o4 ! usec counter
ldd [%g2+%lo(_time)], %g4 ! see if time values changed
cmp %g4, %o2
bne 2b ! if time.tv_sec changed
cmp %g5, %o3
bne 2b ! if time.tv_usec changed
tst %o4
bpos 3f ! reached limit?
srl %o4, TMR_SHIFT, %o4 ! convert counter to usec
sethi %hi(_tick), %g4 ! bump usec by 1 tick
ld [%g4+%lo(_tick)], %o1
set TMR_MASK, %g5
add %o1, %o3, %o3
and %o4, %g5, %o4
3:
add %o4, %o3, %o3
set 1000000, %g5 ! normalize usec value
cmp %o3, %g5
bl,a 4f
st %o2, [%o0] ! (should be able to std here)
add %o2, 1, %o2 ! overflow
sub %o3, %g5, %o3
st %o2, [%o0] ! (should be able to std here)
4:
retl
st %o3, [%o0+4]
#if defined(KGDB) || defined(DDB)
/*
* Write all windows (user or otherwise), except the current one.
*
* THIS COULD BE DONE IN USER CODE
*/
ENTRY(write_all_windows)
/*
* g2 = g1 = nwindows - 1;
* while (--g1 > 0) save();
* while (--g2 > 0) restore();
*/
sethi %hi(_nwindows), %g1
ld [%g1 + %lo(_nwindows)], %g1
dec %g1
mov %g1, %g2
1: deccc %g1
bg,a 1b
save %sp, -64, %sp
2: deccc %g2
bg,a 2b
restore
retl
nop
#endif /* KGDB */
ENTRY(setjmp)
std %sp, [%o0+0] ! stack pointer & return pc
st %fp, [%o0+8] ! frame pointer
retl
clr %o0
Lpanic_ljmp:
.asciz "longjmp botch"
_ALIGN
ENTRY(longjmp)
addcc %o1, %g0, %g6 ! compute v ? v : 1 in a global register
be,a 0f
mov 1, %g6
0:
mov %o0, %g1 ! save a in another global register
ld [%g1+8], %g7 /* get caller's frame */
1:
cmp %fp, %g7 ! compare against desired frame
bl,a 1b ! if below,
restore ! pop frame and loop
be,a 2f ! if there,
ldd [%g1+0], %o2 ! fetch return %sp and pc, and get out
Llongjmpbotch:
! otherwise, went too far; bomb out
save %sp, -CCFSZ, %sp /* preserve current window */
sethi %hi(Lpanic_ljmp), %o0
call _panic
or %o0, %lo(Lpanic_ljmp), %o0;
unimp 0
2:
cmp %o2, %sp ! %sp must not decrease
bge,a 3f
mov %o2, %sp ! it is OK, put it in place
b,a Llongjmpbotch
3:
jmp %o3 + 8 ! success, return %g6
mov %g6, %o0
.data
#ifdef DDB
.globl _esym
_esym:
.word 0
#endif
.globl _cold
_cold:
.word 1 ! cold start flag
.globl _proc0paddr
_proc0paddr:
.word _u0 ! KVA of proc0 uarea
/* interrupt counters XXX THESE BELONG ELSEWHERE (if anywhere) */
.globl _intrcnt, _eintrcnt, _intrnames, _eintrnames
_intrnames:
.asciz "spur"
.asciz "lev1"
.asciz "lev2"
.asciz "lev3"
.asciz "lev4"
.asciz "lev5"
.asciz "lev6"
.asciz "lev7"
.asciz "lev8"
.asciz "lev9"
.asciz "clock"
.asciz "lev11"
.asciz "lev12"
.asciz "lev13"
.asciz "prof"
_eintrnames:
_ALIGN
_intrcnt:
.skip 4*15
_eintrcnt:
.comm _nwindows, 4
.comm _promvec, 4
.comm _curproc, 4
.comm _qs, 32 * 8
.comm _whichqs, 4