aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/intc/pnv_xive.c16
-rw-r--r--hw/intc/pnv_xive2.c140
-rw-r--r--hw/intc/pnv_xive2_regs.h1
-rw-r--r--hw/intc/spapr_xive.c18
-rw-r--r--hw/intc/trace-events12
-rw-r--r--hw/intc/xive.c555
-rw-r--r--hw/intc/xive2.c717
-rw-r--r--hw/ppc/pnv.c48
-rw-r--r--hw/ppc/spapr.c21
9 files changed, 1063 insertions, 465 deletions
diff --git a/hw/intc/pnv_xive.c b/hw/intc/pnv_xive.c
index 935c0e4..c2ca40b 100644
--- a/hw/intc/pnv_xive.c
+++ b/hw/intc/pnv_xive.c
@@ -470,14 +470,13 @@ static bool pnv_xive_is_cpu_enabled(PnvXive *xive, PowerPCCPU *cpu)
return xive->regs[reg >> 3] & PPC_BIT(bit);
}
-static int pnv_xive_match_nvt(XivePresenter *xptr, uint8_t format,
- uint8_t nvt_blk, uint32_t nvt_idx,
- bool crowd, bool cam_ignore, uint8_t priority,
- uint32_t logic_serv, XiveTCTXMatch *match)
+static bool pnv_xive_match_nvt(XivePresenter *xptr, uint8_t format,
+ uint8_t nvt_blk, uint32_t nvt_idx,
+ bool crowd, bool cam_ignore, uint8_t priority,
+ uint32_t logic_serv, XiveTCTXMatch *match)
{
PnvXive *xive = PNV_XIVE(xptr);
PnvChip *chip = xive->chip;
- int count = 0;
int i, j;
for (i = 0; i < chip->nr_cores; i++) {
@@ -510,17 +509,18 @@ static int pnv_xive_match_nvt(XivePresenter *xptr, uint8_t format,
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: already found a "
"thread context NVT %x/%x\n",
nvt_blk, nvt_idx);
- return -1;
+ match->count++;
+ continue;
}
match->ring = ring;
match->tctx = tctx;
- count++;
+ match->count++;
}
}
}
- return count;
+ return !!match->count;
}
static uint32_t pnv_xive_presenter_get_config(XivePresenter *xptr)
diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c
index ec8b0c6..e019cad 100644
--- a/hw/intc/pnv_xive2.c
+++ b/hw/intc/pnv_xive2.c
@@ -101,12 +101,10 @@ static uint32_t pnv_xive2_block_id(PnvXive2 *xive)
}
/*
- * Remote access to controllers. HW uses MMIOs. For now, a simple scan
- * of the chips is good enough.
- *
- * TODO: Block scope support
+ * Remote access to INT controllers. HW uses MMIOs(?). For now, a simple
+ * scan of all the chips INT controller is good enough.
*/
-static PnvXive2 *pnv_xive2_get_remote(uint8_t blk)
+static PnvXive2 *pnv_xive2_get_remote(uint32_t vsd_type, hwaddr fwd_addr)
{
PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine());
int i;
@@ -115,10 +113,23 @@ static PnvXive2 *pnv_xive2_get_remote(uint8_t blk)
Pnv10Chip *chip10 = PNV10_CHIP(pnv->chips[i]);
PnvXive2 *xive = &chip10->xive;
- if (pnv_xive2_block_id(xive) == blk) {
+ /*
+ * Is this the XIVE matching the forwarded VSD address is for this
+ * VSD type
+ */
+ if ((vsd_type == VST_ESB && fwd_addr == xive->esb_base) ||
+ (vsd_type == VST_END && fwd_addr == xive->end_base) ||
+ ((vsd_type == VST_NVP ||
+ vsd_type == VST_NVG) && fwd_addr == xive->nvpg_base) ||
+ (vsd_type == VST_NVC && fwd_addr == xive->nvc_base)) {
return xive;
}
}
+
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "XIVE: >>>>> %s vsd_type %u fwd_addr 0x%"HWADDR_PRIx
+ " NOT FOUND\n",
+ __func__, vsd_type, fwd_addr);
return NULL;
}
@@ -251,8 +262,7 @@ static uint64_t pnv_xive2_vst_addr(PnvXive2 *xive, uint32_t type, uint8_t blk,
/* Remote VST access */
if (GETFIELD(VSD_MODE, vsd) == VSD_MODE_FORWARD) {
- xive = pnv_xive2_get_remote(blk);
-
+ xive = pnv_xive2_get_remote(type, (vsd & VSD_ADDRESS_MASK));
return xive ? pnv_xive2_vst_addr(xive, type, blk, idx) : 0;
}
@@ -595,20 +605,28 @@ static uint32_t pnv_xive2_get_config(Xive2Router *xrtr)
{
PnvXive2 *xive = PNV_XIVE2(xrtr);
uint32_t cfg = 0;
+ uint64_t reg = xive->cq_regs[CQ_XIVE_CFG >> 3];
- if (xive->cq_regs[CQ_XIVE_CFG >> 3] & CQ_XIVE_CFG_GEN1_TIMA_OS) {
+ if (reg & CQ_XIVE_CFG_GEN1_TIMA_OS) {
cfg |= XIVE2_GEN1_TIMA_OS;
}
- if (xive->cq_regs[CQ_XIVE_CFG >> 3] & CQ_XIVE_CFG_EN_VP_SAVE_RESTORE) {
+ if (reg & CQ_XIVE_CFG_EN_VP_SAVE_RESTORE) {
cfg |= XIVE2_VP_SAVE_RESTORE;
}
- if (GETFIELD(CQ_XIVE_CFG_HYP_HARD_RANGE,
- xive->cq_regs[CQ_XIVE_CFG >> 3]) == CQ_XIVE_CFG_THREADID_8BITS) {
+ if (GETFIELD(CQ_XIVE_CFG_HYP_HARD_RANGE, reg) ==
+ CQ_XIVE_CFG_THREADID_8BITS) {
cfg |= XIVE2_THREADID_8BITS;
}
+ if (reg & CQ_XIVE_CFG_EN_VP_GRP_PRIORITY) {
+ cfg |= XIVE2_EN_VP_GRP_PRIORITY;
+ }
+
+ cfg = SETFIELD(XIVE2_VP_INT_PRIO, cfg,
+ GETFIELD(CQ_XIVE_CFG_VP_INT_PRIO, reg));
+
return cfg;
}
@@ -622,24 +640,28 @@ static bool pnv_xive2_is_cpu_enabled(PnvXive2 *xive, PowerPCCPU *cpu)
return xive->tctxt_regs[reg >> 3] & PPC_BIT(bit);
}
-static int pnv_xive2_match_nvt(XivePresenter *xptr, uint8_t format,
- uint8_t nvt_blk, uint32_t nvt_idx,
- bool crowd, bool cam_ignore, uint8_t priority,
- uint32_t logic_serv, XiveTCTXMatch *match)
+static bool pnv_xive2_match_nvt(XivePresenter *xptr, uint8_t format,
+ uint8_t nvt_blk, uint32_t nvt_idx,
+ bool crowd, bool cam_ignore, uint8_t priority,
+ uint32_t logic_serv, XiveTCTXMatch *match)
{
PnvXive2 *xive = PNV_XIVE2(xptr);
PnvChip *chip = xive->chip;
- int count = 0;
int i, j;
bool gen1_tima_os =
xive->cq_regs[CQ_XIVE_CFG >> 3] & CQ_XIVE_CFG_GEN1_TIMA_OS;
+ static int next_start_core;
+ static int next_start_thread;
+ int start_core = next_start_core;
+ int start_thread = next_start_thread;
for (i = 0; i < chip->nr_cores; i++) {
- PnvCore *pc = chip->cores[i];
+ PnvCore *pc = chip->cores[(i + start_core) % chip->nr_cores];
CPUCore *cc = CPU_CORE(pc);
for (j = 0; j < cc->nr_threads; j++) {
- PowerPCCPU *cpu = pc->threads[j];
+ /* Start search for match with different thread each call */
+ PowerPCCPU *cpu = pc->threads[(j + start_thread) % cc->nr_threads];
XiveTCTX *tctx;
int ring;
@@ -669,7 +691,8 @@ static int pnv_xive2_match_nvt(XivePresenter *xptr, uint8_t format,
"thread context NVT %x/%x\n",
nvt_blk, nvt_idx);
/* Should set a FIR if we ever model it */
- return -1;
+ match->count++;
+ continue;
}
/*
* For a group notification, we need to know if the
@@ -684,14 +707,23 @@ static int pnv_xive2_match_nvt(XivePresenter *xptr, uint8_t format,
if (!match->tctx) {
match->ring = ring;
match->tctx = tctx;
+
+ next_start_thread = j + start_thread + 1;
+ if (next_start_thread >= cc->nr_threads) {
+ next_start_thread = 0;
+ next_start_core = i + start_core + 1;
+ if (next_start_core >= chip->nr_cores) {
+ next_start_core = 0;
+ }
+ }
}
- count++;
+ match->count++;
}
}
}
}
- return count;
+ return !!match->count;
}
static uint32_t pnv_xive2_presenter_get_config(XivePresenter *xptr)
@@ -1173,7 +1205,8 @@ static void pnv_xive2_ic_cq_write(void *opaque, hwaddr offset,
case CQ_FIRMASK_OR: /* FIR error reporting */
break;
default:
- xive2_error(xive, "CQ: invalid write 0x%"HWADDR_PRIx, offset);
+ xive2_error(xive, "CQ: invalid write 0x%"HWADDR_PRIx" value 0x%"PRIx64,
+ offset, val);
return;
}
@@ -1304,7 +1337,6 @@ static uint64_t pnv_xive2_ic_vc_read(void *opaque, hwaddr offset,
case VC_ENDC_WATCH2_SPEC:
case VC_ENDC_WATCH3_SPEC:
watch_engine = (offset - VC_ENDC_WATCH0_SPEC) >> 6;
- xive->vc_regs[reg] &= ~(VC_ENDC_WATCH_FULL | VC_ENDC_WATCH_CONFLICT);
pnv_xive2_endc_cache_watch_release(xive, watch_engine);
val = xive->vc_regs[reg];
break;
@@ -1315,10 +1347,11 @@ static uint64_t pnv_xive2_ic_vc_read(void *opaque, hwaddr offset,
case VC_ENDC_WATCH3_DATA0:
/*
* Load DATA registers from cache with data requested by the
- * SPEC register
+ * SPEC register. Clear gen_flipped bit in word 1.
*/
watch_engine = (offset - VC_ENDC_WATCH0_DATA0) >> 6;
pnv_xive2_end_cache_load(xive, watch_engine);
+ xive->vc_regs[reg] &= ~(uint64_t)END2_W1_GEN_FLIPPED;
val = xive->vc_regs[reg];
break;
@@ -1386,7 +1419,14 @@ static void pnv_xive2_ic_vc_write(void *opaque, hwaddr offset,
/*
* ESB cache updates (not modeled)
*/
- /* case VC_ESBC_FLUSH_CTRL: */
+ case VC_ESBC_FLUSH_CTRL:
+ if (val & VC_ESBC_FLUSH_CTRL_WANT_CACHE_DISABLE) {
+ xive2_error(xive, "VC: unsupported write @0x%"HWADDR_PRIx
+ " value 0x%"PRIx64" bit[2] poll_want_cache_disable",
+ offset, val);
+ return;
+ }
+ break;
case VC_ESBC_FLUSH_POLL:
xive->vc_regs[VC_ESBC_FLUSH_CTRL >> 3] |= VC_ESBC_FLUSH_CTRL_POLL_VALID;
/* ESB update */
@@ -1402,7 +1442,14 @@ static void pnv_xive2_ic_vc_write(void *opaque, hwaddr offset,
/*
* EAS cache updates (not modeled)
*/
- /* case VC_EASC_FLUSH_CTRL: */
+ case VC_EASC_FLUSH_CTRL:
+ if (val & VC_EASC_FLUSH_CTRL_WANT_CACHE_DISABLE) {
+ xive2_error(xive, "VC: unsupported write @0x%"HWADDR_PRIx
+ " value 0x%"PRIx64" bit[2] poll_want_cache_disable",
+ offset, val);
+ return;
+ }
+ break;
case VC_EASC_FLUSH_POLL:
xive->vc_regs[VC_EASC_FLUSH_CTRL >> 3] |= VC_EASC_FLUSH_CTRL_POLL_VALID;
/* EAS update */
@@ -1441,7 +1488,14 @@ static void pnv_xive2_ic_vc_write(void *opaque, hwaddr offset,
break;
- /* case VC_ENDC_FLUSH_CTRL: */
+ case VC_ENDC_FLUSH_CTRL:
+ if (val & VC_ENDC_FLUSH_CTRL_WANT_CACHE_DISABLE) {
+ xive2_error(xive, "VC: unsupported write @0x%"HWADDR_PRIx
+ " value 0x%"PRIx64" bit[2] poll_want_cache_disable",
+ offset, val);
+ return;
+ }
+ break;
case VC_ENDC_FLUSH_POLL:
xive->vc_regs[VC_ENDC_FLUSH_CTRL >> 3] |= VC_ENDC_FLUSH_CTRL_POLL_VALID;
break;
@@ -1470,7 +1524,8 @@ static void pnv_xive2_ic_vc_write(void *opaque, hwaddr offset,
break;
default:
- xive2_error(xive, "VC: invalid write @%"HWADDR_PRIx, offset);
+ xive2_error(xive, "VC: invalid write @0x%"HWADDR_PRIx" value 0x%"PRIx64,
+ offset, val);
return;
}
@@ -1661,7 +1716,14 @@ static void pnv_xive2_ic_pc_write(void *opaque, hwaddr offset,
pnv_xive2_nxc_update(xive, watch_engine);
break;
- /* case PC_NXC_FLUSH_CTRL: */
+ case PC_NXC_FLUSH_CTRL:
+ if (val & PC_NXC_FLUSH_CTRL_WANT_CACHE_DISABLE) {
+ xive2_error(xive, "VC: unsupported write @0x%"HWADDR_PRIx
+ " value 0x%"PRIx64" bit[2] poll_want_cache_disable",
+ offset, val);
+ return;
+ }
+ break;
case PC_NXC_FLUSH_POLL:
xive->pc_regs[PC_NXC_FLUSH_CTRL >> 3] |= PC_NXC_FLUSH_CTRL_POLL_VALID;
break;
@@ -1678,7 +1740,8 @@ static void pnv_xive2_ic_pc_write(void *opaque, hwaddr offset,
break;
default:
- xive2_error(xive, "PC: invalid write @%"HWADDR_PRIx, offset);
+ xive2_error(xive, "PC: invalid write @0x%"HWADDR_PRIx" value 0x%"PRIx64,
+ offset, val);
return;
}
@@ -1765,7 +1828,8 @@ static void pnv_xive2_ic_tctxt_write(void *opaque, hwaddr offset,
xive->tctxt_regs[reg] = val;
break;
default:
- xive2_error(xive, "TCTXT: invalid write @%"HWADDR_PRIx, offset);
+ xive2_error(xive, "TCTXT: invalid write @0x%"HWADDR_PRIx
+ " data 0x%"PRIx64, offset, val);
return;
}
}
@@ -1836,7 +1900,8 @@ static void pnv_xive2_xscom_write(void *opaque, hwaddr offset,
pnv_xive2_ic_tctxt_write(opaque, mmio_offset, val, size);
break;
default:
- xive2_error(xive, "XSCOM: invalid write @%"HWADDR_PRIx, offset);
+ xive2_error(xive, "XSCOM: invalid write @%"HWADDR_PRIx
+ " value 0x%"PRIx64, offset, val);
}
}
@@ -1904,7 +1969,8 @@ static void pnv_xive2_ic_notify_write(void *opaque, hwaddr offset,
break;
default:
- xive2_error(xive, "NOTIFY: invalid write @%"HWADDR_PRIx, offset);
+ xive2_error(xive, "NOTIFY: invalid write @%"HWADDR_PRIx
+ " value 0x%"PRIx64, offset, val);
}
}
@@ -1946,7 +2012,8 @@ static void pnv_xive2_ic_lsi_write(void *opaque, hwaddr offset,
{
PnvXive2 *xive = PNV_XIVE2(opaque);
- xive2_error(xive, "LSI: invalid write @%"HWADDR_PRIx, offset);
+ xive2_error(xive, "LSI: invalid write @%"HWADDR_PRIx" value 0x%"PRIx64,
+ offset, val);
}
static const MemoryRegionOps pnv_xive2_ic_lsi_ops = {
@@ -2049,7 +2116,8 @@ static void pnv_xive2_ic_sync_write(void *opaque, hwaddr offset,
inject_type = PNV_XIVE2_QUEUE_NXC_ST_RMT_CI;
break;
default:
- xive2_error(xive, "SYNC: invalid write @%"HWADDR_PRIx, offset);
+ xive2_error(xive, "SYNC: invalid write @%"HWADDR_PRIx" value 0x%"PRIx64,
+ offset, val);
return;
}
diff --git a/hw/intc/pnv_xive2_regs.h b/hw/intc/pnv_xive2_regs.h
index e8b87b3..d53300f 100644
--- a/hw/intc/pnv_xive2_regs.h
+++ b/hw/intc/pnv_xive2_regs.h
@@ -66,6 +66,7 @@
#define CQ_XIVE_CFG_GEN1_TIMA_HYP_BLK0 PPC_BIT(26) /* 0 if bit[25]=0 */
#define CQ_XIVE_CFG_GEN1_TIMA_CROWD_DIS PPC_BIT(27) /* 0 if bit[25]=0 */
#define CQ_XIVE_CFG_GEN1_END_ESX PPC_BIT(28)
+#define CQ_XIVE_CFG_EN_VP_GRP_PRIORITY PPC_BIT(32) /* 0 if bit[25]=1 */
#define CQ_XIVE_CFG_EN_VP_SAVE_RESTORE PPC_BIT(38) /* 0 if bit[25]=1 */
#define CQ_XIVE_CFG_EN_VP_SAVE_REST_STRICT PPC_BIT(39) /* 0 if bit[25]=1 */
diff --git a/hw/intc/spapr_xive.c b/hw/intc/spapr_xive.c
index 440edb9..e393f5d 100644
--- a/hw/intc/spapr_xive.c
+++ b/hw/intc/spapr_xive.c
@@ -428,14 +428,13 @@ static int spapr_xive_write_nvt(XiveRouter *xrtr, uint8_t nvt_blk,
g_assert_not_reached();
}
-static int spapr_xive_match_nvt(XivePresenter *xptr, uint8_t format,
- uint8_t nvt_blk, uint32_t nvt_idx,
- bool crowd, bool cam_ignore,
- uint8_t priority,
- uint32_t logic_serv, XiveTCTXMatch *match)
+static bool spapr_xive_match_nvt(XivePresenter *xptr, uint8_t format,
+ uint8_t nvt_blk, uint32_t nvt_idx,
+ bool crowd, bool cam_ignore,
+ uint8_t priority,
+ uint32_t logic_serv, XiveTCTXMatch *match)
{
CPUState *cs;
- int count = 0;
CPU_FOREACH(cs) {
PowerPCCPU *cpu = POWERPC_CPU(cs);
@@ -463,16 +462,17 @@ static int spapr_xive_match_nvt(XivePresenter *xptr, uint8_t format,
if (match->tctx) {
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: already found a thread "
"context NVT %x/%x\n", nvt_blk, nvt_idx);
- return -1;
+ match->count++;
+ continue;
}
match->ring = ring;
match->tctx = tctx;
- count++;
+ match->count++;
}
}
- return count;
+ return !!match->count;
}
static uint32_t spapr_xive_presenter_get_config(XivePresenter *xptr)
diff --git a/hw/intc/trace-events b/hw/intc/trace-events
index 334aa6a..018c609 100644
--- a/hw/intc/trace-events
+++ b/hw/intc/trace-events
@@ -274,11 +274,13 @@ kvm_xive_cpu_connect(uint32_t id) "connect CPU%d to KVM device"
kvm_xive_source_reset(uint32_t srcno) "IRQ 0x%x"
# xive.c
-xive_tctx_accept(uint32_t index, uint8_t ring, uint8_t ipb, uint8_t pipr, uint8_t cppr, uint8_t nsr) "target=%d ring=0x%x IBP=0x%02x PIPR=0x%02x CPPR=0x%02x NSR=0x%02x ACK"
-xive_tctx_notify(uint32_t index, uint8_t ring, uint8_t ipb, uint8_t pipr, uint8_t cppr, uint8_t nsr) "target=%d ring=0x%x IBP=0x%02x PIPR=0x%02x CPPR=0x%02x NSR=0x%02x raise !"
-xive_tctx_set_cppr(uint32_t index, uint8_t ring, uint8_t ipb, uint8_t pipr, uint8_t cppr, uint8_t nsr) "target=%d ring=0x%x IBP=0x%02x PIPR=0x%02x new CPPR=0x%02x NSR=0x%02x"
+xive_tctx_accept(uint32_t index, uint8_t ring, uint8_t ipb, uint8_t pipr, uint8_t cppr, uint8_t nsr) "target=%d ring=0x%x IPB=0x%02x PIPR=0x%02x CPPR=0x%02x NSR=0x%02x ACK"
+xive_tctx_notify(uint32_t index, uint8_t ring, uint8_t ipb, uint8_t pipr, uint8_t cppr, uint8_t nsr) "target=%d ring=0x%x IPB=0x%02x PIPR=0x%02x CPPR=0x%02x NSR=0x%02x raise !"
+xive_tctx_set_cppr(uint32_t index, uint8_t ring, uint8_t ipb, uint8_t pipr, uint8_t cppr, uint8_t nsr) "target=%d ring=0x%x IPB=0x%02x PIPR=0x%02x new CPPR=0x%02x NSR=0x%02x"
xive_source_esb_read(uint64_t addr, uint32_t srcno, uint64_t value) "@0x%"PRIx64" IRQ 0x%x val=0x%"PRIx64
xive_source_esb_write(uint64_t addr, uint32_t srcno, uint64_t value) "@0x%"PRIx64" IRQ 0x%x val=0x%"PRIx64
+xive_source_notify(uint32_t srcno) "Processing notification for queued IRQ 0x%x"
+xive_source_blocked(uint32_t srcno) "No action needed for IRQ 0x%x currently"
xive_router_end_notify(uint8_t end_blk, uint32_t end_idx, uint32_t end_data) "END 0x%02x/0x%04x -> enqueue 0x%08x"
xive_router_end_escalate(uint8_t end_blk, uint32_t end_idx, uint8_t esc_blk, uint32_t esc_idx, uint32_t end_data) "END 0x%02x/0x%04x -> escalate END 0x%02x/0x%04x data 0x%08x"
xive_tctx_tm_write(uint32_t index, uint64_t offset, unsigned int size, uint64_t value) "target=%d @0x%"PRIx64" sz=%d val=0x%" PRIx64
@@ -289,6 +291,10 @@ xive_end_source_read(uint8_t end_blk, uint32_t end_idx, uint64_t addr) "END 0x%x
# xive2.c
xive_nvp_backlog_op(uint8_t blk, uint32_t idx, uint8_t op, uint8_t priority, uint8_t rc) "NVP 0x%x/0x%x operation=%d priority=%d rc=%d"
xive_nvgc_backlog_op(bool c, uint8_t blk, uint32_t idx, uint8_t op, uint8_t priority, uint32_t rc) "NVGC crowd=%d 0x%x/0x%x operation=%d priority=%d rc=%d"
+xive_redistribute(uint32_t index, uint8_t ring, uint8_t end_blk, uint32_t end_idx) "Redistribute from target=%d ring=0x%x NVP 0x%x/0x%x"
+xive_end_enqueue(uint8_t end_blk, uint32_t end_idx, uint32_t end_data) "Queue event for END 0x%x/0x%x data=0x%x"
+xive_escalate_end(uint8_t end_blk, uint32_t end_idx, uint8_t esc_blk, uint32_t esc_idx, uint32_t esc_data) "Escalate from END 0x%x/0x%x to END 0x%x/0x%x data=0x%x"
+xive_escalate_esb(uint8_t end_blk, uint32_t end_idx, uint32_t lisn) "Escalate from END 0x%x/0x%x to LISN=0x%x"
# pnv_xive.c
pnv_xive_ic_hw_trigger(uint64_t addr, uint64_t val) "@0x%"PRIx64" val=0x%"PRIx64
diff --git a/hw/intc/xive.c b/hw/intc/xive.c
index 27b473e..e0ffcf8 100644
--- a/hw/intc/xive.c
+++ b/hw/intc/xive.c
@@ -25,6 +25,58 @@
/*
* XIVE Thread Interrupt Management context
*/
+bool xive_ring_valid(XiveTCTX *tctx, uint8_t ring)
+{
+ uint8_t cur_ring;
+
+ for (cur_ring = ring; cur_ring <= TM_QW3_HV_PHYS;
+ cur_ring += XIVE_TM_RING_SIZE) {
+ if (!(tctx->regs[cur_ring + TM_WORD2] & 0x80)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool xive_nsr_indicates_exception(uint8_t ring, uint8_t nsr)
+{
+ switch (ring) {
+ case TM_QW1_OS:
+ return !!(nsr & TM_QW1_NSR_EO);
+ case TM_QW2_HV_POOL:
+ case TM_QW3_HV_PHYS:
+ return !!(nsr & TM_QW3_NSR_HE);
+ default:
+ g_assert_not_reached();
+ }
+}
+
+bool xive_nsr_indicates_group_exception(uint8_t ring, uint8_t nsr)
+{
+ if ((nsr & TM_NSR_GRP_LVL) > 0) {
+ g_assert(xive_nsr_indicates_exception(ring, nsr));
+ return true;
+ }
+ return false;
+}
+
+uint8_t xive_nsr_exception_ring(uint8_t ring, uint8_t nsr)
+{
+ /* NSR determines if pool/phys ring is for phys or pool interrupt */
+ if ((ring == TM_QW3_HV_PHYS) || (ring == TM_QW2_HV_POOL)) {
+ uint8_t he = (nsr & TM_QW3_NSR_HE) >> 6;
+
+ if (he == TM_QW3_NSR_HE_PHYS) {
+ return TM_QW3_HV_PHYS;
+ } else if (he == TM_QW3_NSR_HE_POOL) {
+ return TM_QW2_HV_POOL;
+ } else {
+ /* Don't support LSI mode */
+ g_assert_not_reached();
+ }
+ }
+ return ring;
+}
static qemu_irq xive_tctx_output(XiveTCTX *tctx, uint8_t ring)
{
@@ -41,74 +93,83 @@ static qemu_irq xive_tctx_output(XiveTCTX *tctx, uint8_t ring)
}
}
-static uint64_t xive_tctx_accept(XiveTCTX *tctx, uint8_t ring)
+/*
+ * interrupt is accepted on the presentation ring, for PHYS ring the NSR
+ * directs it to the PHYS or POOL rings.
+ */
+uint64_t xive_tctx_accept(XiveTCTX *tctx, uint8_t sig_ring)
{
- uint8_t *regs = &tctx->regs[ring];
- uint8_t nsr = regs[TM_NSR];
+ uint8_t *sig_regs = &tctx->regs[sig_ring];
+ uint8_t nsr = sig_regs[TM_NSR];
- qemu_irq_lower(xive_tctx_output(tctx, ring));
+ g_assert(sig_ring == TM_QW1_OS || sig_ring == TM_QW3_HV_PHYS);
- if (regs[TM_NSR] != 0) {
- uint8_t cppr = regs[TM_PIPR];
- uint8_t alt_ring;
- uint8_t *alt_regs;
+ g_assert(tctx->regs[TM_QW2_HV_POOL + TM_NSR] == 0);
+ g_assert(tctx->regs[TM_QW2_HV_POOL + TM_PIPR] == 0);
+ g_assert(tctx->regs[TM_QW2_HV_POOL + TM_CPPR] == 0);
- /* POOL interrupt uses IPB in QW2, POOL ring */
- if ((ring == TM_QW3_HV_PHYS) && (nsr & (TM_QW3_NSR_HE_POOL << 6))) {
- alt_ring = TM_QW2_HV_POOL;
- } else {
- alt_ring = ring;
- }
- alt_regs = &tctx->regs[alt_ring];
+ if (xive_nsr_indicates_exception(sig_ring, nsr)) {
+ uint8_t cppr = sig_regs[TM_PIPR];
+ uint8_t ring;
+ uint8_t *regs;
+
+ ring = xive_nsr_exception_ring(sig_ring, nsr);
+ regs = &tctx->regs[ring];
- regs[TM_CPPR] = cppr;
+ sig_regs[TM_CPPR] = cppr;
/*
* If the interrupt was for a specific VP, reset the pending
* buffer bit, otherwise clear the logical server indicator
*/
- if (regs[TM_NSR] & TM_NSR_GRP_LVL) {
- regs[TM_NSR] &= ~TM_NSR_GRP_LVL;
- } else {
- alt_regs[TM_IPB] &= ~xive_priority_to_ipb(cppr);
+ if (!xive_nsr_indicates_group_exception(sig_ring, nsr)) {
+ regs[TM_IPB] &= ~xive_priority_to_ipb(cppr);
}
- /* Drop the exception bit and any group/crowd */
- regs[TM_NSR] = 0;
+ /* Clear the exception from NSR */
+ sig_regs[TM_NSR] = 0;
+ qemu_irq_lower(xive_tctx_output(tctx, sig_ring));
- trace_xive_tctx_accept(tctx->cs->cpu_index, alt_ring,
- alt_regs[TM_IPB], regs[TM_PIPR],
- regs[TM_CPPR], regs[TM_NSR]);
+ trace_xive_tctx_accept(tctx->cs->cpu_index, ring,
+ regs[TM_IPB], sig_regs[TM_PIPR],
+ sig_regs[TM_CPPR], sig_regs[TM_NSR]);
}
- return ((uint64_t)nsr << 8) | regs[TM_CPPR];
+ return ((uint64_t)nsr << 8) | sig_regs[TM_CPPR];
}
-void xive_tctx_notify(XiveTCTX *tctx, uint8_t ring, uint8_t group_level)
+/* Change PIPR and calculate NSR and irq based on PIPR, CPPR, group */
+void xive_tctx_pipr_set(XiveTCTX *tctx, uint8_t ring, uint8_t pipr,
+ uint8_t group_level)
{
- /* HV_POOL ring uses HV_PHYS NSR, CPPR and PIPR registers */
- uint8_t alt_ring = (ring == TM_QW2_HV_POOL) ? TM_QW3_HV_PHYS : ring;
- uint8_t *alt_regs = &tctx->regs[alt_ring];
+ uint8_t *sig_regs = xive_tctx_signal_regs(tctx, ring);
uint8_t *regs = &tctx->regs[ring];
- if (alt_regs[TM_PIPR] < alt_regs[TM_CPPR]) {
+ g_assert(!xive_nsr_indicates_group_exception(ring, sig_regs[TM_NSR]));
+
+ sig_regs[TM_PIPR] = pipr;
+
+ if (pipr < sig_regs[TM_CPPR]) {
switch (ring) {
case TM_QW1_OS:
- regs[TM_NSR] = TM_QW1_NSR_EO | (group_level & 0x3F);
+ sig_regs[TM_NSR] = TM_QW1_NSR_EO | (group_level & 0x3F);
break;
case TM_QW2_HV_POOL:
- alt_regs[TM_NSR] = (TM_QW3_NSR_HE_POOL << 6) | (group_level & 0x3F);
+ sig_regs[TM_NSR] = (TM_QW3_NSR_HE_POOL << 6) | (group_level & 0x3F);
break;
case TM_QW3_HV_PHYS:
- regs[TM_NSR] = (TM_QW3_NSR_HE_PHYS << 6) | (group_level & 0x3F);
+ sig_regs[TM_NSR] = (TM_QW3_NSR_HE_PHYS << 6) | (group_level & 0x3F);
break;
default:
g_assert_not_reached();
}
trace_xive_tctx_notify(tctx->cs->cpu_index, ring,
- regs[TM_IPB], alt_regs[TM_PIPR],
- alt_regs[TM_CPPR], alt_regs[TM_NSR]);
+ regs[TM_IPB], pipr,
+ sig_regs[TM_CPPR], sig_regs[TM_NSR]);
qemu_irq_raise(xive_tctx_output(tctx, ring));
+ } else {
+ sig_regs[TM_NSR] = 0;
+ qemu_irq_lower(xive_tctx_output(tctx, ring));
}
}
@@ -124,25 +185,32 @@ void xive_tctx_reset_signal(XiveTCTX *tctx, uint8_t ring)
static void xive_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr)
{
- uint8_t *regs = &tctx->regs[ring];
+ uint8_t *sig_regs = &tctx->regs[ring];
uint8_t pipr_min;
uint8_t ring_min;
+ g_assert(ring == TM_QW1_OS || ring == TM_QW3_HV_PHYS);
+
+ g_assert(tctx->regs[TM_QW2_HV_POOL + TM_NSR] == 0);
+ g_assert(tctx->regs[TM_QW2_HV_POOL + TM_PIPR] == 0);
+ g_assert(tctx->regs[TM_QW2_HV_POOL + TM_CPPR] == 0);
+
+ /* XXX: should show pool IPB for PHYS ring */
trace_xive_tctx_set_cppr(tctx->cs->cpu_index, ring,
- regs[TM_IPB], regs[TM_PIPR],
- cppr, regs[TM_NSR]);
+ sig_regs[TM_IPB], sig_regs[TM_PIPR],
+ cppr, sig_regs[TM_NSR]);
if (cppr > XIVE_PRIORITY_MAX) {
cppr = 0xff;
}
- tctx->regs[ring + TM_CPPR] = cppr;
+ sig_regs[TM_CPPR] = cppr;
/*
* Recompute the PIPR based on local pending interrupts. The PHYS
* ring must take the minimum of both the PHYS and POOL PIPR values.
*/
- pipr_min = xive_ipb_to_pipr(regs[TM_IPB]);
+ pipr_min = xive_ipb_to_pipr(sig_regs[TM_IPB]);
ring_min = ring;
/* PHYS updates also depend on POOL values */
@@ -151,7 +219,6 @@ static void xive_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr)
/* POOL values only matter if POOL ctx is valid */
if (pool_regs[TM_WORD2] & 0x80) {
-
uint8_t pool_pipr = xive_ipb_to_pipr(pool_regs[TM_IPB]);
/*
@@ -165,30 +232,39 @@ static void xive_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr)
}
}
- regs[TM_PIPR] = pipr_min;
+ /* CPPR has changed, this may present or preclude a pending exception */
+ xive_tctx_pipr_set(tctx, ring_min, pipr_min, 0);
+}
+
+static void xive_tctx_pipr_recompute_from_ipb(XiveTCTX *tctx, uint8_t ring)
+{
+ uint8_t *sig_regs = xive_tctx_signal_regs(tctx, ring);
+ uint8_t *regs = &tctx->regs[ring];
- /* CPPR has changed, check if we need to raise a pending exception */
- xive_tctx_notify(tctx, ring_min, 0);
+ /* Does not support a presented group interrupt */
+ g_assert(!xive_nsr_indicates_group_exception(ring, sig_regs[TM_NSR]));
+
+ xive_tctx_pipr_set(tctx, ring, xive_ipb_to_pipr(regs[TM_IPB]), 0);
}
-void xive_tctx_pipr_update(XiveTCTX *tctx, uint8_t ring, uint8_t priority,
- uint8_t group_level)
- {
- /* HV_POOL ring uses HV_PHYS NSR, CPPR and PIPR registers */
- uint8_t alt_ring = (ring == TM_QW2_HV_POOL) ? TM_QW3_HV_PHYS : ring;
- uint8_t *alt_regs = &tctx->regs[alt_ring];
+void xive_tctx_pipr_present(XiveTCTX *tctx, uint8_t ring, uint8_t priority,
+ uint8_t group_level)
+{
+ uint8_t *sig_regs = xive_tctx_signal_regs(tctx, ring);
uint8_t *regs = &tctx->regs[ring];
+ uint8_t pipr = xive_priority_to_pipr(priority);
if (group_level == 0) {
- /* VP-specific */
regs[TM_IPB] |= xive_priority_to_ipb(priority);
- alt_regs[TM_PIPR] = xive_ipb_to_pipr(regs[TM_IPB]);
- } else {
- /* VP-group */
- alt_regs[TM_PIPR] = xive_priority_to_pipr(priority);
+ if (pipr >= sig_regs[TM_PIPR]) {
+ /* VP interrupts can come here with lower priority than PIPR */
+ return;
+ }
}
- xive_tctx_notify(tctx, ring, group_level);
- }
+ g_assert(pipr <= xive_ipb_to_pipr(regs[TM_IPB]));
+ g_assert(pipr < sig_regs[TM_PIPR]);
+ xive_tctx_pipr_set(tctx, ring, pipr, group_level);
+}
/*
* XIVE Thread Interrupt Management Area (TIMA)
@@ -206,25 +282,78 @@ static uint64_t xive_tm_ack_hv_reg(XivePresenter *xptr, XiveTCTX *tctx,
return xive_tctx_accept(tctx, TM_QW3_HV_PHYS);
}
+static void xive_pool_cam_decode(uint32_t cam, uint8_t *nvt_blk,
+ uint32_t *nvt_idx, bool *vp)
+{
+ if (nvt_blk) {
+ *nvt_blk = xive_nvt_blk(cam);
+ }
+ if (nvt_idx) {
+ *nvt_idx = xive_nvt_idx(cam);
+ }
+ if (vp) {
+ *vp = !!(cam & TM_QW2W2_VP);
+ }
+}
+
+static uint32_t xive_tctx_get_pool_cam(XiveTCTX *tctx, uint8_t *nvt_blk,
+ uint32_t *nvt_idx, bool *vp)
+{
+ uint32_t qw2w2 = xive_tctx_word2(&tctx->regs[TM_QW2_HV_POOL]);
+ uint32_t cam = be32_to_cpu(qw2w2);
+
+ xive_pool_cam_decode(cam, nvt_blk, nvt_idx, vp);
+ return qw2w2;
+}
+
+static void xive_tctx_set_pool_cam(XiveTCTX *tctx, uint32_t qw2w2)
+{
+ memcpy(&tctx->regs[TM_QW2_HV_POOL + TM_WORD2], &qw2w2, 4);
+}
+
static uint64_t xive_tm_pull_pool_ctx(XivePresenter *xptr, XiveTCTX *tctx,
hwaddr offset, unsigned size)
{
- uint32_t qw2w2_prev = xive_tctx_word2(&tctx->regs[TM_QW2_HV_POOL]);
uint32_t qw2w2;
+ uint32_t qw2w2_new;
+ uint8_t nvt_blk;
+ uint32_t nvt_idx;
+ bool vp;
+
+ qw2w2 = xive_tctx_get_pool_cam(tctx, &nvt_blk, &nvt_idx, &vp);
+
+ if (!vp) {
+ qemu_log_mask(LOG_GUEST_ERROR, "XIVE: pull invalid POOL NVT %x/%x !?\n",
+ nvt_blk, nvt_idx);
+ }
+
+ /* Invalidate CAM line */
+ qw2w2_new = xive_set_field32(TM_QW2W2_VP, qw2w2, 0);
+ xive_tctx_set_pool_cam(tctx, qw2w2_new);
+
+ xive_tctx_reset_signal(tctx, TM_QW1_OS);
+ xive_tctx_reset_signal(tctx, TM_QW2_HV_POOL);
+ /* Re-check phys for interrupts if pool was disabled */
+ xive_tctx_pipr_recompute_from_ipb(tctx, TM_QW3_HV_PHYS);
- qw2w2 = xive_set_field32(TM_QW2W2_VP, qw2w2_prev, 0);
- memcpy(&tctx->regs[TM_QW2_HV_POOL + TM_WORD2], &qw2w2, 4);
return qw2w2;
}
static uint64_t xive_tm_pull_phys_ctx(XivePresenter *xptr, XiveTCTX *tctx,
hwaddr offset, unsigned size)
{
- uint8_t qw3b8_prev = tctx->regs[TM_QW3_HV_PHYS + TM_WORD2];
- uint8_t qw3b8;
+ uint8_t qw3b8 = tctx->regs[TM_QW3_HV_PHYS + TM_WORD2];
+ uint8_t qw3b8_new;
+
+ qw3b8 = tctx->regs[TM_QW3_HV_PHYS + TM_WORD2];
+ if (!(qw3b8 & TM_QW3B8_VT)) {
+ qemu_log_mask(LOG_GUEST_ERROR, "XIVE: pulling invalid PHYS thread!?\n");
+ }
+ qw3b8_new = qw3b8 & ~TM_QW3B8_VT;
+ tctx->regs[TM_QW3_HV_PHYS + TM_WORD2] = qw3b8_new;
- qw3b8 = qw3b8_prev & ~TM_QW3B8_VT;
- tctx->regs[TM_QW3_HV_PHYS + TM_WORD2] = qw3b8;
+ xive_tctx_reset_signal(tctx, TM_QW1_OS);
+ xive_tctx_reset_signal(tctx, TM_QW3_HV_PHYS);
return qw3b8;
}
@@ -255,14 +384,14 @@ static uint64_t xive_tm_vt_poll(XivePresenter *xptr, XiveTCTX *tctx,
static const uint8_t xive_tm_hw_view[] = {
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 */
+ 3, 3, 3, 3, 3, 3, 0, 2, 3, 3, 3, 3, 0, 0, 0, 3, /* QW-1 OS */
0, 0, 3, 3, 0, 3, 3, 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[] = {
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 */
+ 3, 3, 3, 3, 3, 3, 0, 2, 3, 3, 3, 3, 0, 0, 0, 3, /* QW-1 OS */
0, 0, 3, 3, 0, 3, 3, 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 */
};
@@ -326,7 +455,7 @@ static void xive_tm_raw_write(XiveTCTX *tctx, hwaddr offset, uint64_t value,
*/
if (size < 4 || !mask || ring_offset == TM_QW0_USER) {
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid write access at TIMA @%"
- HWADDR_PRIx"\n", offset);
+ HWADDR_PRIx" size %d\n", offset, size);
return;
}
@@ -357,7 +486,7 @@ static uint64_t xive_tm_raw_read(XiveTCTX *tctx, hwaddr offset, unsigned size)
*/
if (size < 4 || !mask || ring_offset == TM_QW0_USER) {
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid read access at TIMA @%"
- HWADDR_PRIx"\n", offset);
+ HWADDR_PRIx" size %d\n", offset, size);
return -1;
}
@@ -403,6 +532,12 @@ static void xive_tm_set_os_lgs(XivePresenter *xptr, XiveTCTX *tctx,
xive_tctx_set_lgs(tctx, TM_QW1_OS, value & 0xff);
}
+static void xive_tm_set_pool_lgs(XivePresenter *xptr, XiveTCTX *tctx,
+ hwaddr offset, uint64_t value, unsigned size)
+{
+ xive_tctx_set_lgs(tctx, TM_QW2_HV_POOL, value & 0xff);
+}
+
/*
* Adjust the PIPR to allow a CPU to process event queues of other
* priorities during one physical interrupt cycle.
@@ -410,7 +545,12 @@ static void xive_tm_set_os_lgs(XivePresenter *xptr, XiveTCTX *tctx,
static void xive_tm_set_os_pending(XivePresenter *xptr, XiveTCTX *tctx,
hwaddr offset, uint64_t value, unsigned size)
{
- xive_tctx_pipr_update(tctx, TM_QW1_OS, value & 0xff, 0);
+ uint8_t ring = TM_QW1_OS;
+ uint8_t *regs = &tctx->regs[ring];
+
+ /* XXX: how should this work exactly? */
+ regs[TM_IPB] |= xive_priority_to_ipb(value & 0xff);
+ xive_tctx_pipr_recompute_from_ipb(tctx, ring);
}
static void xive_os_cam_decode(uint32_t cam, uint8_t *nvt_blk,
@@ -454,7 +594,7 @@ static uint64_t xive_tm_pull_os_ctx(XivePresenter *xptr, XiveTCTX *tctx,
qw1w2 = xive_tctx_get_os_cam(tctx, &nvt_blk, &nvt_idx, &vo);
if (!vo) {
- qemu_log_mask(LOG_GUEST_ERROR, "XIVE: pulling invalid NVT %x/%x !?\n",
+ qemu_log_mask(LOG_GUEST_ERROR, "XIVE: pull invalid OS NVT %x/%x !?\n",
nvt_blk, nvt_idx);
}
@@ -466,7 +606,7 @@ static uint64_t xive_tm_pull_os_ctx(XivePresenter *xptr, XiveTCTX *tctx,
return qw1w2;
}
-static void xive_tctx_need_resend(XiveRouter *xrtr, XiveTCTX *tctx,
+static void xive_tctx_restore_nvp(XiveRouter *xrtr, XiveTCTX *tctx,
uint8_t nvt_blk, uint32_t nvt_idx)
{
XiveNVT nvt;
@@ -492,16 +632,6 @@ static void xive_tctx_need_resend(XiveRouter *xrtr, XiveTCTX *tctx,
uint8_t *regs = &tctx->regs[TM_QW1_OS];
regs[TM_IPB] |= ipb;
}
-
- /*
- * Always call xive_tctx_pipr_update(). Even if there were no
- * escalation triggered, there could be a pending interrupt which
- * was saved when the context was pulled and that we need to take
- * into account by recalculating the PIPR (which is not
- * saved/restored).
- * It will also raise the External interrupt signal if needed.
- */
- xive_tctx_pipr_update(tctx, TM_QW1_OS, 0xFF, 0); /* fxb */
}
/*
@@ -523,7 +653,17 @@ static void xive_tm_push_os_ctx(XivePresenter *xptr, XiveTCTX *tctx,
/* Check the interrupt pending bits */
if (vo) {
- xive_tctx_need_resend(XIVE_ROUTER(xptr), tctx, nvt_blk, nvt_idx);
+ xive_tctx_restore_nvp(XIVE_ROUTER(xptr), tctx, nvt_blk, nvt_idx);
+
+ /*
+ * Always call xive_tctx_recompute_from_ipb(). Even if there were no
+ * escalation triggered, there could be a pending interrupt which
+ * was saved when the context was pulled and that we need to take
+ * into account by recalculating the PIPR (which is not
+ * saved/restored).
+ * It will also raise the External interrupt signal if needed.
+ */
+ xive_tctx_pipr_recompute_from_ipb(tctx, TM_QW1_OS); /* fxb */
}
}
@@ -542,6 +682,8 @@ typedef struct XiveTmOp {
uint8_t page_offset;
uint32_t op_offset;
unsigned size;
+ bool hw_ok;
+ bool sw_ok;
void (*write_handler)(XivePresenter *xptr, XiveTCTX *tctx,
hwaddr offset,
uint64_t value, unsigned size);
@@ -554,34 +696,34 @@ static const XiveTmOp xive_tm_operations[] = {
* MMIOs below 2K : raw values and special operations without side
* effects
*/
- { XIVE_TM_OS_PAGE, TM_QW1_OS + TM_CPPR, 1, xive_tm_set_os_cppr,
- NULL },
- { XIVE_TM_HV_PAGE, TM_QW1_OS + TM_WORD2, 4, xive_tm_push_os_ctx,
- NULL },
- { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_CPPR, 1, xive_tm_set_hv_cppr,
- NULL },
- { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_WORD2, 1, xive_tm_vt_push,
- NULL },
- { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_WORD2, 1, NULL,
- xive_tm_vt_poll },
+ { XIVE_TM_OS_PAGE, TM_QW1_OS + TM_CPPR, 1, true, true,
+ xive_tm_set_os_cppr, NULL },
+ { XIVE_TM_HV_PAGE, TM_QW1_OS + TM_WORD2, 4, true, true,
+ xive_tm_push_os_ctx, NULL },
+ { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_CPPR, 1, true, true,
+ xive_tm_set_hv_cppr, NULL },
+ { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_WORD2, 1, false, true,
+ xive_tm_vt_push, NULL },
+ { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_WORD2, 1, true, true,
+ NULL, xive_tm_vt_poll },
/* MMIOs above 2K : special operations with side effects */
- { XIVE_TM_OS_PAGE, TM_SPC_ACK_OS_REG, 2, NULL,
- xive_tm_ack_os_reg },
- { XIVE_TM_OS_PAGE, TM_SPC_SET_OS_PENDING, 1, xive_tm_set_os_pending,
- NULL },
- { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX, 4, NULL,
- xive_tm_pull_os_ctx },
- { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX, 8, NULL,
- xive_tm_pull_os_ctx },
- { XIVE_TM_HV_PAGE, TM_SPC_ACK_HV_REG, 2, NULL,
- xive_tm_ack_hv_reg },
- { XIVE_TM_HV_PAGE, TM_SPC_PULL_POOL_CTX, 4, NULL,
- xive_tm_pull_pool_ctx },
- { XIVE_TM_HV_PAGE, TM_SPC_PULL_POOL_CTX, 8, NULL,
- xive_tm_pull_pool_ctx },
- { XIVE_TM_HV_PAGE, TM_SPC_PULL_PHYS_CTX, 1, NULL,
- xive_tm_pull_phys_ctx },
+ { XIVE_TM_OS_PAGE, TM_SPC_ACK_OS_REG, 2, true, false,
+ NULL, xive_tm_ack_os_reg },
+ { XIVE_TM_OS_PAGE, TM_SPC_SET_OS_PENDING, 1, true, false,
+ xive_tm_set_os_pending, NULL },
+ { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX, 4, true, false,
+ NULL, xive_tm_pull_os_ctx },
+ { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX, 8, true, false,
+ NULL, xive_tm_pull_os_ctx },
+ { XIVE_TM_HV_PAGE, TM_SPC_ACK_HV_REG, 2, true, false,
+ NULL, xive_tm_ack_hv_reg },
+ { XIVE_TM_HV_PAGE, TM_SPC_PULL_POOL_CTX, 4, true, false,
+ NULL, xive_tm_pull_pool_ctx },
+ { XIVE_TM_HV_PAGE, TM_SPC_PULL_POOL_CTX, 8, true, false,
+ NULL, xive_tm_pull_pool_ctx },
+ { XIVE_TM_HV_PAGE, TM_SPC_PULL_PHYS_CTX, 1, true, false,
+ NULL, xive_tm_pull_phys_ctx },
};
static const XiveTmOp xive2_tm_operations[] = {
@@ -589,50 +731,58 @@ static const XiveTmOp xive2_tm_operations[] = {
* MMIOs below 2K : raw values and special operations without side
* effects
*/
- { XIVE_TM_OS_PAGE, TM_QW1_OS + TM_CPPR, 1, xive2_tm_set_os_cppr,
- NULL },
- { XIVE_TM_HV_PAGE, TM_QW1_OS + TM_WORD2, 4, xive2_tm_push_os_ctx,
- NULL },
- { XIVE_TM_HV_PAGE, TM_QW1_OS + TM_WORD2, 8, xive2_tm_push_os_ctx,
- NULL },
- { XIVE_TM_OS_PAGE, TM_QW1_OS + TM_LGS, 1, xive_tm_set_os_lgs,
- NULL },
- { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_CPPR, 1, xive2_tm_set_hv_cppr,
- NULL },
- { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_WORD2, 1, xive_tm_vt_push,
- NULL },
- { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_WORD2, 1, NULL,
- xive_tm_vt_poll },
- { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_T, 1, xive2_tm_set_hv_target,
- NULL },
+ { XIVE_TM_OS_PAGE, TM_QW1_OS + TM_CPPR, 1, true, true,
+ xive2_tm_set_os_cppr, NULL },
+ { XIVE_TM_HV_PAGE, TM_QW1_OS + TM_WORD2, 4, true, true,
+ xive2_tm_push_os_ctx, NULL },
+ { XIVE_TM_HV_PAGE, TM_QW1_OS + TM_WORD2, 8, true, true,
+ xive2_tm_push_os_ctx, NULL },
+ { XIVE_TM_OS_PAGE, TM_QW1_OS + TM_LGS, 1, true, true,
+ xive_tm_set_os_lgs, NULL },
+ { XIVE_TM_HV_PAGE, TM_QW2_HV_POOL + TM_WORD2, 4, true, true,
+ xive2_tm_push_pool_ctx, NULL },
+ { XIVE_TM_HV_PAGE, TM_QW2_HV_POOL + TM_WORD2, 8, true, true,
+ xive2_tm_push_pool_ctx, NULL },
+ { XIVE_TM_HV_PAGE, TM_QW2_HV_POOL + TM_LGS, 1, true, true,
+ xive_tm_set_pool_lgs, NULL },
+ { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_CPPR, 1, true, true,
+ xive2_tm_set_hv_cppr, NULL },
+ { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_WORD2, 1, false, true,
+ xive2_tm_push_phys_ctx, NULL },
+ { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_WORD2, 1, true, true,
+ NULL, xive_tm_vt_poll },
+ { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_T, 1, true, true,
+ xive2_tm_set_hv_target, NULL },
/* MMIOs above 2K : special operations with side effects */
- { XIVE_TM_OS_PAGE, TM_SPC_ACK_OS_REG, 2, NULL,
- xive_tm_ack_os_reg },
- { XIVE_TM_OS_PAGE, TM_SPC_SET_OS_PENDING, 1, xive_tm_set_os_pending,
- NULL },
- { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX_G2, 4, NULL,
- xive2_tm_pull_os_ctx },
- { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX, 4, NULL,
- xive2_tm_pull_os_ctx },
- { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX, 8, NULL,
- xive2_tm_pull_os_ctx },
- { XIVE_TM_HV_PAGE, TM_SPC_ACK_HV_REG, 2, NULL,
- xive_tm_ack_hv_reg },
- { XIVE_TM_HV_PAGE, TM_SPC_PULL_POOL_CTX_G2, 4, NULL,
- xive_tm_pull_pool_ctx },
- { XIVE_TM_HV_PAGE, TM_SPC_PULL_POOL_CTX, 4, NULL,
- xive_tm_pull_pool_ctx },
- { XIVE_TM_HV_PAGE, TM_SPC_PULL_POOL_CTX, 8, NULL,
- xive_tm_pull_pool_ctx },
- { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX_OL, 1, xive2_tm_pull_os_ctx_ol,
- NULL },
- { XIVE_TM_HV_PAGE, TM_SPC_PULL_PHYS_CTX_G2, 4, NULL,
- xive_tm_pull_phys_ctx },
- { XIVE_TM_HV_PAGE, TM_SPC_PULL_PHYS_CTX, 1, NULL,
- xive_tm_pull_phys_ctx },
- { XIVE_TM_HV_PAGE, TM_SPC_PULL_PHYS_CTX_OL, 1, xive2_tm_pull_phys_ctx_ol,
- NULL },
+ { XIVE_TM_OS_PAGE, TM_SPC_ACK_OS_REG, 2, true, false,
+ NULL, xive_tm_ack_os_reg },
+ { XIVE_TM_OS_PAGE, TM_SPC_SET_OS_PENDING, 1, true, false,
+ xive2_tm_set_os_pending, NULL },
+ { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX_G2, 4, true, false,
+ NULL, xive2_tm_pull_os_ctx },
+ { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX, 4, true, false,
+ NULL, xive2_tm_pull_os_ctx },
+ { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX, 8, true, false,
+ NULL, xive2_tm_pull_os_ctx },
+ { XIVE_TM_HV_PAGE, TM_SPC_ACK_HV_REG, 2, true, false,
+ NULL, xive_tm_ack_hv_reg },
+ { XIVE_TM_HV_PAGE, TM_SPC_PULL_POOL_CTX_G2, 4, true, false,
+ NULL, xive2_tm_pull_pool_ctx },
+ { XIVE_TM_HV_PAGE, TM_SPC_PULL_POOL_CTX, 4, true, false,
+ NULL, xive2_tm_pull_pool_ctx },
+ { XIVE_TM_HV_PAGE, TM_SPC_PULL_POOL_CTX, 8, true, false,
+ NULL, xive2_tm_pull_pool_ctx },
+ { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX_OL, 1, true, false,
+ xive2_tm_pull_os_ctx_ol, NULL },
+ { XIVE_TM_HV_PAGE, TM_SPC_PULL_PHYS_CTX_G2, 4, true, false,
+ NULL, xive2_tm_pull_phys_ctx },
+ { XIVE_TM_HV_PAGE, TM_SPC_PULL_PHYS_CTX, 1, true, false,
+ NULL, xive2_tm_pull_phys_ctx },
+ { XIVE_TM_HV_PAGE, TM_SPC_PULL_PHYS_CTX_OL, 1, true, false,
+ xive2_tm_pull_phys_ctx_ol, NULL },
+ { XIVE_TM_OS_PAGE, TM_SPC_ACK_OS_EL, 1, true, false,
+ xive2_tm_ack_os_el, NULL },
};
static const XiveTmOp *xive_tm_find_op(XivePresenter *xptr, hwaddr offset,
@@ -674,21 +824,31 @@ void xive_tctx_tm_write(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset,
uint64_t value, unsigned size)
{
const XiveTmOp *xto;
+ uint8_t ring = offset & TM_RING_OFFSET;
+ bool is_valid = xive_ring_valid(tctx, ring);
+ bool hw_owned = is_valid;
trace_xive_tctx_tm_write(tctx->cs->cpu_index, offset, size, value);
/*
- * TODO: check V bit in Q[0-3]W2
- */
-
- /*
* First, check for special operations in the 2K region
*/
+ xto = xive_tm_find_op(tctx->xptr, offset, size, true);
+ if (xto) {
+ if (hw_owned && !xto->hw_ok) {
+ qemu_log_mask(LOG_GUEST_ERROR, "XIVE: undefined write to HW TIMA "
+ "@%"HWADDR_PRIx" size %d\n", offset, size);
+ }
+ if (!hw_owned && !xto->sw_ok) {
+ qemu_log_mask(LOG_GUEST_ERROR, "XIVE: undefined write to SW TIMA "
+ "@%"HWADDR_PRIx" size %d\n", offset, size);
+ }
+ }
+
if (offset & TM_SPECIAL_OP) {
- xto = xive_tm_find_op(tctx->xptr, offset, size, true);
if (!xto) {
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid write access at TIMA "
- "@%"HWADDR_PRIx"\n", offset);
+ "@%"HWADDR_PRIx" size %d\n", offset, size);
} else {
xto->write_handler(xptr, tctx, offset, value, size);
}
@@ -698,7 +858,6 @@ void xive_tctx_tm_write(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset,
/*
* Then, for special operations in the region below 2K.
*/
- xto = xive_tm_find_op(tctx->xptr, offset, size, true);
if (xto) {
xto->write_handler(xptr, tctx, offset, value, size);
return;
@@ -707,6 +866,11 @@ void xive_tctx_tm_write(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset,
/*
* Finish with raw access to the register values
*/
+ if (hw_owned) {
+ /* Store context operations are dangerous when context is valid */
+ qemu_log_mask(LOG_GUEST_ERROR, "XIVE: undefined write to HW TIMA "
+ "@%"HWADDR_PRIx" size %d\n", offset, size);
+ }
xive_tm_raw_write(tctx, offset, value, size);
}
@@ -714,20 +878,30 @@ uint64_t xive_tctx_tm_read(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset,
unsigned size)
{
const XiveTmOp *xto;
+ uint8_t ring = offset & TM_RING_OFFSET;
+ bool is_valid = xive_ring_valid(tctx, ring);
+ bool hw_owned = is_valid;
uint64_t ret;
- /*
- * TODO: check V bit in Q[0-3]W2
- */
+ xto = xive_tm_find_op(tctx->xptr, offset, size, false);
+ if (xto) {
+ if (hw_owned && !xto->hw_ok) {
+ qemu_log_mask(LOG_GUEST_ERROR, "XIVE: undefined read to HW TIMA "
+ "@%"HWADDR_PRIx" size %d\n", offset, size);
+ }
+ if (!hw_owned && !xto->sw_ok) {
+ qemu_log_mask(LOG_GUEST_ERROR, "XIVE: undefined read to SW TIMA "
+ "@%"HWADDR_PRIx" size %d\n", offset, size);
+ }
+ }
/*
* First, check for special operations in the 2K region
*/
if (offset & TM_SPECIAL_OP) {
- xto = xive_tm_find_op(tctx->xptr, offset, size, false);
if (!xto) {
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid read access to TIMA"
- "@%"HWADDR_PRIx"\n", offset);
+ "@%"HWADDR_PRIx" size %d\n", offset, size);
return -1;
}
ret = xto->read_handler(xptr, tctx, offset, size);
@@ -737,7 +911,6 @@ uint64_t xive_tctx_tm_read(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset,
/*
* Then, for special operations in the region below 2K.
*/
- xto = xive_tm_find_op(tctx->xptr, offset, size, false);
if (xto) {
ret = xto->read_handler(xptr, tctx, offset, size);
goto out;
@@ -1191,6 +1364,7 @@ static uint64_t xive_source_esb_read(void *opaque, hwaddr addr, unsigned size)
/* Forward the source event notification for routing */
if (ret) {
+ trace_xive_source_notify(srcno);
xive_source_notify(xsrc, srcno);
}
break;
@@ -1286,6 +1460,8 @@ out:
/* Forward the source event notification for routing */
if (notify) {
xive_source_notify(xsrc, srcno);
+ } else {
+ trace_xive_source_blocked(srcno);
}
}
@@ -1672,8 +1848,8 @@ uint32_t xive_get_vpgroup_size(uint32_t nvp_index)
return 1U << (first_zero + 1);
}
-static uint8_t xive_get_group_level(bool crowd, bool ignore,
- uint32_t nvp_blk, uint32_t nvp_index)
+uint8_t xive_get_group_level(bool crowd, bool ignore,
+ uint32_t nvp_blk, uint32_t nvp_index)
{
int first_zero;
uint8_t level;
@@ -1791,15 +1967,14 @@ int xive_presenter_tctx_match(XivePresenter *xptr, XiveTCTX *tctx,
* This is our simple Xive Presenter Engine model. It is merged in the
* Router as it does not require an extra object.
*/
-bool xive_presenter_notify(XiveFabric *xfb, uint8_t format,
+bool xive_presenter_match(XiveFabric *xfb, uint8_t format,
uint8_t nvt_blk, uint32_t nvt_idx,
bool crowd, bool cam_ignore, uint8_t priority,
- uint32_t logic_serv, bool *precluded)
+ uint32_t logic_serv, XiveTCTXMatch *match)
{
XiveFabricClass *xfc = XIVE_FABRIC_GET_CLASS(xfb);
- XiveTCTXMatch match = { .tctx = NULL, .ring = 0, .precluded = false };
- uint8_t group_level;
- int count;
+
+ memset(match, 0, sizeof(*match));
/*
* Ask the machine to scan the interrupt controllers for a match.
@@ -1824,22 +1999,8 @@ bool xive_presenter_notify(XiveFabric *xfb, uint8_t format,
* a new command to the presenters (the equivalent of the "assign"
* power bus command in the documented full notify sequence.
*/
- count = xfc->match_nvt(xfb, format, nvt_blk, nvt_idx, crowd, cam_ignore,
- priority, logic_serv, &match);
- if (count < 0) {
- return false;
- }
-
- /* handle CPU exception delivery */
- if (count) {
- group_level = xive_get_group_level(crowd, cam_ignore, nvt_blk, nvt_idx);
- trace_xive_presenter_notify(nvt_blk, nvt_idx, match.ring, group_level);
- xive_tctx_pipr_update(match.tctx, match.ring, priority, group_level);
- } else {
- *precluded = match.precluded;
- }
-
- return !!count;
+ return xfc->match_nvt(xfb, format, nvt_blk, nvt_idx, crowd, cam_ignore,
+ priority, logic_serv, match);
}
/*
@@ -1876,7 +2037,7 @@ void xive_router_end_notify(XiveRouter *xrtr, XiveEAS *eas)
uint8_t nvt_blk;
uint32_t nvt_idx;
XiveNVT nvt;
- bool found, precluded;
+ XiveTCTXMatch match;
uint8_t end_blk = xive_get_field64(EAS_END_BLOCK, eas->w);
uint32_t end_idx = xive_get_field64(EAS_END_INDEX, eas->w);
@@ -1956,16 +2117,16 @@ void xive_router_end_notify(XiveRouter *xrtr, XiveEAS *eas)
return;
}
- found = xive_presenter_notify(xrtr->xfb, format, nvt_blk, nvt_idx,
- false /* crowd */,
- xive_get_field32(END_W7_F0_IGNORE, end.w7),
- priority,
- xive_get_field32(END_W7_F1_LOG_SERVER_ID, end.w7),
- &precluded);
- /* we don't support VP-group notification on P9, so precluded is not used */
/* TODO: Auto EOI. */
-
- if (found) {
+ /* we don't support VP-group notification on P9, so precluded is not used */
+ if (xive_presenter_match(xrtr->xfb, format, nvt_blk, nvt_idx,
+ false /* crowd */,
+ xive_get_field32(END_W7_F0_IGNORE, end.w7),
+ priority,
+ xive_get_field32(END_W7_F1_LOG_SERVER_ID, end.w7),
+ &match)) {
+ trace_xive_presenter_notify(nvt_blk, nvt_idx, match.ring, 0);
+ xive_tctx_pipr_present(match.tctx, match.ring, priority, 0);
return;
}
diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c
index a08cf90..ee5fa26 100644
--- a/hw/intc/xive2.c
+++ b/hw/intc/xive2.c
@@ -19,6 +19,13 @@
#include "hw/ppc/xive2_regs.h"
#include "trace.h"
+static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk,
+ uint32_t end_idx, uint32_t end_data,
+ bool redistribute);
+
+static int xive2_tctx_get_nvp_indexes(XiveTCTX *tctx, uint8_t ring,
+ uint8_t *nvp_blk, uint32_t *nvp_idx);
+
uint32_t xive2_router_get_config(Xive2Router *xrtr)
{
Xive2RouterClass *xrc = XIVE2_ROUTER_GET_CLASS(xrtr);
@@ -188,12 +195,27 @@ void xive2_eas_pic_print_info(Xive2Eas *eas, uint32_t lisn, GString *buf)
(uint32_t) xive_get_field64(EAS2_END_DATA, eas->w));
}
+#define XIVE2_QSIZE_CHUNK_CL 128
+#define XIVE2_QSIZE_CHUNK_4k 4096
+/* Calculate max number of queue entries for an END */
+static uint32_t xive2_end_get_qentries(Xive2End *end)
+{
+ uint32_t w3 = end->w3;
+ uint32_t qsize = xive_get_field32(END2_W3_QSIZE, w3);
+ if (xive_get_field32(END2_W3_CL, w3)) {
+ g_assert(qsize <= 4);
+ return (XIVE2_QSIZE_CHUNK_CL << qsize) / sizeof(uint32_t);
+ } else {
+ g_assert(qsize <= 12);
+ return (XIVE2_QSIZE_CHUNK_4k << qsize) / sizeof(uint32_t);
+ }
+}
+
void xive2_end_queue_pic_print_info(Xive2End *end, uint32_t width, GString *buf)
{
uint64_t qaddr_base = xive2_end_qaddr(end);
- uint32_t qsize = xive_get_field32(END2_W3_QSIZE, end->w3);
uint32_t qindex = xive_get_field32(END2_W1_PAGE_OFF, end->w1);
- uint32_t qentries = 1 << (qsize + 10);
+ uint32_t qentries = xive2_end_get_qentries(end);
int i;
/*
@@ -223,8 +245,7 @@ void xive2_end_pic_print_info(Xive2End *end, uint32_t end_idx, GString *buf)
uint64_t qaddr_base = xive2_end_qaddr(end);
uint32_t qindex = xive_get_field32(END2_W1_PAGE_OFF, end->w1);
uint32_t qgen = xive_get_field32(END2_W1_GENERATION, end->w1);
- uint32_t qsize = xive_get_field32(END2_W3_QSIZE, end->w3);
- uint32_t qentries = 1 << (qsize + 10);
+ uint32_t qentries = xive2_end_get_qentries(end);
uint32_t nvx_blk = xive_get_field32(END2_W6_VP_BLOCK, end->w6);
uint32_t nvx_idx = xive_get_field32(END2_W6_VP_OFFSET, end->w6);
@@ -341,13 +362,12 @@ void xive2_nvgc_pic_print_info(Xive2Nvgc *nvgc, uint32_t nvgc_idx, GString *buf)
static void xive2_end_enqueue(Xive2End *end, uint32_t data)
{
uint64_t qaddr_base = xive2_end_qaddr(end);
- uint32_t qsize = xive_get_field32(END2_W3_QSIZE, end->w3);
uint32_t qindex = xive_get_field32(END2_W1_PAGE_OFF, end->w1);
uint32_t qgen = xive_get_field32(END2_W1_GENERATION, end->w1);
uint64_t qaddr = qaddr_base + (qindex << 2);
uint32_t qdata = cpu_to_be32((qgen << 31) | (data & 0x7fffffff));
- uint32_t qentries = 1 << (qsize + 10);
+ uint32_t qentries = xive2_end_get_qentries(end);
if (dma_memory_write(&address_space_memory, qaddr, &qdata, sizeof(qdata),
MEMTXATTRS_UNSPECIFIED)) {
@@ -361,8 +381,8 @@ static void xive2_end_enqueue(Xive2End *end, uint32_t data)
qgen ^= 1;
end->w1 = xive_set_field32(END2_W1_GENERATION, end->w1, qgen);
- /* TODO(PowerNV): reset GF bit on a cache watch operation */
- end->w1 = xive_set_field32(END2_W1_GEN_FLIPPED, end->w1, qgen);
+ /* Set gen flipped to 1, it gets reset on a cache watch operation */
+ end->w1 = xive_set_field32(END2_W1_GEN_FLIPPED, end->w1, 1);
}
end->w1 = xive_set_field32(END2_W1_PAGE_OFF, end->w1, qindex);
}
@@ -492,12 +512,13 @@ static void xive2_presenter_backlog_decr(XivePresenter *xptr,
*/
static void xive2_tctx_save_ctx(Xive2Router *xrtr, XiveTCTX *tctx,
- uint8_t nvp_blk, uint32_t nvp_idx,
- uint8_t ring)
+ uint8_t ring,
+ uint8_t nvp_blk, uint32_t nvp_idx)
{
CPUPPCState *env = &POWERPC_CPU(tctx->cs)->env;
uint32_t pir = env->spr_cb[SPR_PIR].default_value;
Xive2Nvp nvp;
+ uint8_t *sig_regs = xive_tctx_signal_regs(tctx, ring);
uint8_t *regs = &tctx->regs[ring];
if (xive2_router_get_nvp(xrtr, nvp_blk, nvp_idx, &nvp)) {
@@ -533,7 +554,14 @@ static void xive2_tctx_save_ctx(Xive2Router *xrtr, XiveTCTX *tctx,
}
nvp.w2 = xive_set_field32(NVP2_W2_IPB, nvp.w2, regs[TM_IPB]);
- nvp.w2 = xive_set_field32(NVP2_W2_CPPR, nvp.w2, regs[TM_CPPR]);
+
+ if ((nvp.w0 & NVP2_W0_P) || ring != TM_QW2_HV_POOL) {
+ /*
+ * Non-pool contexts always save CPPR (ignore p bit). XXX: Clarify
+ * whether that is the correct behaviour.
+ */
+ nvp.w2 = xive_set_field32(NVP2_W2_CPPR, nvp.w2, sig_regs[TM_CPPR]);
+ }
if (nvp.w0 & NVP2_W0_L) {
/*
* Typically not used. If LSMFB is restored with 0, it will
@@ -555,6 +583,7 @@ static void xive2_tctx_save_ctx(Xive2Router *xrtr, XiveTCTX *tctx,
xive2_router_write_nvp(xrtr, nvp_blk, nvp_idx, &nvp, 1);
}
+/* POOL cam is the same as OS cam encoding */
static void xive2_cam_decode(uint32_t cam, uint8_t *nvp_blk,
uint32_t *nvp_idx, bool *valid, bool *hw)
{
@@ -584,6 +613,79 @@ static uint32_t xive2_tctx_hw_cam_line(XivePresenter *xptr, XiveTCTX *tctx)
return xive2_nvp_cam_line(blk, 1 << tid_shift | (pir & tid_mask));
}
+static void xive2_redistribute(Xive2Router *xrtr, XiveTCTX *tctx, uint8_t ring)
+{
+ uint8_t *sig_regs = xive_tctx_signal_regs(tctx, ring);
+ uint8_t nsr = sig_regs[TM_NSR];
+ uint8_t pipr = sig_regs[TM_PIPR];
+ uint8_t crowd = NVx_CROWD_LVL(nsr);
+ uint8_t group = NVx_GROUP_LVL(nsr);
+ uint8_t nvgc_blk, end_blk, nvp_blk;
+ uint32_t nvgc_idx, end_idx, nvp_idx;
+ Xive2Nvgc nvgc;
+ uint8_t prio_limit;
+ uint32_t cfg;
+
+ /* redistribution is only for group/crowd interrupts */
+ if (!xive_nsr_indicates_group_exception(ring, nsr)) {
+ return;
+ }
+
+ /* Don't check return code since ring is expected to be invalidated */
+ xive2_tctx_get_nvp_indexes(tctx, ring, &nvp_blk, &nvp_idx);
+
+ trace_xive_redistribute(tctx->cs->cpu_index, ring, nvp_blk, nvp_idx);
+
+ trace_xive_redistribute(tctx->cs->cpu_index, ring, nvp_blk, nvp_idx);
+ /* convert crowd/group to blk/idx */
+ if (group > 0) {
+ nvgc_idx = (nvp_idx & (0xffffffff << group)) |
+ ((1 << (group - 1)) - 1);
+ } else {
+ nvgc_idx = nvp_idx;
+ }
+
+ if (crowd > 0) {
+ crowd = (crowd == 3) ? 4 : crowd;
+ nvgc_blk = (nvp_blk & (0xffffffff << crowd)) |
+ ((1 << (crowd - 1)) - 1);
+ } else {
+ nvgc_blk = nvp_blk;
+ }
+
+ /* Use blk/idx to retrieve the NVGC */
+ if (xive2_router_get_nvgc(xrtr, crowd, nvgc_blk, nvgc_idx, &nvgc)) {
+ qemu_log_mask(LOG_GUEST_ERROR, "XIVE: no %s %x/%x\n",
+ crowd ? "NVC" : "NVG", nvgc_blk, nvgc_idx);
+ return;
+ }
+
+ /* retrieve the END blk/idx from the NVGC */
+ end_blk = xive_get_field32(NVGC2_W1_END_BLK, nvgc.w1);
+ end_idx = xive_get_field32(NVGC2_W1_END_IDX, nvgc.w1);
+
+ /* determine number of priorities being used */
+ cfg = xive2_router_get_config(xrtr);
+ if (cfg & XIVE2_EN_VP_GRP_PRIORITY) {
+ prio_limit = 1 << GETFIELD(NVGC2_W1_PSIZE, nvgc.w1);
+ } else {
+ prio_limit = 1 << GETFIELD(XIVE2_VP_INT_PRIO, cfg);
+ }
+
+ /* add priority offset to end index */
+ end_idx += pipr % prio_limit;
+
+ /* trigger the group END */
+ xive2_router_end_notify(xrtr, end_blk, end_idx, 0, true);
+
+ /* clear interrupt indication for the context */
+ sig_regs[TM_NSR] = 0;
+ sig_regs[TM_PIPR] = sig_regs[TM_CPPR];
+ xive_tctx_reset_signal(tctx, ring);
+}
+
+static void xive2_tctx_process_pending(XiveTCTX *tctx, uint8_t sig_ring);
+
static uint64_t xive2_tm_pull_ctx(XivePresenter *xptr, XiveTCTX *tctx,
hwaddr offset, unsigned size, uint8_t ring)
{
@@ -595,10 +697,11 @@ static uint64_t xive2_tm_pull_ctx(XivePresenter *xptr, XiveTCTX *tctx,
uint8_t cur_ring;
bool valid;
bool do_save;
+ uint8_t nsr;
xive2_cam_decode(cam, &nvp_blk, &nvp_idx, &valid, &do_save);
- if (!valid) {
+ if (xive2_tctx_get_nvp_indexes(tctx, ring, &nvp_blk, &nvp_idx)) {
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: pulling invalid NVP %x/%x !?\n",
nvp_blk, nvp_idx);
}
@@ -608,21 +711,53 @@ static uint64_t xive2_tm_pull_ctx(XivePresenter *xptr, XiveTCTX *tctx,
cur_ring += XIVE_TM_RING_SIZE) {
uint32_t ringw2 = xive_tctx_word2(&tctx->regs[cur_ring]);
uint32_t ringw2_new = xive_set_field32(TM2_QW1W2_VO, ringw2, 0);
+ bool is_valid = !!(xive_get_field32(TM2_QW1W2_VO, ringw2));
+ uint8_t *sig_regs;
+
memcpy(&tctx->regs[cur_ring + TM_WORD2], &ringw2_new, 4);
+
+ /* Skip the rest for USER or invalid contexts */
+ if ((cur_ring == TM_QW0_USER) || !is_valid) {
+ continue;
+ }
+
+ /* Active group/crowd interrupts need to be redistributed */
+ sig_regs = xive_tctx_signal_regs(tctx, ring);
+ nsr = sig_regs[TM_NSR];
+ if (xive_nsr_indicates_group_exception(cur_ring, nsr)) {
+ /* Ensure ring matches NSR (for HV NSR POOL vs PHYS rings) */
+ if (cur_ring == xive_nsr_exception_ring(cur_ring, nsr)) {
+ xive2_redistribute(xrtr, tctx, cur_ring);
+ }
+ }
+
+ /*
+ * Lower external interrupt line of requested ring and below except for
+ * USER, which doesn't exist.
+ */
+ if (xive_nsr_indicates_exception(cur_ring, nsr)) {
+ if (cur_ring == xive_nsr_exception_ring(cur_ring, nsr)) {
+ xive_tctx_reset_signal(tctx, cur_ring);
+ }
+ }
}
- if (xive2_router_get_config(xrtr) & XIVE2_VP_SAVE_RESTORE && do_save) {
- xive2_tctx_save_ctx(xrtr, tctx, nvp_blk, nvp_idx, ring);
+ if (ring == TM_QW2_HV_POOL) {
+ /* Re-check phys for interrupts if pool was disabled */
+ nsr = tctx->regs[TM_QW3_HV_PHYS + TM_NSR];
+ if (xive_nsr_indicates_exception(TM_QW3_HV_PHYS, nsr)) {
+ /* Ring must be PHYS because POOL would have been redistributed */
+ g_assert(xive_nsr_exception_ring(TM_QW3_HV_PHYS, nsr) ==
+ TM_QW3_HV_PHYS);
+ } else {
+ xive2_tctx_process_pending(tctx, TM_QW3_HV_PHYS);
+ }
}
- /*
- * Lower external interrupt line of requested ring and below except for
- * USER, which doesn't exist.
- */
- for (cur_ring = TM_QW1_OS; cur_ring <= ring;
- cur_ring += XIVE_TM_RING_SIZE) {
- xive_tctx_reset_signal(tctx, cur_ring);
+ if (xive2_router_get_config(xrtr) & XIVE2_VP_SAVE_RESTORE && do_save) {
+ xive2_tctx_save_ctx(xrtr, tctx, ring, nvp_blk, nvp_idx);
}
+
return target_ringw2;
}
@@ -632,6 +767,18 @@ uint64_t xive2_tm_pull_os_ctx(XivePresenter *xptr, XiveTCTX *tctx,
return xive2_tm_pull_ctx(xptr, tctx, offset, size, TM_QW1_OS);
}
+uint64_t xive2_tm_pull_pool_ctx(XivePresenter *xptr, XiveTCTX *tctx,
+ hwaddr offset, unsigned size)
+{
+ return xive2_tm_pull_ctx(xptr, tctx, offset, size, TM_QW2_HV_POOL);
+}
+
+uint64_t xive2_tm_pull_phys_ctx(XivePresenter *xptr, XiveTCTX *tctx,
+ hwaddr offset, unsigned size)
+{
+ return xive2_tm_pull_ctx(xptr, tctx, offset, size, TM_QW3_HV_PHYS);
+}
+
#define REPORT_LINE_GEN1_SIZE 16
static void xive2_tm_report_line_gen1(XiveTCTX *tctx, uint8_t *data,
@@ -741,12 +888,15 @@ void xive2_tm_pull_phys_ctx_ol(XivePresenter *xptr, XiveTCTX *tctx,
xive2_tm_pull_ctx_ol(xptr, tctx, offset, value, size, TM_QW3_HV_PHYS);
}
-static uint8_t xive2_tctx_restore_os_ctx(Xive2Router *xrtr, XiveTCTX *tctx,
- uint8_t nvp_blk, uint32_t nvp_idx,
- Xive2Nvp *nvp)
+static uint8_t xive2_tctx_restore_ctx(Xive2Router *xrtr, XiveTCTX *tctx,
+ uint8_t ring,
+ uint8_t nvp_blk, uint32_t nvp_idx,
+ Xive2Nvp *nvp)
{
CPUPPCState *env = &POWERPC_CPU(tctx->cs)->env;
uint32_t pir = env->spr_cb[SPR_PIR].default_value;
+ uint8_t *sig_regs = xive_tctx_signal_regs(tctx, ring);
+ uint8_t *regs = &tctx->regs[ring];
uint8_t cppr;
if (!xive2_nvp_is_hw(nvp)) {
@@ -759,10 +909,10 @@ static uint8_t xive2_tctx_restore_os_ctx(Xive2Router *xrtr, XiveTCTX *tctx,
nvp->w2 = xive_set_field32(NVP2_W2_CPPR, nvp->w2, 0);
xive2_router_write_nvp(xrtr, nvp_blk, nvp_idx, nvp, 2);
- tctx->regs[TM_QW1_OS + TM_CPPR] = cppr;
- tctx->regs[TM_QW1_OS + TM_LSMFB] = xive_get_field32(NVP2_W2_LSMFB, nvp->w2);
- tctx->regs[TM_QW1_OS + TM_LGS] = xive_get_field32(NVP2_W2_LGS, nvp->w2);
- tctx->regs[TM_QW1_OS + TM_T] = xive_get_field32(NVP2_W2_T, nvp->w2);
+ sig_regs[TM_CPPR] = cppr;
+ regs[TM_LSMFB] = xive_get_field32(NVP2_W2_LSMFB, nvp->w2);
+ regs[TM_LGS] = xive_get_field32(NVP2_W2_LGS, nvp->w2);
+ regs[TM_T] = xive_get_field32(NVP2_W2_T, nvp->w2);
nvp->w1 = xive_set_field32(NVP2_W1_CO, nvp->w1, 1);
nvp->w1 = xive_set_field32(NVP2_W1_CO_THRID_VALID, nvp->w1, 1);
@@ -771,9 +921,18 @@ static uint8_t xive2_tctx_restore_os_ctx(Xive2Router *xrtr, XiveTCTX *tctx,
/*
* Checkout privilege: 0:OS, 1:Pool, 2:Hard
*
- * TODO: we only support OS push/pull
+ * TODO: we don't support hard push/pull
*/
- nvp->w1 = xive_set_field32(NVP2_W1_CO_PRIV, nvp->w1, 0);
+ switch (ring) {
+ case TM_QW1_OS:
+ nvp->w1 = xive_set_field32(NVP2_W1_CO_PRIV, nvp->w1, 0);
+ break;
+ case TM_QW2_HV_POOL:
+ nvp->w1 = xive_set_field32(NVP2_W1_CO_PRIV, nvp->w1, 1);
+ break;
+ default:
+ g_assert_not_reached();
+ }
xive2_router_write_nvp(xrtr, nvp_blk, nvp_idx, nvp, 1);
@@ -781,18 +940,14 @@ static uint8_t xive2_tctx_restore_os_ctx(Xive2Router *xrtr, XiveTCTX *tctx,
return cppr;
}
-static void xive2_tctx_need_resend(Xive2Router *xrtr, XiveTCTX *tctx,
+/* Restore TIMA VP context from NVP backlog */
+static void xive2_tctx_restore_nvp(Xive2Router *xrtr, XiveTCTX *tctx,
+ uint8_t ring,
uint8_t nvp_blk, uint32_t nvp_idx,
bool do_restore)
{
- XivePresenter *xptr = XIVE_PRESENTER(xrtr);
+ uint8_t *regs = &tctx->regs[ring];
uint8_t ipb;
- uint8_t backlog_level;
- uint8_t group_level;
- uint8_t first_group;
- uint8_t backlog_prio;
- uint8_t group_prio;
- uint8_t *regs = &tctx->regs[TM_QW1_OS];
Xive2Nvp nvp;
/*
@@ -812,9 +967,8 @@ static void xive2_tctx_need_resend(Xive2Router *xrtr, XiveTCTX *tctx,
}
/* Automatically restore thread context registers */
- if (xive2_router_get_config(xrtr) & XIVE2_VP_SAVE_RESTORE &&
- do_restore) {
- xive2_tctx_restore_os_ctx(xrtr, tctx, nvp_blk, nvp_idx, &nvp);
+ if (xive2_router_get_config(xrtr) & XIVE2_VP_SAVE_RESTORE && do_restore) {
+ xive2_tctx_restore_ctx(xrtr, tctx, ring, nvp_blk, nvp_idx, &nvp);
}
ipb = xive_get_field32(NVP2_W2_IPB, nvp.w2);
@@ -822,143 +976,230 @@ static void xive2_tctx_need_resend(Xive2Router *xrtr, XiveTCTX *tctx,
nvp.w2 = xive_set_field32(NVP2_W2_IPB, nvp.w2, 0);
xive2_router_write_nvp(xrtr, nvp_blk, nvp_idx, &nvp, 2);
}
+ /* IPB bits in the backlog are merged with the TIMA IPB bits */
regs[TM_IPB] |= ipb;
- backlog_prio = xive_ipb_to_pipr(ipb);
- backlog_level = 0;
-
- first_group = xive_get_field32(NVP2_W0_PGOFIRST, nvp.w0);
- if (first_group && regs[TM_LSMFB] < backlog_prio) {
- group_prio = xive2_presenter_backlog_scan(xptr, nvp_blk, nvp_idx,
- first_group, &group_level);
- regs[TM_LSMFB] = group_prio;
- if (regs[TM_LGS] && group_prio < backlog_prio) {
- /* VP can take a group interrupt */
- xive2_presenter_backlog_decr(xptr, nvp_blk, nvp_idx,
- group_prio, group_level);
- backlog_prio = group_prio;
- backlog_level = group_level;
- }
- }
-
- /*
- * Compute the PIPR based on the restored state.
- * It will raise the External interrupt signal if needed.
- */
- xive_tctx_pipr_update(tctx, TM_QW1_OS, backlog_prio, backlog_level);
}
/*
- * Updating the OS CAM line can trigger a resend of interrupt
+ * Updating the ring CAM line can trigger a resend of interrupt
*/
-void xive2_tm_push_os_ctx(XivePresenter *xptr, XiveTCTX *tctx,
- hwaddr offset, uint64_t value, unsigned size)
+static void xive2_tm_push_ctx(XivePresenter *xptr, XiveTCTX *tctx,
+ hwaddr offset, uint64_t value, unsigned size,
+ uint8_t ring)
{
uint32_t cam;
- uint32_t qw1w2;
- uint64_t qw1dw1;
+ uint32_t w2;
+ uint64_t dw1;
uint8_t nvp_blk;
uint32_t nvp_idx;
- bool vo;
+ bool v;
bool do_restore;
+ if (xive_ring_valid(tctx, ring)) {
+ qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Attempt to push VP to enabled"
+ " ring 0x%02x\n", ring);
+ return;
+ }
+
/* First update the thead context */
switch (size) {
+ case 1:
+ tctx->regs[ring + TM_WORD2] = value & 0xff;
+ cam = xive2_tctx_hw_cam_line(xptr, tctx);
+ cam |= ((value & 0xc0) << 24); /* V and H bits */
+ break;
case 4:
cam = value;
- qw1w2 = cpu_to_be32(cam);
- memcpy(&tctx->regs[TM_QW1_OS + TM_WORD2], &qw1w2, 4);
+ w2 = cpu_to_be32(cam);
+ memcpy(&tctx->regs[ring + TM_WORD2], &w2, 4);
break;
case 8:
cam = value >> 32;
- qw1dw1 = cpu_to_be64(value);
- memcpy(&tctx->regs[TM_QW1_OS + TM_WORD2], &qw1dw1, 8);
+ dw1 = cpu_to_be64(value);
+ memcpy(&tctx->regs[ring + TM_WORD2], &dw1, 8);
break;
default:
g_assert_not_reached();
}
- xive2_cam_decode(cam, &nvp_blk, &nvp_idx, &vo, &do_restore);
+ xive2_cam_decode(cam, &nvp_blk, &nvp_idx, &v, &do_restore);
/* Check the interrupt pending bits */
- if (vo) {
- xive2_tctx_need_resend(XIVE2_ROUTER(xptr), tctx, nvp_blk, nvp_idx,
- do_restore);
+ if (v) {
+ Xive2Router *xrtr = XIVE2_ROUTER(xptr);
+ uint8_t cur_ring;
+
+ xive2_tctx_restore_nvp(xrtr, tctx, ring,
+ nvp_blk, nvp_idx, do_restore);
+
+ for (cur_ring = TM_QW1_OS; cur_ring <= ring;
+ cur_ring += XIVE_TM_RING_SIZE) {
+ uint8_t *sig_regs = xive_tctx_signal_regs(tctx, cur_ring);
+ uint8_t nsr = sig_regs[TM_NSR];
+
+ if (!xive_ring_valid(tctx, cur_ring)) {
+ continue;
+ }
+
+ if (cur_ring == TM_QW2_HV_POOL) {
+ if (xive_nsr_indicates_exception(cur_ring, nsr)) {
+ g_assert(xive_nsr_exception_ring(cur_ring, nsr) ==
+ TM_QW3_HV_PHYS);
+ xive2_redistribute(xrtr, tctx,
+ xive_nsr_exception_ring(ring, nsr));
+ }
+ xive2_tctx_process_pending(tctx, TM_QW3_HV_PHYS);
+ break;
+ }
+ xive2_tctx_process_pending(tctx, cur_ring);
+ }
}
}
+void xive2_tm_push_os_ctx(XivePresenter *xptr, XiveTCTX *tctx,
+ hwaddr offset, uint64_t value, unsigned size)
+{
+ xive2_tm_push_ctx(xptr, tctx, offset, value, size, TM_QW1_OS);
+}
+
+void xive2_tm_push_pool_ctx(XivePresenter *xptr, XiveTCTX *tctx,
+ hwaddr offset, uint64_t value, unsigned size)
+{
+ xive2_tm_push_ctx(xptr, tctx, offset, value, size, TM_QW2_HV_POOL);
+}
+
+void xive2_tm_push_phys_ctx(XivePresenter *xptr, XiveTCTX *tctx,
+ hwaddr offset, uint64_t value, unsigned size)
+{
+ xive2_tm_push_ctx(xptr, tctx, offset, value, size, TM_QW3_HV_PHYS);
+}
+
+/* returns -1 if ring is invalid, but still populates block and index */
static int xive2_tctx_get_nvp_indexes(XiveTCTX *tctx, uint8_t ring,
- uint32_t *nvp_blk, uint32_t *nvp_idx)
+ uint8_t *nvp_blk, uint32_t *nvp_idx)
{
- uint32_t w2, cam;
+ uint32_t w2;
+ uint32_t cam = 0;
+ int rc = 0;
w2 = xive_tctx_word2(&tctx->regs[ring]);
switch (ring) {
case TM_QW1_OS:
if (!(be32_to_cpu(w2) & TM2_QW1W2_VO)) {
- return -1;
+ rc = -1;
}
cam = xive_get_field32(TM2_QW1W2_OS_CAM, w2);
break;
case TM_QW2_HV_POOL:
if (!(be32_to_cpu(w2) & TM2_QW2W2_VP)) {
- return -1;
+ rc = -1;
}
cam = xive_get_field32(TM2_QW2W2_POOL_CAM, w2);
break;
case TM_QW3_HV_PHYS:
if (!(be32_to_cpu(w2) & TM2_QW3W2_VT)) {
- return -1;
+ rc = -1;
}
cam = xive2_tctx_hw_cam_line(tctx->xptr, tctx);
break;
default:
- return -1;
+ rc = -1;
}
*nvp_blk = xive2_nvp_blk(cam);
*nvp_idx = xive2_nvp_idx(cam);
- return 0;
+ return rc;
}
-static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr)
+static void xive2_tctx_accept_el(XivePresenter *xptr, XiveTCTX *tctx,
+ uint8_t ring, uint8_t cl_ring)
{
- uint8_t *regs = &tctx->regs[ring];
- Xive2Router *xrtr = XIVE2_ROUTER(tctx->xptr);
- uint8_t old_cppr, backlog_prio, first_group, group_level = 0;
- uint8_t pipr_min, lsmfb_min, ring_min;
- bool group_enabled;
- uint32_t nvp_blk, nvp_idx;
+ uint64_t rd;
+ Xive2Router *xrtr = XIVE2_ROUTER(xptr);
+ uint32_t nvp_idx, xive2_cfg;
+ uint8_t nvp_blk;
Xive2Nvp nvp;
- int rc;
+ uint64_t phys_addr;
+ uint8_t OGen = 0;
- trace_xive_tctx_set_cppr(tctx->cs->cpu_index, ring,
- regs[TM_IPB], regs[TM_PIPR],
- cppr, regs[TM_NSR]);
+ xive2_tctx_get_nvp_indexes(tctx, cl_ring, &nvp_blk, &nvp_idx);
- if (cppr > XIVE_PRIORITY_MAX) {
- cppr = 0xff;
+ if (xive2_router_get_nvp(xrtr, (uint8_t)nvp_blk, nvp_idx, &nvp)) {
+ qemu_log_mask(LOG_GUEST_ERROR, "XIVE: No NVP %x/%x\n",
+ nvp_blk, nvp_idx);
+ return;
+ }
+
+ if (!xive2_nvp_is_valid(&nvp)) {
+ qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid NVP %x/%x\n",
+ nvp_blk, nvp_idx);
+ return;
+ }
+
+
+ rd = xive_tctx_accept(tctx, ring);
+
+ if (ring == TM_QW1_OS) {
+ OGen = tctx->regs[ring + TM_OGEN];
+ }
+ xive2_cfg = xive2_router_get_config(xrtr);
+ phys_addr = xive2_nvp_reporting_addr(&nvp);
+ uint8_t report_data[REPORT_LINE_GEN1_SIZE];
+ memset(report_data, 0xff, sizeof(report_data));
+ if ((OGen == 1) || (xive2_cfg & XIVE2_GEN1_TIMA_OS)) {
+ report_data[8] = (rd >> 8) & 0xff;
+ report_data[9] = rd & 0xff;
+ } else {
+ report_data[0] = (rd >> 8) & 0xff;
+ report_data[1] = rd & 0xff;
}
+ cpu_physical_memory_write(phys_addr, report_data, REPORT_LINE_GEN1_SIZE);
+}
+
+void xive2_tm_ack_os_el(XivePresenter *xptr, XiveTCTX *tctx,
+ hwaddr offset, uint64_t value, unsigned size)
+{
+ xive2_tctx_accept_el(xptr, tctx, TM_QW1_OS, TM_QW1_OS);
+}
+
+/* Re-calculate and present pending interrupts */
+static void xive2_tctx_process_pending(XiveTCTX *tctx, uint8_t sig_ring)
+{
+ uint8_t *sig_regs = &tctx->regs[sig_ring];
+ Xive2Router *xrtr = XIVE2_ROUTER(tctx->xptr);
+ uint8_t backlog_prio;
+ uint8_t first_group;
+ uint8_t group_level;
+ uint8_t pipr_min;
+ uint8_t lsmfb_min;
+ uint8_t ring_min;
+ uint8_t cppr = sig_regs[TM_CPPR];
+ bool group_enabled;
+ Xive2Nvp nvp;
+ int rc;
- old_cppr = regs[TM_CPPR];
- regs[TM_CPPR] = cppr;
+ g_assert(sig_ring == TM_QW3_HV_PHYS || sig_ring == TM_QW1_OS);
+ g_assert(sig_regs[TM_WORD2] & 0x80);
+ g_assert(!xive_nsr_indicates_group_exception(sig_ring, sig_regs[TM_NSR]));
/*
* Recompute the PIPR based on local pending interrupts. It will
* be adjusted below if needed in case of pending group interrupts.
*/
- pipr_min = xive_ipb_to_pipr(regs[TM_IPB]);
- group_enabled = !!regs[TM_LGS];
- lsmfb_min = (group_enabled) ? regs[TM_LSMFB] : 0xff;
- ring_min = ring;
+again:
+ pipr_min = xive_ipb_to_pipr(sig_regs[TM_IPB]);
+ group_enabled = !!sig_regs[TM_LGS];
+ lsmfb_min = group_enabled ? sig_regs[TM_LSMFB] : 0xff;
+ ring_min = sig_ring;
+ group_level = 0;
/* PHYS updates also depend on POOL values */
- if (ring == TM_QW3_HV_PHYS) {
- uint8_t *pregs = &tctx->regs[TM_QW2_HV_POOL];
+ if (sig_ring == TM_QW3_HV_PHYS) {
+ uint8_t *pool_regs = &tctx->regs[TM_QW2_HV_POOL];
/* POOL values only matter if POOL ctx is valid */
- if (pregs[TM_WORD2] & 0x80) {
-
- uint8_t pool_pipr = xive_ipb_to_pipr(pregs[TM_IPB]);
- uint8_t pool_lsmfb = pregs[TM_LSMFB];
+ if (pool_regs[TM_WORD2] & 0x80) {
+ uint8_t pool_pipr = xive_ipb_to_pipr(pool_regs[TM_IPB]);
+ uint8_t pool_lsmfb = pool_regs[TM_LSMFB];
/*
* Determine highest priority interrupt and
@@ -972,7 +1213,7 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr)
}
/* Values needed for group priority calculation */
- if (pregs[TM_LGS] && (pool_lsmfb < lsmfb_min)) {
+ if (pool_regs[TM_LGS] && (pool_lsmfb < lsmfb_min)) {
group_enabled = true;
lsmfb_min = pool_lsmfb;
if (lsmfb_min < pipr_min) {
@@ -981,32 +1222,26 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr)
}
}
}
- regs[TM_PIPR] = pipr_min;
-
- rc = xive2_tctx_get_nvp_indexes(tctx, ring_min, &nvp_blk, &nvp_idx);
- if (rc) {
- qemu_log_mask(LOG_GUEST_ERROR, "XIVE: set CPPR on invalid context\n");
- return;
- }
-
- if (cppr < old_cppr) {
- /*
- * FIXME: check if there's a group interrupt being presented
- * and if the new cppr prevents it. If so, then the group
- * interrupt needs to be re-added to the backlog and
- * re-triggered (see re-trigger END info in the NVGC
- * structure)
- */
- }
if (group_enabled &&
lsmfb_min < cppr &&
- lsmfb_min < regs[TM_PIPR]) {
+ lsmfb_min < pipr_min) {
+
+ uint8_t nvp_blk;
+ uint32_t nvp_idx;
+
/*
* Thread has seen a group interrupt with a higher priority
* than the new cppr or pending local interrupt. Check the
* backlog
*/
+ rc = xive2_tctx_get_nvp_indexes(tctx, ring_min, &nvp_blk, &nvp_idx);
+ if (rc) {
+ qemu_log_mask(LOG_GUEST_ERROR, "XIVE: set CPPR on invalid "
+ "context\n");
+ return;
+ }
+
if (xive2_router_get_nvp(xrtr, nvp_blk, nvp_idx, &nvp)) {
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: No NVP %x/%x\n",
nvp_blk, nvp_idx);
@@ -1030,14 +1265,85 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr)
nvp_blk, nvp_idx,
first_group, &group_level);
tctx->regs[ring_min + TM_LSMFB] = backlog_prio;
- if (backlog_prio != 0xFF) {
- xive2_presenter_backlog_decr(tctx->xptr, nvp_blk, nvp_idx,
- backlog_prio, group_level);
- regs[TM_PIPR] = backlog_prio;
+ if (backlog_prio != lsmfb_min) {
+ /*
+ * If the group backlog scan finds a less favored or no interrupt,
+ * then re-do the processing which may turn up a more favored
+ * interrupt from IPB or the other pool. Backlog should not
+ * find a priority < LSMFB.
+ */
+ g_assert(backlog_prio >= lsmfb_min);
+ goto again;
+ }
+
+ xive2_presenter_backlog_decr(tctx->xptr, nvp_blk, nvp_idx,
+ backlog_prio, group_level);
+ pipr_min = backlog_prio;
+ }
+
+ if (pipr_min > cppr) {
+ pipr_min = cppr;
+ }
+ xive_tctx_pipr_set(tctx, ring_min, pipr_min, group_level);
+}
+
+/* NOTE: CPPR only exists for TM_QW1_OS and TM_QW3_HV_PHYS */
+static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t sig_ring, uint8_t cppr)
+{
+ uint8_t *sig_regs = &tctx->regs[sig_ring];
+ Xive2Router *xrtr = XIVE2_ROUTER(tctx->xptr);
+ uint8_t old_cppr;
+ uint8_t nsr = sig_regs[TM_NSR];
+
+ g_assert(sig_ring == TM_QW1_OS || sig_ring == TM_QW3_HV_PHYS);
+
+ g_assert(tctx->regs[TM_QW2_HV_POOL + TM_NSR] == 0);
+ g_assert(tctx->regs[TM_QW2_HV_POOL + TM_PIPR] == 0);
+ g_assert(tctx->regs[TM_QW2_HV_POOL + TM_CPPR] == 0);
+
+ /* XXX: should show pool IPB for PHYS ring */
+ trace_xive_tctx_set_cppr(tctx->cs->cpu_index, sig_ring,
+ sig_regs[TM_IPB], sig_regs[TM_PIPR],
+ cppr, nsr);
+
+ if (cppr > XIVE_PRIORITY_MAX) {
+ cppr = 0xff;
+ }
+
+ old_cppr = sig_regs[TM_CPPR];
+ sig_regs[TM_CPPR] = cppr;
+
+ /* Handle increased CPPR priority (lower value) */
+ if (cppr < old_cppr) {
+ if (cppr <= sig_regs[TM_PIPR]) {
+ /* CPPR lowered below PIPR, must un-present interrupt */
+ if (xive_nsr_indicates_exception(sig_ring, nsr)) {
+ if (xive_nsr_indicates_group_exception(sig_ring, nsr)) {
+ /* redistribute precluded active grp interrupt */
+ xive2_redistribute(xrtr, tctx,
+ xive_nsr_exception_ring(sig_ring, nsr));
+ return;
+ }
+ }
+
+ /* interrupt is VP directed, pending in IPB */
+ xive_tctx_pipr_set(tctx, sig_ring, cppr, 0);
+ return;
+ } else {
+ /* CPPR was lowered, but still above PIPR. No action needed. */
+ return;
}
}
- /* CPPR has changed, check if we need to raise a pending exception */
- xive_tctx_notify(tctx, ring_min, group_level);
+
+ /* CPPR didn't change, nothing needs to be done */
+ if (cppr == old_cppr) {
+ return;
+ }
+
+ /* CPPR priority decreased (higher value) */
+ if (!xive_nsr_indicates_exception(sig_ring, nsr)) {
+ xive2_tctx_process_pending(tctx, sig_ring);
+ }
}
void xive2_tm_set_hv_cppr(XivePresenter *xptr, XiveTCTX *tctx,
@@ -1052,6 +1358,34 @@ void xive2_tm_set_os_cppr(XivePresenter *xptr, XiveTCTX *tctx,
xive2_tctx_set_cppr(tctx, TM_QW1_OS, value & 0xff);
}
+/*
+ * Adjust the IPB to allow a CPU to process event queues of other
+ * priorities during one physical interrupt cycle.
+ */
+void xive2_tm_set_os_pending(XivePresenter *xptr, XiveTCTX *tctx,
+ hwaddr offset, uint64_t value, unsigned size)
+{
+ Xive2Router *xrtr = XIVE2_ROUTER(xptr);
+ uint8_t ring = TM_QW1_OS;
+ uint8_t *regs = &tctx->regs[ring];
+ uint8_t priority = value & 0xff;
+
+ /*
+ * XXX: should this simply set a bit in IPB and wait for it to be picked
+ * up next cycle, or is it supposed to present it now? We implement the
+ * latter here.
+ */
+ regs[TM_IPB] |= xive_priority_to_ipb(priority);
+ if (xive_ipb_to_pipr(regs[TM_IPB]) >= regs[TM_PIPR]) {
+ return;
+ }
+ if (xive_nsr_indicates_group_exception(ring, regs[TM_NSR])) {
+ xive2_redistribute(xrtr, tctx, ring);
+ }
+
+ xive_tctx_pipr_present(tctx, ring, priority, 0);
+}
+
static void xive2_tctx_set_target(XiveTCTX *tctx, uint8_t ring, uint8_t target)
{
uint8_t *regs = &tctx->regs[ring];
@@ -1259,9 +1593,7 @@ int xive2_presenter_tctx_match(XivePresenter *xptr, XiveTCTX *tctx,
bool xive2_tm_irq_precluded(XiveTCTX *tctx, int ring, uint8_t priority)
{
- /* HV_POOL ring uses HV_PHYS NSR, CPPR and PIPR registers */
- uint8_t alt_ring = (ring == TM_QW2_HV_POOL) ? TM_QW3_HV_PHYS : ring;
- uint8_t *alt_regs = &tctx->regs[alt_ring];
+ uint8_t *sig_regs = xive_tctx_signal_regs(tctx, ring);
/*
* The xive2_presenter_tctx_match() above tells if there's a match
@@ -1269,7 +1601,7 @@ bool xive2_tm_irq_precluded(XiveTCTX *tctx, int ring, uint8_t priority)
* priority to know if the thread can take the interrupt now or if
* it is precluded.
*/
- if (priority < alt_regs[TM_CPPR]) {
+ if (priority < sig_regs[TM_PIPR]) {
return false;
}
return true;
@@ -1322,12 +1654,14 @@ static bool xive2_router_end_es_notify(Xive2Router *xrtr, uint8_t end_blk,
* message has the same parameters than in the function below.
*/
static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk,
- uint32_t end_idx, uint32_t end_data)
+ uint32_t end_idx, uint32_t end_data,
+ bool redistribute)
{
Xive2End end;
uint8_t priority;
uint8_t format;
- bool found, precluded;
+ XiveTCTXMatch match;
+ bool crowd, cam_ignore;
uint8_t nvx_blk;
uint32_t nvx_idx;
@@ -1350,7 +1684,8 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk,
return;
}
- if (xive2_end_is_enqueue(&end)) {
+ if (!redistribute && xive2_end_is_enqueue(&end)) {
+ trace_xive_end_enqueue(end_blk, end_idx, end_data);
xive2_end_enqueue(&end, end_data);
/* Enqueuing event data modifies the EQ toggle and index */
xive2_router_write_end(xrtr, end_blk, end_idx, &end, 1);
@@ -1396,16 +1731,28 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk,
*/
nvx_blk = xive_get_field32(END2_W6_VP_BLOCK, end.w6);
nvx_idx = xive_get_field32(END2_W6_VP_OFFSET, end.w6);
-
- found = xive_presenter_notify(xrtr->xfb, format, nvx_blk, nvx_idx,
- xive2_end_is_crowd(&end), xive2_end_is_ignore(&end),
- priority,
- xive_get_field32(END2_W7_F1_LOG_SERVER_ID, end.w7),
- &precluded);
+ crowd = xive2_end_is_crowd(&end);
+ cam_ignore = xive2_end_is_ignore(&end);
/* TODO: Auto EOI. */
+ if (xive_presenter_match(xrtr->xfb, format, nvx_blk, nvx_idx,
+ crowd, cam_ignore, priority,
+ xive_get_field32(END2_W7_F1_LOG_SERVER_ID, end.w7),
+ &match)) {
+ XiveTCTX *tctx = match.tctx;
+ uint8_t ring = match.ring;
+ uint8_t *sig_regs = xive_tctx_signal_regs(tctx, ring);
+ uint8_t nsr = sig_regs[TM_NSR];
+ uint8_t group_level;
+
+ if (priority < sig_regs[TM_PIPR] &&
+ xive_nsr_indicates_group_exception(ring, nsr)) {
+ xive2_redistribute(xrtr, tctx, xive_nsr_exception_ring(ring, nsr));
+ }
- if (found) {
+ group_level = xive_get_group_level(crowd, cam_ignore, nvx_blk, nvx_idx);
+ trace_xive_presenter_notify(nvx_blk, nvx_idx, ring, group_level);
+ xive_tctx_pipr_present(tctx, ring, priority, group_level);
return;
}
@@ -1423,7 +1770,7 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk,
return;
}
- if (!xive2_end_is_ignore(&end)) {
+ if (!cam_ignore) {
uint8_t ipb;
Xive2Nvp nvp;
@@ -1452,9 +1799,6 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk,
} else {
Xive2Nvgc nvgc;
uint32_t backlog;
- bool crowd;
-
- crowd = xive2_end_is_crowd(&end);
/*
* For groups and crowds, the per-priority backlog
@@ -1486,9 +1830,7 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk,
if (backlog == 1) {
XiveFabricClass *xfc = XIVE_FABRIC_GET_CLASS(xrtr->xfb);
xfc->broadcast(xrtr->xfb, nvx_blk, nvx_idx,
- xive2_end_is_crowd(&end),
- xive2_end_is_ignore(&end),
- priority);
+ crowd, cam_ignore, priority);
if (!xive2_end_is_precluded_escalation(&end)) {
/*
@@ -1522,18 +1864,41 @@ do_escalation:
}
}
- /*
- * The END trigger becomes an Escalation trigger
- */
- xive2_router_end_notify(xrtr,
- xive_get_field32(END2_W4_END_BLOCK, end.w4),
- xive_get_field32(END2_W4_ESC_END_INDEX, end.w4),
- xive_get_field32(END2_W5_ESC_END_DATA, end.w5));
+ if (xive2_end_is_escalate_end(&end)) {
+ /*
+ * Perform END Adaptive escalation processing
+ * The END trigger becomes an Escalation trigger
+ */
+ uint8_t esc_blk = xive_get_field32(END2_W4_END_BLOCK, end.w4);
+ uint32_t esc_idx = xive_get_field32(END2_W4_ESC_END_INDEX, end.w4);
+ uint32_t esc_data = xive_get_field32(END2_W5_ESC_END_DATA, end.w5);
+ trace_xive_escalate_end(end_blk, end_idx, esc_blk, esc_idx, esc_data);
+ xive2_router_end_notify(xrtr, esc_blk, esc_idx, esc_data, false);
+ } /* end END adaptive escalation */
+
+ else {
+ uint32_t lisn; /* Logical Interrupt Source Number */
+
+ /*
+ * Perform ESB escalation processing
+ * E[N] == 1 --> N
+ * Req[Block] <- E[ESB_Block]
+ * Req[Index] <- E[ESB_Index]
+ * Req[Offset] <- 0x000
+ * Execute <ESB Store> Req command
+ */
+ lisn = XIVE_EAS(xive_get_field32(END2_W4_END_BLOCK, end.w4),
+ xive_get_field32(END2_W4_ESC_END_INDEX, end.w4));
+
+ trace_xive_escalate_esb(end_blk, end_idx, lisn);
+ xive2_notify(xrtr, lisn, true /* pq_checked */);
+ }
+
+ return;
}
-void xive2_router_notify(XiveNotifier *xn, uint32_t lisn, bool pq_checked)
+void xive2_notify(Xive2Router *xrtr , uint32_t lisn, bool pq_checked)
{
- Xive2Router *xrtr = XIVE2_ROUTER(xn);
uint8_t eas_blk = XIVE_EAS_BLOCK(lisn);
uint32_t eas_idx = XIVE_EAS_INDEX(lisn);
Xive2Eas eas;
@@ -1576,13 +1941,31 @@ void xive2_router_notify(XiveNotifier *xn, uint32_t lisn, bool pq_checked)
return;
}
+ /* TODO: add support for EAS resume */
+ if (xive2_eas_is_resume(&eas)) {
+ qemu_log_mask(LOG_UNIMP,
+ "XIVE: EAS resume processing unimplemented - LISN %x\n",
+ lisn);
+ return;
+ }
+
/*
* The event trigger becomes an END trigger
*/
xive2_router_end_notify(xrtr,
- xive_get_field64(EAS2_END_BLOCK, eas.w),
- xive_get_field64(EAS2_END_INDEX, eas.w),
- xive_get_field64(EAS2_END_DATA, eas.w));
+ xive_get_field64(EAS2_END_BLOCK, eas.w),
+ xive_get_field64(EAS2_END_INDEX, eas.w),
+ xive_get_field64(EAS2_END_DATA, eas.w),
+ false);
+ return;
+}
+
+void xive2_router_notify(XiveNotifier *xn, uint32_t lisn, bool pq_checked)
+{
+ Xive2Router *xrtr = XIVE2_ROUTER(xn);
+
+ xive2_notify(xrtr, lisn, pq_checked);
+ return;
}
static const Property xive2_router_properties[] = {
diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 4a49e9d..d84c906 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -2608,62 +2608,46 @@ static void pnv_pic_print_info(InterruptStatsProvider *obj, GString *buf)
}
}
-static int pnv_match_nvt(XiveFabric *xfb, uint8_t format,
- uint8_t nvt_blk, uint32_t nvt_idx,
- bool crowd, bool cam_ignore, uint8_t priority,
- uint32_t logic_serv,
- XiveTCTXMatch *match)
+static bool pnv_match_nvt(XiveFabric *xfb, uint8_t format,
+ uint8_t nvt_blk, uint32_t nvt_idx,
+ bool crowd, bool cam_ignore, uint8_t priority,
+ uint32_t logic_serv,
+ XiveTCTXMatch *match)
{
PnvMachineState *pnv = PNV_MACHINE(xfb);
- int total_count = 0;
int i;
for (i = 0; i < pnv->num_chips; i++) {
Pnv9Chip *chip9 = PNV9_CHIP(pnv->chips[i]);
XivePresenter *xptr = XIVE_PRESENTER(&chip9->xive);
XivePresenterClass *xpc = XIVE_PRESENTER_GET_CLASS(xptr);
- int count;
- count = xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, crowd,
- cam_ignore, priority, logic_serv, match);
-
- if (count < 0) {
- return count;
- }
-
- total_count += count;
+ xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, crowd,
+ cam_ignore, priority, logic_serv, match);
}
- return total_count;
+ return !!match->count;
}
-static int pnv10_xive_match_nvt(XiveFabric *xfb, uint8_t format,
- uint8_t nvt_blk, uint32_t nvt_idx,
- bool crowd, bool cam_ignore, uint8_t priority,
- uint32_t logic_serv,
- XiveTCTXMatch *match)
+static bool pnv10_xive_match_nvt(XiveFabric *xfb, uint8_t format,
+ uint8_t nvt_blk, uint32_t nvt_idx,
+ bool crowd, bool cam_ignore, uint8_t priority,
+ uint32_t logic_serv,
+ XiveTCTXMatch *match)
{
PnvMachineState *pnv = PNV_MACHINE(xfb);
- int total_count = 0;
int i;
for (i = 0; i < pnv->num_chips; i++) {
Pnv10Chip *chip10 = PNV10_CHIP(pnv->chips[i]);
XivePresenter *xptr = XIVE_PRESENTER(&chip10->xive);
XivePresenterClass *xpc = XIVE_PRESENTER_GET_CLASS(xptr);
- int count;
-
- count = xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, crowd,
- cam_ignore, priority, logic_serv, match);
-
- if (count < 0) {
- return count;
- }
- total_count += count;
+ xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, crowd,
+ cam_ignore, priority, logic_serv, match);
}
- return total_count;
+ return !!match->count;
}
static int pnv10_xive_broadcast(XiveFabric *xfb,
diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index 40f53ad..1855a3c 100644
--- a/hw/ppc/spapr.c
+++ b/hw/ppc/spapr.c
@@ -4468,21 +4468,14 @@ static void spapr_pic_print_info(InterruptStatsProvider *obj, GString *buf)
/*
* This is a XIVE only operation
*/
-static int spapr_match_nvt(XiveFabric *xfb, uint8_t format,
- uint8_t nvt_blk, uint32_t nvt_idx,
- bool crowd, bool cam_ignore, uint8_t priority,
- uint32_t logic_serv, XiveTCTXMatch *match)
+static bool spapr_match_nvt(XiveFabric *xfb, uint8_t format,
+ uint8_t nvt_blk, uint32_t nvt_idx,
+ bool crowd, bool cam_ignore, uint8_t priority,
+ uint32_t logic_serv, XiveTCTXMatch *match)
{
SpaprMachineState *spapr = SPAPR_MACHINE(xfb);
XivePresenter *xptr = XIVE_PRESENTER(spapr->active_intc);
XivePresenterClass *xpc = XIVE_PRESENTER_GET_CLASS(xptr);
- int count;
-
- count = xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, crowd, cam_ignore,
- priority, logic_serv, match);
- if (count < 0) {
- return count;
- }
/*
* When we implement the save and restore of the thread interrupt
@@ -4493,12 +4486,14 @@ static int spapr_match_nvt(XiveFabric *xfb, uint8_t format,
* Until this is done, the sPAPR machine should find at least one
* matching context always.
*/
- if (count == 0) {
+ if (!xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, crowd, cam_ignore,
+ priority, logic_serv, match)) {
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: NVT %x/%x is not dispatched\n",
nvt_blk, nvt_idx);
+ return false;
}
- return count;
+ return true;
}
int spapr_get_vcpu_id(PowerPCCPU *cpu)