Browse Source
These XIVE tests include: - General interrupt IRQ tests that: - enable and trigger an interrupt - acknowledge the interrupt - end of interrupt processing - Test the Pull Thread Context to Odd Thread Reporting Line - Test the different cache flush inject and queue sync inject operations Co-authored-by: Frederic Barrat <fbarrat@linux.ibm.com> Co-authored-by: Glenn Miles <milesg@linux.ibm.com> Co-authored-by: Michael Kowal <kowal@linux.ibm.com> Signed-off-by: Frederic Barrat <fbarrat@linux.ibm.com> Signed-off-by: Glenn Miles <milesg@linux.ibm.com> Signed-off-by: Michael Kowal <kowal@linux.ibm.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>pull/275/head
committed by
Nicholas Piggin
6 changed files with 853 additions and 0 deletions
@ -0,0 +1,190 @@ |
|||
/*
|
|||
* QTest testcase for PowerNV 10 interrupt controller (xive2) |
|||
* - Common functions for XIVE2 tests |
|||
* |
|||
* Copyright (c) 2024, IBM Corporation. |
|||
* |
|||
* SPDX-License-Identifier: GPL-2.0-or-later |
|||
*/ |
|||
#include "qemu/osdep.h" |
|||
#include "libqtest.h" |
|||
|
|||
#include "pnv-xive2-common.h" |
|||
|
|||
|
|||
static uint64_t pnv_xscom_addr(uint32_t pcba) |
|||
{ |
|||
return P10_XSCOM_BASE | ((uint64_t) pcba << 3); |
|||
} |
|||
|
|||
static uint64_t pnv_xive_xscom_addr(uint32_t reg) |
|||
{ |
|||
return pnv_xscom_addr(XIVE_XSCOM + reg); |
|||
} |
|||
|
|||
uint64_t pnv_xive_xscom_read(QTestState *qts, uint32_t reg) |
|||
{ |
|||
return qtest_readq(qts, pnv_xive_xscom_addr(reg)); |
|||
} |
|||
|
|||
void pnv_xive_xscom_write(QTestState *qts, uint32_t reg, uint64_t val) |
|||
{ |
|||
qtest_writeq(qts, pnv_xive_xscom_addr(reg), val); |
|||
} |
|||
|
|||
static void xive_get_struct(QTestState *qts, uint64_t src, void *dest, |
|||
size_t size) |
|||
{ |
|||
uint8_t *destination = (uint8_t *)dest; |
|||
size_t i; |
|||
|
|||
for (i = 0; i < size; i++) { |
|||
*(destination + i) = qtest_readb(qts, src + i); |
|||
} |
|||
} |
|||
|
|||
static void xive_copy_struct(QTestState *qts, void *src, uint64_t dest, |
|||
size_t size) |
|||
{ |
|||
uint8_t *source = (uint8_t *)src; |
|||
size_t i; |
|||
|
|||
for (i = 0; i < size; i++) { |
|||
qtest_writeb(qts, dest + i, *(source + i)); |
|||
} |
|||
} |
|||
|
|||
uint64_t xive_get_queue_addr(uint32_t end_index) |
|||
{ |
|||
return XIVE_QUEUE_MEM + (uint64_t)end_index * XIVE_QUEUE_SIZE; |
|||
} |
|||
|
|||
uint8_t get_esb(QTestState *qts, uint32_t index, uint8_t page, |
|||
uint32_t offset) |
|||
{ |
|||
uint64_t addr; |
|||
|
|||
addr = XIVE_ESB_ADDR + ((uint64_t)index << (XIVE_PAGE_SHIFT + 1)); |
|||
if (page == 1) { |
|||
addr += 1 << XIVE_PAGE_SHIFT; |
|||
} |
|||
return qtest_readb(qts, addr + offset); |
|||
} |
|||
|
|||
void set_esb(QTestState *qts, uint32_t index, uint8_t page, |
|||
uint32_t offset, uint32_t val) |
|||
{ |
|||
uint64_t addr; |
|||
|
|||
addr = XIVE_ESB_ADDR + ((uint64_t)index << (XIVE_PAGE_SHIFT + 1)); |
|||
if (page == 1) { |
|||
addr += 1 << XIVE_PAGE_SHIFT; |
|||
} |
|||
return qtest_writel(qts, addr + offset, cpu_to_be32(val)); |
|||
} |
|||
|
|||
void get_nvp(QTestState *qts, uint32_t index, Xive2Nvp* nvp) |
|||
{ |
|||
uint64_t addr = XIVE_NVP_MEM + (uint64_t)index * sizeof(Xive2Nvp); |
|||
xive_get_struct(qts, addr, nvp, sizeof(Xive2Nvp)); |
|||
} |
|||
|
|||
void set_nvp(QTestState *qts, uint32_t index, uint8_t first) |
|||
{ |
|||
uint64_t nvp_addr; |
|||
Xive2Nvp nvp; |
|||
uint64_t report_addr; |
|||
|
|||
nvp_addr = XIVE_NVP_MEM + (uint64_t)index * sizeof(Xive2Nvp); |
|||
report_addr = (XIVE_REPORT_MEM + (uint64_t)index * XIVE_REPORT_SIZE) >> 8; |
|||
|
|||
memset(&nvp, 0, sizeof(nvp)); |
|||
nvp.w0 = xive_set_field32(NVP2_W0_VALID, 0, 1); |
|||
nvp.w0 = xive_set_field32(NVP2_W0_PGOFIRST, nvp.w0, first); |
|||
nvp.w6 = xive_set_field32(NVP2_W6_REPORTING_LINE, nvp.w6, |
|||
(report_addr >> 24) & 0xfffffff); |
|||
nvp.w7 = xive_set_field32(NVP2_W7_REPORTING_LINE, nvp.w7, |
|||
report_addr & 0xffffff); |
|||
xive_copy_struct(qts, &nvp, nvp_addr, sizeof(nvp)); |
|||
} |
|||
|
|||
static uint64_t get_cl_pair_addr(Xive2Nvp *nvp) |
|||
{ |
|||
uint64_t upper = xive_get_field32(0x0fffffff, nvp->w6); |
|||
uint64_t lower = xive_get_field32(0xffffff00, nvp->w7); |
|||
return (upper << 32) | (lower << 8); |
|||
} |
|||
|
|||
void get_cl_pair(QTestState *qts, Xive2Nvp *nvp, uint8_t *cl_pair) |
|||
{ |
|||
uint64_t addr = get_cl_pair_addr(nvp); |
|||
xive_get_struct(qts, addr, cl_pair, XIVE_REPORT_SIZE); |
|||
} |
|||
|
|||
void set_cl_pair(QTestState *qts, Xive2Nvp *nvp, uint8_t *cl_pair) |
|||
{ |
|||
uint64_t addr = get_cl_pair_addr(nvp); |
|||
xive_copy_struct(qts, cl_pair, addr, XIVE_REPORT_SIZE); |
|||
} |
|||
|
|||
void set_nvg(QTestState *qts, uint32_t index, uint8_t next) |
|||
{ |
|||
uint64_t nvg_addr; |
|||
Xive2Nvgc nvg; |
|||
|
|||
nvg_addr = XIVE_NVG_MEM + (uint64_t)index * sizeof(Xive2Nvgc); |
|||
|
|||
memset(&nvg, 0, sizeof(nvg)); |
|||
nvg.w0 = xive_set_field32(NVGC2_W0_VALID, 0, 1); |
|||
nvg.w0 = xive_set_field32(NVGC2_W0_PGONEXT, nvg.w0, next); |
|||
xive_copy_struct(qts, &nvg, nvg_addr, sizeof(nvg)); |
|||
} |
|||
|
|||
void set_eas(QTestState *qts, uint32_t index, uint32_t end_index, |
|||
uint32_t data) |
|||
{ |
|||
uint64_t eas_addr; |
|||
Xive2Eas eas; |
|||
|
|||
eas_addr = XIVE_EAS_MEM + (uint64_t)index * sizeof(Xive2Eas); |
|||
|
|||
memset(&eas, 0, sizeof(eas)); |
|||
eas.w = xive_set_field64(EAS2_VALID, 0, 1); |
|||
eas.w = xive_set_field64(EAS2_END_INDEX, eas.w, end_index); |
|||
eas.w = xive_set_field64(EAS2_END_DATA, eas.w, data); |
|||
xive_copy_struct(qts, &eas, eas_addr, sizeof(eas)); |
|||
} |
|||
|
|||
void set_end(QTestState *qts, uint32_t index, uint32_t nvp_index, |
|||
uint8_t priority, bool i) |
|||
{ |
|||
uint64_t end_addr, queue_addr, queue_hi, queue_lo; |
|||
uint8_t queue_size; |
|||
Xive2End end; |
|||
|
|||
end_addr = XIVE_END_MEM + (uint64_t)index * sizeof(Xive2End); |
|||
queue_addr = xive_get_queue_addr(index); |
|||
queue_hi = (queue_addr >> 32) & END2_W2_EQ_ADDR_HI; |
|||
queue_lo = queue_addr & END2_W3_EQ_ADDR_LO; |
|||
queue_size = ctz16(XIVE_QUEUE_SIZE) - 12; |
|||
|
|||
memset(&end, 0, sizeof(end)); |
|||
end.w0 = xive_set_field32(END2_W0_VALID, 0, 1); |
|||
end.w0 = xive_set_field32(END2_W0_ENQUEUE, end.w0, 1); |
|||
end.w0 = xive_set_field32(END2_W0_UCOND_NOTIFY, end.w0, 1); |
|||
end.w0 = xive_set_field32(END2_W0_BACKLOG, end.w0, 1); |
|||
|
|||
end.w1 = xive_set_field32(END2_W1_GENERATION, 0, 1); |
|||
|
|||
end.w2 = cpu_to_be32(queue_hi); |
|||
|
|||
end.w3 = cpu_to_be32(queue_lo); |
|||
end.w3 = xive_set_field32(END2_W3_QSIZE, end.w3, queue_size); |
|||
|
|||
end.w6 = xive_set_field32(END2_W6_IGNORE, 0, i); |
|||
end.w6 = xive_set_field32(END2_W6_VP_OFFSET, end.w6, nvp_index); |
|||
|
|||
end.w7 = xive_set_field32(END2_W7_F0_PRIORITY, 0, priority); |
|||
xive_copy_struct(qts, &end, end_addr, sizeof(end)); |
|||
} |
|||
|
|||
@ -0,0 +1,111 @@ |
|||
/*
|
|||
* QTest testcase for PowerNV 10 interrupt controller (xive2) |
|||
* |
|||
* Copyright (c) 2024, IBM Corporation. |
|||
* |
|||
* SPDX-License-Identifier: GPL-2.0-or-later |
|||
*/ |
|||
|
|||
#ifndef TEST_PNV_XIVE2_COMMON_H |
|||
#define TEST_PNV_XIVE2_COMMON_H |
|||
|
|||
#define PPC_BIT(bit) (0x8000000000000000ULL >> (bit)) |
|||
#define PPC_BIT32(bit) (0x80000000 >> (bit)) |
|||
#define PPC_BIT8(bit) (0x80 >> (bit)) |
|||
#define PPC_BITMASK(bs, be) ((PPC_BIT(bs) - PPC_BIT(be)) | PPC_BIT(bs)) |
|||
#define PPC_BITMASK32(bs, be) ((PPC_BIT32(bs) - PPC_BIT32(be)) | \ |
|||
PPC_BIT32(bs)) |
|||
#include "qemu/bswap.h" |
|||
#include "hw/intc/pnv_xive2_regs.h" |
|||
#include "hw/ppc/xive_regs.h" |
|||
#include "hw/ppc/xive2_regs.h" |
|||
|
|||
/*
|
|||
* sizing: |
|||
* 128 interrupts |
|||
* => ESB BAR range: 16M |
|||
* 256 ENDs |
|||
* => END BAR range: 16M |
|||
* 256 VPs |
|||
* => NVPG,NVC BAR range: 32M |
|||
*/ |
|||
#define MAX_IRQS 128 |
|||
#define MAX_ENDS 256 |
|||
#define MAX_VPS 256 |
|||
|
|||
#define XIVE_PAGE_SHIFT 16 |
|||
|
|||
#define XIVE_TRIGGER_PAGE 0 |
|||
#define XIVE_EOI_PAGE 1 |
|||
|
|||
#define XIVE_IC_ADDR 0x0006030200000000ull |
|||
#define XIVE_IC_TM_INDIRECT (XIVE_IC_ADDR + (256 << XIVE_PAGE_SHIFT)) |
|||
#define XIVE_IC_BAR ((0x3ull << 62) | XIVE_IC_ADDR) |
|||
#define XIVE_TM_BAR 0xc006030203180000ull |
|||
#define XIVE_ESB_ADDR 0x0006050000000000ull |
|||
#define XIVE_ESB_BAR ((0x3ull << 62) | XIVE_ESB_ADDR) |
|||
#define XIVE_END_BAR 0xc006060000000000ull |
|||
#define XIVE_NVPG_ADDR 0x0006040000000000ull |
|||
#define XIVE_NVPG_BAR ((0x3ull << 62) | XIVE_NVPG_ADDR) |
|||
#define XIVE_NVC_ADDR 0x0006030208000000ull |
|||
#define XIVE_NVC_BAR ((0x3ull << 62) | XIVE_NVC_ADDR) |
|||
|
|||
/*
|
|||
* Memory layout |
|||
* A check is done when a table is configured to ensure that the max |
|||
* size of the resource fits in the table. |
|||
*/ |
|||
#define XIVE_VST_SIZE 0x10000ull /* must be at least 4k */ |
|||
|
|||
#define XIVE_MEM_START 0x10000000ull |
|||
#define XIVE_ESB_MEM XIVE_MEM_START |
|||
#define XIVE_EAS_MEM (XIVE_ESB_MEM + XIVE_VST_SIZE) |
|||
#define XIVE_END_MEM (XIVE_EAS_MEM + XIVE_VST_SIZE) |
|||
#define XIVE_NVP_MEM (XIVE_END_MEM + XIVE_VST_SIZE) |
|||
#define XIVE_NVG_MEM (XIVE_NVP_MEM + XIVE_VST_SIZE) |
|||
#define XIVE_NVC_MEM (XIVE_NVG_MEM + XIVE_VST_SIZE) |
|||
#define XIVE_SYNC_MEM (XIVE_NVC_MEM + XIVE_VST_SIZE) |
|||
#define XIVE_QUEUE_MEM (XIVE_SYNC_MEM + XIVE_VST_SIZE) |
|||
#define XIVE_QUEUE_SIZE 4096 /* per End */ |
|||
#define XIVE_REPORT_MEM (XIVE_QUEUE_MEM + XIVE_QUEUE_SIZE * MAX_VPS) |
|||
#define XIVE_REPORT_SIZE 256 /* two cache lines per NVP */ |
|||
#define XIVE_MEM_END (XIVE_REPORT_MEM + XIVE_REPORT_SIZE * MAX_VPS) |
|||
|
|||
#define P10_XSCOM_BASE 0x000603fc00000000ull |
|||
#define XIVE_XSCOM 0x2010800ull |
|||
|
|||
#define XIVE_ESB_RESET 0b00 |
|||
#define XIVE_ESB_OFF 0b01 |
|||
#define XIVE_ESB_PENDING 0b10 |
|||
#define XIVE_ESB_QUEUED 0b11 |
|||
|
|||
#define XIVE_ESB_GET 0x800 |
|||
#define XIVE_ESB_SET_PQ_00 0xc00 /* Load */ |
|||
#define XIVE_ESB_SET_PQ_01 0xd00 /* Load */ |
|||
#define XIVE_ESB_SET_PQ_10 0xe00 /* Load */ |
|||
#define XIVE_ESB_SET_PQ_11 0xf00 /* Load */ |
|||
|
|||
#define XIVE_ESB_STORE_EOI 0x400 /* Store */ |
|||
|
|||
|
|||
extern uint64_t pnv_xive_xscom_read(QTestState *qts, uint32_t reg); |
|||
extern void pnv_xive_xscom_write(QTestState *qts, uint32_t reg, uint64_t val); |
|||
extern uint64_t xive_get_queue_addr(uint32_t end_index); |
|||
extern uint8_t get_esb(QTestState *qts, uint32_t index, uint8_t page, |
|||
uint32_t offset); |
|||
extern void set_esb(QTestState *qts, uint32_t index, uint8_t page, |
|||
uint32_t offset, uint32_t val); |
|||
extern void get_nvp(QTestState *qts, uint32_t index, Xive2Nvp* nvp); |
|||
extern void set_nvp(QTestState *qts, uint32_t index, uint8_t first); |
|||
extern void get_cl_pair(QTestState *qts, Xive2Nvp *nvp, uint8_t *cl_pair); |
|||
extern void set_cl_pair(QTestState *qts, Xive2Nvp *nvp, uint8_t *cl_pair); |
|||
extern void set_nvg(QTestState *qts, uint32_t index, uint8_t next); |
|||
extern void set_eas(QTestState *qts, uint32_t index, uint32_t end_index, |
|||
uint32_t data); |
|||
extern void set_end(QTestState *qts, uint32_t index, uint32_t nvp_index, |
|||
uint8_t priority, bool i); |
|||
|
|||
|
|||
void test_flush_sync_inject(QTestState *qts); |
|||
|
|||
#endif /* TEST_PNV_XIVE2_COMMON_H */ |
|||
@ -0,0 +1,205 @@ |
|||
/*
|
|||
* QTest testcase for PowerNV 10 interrupt controller (xive2) |
|||
* - Test cache flush/queue sync injection |
|||
* |
|||
* Copyright (c) 2024, IBM Corporation. |
|||
* |
|||
* SPDX-License-Identifier: GPL-2.0-or-later |
|||
*/ |
|||
#include "qemu/osdep.h" |
|||
#include "libqtest.h" |
|||
|
|||
#include "pnv-xive2-common.h" |
|||
#include "hw/intc/pnv_xive2_regs.h" |
|||
#include "hw/ppc/xive_regs.h" |
|||
#include "hw/ppc/xive2_regs.h" |
|||
|
|||
#define PNV_XIVE2_QUEUE_IPI 0x00 |
|||
#define PNV_XIVE2_QUEUE_HW 0x01 |
|||
#define PNV_XIVE2_QUEUE_NXC 0x02 |
|||
#define PNV_XIVE2_QUEUE_INT 0x03 |
|||
#define PNV_XIVE2_QUEUE_OS 0x04 |
|||
#define PNV_XIVE2_QUEUE_POOL 0x05 |
|||
#define PNV_XIVE2_QUEUE_HARD 0x06 |
|||
#define PNV_XIVE2_CACHE_ENDC 0x08 |
|||
#define PNV_XIVE2_CACHE_ESBC 0x09 |
|||
#define PNV_XIVE2_CACHE_EASC 0x0a |
|||
#define PNV_XIVE2_QUEUE_NXC_LD_LCL_NCO 0x10 |
|||
#define PNV_XIVE2_QUEUE_NXC_LD_LCL_CO 0x11 |
|||
#define PNV_XIVE2_QUEUE_NXC_ST_LCL_NCI 0x12 |
|||
#define PNV_XIVE2_QUEUE_NXC_ST_LCL_CI 0x13 |
|||
#define PNV_XIVE2_QUEUE_NXC_ST_RMT_NCI 0x14 |
|||
#define PNV_XIVE2_QUEUE_NXC_ST_RMT_CI 0x15 |
|||
#define PNV_XIVE2_CACHE_NXC 0x18 |
|||
|
|||
#define PNV_XIVE2_SYNC_IPI 0x000 |
|||
#define PNV_XIVE2_SYNC_HW 0x080 |
|||
#define PNV_XIVE2_SYNC_NxC 0x100 |
|||
#define PNV_XIVE2_SYNC_INT 0x180 |
|||
#define PNV_XIVE2_SYNC_OS_ESC 0x200 |
|||
#define PNV_XIVE2_SYNC_POOL_ESC 0x280 |
|||
#define PNV_XIVE2_SYNC_HARD_ESC 0x300 |
|||
#define PNV_XIVE2_SYNC_NXC_LD_LCL_NCO 0x800 |
|||
#define PNV_XIVE2_SYNC_NXC_LD_LCL_CO 0x880 |
|||
#define PNV_XIVE2_SYNC_NXC_ST_LCL_NCI 0x900 |
|||
#define PNV_XIVE2_SYNC_NXC_ST_LCL_CI 0x980 |
|||
#define PNV_XIVE2_SYNC_NXC_ST_RMT_NCI 0xA00 |
|||
#define PNV_XIVE2_SYNC_NXC_ST_RMT_CI 0xA80 |
|||
|
|||
|
|||
static uint64_t get_sync_addr(uint32_t src_pir, int ic_topo_id, int type) |
|||
{ |
|||
int thread_nr = src_pir & 0x7f; |
|||
uint64_t addr = XIVE_SYNC_MEM + thread_nr * 512 + ic_topo_id * 32 + type; |
|||
return addr; |
|||
} |
|||
|
|||
static uint8_t get_sync(QTestState *qts, uint32_t src_pir, int ic_topo_id, |
|||
int type) |
|||
{ |
|||
uint64_t addr = get_sync_addr(src_pir, ic_topo_id, type); |
|||
return qtest_readb(qts, addr); |
|||
} |
|||
|
|||
static void clr_sync(QTestState *qts, uint32_t src_pir, int ic_topo_id, |
|||
int type) |
|||
{ |
|||
uint64_t addr = get_sync_addr(src_pir, ic_topo_id, type); |
|||
qtest_writeb(qts, addr, 0x0); |
|||
} |
|||
|
|||
static void inject_cache_flush(QTestState *qts, int ic_topo_id, |
|||
uint64_t scom_addr) |
|||
{ |
|||
(void)ic_topo_id; |
|||
pnv_xive_xscom_write(qts, scom_addr, 0); |
|||
} |
|||
|
|||
static void inject_queue_sync(QTestState *qts, int ic_topo_id, uint64_t offset) |
|||
{ |
|||
(void)ic_topo_id; |
|||
uint64_t addr = XIVE_IC_ADDR + (VST_SYNC << XIVE_PAGE_SHIFT) + offset; |
|||
qtest_writeq(qts, addr, 0); |
|||
} |
|||
|
|||
static void inject_op(QTestState *qts, int ic_topo_id, int type) |
|||
{ |
|||
switch (type) { |
|||
case PNV_XIVE2_QUEUE_IPI: |
|||
inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_IPI); |
|||
break; |
|||
case PNV_XIVE2_QUEUE_HW: |
|||
inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_HW); |
|||
break; |
|||
case PNV_XIVE2_QUEUE_NXC: |
|||
inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_NxC); |
|||
break; |
|||
case PNV_XIVE2_QUEUE_INT: |
|||
inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_INT); |
|||
break; |
|||
case PNV_XIVE2_QUEUE_OS: |
|||
inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_OS_ESC); |
|||
break; |
|||
case PNV_XIVE2_QUEUE_POOL: |
|||
inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_POOL_ESC); |
|||
break; |
|||
case PNV_XIVE2_QUEUE_HARD: |
|||
inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_HARD_ESC); |
|||
break; |
|||
case PNV_XIVE2_CACHE_ENDC: |
|||
inject_cache_flush(qts, ic_topo_id, X_VC_ENDC_FLUSH_INJECT); |
|||
break; |
|||
case PNV_XIVE2_CACHE_ESBC: |
|||
inject_cache_flush(qts, ic_topo_id, X_VC_ESBC_FLUSH_INJECT); |
|||
break; |
|||
case PNV_XIVE2_CACHE_EASC: |
|||
inject_cache_flush(qts, ic_topo_id, X_VC_EASC_FLUSH_INJECT); |
|||
break; |
|||
case PNV_XIVE2_QUEUE_NXC_LD_LCL_NCO: |
|||
inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_NXC_LD_LCL_NCO); |
|||
break; |
|||
case PNV_XIVE2_QUEUE_NXC_LD_LCL_CO: |
|||
inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_NXC_LD_LCL_CO); |
|||
break; |
|||
case PNV_XIVE2_QUEUE_NXC_ST_LCL_NCI: |
|||
inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_NXC_ST_LCL_NCI); |
|||
break; |
|||
case PNV_XIVE2_QUEUE_NXC_ST_LCL_CI: |
|||
inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_NXC_ST_LCL_CI); |
|||
break; |
|||
case PNV_XIVE2_QUEUE_NXC_ST_RMT_NCI: |
|||
inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_NXC_ST_RMT_NCI); |
|||
break; |
|||
case PNV_XIVE2_QUEUE_NXC_ST_RMT_CI: |
|||
inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_NXC_ST_RMT_CI); |
|||
break; |
|||
case PNV_XIVE2_CACHE_NXC: |
|||
inject_cache_flush(qts, ic_topo_id, X_PC_NXC_FLUSH_INJECT); |
|||
break; |
|||
default: |
|||
g_assert_not_reached(); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
const uint8_t xive_inject_tests[] = { |
|||
PNV_XIVE2_QUEUE_IPI, |
|||
PNV_XIVE2_QUEUE_HW, |
|||
PNV_XIVE2_QUEUE_NXC, |
|||
PNV_XIVE2_QUEUE_INT, |
|||
PNV_XIVE2_QUEUE_OS, |
|||
PNV_XIVE2_QUEUE_POOL, |
|||
PNV_XIVE2_QUEUE_HARD, |
|||
PNV_XIVE2_CACHE_ENDC, |
|||
PNV_XIVE2_CACHE_ESBC, |
|||
PNV_XIVE2_CACHE_EASC, |
|||
PNV_XIVE2_QUEUE_NXC_LD_LCL_NCO, |
|||
PNV_XIVE2_QUEUE_NXC_LD_LCL_CO, |
|||
PNV_XIVE2_QUEUE_NXC_ST_LCL_NCI, |
|||
PNV_XIVE2_QUEUE_NXC_ST_LCL_CI, |
|||
PNV_XIVE2_QUEUE_NXC_ST_RMT_NCI, |
|||
PNV_XIVE2_QUEUE_NXC_ST_RMT_CI, |
|||
PNV_XIVE2_CACHE_NXC, |
|||
}; |
|||
|
|||
void test_flush_sync_inject(QTestState *qts) |
|||
{ |
|||
int ic_topo_id = 0; |
|||
|
|||
/*
|
|||
* Writes performed by qtest are not done in the context of a thread. |
|||
* This means that QEMU XIVE code doesn't have a way to determine what |
|||
* thread is originating the write. In order to allow for some testing, |
|||
* QEMU XIVE code will assume a PIR of 0 when unable to determine the |
|||
* source thread for cache flush and queue sync inject operations. |
|||
* See hw/intc/pnv_xive2.c: pnv_xive2_inject_notify() for details. |
|||
*/ |
|||
int src_pir = 0; |
|||
int test_nr; |
|||
uint8_t byte; |
|||
|
|||
printf("# ============================================================\n"); |
|||
printf("# Starting cache flush/queue sync injection tests...\n"); |
|||
|
|||
for (test_nr = 0; test_nr < sizeof(xive_inject_tests); |
|||
test_nr++) { |
|||
int op_type = xive_inject_tests[test_nr]; |
|||
|
|||
printf("# Running test %d\n", test_nr); |
|||
|
|||
/* start with status byte set to 0 */ |
|||
clr_sync(qts, src_pir, ic_topo_id, op_type); |
|||
byte = get_sync(qts, src_pir, ic_topo_id, op_type); |
|||
g_assert_cmphex(byte, ==, 0); |
|||
|
|||
/* request cache flush or queue sync operation */ |
|||
inject_op(qts, ic_topo_id, op_type); |
|||
|
|||
/* verify that status byte was written to 0xff */ |
|||
byte = get_sync(qts, src_pir, ic_topo_id, op_type); |
|||
g_assert_cmphex(byte, ==, 0xff); |
|||
|
|||
clr_sync(qts, src_pir, ic_topo_id, op_type); |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,344 @@ |
|||
/*
|
|||
* QTest testcase for PowerNV 10 interrupt controller (xive2) |
|||
* - Test irq to hardware thread |
|||
* - Test 'Pull Thread Context to Odd Thread Reporting Line' |
|||
* |
|||
* Copyright (c) 2024, IBM Corporation. |
|||
* |
|||
* SPDX-License-Identifier: GPL-2.0-or-later |
|||
*/ |
|||
#include "qemu/osdep.h" |
|||
#include "libqtest.h" |
|||
|
|||
#include "pnv-xive2-common.h" |
|||
#include "hw/intc/pnv_xive2_regs.h" |
|||
#include "hw/ppc/xive_regs.h" |
|||
#include "hw/ppc/xive2_regs.h" |
|||
|
|||
#define SMT 4 /* some tests will break if less than 4 */ |
|||
|
|||
|
|||
static void set_table(QTestState *qts, uint64_t type, uint64_t addr) |
|||
{ |
|||
uint64_t vsd, size, log_size; |
|||
|
|||
/*
|
|||
* First, let's make sure that all the resources used fit in the |
|||
* given table. |
|||
*/ |
|||
switch (type) { |
|||
case VST_ESB: |
|||
size = MAX_IRQS / 4; |
|||
break; |
|||
case VST_EAS: |
|||
size = MAX_IRQS * 8; |
|||
break; |
|||
case VST_END: |
|||
size = MAX_ENDS * 32; |
|||
break; |
|||
case VST_NVP: |
|||
case VST_NVG: |
|||
case VST_NVC: |
|||
size = MAX_VPS * 32; |
|||
break; |
|||
case VST_SYNC: |
|||
size = 64 * 1024; |
|||
break; |
|||
default: |
|||
g_assert_not_reached(); |
|||
} |
|||
|
|||
g_assert_cmpuint(size, <=, XIVE_VST_SIZE); |
|||
log_size = ctzl(XIVE_VST_SIZE) - 12; |
|||
|
|||
vsd = ((uint64_t) VSD_MODE_EXCLUSIVE) << 62 | addr | log_size; |
|||
pnv_xive_xscom_write(qts, X_VC_VSD_TABLE_ADDR, type << 48); |
|||
pnv_xive_xscom_write(qts, X_VC_VSD_TABLE_DATA, vsd); |
|||
|
|||
if (type != VST_EAS && type != VST_IC && type != VST_ERQ) { |
|||
pnv_xive_xscom_write(qts, X_PC_VSD_TABLE_ADDR, type << 48); |
|||
pnv_xive_xscom_write(qts, X_PC_VSD_TABLE_DATA, vsd); |
|||
} |
|||
} |
|||
|
|||
static void set_tima8(QTestState *qts, uint32_t pir, uint32_t offset, |
|||
uint8_t b) |
|||
{ |
|||
uint64_t ic_addr; |
|||
|
|||
ic_addr = XIVE_IC_TM_INDIRECT + (pir << XIVE_PAGE_SHIFT); |
|||
qtest_writeb(qts, ic_addr + offset, b); |
|||
} |
|||
|
|||
static void set_tima32(QTestState *qts, uint32_t pir, uint32_t offset, |
|||
uint32_t l) |
|||
{ |
|||
uint64_t ic_addr; |
|||
|
|||
ic_addr = XIVE_IC_TM_INDIRECT + (pir << XIVE_PAGE_SHIFT); |
|||
qtest_writel(qts, ic_addr + offset, l); |
|||
} |
|||
|
|||
static uint8_t get_tima8(QTestState *qts, uint32_t pir, uint32_t offset) |
|||
{ |
|||
uint64_t ic_addr; |
|||
|
|||
ic_addr = XIVE_IC_TM_INDIRECT + (pir << XIVE_PAGE_SHIFT); |
|||
return qtest_readb(qts, ic_addr + offset); |
|||
} |
|||
|
|||
static uint16_t get_tima16(QTestState *qts, uint32_t pir, uint32_t offset) |
|||
{ |
|||
uint64_t ic_addr; |
|||
|
|||
ic_addr = XIVE_IC_TM_INDIRECT + (pir << XIVE_PAGE_SHIFT); |
|||
return qtest_readw(qts, ic_addr + offset); |
|||
} |
|||
|
|||
static uint32_t get_tima32(QTestState *qts, uint32_t pir, uint32_t offset) |
|||
{ |
|||
uint64_t ic_addr; |
|||
|
|||
ic_addr = XIVE_IC_TM_INDIRECT + (pir << XIVE_PAGE_SHIFT); |
|||
return qtest_readl(qts, ic_addr + offset); |
|||
} |
|||
|
|||
static void reset_pool_threads(QTestState *qts) |
|||
{ |
|||
uint8_t first_group = 0; |
|||
int i; |
|||
|
|||
for (i = 0; i < SMT; i++) { |
|||
uint32_t nvp_idx = 0x100 + i; |
|||
set_nvp(qts, nvp_idx, first_group); |
|||
set_tima32(qts, i, TM_QW2_HV_POOL + TM_WORD0, 0x000000ff); |
|||
set_tima32(qts, i, TM_QW2_HV_POOL + TM_WORD1, 0); |
|||
set_tima32(qts, i, TM_QW2_HV_POOL + TM_WORD2, TM_QW2W2_VP | nvp_idx); |
|||
} |
|||
} |
|||
|
|||
static void reset_hw_threads(QTestState *qts) |
|||
{ |
|||
uint8_t first_group = 0; |
|||
uint32_t w1 = 0x000000ff; |
|||
int i; |
|||
|
|||
if (SMT >= 4) { |
|||
/* define 2 groups of 2, part of a bigger group of size 4 */ |
|||
set_nvg(qts, 0x80, 0x02); |
|||
set_nvg(qts, 0x82, 0x02); |
|||
set_nvg(qts, 0x81, 0); |
|||
first_group = 0x01; |
|||
w1 = 0x000300ff; |
|||
} |
|||
|
|||
for (i = 0; i < SMT; i++) { |
|||
set_nvp(qts, 0x80 + i, first_group); |
|||
set_tima32(qts, i, TM_QW3_HV_PHYS + TM_WORD0, 0x00ff00ff); |
|||
set_tima32(qts, i, TM_QW3_HV_PHYS + TM_WORD1, w1); |
|||
set_tima32(qts, i, TM_QW3_HV_PHYS + TM_WORD2, 0x80000000); |
|||
} |
|||
} |
|||
|
|||
static void reset_state(QTestState *qts) |
|||
{ |
|||
size_t mem_used = XIVE_MEM_END - XIVE_MEM_START; |
|||
|
|||
qtest_memset(qts, XIVE_MEM_START, 0, mem_used); |
|||
reset_hw_threads(qts); |
|||
reset_pool_threads(qts); |
|||
} |
|||
|
|||
static void init_xive(QTestState *qts) |
|||
{ |
|||
uint64_t val1, val2, range; |
|||
|
|||
/*
|
|||
* We can take a few shortcuts here, as we know the default values |
|||
* used for xive initialization |
|||
*/ |
|||
|
|||
/*
|
|||
* Set the BARs. |
|||
* We reuse the same values used by firmware to ease debug. |
|||
*/ |
|||
pnv_xive_xscom_write(qts, X_CQ_IC_BAR, XIVE_IC_BAR); |
|||
pnv_xive_xscom_write(qts, X_CQ_TM_BAR, XIVE_TM_BAR); |
|||
|
|||
/* ESB and NVPG use 2 pages per resource. The others only one page */ |
|||
range = (MAX_IRQS << 17) >> 25; |
|||
val1 = XIVE_ESB_BAR | range; |
|||
pnv_xive_xscom_write(qts, X_CQ_ESB_BAR, val1); |
|||
|
|||
range = (MAX_ENDS << 16) >> 25; |
|||
val1 = XIVE_END_BAR | range; |
|||
pnv_xive_xscom_write(qts, X_CQ_END_BAR, val1); |
|||
|
|||
range = (MAX_VPS << 17) >> 25; |
|||
val1 = XIVE_NVPG_BAR | range; |
|||
pnv_xive_xscom_write(qts, X_CQ_NVPG_BAR, val1); |
|||
|
|||
range = (MAX_VPS << 16) >> 25; |
|||
val1 = XIVE_NVC_BAR | range; |
|||
pnv_xive_xscom_write(qts, X_CQ_NVC_BAR, val1); |
|||
|
|||
/*
|
|||
* Enable hw threads. |
|||
* We check the value written. Useless with current |
|||
* implementation, but it validates the xscom read path and it's |
|||
* what the hardware procedure says |
|||
*/ |
|||
val1 = 0xF000000000000000ull; /* core 0, 4 threads */ |
|||
pnv_xive_xscom_write(qts, X_TCTXT_EN0, val1); |
|||
val2 = pnv_xive_xscom_read(qts, X_TCTXT_EN0); |
|||
g_assert_cmphex(val1, ==, val2); |
|||
|
|||
/* Memory tables */ |
|||
set_table(qts, VST_ESB, XIVE_ESB_MEM); |
|||
set_table(qts, VST_EAS, XIVE_EAS_MEM); |
|||
set_table(qts, VST_END, XIVE_END_MEM); |
|||
set_table(qts, VST_NVP, XIVE_NVP_MEM); |
|||
set_table(qts, VST_NVG, XIVE_NVG_MEM); |
|||
set_table(qts, VST_NVC, XIVE_NVC_MEM); |
|||
set_table(qts, VST_SYNC, XIVE_SYNC_MEM); |
|||
|
|||
reset_hw_threads(qts); |
|||
reset_pool_threads(qts); |
|||
} |
|||
|
|||
static void test_hw_irq(QTestState *qts) |
|||
{ |
|||
uint32_t irq = 2; |
|||
uint32_t irq_data = 0x600df00d; |
|||
uint32_t end_index = 5; |
|||
uint32_t target_pir = 1; |
|||
uint32_t target_nvp = 0x80 + target_pir; |
|||
uint8_t priority = 5; |
|||
uint32_t reg32; |
|||
uint16_t reg16; |
|||
uint8_t pq, nsr, cppr; |
|||
|
|||
printf("# ============================================================\n"); |
|||
printf("# Testing irq %d to hardware thread %d\n", irq, target_pir); |
|||
|
|||
/* irq config */ |
|||
set_eas(qts, irq, end_index, irq_data); |
|||
set_end(qts, end_index, target_nvp, priority, false /* group */); |
|||
|
|||
/* enable and trigger irq */ |
|||
get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_SET_PQ_00); |
|||
set_esb(qts, irq, XIVE_TRIGGER_PAGE, 0, 0); |
|||
|
|||
/* check irq is raised on cpu */ |
|||
pq = get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_GET); |
|||
g_assert_cmpuint(pq, ==, XIVE_ESB_PENDING); |
|||
|
|||
reg32 = get_tima32(qts, target_pir, TM_QW3_HV_PHYS + TM_WORD0); |
|||
nsr = reg32 >> 24; |
|||
cppr = (reg32 >> 16) & 0xFF; |
|||
g_assert_cmphex(nsr, ==, 0x80); |
|||
g_assert_cmphex(cppr, ==, 0xFF); |
|||
|
|||
/* ack the irq */ |
|||
reg16 = get_tima16(qts, target_pir, TM_SPC_ACK_HV_REG); |
|||
nsr = reg16 >> 8; |
|||
cppr = reg16 & 0xFF; |
|||
g_assert_cmphex(nsr, ==, 0x80); |
|||
g_assert_cmphex(cppr, ==, priority); |
|||
|
|||
/* check irq data is what was configured */ |
|||
reg32 = qtest_readl(qts, xive_get_queue_addr(end_index)); |
|||
g_assert_cmphex((reg32 & 0x7fffffff), ==, (irq_data & 0x7fffffff)); |
|||
|
|||
/* End Of Interrupt */ |
|||
set_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_STORE_EOI, 0); |
|||
pq = get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_GET); |
|||
g_assert_cmpuint(pq, ==, XIVE_ESB_RESET); |
|||
|
|||
/* reset CPPR */ |
|||
set_tima8(qts, target_pir, TM_QW3_HV_PHYS + TM_CPPR, 0xFF); |
|||
reg32 = get_tima32(qts, target_pir, TM_QW3_HV_PHYS + TM_WORD0); |
|||
nsr = reg32 >> 24; |
|||
cppr = (reg32 >> 16) & 0xFF; |
|||
g_assert_cmphex(nsr, ==, 0x00); |
|||
g_assert_cmphex(cppr, ==, 0xFF); |
|||
} |
|||
|
|||
#define XIVE_ODD_CL 0x80 |
|||
static void test_pull_thread_ctx_to_odd_thread_cl(QTestState *qts) |
|||
{ |
|||
uint32_t target_pir = 1; |
|||
uint32_t target_nvp = 0x80 + target_pir; |
|||
Xive2Nvp nvp; |
|||
uint8_t cl_pair[XIVE_REPORT_SIZE]; |
|||
uint32_t qw1w0, qw3w0, qw1w2, qw2w2; |
|||
uint8_t qw3b8; |
|||
uint32_t cl_word; |
|||
uint32_t word2; |
|||
|
|||
printf("# ============================================================\n"); |
|||
printf("# Testing 'Pull Thread Context to Odd Thread Reporting Line'\n"); |
|||
|
|||
/* clear odd cache line prior to pull operation */ |
|||
memset(cl_pair, 0, sizeof(cl_pair)); |
|||
get_nvp(qts, target_nvp, &nvp); |
|||
set_cl_pair(qts, &nvp, cl_pair); |
|||
|
|||
/* Read some values from TIMA that we expect to see in cacheline */ |
|||
qw1w0 = get_tima32(qts, target_pir, TM_QW1_OS + TM_WORD0); |
|||
qw3w0 = get_tima32(qts, target_pir, TM_QW3_HV_PHYS + TM_WORD0); |
|||
qw1w2 = get_tima32(qts, target_pir, TM_QW1_OS + TM_WORD2); |
|||
qw2w2 = get_tima32(qts, target_pir, TM_QW2_HV_POOL + TM_WORD2); |
|||
qw3b8 = get_tima8(qts, target_pir, TM_QW3_HV_PHYS + TM_WORD2); |
|||
|
|||
/* Execute the pull operation */ |
|||
set_tima8(qts, target_pir, TM_SPC_PULL_PHYS_CTX_OL, 0); |
|||
|
|||
/* Verify odd cache line values match TIMA after pull operation */ |
|||
get_cl_pair(qts, &nvp, cl_pair); |
|||
memcpy(&cl_word, &cl_pair[XIVE_ODD_CL + TM_QW1_OS + TM_WORD0], 4); |
|||
g_assert_cmphex(qw1w0, ==, be32_to_cpu(cl_word)); |
|||
memcpy(&cl_word, &cl_pair[XIVE_ODD_CL + TM_QW3_HV_PHYS + TM_WORD0], 4); |
|||
g_assert_cmphex(qw3w0, ==, be32_to_cpu(cl_word)); |
|||
memcpy(&cl_word, &cl_pair[XIVE_ODD_CL + TM_QW1_OS + TM_WORD2], 4); |
|||
g_assert_cmphex(qw1w2, ==, be32_to_cpu(cl_word)); |
|||
memcpy(&cl_word, &cl_pair[XIVE_ODD_CL + TM_QW2_HV_POOL + TM_WORD2], 4); |
|||
g_assert_cmphex(qw2w2, ==, be32_to_cpu(cl_word)); |
|||
g_assert_cmphex(qw3b8, ==, |
|||
cl_pair[XIVE_ODD_CL + TM_QW3_HV_PHYS + TM_WORD2]); |
|||
|
|||
/* Verify that all TIMA valid bits for target thread are cleared */ |
|||
word2 = get_tima32(qts, target_pir, TM_QW1_OS + TM_WORD2); |
|||
g_assert_cmphex(xive_get_field32(TM_QW1W2_VO, word2), ==, 0); |
|||
word2 = get_tima32(qts, target_pir, TM_QW2_HV_POOL + TM_WORD2); |
|||
g_assert_cmphex(xive_get_field32(TM_QW2W2_VP, word2), ==, 0); |
|||
word2 = get_tima32(qts, target_pir, TM_QW3_HV_PHYS + TM_WORD2); |
|||
g_assert_cmphex(xive_get_field32(TM_QW3W2_VT, word2), ==, 0); |
|||
} |
|||
static void test_xive(void) |
|||
{ |
|||
QTestState *qts; |
|||
|
|||
qts = qtest_initf("-M powernv10 -smp %d,cores=1,threads=%d -nographic " |
|||
"-nodefaults -serial mon:stdio -S " |
|||
"-d guest_errors -trace '*xive*'", |
|||
SMT, SMT); |
|||
init_xive(qts); |
|||
|
|||
test_hw_irq(qts); |
|||
|
|||
/* omit reset_state here and use settings from test_hw_irq */ |
|||
test_pull_thread_ctx_to_odd_thread_cl(qts); |
|||
|
|||
reset_state(qts); |
|||
test_flush_sync_inject(qts); |
|||
|
|||
qtest_quit(qts); |
|||
} |
|||
|
|||
int main(int argc, char **argv) |
|||
{ |
|||
g_test_init(&argc, &argv, NULL); |
|||
qtest_add_func("xive2", test_xive); |
|||
return g_test_run(); |
|||
} |
|||
Loading…
Reference in new issue