ppc/pnv: add XIVE Gen2 TIMA support
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. Reviewed-by: Daniel Henrique Barboza <danielhb413@gmail.com> Signed-off-by: Cédric Le Goater <clg@kaod.org>
This commit is contained in:
parent
707ea7ab4d
commit
95d729e2bc
@ -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)
|
||||
*/
|
||||
|
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user