/*ARGSUSED*/ void mdboot(int cmd, int fcn, char *mdep, boolean_t invoke_cb) { processorid_t bootcpuid = 0; static int is_first_quiesce = 1; static int is_first_reset = 1; int reset_status = 0; static char fallback_str[] = "Falling back to regular reboot.\n"; if (fcn == AD_FASTREBOOT && !newkernel.fi_valid) fcn = AD_BOOT; if (!panicstr) { kpreempt_disable(); if (fcn == AD_FASTREBOOT) { mutex_enter(&cpu_lock); if (CPU_ACTIVE(cpu_get(bootcpuid))) { affinity_set(bootcpuid); } mutex_exit(&cpu_lock); } else { affinity_set(CPU_CURRENT); } } if (force_shutdown_method != AD_UNKNOWN) fcn = force_shutdown_method; /* * 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; /* * Print the reboot message now, before pausing other cpus. * There is a race condition in the printing support that * can deadlock multiprocessor machines. */ if (!(fcn == AD_HALT || fcn == AD_POWEROFF)) prom_printf("rebooting...\n"); if (IN_XPV_PANIC()) reset(); /* * We can't bring up the console from above lock level, so do it now */ pm_cfb_check_and_powerup(); /* make sure there are no more changes to the device tree */ devtree_freeze(); if (invoke_cb) (void) callb_execute_class(CB_CL_MDBOOT, NULL); /* * Clear any unresolved UEs from memory. */ page_retire_mdboot(); #if defined(__xpv) /* * XXPV Should probably think some more about how we deal * with panicing before it's really safe to panic. * On hypervisors, we reboot very quickly.. Perhaps panic * should only attempt to recover by rebooting if, * say, we were able to mount the root filesystem, * or if we successfully launched init(1m). */ if (panicstr && proc_init == NULL) (void) HYPERVISOR_shutdown(SHUTDOWN_poweroff); #endif /* * stop other cpus and 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. */ (void) spl6(); if (!panicstr) { mutex_enter(&cpu_lock); pause_cpus(NULL, NULL); mutex_exit(&cpu_lock); } /* * If the system is panicking, the preloaded kernel is valid, and * fastreboot_onpanic has been set, and the system has been up for * longer than fastreboot_onpanic_uptime (default to 10 minutes), * choose Fast Reboot. */ if (fcn == AD_BOOT && panicstr && newkernel.fi_valid && fastreboot_onpanic && (panic_lbolt - lbolt_at_boot) > fastreboot_onpanic_uptime) { fcn = AD_FASTREBOOT; } /* * Try to quiesce devices. */ if (is_first_quiesce) { /* * Clear is_first_quiesce before calling quiesce_devices() * so that if quiesce_devices() causes panics, it will not * be invoked again. */ is_first_quiesce = 0; quiesce_active = 1; quiesce_devices(ddi_root_node(), &reset_status); if (reset_status == -1) { if (fcn == AD_FASTREBOOT && !force_fastreboot) { prom_printf("Driver(s) not capable of fast " "reboot.\n"); prom_printf(fallback_str); fastreboot_capable = 0; fcn = AD_BOOT; } else if (fcn != AD_FASTREBOOT) fastreboot_capable = 0; } quiesce_active = 0; } /* * Try to reset devices. reset_leaves() should only be called * a) when there are no other threads that could be accessing devices, * and * b) on a system that's not capable of fast reboot (fastreboot_capable * being 0), or on a system where quiesce_devices() failed to * complete (quiesce_active being 1). */ if (is_first_reset && (!fastreboot_capable || quiesce_active)) { /* * Clear is_first_reset before calling reset_devices() * so that if reset_devices() causes panics, it will not * be invoked again. */ is_first_reset = 0; reset_leaves(); } /* Verify newkernel checksum */ if (fastreboot_capable && fcn == AD_FASTREBOOT && fastboot_cksum_verify(&newkernel) != 0) { fastreboot_capable = 0; prom_printf("Fast reboot: checksum failed for the new " "kernel.\n"); prom_printf(fallback_str); } (void) spl8(); if (fastreboot_capable && fcn == AD_FASTREBOOT) { /* * psm_shutdown is called within fast_reboot() */ fast_reboot(); } else { (*psm_shutdownf)(cmd, fcn); if (fcn == AD_HALT || fcn == AD_POWEROFF) halt((char *)NULL); else prom_reboot(""); } /*NOTREACHED*/ }
int cpr(int fcn, void *mdep) { #if defined(__sparc) static const char noswapstr[] = "reusable statefile requires " "that no swap area be configured.\n"; static const char blockstr[] = "reusable statefile must be " "a block device. See power.conf(4) and pmconfig(1M).\n"; static const char normalfmt[] = "cannot run normal " "checkpoint/resume when in reusable statefile mode. " "use uadmin A_FREEZE AD_REUSEFINI (uadmin %d %d) " "to exit reusable statefile mode.\n"; static const char modefmt[] = "%s in reusable mode.\n"; #endif register int rc = 0; int cpr_sleeptype; /* * First, reject commands that we don't (yet) support on this arch. * This is easier to understand broken out like this than grotting * through the second switch below. */ switch (fcn) { #if defined(__sparc) case AD_CHECK_SUSPEND_TO_RAM: case AD_SUSPEND_TO_RAM: return (ENOTSUP); case AD_CHECK_SUSPEND_TO_DISK: case AD_SUSPEND_TO_DISK: case AD_CPR_REUSEINIT: case AD_CPR_NOCOMPRESS: case AD_CPR_FORCE: case AD_CPR_REUSABLE: case AD_CPR_REUSEFINI: case AD_CPR_TESTZ: case AD_CPR_TESTNOZ: case AD_CPR_TESTHALT: case AD_CPR_SUSP_DEVICES: cpr_sleeptype = CPR_TODISK; break; #endif #if defined(__x86) case AD_CHECK_SUSPEND_TO_DISK: case AD_SUSPEND_TO_DISK: case AD_CPR_REUSEINIT: case AD_CPR_NOCOMPRESS: case AD_CPR_FORCE: case AD_CPR_REUSABLE: case AD_CPR_REUSEFINI: case AD_CPR_TESTZ: case AD_CPR_TESTNOZ: case AD_CPR_TESTHALT: case AD_CPR_PRINT: return (ENOTSUP); /* The DEV_* values need to be removed after sys-syspend is fixed */ case DEV_CHECK_SUSPEND_TO_RAM: case DEV_SUSPEND_TO_RAM: case AD_CPR_SUSP_DEVICES: case AD_CHECK_SUSPEND_TO_RAM: case AD_SUSPEND_TO_RAM: case AD_LOOPBACK_SUSPEND_TO_RAM_PASS: case AD_LOOPBACK_SUSPEND_TO_RAM_FAIL: case AD_FORCE_SUSPEND_TO_RAM: case AD_DEVICE_SUSPEND_TO_RAM: cpr_sleeptype = CPR_TORAM; break; #endif } #if defined(__sparc) /* * Need to know if we're in reusable mode, but we will likely have * rebooted since REUSEINIT, so we have to get the info from the * file system */ if (!cpr_reusable_mode) cpr_reusable_mode = cpr_get_reusable_mode(); cpr_forget_cprconfig(); #endif switch (fcn) { #if defined(__sparc) case AD_CPR_REUSEINIT: if (!i_cpr_reusable_supported()) return (ENOTSUP); if (!cpr_statefile_is_spec()) { cpr_err(CE_CONT, blockstr); return (EINVAL); } if ((rc = cpr_check_spec_statefile()) != 0) return (rc); if (swapinfo) { cpr_err(CE_CONT, noswapstr); return (EINVAL); } cpr_test_mode = 0; break; case AD_CPR_NOCOMPRESS: case AD_CPR_COMPRESS: case AD_CPR_FORCE: if (cpr_reusable_mode) { cpr_err(CE_CONT, normalfmt, A_FREEZE, AD_REUSEFINI); return (ENOTSUP); } cpr_test_mode = 0; break; case AD_CPR_REUSABLE: if (!i_cpr_reusable_supported()) return (ENOTSUP); if (!cpr_statefile_is_spec()) { cpr_err(CE_CONT, blockstr); return (EINVAL); } if ((rc = cpr_check_spec_statefile()) != 0) return (rc); if (swapinfo) { cpr_err(CE_CONT, noswapstr); return (EINVAL); } if ((rc = cpr_reusable_mount_check()) != 0) return (rc); cpr_test_mode = 0; break; case AD_CPR_REUSEFINI: if (!i_cpr_reusable_supported()) return (ENOTSUP); cpr_test_mode = 0; break; case AD_CPR_TESTZ: case AD_CPR_TESTNOZ: case AD_CPR_TESTHALT: if (cpr_reusable_mode) { cpr_err(CE_CONT, normalfmt, A_FREEZE, AD_REUSEFINI); return (ENOTSUP); } cpr_test_mode = 1; break; case AD_CPR_CHECK: if (!i_cpr_is_supported(cpr_sleeptype) || cpr_reusable_mode) return (ENOTSUP); return (0); case AD_CPR_PRINT: CPR_STAT_EVENT_END("POST CPR DELAY"); cpr_stat_event_print(); return (0); #endif case AD_CPR_DEBUG0: cpr_debug = 0; return (0); case AD_CPR_DEBUG1: case AD_CPR_DEBUG2: case AD_CPR_DEBUG3: case AD_CPR_DEBUG4: case AD_CPR_DEBUG5: case AD_CPR_DEBUG7: case AD_CPR_DEBUG8: cpr_debug |= CPR_DEBUG_BIT(fcn); return (0); case AD_CPR_DEBUG9: cpr_debug |= CPR_DEBUG6; return (0); /* The DEV_* values need to be removed after sys-syspend is fixed */ case DEV_CHECK_SUSPEND_TO_RAM: case DEV_SUSPEND_TO_RAM: case AD_CHECK_SUSPEND_TO_RAM: case AD_SUSPEND_TO_RAM: cpr_test_point = LOOP_BACK_NONE; break; case AD_LOOPBACK_SUSPEND_TO_RAM_PASS: cpr_test_point = LOOP_BACK_PASS; break; case AD_LOOPBACK_SUSPEND_TO_RAM_FAIL: cpr_test_point = LOOP_BACK_FAIL; break; case AD_FORCE_SUSPEND_TO_RAM: cpr_test_point = FORCE_SUSPEND_TO_RAM; break; case AD_DEVICE_SUSPEND_TO_RAM: if (mdep == NULL) { /* Didn't pass enough arguments */ return (EINVAL); } cpr_test_point = DEVICE_SUSPEND_TO_RAM; cpr_device = (major_t)atoi((char *)mdep); break; case AD_CPR_SUSP_DEVICES: cpr_test_point = FORCE_SUSPEND_TO_RAM; if (cpr_suspend_devices(ddi_root_node()) != DDI_SUCCESS) cmn_err(CE_WARN, "Some devices did not suspend " "and may be unusable"); (void) cpr_resume_devices(ddi_root_node(), 0); return (0); default: return (ENOTSUP); } if (!i_cpr_is_supported(cpr_sleeptype)) return (ENOTSUP); #if defined(__sparc) if ((cpr_sleeptype == CPR_TODISK && !cpr_is_ufs(rootvfs) && !cpr_is_zfs(rootvfs))) return (ENOTSUP); #endif if (fcn == AD_CHECK_SUSPEND_TO_RAM || fcn == DEV_CHECK_SUSPEND_TO_RAM) { ASSERT(i_cpr_is_supported(cpr_sleeptype)); return (0); } #if defined(__sparc) if (fcn == AD_CPR_REUSEINIT) { if (mutex_tryenter(&cpr_slock) == 0) return (EBUSY); if (cpr_reusable_mode) { cpr_err(CE_CONT, modefmt, "already"); mutex_exit(&cpr_slock); return (EBUSY); } rc = i_cpr_reuseinit(); mutex_exit(&cpr_slock); return (rc); } if (fcn == AD_CPR_REUSEFINI) { if (mutex_tryenter(&cpr_slock) == 0) return (EBUSY); if (!cpr_reusable_mode) { cpr_err(CE_CONT, modefmt, "not"); mutex_exit(&cpr_slock); return (EINVAL); } rc = i_cpr_reusefini(); mutex_exit(&cpr_slock); return (rc); } #endif /* * acquire cpr serial lock and init cpr state structure. */ if (rc = cpr_init(fcn)) return (rc); #if defined(__sparc) if (fcn == AD_CPR_REUSABLE) { if ((rc = i_cpr_check_cprinfo()) != 0) { mutex_exit(&cpr_slock); return (rc); } } #endif /* * Call the main cpr routine. If we are successful, we will be coming * down from the resume side, otherwise we are still in suspend. */ cpr_err(CE_CONT, "System is being suspended"); if (rc = cpr_main(cpr_sleeptype)) { CPR->c_flags |= C_ERROR; PMD(PMD_SX, ("cpr: Suspend operation failed.\n")) cpr_err(CE_NOTE, "Suspend operation failed."); } else if (CPR->c_flags & C_SUSPENDING) { /* * In the suspend to RAM case, by the time we get * control back we're already resumed */ if (cpr_sleeptype == CPR_TORAM) { PMD(PMD_SX, ("cpr: cpr CPR_TORAM done\n")) cpr_done(); return (rc); } #if defined(__sparc) PMD(PMD_SX, ("cpr: Suspend operation succeeded.\n")) /* * Back from a successful checkpoint */ if (fcn == AD_CPR_TESTZ || fcn == AD_CPR_TESTNOZ) { mdboot(0, AD_BOOT, "", B_FALSE); /* NOTREACHED */ } /* make sure there are no more changes to the device tree */ PMD(PMD_SX, ("cpr: dev tree freeze\n")) devtree_freeze(); /* * stop other cpus and 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. */ PMD(PMD_SX, ("cpr: stop other cpus\n")) i_cpr_stop_other_cpus(); PMD(PMD_SX, ("cpr: spl6\n")) (void) spl6(); /* * try and reset leaf devices. reset_leaves() should only * be called when there are no other threads that could be * accessing devices */ PMD(PMD_SX, ("cpr: reset leaves\n")) reset_leaves(); /* * If i_cpr_power_down() succeeds, it'll not return * * Drives with write-cache enabled need to flush * their cache. */ if (fcn != AD_CPR_TESTHALT) { PMD(PMD_SX, ("cpr: power down\n")) (void) i_cpr_power_down(cpr_sleeptype); } ASSERT(cpr_sleeptype == CPR_TODISK); /* currently CPR_TODISK comes back via a boot path */ CPR_DEBUG(CPR_DEBUG1, "(Done. Please Switch Off)\n"); halt(NULL); /* NOTREACHED */ #endif } PMD(PMD_SX, ("cpr: cpr done\n")) cpr_done(); return (rc); }