aboutsummaryrefslogtreecommitdiff
path: root/hw/intc
diff options
context:
space:
mode:
Diffstat (limited to 'hw/intc')
-rw-r--r--hw/intc/pnv_xive.c145
-rw-r--r--hw/intc/spapr_xive.c38
-rw-r--r--hw/intc/spapr_xive_kvm.c55
-rw-r--r--hw/intc/xics.c40
-rw-r--r--hw/intc/xics_kvm.c117
-rw-r--r--hw/intc/xics_spapr.c55
-rw-r--r--hw/intc/xive.c72
7 files changed, 333 insertions, 189 deletions
diff --git a/hw/intc/pnv_xive.c b/hw/intc/pnv_xive.c
index a55c2bbc88..4dc92ef1e3 100644
--- a/hw/intc/pnv_xive.c
+++ b/hw/intc/pnv_xive.c
@@ -169,7 +169,7 @@ static uint64_t pnv_xive_vst_addr_indirect(PnvXive *xive, uint32_t type,
vsd = ldq_be_dma(&address_space_memory, vsd_addr);
if (!(vsd & VSD_ADDRESS_MASK)) {
- xive_error(xive, "VST: invalid %s entry %x !?", info->name, 0);
+ xive_error(xive, "VST: invalid %s entry %x !?", info->name, idx);
return 0;
}
@@ -190,7 +190,7 @@ static uint64_t pnv_xive_vst_addr_indirect(PnvXive *xive, uint32_t type,
vsd = ldq_be_dma(&address_space_memory, vsd_addr);
if (!(vsd & VSD_ADDRESS_MASK)) {
- xive_error(xive, "VST: invalid %s entry %x !?", info->name, 0);
+ xive_error(xive, "VST: invalid %s entry %x !?", info->name, idx);
return 0;
}
@@ -294,8 +294,12 @@ static int pnv_xive_write_end(XiveRouter *xrtr, uint8_t blk, uint32_t idx,
word_number);
}
-static int pnv_xive_end_update(PnvXive *xive, uint8_t blk, uint32_t idx)
+static int pnv_xive_end_update(PnvXive *xive)
{
+ uint8_t blk = GETFIELD(VC_EQC_CWATCH_BLOCKID,
+ xive->regs[(VC_EQC_CWATCH_SPEC >> 3)]);
+ uint32_t idx = GETFIELD(VC_EQC_CWATCH_OFFSET,
+ xive->regs[(VC_EQC_CWATCH_SPEC >> 3)]);
int i;
uint64_t eqc_watch[4];
@@ -307,6 +311,24 @@ static int pnv_xive_end_update(PnvXive *xive, uint8_t blk, uint32_t idx)
XIVE_VST_WORD_ALL);
}
+static void pnv_xive_end_cache_load(PnvXive *xive)
+{
+ uint8_t blk = GETFIELD(VC_EQC_CWATCH_BLOCKID,
+ xive->regs[(VC_EQC_CWATCH_SPEC >> 3)]);
+ uint32_t idx = GETFIELD(VC_EQC_CWATCH_OFFSET,
+ xive->regs[(VC_EQC_CWATCH_SPEC >> 3)]);
+ uint64_t eqc_watch[4] = { 0 };
+ int i;
+
+ if (pnv_xive_vst_read(xive, VST_TSEL_EQDT, blk, idx, eqc_watch)) {
+ xive_error(xive, "VST: no END entry %x/%x !?", blk, idx);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(eqc_watch); i++) {
+ xive->regs[(VC_EQC_CWATCH_DAT0 >> 3) + i] = be64_to_cpu(eqc_watch[i]);
+ }
+}
+
static int pnv_xive_get_nvt(XiveRouter *xrtr, uint8_t blk, uint32_t idx,
XiveNVT *nvt)
{
@@ -320,8 +342,12 @@ static int pnv_xive_write_nvt(XiveRouter *xrtr, uint8_t blk, uint32_t idx,
word_number);
}
-static int pnv_xive_nvt_update(PnvXive *xive, uint8_t blk, uint32_t idx)
+static int pnv_xive_nvt_update(PnvXive *xive)
{
+ uint8_t blk = GETFIELD(PC_VPC_CWATCH_BLOCKID,
+ xive->regs[(PC_VPC_CWATCH_SPEC >> 3)]);
+ uint32_t idx = GETFIELD(PC_VPC_CWATCH_OFFSET,
+ xive->regs[(PC_VPC_CWATCH_SPEC >> 3)]);
int i;
uint64_t vpc_watch[8];
@@ -333,6 +359,24 @@ static int pnv_xive_nvt_update(PnvXive *xive, uint8_t blk, uint32_t idx)
XIVE_VST_WORD_ALL);
}
+static void pnv_xive_nvt_cache_load(PnvXive *xive)
+{
+ uint8_t blk = GETFIELD(PC_VPC_CWATCH_BLOCKID,
+ xive->regs[(PC_VPC_CWATCH_SPEC >> 3)]);
+ uint32_t idx = GETFIELD(PC_VPC_CWATCH_OFFSET,
+ xive->regs[(PC_VPC_CWATCH_SPEC >> 3)]);
+ uint64_t vpc_watch[8] = { 0 };
+ int i;
+
+ if (pnv_xive_vst_read(xive, VST_TSEL_VPDT, blk, idx, vpc_watch)) {
+ xive_error(xive, "VST: no NVT entry %x/%x !?", blk, idx);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(vpc_watch); i++) {
+ xive->regs[(PC_VPC_CWATCH_DAT0 >> 3) + i] = be64_to_cpu(vpc_watch[i]);
+ }
+}
+
static int pnv_xive_get_eas(XiveRouter *xrtr, uint8_t blk, uint32_t idx,
XiveEAS *eas)
{
@@ -346,12 +390,6 @@ static int pnv_xive_get_eas(XiveRouter *xrtr, uint8_t blk, uint32_t idx,
return pnv_xive_vst_read(xive, VST_TSEL_IVT, blk, idx, eas);
}
-static int pnv_xive_eas_update(PnvXive *xive, uint8_t blk, uint32_t idx)
-{
- /* All done. */
- return 0;
-}
-
static XiveTCTX *pnv_xive_get_tctx(XiveRouter *xrtr, CPUState *cs)
{
PowerPCCPU *cpu = POWERPC_CPU(cs);
@@ -781,8 +819,7 @@ static void pnv_xive_ic_reg_write(void *opaque, hwaddr offset,
* support recently though)
*/
if (val & (VC_SBC_CONF_CPLX_CIST | VC_SBC_CONF_CIST_BOTH)) {
- object_property_set_int(OBJECT(&xive->ipi_source),
- XIVE_SRC_STORE_EOI, "flags", &error_fatal);
+ xive->ipi_source.esb_flags |= XIVE_SRC_STORE_EOI;
}
break;
@@ -951,28 +988,43 @@ static void pnv_xive_ic_reg_write(void *opaque, hwaddr offset,
* XIVE PC & VC cache updates for EAS, NVT and END
*/
case VC_IVC_SCRUB_MASK:
- break;
case VC_IVC_SCRUB_TRIG:
- pnv_xive_eas_update(xive, GETFIELD(PC_SCRUB_BLOCK_ID, val),
- GETFIELD(VC_SCRUB_OFFSET, val));
break;
- case VC_EQC_SCRUB_MASK:
case VC_EQC_CWATCH_SPEC:
- case VC_EQC_CWATCH_DAT0 ... VC_EQC_CWATCH_DAT3:
+ val &= ~VC_EQC_CWATCH_CONFLICT; /* HW resets this bit */
+ break;
+ case VC_EQC_CWATCH_DAT1 ... VC_EQC_CWATCH_DAT3:
break;
+ case VC_EQC_CWATCH_DAT0:
+ /* writing to DATA0 triggers the cache write */
+ xive->regs[reg] = val;
+ pnv_xive_end_update(xive);
+ break;
+ case VC_EQC_SCRUB_MASK:
case VC_EQC_SCRUB_TRIG:
- pnv_xive_end_update(xive, GETFIELD(VC_SCRUB_BLOCK_ID, val),
- GETFIELD(VC_SCRUB_OFFSET, val));
+ /*
+ * The scrubbing registers flush the cache in RAM and can also
+ * invalidate.
+ */
break;
- case PC_VPC_SCRUB_MASK:
case PC_VPC_CWATCH_SPEC:
- case PC_VPC_CWATCH_DAT0 ... PC_VPC_CWATCH_DAT7:
+ val &= ~PC_VPC_CWATCH_CONFLICT; /* HW resets this bit */
+ break;
+ case PC_VPC_CWATCH_DAT1 ... PC_VPC_CWATCH_DAT7:
break;
+ case PC_VPC_CWATCH_DAT0:
+ /* writing to DATA0 triggers the cache write */
+ xive->regs[reg] = val;
+ pnv_xive_nvt_update(xive);
+ break;
+ case PC_VPC_SCRUB_MASK:
case PC_VPC_SCRUB_TRIG:
- pnv_xive_nvt_update(xive, GETFIELD(PC_SCRUB_BLOCK_ID, val),
- GETFIELD(PC_SCRUB_OFFSET, val));
+ /*
+ * The scrubbing registers flush the cache in RAM and can also
+ * invalidate.
+ */
break;
@@ -1023,15 +1075,6 @@ static uint64_t pnv_xive_ic_reg_read(void *opaque, hwaddr offset, unsigned size)
case PC_GLOBAL_CONFIG:
case PC_VPC_SCRUB_MASK:
- case PC_VPC_CWATCH_SPEC:
- case PC_VPC_CWATCH_DAT0:
- case PC_VPC_CWATCH_DAT1:
- case PC_VPC_CWATCH_DAT2:
- case PC_VPC_CWATCH_DAT3:
- case PC_VPC_CWATCH_DAT4:
- case PC_VPC_CWATCH_DAT5:
- case PC_VPC_CWATCH_DAT6:
- case PC_VPC_CWATCH_DAT7:
case VC_GLOBAL_CONFIG:
case VC_AIB_TX_ORDER_TAG2:
@@ -1044,12 +1087,6 @@ static uint64_t pnv_xive_ic_reg_read(void *opaque, hwaddr offset, unsigned size)
case VC_IRQ_CONFIG_IPI_CASC:
case VC_EQC_SCRUB_MASK:
- case VC_EQC_CWATCH_DAT0:
- case VC_EQC_CWATCH_DAT1:
- case VC_EQC_CWATCH_DAT2:
- case VC_EQC_CWATCH_DAT3:
-
- case VC_EQC_CWATCH_SPEC:
case VC_IVC_SCRUB_MASK:
case VC_SBC_CONFIG:
case VC_AT_MACRO_KILL_MASK:
@@ -1081,6 +1118,38 @@ static uint64_t pnv_xive_ic_reg_read(void *opaque, hwaddr offset, unsigned size)
/*
* XIVE PC & VC cache updates for EAS, NVT and END
*/
+ case VC_EQC_CWATCH_SPEC:
+ xive->regs[reg] = ~(VC_EQC_CWATCH_FULL | VC_EQC_CWATCH_CONFLICT);
+ val = xive->regs[reg];
+ break;
+ case VC_EQC_CWATCH_DAT0:
+ /*
+ * Load DATA registers from cache with data requested by the
+ * SPEC register
+ */
+ pnv_xive_end_cache_load(xive);
+ val = xive->regs[reg];
+ break;
+ case VC_EQC_CWATCH_DAT1 ... VC_EQC_CWATCH_DAT3:
+ val = xive->regs[reg];
+ break;
+
+ case PC_VPC_CWATCH_SPEC:
+ xive->regs[reg] = ~(PC_VPC_CWATCH_FULL | PC_VPC_CWATCH_CONFLICT);
+ val = xive->regs[reg];
+ break;
+ case PC_VPC_CWATCH_DAT0:
+ /*
+ * Load DATA registers from cache with data requested by the
+ * SPEC register
+ */
+ pnv_xive_nvt_cache_load(xive);
+ val = xive->regs[reg];
+ break;
+ case PC_VPC_CWATCH_DAT1 ... PC_VPC_CWATCH_DAT7:
+ val = xive->regs[reg];
+ break;
+
case PC_VPC_SCRUB_TRIG:
case VC_IVC_SCRUB_TRIG:
case VC_EQC_SCRUB_TRIG:
diff --git a/hw/intc/spapr_xive.c b/hw/intc/spapr_xive.c
index 58c2e5d890..3ae311d9ff 100644
--- a/hw/intc/spapr_xive.c
+++ b/hw/intc/spapr_xive.c
@@ -194,13 +194,6 @@ void spapr_xive_pic_print_info(SpaprXive *xive, Monitor *mon)
}
}
-void spapr_xive_map_mmio(SpaprXive *xive)
-{
- sysbus_mmio_map(SYS_BUS_DEVICE(xive), 0, xive->vc_base);
- sysbus_mmio_map(SYS_BUS_DEVICE(xive), 1, xive->end_base);
- sysbus_mmio_map(SYS_BUS_DEVICE(xive), 2, xive->tm_base);
-}
-
void spapr_xive_mmio_set_enabled(SpaprXive *xive, bool enable)
{
memory_region_set_enabled(&xive->source.esb_mmio, enable);
@@ -305,6 +298,7 @@ static void spapr_xive_realize(DeviceState *dev, Error **errp)
error_propagate(errp, local_err);
return;
}
+ sysbus_init_mmio(SYS_BUS_DEVICE(xive), &xsrc->esb_mmio);
/*
* Initialize the END ESB source
@@ -318,6 +312,7 @@ static void spapr_xive_realize(DeviceState *dev, Error **errp)
error_propagate(errp, local_err);
return;
}
+ sysbus_init_mmio(SYS_BUS_DEVICE(xive), &end_xsrc->esb_mmio);
/* Set the mapping address of the END ESB pages after the source ESBs */
xive->end_base = xive->vc_base + (1ull << xsrc->esb_shift) * xsrc->nr_irqs;
@@ -333,31 +328,18 @@ static void spapr_xive_realize(DeviceState *dev, Error **errp)
qemu_register_reset(spapr_xive_reset, dev);
- /* Define all XIVE MMIO regions on SysBus */
- sysbus_init_mmio(SYS_BUS_DEVICE(xive), &xsrc->esb_mmio);
- sysbus_init_mmio(SYS_BUS_DEVICE(xive), &end_xsrc->esb_mmio);
- sysbus_init_mmio(SYS_BUS_DEVICE(xive), &xive->tm_mmio);
-}
-
-void spapr_xive_init(SpaprXive *xive, Error **errp)
-{
- XiveSource *xsrc = &xive->source;
-
- /*
- * The emulated XIVE device can only be initialized once. If the
- * ESB memory region has been already mapped, it means we have been
- * through there.
- */
- if (memory_region_is_mapped(&xsrc->esb_mmio)) {
- return;
- }
-
/* TIMA initialization */
memory_region_init_io(&xive->tm_mmio, OBJECT(xive), &xive_tm_ops, xive,
"xive.tima", 4ull << TM_SHIFT);
+ sysbus_init_mmio(SYS_BUS_DEVICE(xive), &xive->tm_mmio);
- /* Map all regions */
- spapr_xive_map_mmio(xive);
+ /*
+ * Map all regions. These will be enabled or disabled at reset and
+ * can also be overridden by KVM memory regions if active
+ */
+ sysbus_mmio_map(SYS_BUS_DEVICE(xive), 0, xive->vc_base);
+ sysbus_mmio_map(SYS_BUS_DEVICE(xive), 1, xive->end_base);
+ sysbus_mmio_map(SYS_BUS_DEVICE(xive), 2, xive->tm_base);
}
static int spapr_xive_get_eas(XiveRouter *xrtr, uint8_t eas_blk,
diff --git a/hw/intc/spapr_xive_kvm.c b/hw/intc/spapr_xive_kvm.c
index b48f135838..3bf8e7a20e 100644
--- a/hw/intc/spapr_xive_kvm.c
+++ b/hw/intc/spapr_xive_kvm.c
@@ -724,12 +724,13 @@ void kvmppc_xive_connect(SpaprXive *xive, Error **errp)
xsrc->esb_mmap = kvmppc_xive_mmap(xive, KVM_XIVE_ESB_PAGE_OFFSET, esb_len,
&local_err);
if (local_err) {
- error_propagate(errp, local_err);
- return;
+ goto fail;
}
- memory_region_init_ram_device_ptr(&xsrc->esb_mmio, OBJECT(xsrc),
+ memory_region_init_ram_device_ptr(&xsrc->esb_mmio_kvm, OBJECT(xsrc),
"xive.esb", esb_len, xsrc->esb_mmap);
+ memory_region_add_subregion_overlap(&xsrc->esb_mmio, 0,
+ &xsrc->esb_mmio_kvm, 1);
/*
* 2. END ESB pages (No KVM support yet)
@@ -741,11 +742,12 @@ void kvmppc_xive_connect(SpaprXive *xive, Error **errp)
xive->tm_mmap = kvmppc_xive_mmap(xive, KVM_XIVE_TIMA_PAGE_OFFSET, tima_len,
&local_err);
if (local_err) {
- error_propagate(errp, local_err);
- return;
+ goto fail;
}
- memory_region_init_ram_device_ptr(&xive->tm_mmio, OBJECT(xive),
+ memory_region_init_ram_device_ptr(&xive->tm_mmio_kvm, OBJECT(xive),
"xive.tima", tima_len, xive->tm_mmap);
+ memory_region_add_subregion_overlap(&xive->tm_mmio, 0,
+ &xive->tm_mmio_kvm, 1);
xive->change = qemu_add_vm_change_state_handler(
kvmppc_xive_change_state_handler, xive);
@@ -756,24 +758,24 @@ void kvmppc_xive_connect(SpaprXive *xive, Error **errp)
kvmppc_xive_cpu_connect(spapr_cpu_state(cpu)->tctx, &local_err);
if (local_err) {
- error_propagate(errp, local_err);
- return;
+ goto fail;
}
}
/* Update the KVM sources */
kvmppc_xive_source_reset(xsrc, &local_err);
if (local_err) {
- error_propagate(errp, local_err);
- return;
+ goto fail;
}
kvm_kernel_irqchip = true;
kvm_msi_via_irqfd_allowed = true;
kvm_gsi_direct_mapping = true;
+ return;
- /* Map all regions */
- spapr_xive_map_mmio(xive);
+fail:
+ error_propagate(errp, local_err);
+ kvmppc_xive_disconnect(xive, NULL);
}
void kvmppc_xive_disconnect(SpaprXive *xive, Error **errp)
@@ -795,21 +797,29 @@ void kvmppc_xive_disconnect(SpaprXive *xive, Error **errp)
xsrc = &xive->source;
esb_len = (1ull << xsrc->esb_shift) * xsrc->nr_irqs;
- sysbus_mmio_unmap(SYS_BUS_DEVICE(xive), 0);
- munmap(xsrc->esb_mmap, esb_len);
-
- sysbus_mmio_unmap(SYS_BUS_DEVICE(xive), 1);
+ if (xsrc->esb_mmap) {
+ memory_region_del_subregion(&xsrc->esb_mmio, &xsrc->esb_mmio_kvm);
+ object_unparent(OBJECT(&xsrc->esb_mmio_kvm));
+ munmap(xsrc->esb_mmap, esb_len);
+ xsrc->esb_mmap = NULL;
+ }
- sysbus_mmio_unmap(SYS_BUS_DEVICE(xive), 2);
- munmap(xive->tm_mmap, 4ull << TM_SHIFT);
+ if (xive->tm_mmap) {
+ memory_region_del_subregion(&xive->tm_mmio, &xive->tm_mmio_kvm);
+ object_unparent(OBJECT(&xive->tm_mmio_kvm));
+ munmap(xive->tm_mmap, 4ull << TM_SHIFT);
+ xive->tm_mmap = NULL;
+ }
/*
* When the KVM device fd is closed, the KVM device is destroyed
* and removed from the list of devices of the VM. The VCPU
* presenters are also detached from the device.
*/
- close(xive->fd);
- xive->fd = -1;
+ if (xive->fd != -1) {
+ close(xive->fd);
+ xive->fd = -1;
+ }
kvm_kernel_irqchip = false;
kvm_msi_via_irqfd_allowed = false;
@@ -819,5 +829,8 @@ void kvmppc_xive_disconnect(SpaprXive *xive, Error **errp)
kvm_cpu_disable_all();
/* VM Change state handler is not needed anymore */
- qemu_del_vm_change_state_handler(xive->change);
+ if (xive->change) {
+ qemu_del_vm_change_state_handler(xive->change);
+ xive->change = NULL;
+ }
}
diff --git a/hw/intc/xics.c b/hw/intc/xics.c
index 29f7d39781..faa976e2f8 100644
--- a/hw/intc/xics.c
+++ b/hw/intc/xics.c
@@ -267,7 +267,14 @@ static int icp_post_load(void *opaque, int version_id)
ICPState *icp = opaque;
if (kvm_irqchip_in_kernel()) {
- return icp_set_kvm_state(icp);
+ Error *local_err = NULL;
+ int ret;
+
+ ret = icp_set_kvm_state(icp, &local_err);
+ if (ret < 0) {
+ error_report_err(local_err);
+ return ret;
+ }
}
return 0;
@@ -300,7 +307,12 @@ static void icp_reset_handler(void *dev)
qemu_set_irq(icp->output, 0);
if (kvm_irqchip_in_kernel()) {
- icp_set_kvm_state(ICP(dev));
+ Error *local_err = NULL;
+
+ icp_set_kvm_state(ICP(dev), &local_err);
+ if (local_err) {
+ error_report_err(local_err);
+ }
}
}
@@ -351,6 +363,7 @@ static void icp_realize(DeviceState *dev, Error **errp)
return;
}
+ /* Connect the presenter to the VCPU (required for CPU hotplug) */
if (kvm_irqchip_in_kernel()) {
icp_kvm_realize(dev, &err);
if (err) {
@@ -563,7 +576,12 @@ static void ics_simple_reset(DeviceState *dev)
icsc->parent_reset(dev);
if (kvm_irqchip_in_kernel()) {
- ics_set_kvm_state(ICS_BASE(dev));
+ Error *local_err = NULL;
+
+ ics_set_kvm_state(ICS_BASE(dev), &local_err);
+ if (local_err) {
+ error_report_err(local_err);
+ }
}
}
@@ -679,7 +697,14 @@ static int ics_base_post_load(void *opaque, int version_id)
ICSState *ics = opaque;
if (kvm_irqchip_in_kernel()) {
- return ics_set_kvm_state(ics);
+ Error *local_err = NULL;
+ int ret;
+
+ ret = ics_set_kvm_state(ics, &local_err);
+ if (ret < 0) {
+ error_report_err(local_err);
+ return ret;
+ }
}
return 0;
@@ -765,8 +790,13 @@ void ics_set_irq_type(ICSState *ics, int srcno, bool lsi)
lsi ? XICS_FLAGS_IRQ_LSI : XICS_FLAGS_IRQ_MSI;
if (kvm_irqchip_in_kernel()) {
+ Error *local_err = NULL;
+
ics_reset_irq(ics->irqs + srcno);
- ics_set_kvm_state_one(ics, srcno);
+ ics_set_kvm_state_one(ics, srcno, &local_err);
+ if (local_err) {
+ error_report_err(local_err);
+ }
}
}
diff --git a/hw/intc/xics_kvm.c b/hw/intc/xics_kvm.c
index 5ba5b77561..51433b19b0 100644
--- a/hw/intc/xics_kvm.c
+++ b/hw/intc/xics_kvm.c
@@ -106,7 +106,7 @@ void icp_synchronize_state(ICPState *icp)
}
}
-int icp_set_kvm_state(ICPState *icp)
+int icp_set_kvm_state(ICPState *icp, Error **errp)
{
uint64_t state;
int ret;
@@ -126,10 +126,11 @@ int icp_set_kvm_state(ICPState *icp)
| ((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;
}
@@ -240,10 +241,9 @@ void ics_synchronize_state(ICSState *ics)
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;
@@ -278,16 +278,15 @@ int ics_set_kvm_state_one(ICSState *ics, int srcno)
}
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;
@@ -297,10 +296,12 @@ int ics_set_kvm_state(ICSState *ics)
}
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;
}
}
@@ -331,16 +332,7 @@ void ics_kvm_set_irq(ICSState *ics, int srcno, int 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;
@@ -357,42 +349,41 @@ int xics_kvm_init(SpaprMachineState *spapr, Error **errp)
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;
}
@@ -407,27 +398,30 @@ int xics_kvm_init(SpaprMachineState *spapr, Error **errp)
icp_kvm_realize(DEVICE(spapr_cpu_state(cpu)->icp), &local_err);
if (local_err) {
- error_propagate(errp, local_err);
goto fail;
}
}
/* Update the KVM sources */
- ics_set_kvm_state(spapr->ics);
+ 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);
+ icp_set_kvm_state(spapr_cpu_state(cpu)->icp, &local_err);
+ if (local_err) {
+ goto fail;
+ }
}
return 0;
fail:
- 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");
+ error_propagate(errp, local_err);
+ xics_kvm_disconnect(spapr, NULL);
return -1;
}
@@ -451,13 +445,10 @@ void xics_kvm_disconnect(SpaprMachineState *spapr, Error **errp)
* removed from the list of devices of the VM. The VCPU presenters
* are also detached from the device.
*/
- close(kernel_xics_fd);
- kernel_xics_fd = -1;
-
- spapr_rtas_unregister(RTAS_IBM_SET_XIVE);
- spapr_rtas_unregister(RTAS_IBM_GET_XIVE);
- spapr_rtas_unregister(RTAS_IBM_INT_OFF);
- spapr_rtas_unregister(RTAS_IBM_INT_ON);
+ 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");
@@ -471,3 +462,33 @@ void xics_kvm_disconnect(SpaprMachineState *spapr, Error **errp)
/* 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;
+}
diff --git a/hw/intc/xics_spapr.c b/hw/intc/xics_spapr.c
index 5a1835e8b1..7cd3c93d71 100644
--- a/hw/intc/xics_spapr.c
+++ b/hw/intc/xics_spapr.c
@@ -41,11 +41,32 @@
* Guest interfaces
*/
+static bool check_emulated_xics(SpaprMachineState *spapr, const char *func)
+{
+ if (spapr_ovec_test(spapr->ov5_cas, OV5_XIVE_EXPLOIT) ||
+ kvm_irqchip_in_kernel()) {
+ error_report("pseries: %s must only be called for emulated XICS",
+ func);
+ return false;
+ }
+
+ return true;
+}
+
+#define CHECK_EMULATED_XICS_HCALL(spapr) \
+ do { \
+ if (!check_emulated_xics((spapr), __func__)) { \
+ return H_HARDWARE; \
+ } \
+ } while (0)
+
static target_ulong h_cppr(PowerPCCPU *cpu, SpaprMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
target_ulong cppr = args[0];
+ CHECK_EMULATED_XICS_HCALL(spapr);
+
icp_set_cppr(spapr_cpu_state(cpu)->icp, cppr);
return H_SUCCESS;
}
@@ -56,6 +77,8 @@ static target_ulong h_ipi(PowerPCCPU *cpu, SpaprMachineState *spapr,
target_ulong mfrr = args[1];
ICPState *icp = xics_icp_get(XICS_FABRIC(spapr), args[0]);
+ CHECK_EMULATED_XICS_HCALL(spapr);
+
if (!icp) {
return H_PARAMETER;
}
@@ -69,6 +92,8 @@ static target_ulong h_xirr(PowerPCCPU *cpu, SpaprMachineState *spapr,
{
uint32_t xirr = icp_accept(spapr_cpu_state(cpu)->icp);
+ CHECK_EMULATED_XICS_HCALL(spapr);
+
args[0] = xirr;
return H_SUCCESS;
}
@@ -78,6 +103,8 @@ static target_ulong h_xirr_x(PowerPCCPU *cpu, SpaprMachineState *spapr,
{
uint32_t xirr = icp_accept(spapr_cpu_state(cpu)->icp);
+ CHECK_EMULATED_XICS_HCALL(spapr);
+
args[0] = xirr;
args[1] = cpu_get_host_ticks();
return H_SUCCESS;
@@ -88,6 +115,8 @@ static target_ulong h_eoi(PowerPCCPU *cpu, SpaprMachineState *spapr,
{
target_ulong xirr = args[0];
+ CHECK_EMULATED_XICS_HCALL(spapr);
+
icp_eoi(spapr_cpu_state(cpu)->icp, xirr);
return H_SUCCESS;
}
@@ -99,6 +128,8 @@ static target_ulong h_ipoll(PowerPCCPU *cpu, SpaprMachineState *spapr,
uint32_t mfrr;
uint32_t xirr;
+ CHECK_EMULATED_XICS_HCALL(spapr);
+
if (!icp) {
return H_PARAMETER;
}
@@ -111,6 +142,14 @@ static target_ulong h_ipoll(PowerPCCPU *cpu, SpaprMachineState *spapr,
return H_SUCCESS;
}
+#define CHECK_EMULATED_XICS_RTAS(spapr, rets) \
+ do { \
+ if (!check_emulated_xics((spapr), __func__)) { \
+ rtas_st((rets), 0, RTAS_OUT_HW_ERROR); \
+ return; \
+ } \
+ } while (0)
+
static void rtas_set_xive(PowerPCCPU *cpu, SpaprMachineState *spapr,
uint32_t token,
uint32_t nargs, target_ulong args,
@@ -119,6 +158,8 @@ static void rtas_set_xive(PowerPCCPU *cpu, SpaprMachineState *spapr,
ICSState *ics = spapr->ics;
uint32_t nr, srcno, server, priority;
+ CHECK_EMULATED_XICS_RTAS(spapr, rets);
+
if ((nargs != 3) || (nret != 1)) {
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
return;
@@ -152,6 +193,8 @@ static void rtas_get_xive(PowerPCCPU *cpu, SpaprMachineState *spapr,
ICSState *ics = spapr->ics;
uint32_t nr, srcno;
+ CHECK_EMULATED_XICS_RTAS(spapr, rets);
+
if ((nargs != 1) || (nret != 3)) {
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
return;
@@ -182,6 +225,8 @@ static void rtas_int_off(PowerPCCPU *cpu, SpaprMachineState *spapr,
ICSState *ics = spapr->ics;
uint32_t nr, srcno;
+ CHECK_EMULATED_XICS_RTAS(spapr, rets);
+
if ((nargs != 1) || (nret != 1)) {
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
return;
@@ -213,6 +258,8 @@ static void rtas_int_on(PowerPCCPU *cpu, SpaprMachineState *spapr,
ICSState *ics = spapr->ics;
uint32_t nr, srcno;
+ CHECK_EMULATED_XICS_RTAS(spapr, rets);
+
if ((nargs != 1) || (nret != 1)) {
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
return;
@@ -239,14 +286,6 @@ static void rtas_int_on(PowerPCCPU *cpu, SpaprMachineState *spapr,
void xics_spapr_init(SpaprMachineState *spapr)
{
- /* Emulated mode can only be initialized once. */
- if (spapr->ics->init) {
- return;
- }
-
- spapr->ics->init = true;
-
- /* Registration of global state belongs into realize */
spapr_rtas_register(RTAS_IBM_SET_XIVE, "ibm,set-xive", rtas_set_xive);
spapr_rtas_register(RTAS_IBM_GET_XIVE, "ibm,get-xive", rtas_get_xive);
spapr_rtas_register(RTAS_IBM_INT_OFF, "ibm,int-off", rtas_int_off);
diff --git a/hw/intc/xive.c b/hw/intc/xive.c
index 6250c0414d..cf77bdb7d3 100644
--- a/hw/intc/xive.c
+++ b/hw/intc/xive.c
@@ -132,6 +132,11 @@ static void xive_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr)
xive_tctx_notify(tctx, ring);
}
+static inline uint32_t xive_tctx_word2(uint8_t *ring)
+{
+ return *((uint32_t *) &ring[TM_WORD2]);
+}
+
/*
* XIVE Thread Interrupt Management Area (TIMA)
*/
@@ -150,11 +155,12 @@ static uint64_t xive_tm_ack_hv_reg(XiveTCTX *tctx, hwaddr offset, unsigned size)
static uint64_t xive_tm_pull_pool_ctx(XiveTCTX *tctx, hwaddr offset,
unsigned size)
{
- uint64_t ret;
+ uint32_t qw2w2_prev = xive_tctx_word2(&tctx->regs[TM_QW2_HV_POOL]);
+ uint32_t qw2w2;
- ret = tctx->regs[TM_QW2_HV_POOL + TM_WORD2] & TM_QW2W2_POOL_CAM;
- tctx->regs[TM_QW2_HV_POOL + TM_WORD2] &= ~TM_QW2W2_POOL_CAM;
- return ret;
+ qw2w2 = xive_set_field32(TM_QW2W2_VP, qw2w2_prev, 0);
+ memcpy(&tctx->regs[TM_QW2_HV_POOL + TM_WORD2], &qw2w2, 4);
+ return qw2w2;
}
static void xive_tm_vt_push(XiveTCTX *tctx, hwaddr offset,
@@ -182,31 +188,31 @@ static uint64_t xive_tm_vt_poll(XiveTCTX *tctx, hwaddr offset, unsigned size)
*/
static const uint8_t xive_tm_hw_view[] = {
- /* QW-0 User */ 3, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, 0, 0, 0,
- /* QW-1 OS */ 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 0, 0, 0, 0,
- /* QW-2 POOL */ 0, 0, 3, 3, 0, 0, 0, 0, 3, 3, 3, 3, 0, 0, 0, 0,
- /* QW-3 PHYS */ 3, 3, 3, 3, 0, 3, 0, 3, 3, 0, 0, 3, 3, 3, 3, 0,
+ 3, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, 0, 0, 0, /* QW-0 User */
+ 3, 3, 3, 3, 3, 3, 0, 2, 3, 3, 3, 3, 0, 0, 0, 0, /* QW-1 OS */
+ 0, 0, 3, 3, 0, 0, 0, 0, 3, 3, 3, 3, 0, 0, 0, 0, /* QW-2 POOL */
+ 3, 3, 3, 3, 0, 3, 0, 2, 3, 0, 0, 3, 3, 3, 3, 0, /* QW-3 PHYS */
};
static const uint8_t xive_tm_hv_view[] = {
- /* QW-0 User */ 3, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, 0, 0, 0,
- /* QW-1 OS */ 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 0, 0, 0, 0,
- /* QW-2 POOL */ 0, 0, 3, 3, 0, 0, 0, 0, 0, 3, 3, 3, 0, 0, 0, 0,
- /* QW-3 PHYS */ 3, 3, 3, 3, 0, 3, 0, 3, 3, 0, 0, 3, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, 0, 0, 0, /* QW-0 User */
+ 3, 3, 3, 3, 3, 3, 0, 2, 3, 3, 3, 3, 0, 0, 0, 0, /* QW-1 OS */
+ 0, 0, 3, 3, 0, 0, 0, 0, 0, 3, 3, 3, 0, 0, 0, 0, /* QW-2 POOL */
+ 3, 3, 3, 3, 0, 3, 0, 2, 3, 0, 0, 3, 0, 0, 0, 0, /* QW-3 PHYS */
};
static const uint8_t xive_tm_os_view[] = {
- /* QW-0 User */ 3, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, 0, 0, 0,
- /* QW-1 OS */ 2, 3, 2, 2, 2, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0,
- /* QW-2 POOL */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- /* QW-3 PHYS */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, 0, 0, 0, /* QW-0 User */
+ 2, 3, 2, 2, 2, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, /* QW-1 OS */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* QW-2 POOL */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* QW-3 PHYS */
};
static const uint8_t xive_tm_user_view[] = {
- /* QW-0 User */ 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- /* QW-1 OS */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- /* QW-2 POOL */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- /* QW-3 PHYS */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* QW-0 User */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* QW-1 OS */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* QW-2 POOL */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* QW-3 PHYS */
};
/*
@@ -484,11 +490,6 @@ const MemoryRegionOps xive_tm_ops = {
},
};
-static inline uint32_t xive_tctx_word2(uint8_t *ring)
-{
- return *((uint32_t *) &ring[TM_WORD2]);
-}
-
static char *xive_tctx_ring_print(uint8_t *ring)
{
uint32_t w2 = xive_tctx_word2(ring);
@@ -1229,27 +1230,16 @@ XiveTCTX *xive_router_get_tctx(XiveRouter *xrtr, CPUState *cs)
}
/*
- * By default on P9, the HW CAM line (23bits) is hardwired to :
- *
- * 0x000||0b1||4Bit chip number||7Bit Thread number.
+ * Encode the HW CAM line in the block group mode format :
*
- * When the block grouping is enabled, the CAM line is changed to :
- *
- * 4Bit chip number||0x001||7Bit Thread number.
+ * chip << 19 | 0000000 0 0001 thread (7Bit)
*/
-static uint32_t hw_cam_line(uint8_t chip_id, uint8_t tid)
-{
- return 1 << 11 | (chip_id & 0xf) << 7 | (tid & 0x7f);
-}
-
-static bool xive_presenter_tctx_match_hw(XiveTCTX *tctx,
- uint8_t nvt_blk, uint32_t nvt_idx)
+static uint32_t xive_tctx_hw_cam_line(XiveTCTX *tctx)
{
CPUPPCState *env = &POWERPC_CPU(tctx->cs)->env;
uint32_t pir = env->spr_cb[SPR_PIR].default_value;
- return hw_cam_line((pir >> 8) & 0xf, pir & 0x7f) ==
- hw_cam_line(nvt_blk, nvt_idx);
+ return xive_nvt_cam_line((pir >> 8) & 0xf, 1 << 7 | (pir & 0x7f));
}
/*
@@ -1285,7 +1275,7 @@ static int xive_presenter_tctx_match(XiveTCTX *tctx, uint8_t format,
/* PHYS ring */
if ((be32_to_cpu(qw3w2) & TM_QW3W2_VT) &&
- xive_presenter_tctx_match_hw(tctx, nvt_blk, nvt_idx)) {
+ cam == xive_tctx_hw_cam_line(tctx)) {
return TM_QW3_HV_PHYS;
}