summaryrefslogtreecommitdiff
path: root/usr/src/uts/sun4u/os/mach_cpu_states.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/sun4u/os/mach_cpu_states.c')
-rw-r--r--usr/src/uts/sun4u/os/mach_cpu_states.c484
1 files changed, 484 insertions, 0 deletions
diff --git a/usr/src/uts/sun4u/os/mach_cpu_states.c b/usr/src/uts/sun4u/os/mach_cpu_states.c
new file mode 100644
index 0000000000..6fae082669
--- /dev/null
+++ b/usr/src/uts/sun4u/os/mach_cpu_states.c
@@ -0,0 +1,484 @@
+/*
+ * 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"
+
+#include <sys/types.h>
+#include <sys/t_lock.h>
+#include <sys/uadmin.h>
+#include <sys/panic.h>
+#include <sys/reboot.h>
+#include <sys/autoconf.h>
+#include <sys/machsystm.h>
+#include <sys/promif.h>
+#include <sys/membar.h>
+#include <vm/hat_sfmmu.h>
+#include <sys/cpu_module.h>
+#include <sys/cpu_sgnblk_defs.h>
+#include <sys/intreg.h>
+#include <sys/consdev.h>
+#include <sys/kdi_impl.h>
+
+#ifdef TRAPTRACE
+#include <sys/traptrace.h>
+u_longlong_t panic_tick;
+#endif /* TRAPTRACE */
+
+extern u_longlong_t gettick();
+static void reboot_machine(char *);
+extern int disable_watchdog_on_exit;
+
+/*
+ * Machine dependent code to reboot.
+ * "mdep" is interpreted as a character pointer; if non-null, it is a pointer
+ * to a string to be used as the argument string when rebooting.
+ */
+/*ARGSUSED*/
+void
+mdboot(int cmd, int fcn, char *bootstr)
+{
+ page_t *first, *pp;
+ extern void pm_cfb_check_and_powerup(void);
+
+ /*
+ * Disable the hw watchdog timer.
+ */
+ if (disable_watchdog_on_exit && watchdog_activated) {
+ mutex_enter(&tod_lock);
+ (void) tod_ops.tod_clear_watchdog_timer();
+ mutex_exit(&tod_lock);
+ }
+
+ /*
+ * Clear any unresolved UEs from memory. We rely on the fact that on
+ * sun4u, pagezero() will always clear UEs. Since we're rebooting, we
+ * just force p_selock to appear locked so pagezero()'s assert works.
+ *
+ * Pages that were retired successfully due to multiple CEs will
+ * also be cleared.
+ */
+ if (memsegs != NULL) {
+ pp = first = page_first();
+ do {
+ if (page_isretired(pp) || page_istoxic(pp)) {
+ /* pagezero asserts PAGE_LOCKED */
+ pp->p_selock = -1;
+ pagezero(pp, 0, PAGESIZE);
+ }
+ } while ((pp = page_next(pp)) != first);
+ }
+
+ /*
+ * XXX - rconsvp is set to NULL to ensure that output messages
+ * are sent to the underlying "hardware" device using the
+ * monitor's printf routine since we are in the process of
+ * either rebooting or halting the machine.
+ */
+ rconsvp = NULL;
+
+ /*
+ * At a high interrupt level we can't:
+ * 1) bring up the console
+ * or
+ * 2) wait for pending interrupts prior to redistribution
+ * to the current CPU
+ *
+ * so we do them now.
+ */
+ pm_cfb_check_and_powerup();
+
+ /* make sure there are no more changes to the device tree */
+ devtree_freeze();
+
+ /*
+ * stop other cpus which also raise our priority. since there is only
+ * one active cpu after this, and our priority will be too high
+ * for us to be preempted, we're essentially single threaded
+ * from here on out.
+ */
+ stop_other_cpus();
+
+ /*
+ * try and reset leaf devices. reset_leaves() should only
+ * be called when there are no other threads that could be
+ * accessing devices
+ */
+ reset_leaves();
+
+ if (fcn == AD_HALT) {
+ halt((char *)NULL);
+ } else if (fcn == AD_POWEROFF) {
+ power_down(NULL);
+ } else {
+ if (bootstr == NULL) {
+ switch (fcn) {
+
+ case AD_BOOT:
+ bootstr = "";
+ break;
+
+ case AD_IBOOT:
+ bootstr = "-a";
+ break;
+
+ case AD_SBOOT:
+ bootstr = "-s";
+ break;
+
+ case AD_SIBOOT:
+ bootstr = "-sa";
+ break;
+ default:
+ cmn_err(CE_WARN,
+ "mdboot: invalid function %d", fcn);
+ bootstr = "";
+ break;
+ }
+ }
+ reboot_machine(bootstr);
+ }
+ /* MAYBE REACHED */
+}
+
+/* mdpreboot - may be called prior to mdboot while root fs still mounted */
+/*ARGSUSED*/
+void
+mdpreboot(int cmd, int fcn, char *bootstr)
+{
+}
+
+/*
+ * Halt the machine and then reboot with the device
+ * and arguments specified in bootstr.
+ */
+static void
+reboot_machine(char *bootstr)
+{
+ flush_windows();
+ stop_other_cpus(); /* send stop signal to other CPUs */
+ prom_printf("rebooting...\n");
+ /*
+ * For platforms that use CPU signatures, we
+ * need to set the signature block to OS and
+ * the state to exiting for all the processors.
+ */
+ CPU_SIGNATURE(OS_SIG, SIGST_EXIT, SIGSUBST_REBOOT, -1);
+ prom_reboot(bootstr);
+ /*NOTREACHED*/
+}
+
+/*
+ * We use the x-trap mechanism and idle_stop_xcall() to stop the other CPUs.
+ * Once in panic_idle() they raise spl, record their location, and spin.
+ */
+static void
+panic_idle(void)
+{
+ cpu_async_panic_callb(); /* check for async errors */
+
+ (void) spl7();
+
+ debug_flush_windows();
+ (void) setjmp(&curthread->t_pcb);
+
+ CPU->cpu_m.in_prom = 1;
+ membar_stld();
+
+ for (;;);
+}
+
+/*
+ * Force the other CPUs to trap into panic_idle(), and then remove them
+ * from the cpu_ready_set so they will no longer receive cross-calls.
+ */
+/*ARGSUSED*/
+void
+panic_stopcpus(cpu_t *cp, kthread_t *t, int spl)
+{
+ cpuset_t cps;
+ int i;
+
+ (void) splzs();
+ CPUSET_ALL_BUT(cps, cp->cpu_id);
+ xt_some(cps, (xcfunc_t *)idle_stop_xcall, (uint64_t)&panic_idle, NULL);
+
+ for (i = 0; i < NCPU; i++) {
+ if (i != cp->cpu_id && CPU_XCALL_READY(i)) {
+ int ntries = 0x10000;
+
+ while (!cpu[i]->cpu_m.in_prom && ntries) {
+ DELAY(50);
+ ntries--;
+ }
+
+ if (!cpu[i]->cpu_m.in_prom)
+ printf("panic: failed to stop cpu%d\n", i);
+
+ cpu[i]->cpu_flags &= ~CPU_READY;
+ cpu[i]->cpu_flags |= CPU_QUIESCED;
+ CPUSET_DEL(cpu_ready_set, cpu[i]->cpu_id);
+ }
+ }
+}
+
+/*
+ * Platform callback following each entry to panicsys(). If we've panicked at
+ * level 14, we examine t_panic_trap to see if a fatal trap occurred. If so,
+ * we disable further %tick_cmpr interrupts. If not, an explicit call to panic
+ * was made and so we re-enqueue an interrupt request structure to allow
+ * further level 14 interrupts to be processed once we lower PIL. This allows
+ * us to handle panics from the deadman() CY_HIGH_LEVEL cyclic.
+ */
+void
+panic_enter_hw(int spl)
+{
+ if (spl == ipltospl(PIL_14)) {
+ uint_t opstate = disable_vec_intr();
+
+ if (curthread->t_panic_trap != NULL) {
+ tickcmpr_disable();
+ intr_dequeue_req(PIL_14, cbe_level14_inum);
+ } else {
+ if (!tickcmpr_disabled())
+ intr_enqueue_req(PIL_14, cbe_level14_inum);
+ /*
+ * Clear SOFTINT<14>, SOFTINT<0> (TICK_INT)
+ * and SOFTINT<16> (STICK_INT) to indicate
+ * that the current level 14 has been serviced.
+ */
+ wr_clr_softint((1 << PIL_14) |
+ TICK_INT_MASK | STICK_INT_MASK);
+ }
+
+ enable_vec_intr(opstate);
+ }
+}
+
+/*
+ * Miscellaneous hardware-specific code to execute after panicstr is set
+ * by the panic code: we also print and record PTL1 panic information here.
+ */
+/*ARGSUSED*/
+void
+panic_quiesce_hw(panic_data_t *pdp)
+{
+ extern uint_t getpstate(void);
+ extern void setpstate(uint_t);
+
+#ifdef TRAPTRACE
+ /*
+ * Turn off TRAPTRACE and save the current %tick value in panic_tick.
+ */
+ if (!panic_tick)
+ panic_tick = gettick();
+ TRAPTRACE_FREEZE;
+#endif
+ /*
+ * For Platforms that use CPU signatures, we
+ * need to set the signature block to OS, the state to
+ * exiting, and the substate to panic for all the processors.
+ */
+ CPU_SIGNATURE(OS_SIG, SIGST_EXIT, SIGSUBST_PANIC, -1);
+
+ /*
+ * De-activate ECC functions and disable the watchdog timer now that
+ * we've made it through the critical part of the panic code.
+ */
+ if (watchdog_enable)
+ (void) tod_ops.tod_clear_watchdog_timer();
+
+ /*
+ * Disable further ECC errors from the CPU module and the bus nexus.
+ */
+ cpu_disable_errors();
+ (void) bus_func_invoke(BF_TYPE_ERRDIS);
+
+ /*
+ * Redirect all interrupts to the current CPU.
+ */
+ intr_redist_all_cpus_shutdown();
+
+ /*
+ * This call exists solely to support dumps to network
+ * devices after sync from OBP.
+ *
+ * If we came here via the sync callback, then on some
+ * platforms, interrupts may have arrived while we were
+ * stopped in OBP. OBP will arrange for those interrupts to
+ * be redelivered if you say "go", but not if you invoke a
+ * client callback like 'sync'. For some dump devices
+ * (network swap devices), we need interrupts to be
+ * delivered in order to dump, so we have to call the bus
+ * nexus driver to reset the interrupt state machines.
+ */
+ (void) bus_func_invoke(BF_TYPE_RESINTR);
+
+ setpstate(getpstate() | PSTATE_IE);
+}
+
+/*
+ * Platforms that use CPU signatures need to set the signature block to OS and
+ * the state to exiting for all CPUs. PANIC_CONT indicates that we're about to
+ * write the crash dump, which tells the SSP/SMS to begin a timeout routine to
+ * reboot the machine if the dump never completes.
+ */
+/*ARGSUSED*/
+void
+panic_dump_hw(int spl)
+{
+ CPU_SIGNATURE(OS_SIG, SIGST_EXIT, SIGSUBST_DUMP, -1);
+}
+
+/*
+ * for ptl1_panic
+ */
+void
+ptl1_init_cpu(struct cpu *cpu)
+{
+ ptl1_state_t *pstate = &cpu->cpu_m.ptl1_state;
+
+ /*CONSTCOND*/
+ if (sizeof (struct cpu) + PTL1_SSIZE > CPU_ALLOC_SIZE) {
+ panic("ptl1_init_cpu: not enough space left for ptl1_panic "
+ "stack, sizeof (struct cpu) = %d", sizeof (struct cpu));
+ }
+
+ pstate->ptl1_stktop = (uintptr_t)cpu + CPU_ALLOC_SIZE;
+ cpu_pa[cpu->cpu_id] = va_to_pa(cpu);
+}
+
+void
+ptl1_panic_handler(ptl1_state_t *pstate)
+{
+ static const char *ptl1_reasons[] = {
+#ifdef PTL1_PANIC_DEBUG
+ "trap for debug purpose", /* PTL1_BAD_DEBUG */
+#else
+ "unknown trap", /* PTL1_BAD_DEBUG */
+#endif
+ "register window trap", /* PTL1_BAD_WTRAP */
+ "kernel MMU miss", /* PTL1_BAD_KMISS */
+ "kernel protection fault", /* PTL1_BAD_KPROT_FAULT */
+ "ISM MMU miss", /* PTL1_BAD_ISM */
+ "kernel MMU trap", /* PTL1_BAD_MMUTRAP */
+ "kernel trap handler state", /* PTL1_BAD_TRAP */
+ "floating point trap", /* PTL1_BAD_FPTRAP */
+#ifdef DEBUG
+ "pointer to intr_req", /* PTL1_BAD_INTR_REQ */
+#else
+ "unknown trap", /* PTL1_BAD_INTR_REQ */
+#endif
+#ifdef TRAPTRACE
+ "TRACE_PTR state", /* PTL1_BAD_TRACE_PTR */
+#else
+ "unknown trap", /* PTL1_BAD_TRACE_PTR */
+#endif
+ "stack overflow", /* PTL1_BAD_STACK */
+ "DTrace flags", /* PTL1_BAD_DTRACE_FLAGS */
+ "attempt to steal locked ctx", /* PTL1_BAD_CTX_STEAL */
+ "CPU ECC error loop", /* PTL1_BAD_ECC */
+ "unknown trap", /* PTL1_BAD_HCALL */
+ };
+
+ uint_t reason = pstate->ptl1_regs.ptl1_g1;
+ uint_t tl = pstate->ptl1_regs.ptl1_trap_regs[0].ptl1_tl;
+ struct trap_info ti = { 0 };
+
+ /*
+ * Use trap_info for a place holder to call panic_savetrap() and
+ * panic_showtrap() to save and print out ptl1_panic information.
+ */
+ if (curthread->t_panic_trap == NULL)
+ curthread->t_panic_trap = &ti;
+
+ if (reason < sizeof (ptl1_reasons) / sizeof (ptl1_reasons[0]))
+ panic("bad %s at TL %u", ptl1_reasons[reason], tl);
+ else
+ panic("ptl1_panic reason 0x%x at TL %u", reason, tl);
+}
+
+void
+clear_watchdog_on_exit()
+{
+ /*
+ * Only shut down an active hardware watchdog timer if the platform
+ * has expressed an interest to.
+ */
+ if (disable_watchdog_on_exit && watchdog_activated) {
+ prom_printf("Debugging requested; hardware watchdog "
+ "disabled; reboot to re-enable.\n");
+ cmn_err(CE_WARN, "!Debugging requested; hardware watchdog "
+ "disabled; reboot to re-enable.");
+ mutex_enter(&tod_lock);
+ (void) tod_ops.tod_clear_watchdog_timer();
+ mutex_exit(&tod_lock);
+ }
+}
+
+int
+kdi_watchdog_disable(void)
+{
+ if (watchdog_activated) {
+ mutex_enter(&tod_lock);
+ (void) tod_ops.tod_clear_watchdog_timer();
+ mutex_exit(&tod_lock);
+ }
+
+ return (watchdog_activated);
+}
+
+void
+kdi_watchdog_restore(void)
+{
+ if (watchdog_enable) {
+ mutex_enter(&tod_lock);
+ (void) tod_ops.tod_set_watchdog_timer(watchdog_timeout_seconds);
+ mutex_exit(&tod_lock);
+ }
+}
+
+/*ARGSUSED*/
+void
+mach_dump_buffer_init(void)
+{
+ /*
+ * setup dump buffer to store extra crash information
+ * not applicable to sun4u
+ */
+}
+
+/*
+ * xt_sync - wait for previous x-traps to finish
+ */
+void
+xt_sync(cpuset_t cpuset)
+{
+ kpreempt_disable();
+ CPUSET_DEL(cpuset, CPU->cpu_id);
+ CPUSET_AND(cpuset, cpu_ready_set);
+ xt_some(cpuset, (xcfunc_t *)xt_sync_tl1, 0, 0);
+ kpreempt_enable();
+}