#include "trace.h"
#include "sysemu/kvm.h"
#include "hw/ppc/spapr.h"
+#include "hw/ppc/spapr_cpu_core.h"
#include "hw/ppc/xics.h"
#include "hw/ppc/xics_spapr.h"
#include "kvm_ppc.h"
static QLIST_HEAD(, KVMEnabledICP)
kvm_enabled_icps = QLIST_HEAD_INITIALIZER(&kvm_enabled_icps);
+static void kvm_disable_icps(void)
+{
+ KVMEnabledICP *enabled_icp, *next;
+
+ QLIST_FOREACH_SAFE(enabled_icp, &kvm_enabled_icps, node, next) {
+ QLIST_REMOVE(enabled_icp, node);
+ g_free(enabled_icp);
+ }
+}
+
/*
* ICP-KVM
*/
uint64_t state;
int ret;
+ /* The KVM XICS device is not in use */
+ if (kernel_xics_fd == -1) {
+ return;
+ }
+
/* ICP for this CPU thread is not in use, exiting */
if (!icp->cs) {
return;
}
}
-int icp_set_kvm_state(ICPState *icp)
+int icp_set_kvm_state(ICPState *icp, Error **errp)
{
uint64_t state;
int ret;
+ /* The KVM XICS device is not in use */
+ if (kernel_xics_fd == -1) {
+ return 0;
+ }
+
/* ICP for this CPU thread is not in use, exiting */
if (!icp->cs) {
return 0;
| ((uint64_t)icp->pending_priority << KVM_REG_PPC_ICP_PPRI_SHIFT);
ret = kvm_set_one_reg(icp->cs, KVM_REG_PPC_ICP_STATE, &state);
- if (ret != 0) {
- error_report("Unable to restore KVM interrupt controller state (0x%"
- PRIx64 ") for CPU %ld: %s", state, kvm_arch_vcpu_id(icp->cs),
- strerror(errno));
+ if (ret < 0) {
+ error_setg_errno(errp, -ret,
+ "Unable to restore KVM interrupt controller state (0x%"
+ PRIx64 ") for CPU %ld", state,
+ kvm_arch_vcpu_id(icp->cs));
return ret;
}
unsigned long vcpu_id;
int ret;
+ /* The KVM XICS device is not in use */
if (kernel_xics_fd == -1) {
- abort();
+ return;
}
cs = icp->cs;
uint64_t state;
int i;
+ /* The KVM XICS device is not in use */
+ if (kernel_xics_fd == -1) {
+ return;
+ }
+
for (i = 0; i < ics->nr_irqs; i++) {
ICSIRQState *irq = &ics->irqs[i];
ics_get_kvm_state(ics);
}
-int ics_set_kvm_state_one(ICSState *ics, int srcno)
+int ics_set_kvm_state_one(ICSState *ics, int srcno, Error **errp)
{
uint64_t state;
- Error *local_err = NULL;
ICSIRQState *irq = &ics->irqs[srcno];
int ret;
+ /* The KVM XICS device is not in use */
+ if (kernel_xics_fd == -1) {
+ return 0;
+ }
+
state = irq->server;
state |= (uint64_t)(irq->saved_priority & KVM_XICS_PRIORITY_MASK)
<< KVM_XICS_PRIORITY_SHIFT;
}
ret = kvm_device_access(kernel_xics_fd, KVM_DEV_XICS_GRP_SOURCES,
- srcno + ics->offset, &state, true, &local_err);
- if (local_err) {
- error_report_err(local_err);
+ srcno + ics->offset, &state, true, errp);
+ if (ret < 0) {
return ret;
}
return 0;
}
-int ics_set_kvm_state(ICSState *ics)
+int ics_set_kvm_state(ICSState *ics, Error **errp)
{
int i;
+ /* The KVM XICS device is not in use */
+ if (kernel_xics_fd == -1) {
+ return 0;
+ }
+
for (i = 0; i < ics->nr_irqs; i++) {
+ Error *local_err = NULL;
int ret;
- ret = ics_set_kvm_state_one(ics, i);
- if (ret) {
+ ret = ics_set_kvm_state_one(ics, i, &local_err);
+ if (ret < 0) {
+ error_propagate(errp, local_err);
return ret;
}
}
struct kvm_irq_level args;
int rc;
+ /* The KVM XICS device should be in use */
+ assert(kernel_xics_fd != -1);
+
args.irq = srcno + ics->offset;
if (ics->irqs[srcno].flags & XICS_FLAGS_IRQ_MSI) {
if (!val) {
}
}
-static void rtas_dummy(PowerPCCPU *cpu, SpaprMachineState *spapr,
- uint32_t token,
- uint32_t nargs, target_ulong args,
- uint32_t nret, target_ulong rets)
-{
- error_report("pseries: %s must never be called for in-kernel XICS",
- __func__);
-}
-
-int xics_kvm_init(SpaprMachineState *spapr, Error **errp)
+int xics_kvm_connect(SpaprMachineState *spapr, Error **errp)
{
int rc;
+ CPUState *cs;
+ Error *local_err = NULL;
+
+ /*
+ * The KVM XICS device already in use. This is the case when
+ * rebooting under the XICS-only interrupt mode.
+ */
+ if (kernel_xics_fd != -1) {
+ return 0;
+ }
if (!kvm_enabled() || !kvm_check_extension(kvm_state, KVM_CAP_IRQ_XICS)) {
error_setg(errp,
"KVM and IRQ_XICS capability must be present for in-kernel XICS");
- goto fail;
+ return -1;
}
- spapr_rtas_register(RTAS_IBM_SET_XIVE, "ibm,set-xive", rtas_dummy);
- spapr_rtas_register(RTAS_IBM_GET_XIVE, "ibm,get-xive", rtas_dummy);
- spapr_rtas_register(RTAS_IBM_INT_OFF, "ibm,int-off", rtas_dummy);
- spapr_rtas_register(RTAS_IBM_INT_ON, "ibm,int-on", rtas_dummy);
-
rc = kvmppc_define_rtas_kernel_token(RTAS_IBM_SET_XIVE, "ibm,set-xive");
if (rc < 0) {
- error_setg(errp, "kvmppc_define_rtas_kernel_token: ibm,set-xive");
+ error_setg_errno(&local_err, -rc,
+ "kvmppc_define_rtas_kernel_token: ibm,set-xive");
goto fail;
}
rc = kvmppc_define_rtas_kernel_token(RTAS_IBM_GET_XIVE, "ibm,get-xive");
if (rc < 0) {
- error_setg(errp, "kvmppc_define_rtas_kernel_token: ibm,get-xive");
+ error_setg_errno(&local_err, -rc,
+ "kvmppc_define_rtas_kernel_token: ibm,get-xive");
goto fail;
}
rc = kvmppc_define_rtas_kernel_token(RTAS_IBM_INT_ON, "ibm,int-on");
if (rc < 0) {
- error_setg(errp, "kvmppc_define_rtas_kernel_token: ibm,int-on");
+ error_setg_errno(&local_err, -rc,
+ "kvmppc_define_rtas_kernel_token: ibm,int-on");
goto fail;
}
rc = kvmppc_define_rtas_kernel_token(RTAS_IBM_INT_OFF, "ibm,int-off");
if (rc < 0) {
- error_setg(errp, "kvmppc_define_rtas_kernel_token: ibm,int-off");
+ error_setg_errno(&local_err, -rc,
+ "kvmppc_define_rtas_kernel_token: ibm,int-off");
goto fail;
}
/* Create the KVM XICS device */
rc = kvm_create_device(kvm_state, KVM_DEV_TYPE_XICS, false);
if (rc < 0) {
- error_setg_errno(errp, -rc, "Error on KVM_CREATE_DEVICE for XICS");
+ error_setg_errno(&local_err, -rc, "Error on KVM_CREATE_DEVICE for XICS");
goto fail;
}
kvm_msi_via_irqfd_allowed = true;
kvm_gsi_direct_mapping = true;
+ /* Create the presenters */
+ CPU_FOREACH(cs) {
+ PowerPCCPU *cpu = POWERPC_CPU(cs);
+
+ icp_kvm_realize(DEVICE(spapr_cpu_state(cpu)->icp), &local_err);
+ if (local_err) {
+ goto fail;
+ }
+ }
+
+ /* Update the KVM sources */
+ ics_set_kvm_state(spapr->ics, &local_err);
+ if (local_err) {
+ goto fail;
+ }
+
+ /* Connect the presenters to the initial VCPUs of the machine */
+ CPU_FOREACH(cs) {
+ PowerPCCPU *cpu = POWERPC_CPU(cs);
+ icp_set_kvm_state(spapr_cpu_state(cpu)->icp, &local_err);
+ if (local_err) {
+ goto fail;
+ }
+ }
+
return 0;
fail:
+ error_propagate(errp, local_err);
+ xics_kvm_disconnect(spapr, NULL);
+ return -1;
+}
+
+void xics_kvm_disconnect(SpaprMachineState *spapr, Error **errp)
+{
+ /* The KVM XICS device is not in use */
+ if (kernel_xics_fd == -1) {
+ return;
+ }
+
+ if (!kvm_enabled() || !kvm_check_extension(kvm_state, KVM_CAP_IRQ_XICS)) {
+ error_setg(errp,
+ "KVM and IRQ_XICS capability must be present for KVM XICS device");
+ return;
+ }
+
+ /*
+ * Only on P9 using the XICS-on XIVE KVM device:
+ *
+ * When the KVM device fd is closed, the device is destroyed and
+ * removed from the list of devices of the VM. The VCPU presenters
+ * are also detached from the device.
+ */
+ if (kernel_xics_fd != -1) {
+ close(kernel_xics_fd);
+ kernel_xics_fd = -1;
+ }
+
kvmppc_define_rtas_kernel_token(0, "ibm,set-xive");
kvmppc_define_rtas_kernel_token(0, "ibm,get-xive");
kvmppc_define_rtas_kernel_token(0, "ibm,int-on");
kvmppc_define_rtas_kernel_token(0, "ibm,int-off");
- return -1;
+
+ kvm_kernel_irqchip = false;
+ kvm_msi_via_irqfd_allowed = false;
+ kvm_gsi_direct_mapping = false;
+
+ /* Clear the presenter from the VCPUs */
+ kvm_disable_icps();
+}
+
+/*
+ * This is a heuristic to detect older KVMs on POWER9 hosts that don't
+ * support destruction of a KVM XICS device while the VM is running.
+ * Required to start a spapr machine with ic-mode=dual,kernel-irqchip=on.
+ */
+bool xics_kvm_has_broken_disconnect(SpaprMachineState *spapr)
+{
+ int rc;
+
+ rc = kvm_create_device(kvm_state, KVM_DEV_TYPE_XICS, false);
+ if (rc < 0) {
+ /*
+ * The error is ignored on purpose. The KVM XICS setup code
+ * will catch it again anyway. The goal here is to see if
+ * close() actually destroys the device or not.
+ */
+ return false;
+ }
+
+ close(rc);
+
+ rc = kvm_create_device(kvm_state, KVM_DEV_TYPE_XICS, false);
+ if (rc >= 0) {
+ close(rc);
+ return false;
+ }
+
+ return errno == EEXIST;
}