This enables the mce monitor command for ppc, and adds a spapr facility to inject machine check exception to a CPU by setting low level registers.
(qemu) mce 0 0x200000 0x80 0xdeadbeef 1 Disabling lock debugging due to kernel taint MCE: CPU0: machine check (Severe) Host SLB Multihit [Recovered] MCE: CPU0: PID: 495 Comm: a NIP: [0000000130ee07c8] MCE: CPU0: Initiator CPU MCE: CPU0: Unknown Signed-off-by: Nicholas Piggin <npig...@gmail.com> --- hmp-commands.hx | 20 +++++++++++++++++++- hw/ppc/spapr.c | 42 ++++++++++++++++++++++++++++++++++++++++++ include/hw/ppc/spapr.h | 3 +++ target/ppc/cpu.h | 3 +++ target/ppc/monitor.c | 26 ++++++++++++++++++++++++++ 5 files changed, 93 insertions(+), 1 deletion(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index 7f0f3974ad..4a9089b431 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1581,12 +1581,30 @@ ERST .cmd = hmp_mce, }, -#endif SRST ``mce`` *cpu* *bank* *status* *mcgstatus* *addr* *misc* Inject an MCE on the given CPU (x86 only). ERST +#endif + +#if defined(TARGET_PPC) + + { + .name = "mce", + .args_type = "cpu_index:i,srr1_mask:l,dsisr:i,dar:l,recovered:i", + .params = "cpu srr1_mask dsisr dar recovered", + .help = "inject a MCE on the given CPU", + .cmd = hmp_mce, + }, + +SRST +``mce`` *cpu* *srr1_mask* *dsisr* *dar* *recovered* + Inject an MCE on the given CPU (PPC only). +ERST + +#endif + { .name = "getfd", .args_type = "fdname:s", diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 78e649f47d..d83245c438 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -3454,6 +3454,47 @@ static void spapr_nmi(NMIState *n, int cpu_index, Error **errp) } } +typedef struct MCEInjectionParams { + uint64_t srr1_mask; + uint32_t dsisr; + uint64_t dar; + bool recovered; +} MCEInjectionParams; + +static void spapr_do_mce_on_cpu(CPUState *cs, run_on_cpu_data data) +{ + MCEInjectionParams *params = data.host_ptr; + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + uint64_t srr1_mce_bits = PPC_BITMASK(42,45) | PPC_BIT(36); /* POWER9 bits */ + + cpu_synchronize_state(cs); + + env->spr[SPR_SRR0] = env->nip; + env->spr[SPR_SRR1] = (env->msr & ~srr1_mce_bits) | + (params->srr1_mask & srr1_mce_bits); + if (params->dsisr) { + env->spr[SPR_DSISR] = params->dsisr; + env->spr[SPR_DAR] = params->dar; + } + + spapr_mce_req_event(cpu, params->recovered); +} + +static void spapr_cpu_mce_inject(PPCVirtualHypervisor *vhyp, PowerPCCPU *cpu, + uint64_t srr1_mask, uint32_t dsisr, + uint64_t dar, bool recovered) +{ + CPUState *cs = CPU(cpu); + MCEInjectionParams params = { + .srr1_mask = srr1_mask, + .dsisr = dsisr, + .dar = dar, + .recovered = recovered, + }; + run_on_cpu(cs, spapr_do_mce_on_cpu, RUN_ON_CPU_HOST_PTR(¶ms)); +} + int spapr_lmb_dt_populate(SpaprDrc *drc, SpaprMachineState *spapr, void *fdt, int *fdt_start_offset, Error **errp) { @@ -4556,6 +4597,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) vhc->encode_hpt_for_kvm_pr = spapr_encode_hpt_for_kvm_pr; vhc->cpu_exec_enter = spapr_cpu_exec_enter; vhc->cpu_exec_exit = spapr_cpu_exec_exit; + vhc->cpu_mce_inject = spapr_cpu_mce_inject; xic->ics_get = spapr_ics_get; xic->ics_resend = spapr_ics_resend; xic->icp_get = spapr_icp_get; diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index 42d64a0368..72f86a2ee8 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -929,4 +929,7 @@ void spapr_check_pagesize(SpaprMachineState *spapr, hwaddr pagesize, void spapr_set_all_lpcrs(target_ulong value, target_ulong mask); hwaddr spapr_get_rtas_addr(void); + +void spapr_mce_inject(CPUState *cs, uint64_t srr1_mask, uint32_t dsisr, + uint64_t dar, bool recovered); #endif /* HW_SPAPR_H */ diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index f8c7d6f19c..ed8d2015bd 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1189,6 +1189,9 @@ struct PPCVirtualHypervisorClass { #ifndef CONFIG_USER_ONLY void (*cpu_exec_enter)(PPCVirtualHypervisor *vhyp, PowerPCCPU *cpu); void (*cpu_exec_exit)(PPCVirtualHypervisor *vhyp, PowerPCCPU *cpu); + void (*cpu_mce_inject)(PPCVirtualHypervisor *vhyp, PowerPCCPU *cpu, + uint64_t srr1_mask, uint32_t dsisr, uint64_t dar, + bool recovered); #endif }; diff --git a/target/ppc/monitor.c b/target/ppc/monitor.c index a5a177d717..ec997ce673 100644 --- a/target/ppc/monitor.c +++ b/target/ppc/monitor.c @@ -28,6 +28,7 @@ #include "qemu/ctype.h" #include "monitor/hmp-target.h" #include "monitor/hmp.h" +#include "qapi/qmp/qdict.h" static target_long monitor_get_ccr(const struct MonitorDef *md, int val) { @@ -72,6 +73,31 @@ void hmp_info_tlb(Monitor *mon, const QDict *qdict) dump_mmu(env1); } +void hmp_mce(Monitor *mon, const QDict *qdict) +{ + CPUState *cs; + int cpu_index = qdict_get_int(qdict, "cpu_index"); + uint64_t srr1_mask = qdict_get_int(qdict, "srr1_mask"); + uint32_t dsisr = qdict_get_int(qdict, "dsisr"); + uint64_t dar = qdict_get_int(qdict, "dar"); + bool recovered = qdict_get_int(qdict, "recovered"); + + cs = qemu_get_cpu(cpu_index); + + if (cs != NULL) { + PowerPCCPU *cpu = POWERPC_CPU(cs); + + if (cpu->vhyp) { + PPCVirtualHypervisorClass *vhc = + PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); + if (vhc->cpu_mce_inject) { + vhc->cpu_mce_inject(cpu->vhyp, cpu, + srr1_mask, dsisr, dar, recovered); + } + } + } +} + const MonitorDef monitor_defs[] = { { "fpscr", offsetof(CPUPPCState, fpscr) }, /* Next instruction pointer */ -- 2.23.0