xref: /openbmc/qemu/tests/qtest/riscv-iommu-test.c (revision d4f7804b)
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 
riscv_iommu_read_reg32(QRISCVIOMMU * r_iommu,int reg_offset)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 
riscv_iommu_read_reg64(QRISCVIOMMU * r_iommu,int reg_offset)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 
riscv_iommu_write_reg32(QRISCVIOMMU * r_iommu,int reg_offset,uint32_t val)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 
riscv_iommu_write_reg64(QRISCVIOMMU * r_iommu,int reg_offset,uint64_t val)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 
test_pci_config(void * obj,void * data,QGuestAllocator * t_alloc)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 
test_reg_reset(void * obj,void * data,QGuestAllocator * t_alloc)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  */
qtest_wait_for_queue_active(QRISCVIOMMU * r_iommu,uint32_t queue_csr)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  */
test_iommu_init_queues(void * obj,void * data,QGuestAllocator * t_alloc)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 
register_riscv_iommu_test(void)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