2603 lines
71 KiB
ArmAsm
2603 lines
71 KiB
ArmAsm
.ident "@(#)locore.s 1.1 94/10/31 SMI(PATCH)"
|
|
|
|
/*
|
|
* Copyright (c) 1989 by Sun Microsystems, Inc.
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/vmparam.h>
|
|
#include <sys/errno.h>
|
|
#include <netinet/in_systm.h>
|
|
#include <sun4/asm_linkage.h>
|
|
#include <sun4/buserr.h>
|
|
#include <sun4/clock.h>
|
|
#include <sun4/cpu.h>
|
|
#include <sun4/diag.h>
|
|
#include <sun4/enable.h>
|
|
#include <sun4/intreg.h>
|
|
#include <sun4/memerr.h>
|
|
#include <sun4/eccreg.h>
|
|
#include <sun4/eeprom.h>
|
|
#include <sun4/iocache.h>
|
|
#include <sun4/mmu.h>
|
|
#include <sun4/pcb.h>
|
|
#include <sun4/psl.h>
|
|
#include <sun4/pte.h>
|
|
#include <sun4/reg.h>
|
|
#include <sun4/trap.h>
|
|
#include <sun4/scb.h>
|
|
|
|
#include "assym.s"
|
|
|
|
.seg "data"
|
|
|
|
PROT = (PG_V | PG_W) >> PG_S_BIT
|
|
P_INVAL = (PG_V) >> PG_S_BIT
|
|
VALID = (PG_V) >> PG_S_BIT
|
|
|
|
!
|
|
! MINFRAME and the register offsets in struct regs must add up to allow
|
|
! double word loading of struct regs. PCB_WBUF must also be aligned.
|
|
!
|
|
#if (MINFRAME & 7) == 0 || (G2 & 1) == 0 || (O0 & 1) == 0
|
|
ERROR - struct regs not aligned
|
|
#endif
|
|
#if (PCB_WBUF & 7)
|
|
ERROR - pcb_wbuf not aligned
|
|
#endif
|
|
|
|
/*
|
|
* Absolute external symbols.
|
|
* On the sun4 we put the message buffer and the pte's
|
|
* for proc 0 on the same page, since we have such a large page size.
|
|
* We set things up so that the first page of KERNELBASE is illegal
|
|
* to act as a redzone during copyin/copyout type operations. One of
|
|
* the reasons the message buffer is allocated in low memory to
|
|
* prevent being overwritten during booting operations (besides
|
|
* the fact that it is small enough to share a page with others).
|
|
*/
|
|
.global _DVMA, _msgbuf
|
|
_DVMA = DVMABASE ! address of DVMA area
|
|
_msgbuf = KERNELBASE + NBPG ! address of printf message buffer
|
|
|
|
#if MSGBUFSIZE > NBPG
|
|
ERROR - msgbuf too large
|
|
#endif
|
|
|
|
#if USIZE-KERNSTACK >= 4096
|
|
ERROR - user area too large
|
|
#endif
|
|
|
|
/*
|
|
* Define some variables used by post-mortem debuggers
|
|
* to help them work on kernels with changing structures.
|
|
*/
|
|
.global UPAGES_DEBUG, KERNELBASE_DEBUG, VADDR_MASK_DEBUG
|
|
.global PGSHIFT_DEBUG, SLOAD_DEBUG
|
|
|
|
UPAGES_DEBUG = UPAGES
|
|
KERNELBASE_DEBUG = KERNELBASE
|
|
VADDR_MASK_DEBUG = 0xffffffff
|
|
PGSHIFT_DEBUG = PGSHIFT
|
|
SLOAD_DEBUG = SLOAD
|
|
|
|
/*
|
|
* The interrupt stack. This must be the first thing in the data
|
|
* segment (other than an sccs string) so that we don't stomp
|
|
* on anything important during interrupt handling. We get a
|
|
* red zone below this stack for free when the kernel text is
|
|
* write protected. The interrupt entry code assumes that the
|
|
* interrupt stack is at a lower address than
|
|
* both eintstack and the kernel stack in the u area.
|
|
*/
|
|
#define INTSTACKSIZE 0x3000
|
|
|
|
.align 8
|
|
.global intstack, eintstack, intu, eexitstack
|
|
intstack: ! bottom of interrupt stack
|
|
.skip INTSTACKSIZE
|
|
eintstack: ! end (top) of interrupt stack
|
|
exitstack: ! bottom of exit stack
|
|
.skip INTSTACKSIZE
|
|
eexitstack: ! end (top) of exit stack
|
|
|
|
.global _ubasic ! the primordial uarea
|
|
_ubasic:
|
|
.skip KERNSTACK
|
|
_p0uarea:
|
|
.skip USIZE
|
|
intu: ! a fake uarea for the kernel
|
|
.skip USIZE ! when a process is exiting
|
|
.global _uunix
|
|
_uunix:
|
|
.word _p0uarea
|
|
|
|
/*
|
|
* System software page tables
|
|
*/
|
|
|
|
#define vaddr_h(x) ((((x)-_Heapptes)/4)*NBPG + HEAPBASE)
|
|
#define HEAPMAP(mname, vname, npte) \
|
|
.global mname; \
|
|
mname: .skip (4*npte); \
|
|
.global vname; \
|
|
vname = vaddr_h(mname);
|
|
|
|
HEAPMAP(_Heapptes ,_Heapbase ,HEAPPAGES )
|
|
HEAPMAP(_Eheapptes ,_Heaplimit ,0 )
|
|
HEAPMAP(_Bufptes ,_Bufbase ,BUFPAGES )
|
|
HEAPMAP(_Ebufptes ,_Buflimit ,0 )
|
|
|
|
#define vaddr(x) ((((x)-_Sysmap)/4)*NBPG + SYSBASE)
|
|
#define SYSMAP(mname, vname, npte) \
|
|
.global mname; \
|
|
mname: .skip (4*npte); \
|
|
.global vname; \
|
|
vname = vaddr(mname);
|
|
|
|
SYSMAP(_Sysmap ,_Sysbase ,SYSPTSIZE )
|
|
SYSMAP(_CMAP1 ,_CADDR1 ,1 ) ! local tmp
|
|
SYSMAP(_CMAP2 ,_CADDR2 ,1 ) ! local tmp
|
|
SYSMAP(_mmap ,_vmmap ,1 )
|
|
SYSMAP(_Mbmap ,_mbutl ,MBPOOLMMUPAGES )
|
|
SYSMAP(_ESysmap ,_Syslimit ,0 ) ! must be last
|
|
|
|
.global _Syssize
|
|
_Syssize = (_ESysmap-_Heapptes)/4
|
|
|
|
#ifdef SAS
|
|
.global _availmem
|
|
_availmem:
|
|
.word 0
|
|
#endif SAS
|
|
|
|
/*
|
|
* Software copy of system enable register
|
|
* This is always atomically updated
|
|
*/
|
|
.global _enablereg
|
|
_enablereg: .byte 0 ! UNIX's system enable register
|
|
|
|
/*
|
|
* Opcodes for instructions in PATCH macros
|
|
*/
|
|
#define MOVPSRL0 0xa1480000
|
|
#define MOVL4 0xa8102000
|
|
#define BA 0x10800000
|
|
#define NO_OP 0x01000000
|
|
|
|
/*
|
|
* Trap vector macros.
|
|
*
|
|
* A single kernel that supports machines with differing
|
|
* numbers of windows has to write the last byte of every
|
|
* trap vector with NW-1, the number of windows minus 1.
|
|
* It does this at boot time after it has read the implementation
|
|
* type from the psr.
|
|
*
|
|
* NOTE: All trap vectors are generated by the following macros.
|
|
* The macros must maintain that a write to the last byte to every
|
|
* trap vector with the number of windows minus one is safe.
|
|
*/
|
|
#define TRAP(H) \
|
|
b (H); mov %psr,%l0; nop; nop;
|
|
|
|
/* the following trap uses only the trap window, you must be prepared */
|
|
#define WIN_TRAP(H) \
|
|
mov %psr,%l0; mov %wim,%l3; b (H); mov 7,%l6;
|
|
|
|
#define SYS_TRAP(T) \
|
|
mov %psr,%l0; mov (T),%l4; b sys_trap; mov 7,%l6;
|
|
|
|
#define BAD_TRAP SYS_TRAP((. - _scb) >> 4);
|
|
|
|
#define PATCH_ST(T, V) \
|
|
set _scb, %g1; \
|
|
set MOVPSRL0, %g2; \
|
|
st %g2, [%g1 + ((V)*16+0*4)]; \
|
|
set sys_trap, %g2; \
|
|
sub %g2, %g1, %g2; \
|
|
sub %g2, ((V)*16 + 8), %g2; \
|
|
srl %g2, 2, %g2; \
|
|
set BA, %g3; \
|
|
or %g2, %g3, %g2; \
|
|
st %g2, [%g1 + ((V)*16+2*4)]; \
|
|
set MOVL4 + (T), %g2; \
|
|
st %g2, [%g1 + ((V)*16+1*4)];
|
|
|
|
#define PATCH_T(H, V) \
|
|
set _scb, %g1; \
|
|
set (H), %g2; \
|
|
sub %g2, %g1, %g2; \
|
|
sub %g2, (V)*16, %g2; \
|
|
srl %g2, 2, %g2; \
|
|
set BA, %g3; \
|
|
or %g2, %g3, %g2; \
|
|
st %g2, [%g1 + ((V)*16+0*4)]; \
|
|
set MOVPSRL0, %g2; \
|
|
st %g2, [%g1 + ((V)*16+1*4)];
|
|
|
|
/*
|
|
* Trap vector table.
|
|
* This must be the first text in the boot image.
|
|
*
|
|
* When a trap is taken, we vector to KERNELBASE+(TT*16) and we have
|
|
* the following state:
|
|
* 2) traps are disabled
|
|
* 3) the previous state of PSR_S is in PSR_PS
|
|
* 4) the CWP has been incremented into the trap window
|
|
* 5) the previous pc and npc is in %l1 and %l2 respectively.
|
|
*
|
|
* Registers:
|
|
* %l0 - %psr immediately after trap
|
|
* %l1 - trapped pc
|
|
* %l2 - trapped npc
|
|
* %l3 - wim (sys_trap only)
|
|
* %l4 - system trap number (sys_trap only)
|
|
* %l6 - number of windows - 1
|
|
* %l7 - stack pointer (interrupts and system calls)
|
|
*
|
|
* Note: UNIX receives control at vector 0 (trap)
|
|
*/
|
|
.seg "text"
|
|
.align 4
|
|
|
|
.global _start, _scb
|
|
_start:
|
|
_scb:
|
|
TRAP(entry); ! 00
|
|
SYS_TRAP(T_FAULT | T_TEXT_FAULT); ! 01
|
|
WIN_TRAP(multiply_check); ! 02
|
|
SYS_TRAP(T_PRIV_INSTR); ! 03
|
|
SYS_TRAP(T_FP_DISABLED); ! 04
|
|
WIN_TRAP(window_overflow); ! 05
|
|
WIN_TRAP(window_underflow); ! 06
|
|
SYS_TRAP(T_ALIGNMENT); ! 07
|
|
SYS_TRAP(T_FP_EXCEPTION); ! 08
|
|
SYS_TRAP(T_FAULT | T_DATA_FAULT); ! 09
|
|
BAD_TRAP; ! 0A tag_overflow
|
|
BAD_TRAP; BAD_TRAP; ! 0B - 0C
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 0D - 10
|
|
SYS_TRAP(T_INTERRUPT | 1); ! 11
|
|
SYS_TRAP(T_INTERRUPT | 2); ! 12
|
|
SYS_TRAP(T_INTERRUPT | 3); ! 13
|
|
SYS_TRAP(T_INTERRUPT | 4); ! 14
|
|
SYS_TRAP(T_INTERRUPT | 5); ! 15
|
|
SYS_TRAP(T_INTERRUPT | 6); ! 16
|
|
SYS_TRAP(T_INTERRUPT | 7); ! 17
|
|
SYS_TRAP(T_INTERRUPT | 8); ! 18
|
|
SYS_TRAP(T_INTERRUPT | 9); ! 19
|
|
SYS_TRAP(T_INTERRUPT | 10); ! 1A
|
|
SYS_TRAP(T_INTERRUPT | 11); ! 1B
|
|
SYS_TRAP(T_INTERRUPT | 12); ! 1C
|
|
SYS_TRAP(T_INTERRUPT | 13); ! 1D
|
|
#ifdef GPROF
|
|
TRAP(test_prof); ! 1E
|
|
#else
|
|
SYS_TRAP(T_INTERRUPT | 14); ! 1E
|
|
#endif GPROF
|
|
SYS_TRAP(T_INTERRUPT | 15); ! 1F
|
|
|
|
#ifdef SUNDBE
|
|
#define HRES_TRAP TRAP(hrestimetrap)
|
|
#else SUNDBE
|
|
#define HRES_TRAP BAD_TRAP
|
|
#endif SUNDBE
|
|
|
|
/*
|
|
* The rest of the traps in the table up to 0x80 should 'never'
|
|
* be generated by hardware.
|
|
*/
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 20 - 23
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 24 - 27
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 28 - 2B
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 2C - 2F
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 30 - 33
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 34 - 37
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 38 - 33
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 3C - 3F
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 40 - 43
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 44 - 47
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 48 - 4B
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 4C - 4F
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 50 - 53
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 54 - 57
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 58 - 5B
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 5C - 5F
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 60 - 63
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 64 - 67
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 68 - 6B
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 6C - 6F
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 70 - 73
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 74 - 77
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 78 - 7B
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 7C - 7F
|
|
|
|
/*
|
|
* User generated traps
|
|
*/
|
|
SYS_TRAP(T_SYSCALL); ! 80 - system call
|
|
SYS_TRAP(T_BREAKPOINT); ! 81 - user breakpoint
|
|
SYS_TRAP(T_DIV0); ! 82 - divide by zero
|
|
SYS_TRAP(T_FLUSH_WINDOWS); ! 83 - flush windows
|
|
TRAP(clean_windows); ! 84 - clean windows
|
|
BAD_TRAP; ! 85 - range check
|
|
TRAP(fix_alignment); ! 86 - do unaligned references
|
|
SYS_TRAP(T_INT_OVERFLOW); ! 87 - integer overflow
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 88 - 8B
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 8C - 8F
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 90 - 93
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 94 - 97
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 98 - 9B
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 9C - 9F
|
|
TRAP(getcc); ! A0 - get condition codes
|
|
TRAP(setcc); ! A1 - set condition codes
|
|
BAD_TRAP; ! A2
|
|
HRES_TRAP; ! A3 - high res timer
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! A4 - A7
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! A8 - AB
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! AC - AF
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! B0 - B3
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! B4 - B7
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! B8 - BB
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! BC - BF
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! C0 - C3
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! C4 - C7
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! C8 - CB
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! CC - CF
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! D0 - D3
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! D4 - D7
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! D8 - DB
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! DC - DF
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! E0 - E3
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! E4 - E7
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! E8 - EB
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! EC - EF
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! F0 - F3
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! F4 - F7
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! F8 - FB
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! FC - FF
|
|
|
|
/*
|
|
* skip to next page, in case the trap table needs special mappings.
|
|
*
|
|
* CURRRENT SPECIAL MAPPINGS FOR THE SCB:
|
|
* SUNRAY IU v.1 requires SCB to be user readable
|
|
* SUN4_330 requires the SCB to be uncached
|
|
*/
|
|
#if defined(SUN4_330)
|
|
.skip 4096
|
|
#endif
|
|
|
|
!
|
|
! here we have a cache-aligned string of non_writeable
|
|
! zeros used mainly by bcopy hardware
|
|
!
|
|
.global _zeros
|
|
_zeros:
|
|
.word 0,0,0,0,0,0,0,0
|
|
|
|
/*
|
|
* The number of windows, set once on entry. Note that it is
|
|
* in the text segment so that it is write protected in startup().
|
|
*/
|
|
.global _nwindows
|
|
_nwindows:
|
|
.word 8
|
|
|
|
/*
|
|
* System initialization
|
|
*
|
|
* Do a halfhearted job of setting up the mmu so that we can run out
|
|
* of the high address space. We do this by copying the pmeg numbers
|
|
* for the physical locations to the correct virtual locations.
|
|
* NOTE - Assumes that the real and virtual locations have the same
|
|
* segment offsets from 0 and KERNELBASE!!!
|
|
*
|
|
* We make the following assumptions about our environment
|
|
* as set up by the monitor:
|
|
*
|
|
* - traps are disabled
|
|
* - we are loaded at 0x4000
|
|
* - we have enough memory mapped for the entire kernel + some more
|
|
* - all pages are writable
|
|
* - the last pmeg [SEGINV] has no valid pme's
|
|
* - the next to the last pmeg has no valid pme's
|
|
* - when the monitor's romp->v_memorybitmap points to a zero
|
|
* - each low segment i is mapped to use pmeg i
|
|
* - each page map entry i maps physical page i
|
|
* - on systems w/ ecc memory, that the monitor has set the base
|
|
* addresses and enabled all the memory cards correctly
|
|
*
|
|
* We will set the protection properly in startup().
|
|
* Before we set up the new mapping and start running with the correct
|
|
* addresses, all of the code must be carefully written to be position
|
|
* independent code, since we are linked for running out of high addresses,
|
|
* but we get control running in low addresses.
|
|
*/
|
|
entry:
|
|
set PSR_S|PSR_PIL, %g1 ! setup psr, disable traps
|
|
mov %g1, %psr
|
|
|
|
set CONTEXT_REG, %g1 ! setup kernel context (0)
|
|
stba %g0, [%g1]ASI_CTL
|
|
|
|
mov 0x02, %wim ! setup wim
|
|
|
|
!
|
|
! read machine type from idprom
|
|
!
|
|
set ID_PROM, %g1
|
|
inc %g1 ! machine type is second byte of idprom
|
|
lduba [%g1]ASI_CTL, %g5 ! g5 = id.id_machine, cpu type
|
|
|
|
cmp %g5, CPU_SUN4_470 ! relocate sunray by copying regions
|
|
set KERNELBASE, %l1 ! correct virtual address
|
|
set (KERNELBASE + MAINMEM_SIZE), %l2 ! ending virtual address
|
|
bne 3f ! relocate others by copying segments
|
|
clr %l0 ! current virtual address
|
|
|
|
!
|
|
! relocate the sunray kernel by copying region map entries
|
|
! this loop will not work if KERNELBASE is an address
|
|
! that is mod SMGRPSIZE (16M)
|
|
!
|
|
set SMGRPSIZE, %l3
|
|
1:
|
|
or %l0, 2, %l0
|
|
lduha [%l0]ASI_RM, %g1
|
|
or %l1, 2, %l1
|
|
stha %g1, [%l1]ASI_RM
|
|
add %l1, %l3, %l1
|
|
cmp %l1, %l2
|
|
ble,a 1b
|
|
add %l0, %l3, %l0 ! delay slot
|
|
b 2f
|
|
.empty ! hush assembler warnings
|
|
!
|
|
! Loop through the kernel segment maps starting at zero, copying the
|
|
! pmeg numbers to the correct virtual addresses, which start at
|
|
! KERNELBASE.
|
|
! NOTE: This loop won't work if the kernel size is greater than
|
|
! KERNELBASE.
|
|
!
|
|
3: set PMGRPSIZE, %l3
|
|
1: lduha [%l0]ASI_SM, %g1
|
|
stha %g1, [%l1]ASI_SM
|
|
add %l1, %l3, %l1
|
|
cmp %l1, %l2
|
|
ble,a 1b
|
|
add %l0, %l3, %l0 ! delay slot
|
|
|
|
!
|
|
! The correct virtual addresses are now mapped. Do an absolute jump
|
|
! to set the pc to the correct virtual addresses.
|
|
!
|
|
2: set 1f, %g1
|
|
jmp %g1
|
|
nop
|
|
1:
|
|
!
|
|
! Now we are running with correct addresses
|
|
! and can use non-position independent code.
|
|
!
|
|
|
|
!
|
|
! Patch vector 0 trap to "zero" in case it happens again.
|
|
!
|
|
PATCH_ST(T_ZERO, 0)
|
|
|
|
!
|
|
! Find the the number of implemented register windows.
|
|
! The last byte of every trap vector must be equal to
|
|
! the number of windows in the implementation minus one.
|
|
! The trap vector macros (above) depend on it!
|
|
!
|
|
mov %g0, %wim ! note psr has cwp = 0
|
|
set _scb, %g2
|
|
mov 256, %g3 ! number of trap vectors
|
|
sethi %hi(_nwindows), %g4 ! initialize pointer to _nwindows
|
|
|
|
save ! decrement cwp, wraparound to NW-1
|
|
mov %psr, %g1
|
|
and %g1, PSR_CWP, %g1 ! we now have nwindows-1
|
|
restore ! get back to orignal window
|
|
mov 2, %wim ! reset initial wim
|
|
0:
|
|
stb %g1, [%g2 + 15] ! write last byte of trap vectors
|
|
subcc %g3, 1, %g3 ! with nwindows-1
|
|
bnz 0b
|
|
add %g2, 16, %g2
|
|
|
|
inc %g1 ! inialize the nwindows variable
|
|
st %g1, [%g4 + %lo(_nwindows)]
|
|
|
|
#if defined(SUN4_470) || defined(SUN4_330)
|
|
!
|
|
! The code that flushes windows may have an extra save/restore
|
|
! as current sparc implementations have 7 and 8 windows.
|
|
! On those implementations with 7 windows write nops over the
|
|
! unneeded save/restore.
|
|
!
|
|
cmp %g1, 8
|
|
be 1f
|
|
.empty ! hush assembler warnings
|
|
set _fixnwindows, %o3
|
|
set NO_OP, %o2
|
|
st %o2, [%o3]
|
|
st %o2, [%o3 + 4]
|
|
1:
|
|
#endif defined(SUN4_470) || defined(SUN4_330)
|
|
|
|
#ifdef SAS
|
|
!
|
|
! If we are in the simulator we now size memory by counting the
|
|
! valid segment maps.
|
|
!
|
|
clr %l0
|
|
set PMGRPSIZE, %g2
|
|
2:
|
|
lduha [%l0]ASI_SM, %g1
|
|
cmp %g1, 255 ! 64 Meg - handles 260 and 70
|
|
blu,a 2b
|
|
add %l0, %g2, %l0
|
|
|
|
sethi %hi(_availmem), %g1
|
|
st %l0, [%g1 + %lo(_availmem)]
|
|
|
|
cmp %g5, CPU_SUN4_470 ! is this a sunray machine?
|
|
bne 1f
|
|
.empty ! next instruction ok in delay slot
|
|
|
|
!
|
|
! For sunray-type 3 level mmu we use the smeg
|
|
! after SYSPTSIZE for any virtual address
|
|
! mappings that must be in the upper 16 M of vm.
|
|
! The last smeg is reserved as the invalid smeg.
|
|
!
|
|
sethi %hi(0xffffc000), %l0 ! initialize last region for
|
|
! eeprom, clock, ...
|
|
|
|
set ((SYSPTSIZE*NBPG)+SMGRPSIZE-1)/SMGRPSIZE, %g2
|
|
andn %l0, 1, %l0 ! tweak address and data to get
|
|
or %l0, 2, %l0 ! the bits in just the right
|
|
sll %g2, 8, %g2 ! place (per Sunray spec)
|
|
stha %g2, [%l0]ASI_RM ! map second to last smeg
|
|
#endif SAS
|
|
!
|
|
! use a segment to map on board devices
|
|
!
|
|
1: sethi %hi(0xffffc000), %l0 ! initialize last region for
|
|
! eeprom, clock, ...
|
|
set ((SYSPTSIZE*NBPG)+PMGRPSIZE-1)/PMGRPSIZE, %g1
|
|
stha %g1, [%l0]ASI_SM ! write segment map
|
|
!
|
|
! Clear u area.
|
|
!
|
|
set _ubasic, %l0
|
|
set KERNSTACK+USIZE, %g1
|
|
2: subcc %g1, 4, %g1
|
|
bnz 2b
|
|
clr [%l0 + %g1]
|
|
|
|
!
|
|
! get software copy of enable register
|
|
!
|
|
set ENABLEREG, %g1 ! address of real version in hardware
|
|
lduba [%g1]ASI_CTL, %g1
|
|
sethi %hi(_enablereg), %g2 ! software copy of enable register
|
|
stb %g1, [%g2 + %lo(_enablereg)] ! update software copy
|
|
|
|
!
|
|
! Setup trap base and make a kernel stack.
|
|
!
|
|
mov %tbr, %l4 ! save monitor's tbr
|
|
bclr 0xfff, %l4 ! remove tt
|
|
set _scb, %g1 ! setup trap handler
|
|
mov %g1, %tbr
|
|
|
|
!
|
|
! set the initial kernel stack pointer for proc 0
|
|
!
|
|
set _ubasic, %l0
|
|
set KERNSTACK - SA(MINFRAME + REGSIZE), %sp
|
|
add %l0, %sp, %sp
|
|
mov 0, %fp
|
|
!
|
|
! Dummy up fake user registers on the stack.
|
|
!
|
|
set USRSTACK-WINDOWSIZE, %g1
|
|
st %g1, [%sp + MINFRAME + SP*4] ! user stack pointer
|
|
set PSL_USER, %l0
|
|
st %l0, [%sp + MINFRAME + PSR*4] ! psr
|
|
set USRTEXT, %g1
|
|
st %g1, [%sp + MINFRAME + PC*4] ! pc
|
|
add %g1, 4, %g1
|
|
st %g1, [%sp + MINFRAME + nPC*4] ! npc
|
|
|
|
mov %psr, %g1
|
|
wr %g1, PSR_ET, %psr ! enable traps
|
|
!
|
|
! Zero bss.
|
|
!
|
|
set _edata, %o0
|
|
set _end, %o1
|
|
call _bzero
|
|
sub %o1, %o0, %o1
|
|
|
|
#ifndef SAS
|
|
!
|
|
! Save monitor's level14 clock interrupt vector code.
|
|
!
|
|
or %l4, TT(T_INT_LEVEL_14), %o0
|
|
set _mon_clock14_vec, %o1
|
|
call _bcopy ! bcopy(mon tbr level 14 vec,
|
|
mov 16, %o2 ! _mon_clock14_vec, 16)
|
|
#endif SAS
|
|
|
|
!
|
|
! Call main. We will return as process 1 (init).
|
|
!
|
|
call _main
|
|
add %sp, MINFRAME, %o0
|
|
!
|
|
! Proceed as if this was a normal user trap.
|
|
!
|
|
b,a sys_rtt ! fake return from trap
|
|
|
|
/*
|
|
* Generic system trap handler.
|
|
*/
|
|
.global sys_trap
|
|
sys_trap:
|
|
!
|
|
! Prepare to go to C (batten down the hatches).
|
|
!
|
|
mov 0x01, %l5 ! CWM = 0x01 << CWP
|
|
sll %l5, %l0, %l5
|
|
mov %wim, %l3 ! get WIM
|
|
btst PSR_PS, %l0 ! test pS
|
|
bz st_user ! branch if user trap
|
|
btst %l5, %l3 ! delay slot, compare WIM and CWM
|
|
|
|
!
|
|
! Trap from supervisor.
|
|
! We can be either on the system stack or interrupt stack.
|
|
!
|
|
sub %fp, MINFRAME+REGSIZE, %l7 ! save sys globals on stack
|
|
SAVE_GLOBALS(%l7 + MINFRAME)
|
|
#ifdef TRAPWINDOW
|
|
!
|
|
! store the window at the time of the trap into a static area.
|
|
!
|
|
set _trap_window, %g1
|
|
mov %wim, %g2
|
|
st %g2, [%g1+96]
|
|
mov %psr, %g2
|
|
restore
|
|
st %o0, [%g1]; st %o1, [%g1+4]; st %o2, [%g1+8]; st %o3, [%g1+12]
|
|
st %o4, [%g1+16]; st %o5, [%g1+20]; st %o6, [%g1+24]; st %o7, [%g1+28]
|
|
st %l0, [%g1+32]; st %l1, [%g1+36]; st %l2, [%g1+40]; st %l3, [%g1+44]
|
|
st %l4, [%g1+48]; st %l5, [%g1+52]; st %l6, [%g1+56]; st %l7, [%g1+60]
|
|
st %i0, [%g1+64]; st %i1, [%g1+68]; st %i2, [%g1+72]; st %i3, [%g1+76]
|
|
st %i4, [%g1+80]; st %i5, [%g1+84]; st %i6, [%g1+88]; st %i7, [%g1+92]
|
|
mov %g2, %psr
|
|
nop; nop; nop;
|
|
btst %l5, %l3 ! retest
|
|
#endif TRAPWINDOW
|
|
st %fp, [%l7 + MINFRAME + SP*4] ! stack pointer
|
|
st %l0, [%l7 + MINFRAME + PSR*4] ! psr
|
|
st %l1, [%l7 + MINFRAME + PC*4] ! pc
|
|
!
|
|
! If we are in last trap window, all windows are occupied and
|
|
! we must do window overflow stuff in order to do further calls
|
|
!
|
|
bz st_have_window ! if ((CWM&WIM)==0) no overflow
|
|
st %l2, [%l7 + MINFRAME + nPC*4] ! npc
|
|
b,a st_sys_ovf
|
|
|
|
st_user:
|
|
!
|
|
! Trap from user. Save user globals and prepare system stack.
|
|
! Test whether the current window is the last available window
|
|
! in the register file (CWM == WIM).
|
|
!
|
|
set _masterprocp, %l7
|
|
ld [%l7], %l7
|
|
ld [%l7 + P_STACK], %l7 ! initial kernel sp for this process
|
|
|
|
SAVE_GLOBALS(%l7 + MINFRAME)
|
|
SAVE_OUTS(%l7 + MINFRAME)
|
|
st %l0, [%l7 + MINFRAME + PSR*4] ! psr
|
|
st %l1, [%l7 + MINFRAME + PC*4] ! pc
|
|
st %l2, [%l7 + MINFRAME + nPC*4] ! npc
|
|
set _uunix, %g5
|
|
ld [%g5], %g5
|
|
!
|
|
! If we are in last trap window, all windows are occupied and
|
|
! we must do window overflow stuff in order to do further calls
|
|
!
|
|
bz 1f ! if ((CWM&WIM)==0) no overflow
|
|
clr [%g5 + PCB_WBCNT] ! delay slot, save buffer ptr = 0
|
|
|
|
not %l5, %g2 ! UWM = ~CWM
|
|
mov -2, %g3 ! gen window mask from NW-1 in %l6
|
|
sll %g3, %l6, %g3
|
|
andn %g2, %g3, %g2
|
|
b st_user_ovf ! overflow
|
|
srl %l3, 1, %g1 ! delay slot,WIM = %g1 = ror(WIM, 1, NW)
|
|
|
|
!
|
|
! Compute the user window mask (u.u_pcb.pcb_uwm), which is a mask of
|
|
! window which contain user data. It is all the windows "between"
|
|
! CWM and WIM.
|
|
!
|
|
1:
|
|
subcc %l3, %l5, %g1 ! if (WIM >= CWM)
|
|
bneg,a 2f ! u.u_pcb.pcb_uwm = (WIM-CWM)&~CWM
|
|
sub %g1, 1, %g1 ! else
|
|
2: ! u.u_pcb.pcb_uwm = (WIM-CWM-1)&~CWM
|
|
bclr %l5, %g1
|
|
mov -2, %g3 ! gen window mask from NW-1 in %l6
|
|
sll %g3, %l6, %g3
|
|
andn %g1, %g3, %g1
|
|
set _uunix, %g5 ! XXX - global u register?
|
|
ld [%g5], %g5
|
|
st %g1, [%g5 + PCB_UWM]
|
|
|
|
st_have_window:
|
|
!
|
|
! The next window is open.
|
|
!
|
|
mov %l7, %sp ! setup previously computed stack
|
|
!
|
|
! Part of fix for 1041977 (fitoX fix can panic kernel):
|
|
! Before we enable traps, see if this is a machine that needs
|
|
! the fitoX fix. If so, then see if we came from user mode with
|
|
! FPU enabled. If so, save (compute) rp in fpneedsyncfpu.
|
|
! Then, after we process the trap, we see if fpneedsyncfpu is set.
|
|
! If so, we call syncfpu with it, and clear fpneedsyncfpu when
|
|
! it returns. There is a race in that we could call syncfpu
|
|
! twice, but that would be harmless.
|
|
!
|
|
sethi %hi(_fpneedfitoX), %g1 ! do we need FitoX fix?
|
|
ld [%g1 + %lo(_fpneedfitoX)],%g1
|
|
tst %g1
|
|
bz 1f ! branch if needfitoX == 0
|
|
.empty
|
|
set PSR_PS|PSR_EF, %o1
|
|
and %l0, %o1, %o2
|
|
set PSR_EF, %o1
|
|
cmp %o2, %o1
|
|
bne 1f ! branch if PS=1 or EF=0
|
|
add %l7, MINFRAME, %o0
|
|
sethi %hi(fpneedsyncfpu), %g1
|
|
st %o0, [%g1 + %lo(fpneedsyncfpu)]
|
|
1:
|
|
! End of part of fix for 1041977
|
|
!
|
|
! Process trap according to type
|
|
!
|
|
btst T_INTERRUPT, %l4 ! interrupt
|
|
bnz interrupt
|
|
cmp %l4, T_SYSCALL ! syscall
|
|
be syscall
|
|
btst T_FAULT, %l4 ! fault
|
|
bnz,a fault
|
|
bclr T_FAULT, %l4
|
|
cmp %l4, T_FP_EXCEPTION ! floating point exception
|
|
be _fp_exception
|
|
cmp %l4, T_FP_DISABLED ! fpu is disabled
|
|
be _fp_disabled
|
|
cmp %l4, T_FLUSH_WINDOWS ! flush user windows to stack
|
|
bne 1f
|
|
wr %l0, PSR_ET, %psr ! enable traps
|
|
nop ! psr delay
|
|
!
|
|
! Flush windows trap.
|
|
!
|
|
call _flush_user_windows ! flush user windows
|
|
nop
|
|
!
|
|
! Don't redo trap instruction.
|
|
!
|
|
ld [%sp + MINFRAME + nPC*4], %g1
|
|
st %g1, [%sp + MINFRAME + PC*4] ! pc = npc
|
|
add %g1, 4, %g1
|
|
b sys_rtt
|
|
st %g1, [%sp + MINFRAME + nPC*4] ! npc = npc + 4
|
|
nop ! psr delay
|
|
|
|
1:
|
|
!
|
|
! All other traps. Call C trap handler.
|
|
!
|
|
mov %l4, %o0 ! trap(t, rp)
|
|
call _trap ! C trap handler
|
|
add %sp, MINFRAME, %o1
|
|
b,a sys_rtt ! return from trap
|
|
/* end systrap */
|
|
|
|
/*
|
|
* Sys_trap overflow handling.
|
|
* Psuedo subroutine returns to st_have_window.
|
|
*/
|
|
st_sys_ovf:
|
|
!
|
|
! Overflow from system.
|
|
! Determine whether the next window is a user window.
|
|
! If u.u_pcb.pcb_uwm has any bits set, then it is a user window
|
|
! which must be saved.
|
|
!
|
|
#ifdef PERFMETER
|
|
sethi %hi(_overflowcnt), %g5
|
|
ld [%g5 + %lo(_overflowcnt)], %g2
|
|
inc %g2
|
|
st %g2, [%g5 + %lo(_overflowcnt)]
|
|
#endif PERFMETER
|
|
set _uunix, %g5 ! XXX - global u register?
|
|
ld [%g5], %g5
|
|
ld [%g5 + PCB_UWM], %g2 ! if (u.u_pcb.pcb_uwm)
|
|
tst %g2 ! user window
|
|
bnz st_user_ovf
|
|
srl %l3, 1, %g1 ! delay slot,WIM = %g1 = ror(WIM, 1, NW)
|
|
!
|
|
! Save supervisor window. Compute the new WIM and change current window
|
|
! to the window to be saved.
|
|
!
|
|
sll %l3, %l6, %l3 ! %l6 == NW-1
|
|
or %l3, %g1, %g1
|
|
save ! get into window to be saved
|
|
mov %g1, %wim ! install new WIM
|
|
!
|
|
! Save window on the stack.
|
|
!
|
|
st_stack_res:
|
|
SAVE_WINDOW(%sp)
|
|
b st_have_window ! finished overflow processing
|
|
restore ! delay slot, back to original window
|
|
|
|
st_user_ovf:
|
|
!
|
|
! Overflow. Window to be saved is a user window.
|
|
! Compute the new WIM and change the current window to the
|
|
! window to be saved.
|
|
!
|
|
sll %l3, %l6, %l3 ! %l6 == NW-1
|
|
or %l3, %g1, %g1
|
|
bclr %g1, %g2 ! turn off uwm bit for window
|
|
set _uunix, %g5 ! XXX - global u register?
|
|
ld [%g5], %g5
|
|
st %g2, [%g5 + PCB_UWM] ! we are about to save
|
|
save ! get into window to be saved
|
|
mov %g1, %wim ! install new WIM
|
|
!
|
|
! We must check whether the user stack is resident where the window
|
|
! will be saved, which is pointed to by the window's sp.
|
|
! We must also check that the sp is aligned to a word boundary.
|
|
! Normally, we would check the alignment, and then probe the top
|
|
! and bottom of the save area on the stack. However we optimize
|
|
! this by checking that both ends of the save area are within a
|
|
! 4k unit (the biggest mask we can generate in one cycle), and
|
|
! the alignment in one shot. This allows us to do one probe to
|
|
! the page map. NOTE: this assumes a page size of at least 4k.
|
|
!
|
|
and %sp, 0xfff, %g1
|
|
#ifdef VA_HOLE
|
|
! check if the sp points into the hole in the address space
|
|
sethi %hi(_hole_shift), %g2 ! hole shift address
|
|
ld [%g2 + %lo(_hole_shift)], %g3
|
|
add %g1, (14*4), %g1 ! interlock, top of save area
|
|
sra %sp, %g3, %g2
|
|
inc %g2
|
|
andncc %g2, 1, %g2
|
|
bz 1f
|
|
andncc %g1, 0xff8, %g0
|
|
b,a st_stack_not_res ! sp points into the hole
|
|
1:
|
|
#else
|
|
add %g1, (14*4), %g1
|
|
andncc %g1, 0xff8, %g0
|
|
#endif
|
|
bz,a st_sp_bot
|
|
lda [%sp]ASI_PM, %g1 ! check for stack page resident
|
|
!
|
|
! Stack is either misaligned or crosses a 4k boundary.
|
|
!
|
|
btst 0x7, %sp ! test sp alignment
|
|
bz st_sp_top
|
|
add %sp, (14*4), %g1 ! delay slot, check top of save area
|
|
b,a st_stack_not_res ! stack misaligned, catch it later
|
|
|
|
st_sp_top:
|
|
#ifdef VA_HOLE
|
|
! check if the sp points into the hole in the address space
|
|
sra %g1, %g3, %g2
|
|
inc %g2
|
|
andncc %g2, 1, %g2
|
|
bz,a 1f
|
|
lda [%g1]ASI_PM, %g1 ! get pme for this address
|
|
b,a st_stack_not_res ! stack page can never be resident
|
|
1:
|
|
srl %g1, PG_S_BIT, %g1 ! get vws bits
|
|
sra %sp, %g3, %g2
|
|
inc %g2
|
|
andncc %g2, 1, %g2
|
|
bz 1f
|
|
cmp %g1, PROT ! look for valid, writeable, user
|
|
b,a st_stack_not_res ! stack not resident, catch it later
|
|
1:
|
|
#else
|
|
lda [%g1]ASI_PM, %g1 ! get pme for this address
|
|
srl %g1, PG_S_BIT, %g1 ! get vws bits
|
|
cmp %g1, PROT ! look for valid, writeable, user
|
|
#endif
|
|
|
|
be,a st_sp_bot
|
|
lda [%sp]ASI_PM, %g1 ! delay slot, check bottom of save area
|
|
b,a st_stack_not_res ! stack not resident, catch it later
|
|
|
|
st_sp_bot:
|
|
srl %g1, PG_S_BIT, %g1 ! get vws bits
|
|
cmp %g1, PROT ! look for valid, writeable, user
|
|
be st_stack_res
|
|
nop ! extra nop in rare case
|
|
|
|
st_stack_not_res:
|
|
!
|
|
! User stack is not resident, save in u area for processing in sys_rtt.
|
|
!
|
|
sethi %hi(_uunix), %g5 ! XXX - global u register?
|
|
ld [%g5 + %lo(_uunix)], %g5
|
|
ld [%g5 + PCB_WBCNT], %g1
|
|
sll %g1, 2, %g1 ! convert to spbuf offset
|
|
|
|
add %g1, %g5, %g2
|
|
st %sp, [%g2 + PCB_SPBUF] ! save sp
|
|
sll %g1, 4, %g1 ! convert wbcnt to pcb_wbuf offset
|
|
|
|
sethi %hi(_uunix), %g5 ! XXX - global u register?
|
|
ld [%g5 + %lo(_uunix)], %g5
|
|
add %g1, %g5, %g2
|
|
set PCB_WBUF, %g4
|
|
add %g4, %g2, %g2
|
|
SAVE_WINDOW(%g2)
|
|
srl %g1, 6, %g1 ! increment u.u_pcb.pcb_wbcnt
|
|
add %g1, 1, %g1
|
|
|
|
sethi %hi(_uunix), %g5 ! XXX - global u register?
|
|
ld [%g5 + %lo(_uunix)], %g5
|
|
st %g1, [%g5 + PCB_WBCNT]
|
|
b st_have_window ! finished overflow processing
|
|
restore ! delay slot, back to original window
|
|
/* end sys_trap overflow */
|
|
|
|
.seg "data"
|
|
.align 4
|
|
|
|
.global _overflowcnt, _underflowcnt
|
|
_overflowcnt:
|
|
.word 0
|
|
_underflowcnt:
|
|
.word 0
|
|
|
|
.global _qrunflag, _queueflag, _queuerun
|
|
|
|
.global fzero, f0save, fsrsave
|
|
fzero: .word 0 ! used in the fitoX fix below
|
|
f0save: .word 0
|
|
fsrsave: .word 0
|
|
|
|
! part of fix for 1041977 (fitoX fix can panic kernel)
|
|
.global _fpneedfitoX, fpneedsyncfpu
|
|
_fpneedfitoX: .word 0
|
|
fpneedsyncfpu: .word 0
|
|
|
|
.seg "text"
|
|
.align 4
|
|
|
|
/*
|
|
* Return from sys_trap routine.
|
|
*/
|
|
.global sys_rtt
|
|
sys_rtt:
|
|
|
|
#if defined(SUN4_260) || defined(SUN4_110)
|
|
! The above #if is misleading, since all binaries will have this code!
|
|
!
|
|
! code to fix fitoX bug
|
|
! part of fix for 1041977 (fitoX fix can panic kernel)
|
|
! Now that we have processed the trap, we see if fpneedsyncfpu is set.
|
|
! If so, we call syncfpu with it, and clear fpneedsyncfpu when
|
|
! it returns. There is a race in that we could call syncfpu
|
|
! twice, but that would be harmless.
|
|
! Note that we could have switched to another process, but that
|
|
! is okay, as then syncfpu() would have been called from trap()
|
|
! or syscall() prior to the switch, and this one will not cause
|
|
! an exception, so the fact that fpneedsyncfpu contains the
|
|
! "wrong" value is okay.
|
|
!
|
|
sethi %hi(_fpneedfitoX), %o0 ! do we need FitoX fix?
|
|
ld [%o0 + %lo(_fpneedfitoX)],%o0
|
|
sethi %hi(fpneedsyncfpu), %g1
|
|
tst %o0
|
|
bz 1f ! no, skip FitoX fix
|
|
ld [%g1 + %lo(fpneedsyncfpu)], %o0
|
|
tst %o0
|
|
bz 2f
|
|
nop
|
|
call _syncfpu
|
|
nop
|
|
sethi %hi(fpneedsyncfpu), %g1
|
|
st %g0, [%g1 + %lo(fpneedsyncfpu)]
|
|
2:
|
|
! End of part of fix for 1041977
|
|
!
|
|
mov %psr, %o1
|
|
set PSR_EF, %o2 ! is the FPU enabled
|
|
btst %o2, %o1
|
|
bz 1f ! FPU not enabled, skip FitoX fix
|
|
or %o1, PSR_PIL, %o1 ! splhi, while writing global f0save
|
|
mov %o1, %psr
|
|
nop;nop ! psr delay
|
|
sethi %hi(fsrsave), %o0 ! save %fsr
|
|
st %fsr, [%o0 + %lo(fsrsave)]
|
|
sethi %hi(f0save), %o1 ! use %f0, save it before changing
|
|
st %f0, [%o1 + %lo(f0save)]! save %f0
|
|
sethi %hi(fzero), %o2 ! ld value for 0.0
|
|
ld [%o2 + %lo(fzero)], %f0
|
|
.global _klo_fadds
|
|
_klo_fadds:
|
|
fadds %f0, %f0, %f0 ! clear fitoX state machine
|
|
fmovs %f0, %f0 ! if exception generated, ignore
|
|
ld [%o1 + %lo(f0save)], %f0! restore %f0
|
|
sethi %hi(fsrsave), %o0
|
|
ld [%o0 + %lo(fsrsave)], %fsr ! restore %fsr
|
|
1:
|
|
#endif /* SUN4_260 || SUN4_110 */
|
|
|
|
!
|
|
! Return from trap.
|
|
!
|
|
ld [%sp + MINFRAME + PSR*4], %l0 ! get saved psr
|
|
btst PSR_PS, %l0 ! test pS for return to supervisor
|
|
bnz sr_sup
|
|
mov %psr, %g1
|
|
|
|
#ifdef LWP
|
|
.global ___Nrunnable, _lwpschedule
|
|
#endif
|
|
sr_user:
|
|
!
|
|
! Return to user. Turn off traps using the current CWP (because
|
|
! we are returning to user). Test for streams actions.
|
|
! Test for LWP, AST for resched. or prof, or streams.
|
|
!
|
|
sethi %hi(_uunix), %g5 ! XXX - global u register?
|
|
ld [%g5 + %lo(_uunix)], %g5
|
|
and %g1, PSR_CWP, %g1 ! insert current CWP in old psr
|
|
andn %l0, PSR_CWP, %l0
|
|
or %l0, %g1, %l0
|
|
mov %l0, %psr ! install old psr, disable traps
|
|
nop; nop; ! psr delay
|
|
#ifdef LWP
|
|
sethi %hi(___Nrunnable), %g2
|
|
ld [%g2 + %lo(___Nrunnable)], %g2
|
|
tst %g2
|
|
bz 1f
|
|
nop
|
|
!
|
|
! Runnable thread for async I/O
|
|
!
|
|
wr %l0, PSR_ET, %psr ! turn on traps
|
|
nop ! psr delay
|
|
call _flush_user_windows
|
|
nop
|
|
call _lwpschedule
|
|
nop
|
|
|
|
!
|
|
! Use kernel context (for now) since lwpschedule may
|
|
! change the context reg. This causes a fault.
|
|
!
|
|
set CONTEXT_REG, %g2
|
|
b sys_rtt ! try again
|
|
stba %g0, [%g2]ASI_CTL
|
|
1:
|
|
#endif LWP
|
|
sethi %hi(_qrunflag), %g1
|
|
ldub [%g1 + %lo(_qrunflag)], %g1
|
|
sethi %hi(_queueflag), %l5 ! interlock slot
|
|
tst %g1 ! need to run stream queues?
|
|
bz,a 3f ! no
|
|
ld [%g5 + PCB_FLAGS], %g1 ! delay slot, test for ast.
|
|
|
|
ldub [%l5 + %lo(_queueflag)], %g1
|
|
tst %g1 ! already running queues?
|
|
bnz,a 3f ! yes
|
|
ld [%g5 + PCB_FLAGS], %g1 ! delay slot, test for ast.
|
|
!
|
|
! Run the streams queues.
|
|
!
|
|
andn %l0, PSR_PIL, %g2 ! splhi
|
|
or %g2, 10 << PSR_PIL_BIT, %g2
|
|
mov %g2, %psr ! change priority (IU bug)
|
|
wr %g2, PSR_ET, %psr ! turn on traps
|
|
add %g1, 1, %g1 ! mark that we're running the queues
|
|
call _queuerun
|
|
stb %g1, [%l5 + %lo(_queueflag)]
|
|
|
|
clrb [%l5 + %lo(_queueflag)] ! done running queues
|
|
b,a sys_rtt
|
|
|
|
3:
|
|
srl %g1, AST_SCHED_BIT, %g1
|
|
btst 1, %g1
|
|
bz,a 1f
|
|
ld [%g5 + PCB_WBCNT], %g3 ! delay slot, user regs been saved?
|
|
|
|
!
|
|
! Let trap handle the AST.
|
|
!
|
|
wr %l0, PSR_ET, %psr ! turn on traps
|
|
mov T_AST, %o0
|
|
call _trap ! trap(T_AST, rp)
|
|
add %sp, MINFRAME, %o1
|
|
b,a sys_rtt
|
|
|
|
!
|
|
! Return from trap, for yield_child
|
|
!
|
|
.global _fork_rtt
|
|
_fork_rtt:
|
|
sethi %hi(_masterprocp), %l6
|
|
ld [%l6 + %lo(_masterprocp)], %o0
|
|
ld [%o0 + P_STACK], %l7
|
|
mov %sp, %o2
|
|
call _sys_rttchk ! sys_rttchk(procp)
|
|
mov %l7, %o1
|
|
|
|
ld [%sp + MINFRAME + PSR*4], %l0 ! get saved psr
|
|
btst PSR_PS, %l0 ! test pS for return to supervisor
|
|
bnz sr_sup
|
|
mov %psr, %g1
|
|
|
|
sethi %hi(_uunix), %g5
|
|
ld [%g5 + %lo(_uunix)], %g5
|
|
and %g1, PSR_CWP, %g1 ! insert current CWP in old psr
|
|
andn %l0, PSR_CWP, %l0
|
|
or %l0, %g1, %l0
|
|
mov %l0, %psr ! install old psr, disable traps
|
|
nop ! psr delay
|
|
b 3b
|
|
ld [%g5 + PCB_FLAGS], %g1 ! delay slot, test for ast.
|
|
|
|
1:
|
|
!
|
|
! If user regs have been saved to the window buffer we must clean it.
|
|
!
|
|
tst %g3
|
|
bz,a 2f
|
|
ld [%g5 + PCB_UWM], %l4 ! delay slot, user windows in reg file?
|
|
|
|
!
|
|
! User regs have been saved into the u area.
|
|
! Let trap handle putting them on the stack.
|
|
!
|
|
mov %l0, %psr ! in case of changed priority (IU bug)
|
|
wr %l0, PSR_ET, %psr ! turn on traps
|
|
mov T_WIN_OVERFLOW, %o0
|
|
call _trap ! trap(T_WIN_OVERFLOW, rp)
|
|
add %sp, MINFRAME, %o1
|
|
b,a sys_rtt
|
|
|
|
2:
|
|
!
|
|
! We must insure that the rett will not take a window underflow trap.
|
|
!
|
|
RESTORE_OUTS(%sp + MINFRAME) ! restore user outs
|
|
tst %l4
|
|
bnz sr_user_regs
|
|
ld [%sp + MINFRAME + PC*4], %l1 ! restore user pc
|
|
|
|
!
|
|
! The user has no windows in the register file.
|
|
! Try to get one from his stack.
|
|
!
|
|
#ifdef PERFMETER
|
|
sethi %hi(_underflowcnt), %l6
|
|
ld [%l6 + %lo(_underflowcnt)], %l3
|
|
inc %l3
|
|
st %l3, [%l6 + %lo(_underflowcnt)]
|
|
#endif PERFMETER
|
|
set _scb, %l6 ! get NW-1 for rol calculation
|
|
ldub [%l6+(5*16)+15], %l6 ! last byte of overflow trap vector
|
|
mov %wim, %l3 ! get wim
|
|
sll %l3, 1, %l4 ! next WIM = rol(WIM, 1, NW)
|
|
srl %l3, %l6, %l5 ! %l6 == NW-1
|
|
or %l5, %l4, %l5
|
|
mov %l5, %wim ! install it
|
|
!
|
|
! Normally, we would check the alignment, and then probe the top
|
|
! and bottom of the save area on the stack. However we optimize
|
|
! this by checking that both ends of the save area are within a
|
|
! 4k unit (the biggest mask we can generate in one cycle), and
|
|
! the alignment in one shot. This allows us to do one probe to
|
|
! the page map. NOTE: this assumes a page size of at least 4k.
|
|
!
|
|
and %fp, 0xfff, %g1
|
|
#ifdef VA_HOLE
|
|
! check if the fp points into the hole in the address space
|
|
sethi %hi(_hole_shift), %g2 ! hole shift address
|
|
ld [%g2 + %lo(_hole_shift)], %g3
|
|
add %g1, (14*4), %g1 ! interlock, bottom of save area
|
|
sra %fp, %g3, %g2
|
|
inc %g2
|
|
andncc %g2, 1, %g2
|
|
bz 1f
|
|
andncc %g1, 0xff8, %g0
|
|
b,a sr_stack_not_res ! stack page can never be resident
|
|
1:
|
|
#else
|
|
add %g1, (14*4), %g1
|
|
andncc %g1, 0xff8, %g0
|
|
#endif
|
|
bz,a sr_sp_bot
|
|
lda [%fp]ASI_PM, %g2 ! check for stack page resident
|
|
!
|
|
! Stack is either misaligned or crosses a 4k boundary.
|
|
!
|
|
btst 0x7, %fp ! test fp alignment
|
|
bz sr_sp_top
|
|
add %fp, (14*4), %g1 ! delay slot, check top of save area
|
|
|
|
!
|
|
! A user underflow with a misaligned sp.
|
|
! Fake a memory alignment trap.
|
|
!
|
|
mov %l3, %wim ! restore old wim
|
|
sr_align_trap:
|
|
mov %l0, %psr ! in case of changed priority (IU bug)
|
|
wr %l0, PSR_ET, %psr ! turn on traps
|
|
mov T_ALIGNMENT, %o0
|
|
call _trap ! trap(T_ALIGNMENT, rp)
|
|
add %sp, MINFRAME, %o1
|
|
b,a sys_rtt
|
|
|
|
sr_sp_top:
|
|
#ifdef VA_HOLE
|
|
sra %g1, %g3, %g2
|
|
inc %g2
|
|
andncc %g2, 1, %g2
|
|
bz,a 1f
|
|
lda [%g1]ASI_PM, %g2 ! get pme for this address
|
|
b,a sr_stack_not_res ! stack page can never be resident
|
|
1:
|
|
srl %g2, PG_S_BIT, %g2 ! get vws bits
|
|
sra %fp, %g3, %g3
|
|
inc %g3
|
|
andncc %g3, 1, %g3
|
|
bz 1f
|
|
andcc %g2, VALID, %g0 ! look for valid bit
|
|
b sr_stack_not_res ! stack page can never be resident
|
|
mov %fp, %g1
|
|
1:
|
|
#else
|
|
lda [%g1]ASI_PM, %g2 ! get pme for this address
|
|
srl %g2, PG_S_BIT, %g2 ! get vws bits
|
|
andcc %g2, VALID, %g0 ! look for valid bit
|
|
#endif
|
|
|
|
bnz,a sr_sp_bot
|
|
lda [%fp]ASI_PM, %g2 ! delay slot, check bottom of save area
|
|
b,a sr_stack_not_res ! stack page not resident
|
|
|
|
sr_sp_bot:
|
|
srl %g2, PG_S_BIT, %g2 ! get vws bits
|
|
andcc %g2, VALID, %g0 ! look for valid bit
|
|
bnz,a sr_stack_res
|
|
restore ! get into window to be restored
|
|
|
|
mov %fp, %g1 ! save fault address
|
|
sr_stack_not_res:
|
|
!
|
|
! Restore area on user stack is not resident.
|
|
! We punt and fake a page fault so that trap can bring the page in.
|
|
!
|
|
mov %l3, %wim ! restore old wim
|
|
mov %l0, %psr ! in case of changed priority (IU bug)
|
|
wr %l0, PSR_ET, %psr ! enable traps
|
|
mov T_DATA_FAULT, %o0
|
|
add %sp, MINFRAME, %o1
|
|
mov %g1, %o2
|
|
mov BE_INVALID, %o3 ! was stack protected or invalid?
|
|
btst P_INVAL, %g2
|
|
bnz,a 1f
|
|
mov BE_PROTERR, %o3
|
|
1:
|
|
call _trap ! trap(T_DATA_FAULT,
|
|
mov S_READ, %o4 ! rp, addr, be, S_READ)
|
|
|
|
b,a sys_rtt
|
|
|
|
sr_stack_res:
|
|
!
|
|
! Resident user window. Restore window from stack
|
|
!
|
|
RESTORE_WINDOW(%sp)
|
|
save ! get back to original window
|
|
|
|
sr_user_regs:
|
|
!
|
|
! User has at least one window in the register file.
|
|
!
|
|
sethi %hi(_uunix), %g5 ! XXX - global u register?
|
|
ld [%g5 + %lo(_uunix)], %g5
|
|
ld [%g5 + PCB_FLAGS], %l3
|
|
ld [%sp + MINFRAME + nPC*4], %l2 ! user npc
|
|
!
|
|
! check user pc alignment. This can get messed up either using
|
|
! ptrace, or by using the '-T' flag of ld to place the text
|
|
! section at a strange location (bug id #1015631)
|
|
!
|
|
andcc %l1, 0x3, %g0 ! pc must be word aligned
|
|
bz 1f
|
|
andcc %l2, 0x3, %g0 ! npc also
|
|
b,a sr_align_trap
|
|
1:
|
|
bz,a 2f
|
|
btst CLEAN_WINDOWS, %l3
|
|
b,a sr_align_trap
|
|
2:
|
|
bz,a 3f
|
|
mov %l0, %psr ! install old PSR_CC
|
|
|
|
!
|
|
! Maintain clean windows.
|
|
!
|
|
mov %wim, %g2 ! put wim in global
|
|
mov 0, %wim ! zero wim to allow saving
|
|
mov %l0, %g3 ! put original psr in global
|
|
b 2f ! test next window for invalid
|
|
save
|
|
!
|
|
! Loop through windows past the trap window
|
|
! clearing them until we hit the invlaid window.
|
|
!
|
|
1:
|
|
clr %l1 ! clear the window
|
|
clr %l2
|
|
clr %l3
|
|
clr %l4
|
|
clr %l5
|
|
clr %l6
|
|
clr %l7
|
|
clr %o0
|
|
clr %o1
|
|
clr %o2
|
|
clr %o3
|
|
clr %o4
|
|
clr %o5
|
|
clr %o6
|
|
clr %o7
|
|
save
|
|
2:
|
|
mov %psr, %g1 ! get CWP
|
|
srl %g2, %g1, %g1 ! test WIM bit
|
|
btst 1, %g1
|
|
bz,a 1b ! not invalid window yet
|
|
clr %l0 ! clear the window
|
|
|
|
!
|
|
! Clean up trap window.
|
|
!
|
|
mov %g3, %psr ! back to trap window, restore PSR_CC
|
|
mov %g2, %wim ! restore wim
|
|
nop; nop; ! psr delay
|
|
RESTORE_GLOBALS(%sp + MINFRAME) ! restore user globals
|
|
mov %l1, %o6 ! put pc, npc in unobtrusive place
|
|
mov %l2, %o7
|
|
clr %l0 ! clear the rest of the window
|
|
clr %l1
|
|
clr %l2
|
|
clr %l3
|
|
clr %l4
|
|
clr %l5
|
|
clr %l6
|
|
clr %l7
|
|
clr %o0
|
|
clr %o1
|
|
clr %o2
|
|
clr %o3
|
|
clr %o4
|
|
clr %o5
|
|
jmp %o6 ! return
|
|
rett %o7
|
|
3:
|
|
RESTORE_GLOBALS(%sp + MINFRAME) ! restore user globals
|
|
jmp %l1 ! return
|
|
rett %l2
|
|
.empty
|
|
|
|
sr_sup:
|
|
!
|
|
! Returning to supervisor.
|
|
! We will restore the trap psr. This has the effect of disabling
|
|
! traps and changing the CWP back to the original trap CWP. This
|
|
! completely restores the PSR so that if we get a trap between a
|
|
! rdpsr and a wrpsr its OK. We only do this for supervisor return
|
|
! since users can't manipulate the psr.
|
|
!
|
|
sethi %hi(_nwindows), %g5
|
|
ld [%g5 + %lo(_nwindows)], %g5 ! number of windows on this machine
|
|
ld [%sp + MINFRAME + SP*4], %fp ! get sys sp
|
|
xor %g1, %l0, %g1 ! test for CWP change
|
|
btst PSR_CWP, %g1
|
|
bz,a sr_samecwp
|
|
mov %l0, %psr ! install old psr, disable traps
|
|
!
|
|
! The CWP will be changed. We must save sp and the ins
|
|
! and recompute WIM. We know we need to restore the next
|
|
! window in this case.
|
|
!
|
|
mov %l0, %g3 ! save old psr
|
|
mov %sp, %g4 ! save sp, ins for new window
|
|
std %i0, [%sp +(8*4)] ! normal stack save area
|
|
std %i2, [%sp +(10*4)]
|
|
std %i4, [%sp +(12*4)]
|
|
std %i6, [%sp +(14*4)]
|
|
mov %g3, %psr ! old psr, disable traps, CWP, PSR_CC
|
|
mov 0x4, %g1 ! psr delay, compute mask for CWP + 2
|
|
sll %g1, %g3, %g1 ! psr delay, won't work for NW == 32
|
|
srl %g1, %g5, %g2 ! psr delay
|
|
or %g1, %g2, %g1
|
|
mov %g1, %wim ! install new wim
|
|
mov %g4, %sp ! reestablish sp
|
|
ldd [%sp + (8*4)], %i0 ! reestablish ins
|
|
ldd [%sp + (10*4)], %i2
|
|
ldd [%sp + (12*4)], %i4
|
|
ldd [%sp + (14*4)], %i6
|
|
restore ! restore return window
|
|
RESTORE_WINDOW(%sp)
|
|
b sr_out
|
|
save
|
|
|
|
sr_samecwp:
|
|
!
|
|
! There is no CWP change.
|
|
! We must make sure that there is a window to return to.
|
|
!
|
|
mov 0x2, %g1 ! compute mask for CWP + 1
|
|
sll %g1, %l0, %g1 ! XXX won't work for NW == 32
|
|
srl %g1, %g5, %g2 ! %g5 == NW, from above
|
|
or %g1, %g2, %g1
|
|
mov %wim, %g2 ! cmp with wim to check for underflow
|
|
btst %g1, %g2
|
|
bz sr_out
|
|
mov %l0, %psr ! install old PSR_CC
|
|
!
|
|
! No window to return to. Restore it.
|
|
!
|
|
sll %g2, 1, %g1 ! compute new WIM = rol(WIM, 1, NW)
|
|
dec %g5 ! %g5 == NW-1
|
|
srl %g2, %g5, %g2
|
|
or %g1, %g2, %g1
|
|
mov %g1, %wim ! install it
|
|
nop; nop; nop; ! wim delay
|
|
restore ! get into window to be restored
|
|
RESTORE_WINDOW(%sp)
|
|
save ! get back to original window
|
|
sr_out:
|
|
RESTORE_GLOBALS(%sp + MINFRAME) ! restore system globals
|
|
ld [%sp + MINFRAME + PC*4], %l1 ! delay slot, restore sys pc
|
|
ld [%sp + MINFRAME + nPC*4], %l2 ! sys npc
|
|
jmp %l1 ! return to trapped instruction
|
|
rett %l2
|
|
.empty
|
|
/* end sys_rtt*/
|
|
|
|
#ifdef SUNDBE
|
|
!
|
|
! syscall_debug in trap.c is a C language equivalent of the sparc code below
|
|
!
|
|
#ifdef SUNDBE_DEBUG
|
|
syscall:
|
|
wr %l0, PSR_ET, %psr ! enable traps (no priority change)
|
|
nop ! psr delay
|
|
call _syscall_debug ! syscall_debug(rp)
|
|
add %sp, MINFRAME, %o0 ! ptr to reg struct
|
|
b,a sys_rtt
|
|
#else /* SYSCALLOPTIM_DEBUG */
|
|
syscall:
|
|
wr %l0, PSR_ET, %psr ! enable traps (no priority change)
|
|
nop ! psr delay
|
|
|
|
! if (u.u_prof.pr_scale) {
|
|
! sec = u.u_ru.ru_stime.tv_sec; %l2
|
|
! usec = u.u_ru.ru_stime.tv_usec; %l3
|
|
! syst_flag = 1; %l4
|
|
! pid = u.u_procp->p_pid; %l5
|
|
! } else
|
|
! syst_flag = 0;
|
|
!
|
|
sethi %hi(_uunix), %l0
|
|
ld [%l0 + %lo(_uunix)], %l0
|
|
ld [%l0 + U_PROF_SCALE], %o0
|
|
tst %o0
|
|
bz,a sc_preamble
|
|
mov 0, %l4
|
|
ld [%l0 + U_RU_STIME_SEC], %l2
|
|
ld [%l0 + U_RU_STIME_USEC], %l3
|
|
ld [%l0 + U_PROCP], %o0
|
|
set 1, %l4
|
|
ldsh [%o0 + P_PID], %l5
|
|
|
|
sc_preamble:
|
|
!
|
|
! f = syscall_preamble(rp);
|
|
! if (f == NULL)
|
|
! goto sc_postamble;
|
|
!
|
|
call _syscall_preamble
|
|
add %sp, MINFRAME, %o0
|
|
tst %o0
|
|
bz sc_postamble
|
|
mov %o0, %l1
|
|
!
|
|
! if (setjmp(&u.u_qsave)) {
|
|
! if (u.u_error == 0 && u.u_eosys == NORMALRETURN)
|
|
! u.u_error = EINTR;
|
|
! } else {
|
|
! (*funcp)(u.u_ap);
|
|
! }
|
|
!
|
|
call _setjmp
|
|
add %l0, U_QSAVE, %o0
|
|
|
|
tst %o0
|
|
bz sc_dispatch
|
|
ldsb [%l0 + U_ERROR], %o0
|
|
|
|
tst %o0
|
|
bne sc_postamble
|
|
ldsb [%l0 + U_EOSYS], %o0
|
|
|
|
cmp %o0, NORMALRETURN
|
|
bne sc_postamble
|
|
set EINTR, %o0 ! XXX
|
|
|
|
b sc_postamble
|
|
stb %o0, [%l0 + U_ERROR]
|
|
|
|
sc_dispatch:
|
|
call %l1
|
|
ld [%l0 + U_AP], %o0
|
|
|
|
sc_postamble:
|
|
!
|
|
! syscall_postamble(rp);
|
|
!
|
|
call _syscall_postamble
|
|
add %sp, MINFRAME, %o0
|
|
!
|
|
! if (runrun) {
|
|
! (void) spl6();
|
|
! setrq(u.u_procp);
|
|
! u.u_ru.ru_nivcsw++;
|
|
! swtch();
|
|
! (void) spl0();
|
|
! }
|
|
!
|
|
sethi %hi(_runrun), %o0
|
|
ld [%o0 + %lo(_runrun)], %o0
|
|
tst %o0
|
|
be sc_userprof
|
|
nop
|
|
call _spl6
|
|
sethi %hi(_uunix), %l0
|
|
ld [%l0 + %lo(_uunix)], %l0
|
|
call _setrq
|
|
ld [%l0 + U_PROCP], %o0
|
|
ld [%l0 + U_RU_NIVCSW], %o0
|
|
inc %o0
|
|
call _swtch
|
|
st %o0, [%l0 + U_RU_NIVCSW]
|
|
call _spl0
|
|
nop
|
|
|
|
!
|
|
! curpri = u.u_procp->p_pri;
|
|
!
|
|
sethi %hi(_uunix), %l0
|
|
ld [%l0 + %lo(_uunix)], %l0
|
|
sethi %hi(_curpri), %o0
|
|
ld [%l0 + U_PROCP], %l0
|
|
ldsb [%l0 + P_PRI], %l0
|
|
stb %l0, [%o0 + %lo(_curpri)]
|
|
|
|
sc_userprof:
|
|
!
|
|
! if (syst_flag)
|
|
! syscall_userprof(rp, pid, syst.tv_sec, syst.tv_usec);
|
|
!
|
|
tst %l4
|
|
bz sys_rtt
|
|
mov %l5, %o1
|
|
mov %l2, %o2
|
|
mov %l3, %o3
|
|
call _syscall_userprof
|
|
add %sp, MINFRAME, %o0
|
|
b,a sys_rtt
|
|
/* end syscall */
|
|
#endif /* SYSCALLOPTIM_DEBUG */
|
|
|
|
#else /* SYSCALLOPTIM */
|
|
|
|
/*
|
|
* System call handler.
|
|
*/
|
|
syscall:
|
|
wr %l0, PSR_ET, %psr ! enable traps (no priority change)
|
|
nop ! psr delay
|
|
call _syscall ! syscall(rp)
|
|
add %sp, MINFRAME, %o0 ! ptr to reg struct
|
|
b,a sys_rtt
|
|
/* end syscall */
|
|
#endif /* SUNDBE */
|
|
|
|
#define ST_OP_SHIFT 21 /* opcode bit that determines ld vs st */
|
|
|
|
/*
|
|
* Fault handler.
|
|
*/
|
|
fault:
|
|
#ifdef notdef
|
|
/* enable this code when MEMERR_ADDR fix is in */
|
|
mov S_EXEC, %o4 ! assume execute fault
|
|
cmp %l4, T_TEXT_FAULT ! text fault?
|
|
be,a 2f
|
|
mov %l1, %o2 ! pc is text fault address
|
|
#endif notdef
|
|
set MEMERR_ADDR, %g1
|
|
ld [%g1 + ME_VADDR], %o2 ! get data fault address in mem err reg
|
|
ld [%g1], %g2 ! is this a memory error or a fault?
|
|
mov %g2, %o5 ! get cntl reg, for parity stuff
|
|
btst ER_INTR, %g2
|
|
bz,a 1f ! memory error
|
|
clr [%g1 + ME_VADDR] ! clear fault address
|
|
|
|
!
|
|
! Memory error. Enable traps to let interrupt happen.
|
|
!
|
|
wr %l0, PSR_ET, %psr
|
|
nop ! psr delay
|
|
b,a sys_rtt ! return from trap
|
|
|
|
1:
|
|
/* XXX remove from here ... */
|
|
mov S_EXEC, %o4 ! assume execute fault
|
|
cmp %l4, T_TEXT_FAULT ! text fault?
|
|
be,a 2f
|
|
mov %l1, %o2 ! pc is text fault address
|
|
/* ... to here when MEMERR_ADDR fix is in */
|
|
mov S_READ, %o4 ! assume read fault
|
|
ld [%l1], %g1 ! get instruction (must be ld* or st*)
|
|
srl %g1, ST_OP_SHIFT, %g1 ! load or store?
|
|
btst 1, %g1
|
|
bnz,a 2f
|
|
mov S_WRITE, %o4 ! store, write fault
|
|
2:
|
|
set BUS_ERROR_REG, %g2 ! get bus error reg before trap enable
|
|
lduba [%g2]ASI_CTL, %o3
|
|
wr %l0, PSR_ET, %psr ! enable traps (no priority change)
|
|
|
|
cmp %o3, BE_INVALID
|
|
bne 3f
|
|
mov %l4, %o0
|
|
|
|
mov %o2, %l2 ! save %o2 - %o4 in locals
|
|
mov %o3, %l3
|
|
mov %o4, %l5
|
|
|
|
call _hat_fault ! hat_fault(addr)
|
|
mov %o2, %o0
|
|
tst %o0
|
|
be sys_rtt ! hat layer resolved the fault
|
|
|
|
!
|
|
! hat_fault didn't resolve the fault.
|
|
! Restore saved %o2 - %o4 and call trap.
|
|
!
|
|
mov %l2, %o2
|
|
mov %l3, %o3
|
|
mov %l5, %o4
|
|
|
|
mov %l4, %o0
|
|
3:
|
|
!
|
|
! Call C trap handler
|
|
!
|
|
call _trap ! trap(t, rp, addr, be, rw)
|
|
add %sp, MINFRAME, %o1
|
|
b,a sys_rtt ! return from trap
|
|
/* end fault */
|
|
|
|
/*
|
|
* Interrupt vector table
|
|
*/
|
|
.seg "data"
|
|
.align 4
|
|
!
|
|
! all interrupts are vectored via the following table
|
|
! we can't vector directly from the scb because
|
|
! we haven't done window setup
|
|
!
|
|
.global _int_vector
|
|
_int_vector:
|
|
.word _spurious ! level 0, should not happen
|
|
.word level1 ! level 1, softint, interrupt enable register 1
|
|
.word (1<<1) ! level 2, vme level 1
|
|
.word (2<<1) ! level 3, vme level 2
|
|
.word level4 ! level 4, SCSI or IE register 2
|
|
.word (3<<1) ! level 5, vme level 3
|
|
.word level6 ! level 6, ethernet or IE register 3
|
|
.word (4<<1) ! level 7, vme level 4
|
|
.word level8 ! level 8, video retrace
|
|
.word (5<<1) ! level 9, vme level 5
|
|
.word level10 ! level 10, normal clock
|
|
.word (6<<1) ! level 11, vme level 6
|
|
#ifdef SAS
|
|
.word _simcintr ! sas console interrupt
|
|
#else
|
|
.word _zslevel12 ! level 12, scc - serial i/o
|
|
#endif SAS
|
|
.word (7<<1) ! level 13, vme level 7
|
|
.word _spurious ! kprof (not done here) / monitor clock
|
|
.word memory_err ! level 15, memory error
|
|
.seg "text"
|
|
|
|
/*
|
|
* Generic interrupt handler.
|
|
*/
|
|
.global interrupt
|
|
interrupt:
|
|
set eintstack, %g1 ! on interrupt stack?
|
|
cmp %sp, %g1
|
|
bgu,a 1f
|
|
sub %g1, SA(MINFRAME), %sp ! get on it.
|
|
1:
|
|
andn %l0, PSR_PIL, %l5 ! compute new psr with proper PIL
|
|
and %l4, T_INT_LEVEL, %l4
|
|
sll %l4, PSR_PIL_BIT, %g1
|
|
or %l5, %g1, %l0
|
|
!
|
|
! If we just took a memory error we don't want to turn interrupts
|
|
! on just yet in case there is another memory error waiting in the
|
|
! wings. So disable interrupts if the PIL is 15.
|
|
!
|
|
cmp %g1, PSR_PIL
|
|
bne 2f
|
|
sll %l4, 2, %l6 ! convert level to word offset
|
|
mov IR_ENA_INT, %o0
|
|
call _set_intreg
|
|
mov 0, %o1
|
|
2:
|
|
!
|
|
! Get handler address for level.
|
|
!
|
|
set _int_vector, %g1
|
|
ld [%g1 + %l6], %l3 ! grab vector
|
|
!
|
|
! no code can exist at a low address (< 8192)
|
|
! so we use this to weed out vme generated interrupts
|
|
! the vector will be the vme level that we should
|
|
! acknowledge for the current interrupt if it is a vme
|
|
! interrupt otherwise we get an interrupt routine address
|
|
!
|
|
cmp %l3, 16 ! is this a vme generated interrupt?
|
|
blu,a vme_interrupt
|
|
mov (14<<PSR_PIL_BIT), %g1
|
|
!
|
|
! On board interrupt.
|
|
! Due to a bug in the IU, we cannot increase the PIL and
|
|
! enable traps at the same time. In effect, ET changes
|
|
! slightly before the new PIL becomes effective.
|
|
! So we write the psr twice, once to set the PIL and
|
|
! once to set ET.
|
|
!
|
|
mov %l0, %psr ! set level (IU bug)
|
|
wr %l0, PSR_ET, %psr ! enable traps
|
|
nop
|
|
call %l3 ! non-vme interrupt
|
|
nop
|
|
|
|
int_rtt:
|
|
sethi %hi(_cnt+V_INTR), %g2 ! cnt.v_intr++
|
|
ld [%g2 + %lo(_cnt+V_INTR)], %g1
|
|
inc %g1
|
|
st %g1, [%g2 + %lo(_cnt+V_INTR)]
|
|
b sys_rtt ! restore previous stack pointer
|
|
mov %l7, %sp ! reset stack pointer
|
|
/* end interrupt */
|
|
|
|
/*
|
|
* VME interrupt. Do vectoring.
|
|
* The VME vector is read off the VME bus. If this fails (data fault),
|
|
* _trap will check the PC of the fault and jump to _spurious.
|
|
*/
|
|
.global vme_interrupt, _vme_read_vector
|
|
vme_interrupt:
|
|
or %l5, %g1, %l5 ! spl 14
|
|
mov %l5, %psr ! set level (IU bug)
|
|
wr %l5, PSR_ET, %psr ! enable traps
|
|
mov -1, %l5 ! bad vector value for _spurious
|
|
set VME_INT_VEC, %g1 ! get VME vector from the bus
|
|
or %g1, %l3, %g1 ! create address to read in alt space
|
|
or %g1, 1, %g1 ! a0 should always be a 1
|
|
_vme_read_vector:
|
|
lduba [%g1]ASI_CTL, %l5 ! read vector number, acknowledge int.
|
|
wr %l0, PSR_ET, %psr ! set proper interrupt level
|
|
cmp %l5, VEC_MAX ! check vector limits
|
|
bg _spurious
|
|
subcc %l5, VEC_MIN, %g3 ! normalize vector
|
|
bl _spurious
|
|
sll %g3, 2, %g5 ! scale for interrupt counting
|
|
sll %g3, 3, %g3 ! scale vector
|
|
set _intrcnt, %g4 ! per-device interrupt counts table
|
|
ld [%g5 + %g4], %g1 ! interrupt count
|
|
set _vme_vector, %g2 ! table of interrupt vectors
|
|
inc %g1 ! count interrupt
|
|
st %g1, [%g5 + %g4] ! and store result
|
|
ld [%g2 + %g3], %g1 ! get handler address
|
|
add %g2, 4, %g2 ! generate address of arg ptr
|
|
ld [%g2 + %g3], %o0 ! delay slot, get arg ptr
|
|
call %g1 ! call handler
|
|
ld [%o0], %o0 ! read arg ptr
|
|
|
|
b,a int_rtt ! restore previous stack pointer
|
|
/* end vme_interrupt */
|
|
|
|
/*
|
|
* Vme vectors are compatible with the sun3 family in which
|
|
* there were possible valid vectors from 64 to 255 inclusive.
|
|
* This requires 192 vectors, each vector is two words long
|
|
* the first word being the interrupt routine address and the
|
|
* second word is the arg.
|
|
*
|
|
* Vectors 0xC8-0xFF (200-255) are reserved for customer use.
|
|
*/
|
|
|
|
/*
|
|
* The vme vectoring uses the following table of routines
|
|
* and arguments. The values for the vectors in the following
|
|
* table are loaded by autoconf at boot time.
|
|
*/
|
|
#define ERRV .word _spurious, _zeros
|
|
|
|
.seg "data"
|
|
.align 4
|
|
|
|
.global _vme_vector
|
|
_vme_vector: ! vector numbers
|
|
ERRV; ERRV; ERRV; ERRV ! 0x40 - 0x43 sc0 | sc?
|
|
ERRV; ERRV; ERRV; ERRV ! 0x44 - 0x47 xdc0 | xdc1 | xdc2 | xdc3
|
|
ERRV; ERRV; ERRV; ERRV ! 0x48 - 0x4B xyc0 | xyc1 | xyc?
|
|
ERRV; ERRV; ERRV; ERRV ! 0x4C - 0x4F future disk controllers
|
|
ERRV; ERRV; ERRV; ERRV ! 0x50 - 0x53 future disk controllers
|
|
ERRV; ERRV; ERRV; ERRV ! 0x54 - 0x57 future disk controllers
|
|
ERRV; ERRV; ERRV; ERRV ! 0x58 - 0x5B future disk controllers
|
|
ERRV; ERRV; ERRV; ERRV ! 0x5C - 0x5F future disk controllers
|
|
ERRV; ERRV; ERRV; ERRV ! 0x60 - 0x63 tm0 | tm1 | tm?
|
|
ERRV; ERRV; ERRV; ERRV ! 0x64 - 0x67 xtc0 | xtc1 | xtc?
|
|
ERRV; ERRV; ERRV; ERRV ! 0x68 - 0x6B future tape controllers
|
|
ERRV; ERRV; ERRV; ERRV ! 0x6C - 0x6F future tape controllers
|
|
ERRV; ERRV; ERRV; ERRV ! 0x70 - 0x73 ec?
|
|
ERRV; ERRV; ERRV; ERRV ! 0x74 - 0x77 ie0 | ie1 | ie2 | ie3
|
|
ERRV; ERRV; ERRV; ERRV ! 0x78 - 0x7B fddi0 | fddi1 | fddi2 | fddi3
|
|
ERRV; ERRV; ERRV; ERRV ! 0x7C - 0x7F ie4 | future ethernet devices
|
|
ERRV; ERRV; ERRV; ERRV ! 0x80 - 0x83 vpc0 | vpc1 | vpc?
|
|
ERRV; ERRV; ERRV; ERRV ! 0x84 - 0x87 vp?
|
|
ERRV; ERRV; ERRV; ERRV ! 0x88 - 0x8B mti0 | mti1 | mti2 | mti3
|
|
ERRV; ERRV; ERRV; ERRV ! 0x8C - 0x8F SunLink SCP (Systech DCP-8804)
|
|
ERRV; ERRV; ERRV; ERRV ! 0x90 - 0x93 Sun-3 zs0 (8 even vectors)
|
|
ERRV; ERRV; ERRV; ERRV ! 0x94 - 0x97 Sun-3 zs1 (8 odd vectors)
|
|
ERRV; ERRV; ERRV; ERRV ! 0x98 - 0x9B Sun-3 zs0 (8 even vectors)
|
|
ERRV; ERRV; ERRV; ERRV ! 0x9C - 0x9F Sun-3 zs1 (8 odd vectors)
|
|
ERRV; ERRV; ERRV; ERRV ! 0xA0 - 0xA3 future serial
|
|
ERRV; ERRV; ERRV; ERRV ! 0xA4 - 0xA7 pc0 | pc1 | pc2 | pc3
|
|
ERRV; ERRV; ERRV; ERRV ! 0xA8 - 0xAB cg2 | future frame buffers
|
|
ERRV; ERRV; ERRV; ERRV ! 0xAC - 0xAF gp1 | future graphics processors
|
|
ERRV; ERRV; ERRV; ERRV ! 0xB0 - 0xB3 sky0 | ?
|
|
ERRV; ERRV; ERRV; ERRV ! 0xB4 - 0xB7 SunLink / channel attach
|
|
ERRV; ERRV; ERRV; ERRV ! 0xB8 - 0xBB (token bus) tbi0 | tbi1 | ?
|
|
ERRV; ERRV; ERRV; ERRV ! 0xBC - 0xBF Reserved for Sun
|
|
ERRV; ERRV; ERRV; ERRV ! 0xC0 - 0xC3 Reserved for Sun
|
|
ERRV; ERRV; ERRV; ERRV ! 0xC4 - 0xC7 Reserved for Sun
|
|
ERRV; ERRV; ERRV; ERRV ! 0xC8 - 0xCB Reserved for User
|
|
ERRV; ERRV; ERRV; ERRV ! 0xCC - 0xCF Reserved for User
|
|
ERRV; ERRV; ERRV; ERRV ! 0xD0 - 0xD3 Reserved for User
|
|
ERRV; ERRV; ERRV; ERRV ! 0xD4 - 0xD7 Reserved for User
|
|
ERRV; ERRV; ERRV; ERRV ! 0xD8 - 0xDB Reserved for User
|
|
ERRV; ERRV; ERRV; ERRV ! 0xDC - 0xDF Reserved for User
|
|
ERRV; ERRV; ERRV; ERRV ! 0xE0 - 0xE3 Reserved for User
|
|
ERRV; ERRV; ERRV; ERRV ! 0xE4 - 0xE7 Reserved for User
|
|
ERRV; ERRV; ERRV; ERRV ! 0xE8 - 0xEB Reserved for User
|
|
ERRV; ERRV; ERRV; ERRV ! 0xEC - 0xEF Reserved for User
|
|
ERRV; ERRV; ERRV; ERRV ! 0xF0 - 0xF3 Reserved for User
|
|
ERRV; ERRV; ERRV; ERRV ! 0xF4 - 0xF7 Reserved for User
|
|
ERRV; ERRV; ERRV; ERRV ! 0xF8 - 0xFB Reserved for User
|
|
ERRV; ERRV; ERRV; ERRV ! 0xFC - 0xFF Reserved for User
|
|
|
|
/*
|
|
* names of vectored interrupt devices -- for vmstat.
|
|
*/
|
|
|
|
.seg "data"
|
|
.globl _intrnames
|
|
.globl _eintrnames
|
|
_intrnames:
|
|
.asciz "sc0", "sc1", "sc2", "sc3"
|
|
.asciz "xdc0", "xdc1", "xdc2", "xdc3"
|
|
.asciz "xyc0", "xyc1", "xyc2", "xyc3"
|
|
.asciz "?disk0", "?disk1", "?disk2", "?disk3"
|
|
.asciz "?disk0", "?disk1", "?disk2", "?disk3"
|
|
.asciz "?disk0", "?disk1", "?disk2", "?disk3"
|
|
.asciz "?disk0", "?disk1", "?disk2", "?disk3"
|
|
.asciz "?disk0", "?disk1", "?disk2", "?disk3"
|
|
.asciz "tm0", "tm1", "tm2", "tm3"
|
|
.asciz "xtc0", "xtc1", "xtc2", "xtc3"
|
|
.asciz "?tape0", "?tape1", "?tape2", "?tape3"
|
|
.asciz "?tape0", "?tape1", "?tape2", "?tape3"
|
|
.asciz "ec0", "ec1", "ec2", "ec3"
|
|
.asciz "ie0", "ie1", "ie2", "ie3"
|
|
.asciz "fddi0", "fddi1", "fddi2", "fddi3"
|
|
.asciz "ie4", "?ether1", "?ether2", "?ether3"
|
|
.asciz "vpc0", "vpc1", "vpc2", "vpc3"
|
|
.asciz "vp0", "vp1", "vp2", "vp3"
|
|
.asciz "mti0", "mti1", "mti2", "mti3"
|
|
.asciz "SCP", "SCP", "SCP", "SCP"
|
|
.asciz "zs0", "zs0", "zs0", "zs0"
|
|
.asciz "zs1", "zs1", "zs1", "zs1"
|
|
.asciz "zs0", "zs0", "zs0", "zs0"
|
|
.asciz "zs1", "zs1", "zs1", "zs1"
|
|
.asciz "?serial0", "?serial1", "?serial2", "?serial3"
|
|
.asciz "pc0", "pc1", "pc2", "pc3"
|
|
.asciz "cgtwo0", "?frbuf", "?frbuf", "?frbuf"
|
|
.asciz "gpone0", "?gp", "?gp", "?gp"
|
|
.asciz "sky0", "???", "???", "???"
|
|
.asciz "slchan", "slchan", "slchan", "slchan"
|
|
.asciz "tbi0", "tbi1", "tbi2", "tbi3"
|
|
.asciz "?Sun", "?Sun", "?Sun", "?Sun"
|
|
.asciz "?Sun", "?Sun", "?Sun", "?Sun"
|
|
.asciz "?Sun", "?Sun", "?Sun", "?Sun"
|
|
.asciz "?User", "?User", "?User", "?User"
|
|
.asciz "?User", "?User", "?User", "?User"
|
|
.asciz "?User", "?User", "?User", "?User"
|
|
.asciz "?User", "?User", "?User", "?User"
|
|
.asciz "?User", "?User", "?User", "?User"
|
|
.asciz "?User", "?User", "?User", "?User"
|
|
.asciz "?User", "?User", "?User", "?User"
|
|
.asciz "?User", "?User", "?User", "?User"
|
|
.asciz "?User", "?User", "?User", "?User"
|
|
.asciz "?User", "?User", "?User", "?User"
|
|
.asciz "?User", "?User", "?User", "?User"
|
|
.asciz "?User", "?User", "?User", "?User"
|
|
.asciz "?User", "?User", "?User", "?User"
|
|
.asciz "?User", "?User", "?User", "?User"
|
|
_eintrnames:
|
|
|
|
/*
|
|
* Places (initialized to 0) to count vectored interrupts in.
|
|
* Used by vmstat.
|
|
*/
|
|
.seg "bss"
|
|
.align 4
|
|
.globl _intrcnt
|
|
.globl _eintrcnt
|
|
_intrcnt:
|
|
.skip 4 * 192
|
|
_eintrcnt:
|
|
|
|
.seg "text"
|
|
.align 4
|
|
/*
|
|
* Spurious trap... 'should not happen'
|
|
* %l4 - processor interrupt level
|
|
* %l3 - vme interrupt level << 1 (or interrupt handler address)
|
|
* %l5 - vme vector
|
|
*/
|
|
.seg "data"
|
|
.global _ie1_intr
|
|
_ie1_intr: .word 1
|
|
.text
|
|
.global _spurious
|
|
_spurious:
|
|
cmp %l3, 16 ! is this a vme generated interrupt?
|
|
blu spurious_vme
|
|
nop
|
|
|
|
!
|
|
! Spurious on-board interrupt.
|
|
!
|
|
set 1f, %o0
|
|
call _printf
|
|
mov %l4, %o1
|
|
b,a int_rtt
|
|
.seg "data"
|
|
1: .asciz "spurious interrupt at processor level %d\n"
|
|
.seg "text"
|
|
|
|
!
|
|
! Spurious VME interrupt.
|
|
!
|
|
spurious_vme:
|
|
set 2f, %o0
|
|
mov %l4, %o1
|
|
srl %l3, 1, %o2
|
|
set _ie1_intr, %o3
|
|
ld [%o3], %o3
|
|
cmp %o3, 0
|
|
be 1f
|
|
cmp %o2, 3
|
|
be int_rtt
|
|
nop
|
|
1:
|
|
call _printf
|
|
mov %l5, %o3
|
|
b,a int_rtt
|
|
.seg "data"
|
|
2: .asciz "spurious VME interrupt at processor level %d\nVME level %d, VME vector 0x%x\n"
|
|
.seg "text"
|
|
/* end spurious */
|
|
|
|
/*
|
|
* Macro for autovectored interrupts.
|
|
*/
|
|
#define IOINTR(LEVEL) \
|
|
set _level/**/LEVEL/**/_vector,%l5 /* get vector ptr */;\
|
|
clr %l4 /* clear offset */;\
|
|
1: ld [%l5 + %l4], %g1/* get routine address */;\
|
|
call %g1 /* go there */;\
|
|
nop ;\
|
|
tst %o0 /* success? */;\
|
|
bz,a 1b /* no, try next one */;\
|
|
add %l4, 4, %l4 /* delay slot, next one to try */;\
|
|
set _level/**/LEVEL/**/_intcnt, %l5 /* get interrupt counter */;\
|
|
ld [%l5 + %l4], %g1/* increment proper counter */;\
|
|
inc %g1 ;\
|
|
bneg int_rtt /* was interrupt spurious? */;\
|
|
st %g1, [%l5 + %l4];\
|
|
/* non-spurious interrupt, clear count */;\
|
|
sethi %hi(_level/**/LEVEL/**/_spurious), %g1;\
|
|
b int_rtt /* done */;\
|
|
clr [%g1 + %lo(_level/**/LEVEL/**/_spurious)]
|
|
|
|
/*
|
|
* Handle software interrupts
|
|
* Just call C routine softint - executes all accumulated softcalls
|
|
*/
|
|
.global level1
|
|
level1:
|
|
mov %psr, %g2
|
|
or %g2, PSR_PIL, %g1 ! spl hi to protect intreg update
|
|
mov %g1, %psr
|
|
nop; nop; ! psr delay
|
|
set INTREG_ADDR, %l1 ! interrupt register address
|
|
ldub [%l1], %g1 ! get current setting
|
|
bclr IR_SOFT_INT1, %g1 ! reset level one int request bit
|
|
stb %g1, [%l1] ! turn off the level one interrupt
|
|
mov %g2, %psr ! splx
|
|
nop ! psr delay
|
|
call _softint ! go do accumulated softcalls
|
|
nop
|
|
|
|
b,a int_rtt
|
|
/* end level1 */
|
|
|
|
/*
|
|
* Level 4 interrupts, normally used by SCSI or interrupt register 2
|
|
*/
|
|
level4:
|
|
IOINTR(2)
|
|
|
|
/*
|
|
* Level 6 interrupts, normally used by Ethernet or interrupt register 3
|
|
*/
|
|
level6:
|
|
IOINTR(3)
|
|
|
|
/*
|
|
* Level 8 interrupts, normally used by video retrace
|
|
*/
|
|
level8:
|
|
IOINTR(4)
|
|
|
|
/*
|
|
* LED data for idle pattern of diagnostic register.
|
|
* NOTE: pattern is sampled at 100hz.
|
|
*/
|
|
LEDTICKS = 33
|
|
LEDPATCNT = 18
|
|
|
|
.seg "data"
|
|
led:
|
|
! LEDPAT
|
|
.byte 0x7e, 0x7e
|
|
.byte 0x7e, 0xbd
|
|
.byte 0xbd, 0xbd
|
|
.byte 0xdb, 0xdb
|
|
.byte 0xdb, 0xe7
|
|
.byte 0xe7, 0xe7
|
|
.byte 0xdb, 0xdb
|
|
.byte 0xdb, 0xbd
|
|
.byte 0xbd, 0xbd
|
|
! end of LEDPAT
|
|
.byte 0 ! LEDCNT current count of ticks
|
|
.byte 0 ! LEDPTR offset in pattern
|
|
|
|
LEDPAT = 0
|
|
LEDCNT = 18
|
|
LEDPTR = 19
|
|
|
|
.seg "text"
|
|
/*
|
|
* This code assumes that the real time clock interrupts 100 times
|
|
* per second, for SUN4 we call hardclock at that rate.
|
|
*
|
|
* If idle, update the LEDs with new values before calling hardclock so
|
|
* at least the user can tell that something is still running.
|
|
*/
|
|
.seg "bss"
|
|
.align 4
|
|
.globl _clk_intr
|
|
_clk_intr:
|
|
.skip 4
|
|
.seg "text"
|
|
|
|
.global level10
|
|
level10:
|
|
mov %psr, %g3
|
|
or %g3, PSR_PIL, %g1 ! spl hi to protect intreg update
|
|
mov %g1, %psr
|
|
nop; nop; ! psr delay
|
|
|
|
sethi %hi(_clock_type), %l5 ! which clock_type?
|
|
ld [%l5 + %lo(_clock_type)], %g1
|
|
cmp %g1, INTERSIL7170
|
|
bnz 5f ! if intersil then
|
|
nop ! (delay slot)
|
|
#ifndef SAS
|
|
set CLOCK0_ADDR+CLK_INTRREG, %l5 ! read CLOCK_ADDR->CLK_INTRRER
|
|
ldub [%l5], %g1 ! to clear clock chip
|
|
#endif !SAS
|
|
set INTREG_ADDR, %g1
|
|
ldub [%g1], %g2 ! read interrupt register
|
|
bclr IR_ENA_CLK10, %g2
|
|
stb %g2, [%g1] ! reset interrupt
|
|
bset IR_ENA_CLK10, %g2
|
|
stb %g2, [%g1] ! re-enable interrupt
|
|
#ifndef SAS
|
|
!
|
|
! Clear interrupt register again. If we lose
|
|
! an interrupt we will resync later anyway.
|
|
!
|
|
ldub [%l5], %g1
|
|
#endif !SAS
|
|
ba,a 6f
|
|
5: ! else if mostek then
|
|
#ifndef SAS
|
|
set COUNTER_ADDR+LIM10, %l5 ! read limit10 reg
|
|
ld [%l5], %g1 ! to clear interrupt
|
|
#endif !SAS
|
|
6:
|
|
mov %g3, %psr ! restore psr
|
|
|
|
!
|
|
! Check if executing in the idle loop.
|
|
! _swtch immediately follows _idle in swtch.s.
|
|
!
|
|
#ifndef SAS
|
|
set led, %l5 ! countdown to next update of LEDs
|
|
set _swtch, %g1 ! end of idle loop
|
|
cmp %l1, %g1 ! if pc >= swtch not in idle loop
|
|
bgeu 4f ! no, don't update LEDs
|
|
.empty ! next instruction ok in delay slot
|
|
|
|
set _idle, %g1 ! address of idle loop
|
|
cmp %l1, %g1 ! if pc < idle not in idle loop
|
|
bgeu 1f ! yes, update LEDs
|
|
nop
|
|
4:
|
|
ldub [%l5 + LEDCNT], %g1
|
|
subcc %g1, 1, %g1
|
|
bge,a 3f ! not zero, just call hardclock
|
|
stb %g1, [%l5 + LEDCNT] ! delay, write out new ledcnt
|
|
1:
|
|
mov LEDTICKS, %g1
|
|
stb %g1, [%l5 + LEDCNT]
|
|
|
|
ldub [%l5 + LEDPTR], %g1
|
|
ldub [%l5 + %g1], %g2 ! get LED pattern
|
|
subcc %g1, 1, %g1 ! point to next one
|
|
bneg,a 2f
|
|
mov LEDPATCNT-1, %g1
|
|
2:
|
|
stb %g1, [%l5 + LEDPTR] ! update pattern pointer
|
|
set DIAGREG, %g1
|
|
stba %g2, [%g1]ASI_CTL ! write led pattern to LEDs
|
|
#endif !SAS
|
|
3:
|
|
sethi %hi(_clk_intr), %g2 ! count clock interrupt
|
|
ld [%lo(_clk_intr) + %g2], %g3
|
|
inc %g3
|
|
st %g3, [%lo(_clk_intr) + %g2]
|
|
|
|
ld [%l7 + MINFRAME + PC*4], %o0 ! pc
|
|
call _hardclock
|
|
ld [%l7 + MINFRAME + PSR*4], %o1 ! psr
|
|
|
|
b,a int_rtt ! return to restore previous stack
|
|
/* end level10 */
|
|
|
|
/*
|
|
* Level 14 interrupts can be caused by the clock when
|
|
* kernel profiling is enabled. It is handled immediately
|
|
* in the trap window.
|
|
*/
|
|
#ifdef GPROF
|
|
.global test_prof
|
|
test_prof:
|
|
sethi %hi(_clock_type), %l3 ! which clock type?
|
|
ld [%l3 + %lo(_clock_type)], %l4
|
|
cmp %l4, INTERSIL7170
|
|
bnz 1f ! if intersil then
|
|
nop ! (delay slot)
|
|
sethi %hi(CLOCK0_ADDR+CLK_INTRREG), %l3 ! read clk intrreg
|
|
ldub [%l3 + %lo(CLOCK0_ADDR+CLK_INTRREG)], %l3 ! to clear clock chip
|
|
sethi %hi(INTREG_ADDR), %l3
|
|
ldub [%l3 + %lo(INTREG_ADDR)], %l4 ! reset interrupt reg bit
|
|
bclr IR_ENA_CLK14, %l4
|
|
stb %l4, [%l3 + %lo(INTREG_ADDR)]
|
|
bset IR_ENA_CLK14, %l4 ! reenable interrupt
|
|
stb %l4, [%l3 + %lo(INTREG_ADDR)]
|
|
ba,a 2f
|
|
1:
|
|
sethi %hi(COUNTER_ADDR+LIM14), %l3 ! read limit14 reg
|
|
ld [%l3 + %lo(COUNTER_ADDR+LIM14)], %l3 ! to clear intr
|
|
2:
|
|
sethi %hi(_mon_clock_on), %l3 ! see if profiling is enabled
|
|
ldub [%l3 + %lo(_mon_clock_on)], %l3
|
|
tst %l3
|
|
bz kprof ! profiling on, do it.
|
|
nop
|
|
b sys_trap ! do normal interrupt processing
|
|
mov (T_INTERRUPT | 14), %l4
|
|
#endif GPROF
|
|
|
|
/*
|
|
* Level 15 interrupts can only be caused by parity/ECC errors.
|
|
* This is fatal if the error is an incorrectable ECC or parity.
|
|
* If the error is correctable ECC, memerr will eventually return
|
|
* after logging some diagnostic information.
|
|
*/
|
|
.global memory_err
|
|
memory_err:
|
|
set MEMERR_ADDR, %g1 ! get address of mem err reg
|
|
ld [%g1], %g2 ! read it
|
|
btst ER_INTR, %g2 ! valid memory interrupt pending?
|
|
bz 1f
|
|
nop
|
|
call _memerr ! sometimes returns
|
|
nop
|
|
b,a 3f
|
|
1:
|
|
sethi %hi(2f), %o0 ! print stray interrupt message
|
|
call _printf ! print a message to the console
|
|
or %o0, %lo(2f), %o0
|
|
3:
|
|
mov IR_ENA_INT, %o0 ! reenable interrupts
|
|
call _set_intreg
|
|
mov 1, %o1
|
|
b,a int_rtt
|
|
|
|
.seg "data"
|
|
2: .asciz "stray level 15 interrupt\n"
|
|
.seg "text"
|
|
/* end level15 */
|
|
|
|
/*
|
|
* Flush all windows to memory, except for the one we entered in.
|
|
* We do this by doing NWINDOW-2 saves then the same number of restores.
|
|
* This leaves the WIM immediately before window entered in.
|
|
* This is used for context switching.
|
|
*/
|
|
ENTRY(flush_windows)
|
|
save %sp, -WINDOWSIZE, %sp
|
|
save %sp, -WINDOWSIZE, %sp
|
|
save %sp, -WINDOWSIZE, %sp
|
|
save %sp, -WINDOWSIZE, %sp
|
|
save %sp, -WINDOWSIZE, %sp
|
|
|
|
#if defined(SUN4_470) || defined(SUN4_330)
|
|
.global _fixnwindows
|
|
_fixnwindows:
|
|
save %sp, -WINDOWSIZE, %sp ! could be no-ops if machine
|
|
restore ! has only 7 register windows
|
|
#endif SUN4_470 || SUN4_330
|
|
|
|
restore
|
|
restore
|
|
restore
|
|
restore
|
|
ret
|
|
restore
|
|
|
|
/*
|
|
* flush user windows to memory.
|
|
*/
|
|
ENTRY(flush_user_windows)
|
|
sethi %hi(_uunix), %g5 ! XXX - global u register?
|
|
ld [%g5 + %lo(_uunix)], %g5
|
|
ld [%g5 + PCB_UWM], %g1 ! get user window mask
|
|
tst %g1 ! do save until mask is zero
|
|
bz 3f
|
|
clr %g2
|
|
1:
|
|
save %sp, -WINDOWSIZE, %sp
|
|
sethi %hi(_uunix), %g5 ! XXX - global u register?
|
|
ld [%g5 + %lo(_uunix)], %g5
|
|
ld [%g5 + PCB_UWM], %g1 ! get user window mask
|
|
tst %g1 ! do save until mask is zero
|
|
bnz 1b
|
|
add %g2, 1, %g2
|
|
2:
|
|
subcc %g2, 1, %g2 ! restore back to orig window
|
|
bnz 2b
|
|
restore
|
|
3:
|
|
retl
|
|
.empty ! next instruction ok in delya slot
|
|
|
|
/*
|
|
* Throw out any user windows in the register file.
|
|
* Used by setregs (exec) to clean out old user.
|
|
* Used by sigcleanup to remove extraneous windows when returning from a
|
|
* signal.
|
|
*/
|
|
ENTRY(trash_user_windows)
|
|
sethi %hi(_uunix), %g5 ! XXX - global u register?
|
|
ld [%g5 + %lo(_uunix)], %g5
|
|
ld [%g5 + PCB_UWM], %g1 ! get user window mask
|
|
tst %g1
|
|
bz 3f ! user windows?
|
|
nop
|
|
!
|
|
! There are old user windows in the register file. We disable traps
|
|
! and increment the WIM so that we don't overflow on these windows.
|
|
! Also, this sets up a nice underflow when first returning to the
|
|
! new user.
|
|
!
|
|
mov %psr, %g4
|
|
or %g4, PSR_PIL, %g1 ! spl hi to prevent interrupts
|
|
mov %g1, %psr
|
|
nop; nop; nop ! psr delay
|
|
ld [%g5 + PCB_UWM], %g1 ! get user window mask
|
|
clr [%g5 + PCB_UWM] ! throw user windows away
|
|
set _scb, %g5
|
|
b 2f
|
|
ld [%g5 + 31], %g5 ! %g5 == NW-1
|
|
|
|
1:
|
|
srl %g2, 1, %g3 ! next WIM = ror(WIM, 1, NW)
|
|
sll %g2, %g5, %g2 ! %g5 == NW-1
|
|
or %g2, %g3, %g2
|
|
mov %g2, %wim ! install wim
|
|
bclr %g2, %g1 ! clear bit from UWM
|
|
2:
|
|
tst %g1 ! more user windows?
|
|
bnz,a 1b
|
|
mov %wim, %g2 ! get wim
|
|
|
|
mov %g4, %psr ! enable traps
|
|
nop ! psr delay
|
|
3:
|
|
sethi %hi(_uunix), %g5 ! XXX - global u register?
|
|
ld [%g5 + %lo(_uunix)], %g5
|
|
retl
|
|
clr [%g5 + PCB_WBCNT] ! zero window buffer cnt
|
|
|
|
/*
|
|
* Clean out register file.
|
|
*/
|
|
clean_windows:
|
|
sethi %hi(_uunix), %l5 ! XXX - global u register?
|
|
ld [%l5 + %lo(_uunix)], %l5
|
|
ld [%l5 + PCB_FLAGS], %l4 ! set CLEAN_WINDOWS in pcb_flags
|
|
mov %wim, %l3
|
|
bset CLEAN_WINDOWS, %l4
|
|
st %l4, [%l5 + PCB_FLAGS]
|
|
srl %l3, %l0, %l3 ! test WIM bit
|
|
btst 1, %l3
|
|
bnz,a cw_out ! invalid window, just return
|
|
mov %l0, %psr ! restore PSR_CC
|
|
|
|
mov %g1, %l5 ! save some globals
|
|
mov %g2, %l6
|
|
mov %g3, %l7
|
|
mov %wim, %g2 ! put wim in global
|
|
mov 0, %wim ! zero wim to allow saving
|
|
mov %l0, %g3 ! put original psr in global
|
|
b 2f ! test next window for invalid
|
|
save
|
|
!
|
|
! Loop through windows past the trap window
|
|
! clearing them until we hit the invlaid window.
|
|
!
|
|
1:
|
|
clr %l1 ! clear the window
|
|
clr %l2
|
|
clr %l3
|
|
clr %l4
|
|
clr %l5
|
|
clr %l6
|
|
clr %l7
|
|
clr %o0
|
|
clr %o1
|
|
clr %o2
|
|
clr %o3
|
|
clr %o4
|
|
clr %o5
|
|
clr %o6
|
|
clr %o7
|
|
save
|
|
2:
|
|
mov %psr, %g1 ! get CWP
|
|
srl %g2, %g1, %g1 ! test WIM bit
|
|
btst 1, %g1
|
|
bz,a 1b ! not invalid window yet
|
|
clr %l0 ! clear the window
|
|
|
|
!
|
|
! Clean up trap window.
|
|
!
|
|
mov %g3, %psr ! back to trap window, restore PSR_CC
|
|
mov %g2, %wim ! restore wim
|
|
nop; nop; ! psr delay
|
|
mov %l5, %g1 ! restore globals
|
|
mov %l6, %g2
|
|
mov %l7, %g3
|
|
mov %l2, %o6 ! put npc in unobtrusive place
|
|
clr %l0 ! clear the rest of the window
|
|
clr %l1
|
|
clr %l2
|
|
clr %l3
|
|
clr %l4
|
|
clr %l5
|
|
clr %l6
|
|
clr %l7
|
|
clr %o0
|
|
clr %o1
|
|
clr %o2
|
|
clr %o3
|
|
clr %o4
|
|
clr %o5
|
|
clr %o7
|
|
jmp %o6 ! return to npc
|
|
rett %o6 + 4
|
|
|
|
cw_out:
|
|
nop ! psr delay
|
|
jmp %l2 ! return to npc
|
|
rett %l2 + 4
|
|
|
|
/*
|
|
* Enter the monitor -- called from console abort
|
|
*/
|
|
ENTRY(montrap)
|
|
save %sp, -SA(MINFRAME), %sp ! get a new window
|
|
call _flush_windows ! flush windows to stack
|
|
nop
|
|
|
|
#ifdef SAS
|
|
ta 255 ! trap to siumlator
|
|
nop
|
|
#else
|
|
call %i0 ! go to monitor
|
|
nop
|
|
|
|
#endif SAS
|
|
ret
|
|
restore
|
|
|
|
/*
|
|
* return the condition codes in %g1
|
|
*/
|
|
getcc:
|
|
sll %l0, 8, %g1 ! right justify condition code
|
|
srl %g1, 28, %g1
|
|
1: jmp %l2 ! return, skip trap instruction
|
|
rett %l2+4
|
|
|
|
/*
|
|
* set the condtion codes from the value in %g1
|
|
*/
|
|
setcc:
|
|
sll %g1, 20, %l5 ! mov icc bits to their position in psr
|
|
set PSR_ICC, %l4 ! condition code mask
|
|
andn %l0, %l4, %l0 ! zero the current bits in the psr
|
|
or %l5, %l0, %l0 ! or new icc bits
|
|
mov %l0, %psr ! write new psr
|
|
nop ! psr delay
|
|
b,a 1b
|
|
|
|
/*
|
|
* some user has to do unaligned references, yuk!
|
|
* set a flag in the pcb so that when alignment traps happen
|
|
* we fix it up instead of killing the user
|
|
* Note: this routine is using the trap window
|
|
*/
|
|
fix_alignment:
|
|
sethi %hi(_uunix), %l5
|
|
ld [%l5 + %lo(_uunix)], %l5
|
|
ld [%l5 + PCB_FLAGS], %l4 ! get pcb_flags
|
|
bset FIX_ALIGNMENT, %l4
|
|
ba 1b
|
|
st %l4, [%l5 + PCB_FLAGS]
|
|
|
|
/*
|
|
* icode1
|
|
* When a process is created by main to do init, it starts here.
|
|
* We hack the stack to make it look like a system call frame.
|
|
* Then, icode exec's init.
|
|
*/
|
|
ENTRY(icode1)
|
|
call _icode
|
|
add %sp, MINFRAME, %o0 ! pointer to struct regs
|
|
b,a sys_rtt
|
|
|
|
#ifdef SUNDBE
|
|
/*
|
|
* Fast trap to return hi-res-time, use trap windows, leaves traps disabled.
|
|
* Returns the value of the hi-res timer in the globals g2 and g3.
|
|
* returns with the number of seconds in g2 and the number ticks in g3.
|
|
* [since since 00:00 GMT, January 1, 1970 (zero hour)]
|
|
*
|
|
* Note: only the MOSTEK chip (4/3x0, 4/4x0) has the high res. counters.
|
|
* Sun 4/2x0 doesn't support the high res. counters. The resolution
|
|
* for 4/2x0 will be 10 millisecs because we return the value of `time'.
|
|
*
|
|
*/
|
|
hrestimetrap: ! Uses %l3-%l5
|
|
sethi %hi(_clock_type), %l5 ! which clock_type?
|
|
ld [%l5 + %lo(_clock_type)], %g1
|
|
cmp %g1, MOSTEK48T02
|
|
bz,a 0f ! mostek chip?
|
|
sethi %hi(COUNTER_ADDR), %l3 ! delay slot
|
|
|
|
sethi %hi(_time), %l4
|
|
b 1f
|
|
clr %l3
|
|
0:
|
|
ld [%l3 + %lo(COUNTER_ADDR)], %l3 ! read counter from COUNTER_ADDR
|
|
sethi %hi(_time), %l4
|
|
tst %l3 ! check bit 31 (CTR_LIMIT_BIT)
|
|
bpos,a 1f ! limit bit not set
|
|
srl %l3, CTR_USEC_SHIFT, %l3 ! get usec count in %l3
|
|
! (delay slot)
|
|
set 10000, %l3
|
|
1:
|
|
ld [%l4 + %lo(_time)], %g2 ! g2 --- time.tv_sec
|
|
sethi %hi(1000000), %l5 ! 1000000 (ticks/sec)
|
|
ld [%l4 + %lo(_time+4)], %g3 ! g3 --- time.tv_ticks
|
|
or %l5, %lo(1000000), %l5 ! 1000000 (ticks/sec)
|
|
add %g3, %l3, %g3 ! %g3 <- tv_ticks + tick in ctr
|
|
cmp %g3, %l5 ! compar sum and tick per sec
|
|
bl 2f ! if (%l3 < 1 sec) goto 2f
|
|
mov %l0, %psr ! restore psr
|
|
sub %g3, %l5, %g3 ! Otherwise, %g3 -= 1 sec,
|
|
add %g2, 0x1, %g2 ! tv_sec += 1
|
|
2:
|
|
jmp %l2
|
|
rett %l2+4
|
|
#endif SUNDBE
|