target/ppc: Handle NMI guest exit
Memory error such as bit flips that cannot be corrected by hardware are passed on to the kernel for handling. If the memory address in error belongs to guest then the guest kernel is responsible for taking suitable action. Patch [1] enhances KVM to exit guest with exit reason set to KVM_EXIT_NMI in such cases. This patch handles KVM_EXIT_NMI exit. [1] https://www.spinics.net/lists/kvm-ppc/msg12637.html (e20bbd3d and related commits) Signed-off-by: Aravinda Prasad <arawinda.p@gmail.com> Signed-off-by: Ganesh Goudar <ganeshgr@linux.ibm.com> Reviewed-by: David Gibson <david@gibson.dropbear.id.au> Reviewed-by: Greg Kurz <groug@kaod.org> Message-Id: <20200130184423.20519-4-ganeshgr@linux.ibm.com> [dwg: #ifdefs to fix compile for 32-bit target] Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
This commit is contained in:
parent
9d953ce447
commit
9ac703ac5f
@ -1677,6 +1677,12 @@ static void spapr_machine_reset(MachineState *machine)
|
|||||||
first_ppc_cpu->env.gpr[5] = 0;
|
first_ppc_cpu->env.gpr[5] = 0;
|
||||||
|
|
||||||
spapr->cas_reboot = false;
|
spapr->cas_reboot = false;
|
||||||
|
|
||||||
|
spapr->mc_status = -1;
|
||||||
|
spapr->guest_machine_check_addr = -1;
|
||||||
|
|
||||||
|
/* Signal all vCPUs waiting on this condition */
|
||||||
|
qemu_cond_broadcast(&spapr->mc_delivery_cond);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void spapr_create_nvram(SpaprMachineState *spapr)
|
static void spapr_create_nvram(SpaprMachineState *spapr)
|
||||||
@ -2971,6 +2977,8 @@ static void spapr_machine_init(MachineState *machine)
|
|||||||
|
|
||||||
kvmppc_spapr_enable_inkernel_multitce();
|
kvmppc_spapr_enable_inkernel_multitce();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qemu_cond_init(&spapr->mc_delivery_cond);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int spapr_kvm_type(MachineState *machine, const char *vm_type)
|
static int spapr_kvm_type(MachineState *machine, const char *vm_type)
|
||||||
|
@ -40,6 +40,7 @@
|
|||||||
#include "hw/ppc/spapr_drc.h"
|
#include "hw/ppc/spapr_drc.h"
|
||||||
#include "qemu/help_option.h"
|
#include "qemu/help_option.h"
|
||||||
#include "qemu/bcd.h"
|
#include "qemu/bcd.h"
|
||||||
|
#include "qemu/main-loop.h"
|
||||||
#include "hw/ppc/spapr_ovec.h"
|
#include "hw/ppc/spapr_ovec.h"
|
||||||
#include <libfdt.h>
|
#include <libfdt.h>
|
||||||
|
|
||||||
@ -622,6 +623,42 @@ void spapr_hotplug_req_remove_by_count_indexed(SpaprDrcType drc_type,
|
|||||||
RTAS_LOG_V6_HP_ACTION_REMOVE, drc_type, &drc_id);
|
RTAS_LOG_V6_HP_ACTION_REMOVE, drc_type, &drc_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void spapr_mce_req_event(PowerPCCPU *cpu)
|
||||||
|
{
|
||||||
|
SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
|
||||||
|
CPUState *cs = CPU(cpu);
|
||||||
|
|
||||||
|
if (spapr->guest_machine_check_addr == -1) {
|
||||||
|
/*
|
||||||
|
* This implies that we have hit a machine check either when the
|
||||||
|
* guest has not registered FWNMI (i.e., "ibm,nmi-register" not
|
||||||
|
* called) or between system reset and "ibm,nmi-register".
|
||||||
|
* Fall back to the old machine check behavior in such cases.
|
||||||
|
*/
|
||||||
|
cs->exception_index = POWERPC_EXCP_MCHECK;
|
||||||
|
ppc_cpu_do_interrupt(cs);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (spapr->mc_status != -1) {
|
||||||
|
/*
|
||||||
|
* Check whether the same CPU got machine check error
|
||||||
|
* while still handling the mc error (i.e., before
|
||||||
|
* that CPU called "ibm,nmi-interlock")
|
||||||
|
*/
|
||||||
|
if (spapr->mc_status == cpu->vcpu_id) {
|
||||||
|
qemu_system_guest_panicked(NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
qemu_cond_wait_iothread(&spapr->mc_delivery_cond);
|
||||||
|
/* Meanwhile if the system is reset, then just return */
|
||||||
|
if (spapr->guest_machine_check_addr == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spapr->mc_status = cpu->vcpu_id;
|
||||||
|
}
|
||||||
|
|
||||||
static void check_exception(PowerPCCPU *cpu, SpaprMachineState *spapr,
|
static void check_exception(PowerPCCPU *cpu, SpaprMachineState *spapr,
|
||||||
uint32_t token, uint32_t nargs,
|
uint32_t token, uint32_t nargs,
|
||||||
target_ulong args,
|
target_ulong args,
|
||||||
|
@ -191,6 +191,15 @@ struct SpaprMachineState {
|
|||||||
* occurs during the unplug process. */
|
* occurs during the unplug process. */
|
||||||
QTAILQ_HEAD(, SpaprDimmState) pending_dimm_unplugs;
|
QTAILQ_HEAD(, SpaprDimmState) pending_dimm_unplugs;
|
||||||
|
|
||||||
|
/* State related to "ibm,nmi-register" and "ibm,nmi-interlock" calls */
|
||||||
|
target_ulong guest_machine_check_addr;
|
||||||
|
/*
|
||||||
|
* mc_status is set to -1 if mc is not in progress, else is set to the CPU
|
||||||
|
* handling the mc.
|
||||||
|
*/
|
||||||
|
int mc_status;
|
||||||
|
QemuCond mc_delivery_cond;
|
||||||
|
|
||||||
/*< public >*/
|
/*< public >*/
|
||||||
char *kvm_type;
|
char *kvm_type;
|
||||||
char *host_model;
|
char *host_model;
|
||||||
@ -804,6 +813,7 @@ void spapr_clear_pending_events(SpaprMachineState *spapr);
|
|||||||
int spapr_max_server_number(SpaprMachineState *spapr);
|
int spapr_max_server_number(SpaprMachineState *spapr);
|
||||||
void spapr_store_hpte(PowerPCCPU *cpu, hwaddr ptex,
|
void spapr_store_hpte(PowerPCCPU *cpu, hwaddr ptex,
|
||||||
uint64_t pte0, uint64_t pte1);
|
uint64_t pte0, uint64_t pte1);
|
||||||
|
void spapr_mce_req_event(PowerPCCPU *cpu);
|
||||||
|
|
||||||
/* DRC callbacks. */
|
/* DRC callbacks. */
|
||||||
void spapr_core_release(DeviceState *dev);
|
void spapr_core_release(DeviceState *dev);
|
||||||
|
@ -1705,6 +1705,13 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
|
|||||||
ret = 0;
|
ret = 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
#if defined(TARGET_PPC64)
|
||||||
|
case KVM_EXIT_NMI:
|
||||||
|
trace_kvm_handle_nmi_exception();
|
||||||
|
ret = kvm_handle_nmi(cpu, run);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "KVM: unknown exit reason %d\n", run->exit_reason);
|
fprintf(stderr, "KVM: unknown exit reason %d\n", run->exit_reason);
|
||||||
ret = -1;
|
ret = -1;
|
||||||
@ -2800,6 +2807,17 @@ int kvm_arch_msi_data_to_gsi(uint32_t data)
|
|||||||
return data & 0xffff;
|
return data & 0xffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(TARGET_PPC64)
|
||||||
|
int kvm_handle_nmi(PowerPCCPU *cpu, struct kvm_run *run)
|
||||||
|
{
|
||||||
|
cpu_synchronize_state(CPU(cpu));
|
||||||
|
|
||||||
|
spapr_mce_req_event(cpu);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
int kvmppc_enable_hwrng(void)
|
int kvmppc_enable_hwrng(void)
|
||||||
{
|
{
|
||||||
if (!kvm_enabled() || !kvm_check_extension(kvm_state, KVM_CAP_PPC_HWRNG)) {
|
if (!kvm_enabled() || !kvm_check_extension(kvm_state, KVM_CAP_PPC_HWRNG)) {
|
||||||
|
@ -84,6 +84,8 @@ void kvm_check_mmu(PowerPCCPU *cpu, Error **errp);
|
|||||||
void kvmppc_set_reg_ppc_online(PowerPCCPU *cpu, unsigned int online);
|
void kvmppc_set_reg_ppc_online(PowerPCCPU *cpu, unsigned int online);
|
||||||
void kvmppc_set_reg_tb_offset(PowerPCCPU *cpu, int64_t tb_offset);
|
void kvmppc_set_reg_tb_offset(PowerPCCPU *cpu, int64_t tb_offset);
|
||||||
|
|
||||||
|
int kvm_handle_nmi(PowerPCCPU *cpu, struct kvm_run *run);
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
static inline uint32_t kvmppc_get_tbfreq(void)
|
static inline uint32_t kvmppc_get_tbfreq(void)
|
||||||
|
@ -28,3 +28,4 @@ kvm_handle_papr_hcall(void) "handle PAPR hypercall"
|
|||||||
kvm_handle_epr(void) "handle epr"
|
kvm_handle_epr(void) "handle epr"
|
||||||
kvm_handle_watchdog_expiry(void) "handle watchdog expiry"
|
kvm_handle_watchdog_expiry(void) "handle watchdog expiry"
|
||||||
kvm_handle_debug_exception(void) "handle debug exception"
|
kvm_handle_debug_exception(void) "handle debug exception"
|
||||||
|
kvm_handle_nmi_exception(void) "handle NMI exception"
|
||||||
|
Loading…
Reference in New Issue
Block a user