diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index e22049424f..012b238fd0 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -1611,15 +1611,32 @@ static const MemoryRegionOps pnv_xive2_ic_tm_indirect_ops = { * TIMA ops */ +/* + * Special TIMA offsets to handle accesses in a POWER10 way. + * + * Only the CAM line updates done by the hypervisor should be handled + * specifically. + */ +#define HV_PAGE_OFFSET (XIVE_TM_HV_PAGE << TM_SHIFT) +#define HV_PUSH_OS_CTX_OFFSET (HV_PAGE_OFFSET | (TM_QW1_OS + TM_WORD2)) +#define HV_PULL_OS_CTX_OFFSET (HV_PAGE_OFFSET | TM_SPC_PULL_OS_CTX) + static void pnv_xive2_tm_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { PowerPCCPU *cpu = POWERPC_CPU(current_cpu); PnvXive2 *xive = pnv_xive2_tm_get_xive(cpu); XiveTCTX *tctx = XIVE_TCTX(pnv_cpu_state(cpu)->intc); + XivePresenter *xptr = XIVE_PRESENTER(xive); + + /* TODO: should we switch the TM ops table instead ? */ + if (offset == HV_PUSH_OS_CTX_OFFSET) { + xive2_tm_push_os_ctx(xptr, tctx, offset, value, size); + return; + } /* Other TM ops are the same as XIVE1 */ - xive_tctx_tm_write(XIVE_PRESENTER(xive), tctx, offset, value, size); + xive_tctx_tm_write(xptr, tctx, offset, value, size); } static uint64_t pnv_xive2_tm_read(void *opaque, hwaddr offset, unsigned size) @@ -1627,9 +1644,15 @@ static uint64_t pnv_xive2_tm_read(void *opaque, hwaddr offset, unsigned size) PowerPCCPU *cpu = POWERPC_CPU(current_cpu); PnvXive2 *xive = pnv_xive2_tm_get_xive(cpu); XiveTCTX *tctx = XIVE_TCTX(pnv_cpu_state(cpu)->intc); + XivePresenter *xptr = XIVE_PRESENTER(xive); + + /* TODO: should we switch the TM ops table instead ? */ + if (offset == HV_PULL_OS_CTX_OFFSET) { + return xive2_tm_pull_os_ctx(xptr, tctx, offset, size); + } /* Other TM ops are the same as XIVE1 */ - return xive_tctx_tm_read(XIVE_PRESENTER(xive), tctx, offset, size); + return xive_tctx_tm_read(xptr, tctx, offset, size); } static const MemoryRegionOps pnv_xive2_tm_ops = { diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 3720e70422..048b98dbbe 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -159,6 +159,101 @@ static void xive2_end_enqueue(Xive2End *end, uint32_t data) } end->w1 = xive_set_field32(END2_W1_PAGE_OFF, end->w1, qindex); } + +/* + * XIVE Thread Interrupt Management Area (TIMA) - Gen2 mode + */ + +static void xive2_os_cam_decode(uint32_t cam, uint8_t *nvp_blk, + uint32_t *nvp_idx, bool *vo) +{ + *nvp_blk = xive2_nvp_blk(cam); + *nvp_idx = xive2_nvp_idx(cam); + *vo = !!(cam & TM2_QW1W2_VO); +} + +uint64_t xive2_tm_pull_os_ctx(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, unsigned size) +{ + uint32_t qw1w2 = xive_tctx_word2(&tctx->regs[TM_QW1_OS]); + uint32_t qw1w2_new; + uint32_t cam = be32_to_cpu(qw1w2); + uint8_t nvp_blk; + uint32_t nvp_idx; + bool vo; + + xive2_os_cam_decode(cam, &nvp_blk, &nvp_idx, &vo); + + if (!vo) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: pulling invalid NVP %x/%x !?\n", + nvp_blk, nvp_idx); + } + + /* Invalidate CAM line */ + qw1w2_new = xive_set_field32(TM2_QW1W2_VO, qw1w2, 0); + memcpy(&tctx->regs[TM_QW1_OS + TM_WORD2], &qw1w2_new, 4); + + return qw1w2; +} + +static void xive2_tctx_need_resend(Xive2Router *xrtr, XiveTCTX *tctx, + uint8_t nvp_blk, uint32_t nvp_idx) +{ + Xive2Nvp nvp; + uint8_t ipb; + uint8_t cppr = 0; + + /* + * Grab the associated thread interrupt context registers in the + * associated NVP + */ + 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); + return; + } + + if (!xive2_nvp_is_valid(&nvp)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid NVP %x/%x\n", + nvp_blk, nvp_idx); + return; + } + + ipb = xive_get_field32(NVP2_W2_IPB, nvp.w2); + if (ipb) { + nvp.w2 = xive_set_field32(NVP2_W2_IPB, nvp.w2, 0); + xive2_router_write_nvp(xrtr, nvp_blk, nvp_idx, &nvp, 2); + } + + /* An IPB or CPPR change can trigger a resend */ + if (ipb || cppr) { + xive_tctx_ipb_update(tctx, TM_QW1_OS, ipb); + } +} + +/* + * Updating the OS 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) +{ + uint32_t cam = value; + uint32_t qw1w2 = cpu_to_be32(cam); + uint8_t nvp_blk; + uint32_t nvp_idx; + bool vo; + + xive2_os_cam_decode(cam, &nvp_blk, &nvp_idx, &vo); + + /* First update the thead context */ + memcpy(&tctx->regs[TM_QW1_OS + TM_WORD2], &qw1w2, 4); + + /* Check the interrupt pending bits */ + if (vo) { + xive2_tctx_need_resend(XIVE2_ROUTER(xptr), tctx, nvp_blk, nvp_idx); + } +} + /* * XIVE Router (aka. Virtualization Controller or IVRE) */ diff --git a/include/hw/ppc/xive2.h b/include/hw/ppc/xive2.h index eb255b6dd8..c8c4505b51 100644 --- a/include/hw/ppc/xive2.h +++ b/include/hw/ppc/xive2.h @@ -87,5 +87,13 @@ typedef struct Xive2EndSource { Xive2Router *xrtr; } Xive2EndSource; +/* + * XIVE2 Thread Interrupt Management Area (POWER10) + */ + +void xive2_tm_push_os_ctx(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, + uint64_t value, unsigned size); +uint64_t xive2_tm_pull_os_ctx(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, unsigned size); #endif /* PPC_XIVE2_H */