Message ID | 20211126115349.2737605-15-clg@kaod.org |
---|---|
State | Superseded |
Headers | show |
Series | ppc/pnv: Extend the powernv10 machine | expand |
On 11/26/21 08:53, Cédric Le Goater wrote: > Only the CAM line updates done by the hypervisor are specific to > POWER10. Instead of duplicating the TM ops table, we handle these > commands locally under the PowerNV XIVE2 model. > > Signed-off-by: Cédric Le Goater <clg@kaod.org> > --- Reviewed-by: Daniel Henrique Barboza <danielhb413@gmail.com> > include/hw/ppc/xive2.h | 8 ++++ > hw/intc/pnv_xive2.c | 27 +++++++++++- > hw/intc/xive2.c | 95 ++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 128 insertions(+), 2 deletions(-) > > diff --git a/include/hw/ppc/xive2.h b/include/hw/ppc/xive2.h > index 9222b5b36979..cf6211a0ecb9 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 */ > diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c > index cb12cea14fc6..4a2649893232 100644 > --- a/hw/intc/pnv_xive2.c > +++ b/hw/intc/pnv_xive2.c > @@ -1610,15 +1610,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) > @@ -1626,9 +1643,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 26af08a5de07..e31037e1f030 100644 > --- a/hw/intc/xive2.c > +++ b/hw/intc/xive2.c > @@ -158,6 +158,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 9222b5b36979..cf6211a0ecb9 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 */ diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index cb12cea14fc6..4a2649893232 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -1610,15 +1610,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) @@ -1626,9 +1643,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 26af08a5de07..e31037e1f030 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -158,6 +158,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) */
Only the CAM line updates done by the hypervisor are specific to POWER10. Instead of duplicating the TM ops table, we handle these commands locally under the PowerNV XIVE2 model. Signed-off-by: Cédric Le Goater <clg@kaod.org> --- include/hw/ppc/xive2.h | 8 ++++ hw/intc/pnv_xive2.c | 27 +++++++++++- hw/intc/xive2.c | 95 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 128 insertions(+), 2 deletions(-)