blob: 6f6ef3ae2de01b79840ec21de76539c642caa937 [file] [log] [blame]
// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
/*
* Full IPL is slow, let's cheat!
*
* Copyright 2013-2019 IBM Corp.
*/
#include <skiboot.h>
#include <slw.h>
#include <cpu.h>
#include <console.h>
#include <fsp.h>
#include <psi.h>
#include <opal.h>
#include <mem_region.h>
#include <xscom.h>
#include <interrupts.h>
#include <cec.h>
#include <timebase.h>
#include <pci.h>
#include <xive.h>
#include <chip.h>
#include <chiptod.h>
#include <ipmi.h>
#include <direct-controls.h>
#include <nvram.h>
/* Flag tested by the OPAL entry code */
static volatile bool fast_boot_release;
static volatile bool spr_set_release;
static volatile bool nmi_mce_release;
static void wait_on(volatile bool *cond)
{
sync();
if (!*cond) {
smt_lowest();
while (!*cond)
barrier();
smt_medium();
}
sync();
}
static bool cpu_state_wait_all_others(enum cpu_thread_state state,
unsigned long timeout_tb)
{
struct cpu_thread *cpu;
unsigned long end = mftb() + timeout_tb;
sync();
for_each_ungarded_cpu(cpu) {
if (cpu == this_cpu())
continue;
if (cpu->state != state) {
smt_lowest();
while (cpu->state != state) {
barrier();
if (timeout_tb && (tb_compare(mftb(), end) == TB_AAFTERB)) {
smt_medium();
return false;
}
}
smt_medium();
}
}
sync();
return true;
}
static const char *fast_reboot_disabled = NULL;
void disable_fast_reboot(const char *reason)
{
if (fast_reboot_disabled)
return;
prlog(PR_NOTICE, "RESET: Fast reboot disabled: %s\n", reason);
fast_reboot_disabled = reason;
}
void add_fast_reboot_dt_entries(void)
{
dt_check_del_prop(opal_node, "fast-reboot");
if (fast_reboot_disabled) {
dt_add_property_string(opal_node, "fast-reboot", fast_reboot_disabled);
} else {
dt_add_property_string(opal_node, "fast-reboot", "okay");
}
}
/*
* This is called by the reboot CPU after all other CPUs have been
* quiesced and stopped, to perform various sanity checks on firmware
* data (and potentially hardware), to determine whether the fast
* reboot should go ahead.
*/
static bool fast_reboot_sanity_check(void)
{
if (!mem_check_all()) {
disable_fast_reboot("Inconsistent firmware data");
return false;
}
if (!verify_romem()) {
disable_fast_reboot("Inconsistent firmware romem checksum");
return false;
}
return true;
}
void fast_reboot(void)
{
static int fast_reboot_count = 0;
if (chip_quirk(QUIRK_NO_DIRECT_CTL)) {
prlog(PR_DEBUG,
"RESET: Fast reboot disabled by quirk\n");
return;
}
/*
* Ensure all other CPUs have left OPAL calls.
*/
if (!opal_quiesce(QUIESCE_HOLD, -1)) {
disable_fast_reboot("OPAL quiesce timeout");
return;
}
if (fast_reboot_disabled &&
nvram_query_eq_dangerous("force-fast-reset", "1")) {
/* Do fast reboot even if it's been disabled */
prlog(PR_NOTICE, "RESET: Ignoring fast reboot disabled: %s\n",
fast_reboot_disabled);
} else if (fast_reboot_disabled) {
prlog(PR_NOTICE, "RESET: Fast reboot disabled: %s\n",
fast_reboot_disabled);
opal_quiesce(QUIESCE_RESUME, -1);
return;
}
prlog(PR_NOTICE, "RESET: Initiating fast reboot %d...\n", ++fast_reboot_count);
fast_boot_release = false;
spr_set_release = false;
nmi_mce_release = false;
sync();
/* Put everybody in stop except myself */
if (sreset_all_prepare()) {
prlog(PR_NOTICE, "RESET: Fast reboot failed to prepare "
"secondaries for system reset\n");
opal_quiesce(QUIESCE_RESUME, -1);
return;
}
if (!fast_reboot_sanity_check()) {
opal_quiesce(QUIESCE_RESUME, -1);
return;
}
cpu_set_sreset_enable(false);
cpu_set_ipi_enable(false);
/*
* The fast reboot sreset vector has FIXUP_ENDIAN, so secondaries can
* cope with a wrong HILE setting.
*/
copy_sreset_vector_fast_reboot();
/*
* There is no point clearing special wakeup or un-quiesce due to
* failure after this point, because we will be going to full IPL.
* Less cleanup work means less opportunity to fail.
*/
/* Send everyone else to 0x100 */
if (sreset_all_others() != OPAL_SUCCESS) {
prlog(PR_NOTICE, "RESET: Fast reboot failed to system reset "
"secondaries\n");
return;
}
/* Ensure all the sresets get through */
if (!cpu_state_wait_all_others(cpu_state_fast_reboot_entry, msecs_to_tb(1000))) {
prlog(PR_NOTICE, "RESET: Fast reboot timed out waiting for "
"secondaries to call in\n");
return;
}
prlog(PR_DEBUG, "RESET: Releasing special wakeups...\n");
sreset_all_finish();
/* This resets our quiesce state ready to enter the new kernel. */
opal_quiesce(QUIESCE_RESUME_FAST_REBOOT, -1);
console_complete_flush();
mtmsrd(0, 1); /* Clear MSR[RI] for 0x100 reset */
asm volatile("ba 0x100\n\t" : : : "memory");
for (;;)
;
}
void __noreturn enter_nap(void);
static void check_split_core(void)
{
struct cpu_thread *cpu;
u64 mask, hid0;
hid0 = mfspr(SPR_HID0);
mask = SPR_HID0_POWER8_4LPARMODE | SPR_HID0_POWER8_2LPARMODE;
if ((hid0 & mask) == 0)
return;
prlog(PR_INFO, "RESET: CPU 0x%04x is split !\n", this_cpu()->pir);
/* If it's a secondary thread, just send it to nap */
if (this_cpu()->pir & 7) {
/* Prepare to be woken up */
icp_prep_for_pm();
/* Setup LPCR to wakeup on external interrupts only */
mtspr(SPR_LPCR, ((mfspr(SPR_LPCR) & ~SPR_LPCR_P8_PECE) |
SPR_LPCR_P8_PECE2));
isync();
/* Go to nap (doesn't return) */
enter_nap();
}
prlog(PR_INFO, "RESET: Primary, unsplitting... \n");
/* Trigger unsplit operation and update SLW image */
hid0 &= ~SPR_HID0_POWER8_DYNLPARDIS;
set_hid0(hid0);
opal_slw_set_reg(this_cpu()->pir, SPR_HID0, hid0);
/* Wait for unsplit */
while (mfspr(SPR_HID0) & mask)
cpu_relax();
/* Now the guys are sleeping, wake'em up. They will come back
* via reset and continue the fast reboot process normally.
* No need to wait.
*/
prlog(PR_INFO, "RESET: Waking unsplit secondaries... \n");
for_each_cpu(cpu) {
if (!cpu_is_sibling(cpu, this_cpu()) || (cpu == this_cpu()))
continue;
icp_kick_cpu(cpu);
}
}
static void cleanup_cpu_state(void)
{
struct cpu_thread *cpu = this_cpu();
if (proc_gen == proc_gen_p9)
xive_cpu_reset();
else if (proc_gen == proc_gen_p10)
xive2_cpu_reset();
/* Per core cleanup */
if (cpu_is_thread0(cpu) || cpu_is_core_chiplet_primary(cpu)) {
/* Shared SPRs whacked back to normal */
/* XXX Update the SLW copies ! Also dbl check HIDs etc... */
init_shared_sprs();
#ifdef CONFIG_P8
if (proc_gen == proc_gen_p8) {
/* If somebody was in fast_sleep, we may have a
* workaround to undo
*/
if (cpu->in_fast_sleep) {
prlog(PR_DEBUG, "RESET: CPU 0x%04x in fast sleep"
" undoing workarounds...\n", cpu->pir);
fast_sleep_exit();
}
/* The TLB surely contains garbage.
* P9 clears TLBs in cpu_fast_reboot_complete
*/
cleanup_local_tlb();
}
#endif
/* And we might have lost TB sync */
chiptod_wakeup_resync();
}
/* Per-thread additional cleanup */
init_replicated_sprs();
// XXX Cleanup SLW, check HIDs ...
}
/* Entry from asm after a fast reset */
void __noreturn fast_reboot_entry(void);
void __noreturn fast_reboot_entry(void)
{
struct cpu_thread *cpu = this_cpu();
void *skiboot_constant_addr kerneal_load_base_addr = KERNEL_LOAD_BASE;
void *skiboot_constant_addr initramfs_load_base_addr = INITRAMFS_LOAD_BASE;
if (proc_gen == proc_gen_p8) {
/* We reset our ICP first ! Otherwise we might get stray
* interrupts when unsplitting
*/
reset_cpu_icp();
/* If we are split, we need to unsplit. Since that can send us
* to NAP, which will come back via reset, we do it now
*/
check_split_core();
}
/* Until SPRs (notably HID[HILE]) are set and new exception vectors
* installed, nobody should take machine checks. Try to do minimal
* work between these points.
*/
disable_machine_check();
mtmsrd(0, 1); /* Clear RI */
sync();
cpu->state = cpu_state_fast_reboot_entry;
sync();
if (cpu == boot_cpu) {
cpu_state_wait_all_others(cpu_state_fast_reboot_entry, 0);
spr_set_release = true;
} else {
wait_on(&spr_set_release);
}
/* Reset SPRs */
if (cpu_is_thread0(cpu))
init_shared_sprs();
init_replicated_sprs();
if (cpu == boot_cpu) {
/* Restore skiboot vectors */
copy_exception_vectors();
copy_sreset_vector();
patch_traps(true);
}
/* Must wait for others to because shared SPRs like HID0 are only set
* by thread0, so can't enable machine checks until those have been
* set.
*/
sync();
cpu->state = cpu_state_present;
sync();
if (cpu == boot_cpu) {
cpu_state_wait_all_others(cpu_state_present, 0);
nmi_mce_release = true;
} else {
wait_on(&nmi_mce_release);
}
/* At this point skiboot exception vectors are in place and all
* cores/threads have SPRs set for running skiboot.
*/
enable_machine_check();
mtmsrd(MSR_RI, 1);
cleanup_cpu_state();
prlog(PR_DEBUG, "RESET: CPU 0x%04x reset in\n", cpu->pir);
/* The original boot CPU (not the fast reboot initiator) takes
* command. Secondaries wait for the signal then go to their secondary
* entry point.
*/
if (cpu != boot_cpu) {
wait_on(&fast_boot_release);
__secondary_cpu_entry();
}
if (proc_gen == proc_gen_p9)
xive_reset();
else if (proc_gen == proc_gen_p10)
xive2_reset();
/* Let the CPU layer do some last minute global cleanups */
cpu_fast_reboot_complete();
/* We can now do NAP mode */
cpu_set_sreset_enable(true);
cpu_set_ipi_enable(true);
prlog(PR_INFO, "RESET: Releasing secondaries...\n");
/* Release everybody */
sync();
fast_boot_release = true;
sync();
cpu->state = cpu_state_active;
sync();
/* Wait for them to respond */
cpu_state_wait_all_others(cpu_state_active, 0);
sync();
prlog(PR_INFO, "RESET: All done, cleaning up...\n");
/* Clear release flag for next time */
fast_boot_release = false;
if (!chip_quirk(QUIRK_MAMBO_CALLOUTS)) {
/*
* mem_region_clear_unused avoids these preload regions
* so it can run along side image preloading. Clear these
* regions now to catch anything not overwritten by
* preload.
*
* Mambo may have embedded payload here, so don't clear
* it at all.
*/
memset(kerneal_load_base_addr, 0, KERNEL_LOAD_SIZE);
memset(initramfs_load_base_addr, 0, INITRAMFS_LOAD_SIZE);
}
/* Start preloading kernel and ramdisk */
start_preload_kernel();
/* Start clearing memory */
start_mem_region_clear_unused();
if (platform.fast_reboot_init)
platform.fast_reboot_init();
if (proc_gen == proc_gen_p8) {
/* XXX */
/* Reset/EOI the PSI interrupt */
psi_irq_reset();
}
/* update pci nvram settings */
pci_nvram_init();
/* Remove all PCI devices */
if (pci_reset()) {
prlog(PR_NOTICE, "RESET: Fast reboot failed to reset PCI\n");
/*
* Can't return to caller here because we're past no-return.
* Attempt an IPL here which is what the caller would do.
*/
if (platform.cec_reboot)
platform.cec_reboot();
for (;;)
;
}
ipmi_set_fw_progress_sensor(IPMI_FW_PCI_INIT);
wait_mem_region_clear_unused();
/* Load and boot payload */
load_and_boot_kernel(true);
}