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