@@ -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 */
@@ -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 = {
@@ -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)
*/