kaif_startup.s revision febcc4a52c3ed7fe3a106da2c2ba52c56afd5111
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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
* 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 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#if !defined(__lint)
#include <sys/asm_linkage.h>
#define _KERNEL
#include <sys/privregs.h>
#include <sys/machthread.h>
#include <sys/machtrap.h>
#include <sys/machparam.h>
#endif
#include <mdb/mdb_kreg_impl.h>
#include <kmdb/kaif_regs.h>
#include <kmdb/kaif_off.h>
#include <kmdb/kaif_asmutil.h>
#define KAIF_CPU_INDEX \
nop; \
1:
#define KAIF_CPU_GETADDR_TL1 \
1:
#ifndef sun4v
/*
* Creates a new primary context register value by copying the nucleus page
* size bits to the primary context page size bits and setting the primary
* context to zero. The updated value is stored in the ctx parameter.
*/
#endif /* sun4v */
#if !defined(__lint)
/*
* Calculate the address of the save area for the current CPU. This
* would be a macro, but for need to call platform-specific CPU ID
* routines. The kernel provides, via the KDI, a TL=1-safe "function"
* for CPU ID retrieval, which we call here. The retrieval code returns
* the ID in %g1, and is allowed to clobber %g2. It also assumes that
* the return address is in %g7.
*
* Arguments:
* %g7 - return address
* Returns:
* %g6 - address of save area
*
* %g4 will be preserved.
*/
KAIF_CPU_INDEX ! index returned in %g1, clobbers %g2, %g7
set KRS_SIZE, %g2
mulx %g1, %g2, %g2
set kaif_cpusave, %g6
ldx [%g6], %g6
jmp %g5 ! return to caller-provided address
add %g6, %g2, %g6
SET_SIZE(kaif_cpusave_getaddr)
/*
*
*
* Parameters:
*/
/*
* Switch over to the normal globals, so we can save them. We'll need
* our gregs pointer and the return %pstate value, so stash them in
* registers that will be available to us on both sides.
*
* NOTE: Global register sets is selected by %gl register in sun4v.
* There is no PSTATE.AG bit in sun4v to select global set.
* - Normal globals is the set when %gl = 0.
* - TL1 globals is the set when %gl = 1.
*/
SWITCH_TO_NORMAL_GLOBALS(); /* saves %o5 and %o4 */
/*
* Restore saved %o registers and return.
*/
SWITCH_TO_TL1_GLOBALS_AND_RET(); /* restores %o5 and %o4 */
/*
* Save the remaining state, and prepare to enter the debugger.
*/
/* Make sure the world is as it should be */
SET_GL(0);
1: /* CPU save area address is now in %g6 */
/*
* The %tba is special. With normal entry, we're on the same trap table
* the kernel is using (this could be OBP's table if we're early enough
* in the boot process). We want to save it, but we don't want to
* switch to OBP's table just yet, as we need to ensure that only one
* CPU uses OBP's table at a time. We do this by waiting until we've
* selected the master before switching.
*
* Single-step is a bit different. Everything about the CPU's state is
* as it should be, with the exception of %tba. We need to step on
* OBP's trap table, so we didn't restore %tba during resume. The save
* state area still contains the real %tba value - the one we had when
* we first entered the debugger. We don't want to clobber that, so
* we'll only save %tba if we're not stepping.
*/
be 1f
1:
/*
* Save window state and windows
*/
ble 1b
/*
* Save FP state
*/
wr %g0, FPRS_FEF, %fprs ! enable FP
STORE_FPREGS(%g4)
stx %fsr, [%g4 + FPU_FSR]
1: /*
* saving state, and for the next stage of debugger startup/resumption,
* when we designate the master. The slaves will continue to run on
* this stack until released or turned into masters.
*/
ldx [%g5 + KREG_OFF(KREG_CWP)], %g4
wrpr %g4, %cwp
set KRS_CPUSTACK + KAIF_CPU_STKSZ - 1, %g1
add %g1, %g6, %g1
and %g1, -STACK_ALIGN64, %g1
sub %g1, SA64(MINFRAME) + V9BIAS64, %sp
clr %fp
save %sp, -SA64(MINFRAME64), %sp
/*
*/
mov %g6, %l6
mov %g5, %l5
/*
* Now that we have a stack, we can save %stick. %stick isn't present
*/
/*
* We found %stick. Set the %stick-found flag.
*/
1: /*
* Enter the next phase of debugger startup
*/
/*NOTREACHED*/
#endif /* !__lint */
/*
* The primary debugger-entry routine. This routine is the trap handler
* for programmed entry, watchpoints, and breakpoints, and is entered at
* TL=1, on the kernel's trap table, with PSTATE.AG set. It is used in
* the following cases:
*
* 1. (common case) - intentional entry by a CPU intending to be the
* master. The CPU may have encountered a watchpoint, a breakpoint,
* or a programmed entry trap, and is *NOT* coming from OBP. The CPU
* is allowed direct entry into the debugger.
*
* 2. A CPU was cross-called into kaif_slave_entry while executing in
* OBP. The CPU was released, but a programmed entry trap was
* activated, designed to be encountered when the cross-called CPU
* returned from OBP. The CPU is allowed to enter the debugger. We
* don't know how many other CPUs need the PROM-return trap, so we'll
* leave it active until everyone arrives.
*
* The remaining cases deal with instances where OBP got in the way.
* We can't allow a CPU into the debugger if it is currently executing
* in OBP, as chaos would ensue (OBP isn't re-entrant). As such, we
* have to ask the CPU to come back when it has finished with OBP (or
* vice versa). Depending on the circumstances, we'll need to dance
* around it.
*
* 3. A bystander CPU runs into the PROM-return trap described above
* before being cross-called. We'll let it into the debugger now, as
* it would have ended up here anyway.
*
* 4. An innocent CPU encounters a watchpoint while executing in OBP.
* We can't let the CPU into the debugger for the reasons given
* above, so we'll need to ignore the watchpoint. We disable
* watchpoints, place a programmed-entry trap at %npc, and release
* the CPU.
*
* 5. The stepping CPU described in case 4 encounters the programmed-
* entry trap. We'll remove the trap, re-enable watchpoints, and
* send the CPU on its way.
*
* 6. Someone encounters a breakpoint or a programmed-entry trap in OBP.
* We can step through watchpoints, as the text hasn't been touched.
* With breakpoints and programmed-entry traps, however, chances are
* high that someone replaced an instruction in the text with the
* trap instruction. We don't know where they stashed the
* (presumably) saved instruction, so we can't step through it. This
* is a very unlikely scenario, so we're going to throw up our hands,
* and will attempt to trigger a panic.
*/
#if defined(__lint)
void
kaif_ktrap(void)
{
}
#else /* __lint */
1: /* CPU save area address is now in %g6 */
/*
* The CPU was in OBP when it encountered the trap that sent it here.
* See cases 3-6 above.
*/
/* This shouldn't happen - all valid traps should be checked above */
ba,a 1b
/* Cases 1 and 2 - head into the debugger, via the state-saver */
/* A formality - we know we came from kernel context */
#ifndef sun4v
/*
* If OBP supports preserving the Solaris kernel context register,
* then shift the nucleus bits into the primary and set context to 0,
* Otherwise, flush TLBs and clear the entire context register since
* OBP will clear it without flushing on entry to OBP.
*/
/*
* Move nucleus context page size bits into primary context page size
* and set context to 0. Use %g4 as a temporary.
*/
ba 2f
1:
#endif /* sun4v */
/*
* Flush TLBs and clear primary context register.
*/
2:
/*NOTREACHED*/
/* Case 4 - watchpoint in OBP - step over it */
#ifndef sun4v
/* Turn off watchpoints */
#endif /* sun4v */
/*
* SPARC only supports data watchpoints, and we know that only certain
* types of instructions, none of which include branches, can trigger
* memory reads. As such, we can simply place a breakpoint at %npc.
*/
/* Back into the pool */
/* Case 5 - programmed entry from wapt step - restore and resume */
nop
st %g3, [%g4]
membar #Sync
st %g0, [%g6 + KRS_INSTR_SAVE]
/* XXX I$ invalidate? */
#ifndef sun4v
ldx [%g6 + KRS_LSUCR_SAVE], %g4
stxa %g4, [%g0]ASI_LSU
#endif /* sun4v */
/* Restored - throw it back */
retry
/* Case 6 - breakpoint or unclaimed programmed entry */
main_obp_breakpoint:
main_obp_fail:
ldx [%g0], %g0
ba,a main_obp_fail
SET_SIZE(kaif_ktrap)
#endif /* __lint */
/*
* The target for slave-stopping cross calls. This routine is entered at
*
*
* world-stopper. This could happen if two CPUs encountered
* breakpoints simultaneously, triggering a race to become master.
* One would lose, and would already be in the slave loop when the
* master started trying to stop the world. The CPU is already where
* it is supposed to be, so we ignore the trap.
*
*/
#if defined(__lint)
void
kaif_slave_entry(void)
{
}
#else /* __lint */
/*
* We may have arrived from userland. We need to be in kernel context
* before we can save state, so we'll stash the current value in %g4
* until we've calculated the save address and have decided that we're
* heading into the debugger.
*
* %g4 is used to hold the entry MMU context until we decide whether to
* return or re-enter the debugger.
*/
#ifndef sun4v
/*
* If OBP supports preserving the Solaris kernel context register,
* then shift the nucleus bits into the primary and set context to 0,
* Otherwise, flush TLBs and clear the entire context register since
* OBP will clear it without flushing on entry to OBP.
*/
/*
* Move nucleus context page size bits into primary context page size
* and set context to 0. Use %g2 as a temporary.
*/
ba 2f
1:
#endif /* sun4v */
/*
* Flush TLBs and clear primary context register.
*/
2:
1: /* CPU save area address is now in %g6 */
/* Case 2 - CPU was already stopped, so ignore this cross call */
/* Restore MMU_PCONTEXT, which we set on the way in */
/* Were we in OBP's memory range? */
/* Case 3 - CPU in OBP - arm return trap, release the CPU */
/* We were already in kernel context, so no need to restore it */
/* Case 1 - head into debugger, via the state-saver */
/*NOTREACHED*/
#endif
/*
* The trap handler used when we're on OBP's trap table, which is used
* during initial system startup, while the debugger itself is
* executing, and when we're single-stepping. When a trap occurs that
* it can't handle, OBP will execute our Forth word (kmdb_callback).
* Our word saves TL1 state, much as kaif_save_tl1_state does for the
* other handlers. kmdb_callback will then cause control to be
* transferred to this routine.
*
* CPUs entering this routine will fall into the following categories:
*
* 1. The system is booting, and we encountered a trap that OBP couldn't
* handle. We save the CPU's state, and let it into the debugger.
*
* 2. We were single-stepping this CPU, causing it to encounter one of
* the breakpoint traps we installed for stepping. We save the CPU's
* state, and let it back into the debugger.
*
* 3. We took a trap while executing in the debugger. Before saving
* this CPU's state in the CPU-specific save area, we will let the
* debugger handle the trap. If the trap resulted from a debugger
* problem, and if the user decides to use the debugger to debug
* itself, we'll overwrite the existing state with the state saved
* by the Forth word, after which we'll let the CPU enter the
* debugger.
*
* NOTE: The Forth word and the copying code here *must* be kept
* in sync with kaif_save_tl1_state.
*/
#if defined(__lint)
void
kaif_trap_obp(void)
{
}
#else /* __lint */
1: /* CPU save area address is now in %g6 */
/*
* Are we here because of a trap we took while running the debugger, or
* because of one we took while executing kernel code?
*/
/*
* The debugger fault code will need access to saved copies of the outs
* and %y if the user elects to panic. We'll also need the saved outs if
* they decide to debug the fault with the debugger, as we'll have
* trashed the outs while asking the user how to handle the fault.
*/
/*
* Receipt of an XIR while on the debugger's stack is likely to mean
* that something has gone very wrong in the debugger. Our safest
* course of action is to bail out to OBP, thus preserving as much state
* as we can.
*/
bne 1f
1:
/*
* We're still on the debugger's stack, as we were when we took the
* fault. Re-arm the Forth word and transfer control to the debugger.
*/
/*
* If we return from kmdb_dpi_handle_fault, the trap was due to a
* problem in the debugger, and the user has elected to diagnose it
* using the debugger. When we pass back into the normal kaif_trap_obp
* flow, we'll save the debugger fault state over the state saved when
* we initially entered the debugger. Debugger fault handling trashed
* the out registers, so we'll need to restore them before returning
* to the normal flow.
*/
1: /* CPU save area address is now in %g6 */
/*
* Register state has been saved in kaif_cb_save. Now that we're sure
* we're going into the debugger using this state, copy it to the CPU-
* specific save area.
*/
/* A formality */
#ifndef sun4v
/*
* If OBP supports preserving the Solaris kernel context register,
* then shift the nucleus bits into the primary and set context to 0,
* Otherwise, flush TLBs and clear the entire context register since
* OBP will clear it without flushing on entry to OBP.
*/
/*
* Move nucleus context page size bits into primary context page size
* and set context to 0. Use %g4 as a temporary.
*/
ba 2f
1:
#endif /* sun4v */
/*
* Flush TLBs and clear primary context register.
*/
2:
#endif /* __lint */
#if defined(lint)
void
kaif_dtrap_dprot(void)
{
}
#else /* lint */
/*
* This routine is used to handle all "failed" traps. A trap is
* considered to have failed if it was not able to return to the code
* that caused the trap. A DTLB miss handler, for example, fails if
* it can't find a translation for a given address. Some traps always
* fail, because the thing that caused the trap is an actual problem
* that can't be resolved by the handler. Examples of these include
* alignment and DTLB protection faults.
*/
KAIF_CPU_GETADDR_TL1 /* uses label 1, %g1, %g2, %g7, ret in %g6 */
SET_GL(0);
#endif /* lint */