3388 lines
90 KiB
ArmAsm
3388 lines
90 KiB
ArmAsm
.ident "@(#)locore.s 1.1 92/07/30 SMI"
|
|
|
|
/*
|
|
* Copyright (c) 1990 by Sun Microsystems, Inc.
|
|
*/
|
|
|
|
#undef SYS_TRAP_CHECKS_CPUID
|
|
#undef SYS_RTT_CHECKS_CPUID
|
|
|
|
#define CACHE_BYTES 65536 /* for Ross 605 flush workaround code */
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/vmparam.h>
|
|
#include <sys/errno.h>
|
|
#include <sys/resource.h>
|
|
#include <netinet/in_systm.h>
|
|
#include <machine/asm_linkage.h>
|
|
#include <machine/buserr.h>
|
|
#include <machine/clock.h>
|
|
#include <machine/cpu.h>
|
|
#include <machine/intreg.h>
|
|
#include <machine/async.h>
|
|
#include <machine/memerr.h>
|
|
#include <machine/eeprom.h>
|
|
#include <machine/mmu.h>
|
|
#include <machine/pcb.h>
|
|
#include <machine/psl.h>
|
|
#include <machine/pte.h>
|
|
#include <machine/reg.h>
|
|
#include <machine/trap.h>
|
|
#include <machine/scb.h>
|
|
#include <machine/auxio.h>
|
|
|
|
#include "assym.s"
|
|
|
|
#include <machine/iommu.h>
|
|
|
|
#ifdef IOC
|
|
#include <machine/iocache.h>
|
|
#endif IOC
|
|
|
|
|
|
#include "percpu_def.h"
|
|
#include "percpu.s"
|
|
|
|
.seg "data"
|
|
|
|
! 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 sun4m we put the message buffer in the third and fourth pages.
|
|
* We set things up so that the first 2 pages 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 pages with others).
|
|
*/
|
|
.global _DVMA, _msgbuf
|
|
_DVMA = DVMABASE ! address of DVMA area
|
|
_msgbuf = KERNELBASE + (2 * NBPG) ! address of printf message buffer
|
|
|
|
#ifdef PROVIDE_SYMBOLS
|
|
/*
|
|
* these need to be chased, commented, and
|
|
* perhaps even expanded to symbols for each
|
|
* register in the mapped areas.
|
|
*/
|
|
_4m_iommu = V_IOMMU_ADDR
|
|
_4m_sbusctl = V_SBUSCTL_ADDR
|
|
_4m_memerr = V_MEMERR_ADDR
|
|
_4m_auxio = V_AUXIO_ADDR
|
|
#ifdef VME
|
|
_4m_vmectl = V_VMECTL_ADDR
|
|
#endif VME
|
|
_4m_interrupt = V_INTERRUPT_ADDR
|
|
_4m_counter = V_COUNTER_ADDR
|
|
_4m_eeprom = V_EEPROM_ADDR
|
|
_4m_prom = 0xFFD00000
|
|
_4m_prom_scb = 0xFFEFA000
|
|
#endif PROVIDE_SYMBOLS
|
|
|
|
#if MSGBUFSIZE > (2 * 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
|
|
|
|
/*
|
|
* Since we don't fix the UADDR anymore, we can recycle that
|
|
* address and use it's pmeg to map stuff. We choose to use it
|
|
* to map the onboard devices. In order to hardwire an address
|
|
* into an instruction, we'll use the same mnemnonic here.
|
|
*/
|
|
#define OBADDR (0-(NBPG*UPAGES))
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
|
|
#ifndef MULTIPROCESSOR
|
|
.align 8
|
|
.global intstack, eintstack
|
|
intstack: ! bottom of interrupt stack
|
|
.skip INTSTACKSIZE
|
|
eintstack: ! end (top) of interrupt stack
|
|
.global exitstack, eexitstack
|
|
exitstack: ! bottom of exit stack
|
|
.skip INTSTACKSIZE
|
|
eexitstack: ! end (top) of exit stack
|
|
|
|
.global _ubasic ! the primordial uarea
|
|
_ubasic:
|
|
.skip KERNSTACK
|
|
.global ekernstack
|
|
ekernstack:
|
|
.global _p0uarea ! so we have a "struct user" available
|
|
_p0uarea:
|
|
.skip USIZE
|
|
|
|
.global intu
|
|
intu: ! a fake uarea for the kernel
|
|
.skip USIZE ! when a process is exiting
|
|
#else MULTIPROCESSOR
|
|
! %%% TODO: dynamicly allocate percpu pages from physmem
|
|
! allocate pages for each CPUs PERCPU area. note that
|
|
! cpu0 (ie. getprocessorid() returns 0) uses the first
|
|
! PERCPU_PAGES, cpu 1 uses the next PERCPU_PAGES, etc.
|
|
|
|
!!! .align 4096 ! must be page aligned, but .align can't do it for us.
|
|
.global _cpu0
|
|
_cpu0:
|
|
.skip PERCPU_PAGES*PAGESIZE*NCPU
|
|
#endif MULTIPROCESSOR
|
|
|
|
.global _kptstart
|
|
_kptstart:
|
|
#ifndef MULTIPROCESSOR
|
|
! allocate space for a level-1 page table.
|
|
.skip (KL1PT_SIZE*NL1PTEPERPT) !alignment slop
|
|
.skip (KL1PT_SIZE*NL1PTEPERPT)
|
|
#else MULTIPROCESSOR
|
|
! allocate a kernel level-1 page table for each CPU.
|
|
! note that the PERCPU implementation depends upon these
|
|
! level 1 page tables being aligned on a page boundary,
|
|
! even though a level 1 page table is smaller than a
|
|
! page.
|
|
.skip PAGESIZE !alignment slop
|
|
.skip PAGESIZE*NCPU
|
|
#endif MULTIPROCESSOR
|
|
! allocate level 2 page tables.
|
|
.skip KL2PT_SIZE*NKL2PTS*NL2PTEPERPT
|
|
_kptend:
|
|
|
|
.global _kclock14_vec, _mon_clock14_vec
|
|
.align 8 ! must be doubleword aligned
|
|
_kclock14_vec:
|
|
.word 0,0,0,0
|
|
_mon_clock14_vec:
|
|
.word 0,0,0,0
|
|
|
|
.word 0 ! paranoia.
|
|
/*
|
|
* 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(_Mbmap ,_mbutl ,MBPOOLMMUPAGES )
|
|
SYSMAP(_CMAP1 ,_CADDR1 ,1 ) ! local tmp
|
|
SYSMAP(_CMAP2 ,_CADDR2 ,1 ) ! local tmp
|
|
SYSMAP(_mmap ,_vmmap ,1 )
|
|
SYSMAP(_ESysmap ,_Syslimit ,0 ) ! must be last
|
|
|
|
.global _Syssize
|
|
_Syssize = (_ESysmap-_Heapptes)/4
|
|
|
|
#ifdef NOPROM
|
|
.global _availmem
|
|
_availmem:
|
|
.word 0
|
|
#endif NOPROM
|
|
|
|
#ifndef MULTIPROCESSOR
|
|
.global _uunix
|
|
_uunix:
|
|
.word _p0uarea
|
|
#endif MULTIPROCESSOR
|
|
|
|
.global _bad_fault_at
|
|
_bad_fault_at:
|
|
.word 0xDEADBEEF
|
|
|
|
#ifdef LWP
|
|
#ifdef MULTIPROCESSOR
|
|
.global _force_lwps
|
|
_force_lwps:
|
|
.word 0
|
|
#endif MULTIPROCESSOR
|
|
#endif LWP
|
|
|
|
/*
|
|
* 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 TRAPCOM(H,T) mov %psr, %l0; sethi %hi(H),%l3; jmp %l3+%lo(H); mov (T),%l4
|
|
#define TRAP(H) TRAPCOM(H,0)
|
|
#define WIN_TRAP(H) TRAPCOM(H,0)
|
|
#define SYS_TRAP(T) TRAPCOM(sys_trap,T)
|
|
#define TRAP_MON(T) TRAPCOM(trap_mon,T)
|
|
|
|
#ifdef SIMUL
|
|
/*
|
|
* For hardware simulator, want to double trap on any unknown trap
|
|
* (which is usually a call into SAS)
|
|
*/
|
|
#define BAD_TRAP \
|
|
mov %psr, %l0; mov %tbr, %l4; t 0; nop;
|
|
#else
|
|
#define BAD_TRAP SYS_TRAP((. - _start) >> 4);
|
|
#endif
|
|
|
|
|
|
/*
|
|
* Trap vector table.
|
|
* This must be the first text in the boot image.
|
|
*
|
|
* The first entry in this list is only used once during initial
|
|
* startup, and is replaced with a BAD_TRAP entry before we even
|
|
* change our %tbr to point at this scb. It must be relative, as
|
|
* it is used during initial startup to get to the real code before
|
|
* we are mapped in at KERNELBASE.
|
|
*
|
|
* 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:
|
|
b entry ; nop ; nop ; nop ! 00 - reset
|
|
SYS_TRAP(T_FAULT | T_TEXT_FAULT); ! 01 - instruction access
|
|
TRAP(multiply_check); ! 02 - illegal instruction
|
|
SYS_TRAP(T_PRIV_INSTR); ! 03 - privileged instruction
|
|
SYS_TRAP(T_FP_DISABLED); ! 04 - floating point disabled
|
|
WIN_TRAP(window_overflow); ! 05
|
|
WIN_TRAP(window_underflow); ! 06
|
|
SYS_TRAP(T_ALIGNMENT); ! 07 - alignment fault.
|
|
SYS_TRAP(T_FP_EXCEPTION); ! 08
|
|
SYS_TRAP(T_FAULT | T_DATA_FAULT); ! 09 - data fault
|
|
BAD_TRAP; ! 0A - tag_overflow
|
|
BAD_TRAP; ! 0B
|
|
BAD_TRAP; ! 0C
|
|
BAD_TRAP; ! 0D
|
|
BAD_TRAP; ! 0E
|
|
BAD_TRAP; ! 0F
|
|
BAD_TRAP; ! 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
|
|
#ifndef GPROF
|
|
SYS_TRAP(T_INTERRUPT | 14); ! 1E
|
|
#else GPROF
|
|
TRAP(test_prof); ! 1E
|
|
#endif GPROF
|
|
SYS_TRAP(T_INTERRUPT | 15); ! 1F
|
|
|
|
/*
|
|
* 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 (24 - coprocessor disabled)
|
|
BAD_TRAP; BAD_TRAP; ! 28 - 29 (28 - coprocessor exception)
|
|
BAD_TRAP; ! 2A (Integer divide by 0)
|
|
SYS_TRAP(T_FAULT | T_DATA_STORE); ! 2B (Data Store exception)
|
|
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 - 3B
|
|
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; BAD_TRAP; ! A2 - A3
|
|
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
|
|
TRAP(halt_and_catch_fire); ! C8 - halt and catch fire
|
|
BAD_TRAP; BAD_TRAP; BAD_TRAP; ! C9 - 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; ! FC - FE
|
|
TRAP_MON(0xff) ! FF - breakpoint
|
|
_escb: ! placeholder for "end of scb" for sanity check.
|
|
|
|
!
|
|
! here we have a cache-aligned string of non_writeable
|
|
! zeros used mainly by bcopy hardware
|
|
! and by fpu_probe
|
|
!
|
|
.global _zeros
|
|
_zeros:
|
|
.word 0,0,0,0,0,0,0,0
|
|
|
|
/*
|
|
* This small save area is for stashing information
|
|
* directly from trap handlers so we can see it
|
|
* on the logic analyzer. When we are not interesed
|
|
* in seeing PSR,PC,NPC,WIM on the logic analyzer
|
|
* these can go away.
|
|
*
|
|
* NOTE: this stuff is down here so it is at a
|
|
* well-known address; it is only written using
|
|
* mmu-bypass ASI accesses, to eliminate the chance
|
|
* of maybe not having it mapped properly. This
|
|
* also covers the fact that it is in the text
|
|
* segment, and therefore is not mapped writable.
|
|
*/
|
|
.global _trapinfo
|
|
.align 8
|
|
_trapinfo: ! saved by trap type 1, 2, 9 debug code
|
|
.word 0,0,0,0
|
|
|
|
/*
|
|
* The global "romp" and "dvec" pointers,
|
|
* as passed to entry by the caller. These
|
|
* are set once during boot, and must not
|
|
* be changed later. Placing them here
|
|
* in the text segment makes that happen
|
|
* quite nicely, thanks.
|
|
*/
|
|
.global _romp, _dvec
|
|
_romp: .word 0
|
|
_dvec: .word 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
|
|
|
|
.global _ctxtabptr
|
|
_ctxtabptr: ! physaddr of context table for ctx
|
|
.word 0
|
|
|
|
.global _afsrbuf
|
|
_afsrbuf:
|
|
.word 0,0,0,0
|
|
|
|
/************************************************************************
|
|
* trigger_logan: do something unique.
|
|
*/
|
|
#ifdef VIKING
|
|
/* There might be other ways of getting a trigger from a viking module,
|
|
* we need to put the code in, if that's the case.
|
|
* If vikings also use the same method, we have to make sure the
|
|
* MCNTL.AC bit is turned on when viking with E$ turn on
|
|
*/
|
|
#endif VIKING
|
|
|
|
ALTENTRY(trigger_logan)
|
|
retl
|
|
lduha [%g0]ASI_MEM, %g0
|
|
|
|
/************************************************************************
|
|
* halt_and_catch_fire: trigger a trap #0xC8,
|
|
* which vectors back here. guaranteed to
|
|
* cause this processor to go no further,
|
|
* probably stopping the simulation ;-)
|
|
* or triggering a watchdog.
|
|
*/
|
|
#ifdef VIKING
|
|
/* There might be other ways of getting a trigger from a viking module,
|
|
* we need to put the code in, if that's the case.
|
|
* If vikings also use the same method, we have to make sure the
|
|
* MCNTL.AC bit is turned on when viking with E$ turn on
|
|
*/
|
|
#endif VIKING
|
|
|
|
/*
|
|
* SECURITY NOTE: while this is super useful from a system
|
|
* debugging standpoint, it opens up a bad "denial of service"
|
|
* attack where any user can watchdog the system trivially.
|
|
* Thus the added code that bypasses the watchdogger if
|
|
* a user tries to trigger this via a software trap. The
|
|
* actual trap goes to the RTENTRY point, and supervisor
|
|
* calls from C code go to the ALTENTRY point (which gets
|
|
* an underbar prepended for C).
|
|
*/
|
|
RTENTRY(halt_and_catch_fire)
|
|
andcc %l0, PSR_PS, %g0
|
|
bz,a sys_trap
|
|
mov 0xC8, %l4
|
|
|
|
ALTENTRY(halt_and_catch_fire)
|
|
1:
|
|
lduha [%g0]ASI_MEM, %g0
|
|
ta 0xC8
|
|
b,a 1b
|
|
|
|
/************************************************************************
|
|
* System initialization
|
|
*
|
|
* Our contract with the boot prom specifies that the MMU is on
|
|
* and the first 16 meg of memory is mapped with a level-1 pte.
|
|
* We are called with romp in %o0 and dvec in %o1; we start
|
|
* execution directly from physical memory, so we need to get
|
|
* up into our proper addresses quickly: all code before we
|
|
* do this must be position independent.
|
|
*
|
|
* Our tasks are:
|
|
* save parameters
|
|
* construct mappings for KERNELBASE
|
|
* hop up into high memory
|
|
* initialize stack pointer
|
|
* initialize trap base register
|
|
* initialize window invalid mask
|
|
* initialize psr (with traps enabled)
|
|
* figure out all the module type stuff
|
|
* tear down the 1-1 mappings
|
|
* dive into main()
|
|
*
|
|
*/
|
|
entry:
|
|
!
|
|
! save the incoming arguments in locals in case we need
|
|
! to call the parent's callback routine
|
|
!
|
|
mov %o0, %l0 ! save arg (romp) until bss is clear
|
|
mov %o1, %l1 ! save dvec
|
|
|
|
tst %o2 ! if MUNIX (or old proms), this is zero
|
|
be 1f ! only call back if %o2 != 0
|
|
nop
|
|
|
|
jmpl %o2, %o7 ! pass return addr in %o0 for
|
|
add %o7, 8, %o0 ! romp->op_chain() function
|
|
1:
|
|
mov %l0, %g7 ! now move the locals to globals
|
|
mov %l1, %g6 ! so we can reset the psr/wim
|
|
|
|
mov 0x02, %wim ! setup wim
|
|
set PSR_PIL|PSR_S, %g1
|
|
mov %g1, %psr ! initialze psr: supervisor, splmax
|
|
wr %g1, PSR_ET, %psr ! enable traps, too.
|
|
nop ; nop ; nop ! psr delay
|
|
|
|
! establish mappings for kernel in upper virtual memory space
|
|
! for now, just copy whatever was in the zero slot in the
|
|
! level-1 page table into the 0xF8 slot.
|
|
!
|
|
! this is only temporary, we will construct a good level-2 table
|
|
! for this mapping later.
|
|
!
|
|
! In case of VIKING:
|
|
! lda [%g0]ASI_MOD, %o0 ! find module type
|
|
! if viking MBUS mode, don't do anything.
|
|
! if viking CC mode, check if cacheable
|
|
! then 1. disable traps; 2. set the AC bit; 3. do ASI_MEM;
|
|
! 4. clear AC bit 5. enable traps again
|
|
!
|
|
! XXX-Make test changes to routine so it looks for
|
|
! whether we are viking (via %psr, since mcr doesnt have
|
|
! the info), then whether we have E$ (via MB bit). If so,
|
|
! we set AC bit before doing ASI_MEM.
|
|
! Purposely made simple for clarity in debug.
|
|
! Look to optimize later.
|
|
set 0x40000000, %l0 ! IMPL = VIKING (bit in %psr)
|
|
mov %psr, %l1
|
|
and %l0, %l1, %l0
|
|
cmp %l0, %g0
|
|
bz 1f ! not a VIKING
|
|
nop
|
|
|
|
lda [%g0]ASI_MOD, %l0 ! find module type
|
|
set CPU_VIK_MB, %l2
|
|
and %l0, %l2, %l0 ! %l0 keeps the test value
|
|
cmp %l0, %g0 ! viking/E$?
|
|
bz 2f ! yes
|
|
nop
|
|
|
|
1: set RMMU_CTP_REG, %g5 ! this works for all modules
|
|
lda [%g5]ASI_MOD, %g5 ! get the context table ptr reg
|
|
sll %g5, 4, %g5 ! convert to phys addr
|
|
lda [%g5]ASI_MEM, %g5 ! get the level-1 table ptr
|
|
srl %g5, 4, %g5 ! strip off PTP bits
|
|
sll %g5, 8, %g5 ! convert to phys addr
|
|
lda [%g5]ASI_MEM, %g1 ! get one-to-one mapping information
|
|
add %g5, (KERNELBASE>>24)<<2, %g4
|
|
sta %g1, [%g4]ASI_MEM ! duplicate at KERNELBASE
|
|
b 4f
|
|
nop
|
|
|
|
2: andn %l1, PSR_ET, %g1 ! disable traps
|
|
mov %g1, %psr
|
|
nop; nop; nop ! PSR delay
|
|
set RMMU_CTP_REG, %g5 ! this works for all modules
|
|
lda [%g5]ASI_MOD, %g5 ! get the context table ptr reg
|
|
sll %g5, 4, %g5 ! convert to phys addr
|
|
lda [%g0]ASI_MOD, %l2 ! get MMU CSR, %l2 keeps the saved CSR
|
|
set CPU_VIK_AC, %l3 ! AC bit
|
|
or %l2, %l3, %l3 ! or in AC bit
|
|
sta %l3, [%g0]ASI_MOD ! store new CSR
|
|
|
|
lda [%g5]ASI_MEM, %g5 ! get the level-1 table ptr
|
|
srl %g5, 4, %g5 ! strip off PTP bits
|
|
sll %g5, 8, %g5 ! convert to phys addr
|
|
lda [%g5]ASI_MEM, %g1 ! get one-to-one mapping information
|
|
add %g5, (KERNELBASE>>24)<<2, %g4
|
|
sta %g1, [%g4]ASI_MEM ! duplicate at KERNELBASE
|
|
sta %l2, [%g0]ASI_MOD ! restore CSR;
|
|
mov %l1, %psr ! restore psr; enable traps again
|
|
nop; nop; nop ! PSR delay
|
|
!
|
|
! The correct virtual addresses are now mapped.
|
|
! Do an absolute jump to set the pc to the
|
|
! correct virtual addresses.
|
|
!
|
|
4: set 1f, %g1
|
|
jmp %g1
|
|
nop
|
|
1:
|
|
.global _firsthighinstr
|
|
_firsthighinstr:
|
|
! *** now running in high virtual memory ***
|
|
!
|
|
! save the romp and dvec
|
|
!
|
|
sethi %hi(_romp), %g1
|
|
st %g7, [%g1 + %lo(_romp)]
|
|
sethi %hi(_dvec), %g1
|
|
st %g6, [%g1 + %lo(_dvec)]
|
|
|
|
! Switch to a large stack (prom's may not be big enuf)
|
|
! Must be atomic. May not be the final location ...
|
|
|
|
set ekernstack - SA(MINFRAME + REGSIZE), %g1
|
|
#ifdef MULTIPROCESSOR
|
|
set _cpu0, %g2
|
|
add %g1, %g2, %g1 ! convert to address inside cpu0
|
|
set VA_PERCPUME, %g2
|
|
sub %g1, %g2, %g1
|
|
#endif MULTIPROCESSOR
|
|
mov %g1, %sp
|
|
mov 0, %fp
|
|
|
|
#ifdef MULTIPROCESSOR
|
|
!
|
|
! point the kernel lock at us.
|
|
! Note that the MID may be wrong, we do not
|
|
! know what the %tbr bits are, but at least
|
|
! it will be consistant.
|
|
!
|
|
call _klock_init
|
|
nop
|
|
#endif MULTIPROCESSOR
|
|
|
|
! using the boot prom's %tbr, call the temporary load of ptes
|
|
|
|
call _module_setup ! setup correct module routines
|
|
lda [%g0]ASI_MOD, %o0 ! find module type
|
|
|
|
!
|
|
! clear status of any registers that might be latching errors.
|
|
!
|
|
call _mmu_getsyncflt ! clear module I-SFSR
|
|
mov 1, %g6
|
|
call _mmu_getsyncflt ! clear module D-SFSR
|
|
mov 9, %g6
|
|
sethi %hi(_afsrbuf), %o0
|
|
call _mmu_getasyncflt ! clear module AFSR
|
|
or %o0, %lo(_afsrbuf), %o0
|
|
|
|
call _load_tmpptes ! Routine to load temporary ptes
|
|
nop
|
|
|
|
! Since we have called module_setup we have the value
|
|
! of cache set. Use it to determine if have E$.
|
|
! Set AC bit whenever have viking/e$ and doing ASI_MEM.
|
|
!
|
|
! %o0 keeps the level-1 table ptr with PTP bits
|
|
! switch to our mapping
|
|
set RMMU_CTP_REG, %g5 ! this works for all modules
|
|
lda [%g5]ASI_MOD, %g5 ! get the context table ptr reg
|
|
sll %g5, 4, %g4 ! convert to phys addr
|
|
|
|
sethi %hi(_cache), %l3
|
|
ld [%l3 + %lo(_cache)], %l3
|
|
cmp %l3, CACHE_PAC_E
|
|
bne,a 1f
|
|
sta %o0, [%g4]ASI_MEM
|
|
|
|
mov %psr, %l1
|
|
andn %l1, PSR_ET, %l3
|
|
mov %l3, %psr
|
|
nop; nop; nop
|
|
lda [%g0]ASI_MOD, %l3
|
|
set CPU_VIK_AC, %l4
|
|
or %l3, %l4, %l4
|
|
sta %l4, [%g0]ASI_MOD
|
|
sta %o0, [%g4]ASI_MEM ! put our level-1 table ptr into ctx tbl
|
|
sta %l3, [%g0]ASI_MOD
|
|
mov %l1, %psr
|
|
nop; nop; nop
|
|
|
|
1: call _mmu_setctx ! switch to our mapping
|
|
mov %g0, %o0
|
|
call _mmu_flushall
|
|
nop
|
|
|
|
|
|
call _early_startup
|
|
nop
|
|
|
|
#ifdef MULTIPROCESSOR
|
|
! The PerCPU area is now properly mapped, so we can
|
|
! switch our stack over to it.
|
|
|
|
set ekernstack - SA(MINFRAME + REGSIZE), %g1
|
|
mov %g1, %sp
|
|
#ifdef VAC
|
|
! since the temporary stack used an alias for the real stack
|
|
! that was probably not aligned, we need to flush the old
|
|
! stuff from the cache to avoid "cache data bombs".
|
|
|
|
set _cpu0, %o0
|
|
set PERCPU_PAGES*PAGESIZE, %o1
|
|
call _vac_flush
|
|
nop
|
|
#endif
|
|
#endif MULTIPROCESSOR
|
|
|
|
!
|
|
! find our trap table. This register remains "live" until
|
|
! we use it to set up %tbr below.
|
|
!
|
|
#ifndef PC_vscb
|
|
set scb, %g2
|
|
#else PC_vscb
|
|
set VA_PERCPU+PC_vscb, %g2
|
|
#endif PC_vscb
|
|
|
|
!
|
|
! Patch vector 0 trap to "zero" in case it happens again.
|
|
! We use "trap 0xF" as our prototype for BADTRAP.
|
|
!
|
|
ldd [%g2+0xF0], %o2
|
|
ldd [%g2+0xF8], %o4
|
|
andn %o5, 0xFF, %o5 ! set trap type to zero
|
|
std %o2, [%g2+0x00]
|
|
std %o4, [%g2+0x08]
|
|
|
|
!
|
|
! 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
|
|
|
|
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
|
|
|
|
inc %g1
|
|
sethi %hi(_nwindows), %g4 ! inialize the nwindows variable
|
|
st %g1, [%g4 + %lo(_nwindows)]
|
|
|
|
#ifdef NOPROM
|
|
!
|
|
! If we are in the simulator ask it for the memory size.
|
|
! probably should shift to having the right stuff in romp.
|
|
!
|
|
lda [%g0]ASI_SAS, %l0 ! Load from 0 in ASI_SAS (0x30) will
|
|
sethi %hi(_availmem), %g1 ! return the available amount of memory
|
|
st %l0, [%g1 + %lo(_availmem)]
|
|
#endif
|
|
|
|
!
|
|
! Switch to our trap base register
|
|
!
|
|
mov %tbr, %l4 ! save monitor's tbr
|
|
bclr 0xfff, %l4 ! remove tt
|
|
|
|
!
|
|
! Save monitor's level14 clock interrupt vector code.
|
|
!
|
|
set _mon_clock14_vec, %o1
|
|
ldd [%l4+TT(T_INT_LEVEL_14)], %o2
|
|
ldd [%l4+TT(T_INT_LEVEL_14)+8], %o4
|
|
std %o2, [%o1]
|
|
std %o4, [%o1+8]
|
|
!
|
|
! Save monitor's breakpoint vector code, and
|
|
! install it in our trap table.
|
|
!
|
|
set mon_breakpoint_vec, %o1
|
|
ldd [%l4+TT(ST_MON_BREAKPOINT+T_SOFTWARE_TRAP)], %o2
|
|
ldd [%l4+TT(ST_MON_BREAKPOINT+T_SOFTWARE_TRAP)+8], %o4
|
|
std %o2, [%o1]
|
|
std %o4, [%o1+8]
|
|
std %o2, [%g2+TT(ST_MON_BREAKPOINT+T_SOFTWARE_TRAP)]
|
|
std %o4, [%g2+TT(ST_MON_BREAKPOINT+T_SOFTWARE_TRAP)+8]
|
|
|
|
#ifdef MULTIPROCESSOR
|
|
! disable traps while munging %tbr
|
|
|
|
mov %psr, %g1
|
|
andn %g1, PSR_ET, %g1
|
|
mov %g1, %psr
|
|
nop ; nop ; nop ! psr delay
|
|
|
|
#endif MULTIPROCESSOR
|
|
|
|
!
|
|
! Switch from boot prom's trap table to our own
|
|
!
|
|
mov %g2, %tbr
|
|
|
|
#ifdef MULTIPROCESSOR
|
|
!
|
|
! Now that we have the correct %tbr,
|
|
! reinitialize the klock to have the
|
|
! correct owner.
|
|
!
|
|
call _klock_init
|
|
nop
|
|
#endif MULTIPROCESSOR
|
|
|
|
!
|
|
! initialize psr: supervisor, traps enabled, splmax.
|
|
!
|
|
set PSR_S|PSR_PIL, %g1
|
|
mov %g1, %psr ! supv, splmax;
|
|
wr %g1, PSR_ET, %psr ! enable traps, too.
|
|
nop ; nop ; nop ! psr delay
|
|
|
|
! invalidate the dual (one-to-one) mapping for low memory,
|
|
! we do not need it any more.
|
|
|
|
! In case of VIKING:
|
|
! lda [%g0]ASI_MOD, %o0 ! find module type
|
|
! if viking MBUS mode, don't do anything.
|
|
! if viking CC mode, check if cacheable
|
|
! then 1. disable traps; 2. set the AC bit; 3. do ASI_MEM; 4. clear AC bit
|
|
! 5. enable traps again
|
|
!
|
|
! Use the value of cache variable to determine if have vik/e$
|
|
! if so, set the AC bit before doing ASI_MEM.
|
|
sethi %hi(_cache), %l3
|
|
ld [%l3 + %lo(_cache)], %l3
|
|
cmp %l3, CACHE_PAC_E
|
|
bz 2f
|
|
nop
|
|
|
|
1: set RMMU_CTP_REG, %g5 ! this works for all modules
|
|
lda [%g5]ASI_MOD, %g5 ! get the context table ptr reg
|
|
sll %g5, 4, %g5 ! convert to phys addr
|
|
lda [%g5]ASI_MEM, %g5 ! get the level-1 table ptr
|
|
srl %g5, 4, %g5 ! strip off PTP bits
|
|
sll %g5, 8, %g5 ! convert to phys addr
|
|
call _mmu_flushall
|
|
sta %g0, [%g5]ASI_MEM
|
|
b 3f
|
|
nop
|
|
|
|
2: mov %psr, %l1
|
|
andn %l1, PSR_ET, %g1 ! disable traps
|
|
mov %g1, %psr
|
|
nop; nop; nop ! PSR delay
|
|
set RMMU_CTP_REG, %g5 ! this works for all modules
|
|
lda [%g5]ASI_MOD, %g5 ! context table ptr reg
|
|
sll %g5, 4, %g5 ! convert to phys addr
|
|
lda [%g0]ASI_MOD, %l2 ! get MMU CSR, %l2 keeps the saved CSR
|
|
set CPU_VIK_AC, %l3 ! AC bit
|
|
or %l2, %l3, %l3 ! or in AC bit
|
|
sta %l3, [%g0]ASI_MOD ! store new CSR
|
|
|
|
lda [%g5]ASI_MEM, %g5 ! get the level-1 table ptr
|
|
srl %g5, 4, %g5 ! strip off PTP bits
|
|
sll %g5, 8, %g5 ! convert to phys addr
|
|
sta %g0, [%g5]ASI_MEM ! null out 1-1 mapping
|
|
call _mmu_flushall
|
|
sta %l2, [%g0]ASI_MOD ! restore CSR;
|
|
mov %l1, %psr ! restore psr; enable traps again
|
|
nop; nop; nop ! PSR delay
|
|
!
|
|
! Dummy up fake user registers on the stack,
|
|
! so we can dive into user code as if we were
|
|
! returning from a trap.
|
|
!
|
|
3: set USRSTACK-WINDOWSIZE, %g1
|
|
st %g1, [%sp + MINFRAME + SP*4] ! user stack pointer
|
|
set PSL_USER, %g1
|
|
st %g1, [%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
|
|
|
|
!
|
|
! enable interrupts in sipr
|
|
! by writing all-ones to the
|
|
! sipr mask "clear" location.
|
|
!
|
|
sethi %hi(V_INT_REGS+0x4008), %g1
|
|
sub %g0, 1, %g2
|
|
st %g2, [%g1+%lo(V_INT_REGS+0x4008)]
|
|
|
|
!
|
|
! 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 sys_rtt ! fake return from trap
|
|
nop
|
|
|
|
ALTENTRY(cpuid_bad)
|
|
b _halt_and_catch_fire
|
|
nop
|
|
|
|
/*
|
|
* Generic system trap handler.
|
|
*
|
|
* %l0 trap-psr
|
|
* %l1 trap-pc
|
|
* %l2 trap-npc
|
|
* %l3 reserved for %wim
|
|
* %l4 trap-type
|
|
* %l5
|
|
* %l6 reserved for nwindows - 1
|
|
* %l7 reserved for stack
|
|
*/
|
|
.global _sys_trap
|
|
.global sys_trap
|
|
_sys_trap:
|
|
sys_trap:
|
|
sethi %hi(_nwindows), %l6
|
|
ld [%l6+%lo(_nwindows)], %l6
|
|
dec %l6
|
|
|
|
#ifdef SYS_TRAP_CHECKS_CPUID
|
|
set _cpuid, %l5
|
|
lda [%l5]ASI_FLPR, %l7
|
|
and %l7, 3, %l7
|
|
cmp %l7, 2 ! cpuid not mapped
|
|
bne 1f
|
|
nop
|
|
|
|
sta %g0, [%l5]ASI_FLPR ! flush tlb
|
|
|
|
set CACHE_BYTES-1, %l3
|
|
and %l5, %l3, %l7 ! modulo cache size
|
|
set PAGEMASK, %l3
|
|
and %l7, %l3, %l7 ! round down to base of page
|
|
set KERNELBASE + CACHE_BYTES, %l3
|
|
ld [%l3 + %l7], %g0 ! replace the cache line
|
|
|
|
ld [%l5], %l7
|
|
GETCPU(%l5)
|
|
cmp %l5, %l7
|
|
bne _cpuid_bad
|
|
nop
|
|
|
|
!!! add more tests here, as desired.
|
|
|
|
1:
|
|
#endif SYS_TRAP_CHECKS_CPUID
|
|
!
|
|
! 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)
|
|
/*
|
|
* call mmu_getsyncflt to get the status reg in %g6 and
|
|
* address reg in %g7. In harvard archetectures,
|
|
* mmu_getsyncflt determines which MMU had the fault by
|
|
* checking the input parameter which indicates whether it was
|
|
* a text or data fault. Note that this assumes that only one
|
|
* data fault at a time can occurr on CPUs with parallel MMUs.
|
|
*
|
|
* NOTE - %g6 and %g7 are used to store the SFSR and
|
|
* SFAR until used later (see below). The reason for
|
|
* reading these synchronous fault registers now is that
|
|
* the window overflow processing that we are about to do
|
|
* may also cause another fault. This requres some care in
|
|
* validating code that might be executed in the meantime;
|
|
* some sections of code assume %g6 and %g7 may be stepped on.
|
|
*/
|
|
btst T_FAULT, %l4
|
|
bz,a 2f
|
|
clr %g6 !don't leave %g6 unset if SFSR not read
|
|
mov %o7, %g3 !save o7 in g3
|
|
call _mmu_getsyncflt
|
|
mov %l4, %g6 !indicates text or data fault.
|
|
mov %g3, %o7 !restore o7.
|
|
2:
|
|
#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;
|
|
#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
|
|
!
|
|
btst %l5, %l3 ! retest
|
|
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)
|
|
/*
|
|
* call mmu_getsyncflt to get the status reg in %g6 and
|
|
* address reg in %g7. In harvard archetectures,
|
|
* mmu_getsyncflt determines which MMU had the fault by
|
|
* checking the input parameter which indicates whether it was
|
|
* a text or data fault. Note that this assumes that only one
|
|
* data fault at a time can occurr on CPUs with parallel MMUs.
|
|
*
|
|
* NOTE - %g6 and %g7 are used to store the SFSR and
|
|
* SFAR until used later (see below). The reason for
|
|
* reading these synchronous fault registers now is that
|
|
* the window overflow processing that we are about to do
|
|
* may also cause another fault. This requres some care in
|
|
* validating code that might be executed in the meantime;
|
|
* some sections of code assume %g6 and %g7 may be stepped on.
|
|
*/
|
|
btst T_FAULT, %l4
|
|
bz,a 1f
|
|
clr %g6 !don't leave %g6 unset if SFSR not read
|
|
mov %o7, %g3 !save o7 in g3
|
|
call _mmu_getsyncflt
|
|
mov %l4, %g6 !indicates text or data fault.
|
|
mov %g3, %o7
|
|
1:
|
|
!
|
|
st %l0, [%l7 + MINFRAME + PSR*4] ! psr
|
|
set _uunix, %g5
|
|
ld [%g5], %g5
|
|
st %l1, [%l7 + MINFRAME + PC*4] ! pc
|
|
st %l2, [%l7 + MINFRAME + nPC*4] ! npc
|
|
|
|
!
|
|
! If we are in last trap window, all windows are occupied and
|
|
! we must do window overflow stuff in order to do further calls
|
|
!
|
|
btst %l5, %l3 ! retest
|
|
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
|
|
!
|
|
! Process trap according to type
|
|
!
|
|
btst T_INTERRUPT, %l4 ! interrupt
|
|
bnz interrupt
|
|
cmp %l4, T_SYSCALL ! syscall
|
|
be syscall
|
|
btst T_FAULT, %l4 ! fault
|
|
|
|
fixfault:
|
|
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
|
|
|
|
1:
|
|
!
|
|
! All other traps. Call C trap handler.
|
|
!
|
|
mov %l4, %o0 ! trap(t, rp)
|
|
clr %o2 ! addr = 0
|
|
clr %o3 ! be = 0
|
|
mov S_OTHER, %o4 ! rw = S_OTHER
|
|
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
|
|
!
|
|
! In order to save the window onto the stack, the stack
|
|
! must be aligned on a word boundary, and the part of the
|
|
! stack where the save will be done must be present.
|
|
! We first check for alignment.
|
|
!
|
|
btst 0x7, %sp ! test sp alignment
|
|
bnz st_stack_not_res ! stack misaligned, catch it later
|
|
|
|
! branch to code which is dependent upon the particular type
|
|
! of SRMMU module. this code will attempt to save the
|
|
! overflowed window onto the stack, without first verifying
|
|
! the presence of the stack.
|
|
|
|
! Most of the time the stack is resident in main memory, so we
|
|
! don't verify its presence before attempting to save the
|
|
! window onto the stack. Rather, we simply set the no-fault
|
|
! bit of the SRMMU's control register, so that doing the save
|
|
! won't cause another trap in the case where the stack is not
|
|
! present. By checking the synchronous fault status register,
|
|
! we can determine whether the save actually worked (ie. stack
|
|
! was present), or whether we first need to fault in the
|
|
! stack.
|
|
|
|
! Other sun4 trap handlers first probe for the stack, and
|
|
! then, if the stack is present, they store to the stack.
|
|
! This approach CANNOT be used with a multiprocessor system
|
|
! because of a race condition: between the time that the stack
|
|
! is probed, and the store to the stack is done, the stack
|
|
! could be stolen by the page daemon.
|
|
|
|
! Of course, we have to be sure that this really is
|
|
! in userland, or the user could overflow data into
|
|
! supvervisor space ...
|
|
|
|
set KERNELBASE, %g1
|
|
cmp %g1, %sp
|
|
bgu _mmu_sys_ovf ! ok, go do it.
|
|
nop
|
|
mov 0x8E, %g1 ! [fake up SFSR]
|
|
b st_stack_not_res
|
|
mov %sp, %g2 ! [fake up SFAR]
|
|
|
|
|
|
! here is where we return after attempting to save the
|
|
! overflowed window onto the stack. check to see if the
|
|
! stack was actually present.
|
|
|
|
.global st_chk_flt
|
|
st_chk_flt:
|
|
|
|
btst MMU_SFSR_FAV, %g1 ! did a fault occurr?
|
|
bz,a st_have_window
|
|
restore
|
|
|
|
! a fault occurred, so the stack is not resident.
|
|
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
|
|
|
|
.seg "text"
|
|
.align 4
|
|
|
|
/*
|
|
* Return from sys_trap routine.
|
|
*/
|
|
.global sys_rtt
|
|
sys_rtt:
|
|
! code for fItoX bug removed from sun4c (not needed)
|
|
! and, since sun4m inherits from sun4c, it is still gone.
|
|
|
|
#ifdef SYS_RTT_CHECKS_CPUID
|
|
set _cpuid, %g1
|
|
lda [%g1]ASI_FLPR, %l0
|
|
and %l0, 3, %l0
|
|
cmp %l0, 2 ! cpuid not mapped
|
|
bne 1f
|
|
nop
|
|
|
|
ld [%g1], %l0
|
|
GETCPU(%g1)
|
|
cmp %g1, %l0
|
|
bne _cpuid_bad
|
|
nop
|
|
|
|
!!! add more tests here, as desired.
|
|
|
|
1:
|
|
#endif SYS_RTT_CHECKS_CPUID
|
|
|
|
! 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 LWP
|
|
|
|
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
|
|
#ifdef MULTIPROCESSOR
|
|
mov 1, %g2
|
|
GETCPU(%g1)
|
|
sll %g2, %g1, %g2
|
|
sethi %hi(_force_lwps), %g1 ! check the lwp force bits;
|
|
ld [%g1 + %lo(_force_lwps)], %g1
|
|
andcc %g1, %g2, %g0 ! if our bit is on,
|
|
bnz 0f ! always run lwpschedule
|
|
nop
|
|
|
|
tst %g1 ! if anyone else's is on,
|
|
bnz 1f ! do not try to run lwpschedule
|
|
nop
|
|
|
|
sethi %hi(_runthreads), %g1
|
|
ld [%g1 + %lo(_runthreads)], %g2
|
|
tst %g2 ! if threads are running,
|
|
bnz 1f ! do not bother running them.
|
|
nop
|
|
#endif MULTIPROCESSOR
|
|
|
|
sethi %hi(___Nrunnable), %g2
|
|
ld [%g2 + %lo(___Nrunnable)], %g2
|
|
tst %g2 ! if nobody is runnable,
|
|
bz 1f ! don't bother checking.
|
|
nop
|
|
0:
|
|
!
|
|
! Runnable thread for async I/O
|
|
!
|
|
wr %l0, PSR_ET, %psr ! turn on traps
|
|
nop ! psr delay
|
|
#ifdef MULTIPROCESSOR
|
|
call _klock_enter
|
|
nop
|
|
#endif MULTIPROCESSOR
|
|
call _flush_user_windows
|
|
nop
|
|
call _lwpschedule
|
|
nop
|
|
|
|
!
|
|
! Use kernel context (for now) since lwpschedule may
|
|
! change the context reg. This causes a fault.
|
|
call _mmu_setctx
|
|
mov %g0, %o0
|
|
b sys_rtt ! try again
|
|
nop
|
|
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.
|
|
|
|
!
|
|
! Enable traps, so we can call C code.
|
|
! Keep the SPL really high for now.
|
|
!
|
|
andn %l0, PSR_PIL, %g2 ! splhi
|
|
or %g2, 13 << PSR_PIL_BIT, %g2
|
|
mov %g2, %psr ! change priority (IU bug)
|
|
wr %g2, PSR_ET, %psr ! turn on traps
|
|
|
|
#ifdef MULTIPROCESSOR
|
|
nop ! psr delay
|
|
call _klock_knock
|
|
nop
|
|
tst %o0
|
|
bz 9f
|
|
nop
|
|
#endif MULTIPROCESSOR
|
|
!
|
|
! Run the streams queues.
|
|
!
|
|
mov 1, %g1 ! mark that we're running the queues
|
|
call _queuerun
|
|
stb %g1, [%l5 + %lo(_queueflag)]
|
|
|
|
stb %g0, [%l5 + %lo(_queueflag)] ! done running queues
|
|
|
|
b sys_rtt
|
|
nop
|
|
|
|
#ifdef MULTIPROCESSOR
|
|
9:
|
|
!
|
|
! unable to acquire the lock.
|
|
! reload the globals we lost
|
|
! and proceed with the rtt.
|
|
!
|
|
wr %l0, %psr ! disable traps
|
|
nop ; nop ; nop ! psr delay
|
|
|
|
sethi %hi(_uunix), %g5 ! XXX - global u register?
|
|
ld [%g5 + %lo(_uunix)], %g5
|
|
ld [%g5 + PCB_FLAGS], %g1 ! delay slot, test for ast.
|
|
|
|
#endif MULTIPROCESSOR
|
|
|
|
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
|
|
sethi %hi(_nwindows), %l6
|
|
ld [%l6+%lo(_nwindows)], %l6
|
|
dec %l6 ! get NW-1 for rol calculation
|
|
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
|
|
!
|
|
! In order to restore the window from the stack, the stack
|
|
! must be aligned on a word boundary, and the part of the
|
|
! stack where the save will be done must be present.
|
|
! We first check for alignment.
|
|
!
|
|
btst 0x7, %fp ! test fp alignment
|
|
bz sr_assume_stack_res
|
|
nop
|
|
|
|
!
|
|
! 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
|
|
|
|
! branch to code which is dependent upon the particular
|
|
! type of SRMMU module. this code will attempt to restore
|
|
! the underflowed window from the stack, without first
|
|
! verifying the presence of the stack.
|
|
! Most of the time the stack is resident in main memory,
|
|
! so we don't verify its presence before attempting to restore
|
|
! the window from the stack. Rather, we simply set the
|
|
! no-fault bit of the SRMMU's control register, so that
|
|
! doing the restore won't cause another trap in the case
|
|
! where the stack is not present. By checking the
|
|
! synchronous fault status register, we can determine
|
|
! whether the restore actually worked (ie. stack was present),
|
|
! or whether we first need to fault in the stack.
|
|
! Other sun4 trap handlers first probe for the stack, and
|
|
! then, if the stack is present, they restore from the stack.
|
|
! This approach CANNOT be used with a multiprocessor system
|
|
! because of a race condition: between the time that the
|
|
! stack is probed, and the restore from the stack is done, the
|
|
! stack could be stolen by the page daemon.
|
|
sr_assume_stack_res:
|
|
set KERNELBASE, %g1
|
|
cmp %g1, %fp
|
|
bgu 1f
|
|
nop
|
|
mov 0x0E, %g1 ! [fake up SFSR]
|
|
b sr_stack_not_res
|
|
mov %fp, %g2 ! [fake up SFAR]
|
|
|
|
1:
|
|
sethi %hi(_v_mmu_sys_unf), %g1
|
|
ld [%g1 + %lo(_v_mmu_sys_unf)], %g1
|
|
jmp %g1
|
|
nop
|
|
|
|
! here is where we return after attempting to restore the
|
|
! underflowed window from the stack. check to see if the
|
|
! stack was actually present.
|
|
.global sr_chk_flt
|
|
sr_chk_flt:
|
|
btst MMU_SFSR_FAV, %g1 ! did a fault occurr?
|
|
bz sr_user_regs
|
|
nop
|
|
|
|
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
|
|
nop; nop; nop;
|
|
mov T_DATA_FAULT, %o0
|
|
add %sp, MINFRAME, %o1
|
|
mov %g2, %o2 ! save fault address
|
|
mov %l0, %psr ! in case of changed priority (IU bug)
|
|
wr %l0, PSR_ET, %psr ! enable traps
|
|
mov %g1, %o3
|
|
call _trap ! trap(T_DATA_FAULT,
|
|
mov S_WRITE, %o4 ! rp, addr, be, S_WRITE)
|
|
|
|
b,a sys_rtt
|
|
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 [%sp + MINFRAME + nPC*4], %l2 ! user npc
|
|
ld [%g5 + PCB_FLAGS], %l3
|
|
!
|
|
! 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)
|
|
!
|
|
or %l1, %l2, %g2
|
|
btst 0x3, %g2 ! if either PC or NPC badly aligned,
|
|
bnz sr_align_trap ! die a horrible death.
|
|
nop
|
|
|
|
btst CLEAN_WINDOWS, %l3
|
|
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
|
|
|
|
! can't use globals after this.
|
|
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
|
|
#ifndef MULTIPROCESSOR
|
|
jmp %o6 ! return
|
|
rett %o7
|
|
#else MULTIPROCESSOR
|
|
b,a _klock_rett_o6o7
|
|
#endif MULTIPROCESSOR
|
|
|
|
3:
|
|
! can't use globals after this.
|
|
RESTORE_GLOBALS(%sp + MINFRAME) ! restore user globals
|
|
|
|
#ifndef MULTIPROCESSOR
|
|
jmp %l1 ! return
|
|
rett %l2
|
|
#else MULTIPROCESSOR
|
|
b,a _klock_rett
|
|
#endif MULTIPROCESSOR
|
|
|
|
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
|
|
|
|
mov %l2, %l2 !VIKING HW BUG WORKAROUND
|
|
! REMOVE WHEN VIKING ARE > 1.2 REV
|
|
|
|
#ifndef MULTIPROCESSOR
|
|
jmp %l1 ! return to trapped instruction
|
|
rett %l2
|
|
#else MULTIPROCESSOR
|
|
b,a _klock_rett
|
|
#endif MULTIPROCESSOR
|
|
|
|
/* end sys_rtt*/
|
|
|
|
/*
|
|
* 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 */
|
|
|
|
/*
|
|
* Fault handler.
|
|
*/
|
|
fault:
|
|
1:
|
|
/*
|
|
* The synchronous fault status register (SFSR) and
|
|
* synchronous fault address register (SFAR) were obtained
|
|
* above by calling mmu_getsyncflt, and have been stored
|
|
* in %g6 and %g7, respectively.
|
|
* For the call to trap below, move the status reg into
|
|
* %o3, and the address reg into %o2
|
|
*/
|
|
|
|
mov %g6, %o3 ! SFSR
|
|
cmp %l4, T_TEXT_FAULT ! text fault?
|
|
be,a 2f
|
|
mov %l1, %o2 ! use saved pc as the fault address
|
|
mov %g7, %o2 ! SFAR
|
|
|
|
! check for FT_NONE, if it is zero just return
|
|
|
|
2: srl %o3, FS_FTSHIFT, %g2 ! get the fault type
|
|
and %g2, FS_FTMASK, %g2
|
|
cmp %g2, %g0
|
|
bz sys_rtt ! if FT type is zero we have nothing do
|
|
nop
|
|
|
|
4: srl %o3, FS_ATSHIFT, %g2 !Get the access type bits
|
|
and %g2, FS_ATMASK, %g2
|
|
set 1,%o4 ! set to S_READ
|
|
cmp %g2, AT_UREAD
|
|
be 3f
|
|
nop
|
|
cmp %g2, AT_SREAD
|
|
be 3f
|
|
nop
|
|
add %o4, 1, %o4
|
|
cmp %g2, AT_UWRITE
|
|
be 3f
|
|
nop
|
|
cmp %g2, AT_SWRITE
|
|
be 3f
|
|
nop
|
|
! Must be an execute cycle
|
|
add %o4, 1, %o4
|
|
|
|
! don't believe the sfar for instruction faults
|
|
mov %l1, %o2 ! since fav is zero,
|
|
! put pc as the faulting address.
|
|
!
|
|
3:
|
|
6: wr %l0, PSR_ET, %psr !enable traps (no priority change)
|
|
/*
|
|
* %o0, %o1 is setup by SYS_TRAP,
|
|
* %o2 from fav/%l1 (pc), %o3 from fsr,
|
|
* %o4 from looking at fsr
|
|
*/
|
|
|
|
mov %l4, %o0 !trap(t, rp, addr, fsr, rw)
|
|
|
|
call _trap ! C Trap handler
|
|
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
|
|
!
|
|
! first sixteen entries are for the sixteen
|
|
! levels of hardware interrupt.
|
|
#ifdef MULTIPROCESSOR
|
|
! the lock has been acquired before we call
|
|
! for hardware level 1 through 13 service;
|
|
! if acquisition failed, the holder of the
|
|
! lock will see the pending interrupt when
|
|
! he lowers his SPL.
|
|
#endif MULTIPROCESSOR
|
|
! the next sixteen entries are for the
|
|
! sixteen levels of software interrupt.
|
|
#ifdef MULTIPROCESSOR
|
|
! the lock has been acquired before we call
|
|
! for software level 1 through 9* service;
|
|
! if acquisition failed, we forward it to
|
|
! the current holder of the kernel lock.
|
|
! (* "9" can be changed in klock_asm.s)
|
|
#endif MULTIPROCESSOR
|
|
.global _int_vector
|
|
_int_vector:
|
|
/* first 16 words: hard interrupt service */
|
|
.word level0 ! level 0, should not happen
|
|
.word level1 ! level 1
|
|
.word level2 ! level 2, sbus/vme level 1
|
|
.word level3 ! level 3, sbus/vme level 2
|
|
.word level4 ! level 4, onboard scsi
|
|
.word level5 ! level 5, sbus/vme level 3
|
|
.word level6 ! level 6, onboard ether
|
|
.word level7 ! level 7, sbus/vme level 4
|
|
.word level8 ! level 8, onboard video
|
|
.word level9 ! level 9, sbus/vme level 5, module interrupt
|
|
.word level10 ! level 10, normal clock
|
|
.word level11 ! level 11, sbus/vme level 6, floppy
|
|
.word level12 ! level 12, serial i/o
|
|
.word level13 ! level 13, sbus/vme/vme level 7, audio
|
|
.word level14 ! level 14, high level clock
|
|
.word level15 ! level 15, memory error
|
|
/* second 16 words: soft interrupt service */
|
|
.word softlvl0 ! level 0, should not happen.
|
|
.word softlvl1 ! level 1, softclock interrupts
|
|
.word softlvl2 ! level 2
|
|
.word softlvl3 ! level 3
|
|
.word softlvl4 ! level 4, floppy and audio soft ints
|
|
.word softlvl5 ! level 5
|
|
.word softlvl6 ! level 6, serial driver soft interrupt
|
|
.word softlvl7 ! level 7
|
|
.word softlvl8 ! level 8
|
|
.word softlvl9 ! level 9
|
|
.word softlvl10 ! level 10
|
|
.word softlvl11 ! level 11
|
|
.word softlvl12 ! level 12
|
|
.word softlvl13 ! level 13
|
|
.word softlvl14 ! level 14
|
|
.word softlvl15 ! level 15
|
|
|
|
#ifdef VME
|
|
/*
|
|
* 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 0, _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 isc0 | isc1 | isc2 | isc3
|
|
ERRV; ERRV; ERRV; ERRV ! 0x50 - 0x53 isc4 | isc5 | isc6 | isc7
|
|
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
|
|
#endif VME
|
|
|
|
/*
|
|
* names of vectored interrupt devices -- for vmstat.
|
|
* Names must correspond to "VEC_MIN" thru "VEC_MAX".
|
|
*/
|
|
|
|
.seg "data"
|
|
.globl _intrnames
|
|
.globl _eintrnames
|
|
_intrnames:
|
|
.asciz "sc0", "sc1", "sc2", "sc3" ! 0x40 - 0x43 sc0 | sc?
|
|
.asciz "xdc0", "xdc1", "xdc2", "xdc3" ! 0x44 - 0x47 xdc0 | xdc1 | xdc2 | xdc3
|
|
.asciz "xyc0", "xyc1", "xyc2", "xyc3" ! 0x48 - 0x4B xyc0 | xyc1 | xyc?
|
|
.asciz "isc0", "isc1", "isc2", "isc3" ! 0x4C - 0x4F isc0 | isc1 | isc2 | isc3
|
|
.asciz "isc4", "isc5", "isc6", "isc7" ! 0x50 - 0x53 isc4 | isc5 | isc6 | isc7
|
|
.asciz "?disk0", "?disk1", "?disk2", "?disk3" ! 0x54 - 0x57 future disk controllers
|
|
.asciz "?disk0", "?disk1", "?disk2", "?disk3" ! 0x58 - 0x5B future disk controllers
|
|
.asciz "?disk0", "?disk1", "?disk2", "?disk3" ! 0x5C - 0x5F future disk controllers
|
|
.asciz "tm0", "tm1", "tm2", "tm3" ! 0x60 - 0x63 tm0 | tm1 | tm?
|
|
.asciz "xtc0", "xtc1", "xtc2", "xtc3" ! 0x64 - 0x67 xtc0 | xtc1 | xtc?
|
|
.asciz "?tape0", "?tape1", "?tape2", "?tape3" ! 0x68 - 0x6B future tape controllers
|
|
.asciz "?tape0", "?tape1", "?tape2", "?tape3" ! 0x6C - 0x6F future tape controllers
|
|
.asciz "ec0", "ec1", "ec2", "ec3" ! 0x70 - 0x73 ec?
|
|
.asciz "ie0", "ie1", "ie2", "ie3" ! 0x74 - 0x77 ie0 | ie1 | ie2 | ie3
|
|
.asciz "fddi0", "fddi1", "fddi2", "fddi3" ! 0x78 - 0x7B fddi0 | fddi1 | fddi2 | fddi3
|
|
.asciz "ie4", "?ether1", "?ether2", "?ether3" ! 0x7C - 0x7F ie4 | future ethernet devices
|
|
.asciz "vpc0", "vpc1", "vpc2", "vpc3" ! 0x80 - 0x83 vpc0 | vpc1 | vpc?
|
|
.asciz "vp0", "vp1", "vp2", "vp3" ! 0x84 - 0x87 vp?
|
|
.asciz "mti0", "mti1", "mti2", "mti3" ! 0x88 - 0x8B mti0 | mti1 | mti2 | mti3
|
|
.asciz "SCP", "SCP", "SCP", "SCP" ! 0x8C - 0x8F SunLink SCP (Systech DCP-8804)
|
|
.asciz "zs0", "zs0", "zs0", "zs0" ! 0x90 - 0x93 Sun-3 zs0 (8 even vectors)
|
|
.asciz "zs1", "zs1", "zs1", "zs1" ! 0x94 - 0x97 Sun-3 zs1 (8 odd vectors)
|
|
.asciz "zs0", "zs0", "zs0", "zs0" ! 0x98 - 0x9B Sun-3 zs0 (8 even vectors)
|
|
.asciz "zs1", "zs1", "zs1", "zs1" ! 0x9C - 0x9F Sun-3 zs1 (8 odd vectors)
|
|
.asciz "?serial0", "?serial1", "?serial2", "?serial3" ! 0xA0 - 0xA3 future serial
|
|
.asciz "pc0", "pc1", "pc2", "pc3" ! 0xA4 - 0xA7 pc0 | pc1 | pc2 | pc3
|
|
.asciz "cgtwo0", "?frbuf", "?frbuf", "?frbuf" ! 0xA8 - 0xAB cg2 | future frame buffers
|
|
.asciz "gpone0", "?gp", "?gp", "?gp" ! 0xAC - 0xAF gp1 | future graphics processors
|
|
.asciz "sky0", "???", "???", "???" ! 0xB0 - 0xB3 sky0 | ?
|
|
.asciz "slchan", "slchan", "slchan", "slchan" ! 0xB4 - 0xB7 SunLink / channel attach
|
|
.asciz "tbi0", "tbi1", "tbi2", "tbi3" ! 0xB8 - 0xBB (token bus) tbi0 | tbi1 | ?
|
|
.asciz "?Sun", "?Sun", "?Sun", "?Sun" ! 0xBC - 0xBF Reserved for Sun
|
|
.asciz "?Sun", "?Sun", "?Sun", "?Sun" ! 0xC0 - 0xC3 Reserved for Sun
|
|
.asciz "?Sun", "?Sun", "?Sun", "?Sun" ! 0xC4 - 0xC7 Reserved for Sun
|
|
.asciz "?User", "?User", "?User", "?User" ! 0xC8 - 0xCB Reserved for User
|
|
.asciz "?User", "?User", "?User", "?User" ! 0xCC - 0xCF Reserved for User
|
|
.asciz "?User", "?User", "?User", "?User" ! 0xD0 - 0xD3 Reserved for User
|
|
.asciz "?User", "?User", "?User", "?User" ! 0xD4 - 0xD7 Reserved for User
|
|
.asciz "?User", "?User", "?User", "?User" ! 0xD8 - 0xDB Reserved for User
|
|
.asciz "?User", "?User", "?User", "?User" ! 0xDC - 0xDF Reserved for User
|
|
.asciz "?User", "?User", "?User", "?User" ! 0xE0 - 0xE3 Reserved for User
|
|
.asciz "?User", "?User", "?User", "?User" ! 0xE4 - 0xE7 Reserved for User
|
|
.asciz "?User", "?User", "?User", "?User" ! 0xE8 - 0xEB Reserved for User
|
|
.asciz "?User", "?User", "?User", "?User" ! 0xEC - 0xEF Reserved for User
|
|
.asciz "?User", "?User", "?User", "?User" ! 0xF0 - 0xF3 Reserved for User
|
|
.asciz "?User", "?User", "?User", "?User" ! 0xF4 - 0xF7 Reserved for User
|
|
.asciz "?User", "?User", "?User", "?User" ! 0xF8 - 0xFB Reserved for User
|
|
.asciz "?User", "?User", "?User", "?User" ! 0xFC - 0xFF Reserved for 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 * (VEC_MAX - VEC_MIN + 1)
|
|
_eintrcnt:
|
|
|
|
.seg "text"
|
|
.align 4
|
|
|
|
.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.
|
|
#ifdef MULTIPROCESSOR
|
|
/*
|
|
* MULTIPROCESSOR kernels move the intstack up into
|
|
* the percpu area, which is after -- not before --
|
|
* the various U areas. So, also put us onto the int
|
|
* stack if we are less than "intstack".
|
|
*/
|
|
set intstack, %l5 ! on interrupt stack?
|
|
cmp %l5, %sp
|
|
bgu,a 1f
|
|
sub %g1, SA(MINFRAME), %sp ! get on it.
|
|
#endif MULTIPROCESSOR
|
|
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
|
|
set IR_ENA_INT, %o0
|
|
call _set_intmask ! disable interrupts
|
|
!!! %%% this is a bug, but before we fix it
|
|
!!! %%% we need to figure out when to turn
|
|
!!! %%% interrupt enable back on.
|
|
mov 0, %o1 ! by turning on the ENA bit.
|
|
!!! mov 1, %o1 ! by turning on the ENA bit.
|
|
2:
|
|
!
|
|
! check hard/soft interrupt info.
|
|
! if soft pending, add 16 to
|
|
! the interrupt level to put us in
|
|
! the range for soft interrupts,
|
|
! else if hard pending, just go on.
|
|
! else if neither pending, then
|
|
! someone took the interrupt away;
|
|
! this happens (but rarely).
|
|
!
|
|
GETCPU(%o0) ! get cpu index 0..3
|
|
sll %o0, 12, %o0 ! shift to +0x1000 per MID
|
|
set V_INT_REGS, %g1
|
|
add %g1, %o0, %g1 ! will need this base addr later, too.
|
|
ld [%g1], %o0 ! get module int pending reg
|
|
set 0x10000, %o1
|
|
sll %o1, %l4, %o1 ! calculate softint bit
|
|
andcc %o0, %o1, %g0
|
|
bne,a 0f ! if (softint is set)
|
|
add %l6, 64, %l6 ! use the softint vector table;
|
|
srl %o1, 16, %o1 ! else { calculate hardint bit
|
|
andcc %o0, %o1, %g0 ! if (hardint is not set) {
|
|
beq,a sys_rtt ! return via sys_rtt; (fix %sp)
|
|
mov %l7, %sp ! } /* is the fix %sp needed? */
|
|
0: ! }
|
|
|
|
!
|
|
! For some interrupts, we need to ack the
|
|
! fact that we got it in the MIPR.
|
|
!
|
|
set 0xFFFF8000, %l3 ! ints we have to ack
|
|
andcc %o1, %l3, %g0 ! do we ack this int?
|
|
bnz,a 0f ! if we do,
|
|
st %o1, [%g1+4] ! ack the int
|
|
0:
|
|
bnz,a 0f ! if we do,
|
|
ld [%g1], %g0 ! interlock MIPR to avoid repeated ints
|
|
0:
|
|
|
|
#ifdef MULTIPROCESSOR
|
|
!
|
|
! See if we are supposed to handle the interrupt
|
|
! on this cpu. We may need to acquire the klock, or
|
|
! forward the interrupt to the klock owner.
|
|
!
|
|
call _klock_intlock
|
|
mov %o1, %o0 ! parameter is ITR bit
|
|
tst %o0 ! returns zero if
|
|
bz 0f ! we are to do this int
|
|
nop
|
|
|
|
mov %l0, %psr ! set level (IU bug)
|
|
wr %l0, PSR_ET, %psr ! enable traps
|
|
b sys_rtt ! we are to ignore this int.
|
|
mov %l7, %sp ! reset stack pointer (???)
|
|
0:
|
|
#endif MULTIPROCESSOR
|
|
|
|
!
|
|
! Get handler address for level.
|
|
!
|
|
set _int_vector, %g1
|
|
ld [%g1 + %l6], %l3 ! grab vector
|
|
|
|
!
|
|
! Read System Interrupt Pending Register
|
|
!
|
|
sethi %hi(V_INT_REGS+0x4000), %l4
|
|
ld [%l4+%lo(V_INT_REGS+0x4000)], %l4
|
|
|
|
!
|
|
! 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 ! psr delay
|
|
call %l3 ! interrupt handler
|
|
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)]
|
|
!
|
|
! Device drivers are not aware that it takes time
|
|
! between when they write to a device register,
|
|
! and when the write takes effect, so the interrupt
|
|
! may still be pending. That is why we flush
|
|
! the M-to-S and VME write buffers unconditionally.
|
|
! Flushing the buffers conditionally by checking the
|
|
! system interrupt pending register cannot guarantee that
|
|
! ALL device stores have indeed reached the device.
|
|
!
|
|
call _flush_poke_writebuffers
|
|
nop
|
|
b sys_rtt ! restore previous stack pointer
|
|
mov %l7, %sp ! reset stack pointer
|
|
/* end interrupt */
|
|
|
|
/*
|
|
* Spurious trap... 'should not happen'
|
|
* called only from level-0 interrupt
|
|
* was "_spurious"
|
|
*/
|
|
level0:
|
|
softlvl0:
|
|
set 1f, %o0
|
|
call _printf
|
|
nop
|
|
b,a int_rtt
|
|
.seg "data"
|
|
1: .asciz "level zero interrupt!"
|
|
.seg "text"
|
|
|
|
/*
|
|
* Macros for autovectored interrupts.
|
|
*/
|
|
|
|
/*
|
|
* SYNCWB: synchronize write buffers
|
|
*
|
|
* Sun-4M requires that we synchronize write buffers
|
|
* before dispatching to device drivers, so they
|
|
* do not need to know to do this. Reading the
|
|
* M-to-S Async Fault Status Register three times
|
|
* is sufficient.
|
|
*
|
|
*/
|
|
#define SYNCWB \
|
|
call _flush_writebuffers ;\
|
|
nop
|
|
|
|
/*
|
|
* NSSET: set up parameters for the call to not_serviced
|
|
* for this interrupt level to be "SPARC Level N"; if we
|
|
* identify the interrupt as being from OBIO, SBUS, or VME
|
|
* then we will change these registers accordingly.
|
|
*/
|
|
#define NSSET(LEVEL) \
|
|
set _level/**/LEVEL/**/_spurious, %l0 ;\
|
|
set LEVEL, %l1 ;\
|
|
set _busname_vec, %l2
|
|
|
|
/*
|
|
* IOINTR: Generic hardware interrupt.
|
|
* branch to int_rtt after successful processing
|
|
* if nobody in list claims interrupt,
|
|
* report spurious and branch to int_rtt.
|
|
* NOTE: parameters for spurious report
|
|
* may have already been filled in by SBINTR,
|
|
* OBINTR, or VBINTR in %l0,%l1,%l2.
|
|
*/
|
|
#define IOINTR(LEVEL) \
|
|
set _level/**/LEVEL, %l5 /* get vector ptr */;\
|
|
1: ld [%l5 + AV_VECTOR], %g1 /* get routine address */;\
|
|
tst %g1 /* end of vector? */ ;\
|
|
bz 2f /* if it is, */ ;\
|
|
nop /* report spurious */ ;\
|
|
call %g1 /* go there */ ;\
|
|
nop ;\
|
|
tst %o0 /* success? */ ;\
|
|
bz,a 1b /* no, try next one */ ;\
|
|
add %l5, AUTOVECSIZE, %l5 /* delay slot, find next */ ;\
|
|
ld [%l5 + AV_INTCNT], %g1 /* increment counter */ ;\
|
|
inc %g1 ;\
|
|
st %g1, [%l5 + AV_INTCNT] ;\
|
|
sethi %hi(_level/**/LEVEL/**/_spurious), %g1 ;\
|
|
b int_rtt /* done */ ;\
|
|
clr [%g1 + %lo(_level/**/LEVEL/**/_spurious)] ;\
|
|
2: /* spurious interrupt */ ;\
|
|
mov %l0, %o0 ;\
|
|
mov %l1, %o1 ;\
|
|
call _not_serviced ;\
|
|
mov %l2, %o2 ;\
|
|
b,a int_rtt
|
|
|
|
/*
|
|
* OBINTR: onboard interrupt
|
|
* branch to int_rtt after successful processing
|
|
* drop out bottom if nobody in list claims interrupt.
|
|
*/
|
|
|
|
#define OBINTR(LEVEL,MASK) \
|
|
set MASK, %l5 ;\
|
|
andcc %l4, %l5, %g0 /* see if masked bit(s) set */ ;\
|
|
bz 3f /* if not, skip around. */ ;\
|
|
.empty /* next safe in trailer */ ;\
|
|
set _olvl/**/LEVEL, %l5 /* get vector ptr */ ;\
|
|
1: ld [%l5 + AV_VECTOR], %g1 /* get routine address */ ;\
|
|
tst %g1 /* end of vector? */ ;\
|
|
bz 2f /* if it is, */ ;\
|
|
nop /* report spurious */ ;\
|
|
call %g1 /* go there */ ;\
|
|
nop ;\
|
|
tst %o0 /* success? */ ;\
|
|
bz,a 1b /* no, try next one */ ;\
|
|
add %l5, AUTOVECSIZE, %l5 /* delay slot, step to next */ ;\
|
|
ld [%l5 + AV_INTCNT], %g1 /* increment interrupt ctr */ ;\
|
|
inc %g1 ;\
|
|
st %g1, [%l5 + AV_INTCNT] ;\
|
|
sethi %hi(_olvl/**/LEVEL/**/_spurious), %g1 ;\
|
|
b int_rtt /* all done! */ ;\
|
|
clr [%g1 + %lo(_olvl/**/LEVEL/**/_spurious)] ;\
|
|
2: /* unclaimed onboard */ ;\
|
|
set _olvl/**/LEVEL/**/_spurious, %l0 ;\
|
|
set LEVEL, %l1 ;\
|
|
set _busname_ovec, %l2 ;\
|
|
3: /* not onboard */
|
|
|
|
/*
|
|
* SBINTR: SBus interrupt
|
|
* if no sbus int pending, drop out bottom
|
|
* if anyone on sbus list claims it
|
|
* branch to int_rtt
|
|
* set parameters for "spurious sbus level LEVEL" report
|
|
*/
|
|
#define SBINTR(LEVEL) \
|
|
srl %l4, 7, %g1 ;\
|
|
andcc %g1, 1<<(LEVEL-1), %g0 /* see if sbus int pending */ ;\
|
|
bz 3f /* if not, skip around. */ ;\
|
|
.empty /* next safe in trailer */ ;\
|
|
set _slvl/**/LEVEL, %l5 /* get vector ptr */ ;\
|
|
1: /* try another entry */ ;\
|
|
ld [%l5 + AV_VECTOR], %g1 /* get routine address */ ;\
|
|
tst %g1 /* end of vector? */ ;\
|
|
bz 2f /* if it is, */ ;\
|
|
nop /* report spurious */ ;\
|
|
call %g1 /* go there */ ;\
|
|
nop ;\
|
|
tst %o0 /* success? */ ;\
|
|
bz,a 1b /* no, try next one */ ;\
|
|
add %l5, AUTOVECSIZE, %l5 /* delay slot, find next */ ;\
|
|
ld [%l5 + AV_INTCNT], %g1 /* increment interrupt ctr */ ;\
|
|
inc %g1 ;\
|
|
st %g1, [%l5 + AV_INTCNT] ;\
|
|
sethi %hi(_slvl/**/LEVEL/**/_spurious), %g1 ;\
|
|
b int_rtt /* REALLY all done! */ ;\
|
|
clr [%g1 + %lo(_slvl/**/LEVEL/**/_spurious)] ;\
|
|
2: /* unclaimed sbus */ ;\
|
|
set _slvl/**/LEVEL/**/_spurious, %l0 ;\
|
|
set LEVEL, %l1 ;\
|
|
set _busname_svec, %l2 ;\
|
|
3: /* not an SBus interrupt */
|
|
|
|
#ifdef VME
|
|
/*
|
|
* VBINTR: VME interrupt
|
|
* if no vme int pending, drop out bottom
|
|
* get vme vector
|
|
* if someone interested in this vector, call him;
|
|
* assume he handled it, branch to int_rtt
|
|
* if anyone on vme list claims it, branch to int_rtt
|
|
* set parameters for "spurious vme level LEVEL" report
|
|
*/
|
|
#define VBINTR(LEVEL) \
|
|
andcc %l4, 1<<(LEVEL-1), %g0 /* see if vme int pending */ ;\
|
|
bz 3f /* if not, skip around. */ ;\
|
|
sub %g0, 1, %o1 /* if load traps, we use this value. */ ;\
|
|
sethi %hi(V_VMEBUS_VEC+(LEVEL<<1)+1), %l5 ;\
|
|
ldub [%l5+%lo(V_VMEBUS_VEC+(LEVEL<<1)+1)], %o1 /* ack int */ ;\
|
|
cmp %o1, VEC_MAX /* if vector number too high, */ ;\
|
|
bg 0f /* ... not a vectored interrupt. */ ;\
|
|
subcc %o1, VEC_MIN, %o2 /* tables start at VEC_MIN */ ;\
|
|
bl 0f /* vector too low, ignore. */ ;\
|
|
sll %o2, 3, %g1 /* eight bytes per vector */ ;\
|
|
set _vme_vector, %l5 ;\
|
|
add %g1, 4, %g2 /* offset for handler argument */ ;\
|
|
ld [%l5+%g1], %g1 /* get function that will handle it */ ;\
|
|
tst %g1 /* NULL == nobody claimed this vector */ ;\
|
|
beq 0f /* if so, just wander level list */ ;\
|
|
ld [%l5+%g2], %o0 /* get address of handler arg */ ;\
|
|
ld [%o0], %o0 /* ... get handler argument */ ;\
|
|
sll %o2, 2, %g5 /* four bytes per count */ ;\
|
|
set _intrcnt, %l5 /* locate vec-int table */ ;\
|
|
ld [%l5+%g5], %g4 ;\
|
|
inc %g4 /* update count for this vector */ ;\
|
|
st %g4, [%l5+%g5] ;\
|
|
call %g1 /* call vector handler */ ;\
|
|
nop ;\
|
|
b 1f /* .. clean up and return */ ;\
|
|
sethi %hi(_vlvl/**/LEVEL/**/_spurious), %g1 ;\
|
|
0: /* no vme vector, try auto chain */ ;\
|
|
set _vlvl/**/LEVEL, %l5 /* get vector ptr */ ;\
|
|
ld [%l5 + AV_VECTOR], %g1 /* get routine address */ ;\
|
|
tst %g1 /* end of vector? */ ;\
|
|
bz 2f /* if it is, */ ;\
|
|
nop /* report spurious */ ;\
|
|
call %g1 /* go there */ ;\
|
|
nop ;\
|
|
tst %o0 /* success? */ ;\
|
|
bz,a 0b /* no, try next one */ ;\
|
|
add %l5, AUTOVECSIZE, %l5 /* delay slot, find next */ ;\
|
|
ld [%l5 + AV_INTCNT], %g1 /* increment interrupt ctr */ ;\
|
|
inc %g1 ;\
|
|
st %g1, [%l5 + AV_INTCNT] ;\
|
|
sethi %hi(_vlvl/**/LEVEL/**/_spurious), %g1 ;\
|
|
1: /* int handled */ ;\
|
|
b int_rtt /* REALLY all done. */ ;\
|
|
clr [%g1 + %lo(_vlvl/**/LEVEL/**/_spurious)] ;\
|
|
2: /* unclaimed vme */ ;\
|
|
set _vlvl/**/LEVEL/**/_spurious, %l0 ;\
|
|
set LEVEL, %l1 ;\
|
|
set _busname_vvec, %l2 ;\
|
|
3: /* not a vme interrupt */
|
|
#endif VME
|
|
|
|
/*
|
|
* SOINTR: software interrupt
|
|
* call ALL attached routines
|
|
* branch to int_rtt when done.
|
|
* NOTE: scans the "_xlvl" list AND the "_level" list.
|
|
* NOTE: silently ignore spurious soft ints.
|
|
*/
|
|
#define SOINTR(LEVEL) \
|
|
set _xlvl/**/LEVEL, %l5 /* get vector ptr */ ;\
|
|
1: ld [%l5 + AV_VECTOR], %g1 /* get routine address */ ;\
|
|
tst %g1 /* all done? */ ;\
|
|
beq 2f ;\
|
|
nop ;\
|
|
call %g1 /* go there */ ;\
|
|
add %l5, AUTOVECSIZE, %l5 /* delay slot, find next */ ;\
|
|
tst %o0 ;\
|
|
beq 1b ;\
|
|
ld [%l5 + AV_INTCNT], %g1 /* increment interrupt ctr */ ;\
|
|
st %g1, [%l5 + AV_INTCNT] ;\
|
|
b 1b /* go get another */ ;\
|
|
nop ;\
|
|
2: set _level/**/LEVEL, %l5 /* get vector ptr */ ;\
|
|
1: ld [%l5 + AV_VECTOR], %g1 /* get routine address */ ;\
|
|
tst %g1 /* all done? */ ;\
|
|
beq 2f ;\
|
|
nop ;\
|
|
call %g1 /* go there */ ;\
|
|
add %l5, AUTOVECSIZE, %l5 /* delay slot, find next */ ;\
|
|
tst %o0 ;\
|
|
beq 1b ;\
|
|
ld [%l5 + AV_INTCNT], %g1 /* increment interrupt ctr */ ;\
|
|
st %g1, [%l5 + AV_INTCNT] ;\
|
|
b 1b /* go get another */ ;\
|
|
nop ;\
|
|
2: sethi %hi(_level/**/LEVEL/**/_spurious), %g1 ;\
|
|
clr [%g1 + %lo(_level/**/LEVEL/**/_spurious)] ;\
|
|
b,a int_rtt
|
|
|
|
#ifdef MULTIPROCESSOR
|
|
.seg "data"
|
|
.align 4
|
|
lcfile:
|
|
.asciz "__FILE__"
|
|
.align 4
|
|
.seg "text"
|
|
.align 4
|
|
|
|
#ifdef KLOCK_PARANOID
|
|
#define KLREQ \
|
|
set lcfile, %o0 ; \
|
|
set __LINE__, %o1 ; \
|
|
call _klock_require ; \
|
|
mov %g0, %o2
|
|
#endif KLOCK_PARANOID
|
|
|
|
#endif MULTIPROCESSOR
|
|
|
|
#ifndef KLREQ
|
|
#define KLREQ
|
|
#endif
|
|
|
|
softlvl1:
|
|
KLREQ
|
|
SOINTR(1)
|
|
softlvl2:
|
|
KLREQ
|
|
SOINTR(2)
|
|
softlvl3:
|
|
KLREQ
|
|
SOINTR(3)
|
|
softlvl4:
|
|
KLREQ
|
|
SOINTR(4)
|
|
softlvl5:
|
|
KLREQ
|
|
SOINTR(5)
|
|
softlvl6:
|
|
KLREQ
|
|
SOINTR(6)
|
|
softlvl7:
|
|
KLREQ
|
|
SOINTR(7)
|
|
softlvl8:
|
|
KLREQ
|
|
SOINTR(8)
|
|
softlvl9:
|
|
KLREQ
|
|
SOINTR(9)
|
|
softlvl10:
|
|
SOINTR(10)
|
|
softlvl11:
|
|
SOINTR(11)
|
|
softlvl12:
|
|
SOINTR(12)
|
|
softlvl13:
|
|
SOINTR(13)
|
|
softlvl14:
|
|
SOINTR(14)
|
|
softlvl15:
|
|
#ifdef PC_prom_mailbox
|
|
!
|
|
! given a prom mailbox message, branch to the
|
|
! proper prom service (via the prom library).
|
|
! prom services will return to the above label.
|
|
!
|
|
set _prom_mailbox, %o0 ! find our mailbox
|
|
andn %o0, 0xFFF, %o1 ! Truncate to page base
|
|
lda [%o1]ASI_FLPR, %o1
|
|
and %o1, 3, %o1
|
|
subcc %o1, 2, %o1 ! if mbox ptr mapped in,
|
|
bne 1f
|
|
nop
|
|
|
|
ld [%o0], %o0 ! and mbox ptr not null,
|
|
tst %o0
|
|
beq 1f
|
|
nop
|
|
|
|
ldub [%o0], %o0 ! get mailbox message
|
|
|
|
cmp %o0, 0xFB ! 0xFB: someone did an op_exit
|
|
bne 2f
|
|
nop
|
|
call _prom_stopcpu ! prom wants us to "stop"
|
|
mov %g0, %o0
|
|
b 1f
|
|
nop
|
|
2:
|
|
cmp %o0, 0xFC ! 0xFC: someone did an op_enter
|
|
bne 2f
|
|
nop
|
|
call _prom_idlecpu ! prom wants us to "idle"
|
|
mov %g0, %o0
|
|
b 1f
|
|
nop
|
|
2:
|
|
cmp %o0, 0xFD ! 0xFD: someone hit a breakpoint
|
|
bne 2f
|
|
nop
|
|
call _prom_idlecpu ! prom wants us to "idle"
|
|
mov %g0, %o0
|
|
b 1f
|
|
nop
|
|
2:
|
|
cmp %o0, 0xFE ! 0xFE: someone watchdogged
|
|
bne 2f
|
|
nop
|
|
call _prom_stopcpu ! prom wants us to "stop"
|
|
mov %g0, %o0
|
|
b 1f
|
|
nop
|
|
2:
|
|
!! add more prom mailbox codes here.
|
|
|
|
1: ! no prom calls, or prom call done.
|
|
|
|
#endif PC_prom_mailbox
|
|
#ifdef MULTIPROCESSOR
|
|
!
|
|
! Let the crosscall mechanism have a crack at its data.
|
|
!
|
|
call _xc_serv
|
|
nop
|
|
#endif MULTIPROCESSOR
|
|
!
|
|
! Do not count this interrupt in cnt.v_intr
|
|
!
|
|
b sys_rtt
|
|
mov %l7, %sp
|
|
|
|
/*
|
|
* Level 1 interrupts, from out of the blue.
|
|
*/
|
|
level1:
|
|
KLREQ
|
|
SYNCWB
|
|
NSSET(1)
|
|
IOINTR(1)
|
|
|
|
/*
|
|
* Level 2 interrupts, from Sbus level 1, or VMEbus level 1.
|
|
*/
|
|
level2:
|
|
KLREQ
|
|
SYNCWB
|
|
NSSET(2)
|
|
SBINTR(1)
|
|
#ifdef VME
|
|
VBINTR(1)
|
|
#endif VME
|
|
IOINTR(2)
|
|
|
|
/*
|
|
* Level 3 interrupts, from Sbus level 2, or VMEbus level 2
|
|
*/
|
|
level3:
|
|
KLREQ
|
|
SYNCWB
|
|
NSSET(3)
|
|
SBINTR(2)
|
|
#ifdef VME
|
|
VBINTR(2)
|
|
#endif VME
|
|
IOINTR(3)
|
|
|
|
/*
|
|
* Level 4 interrupts, from onboard SCSI
|
|
*/
|
|
level4:
|
|
KLREQ
|
|
SYNCWB
|
|
NSSET(4)
|
|
OBINTR(4, SIR_SCSI)
|
|
IOINTR(4)
|
|
|
|
/*
|
|
* Level 5 interrupts, from Sbus/VME level 3
|
|
*/
|
|
level5:
|
|
KLREQ
|
|
SYNCWB
|
|
NSSET(5)
|
|
SBINTR(3)
|
|
#ifdef VME
|
|
VBINTR(3)
|
|
#endif VME
|
|
IOINTR(5)
|
|
|
|
/*
|
|
* Level 6 interrupts, from onboard ethernet
|
|
*/
|
|
level6:
|
|
KLREQ
|
|
SYNCWB
|
|
NSSET(6)
|
|
OBINTR(6, SIR_ETHERNET)
|
|
IOINTR(6)
|
|
|
|
/*
|
|
* Level 7 interrupts, from SBus/VME level 4
|
|
*/
|
|
level7:
|
|
KLREQ
|
|
SYNCWB
|
|
NSSET(7)
|
|
SBINTR(4)
|
|
#ifdef VME
|
|
VBINTR(4)
|
|
#endif VME
|
|
IOINTR(7)
|
|
|
|
/*
|
|
* Level 8 interrupts, from onboard video
|
|
*/
|
|
level8:
|
|
KLREQ
|
|
SYNCWB
|
|
NSSET(8)
|
|
OBINTR(8, SIR_VIDEO)
|
|
IOINTR(8)
|
|
|
|
/*
|
|
* Level 9 interrupts, from SBus/VME level 5 and module interrupt
|
|
*/
|
|
level9:
|
|
KLREQ
|
|
SYNCWB
|
|
NSSET(9)
|
|
OBINTR(9, SIR_MODULEINT)
|
|
SBINTR(5)
|
|
#ifdef VME
|
|
VBINTR(5)
|
|
#endif VME
|
|
IOINTR(9)
|
|
|
|
/*
|
|
* Level 10 interrupts, from onboard clock
|
|
*
|
|
* REORGANIZATION NOTE:
|
|
* level 10 hardint should be handled
|
|
* by the "real time clock driver", which
|
|
* should be linked into the level-10
|
|
* onboard vector. In turn, it should
|
|
* trigger the LED update either by directly
|
|
* calling the "led display driver" or by
|
|
* pending a level-10 software interrupt.
|
|
* The softint route is more general.
|
|
* Making these separate device modules
|
|
* will make it easier to work with possible
|
|
* alternate implementations (like different
|
|
* numbers of LEDs, or different clock chips).
|
|
*
|
|
* Not Yet, tho.
|
|
*/
|
|
#if 0
|
|
level10:
|
|
KLREQ
|
|
NSSET(10)
|
|
OBINTR(10, SIR_REALTIME)
|
|
IOINTR(10)
|
|
#endif
|
|
|
|
/*
|
|
* Level 11 interrupts, from SBus/VME level 6 or floppy
|
|
* NOTE: FLOPPY DRIVER SHOULD BE MOVED FROM TRAP LEVEL, IF POSSIBLE
|
|
*/
|
|
level11:
|
|
KLREQ
|
|
SYNCWB
|
|
NSSET(11)
|
|
OBINTR(11, SIR_FLOPPY)
|
|
SBINTR(6)
|
|
#ifdef VME
|
|
VBINTR(6)
|
|
#endif VME
|
|
IOINTR(11)
|
|
|
|
/*
|
|
* Level 12 interrupts, from onboard serial ports
|
|
*/
|
|
level12:
|
|
KLREQ
|
|
SYNCWB
|
|
NSSET(12)
|
|
OBINTR(12, SIR_KBDMS|SIR_SERIAL)
|
|
IOINTR(12)
|
|
|
|
/*
|
|
* Level 13 interrupts, from SBus/VME level 7, or onboard audio
|
|
* NOTE: AUDIO DRIVER SHOULD BE MOVED FROM TRAP LEVEL, IF POSSIBLE
|
|
*/
|
|
level13:
|
|
KLREQ
|
|
SYNCWB
|
|
NSSET(13)
|
|
OBINTR(13, SIR_AUDIO)
|
|
SBINTR(7)
|
|
#ifdef VME
|
|
VBINTR(7)
|
|
#endif VME
|
|
IOINTR(13)
|
|
|
|
level14:
|
|
IOINTR(14)
|
|
|
|
level15:
|
|
/*NSSET(15)*/
|
|
call _l15_async_fault
|
|
mov %l4, %o0
|
|
b,a int_rtt
|
|
/*IOINTR(15)*/
|
|
|
|
.seg "text"
|
|
/*
|
|
* This code assumes that the real time clock interrupts 100 times
|
|
* per second, for SUN4M we call hardclock at that rate.
|
|
*/
|
|
.seg "bss"
|
|
.align 4
|
|
.globl _clk_intr
|
|
_clk_intr:
|
|
.skip 4
|
|
.seg "text"
|
|
|
|
.global level10
|
|
level10:
|
|
KLREQ
|
|
set COUNTER_ADDR+CNTR_LIMIT10, %l5 ! read limit10 reg
|
|
ld [%l5], %g1 ! to clear intr
|
|
|
|
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
|
|
#ifdef MEASURE_KERNELSTACK
|
|
ld [%l7 + MINFRAME + SP*4], %o2 ! sp /*XXX-instrument*/
|
|
#endif MEASURE_KERNELSTACK
|
|
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:
|
|
#ifndef PC_utimers
|
|
GETCPU(%l4) ! get cpu index, 0..3
|
|
sll %l4,12,%l4 ! offset 0x1000 each
|
|
set COUNTER_ADDR, %l3 ! address of counter reg.
|
|
add %l3,%l4,%l3 ! add module number
|
|
ld [%l3],%g0 ! read limit register
|
|
#else PC_utimers
|
|
sethi %hi(_utimers), %l3 ! read my timer limit register
|
|
ld [%l3 + %lo(_utimers)], %g0
|
|
#endif PC_utimers
|
|
|
|
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
|
|
|
|
/*
|
|
* 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
|
|
save %sp, -WINDOWSIZE, %sp
|
|
#if 0
|
|
save %sp, -WINDOWSIZE, %sp ! one for the road
|
|
restore ! one for the road
|
|
#endif
|
|
restore
|
|
restore
|
|
restore
|
|
restore
|
|
restore
|
|
ret
|
|
restore
|
|
|
|
/*
|
|
* flush user windows to memory.
|
|
* This is a leaf routine that only wipes %g1, %g2, and %g5.
|
|
* Some callers may depend on this behavior.
|
|
*/
|
|
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
|
|
nop
|
|
|
|
/*
|
|
* 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
|
|
sethi %hi(_nwindows), %g5
|
|
ld [%g5+%lo(_nwindows)], %g5
|
|
b 2f
|
|
dec %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
|
|
|
|
call %i0 ! go to monitor
|
|
nop
|
|
ret
|
|
restore
|
|
|
|
/*
|
|
* return the condition codes in %g1
|
|
*/
|
|
getcc:
|
|
sll %l0, 8, %g1 ! right justify condition code
|
|
srl %g1, 28, %g1
|
|
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 ; nop ; nop ! psr delay
|
|
jmp %l2 ! return, skip trap instruction
|
|
rett %l2+4
|
|
|
|
/*
|
|
* 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
|
|
st %l4, [%l5 + PCB_FLAGS]
|
|
jmp %l2 ! return, skip trap instruction
|
|
rett %l2+4
|
|
|
|
/*
|
|
* 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
|
|
|
|
/*
|
|
* Glue code for traps that should take us to the monitor/kadb if they
|
|
* occur in kernel mode, but that the kernel should handle if they occur
|
|
* in user mode.
|
|
*/
|
|
.global _kadb_tcode, _trap_ff_tcode, _trap_fe_tcode
|
|
|
|
/* tcode to replace trap vectors if kadb steals them away */
|
|
_trap_ff_tcode:
|
|
mov %psr, %l0
|
|
sethi %hi(trap_mon), %l4
|
|
jmp %l4 + %lo(trap_mon)
|
|
mov 0xff, %l4
|
|
_trap_fe_tcode:
|
|
mov %psr, %l0
|
|
sethi %hi(trap_kadb), %l4
|
|
jmp %l4 + %lo(trap_kadb)
|
|
mov 0xfe, %l4
|
|
/*
|
|
* This code assumes that:
|
|
* 1. the monitor uses trap ff to breakpoint us
|
|
* 2. kadb steals both ff and fe when we call scbsync()
|
|
* 3. kadb uses the same tcode for both ff and fe.
|
|
* XXX The monitor shouldn't use the same trap as kadb!
|
|
*/
|
|
trap_mon:
|
|
btst PSR_PS, %l0 ! test pS
|
|
bz sys_trap ! user-mode, treat as bad trap
|
|
nop
|
|
|
|
mov %l0, %psr ! restore psr
|
|
nop ! psr delay
|
|
b mon_breakpoint_vec
|
|
nop ! psr delay
|
|
|
|
trap_kadb:
|
|
btst PSR_PS, %l0 ! test pS
|
|
bz sys_trap ! user-mode, treat as bad trap
|
|
nop
|
|
|
|
mov %l0, %psr ! restore psr
|
|
nop ! psr delay
|
|
b _kadb_tcode
|
|
nop ! psr delay
|
|
|
|
.align 8 ! mon_breakpoint_vec MUST BE DOUBLE ALIGNED.
|
|
.global mon_breakpoint_vec
|
|
mon_breakpoint_vec:
|
|
SYS_TRAP(0xff) ! gets overlaid with data from prom.
|
|
_kadb_tcode:
|
|
SYS_TRAP(0xfe) ! gets overlaid with data from prom.
|
|
|
|
#if defined(SAS)
|
|
ALTENTRY(mpsas_idle_trap)
|
|
retl
|
|
t 211 ! stall here until something fun happens.
|
|
#endif
|
|
|
|
ALTENTRY(check_cpuid) ! int check_cpuid(int *exp, int *act): 0=ok
|
|
|
|
mov %psr, %g1
|
|
andn %g1, PSR_ET, %o5
|
|
mov %o5, %psr ! disable traps
|
|
nop ; nop ; nop
|
|
|
|
mov 0, %o2
|
|
|
|
set _cpuid, %o5
|
|
|
|
sethi %hi(_cache), %o3
|
|
ld [%o3 + %lo(_cache)], %o3
|
|
cmp %o3, CACHE_PAC
|
|
bge,a 2f ! PAC (viking), skip ross check
|
|
ld [%o5], %o3 ! from pac, get cpuid
|
|
|
|
lda [%g0]ASI_MOD, %o4
|
|
srl %o4, 15, %o4
|
|
and %o4, 15, %o4
|
|
cmp %o4, 15 ! if we are a Ross 604,
|
|
be 0f ! no tests needed.
|
|
nop
|
|
|
|
ld [%o5], %o4 ! from vac
|
|
set CACHE_BYTES-1, %g2
|
|
and %o5, %g2, %o3 ! modulo cache size
|
|
set PAGEMASK, %g2
|
|
and %o3, %g2, %o3 ! round down to base of page
|
|
set KERNELBASE + CACHE_BYTES, %g2
|
|
ld [%g2 + %o3], %g0 ! replace the line
|
|
ld [%o5], %o3 ! from memory
|
|
cmp %o3, %o4
|
|
bne 1f ! ERROR: flushing vac changed cpuid
|
|
mov 1, %o2
|
|
! inc %o2
|
|
|
|
2: mov %o3, %o4 ! possible tlb hit
|
|
sta %g0, [%o5]ASI_FLPR ! flush tlb
|
|
ld [%o5], %o3 ! force tlbe miss
|
|
cmp %o3, %o4
|
|
bne 1f ! ERROR: flushing tlb changed cpuid
|
|
mov 2, %o2 ! change inc to mov because VIKING
|
|
! skips the flushing vac test
|
|
! inc %o2
|
|
|
|
mov %o3, %o4
|
|
GETCPU(%o3)
|
|
cmp %o3, %o4
|
|
bne 1f ! ERROR: cpuid does not match tbr
|
|
mov 3, %o2
|
|
! inc %o2
|
|
|
|
sethi %hi(_cache), %o4
|
|
ld [%o4 + %lo(_cache)], %o4
|
|
tst %o4
|
|
bz 0f ! skip the next test if non-cached or
|
|
! Viking MBus mode.
|
|
nop
|
|
cmp %o4, CACHE_PAC
|
|
be 0f
|
|
nop
|
|
cmp %o4, CACHE_PAC_E ! if VIking CC mode, load MID from
|
|
! MXCC
|
|
be 3f
|
|
nop
|
|
|
|
mov %o3, %o4
|
|
lda [%g0]ASI_MOD, %o3
|
|
srl %o3, 15, %o3
|
|
and %o3, 3, %o3 ! cpu number from MCR.MID
|
|
cmp %o3, %o4
|
|
bne 1f ! ERROR: tbr does not match MCR.MID
|
|
mov 4, %o2
|
|
! inc %o2
|
|
b 0f
|
|
nop
|
|
|
|
3: mov %o3, %o4
|
|
set MXCC_PORT, %o3
|
|
lda [%o3]ASI_MXCC, %o3
|
|
srl %o3, 24, %o3
|
|
and %o3, 3, %o3 ! cpu number from MXCC's Mbus port reg.
|
|
cmp %o3, %o4
|
|
bne 1f ! ERROR: tbr does not match MXCC.MID
|
|
mov 4, %o2
|
|
|
|
!!! add more tests here as desired.
|
|
|
|
0: ! branch here on success
|
|
mov 0, %o2
|
|
1: ! branch here on failure
|
|
|
|
nop ; nop ; nop
|
|
mov %g1, %psr ! re-enable traps
|
|
nop ; nop ; nop
|
|
|
|
st %o3, [%o0] ! stash expected value
|
|
st %o4, [%o1] ! stash actual value
|
|
retl
|
|
mov %o2, %o0 ! return error code
|
|
|
|
/*
|
|
* Turn on or off bits in the auxiliary i/o register.
|
|
* We must lock out interrupts, since we don't have an atomic or/and to mem.
|
|
* set_auxioreg(bit, flag)
|
|
* int bit; bit mask in aux i/o reg
|
|
* int flag; 0 = off, otherwise on
|
|
*/
|
|
ALTENTRY(set_auxioreg)
|
|
mov %psr, %g2
|
|
or %g2, PSR_PIL, %g1 ! spl hi to protect aux i/o reg update
|
|
mov %g1, %psr
|
|
nop; ! psr delay
|
|
tst %o1
|
|
set AUXIO_REG, %o2 ! aux i/o register address
|
|
ldub [%o2], %g1 ! read aux i/o register
|
|
bnz,a 1f
|
|
bset %o0, %g1 ! on
|
|
bclr %o0, %g1 ! off
|
|
1:
|
|
or %g1, AUX_MBO, %g1 ! Must Be Ones
|
|
stb %g1, [%o2] ! write aux i/o register
|
|
mov %g2, %psr ! splx
|
|
nop ! psr delay
|
|
retl
|
|
nop
|
|
/* end set_auxioreg */
|
|
|
|
/*
|
|
* Trigger routine for C2 lab logi analyzer
|
|
*/
|
|
ENTRY(trigger)
|
|
|
|
set 0xE0000000, %o0
|
|
retl
|
|
lda [%o0]0x2f, %g0
|
|
|