ppc/xive2: Add support for MMIO operations on the NVPG/NVC BAR

Add support for the NVPG and NVC BARs.  Access to the BAR pages will
cause backlog counter operations to either increment or decriment
the counter.

Also added qtests for the same.

Signed-off-by: Frederic Barrat <fbarrat@linux.ibm.com>
Signed-off-by: Michael Kowal <kowal@linux.ibm.com>
Reviewed-by: Nicholas Piggin <npiggin@gmail.com>
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
This commit is contained in:
Frederic Barrat 2025-03-11 11:53:45 +10:00 committed by Nicholas Piggin
parent c2b7fade9f
commit 96a2132ce9
9 changed files with 328 additions and 15 deletions

View file

@ -2202,21 +2202,40 @@ static const MemoryRegionOps pnv_xive2_tm_ops = {
},
};
static uint64_t pnv_xive2_nvc_read(void *opaque, hwaddr offset,
static uint64_t pnv_xive2_nvc_read(void *opaque, hwaddr addr,
unsigned size)
{
PnvXive2 *xive = PNV_XIVE2(opaque);
XivePresenter *xptr = XIVE_PRESENTER(xive);
uint32_t page = addr >> xive->nvpg_shift;
uint16_t op = addr & 0xFFF;
uint8_t blk = pnv_xive2_block_id(xive);
xive2_error(xive, "NVC: invalid read @%"HWADDR_PRIx, offset);
return -1;
if (size != 2) {
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid nvc load size %d\n",
size);
return -1;
}
return xive2_presenter_nvgc_backlog_op(xptr, true, blk, page, op, 1);
}
static void pnv_xive2_nvc_write(void *opaque, hwaddr offset,
static void pnv_xive2_nvc_write(void *opaque, hwaddr addr,
uint64_t val, unsigned size)
{
PnvXive2 *xive = PNV_XIVE2(opaque);
XivePresenter *xptr = XIVE_PRESENTER(xive);
uint32_t page = addr >> xive->nvc_shift;
uint16_t op = addr & 0xFFF;
uint8_t blk = pnv_xive2_block_id(xive);
xive2_error(xive, "NVC: invalid write @%"HWADDR_PRIx, offset);
if (size != 1) {
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid nvc write size %d\n",
size);
return;
}
(void)xive2_presenter_nvgc_backlog_op(xptr, true, blk, page, op, val);
}
static const MemoryRegionOps pnv_xive2_nvc_ops = {
@ -2224,30 +2243,63 @@ static const MemoryRegionOps pnv_xive2_nvc_ops = {
.write = pnv_xive2_nvc_write,
.endianness = DEVICE_BIG_ENDIAN,
.valid = {
.min_access_size = 8,
.min_access_size = 1,
.max_access_size = 8,
},
.impl = {
.min_access_size = 8,
.min_access_size = 1,
.max_access_size = 8,
},
};
static uint64_t pnv_xive2_nvpg_read(void *opaque, hwaddr offset,
static uint64_t pnv_xive2_nvpg_read(void *opaque, hwaddr addr,
unsigned size)
{
PnvXive2 *xive = PNV_XIVE2(opaque);
XivePresenter *xptr = XIVE_PRESENTER(xive);
uint32_t page = addr >> xive->nvpg_shift;
uint16_t op = addr & 0xFFF;
uint32_t index = page >> 1;
uint8_t blk = pnv_xive2_block_id(xive);
xive2_error(xive, "NVPG: invalid read @%"HWADDR_PRIx, offset);
return -1;
if (size != 2) {
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid nvpg load size %d\n",
size);
return -1;
}
if (page % 2) {
/* odd page - NVG */
return xive2_presenter_nvgc_backlog_op(xptr, false, blk, index, op, 1);
} else {
/* even page - NVP */
return xive2_presenter_nvp_backlog_op(xptr, blk, index, op);
}
}
static void pnv_xive2_nvpg_write(void *opaque, hwaddr offset,
static void pnv_xive2_nvpg_write(void *opaque, hwaddr addr,
uint64_t val, unsigned size)
{
PnvXive2 *xive = PNV_XIVE2(opaque);
XivePresenter *xptr = XIVE_PRESENTER(xive);
uint32_t page = addr >> xive->nvpg_shift;
uint16_t op = addr & 0xFFF;
uint32_t index = page >> 1;
uint8_t blk = pnv_xive2_block_id(xive);
xive2_error(xive, "NVPG: invalid write @%"HWADDR_PRIx, offset);
if (size != 1) {
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid nvpg write size %d\n",
size);
return;
}
if (page % 2) {
/* odd page - NVG */
(void)xive2_presenter_nvgc_backlog_op(xptr, false, blk, index, op, val);
} else {
/* even page - NVP */
(void)xive2_presenter_nvp_backlog_op(xptr, blk, index, op);
}
}
static const MemoryRegionOps pnv_xive2_nvpg_ops = {
@ -2255,11 +2307,11 @@ static const MemoryRegionOps pnv_xive2_nvpg_ops = {
.write = pnv_xive2_nvpg_write,
.endianness = DEVICE_BIG_ENDIAN,
.valid = {
.min_access_size = 8,
.min_access_size = 1,
.max_access_size = 8,
},
.impl = {
.min_access_size = 8,
.min_access_size = 1,
.max_access_size = 8,
},
};

View file

@ -286,6 +286,10 @@ xive_tctx_tm_read(uint32_t index, uint64_t offset, unsigned int size, uint64_t v
xive_presenter_notify(uint8_t nvt_blk, uint32_t nvt_idx, uint8_t ring, uint8_t group_level) "found NVT 0x%x/0x%x ring=0x%x group_level=%d"
xive_end_source_read(uint8_t end_blk, uint32_t end_idx, uint64_t addr) "END 0x%x/0x%x @0x%"PRIx64
# 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"
# pnv_xive.c
pnv_xive_ic_hw_trigger(uint64_t addr, uint64_t val) "@0x%"PRIx64" val=0x%"PRIx64

View file

@ -88,6 +88,93 @@ static void xive2_nvgc_set_backlog(Xive2Nvgc *nvgc, uint8_t priority,
}
}
uint64_t xive2_presenter_nvgc_backlog_op(XivePresenter *xptr,
bool crowd,
uint8_t blk, uint32_t idx,
uint16_t offset, uint16_t val)
{
Xive2Router *xrtr = XIVE2_ROUTER(xptr);
uint8_t priority = GETFIELD(NVx_BACKLOG_PRIO, offset);
uint8_t op = GETFIELD(NVx_BACKLOG_OP, offset);
Xive2Nvgc nvgc;
uint32_t count, old_count;
if (xive2_router_get_nvgc(xrtr, crowd, blk, idx, &nvgc)) {
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: No %s %x/%x\n",
crowd ? "NVC" : "NVG", blk, idx);
return -1;
}
if (!xive2_nvgc_is_valid(&nvgc)) {
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Invalid NVG %x/%x\n", blk, idx);
return -1;
}
old_count = xive2_nvgc_get_backlog(&nvgc, priority);
count = old_count;
/*
* op:
* 0b00 => increment
* 0b01 => decrement
* 0b1- => read
*/
if (op == 0b00 || op == 0b01) {
if (op == 0b00) {
count += val;
} else {
if (count > val) {
count -= val;
} else {
count = 0;
}
}
xive2_nvgc_set_backlog(&nvgc, priority, count);
xive2_router_write_nvgc(xrtr, crowd, blk, idx, &nvgc);
}
trace_xive_nvgc_backlog_op(crowd, blk, idx, op, priority, old_count);
return old_count;
}
uint64_t xive2_presenter_nvp_backlog_op(XivePresenter *xptr,
uint8_t blk, uint32_t idx,
uint16_t offset)
{
Xive2Router *xrtr = XIVE2_ROUTER(xptr);
uint8_t priority = GETFIELD(NVx_BACKLOG_PRIO, offset);
uint8_t op = GETFIELD(NVx_BACKLOG_OP, offset);
Xive2Nvp nvp;
uint8_t ipb, old_ipb, rc;
if (xive2_router_get_nvp(xrtr, blk, idx, &nvp)) {
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: No NVP %x/%x\n", blk, idx);
return -1;
}
if (!xive2_nvp_is_valid(&nvp)) {
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Invalid NVP %x/%x\n", blk, idx);
return -1;
}
old_ipb = xive_get_field32(NVP2_W2_IPB, nvp.w2);
ipb = old_ipb;
/*
* op:
* 0b00 => set priority bit
* 0b01 => reset priority bit
* 0b1- => read
*/
if (op == 0b00 || op == 0b01) {
if (op == 0b00) {
ipb |= xive_priority_to_ipb(priority);
} else {
ipb &= ~xive_priority_to_ipb(priority);
}
nvp.w2 = xive_set_field32(NVP2_W2_IPB, nvp.w2, ipb);
xive2_router_write_nvp(xrtr, blk, idx, &nvp, 2);
}
rc = !!(old_ipb & xive_priority_to_ipb(priority));
trace_xive_nvp_backlog_op(blk, idx, op, priority, rc);
return rc;
}
void xive2_eas_pic_print_info(Xive2Eas *eas, uint32_t lisn, GString *buf)
{
if (!xive2_eas_is_valid(eas)) {