From: Aditya Gupta <[email protected]> During MPIPL (aka fadump), after a kernel crash, the kernel does opal_cec_reboot2 opal call, signifying an abnormal termination. When OPAL receives this opal call, it further triggers SBE S0 interrupt, to trigger a MPIPL boot.
Currently S0 interrupt is unimplemented in QEMU. Implement S0 interrupt as 'pause_vcpus' + 'guest_reset' in QEMU, as the SBE's implementation of S0 seems to be basically "stop all clocks" and then "host reset". pause_vcpus is done in a later patch when register preserving support is added See 'stopClocksS0' in SBE source code for more information. Also log both S0 and S1 interrupts. Reviewed-by: Hari Bathini <[email protected]> Reviewed-by: Sourabh Jain <[email protected]> Signed-off-by: Aditya Gupta <[email protected]> Tested-by: Shivang Upadhyay <[email protected]> Link: https://lore.kernel.org/qemu-devel/[email protected] Signed-off-by: Harsh Prateek Bora <[email protected]> --- include/hw/ppc/pnv.h | 5 +++++ include/hw/ppc/pnv_mpipl.h | 19 +++++++++++++++++++ hw/ppc/pnv_mpipl.c | 26 ++++++++++++++++++++++++++ hw/ppc/pnv_sbe.c | 29 +++++++++++++++++++++++++++++ hw/ppc/meson.build | 1 + 5 files changed, 80 insertions(+) create mode 100644 include/hw/ppc/pnv_mpipl.h create mode 100644 hw/ppc/pnv_mpipl.c diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h index ce3ce73b53..19c7170e74 100644 --- a/include/hw/ppc/pnv.h +++ b/include/hw/ppc/pnv.h @@ -25,6 +25,7 @@ #include "hw/core/sysbus.h" #include "hw/ipmi/ipmi.h" #include "hw/ppc/pnv_pnor.h" +#include "hw/ppc/pnv_mpipl.h" #define TYPE_PNV_CHIP "pnv-chip" @@ -113,6 +114,7 @@ struct PnvMachineState { bool lpar_per_core; Notifier machine_init_done; + MpiplPreservedState mpipl_state; }; PnvChip *pnv_get_chip(PnvMachineState *pnv, uint32_t chip_id); @@ -292,4 +294,7 @@ void pnv_bmc_set_pnor(IPMIBmc *bmc, PnvPnor *pnor); #define PNV11_OCC_SENSOR_BASE(chip) PNV10_OCC_SENSOR_BASE(chip) +/* MPIPL helpers */ +void do_mpipl_preserve(PnvMachineState *pnv); + #endif /* PPC_PNV_H */ diff --git a/include/hw/ppc/pnv_mpipl.h b/include/hw/ppc/pnv_mpipl.h new file mode 100644 index 0000000000..61ef7ef8fe --- /dev/null +++ b/include/hw/ppc/pnv_mpipl.h @@ -0,0 +1,19 @@ +/* + * Emulation of MPIPL (Memory Preserving Initial Program Load), aka fadump + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PNV_MPIPL_H +#define PNV_MPIPL_H + +#include <stdbool.h> + +typedef struct MpiplPreservedState MpiplPreservedState; + +/* Preserved state to be saved in PnvMachineState */ +struct MpiplPreservedState { + bool is_next_boot_mpipl; +}; + +#endif diff --git a/hw/ppc/pnv_mpipl.c b/hw/ppc/pnv_mpipl.c new file mode 100644 index 0000000000..d8c9b7a428 --- /dev/null +++ b/hw/ppc/pnv_mpipl.c @@ -0,0 +1,26 @@ +/* + * Emulation of MPIPL (Memory Preserving Initial Program Load), aka fadump + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "system/runstate.h" +#include "hw/ppc/pnv.h" +#include "hw/ppc/pnv_mpipl.h" + +void do_mpipl_preserve(PnvMachineState *pnv) +{ + /* Mark next boot as Memory-preserving boot */ + pnv->mpipl_state.is_next_boot_mpipl = true; + + /* + * Do a guest reset. + * Next reset will see 'is_next_boot_mpipl' as true, and trigger MPIPL + * + * Requirement: + * GUEST_RESET is expected to NOT clear the memory, as is the case when + * this is merged + */ + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); +} diff --git a/hw/ppc/pnv_sbe.c b/hw/ppc/pnv_sbe.c index 247617338a..5a2b3342d1 100644 --- a/hw/ppc/pnv_sbe.c +++ b/hw/ppc/pnv_sbe.c @@ -26,6 +26,9 @@ #include "hw/ppc/pnv.h" #include "hw/ppc/pnv_xscom.h" #include "hw/ppc/pnv_sbe.h" +#include "hw/ppc/pnv_mpipl.h" +#include "system/cpus.h" +#include "system/runstate.h" #include "trace.h" /* @@ -113,11 +116,37 @@ static uint64_t pnv_sbe_power9_xscom_ctrl_read(void *opaque, hwaddr addr, static void pnv_sbe_power9_xscom_ctrl_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { + PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine()); + PnvSBE *sbe = opaque; uint32_t offset = addr >> 3; trace_pnv_sbe_xscom_ctrl_write(addr, val); switch (offset) { + case SBE_CONTROL_REG_RW: + switch (val) { + case SBE_CONTROL_REG_S0: + qemu_log_mask(LOG_UNIMP, "SBE: S0 Interrupt triggered\n"); + + pnv_sbe_set_host_doorbell(sbe, sbe->host_doorbell | SBE_HOST_RESPONSE_MASK); + + /* Preserve memory regions and CPU state, if MPIPL is registered */ + do_mpipl_preserve(pnv); + + /* + * Control may not come back here as 'do_mpipl_preserve' triggers + * a guest reboot + */ + break; + case SBE_CONTROL_REG_S1: + qemu_log_mask(LOG_UNIMP, "SBE: S1 Interrupt triggered\n"); + break; + default: + qemu_log_mask(LOG_UNIMP, + "SBE: CONTROL_REG_RW: Unknown value: Ox%." + HWADDR_PRIx "\n", val); + } + break; default: qemu_log_mask(LOG_UNIMP, "SBE Unimplemented register: Ox%" HWADDR_PRIx "\n", addr >> 3); diff --git a/hw/ppc/meson.build b/hw/ppc/meson.build index f7dac87a2a..c61fba4ec8 100644 --- a/hw/ppc/meson.build +++ b/hw/ppc/meson.build @@ -56,6 +56,7 @@ ppc_ss.add(when: 'CONFIG_POWERNV', if_true: files( 'pnv_pnor.c', 'pnv_nest_pervasive.c', 'pnv_n1_chiplet.c', + 'pnv_mpipl.c', )) # PowerPC 4xx boards ppc_ss.add(when: 'CONFIG_PPC405', if_true: files( -- 2.52.0
