1 /*
2 * QTest testcase for RISC-V IOMMU
3 *
4 * Copyright (c) 2024 Ventana Micro Systems Inc.
5 *
6 * This work is licensed under the terms of the GNU GPL, version 2 or (at your
7 * option) any later version. See the COPYING file in the top-level directory.
8 *
9 */
10
11 #include "qemu/osdep.h"
12 #include "libqtest-single.h"
13 #include "qemu/module.h"
14 #include "libqos/qgraph.h"
15 #include "libqos/riscv-iommu.h"
16 #include "hw/pci/pci_regs.h"
17
riscv_iommu_read_reg32(QRISCVIOMMU * r_iommu,int reg_offset)18 static uint32_t riscv_iommu_read_reg32(QRISCVIOMMU *r_iommu, int reg_offset)
19 {
20 return qpci_io_readl(&r_iommu->dev, r_iommu->reg_bar, reg_offset);
21 }
22
riscv_iommu_read_reg64(QRISCVIOMMU * r_iommu,int reg_offset)23 static uint64_t riscv_iommu_read_reg64(QRISCVIOMMU *r_iommu, int reg_offset)
24 {
25 return qpci_io_readq(&r_iommu->dev, r_iommu->reg_bar, reg_offset);
26 }
27
riscv_iommu_write_reg32(QRISCVIOMMU * r_iommu,int reg_offset,uint32_t val)28 static void riscv_iommu_write_reg32(QRISCVIOMMU *r_iommu, int reg_offset,
29 uint32_t val)
30 {
31 qpci_io_writel(&r_iommu->dev, r_iommu->reg_bar, reg_offset, val);
32 }
33
riscv_iommu_write_reg64(QRISCVIOMMU * r_iommu,int reg_offset,uint64_t val)34 static void riscv_iommu_write_reg64(QRISCVIOMMU *r_iommu, int reg_offset,
35 uint64_t val)
36 {
37 qpci_io_writeq(&r_iommu->dev, r_iommu->reg_bar, reg_offset, val);
38 }
39
test_pci_config(void * obj,void * data,QGuestAllocator * t_alloc)40 static void test_pci_config(void *obj, void *data, QGuestAllocator *t_alloc)
41 {
42 QRISCVIOMMU *r_iommu = obj;
43 QPCIDevice *dev = &r_iommu->dev;
44 uint16_t vendorid, deviceid, classid;
45
46 vendorid = qpci_config_readw(dev, PCI_VENDOR_ID);
47 deviceid = qpci_config_readw(dev, PCI_DEVICE_ID);
48 classid = qpci_config_readw(dev, PCI_CLASS_DEVICE);
49
50 g_assert_cmpuint(vendorid, ==, RISCV_IOMMU_PCI_VENDOR_ID);
51 g_assert_cmpuint(deviceid, ==, RISCV_IOMMU_PCI_DEVICE_ID);
52 g_assert_cmpuint(classid, ==, RISCV_IOMMU_PCI_DEVICE_CLASS);
53 }
54
test_reg_reset(void * obj,void * data,QGuestAllocator * t_alloc)55 static void test_reg_reset(void *obj, void *data, QGuestAllocator *t_alloc)
56 {
57 QRISCVIOMMU *r_iommu = obj;
58 uint64_t cap;
59 uint32_t reg;
60
61 cap = riscv_iommu_read_reg64(r_iommu, RISCV_IOMMU_REG_CAP);
62 g_assert_cmpuint(cap & RISCV_IOMMU_CAP_VERSION, ==, 0x10);
63
64 reg = riscv_iommu_read_reg32(r_iommu, RISCV_IOMMU_REG_CQCSR);
65 g_assert_cmpuint(reg & RISCV_IOMMU_CQCSR_CQEN, ==, 0);
66 g_assert_cmpuint(reg & RISCV_IOMMU_CQCSR_CIE, ==, 0);
67 g_assert_cmpuint(reg & RISCV_IOMMU_CQCSR_CQON, ==, 0);
68 g_assert_cmpuint(reg & RISCV_IOMMU_CQCSR_BUSY, ==, 0);
69
70 reg = riscv_iommu_read_reg32(r_iommu, RISCV_IOMMU_REG_FQCSR);
71 g_assert_cmpuint(reg & RISCV_IOMMU_FQCSR_FQEN, ==, 0);
72 g_assert_cmpuint(reg & RISCV_IOMMU_FQCSR_FIE, ==, 0);
73 g_assert_cmpuint(reg & RISCV_IOMMU_FQCSR_FQON, ==, 0);
74 g_assert_cmpuint(reg & RISCV_IOMMU_FQCSR_BUSY, ==, 0);
75
76 reg = riscv_iommu_read_reg32(r_iommu, RISCV_IOMMU_REG_PQCSR);
77 g_assert_cmpuint(reg & RISCV_IOMMU_PQCSR_PQEN, ==, 0);
78 g_assert_cmpuint(reg & RISCV_IOMMU_PQCSR_PIE, ==, 0);
79 g_assert_cmpuint(reg & RISCV_IOMMU_PQCSR_PQON, ==, 0);
80 g_assert_cmpuint(reg & RISCV_IOMMU_PQCSR_BUSY, ==, 0);
81
82 reg = riscv_iommu_read_reg32(r_iommu, RISCV_IOMMU_REG_DDTP);
83 g_assert_cmpuint(reg & RISCV_IOMMU_DDTP_BUSY, ==, 0);
84 g_assert_cmpuint(reg & RISCV_IOMMU_DDTP_MODE, ==,
85 RISCV_IOMMU_DDTP_MODE_OFF);
86
87 reg = riscv_iommu_read_reg32(r_iommu, RISCV_IOMMU_REG_IPSR);
88 g_assert_cmpuint(reg, ==, 0);
89 }
90
91 /*
92 * Common timeout-based poll for CQCSR, FQCSR and PQCSR. All
93 * their ON bits are mapped as RISCV_IOMMU_QUEUE_ACTIVE (16),
94 */
qtest_wait_for_queue_active(QRISCVIOMMU * r_iommu,uint32_t queue_csr)95 static void qtest_wait_for_queue_active(QRISCVIOMMU *r_iommu,
96 uint32_t queue_csr)
97 {
98 QTestState *qts = global_qtest;
99 guint64 timeout_us = 2 * 1000 * 1000;
100 gint64 start_time = g_get_monotonic_time();
101 uint32_t reg;
102
103 for (;;) {
104 qtest_clock_step(qts, 100);
105
106 reg = riscv_iommu_read_reg32(r_iommu, queue_csr);
107 if (reg & RISCV_IOMMU_QUEUE_ACTIVE) {
108 break;
109 }
110 g_assert(g_get_monotonic_time() - start_time <= timeout_us);
111 }
112 }
113
114 /*
115 * Goes through the queue activation procedures of chapter 6.2,
116 * "Guidelines for initialization", of the RISCV-IOMMU spec.
117 */
test_iommu_init_queues(void * obj,void * data,QGuestAllocator * t_alloc)118 static void test_iommu_init_queues(void *obj, void *data,
119 QGuestAllocator *t_alloc)
120 {
121 QRISCVIOMMU *r_iommu = obj;
122 uint64_t reg64, q_addr;
123 uint32_t reg;
124 int k = 2;
125
126 reg64 = riscv_iommu_read_reg64(r_iommu, RISCV_IOMMU_REG_CAP);
127 g_assert_cmpuint(reg64 & RISCV_IOMMU_CAP_VERSION, ==, 0x10);
128
129 /*
130 * Program the command queue. Write 0xF to civ, fiv, pmiv and
131 * piv. With the current PCI device impl we expect 2 writable
132 * bits for each (k = 2) since we have N = 4 total vectors (2^k).
133 */
134 riscv_iommu_write_reg32(r_iommu, RISCV_IOMMU_REG_IVEC, 0xFFFF);
135 reg = riscv_iommu_read_reg32(r_iommu, RISCV_IOMMU_REG_IVEC);
136 g_assert_cmpuint(reg & RISCV_IOMMU_REG_IVEC_CIV, ==, 0x3);
137 g_assert_cmpuint(reg & RISCV_IOMMU_REG_IVEC_FIV, ==, 0x30);
138 g_assert_cmpuint(reg & RISCV_IOMMU_REG_IVEC_PMIV, ==, 0x300);
139 g_assert_cmpuint(reg & RISCV_IOMMU_REG_IVEC_PIV, ==, 0x3000);
140
141 /* Alloc a 4*16 bytes buffer and use it to set cqb */
142 q_addr = guest_alloc(t_alloc, 4 * 16);
143 reg64 = 0;
144 deposit64(reg64, RISCV_IOMMU_CQB_PPN_START,
145 RISCV_IOMMU_CQB_PPN_LEN, q_addr);
146 deposit64(reg64, RISCV_IOMMU_CQB_LOG2SZ_START,
147 RISCV_IOMMU_CQB_LOG2SZ_LEN, k - 1);
148 riscv_iommu_write_reg64(r_iommu, RISCV_IOMMU_REG_CQB, reg64);
149
150 /* cqt = 0, cqcsr.cqen = 1, poll cqcsr.cqon until it reads 1 */
151 riscv_iommu_write_reg32(r_iommu, RISCV_IOMMU_REG_CQT, 0);
152
153 reg = riscv_iommu_read_reg32(r_iommu, RISCV_IOMMU_REG_CQCSR);
154 reg |= RISCV_IOMMU_CQCSR_CQEN;
155 riscv_iommu_write_reg32(r_iommu, RISCV_IOMMU_REG_CQCSR, reg);
156
157 qtest_wait_for_queue_active(r_iommu, RISCV_IOMMU_REG_CQCSR);
158
159 /*
160 * Program the fault queue. Alloc a 4*32 bytes (instead of 4*16)
161 * buffer and use it to set fqb.
162 */
163 q_addr = guest_alloc(t_alloc, 4 * 32);
164 reg64 = 0;
165 deposit64(reg64, RISCV_IOMMU_FQB_PPN_START,
166 RISCV_IOMMU_FQB_PPN_LEN, q_addr);
167 deposit64(reg64, RISCV_IOMMU_FQB_LOG2SZ_START,
168 RISCV_IOMMU_FQB_LOG2SZ_LEN, k - 1);
169 riscv_iommu_write_reg64(r_iommu, RISCV_IOMMU_REG_FQB, reg64);
170
171 /* fqt = 0, fqcsr.fqen = 1, poll fqcsr.fqon until it reads 1 */
172 riscv_iommu_write_reg32(r_iommu, RISCV_IOMMU_REG_FQT, 0);
173
174 reg = riscv_iommu_read_reg32(r_iommu, RISCV_IOMMU_REG_FQCSR);
175 reg |= RISCV_IOMMU_FQCSR_FQEN;
176 riscv_iommu_write_reg32(r_iommu, RISCV_IOMMU_REG_FQCSR, reg);
177
178 qtest_wait_for_queue_active(r_iommu, RISCV_IOMMU_REG_FQCSR);
179
180 /*
181 * Program the page-request queue. Alloc a 4*16 bytes buffer
182 * and use it to set pqb.
183 */
184 q_addr = guest_alloc(t_alloc, 4 * 16);
185 reg64 = 0;
186 deposit64(reg64, RISCV_IOMMU_PQB_PPN_START,
187 RISCV_IOMMU_PQB_PPN_LEN, q_addr);
188 deposit64(reg64, RISCV_IOMMU_PQB_LOG2SZ_START,
189 RISCV_IOMMU_PQB_LOG2SZ_LEN, k - 1);
190 riscv_iommu_write_reg64(r_iommu, RISCV_IOMMU_REG_PQB, reg64);
191
192 /* pqt = 0, pqcsr.pqen = 1, poll pqcsr.pqon until it reads 1 */
193 riscv_iommu_write_reg32(r_iommu, RISCV_IOMMU_REG_PQT, 0);
194
195 reg = riscv_iommu_read_reg32(r_iommu, RISCV_IOMMU_REG_PQCSR);
196 reg |= RISCV_IOMMU_PQCSR_PQEN;
197 riscv_iommu_write_reg32(r_iommu, RISCV_IOMMU_REG_PQCSR, reg);
198
199 qtest_wait_for_queue_active(r_iommu, RISCV_IOMMU_REG_PQCSR);
200 }
201
register_riscv_iommu_test(void)202 static void register_riscv_iommu_test(void)
203 {
204 qos_add_test("pci_config", "riscv-iommu-pci", test_pci_config, NULL);
205 qos_add_test("reg_reset", "riscv-iommu-pci", test_reg_reset, NULL);
206 qos_add_test("iommu_init_queues", "riscv-iommu-pci",
207 test_iommu_init_queues, NULL);
208 }
209
210 libqos_init(register_riscv_iommu_test);
211