mach_locore.s revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#if defined(lint)
#include <sys/types.h>
#include <sys/t_lock.h>
#include <sys/promif.h>
#include <sys/prom_isa.h>
#endif /* lint */
#include <sys/asm_linkage.h>
#include <sys/intreg.h>
#include <sys/mmu.h>
#include <sys/machpcb.h>
#include <sys/machtrap.h>
#include <sys/machlock.h>
#include <sys/fdreg.h>
#include <sys/vis.h>
#include <sys/traptrace.h>
#include <sys/panic.h>
#include <sys/machasi.h>
#include <sys/clock.h>
#include <vm/hat_sfmmu.h>
#if defined(lint)
#include <sys/thread.h>
#include <sys/time.h>
#else /* lint */
#include "assym.h"
!
! REGOFF must add up to allow double word access to r_tstate.
! PCB_WBUF must also be aligned.
!
#if (REGOFF & 7) != 0
#error "struct regs not aligned"
#endif
/*
* Absolute external symbols.
* On the sun4u we put the panic 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 panic 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).
*/
.seg ".data"
.global panicbuf
PROM = 0xFFE00000 ! address of prom virtual area
panicbuf = SYSBASE32 + PAGESIZE ! address of panic buffer
.type panicbuf, #object
.size panicbuf, PANICBUFSIZE
/*
* Absolute external symbol - intr_vector.
*
* With new bus structures supporting a larger number of interrupt
* numbers, the interrupt vector table, intr_vector[], has been
* moved out of kernel nucleus and allocated after panicbuf.
*/
.global intr_vector
intr_vector = SYSBASE32 + PAGESIZE + PANICBUFSIZE ! address of interrupt table
.type intr_vector, #object
.size intr_vector, MAXIVNUM * INTR_VECTOR_SIZE
/*
* The thread 0 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 if the stack overflows. We get a
* red zone below this stack for free when the kernel text is
* write protected.
*/
.global t0stack
.align 16
.type t0stack, #object
t0stack:
.skip T0STKSZ ! thread 0 stack
t0stacktop:
.size t0stack, T0STKSZ
/*
* cpu0 and its ptl1_panic stack. The cpu structure must be allocated
* on a single page for ptl1_panic's physical address accesses.
*/
.global cpu0
.align MMU_PAGESIZE
cpu0:
.type cpu0, #object
.skip CPU_ALLOC_SIZE
.size cpu0, CPU_ALLOC_SIZE
.global t0
.align PTR24_ALIGN ! alignment for mutex.
.type t0, #object
t0:
.skip THREAD_SIZE ! thread 0
.size t0, THREAD_SIZE
#ifdef TRAPTRACE
.global trap_trace_ctl
.global trap_tr0
.global trap_trace_bufsize
.global trap_freeze
.global trap_freeze_pc
.align 4
trap_trace_bufsize:
.word TRAP_TSIZE ! default trap buffer size
trap_freeze:
.word 0
.align 64
trap_trace_ctl:
.skip NCPU * TRAPTR_SIZE ! NCPU control headers
.align 16
trap_tr0:
.skip TRAP_TSIZE ! one buffer for the boot cpu
/*
* When an assertion in TRACE_PTR was failed, %pc is saved in trap_freeze_pc to
* show in which TRACE_PTR the assertion failure happened.
*/
.align 8
trap_freeze_pc:
.nword 0
#endif /* TRAPTRACE */
.align 4
.seg ".text"
#ifdef NOPROM
.global availmem
availmem:
.word 0
#endif /* NOPROM */
.align 8
_local_p1275cis:
.nword 0
#endif /* lint */
#if defined(lint)
void
_start(void)
{}
#else /* lint */
.seg ".data"
.global nwindows, nwin_minus_one, winmask
nwindows:
.word 8
nwin_minus_one:
.word 7
winmask:
.word 8
.global afsrbuf
afsrbuf:
.word 0,0,0,0
/*
* 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 p1275cis ptr in %o0 and kdi_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.
*
* NB: Above is not true for boot/stick kernel, the only thing mapped is
* the text+data+bss. The kernel is loaded directly into KERNELBASE.
*
* entry, the romvec pointer (romp) is the first argument;
* i.e., %o0.
* the bootops vector is in the third argument (%o1)
*
* Our tasks are:
* save parameters
* construct mappings for KERNELBASE (not needed for boot/stick kernel)
* hop up into high memory (not needed for boot/stick kernel)
* 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_NP(_start)
!
! Stash away our arguments in memory.
!
sethi %hi(_local_p1275cis), %g1
stn %o0, [%g1 + %lo(_local_p1275cis)]
sethi %hi(bootops), %g1
stn %o1, [%g1 + %lo(bootops)]
!
! Initialize CPU state registers
!
wrpr %g0, PSTATE_KERN, %pstate
wr %g0, %g0, %fprs
CLEARTICKNPT ! allow user rdtick
!
! Get maxwin from %ver
!
rdpr %ver, %g1
and %g1, VER_MAXWIN, %g1
!
! Stuff some memory cells related to numbers of windows.
!
sethi %hi(nwin_minus_one), %g2
st %g1, [%g2 + %lo(nwin_minus_one)]
inc %g1
sethi %hi(nwindows), %g2
st %g1, [%g2 + %lo(nwindows)]
dec %g1
mov -2, %g2
sll %g2, %g1, %g2
sethi %hi(winmask), %g4
st %g2, [%g4 + %lo(winmask)]
!
! save a pointer to obp's tba for later use by kmdb
!
rdpr %tba, %g1
set boot_tba, %g2
stx %g1, [%g2]
!
! copy obp's breakpoint trap entry to obp_bpt
!
rdpr %tba, %g1
set T_SOFTWARE_TRAP | ST_MON_BREAKPOINT, %g2
sll %g2, 5, %g2
or %g1, %g2, %g1
set obp_bpt, %g2
ldx [%g1], %g3
stx %g3, [%g2]
flush %g2
ldx [%g1 + 8], %g3
stx %g3, [%g2 + 8]
flush %g2 + 8
ldx [%g1 + 16], %g3
stx %g3, [%g2 + 16]
flush %g2 + 16
ldx [%g1 + 24], %g3
stx %g3, [%g2 + 24]
flush %g2 + 24
!
! Initialize thread 0's stack.
!
set t0stacktop, %g1 ! setup kernel stack pointer
sub %g1, SA(KFPUSIZE+GSR_SIZE), %g2
and %g2, 0x3f, %g3
sub %g2, %g3, %o2
sub %o2, SA(MPCBSIZE) + STACK_BIAS, %sp
!
! Initialize global thread register.
!
set t0, THREAD_REG
!
! Fill in enough of the cpu structure so that
! the wbuf management code works. Make sure the
! boot cpu is inserted in cpu[] based on cpuid.
!
CPU_INDEX(%g2, %g1)
sll %g2, CPTRSHIFT, %g2 ! convert cpuid to cpu[] offset
set cpu0, %o0 ! &cpu0
set cpu, %g1 ! &cpu[]
stn %o0, [%g1 + %g2] ! cpu[cpuid] = &cpu0
stn %o0, [THREAD_REG + T_CPU] ! threadp()->t_cpu = cpu[cpuid]
stn THREAD_REG, [%o0 + CPU_THREAD] ! cpu[cpuid]->cpu_thread = threadp()
! We do NOT need to bzero our BSS...boot has already done it for us.
! Just need to reference edata so that we don't break /dev/ksyms
set edata, %g0
!
! Call mlsetup with address of prototype user registers.
! and p1275cis pointers.
sethi %hi(_local_p1275cis), %o1
ldn [%o1 + %lo(_local_p1275cis)], %o1
call mlsetup
add %sp, REGOFF + STACK_BIAS, %o0
#if (REGOFF != MPCB_REGS)
#error "hole in struct machpcb between frame and regs?"
#endif
!
! Now call main. We will return as process 1 (init).
!
call main
nop
!
! Main should never return.
!
set .mainretmsg, %o0
call panic
nop
SET_SIZE(_start)
.mainretmsg:
.asciz "main returned"
.align 4
#endif /* lint */
/*
* Generic system trap handler.
*
* Some kernel trap handlers save themselves from buying a window by
* borrowing some of sys_trap's unused locals. %l0 thru %l3 may be used
* for this purpose, as user_rtt and priv_rtt do not depend on them.
* %l4 thru %l7 should NOT be used this way.
*
* Entry Conditions:
* %pstate am:0 priv:1 ie:0
* globals are either ag or ig (not mg!)
*
* Register Inputs:
* %g1 pc of trap handler
* %g2, %g3 args for handler
* %g4 desired %pil (-1 means current %pil)
* %g5, %g6 destroyed
* %g7 saved
*
* Register Usage:
* %l0, %l1 temps
* %l3 saved %g1
* %l6 curthread for user traps, %pil for priv traps
* %l7 regs
*
* Called function prototype variants:
*
* func(struct regs *rp);
* func(struct regs *rp, uintptr_t arg1 [%g2], uintptr_t arg2 [%g3])
* func(struct regs *rp, uintptr_t arg1 [%g2],
* uint32_t arg2 [%g3.l], uint32_t arg3 [%g3.h])
* func(struct regs *rp, uint32_t arg1 [%g2.l],
* uint32_t arg2 [%g3.l], uint32_t arg3 [%g3.h], uint32_t [%g2.h])
*/
#if defined(lint)
void
sys_trap(void)
{}
#else /* lint */
ENTRY_NP(sys_trap)
!
! force tl=1, update %cwp, branch to correct handler
!
wrpr %g0, 1, %tl
rdpr %tstate, %g5
btst TSTATE_PRIV, %g5
and %g5, TSTATE_CWP, %g6
bnz,pn %xcc, priv_trap
wrpr %g0, %g6, %cwp
ALTENTRY(user_trap)
!
! user trap
!
! make all windows clean for kernel
! buy a window using the current thread's stack
!
sethi %hi(nwin_minus_one), %g5
ld [%g5 + %lo(nwin_minus_one)], %g5
wrpr %g0, %g5, %cleanwin
CPU_ADDR(%g5, %g6)
ldn [%g5 + CPU_THREAD], %g5
ldn [%g5 + T_STACK], %g6
sub %g6, STACK_BIAS, %g6
save %g6, 0, %sp
!
! set window registers so that current windows are "other" windows
!
rdpr %canrestore, %l0
rdpr %wstate, %l1
wrpr %g0, 0, %canrestore
sllx %l1, WSTATE_SHIFT, %l1
wrpr %l1, WSTATE_K64, %wstate
wrpr %g0, %l0, %otherwin
!
! set pcontext to run kernel
!
sethi %hi(kcontextreg), %l0
ldx [%l0 + %lo(kcontextreg)], %l0
mov MMU_PCONTEXT, %l1 ! if kcontextreg==PCONTEXT, do nothing
ldxa [%l1]ASI_MMU_CTX, %l2
xor %l0, %l2, %l2
srlx %l2, CTXREG_NEXT_SHIFT, %l2
brz %l2, 2f ! if N_pgsz0/1 changed, need demap
sethi %hi(FLUSH_ADDR), %l3
mov DEMAP_ALL_TYPE, %l2
stxa %g0, [%l2]ASI_DTLB_DEMAP
stxa %g0, [%l2]ASI_ITLB_DEMAP
2:
stxa %l0, [%l1]ASI_MMU_CTX
flush %l3 ! flush required by immu
1:
set utl0, %g6 ! bounce to utl0
have_win:
SYSTRAP_TRACE(%o1, %o2, %o3)
!
! at this point we have a new window we can play in,
! and %g6 is the label we want done to bounce to
!
! save needed current globals
!
mov %g1, %l3 ! pc
mov %g2, %o1 ! arg #1
mov %g3, %o2 ! arg #2
srlx %g3, 32, %o3 ! pseudo arg #3
srlx %g2, 32, %o4 ! pseudo arg #4
mov %g5, %l6 ! curthread if user trap, %pil if priv trap
!
! save trap state on stack
!
add %sp, REGOFF + STACK_BIAS, %l7
rdpr %tpc, %l0
rdpr %tnpc, %l1
rdpr %tstate, %l2
stn %l0, [%l7 + PC_OFF]
stn %l1, [%l7 + nPC_OFF]
stx %l2, [%l7 + TSTATE_OFF]
!
! setup pil
!
brlz,pt %g4, 1f
nop
#ifdef DEBUG
!
! ASSERT(%g4 >= %pil).
!
rdpr %pil, %l0
cmp %g4, %l0
bge,pt %xcc, 0f
nop ! yes, nop; to avoid anull
set bad_g4_called, %l3
mov 1, %o1
st %o1, [%l3]
set bad_g4, %l3 ! pc
set sys_trap_wrong_pil, %o1 ! arg #1
mov %g4, %o2 ! arg #2
ba 1f ! stay at the current %pil
mov %l0, %o3 ! arg #3
0:
#endif /* DEBUG */
wrpr %g0, %g4, %pil
1:
!
! set trap regs to execute in kernel at %g6
! done resumes execution there
!
wrpr %g0, %g6, %tnpc
rdpr %cwp, %l0
set TSTATE_KERN, %l1
wrpr %l1, %l0, %tstate
done
/* NOTREACHED */
SET_SIZE(user_trap)
SET_SIZE(sys_trap)
ENTRY_NP(prom_trap)
!
! prom trap switches the stack to 32-bit
! if we took a trap from a 64-bit window
! Then buys a window on the current stack.
!
save %sp, -SA64(REGOFF + REGSIZE), %sp
/* 32 bit frame, 64 bit sized */
set ptl0, %g6
ba,a,pt %xcc, have_win
SET_SIZE(prom_trap)
ENTRY_NP(priv_trap)
!
! kernel trap
! buy a window on the current stack
!
! is the trap PC in the range allocated to Open Firmware?
rdpr %tpc, %g5
set OFW_END_ADDR, %g6
cmp %g5, %g6
bgu,a,pn %xcc, 1f
rdpr %pil, %g5
set OFW_START_ADDR, %g6
cmp %g5, %g6
bgeu,pn %xcc, prom_trap
rdpr %pil, %g5
1:
set ktl0, %g6
save %sp, -SA(REGOFF + REGSIZE), %sp
ba,a,pt %xcc, have_win
SET_SIZE(priv_trap)
ENTRY_NP(utl0)
SAVE_GLOBALS(%l7)
SAVE_OUTS(%l7)
mov %l6, THREAD_REG
wrpr %g0, PSTATE_KERN, %pstate ! enable ints
jmpl %l3, %o7 ! call trap handler
mov %l7, %o0
!
ALTENTRY(user_rtt)
!
! Register inputs
! %l7 - regs
!
! disable interrupts and check for ASTs and wbuf restores
! keep cpu_base_spl in %l4 and THREAD_REG in %l6 (needed
! in wbuf.s when globals have already been restored).
!
wrpr %g0, PIL_MAX, %pil
ldn [THREAD_REG + T_CPU], %l0
ld [%l0 + CPU_BASE_SPL], %l4
ldub [THREAD_REG + T_ASTFLAG], %l2
brz,pt %l2, 1f
ld [%sp + STACK_BIAS + MPCB_WBCNT], %l3
!
! call trap to do ast processing
!
wrpr %g0, %l4, %pil ! pil = cpu_base_spl
mov %l7, %o0
call trap
mov T_AST, %o2
ba,a,pt %xcc, user_rtt
1:
brz,pt %l3, 2f
mov THREAD_REG, %l6
!
! call restore_wbuf to push wbuf windows to stack
!
wrpr %g0, %l4, %pil ! pil = cpu_base_spl
mov %l7, %o0
call trap
mov T_FLUSH_PCB, %o2
ba,a,pt %xcc, user_rtt
2:
#ifdef TRAPTRACE
TRACE_RTT(TT_SYS_RTT_USER, %l0, %l1, %l2, %l3)
#endif /* TRAPTRACE */
ld [%sp + STACK_BIAS + MPCB_WSTATE], %l3 ! get wstate
!
! restore user globals and outs
!
rdpr %pstate, %l1
wrpr %l1, PSTATE_IE, %pstate
RESTORE_GLOBALS(%l7)
! switch to alternate globals, saving THREAD_REG in %l6
wrpr %l1, PSTATE_IE | PSTATE_AG, %pstate
mov %sp, %g6 ! remember the mpcb pointer in %g6
RESTORE_OUTS(%l7)
!
! set %pil from cpu_base_spl
!
wrpr %g0, %l4, %pil
!
! raise tl (now using nucleus context)
! set pcontext to scontext for user execution
!
wrpr %g0, 1, %tl
mov MMU_SCONTEXT, %g1
ldxa [%g1]ASI_MMU_CTX, %g2
mov MMU_PCONTEXT, %g1
ldxa [%g1]ASI_MMU_CTX, %g4 ! need N_pgsz0/1 bits
srlx %g4, CTXREG_NEXT_SHIFT, %g4
sllx %g4, CTXREG_NEXT_SHIFT, %g4
or %g4, %g2, %g2 ! Or in Nuc pgsz bits
sethi %hi(FLUSH_ADDR), %g3
stxa %g2, [%g1]ASI_MMU_CTX
flush %g3 ! flush required by immu
!
! setup trap regs
!
ldn [%l7 + PC_OFF], %g1
ldn [%l7 + nPC_OFF], %g2
ldx [%l7 + TSTATE_OFF], %l0
andn %l0, TSTATE_CWP, %g7
wrpr %g1, %tpc
wrpr %g2, %tnpc
!
! switch "other" windows back to "normal" windows and
! restore to window we originally trapped in
!
rdpr %otherwin, %g1
wrpr %g0, 0, %otherwin
add %l3, WSTATE_CLEAN_OFFSET, %l3 ! convert to "clean" wstate
wrpr %g0, %l3, %wstate
wrpr %g0, %g1, %canrestore
!
! First attempt to restore from the watchpoint saved register window
tst %g1
bne,a 1f
clrn [%g6 + STACK_BIAS + MPCB_RSP0]
tst %fp
be,a 1f
clrn [%g6 + STACK_BIAS + MPCB_RSP0]
! test for user return window in pcb
ldn [%g6 + STACK_BIAS + MPCB_RSP0], %g1
cmp %fp, %g1
bne 1f
clrn [%g6 + STACK_BIAS + MPCB_RSP0]
restored
restore
! restore from user return window
RESTORE_V9WINDOW(%g6 + STACK_BIAS + MPCB_RWIN0)
!
! Attempt to restore from the scond watchpoint saved register window
tst %fp
be,a 2f
clrn [%g6 + STACK_BIAS + MPCB_RSP1]
ldn [%g6 + STACK_BIAS + MPCB_RSP1], %g1
cmp %fp, %g1
bne 2f
clrn [%g6 + STACK_BIAS + MPCB_RSP1]
restored
restore
RESTORE_V9WINDOW(%g6 + STACK_BIAS + MPCB_RWIN1)
save
b,a 2f
1:
restore ! should not trap
2:
!
! set %cleanwin to %canrestore
! set %tstate to the correct %cwp
! retry resumes user execution
!
rdpr %canrestore, %g1
wrpr %g0, %g1, %cleanwin
rdpr %cwp, %g1
wrpr %g1, %g7, %tstate
retry
/* NOTREACHED */
SET_SIZE(user_rtt)
SET_SIZE(utl0)
ENTRY_NP(ptl0)
SAVE_GLOBALS(%l7)
SAVE_OUTS(%l7)
CPU_ADDR(%g5, %g6)
ldn [%g5 + CPU_THREAD], THREAD_REG
wrpr %g0, PSTATE_KERN, %pstate ! enable ints
jmpl %l3, %o7 ! call trap handler
mov %l7, %o0
!
ALTENTRY(prom_rtt)
#ifdef TRAPTRACE
TRACE_RTT(TT_SYS_RTT_PROM, %l0, %l1, %l2, %l3)
#endif /* TRAPTRACE */
ba,pt %xcc, common_rtt
mov THREAD_REG, %l0
SET_SIZE(prom_rtt)
SET_SIZE(ptl0)
ENTRY_NP(ktl0)
SAVE_GLOBALS(%l7)
SAVE_OUTS(%l7) ! for the call bug workaround
wrpr %g0, PSTATE_KERN, %pstate ! enable ints
jmpl %l3, %o7 ! call trap handler
mov %l7, %o0
!
ALTENTRY(priv_rtt)
#ifdef TRAPTRACE
TRACE_RTT(TT_SYS_RTT_PRIV, %l0, %l1, %l2, %l3)
#endif /* TRAPTRACE */
!
! Register inputs
! %l7 - regs
! %l6 - trap %pil
!
! Check for a kernel preemption request
!
ldn [THREAD_REG + T_CPU], %l0
ldub [%l0 + CPU_KPRUNRUN], %l0
brz,pt %l0, 1f
nop
!
! Attempt to preempt
!
ldstub [THREAD_REG + T_PREEMPT_LK], %l0 ! load preempt lock
brnz,pn %l0, 1f ! can't call kpreempt if this thread is
nop ! already in it...
call kpreempt
mov %l6, %o0 ! pass original interrupt level
stub %g0, [THREAD_REG + T_PREEMPT_LK] ! nuke the lock
rdpr %pil, %o0 ! compare old pil level
cmp %l6, %o0 ! with current pil level
movg %xcc, %o0, %l6 ! if current is lower, drop old pil
1:
!
! If we interrupted the mutex_exit() critical region we must reset
! the PC and nPC back to the beginning to prevent missed wakeups.
! See the comments in mutex_exit() for details.
!
ldn [%l7 + PC_OFF], %l0
set mutex_exit_critical_start, %l1
sub %l0, %l1, %l0
cmp %l0, mutex_exit_critical_size
bgeu,pt %xcc, common_rtt
mov THREAD_REG, %l0
stn %l1, [%l7 + PC_OFF] ! restart mutex_exit()
add %l1, 4, %l1
stn %l1, [%l7 + nPC_OFF]
common_rtt:
!
! restore globals and outs
!
rdpr %pstate, %l1
wrpr %l1, PSTATE_IE, %pstate
RESTORE_GLOBALS(%l7)
! switch to alternate globals
wrpr %l1, PSTATE_IE | PSTATE_AG, %pstate
RESTORE_OUTS(%l7)
!
! set %pil from max(old pil, cpu_base_spl)
!
ldn [%l0 + T_CPU], %l0
ld [%l0 + CPU_BASE_SPL], %l0
cmp %l6, %l0
movg %xcc, %l6, %l0
wrpr %g0, %l0, %pil
!
! raise tl
! setup trap regs
! restore to window we originally trapped in
!
wrpr %g0, 1, %tl
ldn [%l7 + PC_OFF], %g1
ldn [%l7 + nPC_OFF], %g2
ldx [%l7 + TSTATE_OFF], %l0
andn %l0, TSTATE_CWP, %g7
wrpr %g1, %tpc
wrpr %g2, %tnpc
restore
!
! set %tstate to the correct %cwp
! retry resumes prom execution
!
rdpr %cwp, %g1
wrpr %g1, %g7, %tstate
retry
/* NOTREACHED */
SET_SIZE(priv_rtt)
SET_SIZE(ktl0)
#endif /* lint */
#ifndef lint
#ifdef DEBUG
.seg ".data"
.align 4
.global bad_g4_called
bad_g4_called:
.word 0
sys_trap_wrong_pil:
.asciz "sys_trap: %g4(%d) is lower than %pil(%d)"
.align 4
.seg ".text"
ENTRY_NP(bad_g4)
mov %o1, %o0
mov %o2, %o1
call panic
mov %o3, %o2
SET_SIZE(bad_g4)
#endif /* DEBUG */
#endif /* lint */
/*
* sys_tl1_panic can be called by traps at tl1 which
* really want to panic, but need the rearrangement of
* the args as provided by this wrapper routine.
*/
#if defined(lint)
void
sys_tl1_panic(void)
{}
#else /* lint */
ENTRY_NP(sys_tl1_panic)
mov %o1, %o0
mov %o2, %o1
call panic
mov %o3, %o2
SET_SIZE(sys_tl1_panic)
#endif /* lint */
/*
* Turn on or off bits in the auxiliary i/o register.
*
* set_auxioreg(bit, flag)
* int bit; bit mask in aux i/o reg
* int flag; 0 = off, otherwise on
*
* This is intrinsicly ugly but is used by the floppy driver. It is also
* used to turn on/off the led.
*/
#if defined(lint)
/* ARGSUSED */
void
set_auxioreg(int bit, int flag)
{}
#else /* lint */
.seg ".data"
.align 4
auxio_panic:
.asciz "set_auxioreg: interrupts already disabled on entry"
.align 4
.seg ".text"
ENTRY_NP(set_auxioreg)
/*
* o0 = bit mask
* o1 = flag: 0 = off, otherwise on
*
* disable interrupts while updating auxioreg
*/
rdpr %pstate, %o2
#ifdef DEBUG
andcc %o2, PSTATE_IE, %g0 /* if interrupts already */
bnz,a,pt %icc, 1f /* disabled, panic */
nop
sethi %hi(auxio_panic), %o0
call panic
or %o0, %lo(auxio_panic), %o0
1:
#endif /* DEBUG */
wrpr %o2, PSTATE_IE, %pstate /* disable interrupts */
sethi %hi(v_auxio_addr), %o3
ldn [%o3 + %lo(v_auxio_addr)], %o4
ldub [%o4], %g1 /* read aux i/o register */
tst %o1
bnz,a 2f
bset %o0, %g1 /* on */
bclr %o0, %g1 /* off */
2:
or %g1, AUX_MBO, %g1 /* Must Be Ones */
stb %g1, [%o4] /* write aux i/o register */
retl
wrpr %g0, %o2, %pstate /* enable interrupt */
SET_SIZE(set_auxioreg)
#endif /* lint */
/*
* 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.
*/
#if defined(lint)
void
flush_windows(void)
{}
#else /* lint */
ENTRY_NP(flush_windows)
retl
flushw
SET_SIZE(flush_windows)
#endif /* lint */
#if defined(lint)
void
debug_flush_windows(void)
{}
#else /* lint */
ENTRY_NP(debug_flush_windows)
set nwindows, %g1
ld [%g1], %g1
mov %g1, %g2
1:
save %sp, -WINDOWSIZE, %sp
brnz %g2, 1b
dec %g2
mov %g1, %g2
2:
restore
brnz %g2, 2b
dec %g2
retl
nop
SET_SIZE(debug_flush_windows)
#endif /* lint */
/*
* flush user windows to memory.
*/
#if defined(lint)
void
flush_user_windows(void)
{}
#else /* lint */
ENTRY_NP(flush_user_windows)
rdpr %otherwin, %g1
brz %g1, 3f
clr %g2
1:
save %sp, -WINDOWSIZE, %sp
rdpr %otherwin, %g1
brnz %g1, 1b
add %g2, 1, %g2
2:
sub %g2, 1, %g2 ! restore back to orig window
brnz %g2, 2b
restore
3:
retl
nop
SET_SIZE(flush_user_windows)
#endif /* lint */
/*
* 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.
*/
#if defined(lint)
void
trash_user_windows(void)
{}
#else /* lint */
ENTRY_NP(trash_user_windows)
rdpr %otherwin, %g1
brz %g1, 3f ! no user windows?
ldn [THREAD_REG + T_STACK], %g5
!
! There are old user windows in the register file. We disable ints
! and increment cansave so that we don't overflow on these windows.
! Also, this sets up a nice underflow when first returning to the
! new user.
!
rdpr %pstate, %g2
wrpr %g2, PSTATE_IE, %pstate
rdpr %cansave, %g3
rdpr %otherwin, %g1 ! re-read in case of interrupt
add %g3, %g1, %g3
wrpr %g0, 0, %otherwin
wrpr %g0, %g3, %cansave
wrpr %g0, %g2, %pstate
3:
retl
clr [%g5 + MPCB_WBCNT] ! zero window buffer cnt
SET_SIZE(trash_user_windows)
#endif /* lint */
/*
* Setup g7 via the CPU data structure.
*/
#if defined(lint)
struct scb *
set_tbr(struct scb *s)
{ return (s); }
#else /* lint */
ENTRY_NP(set_tbr)
retl
ta 72 ! no tbr, stop simulation
SET_SIZE(set_tbr)
#endif /* lint */
#if defined(lint)
/*
* These need to be defined somewhere to lint and there is no "hicore.s"...
*/
char etext[1], end[1];
#endif /* lint*/
#if defined (lint)
/* ARGSUSED */
void
ptl1_panic(u_int reason)
{}
#else /* lint */
#define PTL1_SAVE_WINDOW(RP) \
stxa %l0, [RP + RW64_LOCAL + (0 * RW64_LOCAL_INCR)] %asi; \
stxa %l1, [RP + RW64_LOCAL + (1 * RW64_LOCAL_INCR)] %asi; \
stxa %l2, [RP + RW64_LOCAL + (2 * RW64_LOCAL_INCR)] %asi; \
stxa %l3, [RP + RW64_LOCAL + (3 * RW64_LOCAL_INCR)] %asi; \
stxa %l4, [RP + RW64_LOCAL + (4 * RW64_LOCAL_INCR)] %asi; \
stxa %l5, [RP + RW64_LOCAL + (5 * RW64_LOCAL_INCR)] %asi; \
stxa %l6, [RP + RW64_LOCAL + (6 * RW64_LOCAL_INCR)] %asi; \
stxa %l7, [RP + RW64_LOCAL + (7 * RW64_LOCAL_INCR)] %asi; \
stxa %i0, [RP + RW64_IN + (0 * RW64_IN_INCR)] %asi; \
stxa %i1, [RP + RW64_IN + (1 * RW64_IN_INCR)] %asi; \
stxa %i2, [RP + RW64_IN + (2 * RW64_IN_INCR)] %asi; \
stxa %i3, [RP + RW64_IN + (3 * RW64_IN_INCR)] %asi; \
stxa %i4, [RP + RW64_IN + (4 * RW64_IN_INCR)] %asi; \
stxa %i5, [RP + RW64_IN + (5 * RW64_IN_INCR)] %asi; \
stxa %i6, [RP + RW64_IN + (6 * RW64_IN_INCR)] %asi; \
stxa %i7, [RP + RW64_IN + (7 * RW64_IN_INCR)] %asi
#define PTL1_NEXT_WINDOW(scr) \
add scr, RWIN64SIZE, scr
#define PTL1_RESET_RWINDOWS(scr) \
sethi %hi(nwin_minus_one), scr; \
ld [scr + %lo(nwin_minus_one)], scr; \
wrpr scr, %cleanwin; \
dec scr; \
wrpr scr, %cansave; \
wrpr %g0, %canrestore; \
wrpr %g0, %otherwin
#define PTL1_DCACHE_LINE_SIZE 4 /* small enough for all CPUs */
/*
* ptl1_panic is called when the kernel detects that it is in an invalid state
* and the trap level is greater than 0. ptl1_panic is responsible to save the
* current CPU state, to restore the CPU state to normal, and to call panic.
* The CPU state must be saved reliably without causing traps. ptl1_panic saves
* it in the ptl1_state structure, which is a member of the machcpu structure.
* In order to access the ptl1_state structure without causing traps, physical
* addresses are used so that we can avoid MMU miss traps. The restriction of
* physical memory accesses is that the ptl1_state structure must be on a single
* physical page. This is because (1) a single physical address for each
* ptl1_state structure is needed and (2) it simplifies physical address
* calculation for each member of the structure.
* ptl1_panic is a likely spot for stack overflows to wind up; thus, the current
* stack may not be usable. In order to call panic reliably in such a state,
* each CPU needs a dedicated ptl1 panic stack.
* CPU_ALLOC_SIZE, which is defined to be MMU_PAGESIZE, is used to allocate the
* cpu structure and a ptl1 panic stack. They are put together on the same page
* for memory space efficiency. The low address part is used for the cpu
* structure, and the high address part is for a ptl1 panic stack.
* The cpu_pa array holds the physical addresses of the allocated cpu structures,
* as the cpu array holds their virtual addresses.
*
* %g1 reason to be called
* %g2 broken
* %g3 broken
*/
ENTRY_NP(ptl1_panic)
!
! flush D$ first, so that stale data will not be accessed later.
! Data written via ASI_MEM bypasses D$. If D$ contains data at the same
! address, where data was written via ASI_MEM, a load from that address
! using a virtual address and the default ASI still takes the old data.
! Flushing D$ erases old data in D$, so that it will not be loaded.
! Since we can afford only 2 registers (%g2 and %g3) for this job, we
! flush entire D$.
!
sethi %hi(dcache_size), %g2
ld [%g2 + %lo(dcache_size)], %g2
sethi %hi(dcache_linesize), %g3
ld [%g3 + %lo(dcache_linesize)], %g3
sub %g2, %g3, %g2
0: stxa %g0, [%g2] ASI_DC_TAG
membar #Sync
brnz,pt %g2, 0b
sub %g2, %g3, %g2
!
! increment the entry counter.
! save CPU state if this is the first entry.
!
CPU_PADDR(%g2, %g3);
add %g2, CPU_PTL1, %g2 ! pstate = &CPU->mcpu.ptl1_state
wr %g0, ASI_MEM, %asi ! physical address access
!
! pstate->ptl1_entry_count++
!
lduwa [%g2 + PTL1_ENTRY_COUNT] %asi, %g3
add %g3, 1, %g3
stuwa %g3, [%g2 + PTL1_ENTRY_COUNT] %asi
!
! CPU state saving is skipped from the 2nd entry to ptl1_panic since we
! do not want to clobber the state from the original failure. panic()
! is responsible for handling multiple or recursive panics.
!
cmp %g3, 2 ! if (ptl1_entry_count >= 2)
bge,pn %icc, state_saved ! goto state_saved
add %g2, PTL1_REGS, %g3 ! %g3 = &pstate->ptl1_regs[0]
!
! save CPU state
!
save_cpu_state:
! save current global registers
! so that all them become available for use
!
stxa %g1, [%g3 + PTL1_G1] %asi
stxa %g2, [%g3 + PTL1_G2] %asi
stxa %g3, [%g3 + PTL1_G3] %asi
stxa %g4, [%g3 + PTL1_G4] %asi
stxa %g5, [%g3 + PTL1_G5] %asi
stxa %g6, [%g3 + PTL1_G6] %asi
stxa %g7, [%g3 + PTL1_G7] %asi
!
! %tl, %tt, %tstate, %tpc, %tnpc for each TL
!
rdpr %tl, %g1
brz %g1, 1f ! if(trap_level == 0) -------+
add %g3, PTL1_TRAP_REGS, %g4 ! %g4 = &ptl1_trap_regs[0]; !
0: ! -----------<----------+ !
stwa %g1, [%g4 + PTL1_TL] %asi ! !
rdpr %tt, %g5 ! !
stwa %g5, [%g4 + PTL1_TT] %asi ! !
rdpr %tstate, %g5 ! !
stxa %g5, [%g4 + PTL1_TSTATE] %asi ! !
rdpr %tpc, %g5 ! !
stxa %g5, [%g4 + PTL1_TPC] %asi ! !
rdpr %tnpc, %g5 ! !
stxa %g5, [%g4 + PTL1_TNPC] %asi ! !
add %g4, PTL1_TRAP_REGS_INCR, %g4 ! !
deccc %g1 ! !
bnz,a,pt %icc, 0b ! if(trap_level != 0) --+ !
wrpr %g1, %tl !
1: ! ----------<----------------+
!
! %pstate, %pil, SOFTINT, (S)TICK
! Pending interrupts is also cleared in order to avoid a recursive call
! to ptl1_panic in case the interrupt handler causes a panic.
!
rdpr %pil, %g1
stba %g1, [%g3 + PTL1_PIL] %asi
rdpr %pstate, %g1
stha %g1, [%g3 + PTL1_PSTATE] %asi
rd SOFTINT, %g1
sta %g1, [%g3 + PTL1_SOFTINT] %asi
wr %g1, CLEAR_SOFTINT
sethi %hi(traptrace_use_stick), %g1
ld [%g1 + %lo(traptrace_use_stick)], %g1
brz,a,pn %g1, 2f
rdpr %tick, %g1
rd STICK, %g1
2: stxa %g1, [%g3 + PTL1_TICK] %asi
!
! MMU registers because ptl1_panic may be called from
! the MMU trap handlers.
!
mov MMU_SFAR, %g1
ldxa [%g1]ASI_DMMU, %g4
stxa %g4, [%g3 + PTL1_DMMU_SFAR]%asi
mov MMU_SFSR, %g1
ldxa [%g1]ASI_DMMU, %g4
stxa %g4, [%g3 + PTL1_DMMU_SFSR]%asi
ldxa [%g1]ASI_IMMU, %g4
stxa %g4, [%g3 + PTL1_IMMU_SFSR]%asi
mov MMU_TAG_ACCESS, %g1
ldxa [%g1]ASI_DMMU, %g4
stxa %g4, [%g3 + PTL1_DMMU_TAG_ACCESS]%asi
ldxa [%g1]ASI_IMMU, %g4
stxa %g4, [%g3 + PTL1_IMMU_TAG_ACCESS]%asi
!
! Save register window state and register windows.
!
rdpr %cwp, %g1
stba %g1, [%g3 + PTL1_CWP] %asi
rdpr %wstate, %g1
stba %g1, [%g3 + PTL1_WSTATE] %asi
rdpr %otherwin, %g1
stba %g1, [%g3 + PTL1_OTHERWIN] %asi
rdpr %cleanwin, %g1
stba %g1, [%g3 + PTL1_CLEANWIN] %asi
rdpr %cansave, %g1
stba %g1, [%g3 + PTL1_CANSAVE] %asi
rdpr %canrestore, %g1
stba %g1, [%g3 + PTL1_CANRESTORE] %asi
PTL1_RESET_RWINDOWS(%g1)
clr %g1
wrpr %g1, %cwp
add %g3, PTL1_RWINDOW, %g4 ! %g4 = &ptl1_rwindow[0];
3: PTL1_SAVE_WINDOW(%g4) ! <-------------+
inc %g1 !
cmp %g1, MAXWIN !
bgeu,pn %icc, 5f !
wrpr %g1, %cwp !
rdpr %cwp, %g2 !
cmp %g1, %g2 ! saturation check
be,pt %icc, 3b !
PTL1_NEXT_WINDOW(%g4) ! ------+
5:
!
! most crucial CPU state was saved.
! Proceed to go back to TL = 0.
!
state_saved:
wrpr %g0, 1, %tl
wrpr %g0, PIL_MAX, %pil
!
PTL1_RESET_RWINDOWS(%g1)
wrpr %g0, %cwp
wrpr %g0, %cleanwin
wrpr %g0, WSTATE_KERN, %wstate
!
! Set pcontext to run kernel.
!
set DEMAP_ALL_TYPE, %g1
stxa %g0, [%g1]ASI_DTLB_DEMAP
stxa %g0, [%g1]ASI_ITLB_DEMAP
set MMU_PCONTEXT, %g1
stxa %g0, [%g1]ASI_MMU_CTX
sethi %hi(FLUSH_ADDR), %g3
membar #Sync
flush %g3
rdpr %cwp, %g1
set TSTATE_KERN, %g3
wrpr %g3, %g1, %tstate
set ptl1_panic_tl0, %g3
wrpr %g0, %g3, %tnpc
done ! go to -->-+ TL:1
!
ptl1_panic_tl0: ! ----<-----+ TL:0
CPU_ADDR(%l0, %l1) ! %l0 = cpu[cpuid]
add %l0, CPU_PTL1, %l1 ! %l1 = &CPU->mcpu.ptl1_state
!
! prepare to call panic()
!
ldn [%l0 + CPU_THREAD], THREAD_REG ! restore %g7
ldn [%l1 + PTL1_STKTOP], %l2 ! %sp = ptl1_stktop
sub %l2, SA(MINFRAME) + STACK_BIAS, %sp
clr %fp ! no frame below this window
clr %i7
!
! enable limited interrupts
!
wrpr %g0, CLOCK_LEVEL, %pil
wrpr %g0, PSTATE_KERN, %pstate
!
ba,pt %xcc, ptl1_panic_handler
mov %l1, %o0
/*NOTREACHED*/
SET_SIZE(ptl1_panic)
#endif /* lint */
#ifdef PTL1_PANIC_DEBUG
#if defined (lint)
/*
* ptl1_recurse() calls itself a number of times to either set up a known
* stack or to cause a kernel stack overflow. It decrements the arguments
* on each recursion.
* It's called by #ifdef PTL1_PANIC_DEBUG code in startup.c to set the
* registers to a known state to facilitate debugging.
*/
/* ARGSUSED */
void
ptl1_recurse(int count_threshold, int trap_threshold)
{}
#else /* lint */
ENTRY_NP(ptl1_recurse)
save %sp, -SA(MINFRAME), %sp
set ptl1_recurse_call, %o7
cmp %o7, %i7 ! if ptl1_recurse is called
be,pt %icc, 0f ! by itself, then skip
nop ! register initialization
/*
* Initialize Out Registers to Known Values
*/
set 0x01000, %l0 ! %i0 is the ...
! recursion_depth_count
sub %i0, 1, %o0;
sub %i1, 1, %o1;
add %l0, %o0, %o2;
add %l0, %o2, %o3;
add %l0, %o3, %o4;
add %l0, %o4, %o5;
ba,a 1f
nop
0: /* Outs = Ins - 1 */
sub %i0, 1, %o0;
sub %i1, 1, %o1;
sub %i2, 1, %o2;
sub %i3, 1, %o3;
sub %i4, 1, %o4;
sub %i5, 1, %o5;
/* Locals = Ins + 1 */
1: add %i0, 1, %l0;
add %i1, 1, %l1;
add %i2, 1, %l2;
add %i3, 1, %l3;
add %i4, 1, %l4;
add %i5, 1, %l5;
set 0x0100000, %g5
add %g5, %g0, %g1
add %g5, %g1, %g2
add %g5, %g2, %g3
add %g5, %g3, %g4
add %g5, %g4, %g5
brz,pn %i1, ptl1_recurse_trap ! if trpp_count == 0) {
nop ! trap to ptl1_panic
!
brz,pn %i0, ptl1_recure_exit ! if(depth_count == 0) {
nop ! skip recursive call
! }
ptl1_recurse_call:
call ptl1_recurse
nop
ptl1_recure_exit:
ret
restore
ptl1_recurse_trap:
ta PTL1_DEBUG_TRAP; ! Trap Always to ptl1_panic()
nop ! NOTREACHED
SET_SIZE(ptl1_recurse)
#endif /* lint */
#if defined (lint)
/* ARGSUSED */
void
ptl1_panic_xt(int arg1, int arg2)
{}
#else /* lint */
/*
* Asm function to handle a cross trap to call ptl1_panic()
*/
ENTRY_NP(ptl1_panic_xt)
ba ptl1_panic
mov PTL1_BAD_DEBUG, %g1
SET_SIZE(ptl1_panic_xt)
#endif /* lint */
#endif /* PTL1_PANIC_DEBUG */
#ifdef TRAPTRACE
#if defined (lint)
void
trace_ptr_panic(void)
{
}
#else /* lint */
ENTRY_NP(trace_ptr_panic)
!
! freeze the trap trace to disable the assertions. Otherwise,
! ptl1_panic is likely to be repeatedly called from there.
! %g2 and %g3 are used as scratch registers in ptl1_panic.
!
mov 1, %g3
sethi %hi(trap_freeze), %g2
st %g3, [%g2 + %lo(trap_freeze)]
!
! %g1 contains the %pc address where an assertion was failed.
! save it in trap_freeze_pc for a debugging hint if there is
! no value saved in it.
!
set trap_freeze_pc, %g2
casn [%g2], %g0, %g1
ba ptl1_panic
mov PTL1_BAD_TRACE_PTR, %g1
SET_SIZE(trace_ptr_panic)
#endif /* lint */
#endif /* TRAPTRACE */
#if defined (lint)
/*
* set_kcontextreg() sets PCONTEXT to kctx
* if PCONTEXT==kctx, do nothing
* if N_pgsz0|N_pgsz1 differ, do demap all first
*/
/* ARGSUSED */
void
set_kcontextreg()
{
}
#else /* lint */
ENTRY_NP(set_kcontextreg)
! SET_KCONTEXTREG(reg0, reg1, reg2, reg3, reg4, label1, label2, label3)
SET_KCONTEXTREG(%o0, %o1, %o2, %o3, %o4, l1, l2, l3)
retl
nop
SET_SIZE(set_kcontextreg)
#endif /* lint */
/*
* The interface for a 32-bit client program that takes over the TBA
* calling the 64-bit romvec OBP.
*/
#if defined(lint)
/* ARGSUSED */
int
client_handler(void *cif_handler, void *arg_array)
{ return 0; }
#else /* lint */
ENTRY(client_handler)
save %sp, -SA64(MINFRAME64), %sp ! 32 bit frame, 64 bit sized
sethi %hi(tba_taken_over), %l2
ld [%l2+%lo(tba_taken_over)], %l3
brz %l3, 1f ! is the tba_taken_over = 1 ?
rdpr %wstate, %l5 ! save %wstate
andn %l5, WSTATE_MASK, %l6
wrpr %l6, WSTATE_KMIX, %wstate
! switch to PCONTEXT=0
mov MMU_PCONTEXT, %o2
ldxa [%o2]ASI_DMMU, %o2
srlx %o2, CTXREG_NEXT_SHIFT, %o2
brz,pt %o2, 1f ! nucleus pgsz is 0, no problem
nop
rdpr %pstate, %l4 ! disable interrupts
andn %l4, PSTATE_IE, %o2
wrpr %g0, %o2, %pstate
mov DEMAP_ALL_TYPE, %o2 ! set PCONTEXT=0
stxa %g0, [%o2]ASI_DTLB_DEMAP
stxa %g0, [%o2]ASI_ITLB_DEMAP
mov MMU_PCONTEXT, %o2
stxa %g0, [%o2]ASI_DMMU
membar #Sync
sethi %hi(FLUSH_ADDR), %o2
flush %o2 ! flush required by immu
wrpr %g0, %l4, %pstate ! restore interrupt state
!
1: mov %i1, %o0
1: rdpr %pstate, %l4 ! Get the present pstate value
andn %l4, PSTATE_AM, %l6
wrpr %l6, 0, %pstate ! Set PSTATE_AM = 0
jmpl %i0, %o7 ! Call cif handler
nop
wrpr %l4, 0, %pstate ! restore pstate
brz %l3, 1f ! is the tba_taken_over = 1
nop
wrpr %g0, %l5, %wstate ! restore wstate
! switch to PCONTEXT=kcontexreg
sethi %hi(kcontextreg), %o3
ldx [%o3 + %lo(kcontextreg)], %o3
brz %o3, 1f
nop
rdpr %pstate, %l4 ! disable interrupts
andn %l4, PSTATE_IE, %o2
wrpr %g0, %o2, %pstate
mov DEMAP_ALL_TYPE, %o2
stxa %g0, [%o2]ASI_DTLB_DEMAP
stxa %g0, [%o2]ASI_ITLB_DEMAP
mov MMU_PCONTEXT, %o2
stxa %o3, [%o2]ASI_DMMU
membar #Sync
sethi %hi(FLUSH_ADDR), %o2
flush %o2 ! flush required by immu
wrpr %g0, %l4, %pstate ! restore interrupt state
!
1: ret ! Return result ...
restore %o0, %g0, %o0 ! delay; result in %o0
SET_SIZE(client_handler)
#endif /* lint */