140b44316SDaniel Henrique Barboza /* 240b44316SDaniel Henrique Barboza * QTest testcase for RISC-V IOMMU 340b44316SDaniel Henrique Barboza * 440b44316SDaniel Henrique Barboza * Copyright (c) 2024 Ventana Micro Systems Inc. 540b44316SDaniel Henrique Barboza * 640b44316SDaniel Henrique Barboza * This work is licensed under the terms of the GNU GPL, version 2 or (at your 740b44316SDaniel Henrique Barboza * option) any later version. See the COPYING file in the top-level directory. 840b44316SDaniel Henrique Barboza * 940b44316SDaniel Henrique Barboza */ 1040b44316SDaniel Henrique Barboza 1140b44316SDaniel Henrique Barboza #include "qemu/osdep.h" 1240b44316SDaniel Henrique Barboza #include "libqtest-single.h" 1340b44316SDaniel Henrique Barboza #include "qemu/module.h" 1440b44316SDaniel Henrique Barboza #include "libqos/qgraph.h" 1540b44316SDaniel Henrique Barboza #include "libqos/riscv-iommu.h" 1640b44316SDaniel Henrique Barboza #include "hw/pci/pci_regs.h" 1740b44316SDaniel Henrique Barboza 1840b44316SDaniel Henrique Barboza static uint32_t riscv_iommu_read_reg32(QRISCVIOMMU *r_iommu, int reg_offset) 1940b44316SDaniel Henrique Barboza { 2040b44316SDaniel Henrique Barboza return qpci_io_readl(&r_iommu->dev, r_iommu->reg_bar, reg_offset); 2140b44316SDaniel Henrique Barboza } 2240b44316SDaniel Henrique Barboza 2340b44316SDaniel Henrique Barboza static uint64_t riscv_iommu_read_reg64(QRISCVIOMMU *r_iommu, int reg_offset) 2440b44316SDaniel Henrique Barboza { 2540b44316SDaniel Henrique Barboza return qpci_io_readq(&r_iommu->dev, r_iommu->reg_bar, reg_offset); 2640b44316SDaniel Henrique Barboza } 2740b44316SDaniel Henrique Barboza 28*d4f7804bSDaniel Henrique Barboza static void riscv_iommu_write_reg32(QRISCVIOMMU *r_iommu, int reg_offset, 29*d4f7804bSDaniel Henrique Barboza uint32_t val) 30*d4f7804bSDaniel Henrique Barboza { 31*d4f7804bSDaniel Henrique Barboza qpci_io_writel(&r_iommu->dev, r_iommu->reg_bar, reg_offset, val); 32*d4f7804bSDaniel Henrique Barboza } 33*d4f7804bSDaniel Henrique Barboza 34*d4f7804bSDaniel Henrique Barboza static void riscv_iommu_write_reg64(QRISCVIOMMU *r_iommu, int reg_offset, 35*d4f7804bSDaniel Henrique Barboza uint64_t val) 36*d4f7804bSDaniel Henrique Barboza { 37*d4f7804bSDaniel Henrique Barboza qpci_io_writeq(&r_iommu->dev, r_iommu->reg_bar, reg_offset, val); 38*d4f7804bSDaniel Henrique Barboza } 39*d4f7804bSDaniel Henrique Barboza 4040b44316SDaniel Henrique Barboza static void test_pci_config(void *obj, void *data, QGuestAllocator *t_alloc) 4140b44316SDaniel Henrique Barboza { 4240b44316SDaniel Henrique Barboza QRISCVIOMMU *r_iommu = obj; 4340b44316SDaniel Henrique Barboza QPCIDevice *dev = &r_iommu->dev; 4440b44316SDaniel Henrique Barboza uint16_t vendorid, deviceid, classid; 4540b44316SDaniel Henrique Barboza 4640b44316SDaniel Henrique Barboza vendorid = qpci_config_readw(dev, PCI_VENDOR_ID); 4740b44316SDaniel Henrique Barboza deviceid = qpci_config_readw(dev, PCI_DEVICE_ID); 4840b44316SDaniel Henrique Barboza classid = qpci_config_readw(dev, PCI_CLASS_DEVICE); 4940b44316SDaniel Henrique Barboza 5040b44316SDaniel Henrique Barboza g_assert_cmpuint(vendorid, ==, RISCV_IOMMU_PCI_VENDOR_ID); 5140b44316SDaniel Henrique Barboza g_assert_cmpuint(deviceid, ==, RISCV_IOMMU_PCI_DEVICE_ID); 5240b44316SDaniel Henrique Barboza g_assert_cmpuint(classid, ==, RISCV_IOMMU_PCI_DEVICE_CLASS); 5340b44316SDaniel Henrique Barboza } 5440b44316SDaniel Henrique Barboza 5540b44316SDaniel Henrique Barboza static void test_reg_reset(void *obj, void *data, QGuestAllocator *t_alloc) 5640b44316SDaniel Henrique Barboza { 5740b44316SDaniel Henrique Barboza QRISCVIOMMU *r_iommu = obj; 5840b44316SDaniel Henrique Barboza uint64_t cap; 5940b44316SDaniel Henrique Barboza uint32_t reg; 6040b44316SDaniel Henrique Barboza 6140b44316SDaniel Henrique Barboza cap = riscv_iommu_read_reg64(r_iommu, RISCV_IOMMU_REG_CAP); 6240b44316SDaniel Henrique Barboza g_assert_cmpuint(cap & RISCV_IOMMU_CAP_VERSION, ==, 0x10); 6340b44316SDaniel Henrique Barboza 6440b44316SDaniel Henrique Barboza reg = riscv_iommu_read_reg32(r_iommu, RISCV_IOMMU_REG_CQCSR); 6540b44316SDaniel Henrique Barboza g_assert_cmpuint(reg & RISCV_IOMMU_CQCSR_CQEN, ==, 0); 6640b44316SDaniel Henrique Barboza g_assert_cmpuint(reg & RISCV_IOMMU_CQCSR_CIE, ==, 0); 6740b44316SDaniel Henrique Barboza g_assert_cmpuint(reg & RISCV_IOMMU_CQCSR_CQON, ==, 0); 6840b44316SDaniel Henrique Barboza g_assert_cmpuint(reg & RISCV_IOMMU_CQCSR_BUSY, ==, 0); 6940b44316SDaniel Henrique Barboza 7040b44316SDaniel Henrique Barboza reg = riscv_iommu_read_reg32(r_iommu, RISCV_IOMMU_REG_FQCSR); 7140b44316SDaniel Henrique Barboza g_assert_cmpuint(reg & RISCV_IOMMU_FQCSR_FQEN, ==, 0); 7240b44316SDaniel Henrique Barboza g_assert_cmpuint(reg & RISCV_IOMMU_FQCSR_FIE, ==, 0); 7340b44316SDaniel Henrique Barboza g_assert_cmpuint(reg & RISCV_IOMMU_FQCSR_FQON, ==, 0); 7440b44316SDaniel Henrique Barboza g_assert_cmpuint(reg & RISCV_IOMMU_FQCSR_BUSY, ==, 0); 7540b44316SDaniel Henrique Barboza 7640b44316SDaniel Henrique Barboza reg = riscv_iommu_read_reg32(r_iommu, RISCV_IOMMU_REG_PQCSR); 7740b44316SDaniel Henrique Barboza g_assert_cmpuint(reg & RISCV_IOMMU_PQCSR_PQEN, ==, 0); 7840b44316SDaniel Henrique Barboza g_assert_cmpuint(reg & RISCV_IOMMU_PQCSR_PIE, ==, 0); 7940b44316SDaniel Henrique Barboza g_assert_cmpuint(reg & RISCV_IOMMU_PQCSR_PQON, ==, 0); 8040b44316SDaniel Henrique Barboza g_assert_cmpuint(reg & RISCV_IOMMU_PQCSR_BUSY, ==, 0); 8140b44316SDaniel Henrique Barboza 8240b44316SDaniel Henrique Barboza reg = riscv_iommu_read_reg32(r_iommu, RISCV_IOMMU_REG_DDTP); 8340b44316SDaniel Henrique Barboza g_assert_cmpuint(reg & RISCV_IOMMU_DDTP_BUSY, ==, 0); 8440b44316SDaniel Henrique Barboza g_assert_cmpuint(reg & RISCV_IOMMU_DDTP_MODE, ==, 8540b44316SDaniel Henrique Barboza RISCV_IOMMU_DDTP_MODE_OFF); 8640b44316SDaniel Henrique Barboza 8740b44316SDaniel Henrique Barboza reg = riscv_iommu_read_reg32(r_iommu, RISCV_IOMMU_REG_IPSR); 8840b44316SDaniel Henrique Barboza g_assert_cmpuint(reg, ==, 0); 8940b44316SDaniel Henrique Barboza } 9040b44316SDaniel Henrique Barboza 91*d4f7804bSDaniel Henrique Barboza /* 92*d4f7804bSDaniel Henrique Barboza * Common timeout-based poll for CQCSR, FQCSR and PQCSR. All 93*d4f7804bSDaniel Henrique Barboza * their ON bits are mapped as RISCV_IOMMU_QUEUE_ACTIVE (16), 94*d4f7804bSDaniel Henrique Barboza */ 95*d4f7804bSDaniel Henrique Barboza static void qtest_wait_for_queue_active(QRISCVIOMMU *r_iommu, 96*d4f7804bSDaniel Henrique Barboza uint32_t queue_csr) 97*d4f7804bSDaniel Henrique Barboza { 98*d4f7804bSDaniel Henrique Barboza QTestState *qts = global_qtest; 99*d4f7804bSDaniel Henrique Barboza guint64 timeout_us = 2 * 1000 * 1000; 100*d4f7804bSDaniel Henrique Barboza gint64 start_time = g_get_monotonic_time(); 101*d4f7804bSDaniel Henrique Barboza uint32_t reg; 102*d4f7804bSDaniel Henrique Barboza 103*d4f7804bSDaniel Henrique Barboza for (;;) { 104*d4f7804bSDaniel Henrique Barboza qtest_clock_step(qts, 100); 105*d4f7804bSDaniel Henrique Barboza 106*d4f7804bSDaniel Henrique Barboza reg = riscv_iommu_read_reg32(r_iommu, queue_csr); 107*d4f7804bSDaniel Henrique Barboza if (reg & RISCV_IOMMU_QUEUE_ACTIVE) { 108*d4f7804bSDaniel Henrique Barboza break; 109*d4f7804bSDaniel Henrique Barboza } 110*d4f7804bSDaniel Henrique Barboza g_assert(g_get_monotonic_time() - start_time <= timeout_us); 111*d4f7804bSDaniel Henrique Barboza } 112*d4f7804bSDaniel Henrique Barboza } 113*d4f7804bSDaniel Henrique Barboza 114*d4f7804bSDaniel Henrique Barboza /* 115*d4f7804bSDaniel Henrique Barboza * Goes through the queue activation procedures of chapter 6.2, 116*d4f7804bSDaniel Henrique Barboza * "Guidelines for initialization", of the RISCV-IOMMU spec. 117*d4f7804bSDaniel Henrique Barboza */ 118*d4f7804bSDaniel Henrique Barboza static void test_iommu_init_queues(void *obj, void *data, 119*d4f7804bSDaniel Henrique Barboza QGuestAllocator *t_alloc) 120*d4f7804bSDaniel Henrique Barboza { 121*d4f7804bSDaniel Henrique Barboza QRISCVIOMMU *r_iommu = obj; 122*d4f7804bSDaniel Henrique Barboza uint64_t reg64, q_addr; 123*d4f7804bSDaniel Henrique Barboza uint32_t reg; 124*d4f7804bSDaniel Henrique Barboza int k = 2; 125*d4f7804bSDaniel Henrique Barboza 126*d4f7804bSDaniel Henrique Barboza reg64 = riscv_iommu_read_reg64(r_iommu, RISCV_IOMMU_REG_CAP); 127*d4f7804bSDaniel Henrique Barboza g_assert_cmpuint(reg64 & RISCV_IOMMU_CAP_VERSION, ==, 0x10); 128*d4f7804bSDaniel Henrique Barboza 129*d4f7804bSDaniel Henrique Barboza /* 130*d4f7804bSDaniel Henrique Barboza * Program the command queue. Write 0xF to civ, fiv, pmiv and 131*d4f7804bSDaniel Henrique Barboza * piv. With the current PCI device impl we expect 2 writable 132*d4f7804bSDaniel Henrique Barboza * bits for each (k = 2) since we have N = 4 total vectors (2^k). 133*d4f7804bSDaniel Henrique Barboza */ 134*d4f7804bSDaniel Henrique Barboza riscv_iommu_write_reg32(r_iommu, RISCV_IOMMU_REG_IVEC, 0xFFFF); 135*d4f7804bSDaniel Henrique Barboza reg = riscv_iommu_read_reg32(r_iommu, RISCV_IOMMU_REG_IVEC); 136*d4f7804bSDaniel Henrique Barboza g_assert_cmpuint(reg & RISCV_IOMMU_REG_IVEC_CIV, ==, 0x3); 137*d4f7804bSDaniel Henrique Barboza g_assert_cmpuint(reg & RISCV_IOMMU_REG_IVEC_FIV, ==, 0x30); 138*d4f7804bSDaniel Henrique Barboza g_assert_cmpuint(reg & RISCV_IOMMU_REG_IVEC_PMIV, ==, 0x300); 139*d4f7804bSDaniel Henrique Barboza g_assert_cmpuint(reg & RISCV_IOMMU_REG_IVEC_PIV, ==, 0x3000); 140*d4f7804bSDaniel Henrique Barboza 141*d4f7804bSDaniel Henrique Barboza /* Alloc a 4*16 bytes buffer and use it to set cqb */ 142*d4f7804bSDaniel Henrique Barboza q_addr = guest_alloc(t_alloc, 4 * 16); 143*d4f7804bSDaniel Henrique Barboza reg64 = 0; 144*d4f7804bSDaniel Henrique Barboza deposit64(reg64, RISCV_IOMMU_CQB_PPN_START, 145*d4f7804bSDaniel Henrique Barboza RISCV_IOMMU_CQB_PPN_LEN, q_addr); 146*d4f7804bSDaniel Henrique Barboza deposit64(reg64, RISCV_IOMMU_CQB_LOG2SZ_START, 147*d4f7804bSDaniel Henrique Barboza RISCV_IOMMU_CQB_LOG2SZ_LEN, k - 1); 148*d4f7804bSDaniel Henrique Barboza riscv_iommu_write_reg64(r_iommu, RISCV_IOMMU_REG_CQB, reg64); 149*d4f7804bSDaniel Henrique Barboza 150*d4f7804bSDaniel Henrique Barboza /* cqt = 0, cqcsr.cqen = 1, poll cqcsr.cqon until it reads 1 */ 151*d4f7804bSDaniel Henrique Barboza riscv_iommu_write_reg32(r_iommu, RISCV_IOMMU_REG_CQT, 0); 152*d4f7804bSDaniel Henrique Barboza 153*d4f7804bSDaniel Henrique Barboza reg = riscv_iommu_read_reg32(r_iommu, RISCV_IOMMU_REG_CQCSR); 154*d4f7804bSDaniel Henrique Barboza reg |= RISCV_IOMMU_CQCSR_CQEN; 155*d4f7804bSDaniel Henrique Barboza riscv_iommu_write_reg32(r_iommu, RISCV_IOMMU_REG_CQCSR, reg); 156*d4f7804bSDaniel Henrique Barboza 157*d4f7804bSDaniel Henrique Barboza qtest_wait_for_queue_active(r_iommu, RISCV_IOMMU_REG_CQCSR); 158*d4f7804bSDaniel Henrique Barboza 159*d4f7804bSDaniel Henrique Barboza /* 160*d4f7804bSDaniel Henrique Barboza * Program the fault queue. Alloc a 4*32 bytes (instead of 4*16) 161*d4f7804bSDaniel Henrique Barboza * buffer and use it to set fqb. 162*d4f7804bSDaniel Henrique Barboza */ 163*d4f7804bSDaniel Henrique Barboza q_addr = guest_alloc(t_alloc, 4 * 32); 164*d4f7804bSDaniel Henrique Barboza reg64 = 0; 165*d4f7804bSDaniel Henrique Barboza deposit64(reg64, RISCV_IOMMU_FQB_PPN_START, 166*d4f7804bSDaniel Henrique Barboza RISCV_IOMMU_FQB_PPN_LEN, q_addr); 167*d4f7804bSDaniel Henrique Barboza deposit64(reg64, RISCV_IOMMU_FQB_LOG2SZ_START, 168*d4f7804bSDaniel Henrique Barboza RISCV_IOMMU_FQB_LOG2SZ_LEN, k - 1); 169*d4f7804bSDaniel Henrique Barboza riscv_iommu_write_reg64(r_iommu, RISCV_IOMMU_REG_FQB, reg64); 170*d4f7804bSDaniel Henrique Barboza 171*d4f7804bSDaniel Henrique Barboza /* fqt = 0, fqcsr.fqen = 1, poll fqcsr.fqon until it reads 1 */ 172*d4f7804bSDaniel Henrique Barboza riscv_iommu_write_reg32(r_iommu, RISCV_IOMMU_REG_FQT, 0); 173*d4f7804bSDaniel Henrique Barboza 174*d4f7804bSDaniel Henrique Barboza reg = riscv_iommu_read_reg32(r_iommu, RISCV_IOMMU_REG_FQCSR); 175*d4f7804bSDaniel Henrique Barboza reg |= RISCV_IOMMU_FQCSR_FQEN; 176*d4f7804bSDaniel Henrique Barboza riscv_iommu_write_reg32(r_iommu, RISCV_IOMMU_REG_FQCSR, reg); 177*d4f7804bSDaniel Henrique Barboza 178*d4f7804bSDaniel Henrique Barboza qtest_wait_for_queue_active(r_iommu, RISCV_IOMMU_REG_FQCSR); 179*d4f7804bSDaniel Henrique Barboza 180*d4f7804bSDaniel Henrique Barboza /* 181*d4f7804bSDaniel Henrique Barboza * Program the page-request queue. Alloc a 4*16 bytes buffer 182*d4f7804bSDaniel Henrique Barboza * and use it to set pqb. 183*d4f7804bSDaniel Henrique Barboza */ 184*d4f7804bSDaniel Henrique Barboza q_addr = guest_alloc(t_alloc, 4 * 16); 185*d4f7804bSDaniel Henrique Barboza reg64 = 0; 186*d4f7804bSDaniel Henrique Barboza deposit64(reg64, RISCV_IOMMU_PQB_PPN_START, 187*d4f7804bSDaniel Henrique Barboza RISCV_IOMMU_PQB_PPN_LEN, q_addr); 188*d4f7804bSDaniel Henrique Barboza deposit64(reg64, RISCV_IOMMU_PQB_LOG2SZ_START, 189*d4f7804bSDaniel Henrique Barboza RISCV_IOMMU_PQB_LOG2SZ_LEN, k - 1); 190*d4f7804bSDaniel Henrique Barboza riscv_iommu_write_reg64(r_iommu, RISCV_IOMMU_REG_PQB, reg64); 191*d4f7804bSDaniel Henrique Barboza 192*d4f7804bSDaniel Henrique Barboza /* pqt = 0, pqcsr.pqen = 1, poll pqcsr.pqon until it reads 1 */ 193*d4f7804bSDaniel Henrique Barboza riscv_iommu_write_reg32(r_iommu, RISCV_IOMMU_REG_PQT, 0); 194*d4f7804bSDaniel Henrique Barboza 195*d4f7804bSDaniel Henrique Barboza reg = riscv_iommu_read_reg32(r_iommu, RISCV_IOMMU_REG_PQCSR); 196*d4f7804bSDaniel Henrique Barboza reg |= RISCV_IOMMU_PQCSR_PQEN; 197*d4f7804bSDaniel Henrique Barboza riscv_iommu_write_reg32(r_iommu, RISCV_IOMMU_REG_PQCSR, reg); 198*d4f7804bSDaniel Henrique Barboza 199*d4f7804bSDaniel Henrique Barboza qtest_wait_for_queue_active(r_iommu, RISCV_IOMMU_REG_PQCSR); 200*d4f7804bSDaniel Henrique Barboza } 201*d4f7804bSDaniel Henrique Barboza 20240b44316SDaniel Henrique Barboza static void register_riscv_iommu_test(void) 20340b44316SDaniel Henrique Barboza { 20440b44316SDaniel Henrique Barboza qos_add_test("pci_config", "riscv-iommu-pci", test_pci_config, NULL); 20540b44316SDaniel Henrique Barboza qos_add_test("reg_reset", "riscv-iommu-pci", test_reg_reset, NULL); 206*d4f7804bSDaniel Henrique Barboza qos_add_test("iommu_init_queues", "riscv-iommu-pci", 207*d4f7804bSDaniel Henrique Barboza test_iommu_init_queues, NULL); 20840b44316SDaniel Henrique Barboza } 20940b44316SDaniel Henrique Barboza 21040b44316SDaniel Henrique Barboza libqos_init(register_riscv_iommu_test); 211