1 /*
2  * QTest testcase for VirtIO IOMMU
3  *
4  * Copyright (c) 2021 Red Hat, Inc.
5  *
6  * Authors:
7  *  Eric Auger <eric.auger@redhat.com>
8  *
9  * This work is licensed under the terms of the GNU GPL, version 2 or (at your
10  * option) any later version.  See the COPYING file in the top-level directory.
11  *
12  */
13 
14 #include "qemu/osdep.h"
15 #include "libqtest-single.h"
16 #include "qemu/module.h"
17 #include "libqos/qgraph.h"
18 #include "libqos/virtio-iommu.h"
19 #include "hw/virtio/virtio-iommu.h"
20 
21 #define PCI_SLOT_HP             0x06
22 #define QVIRTIO_IOMMU_TIMEOUT_US (30 * 1000 * 1000)
23 
24 static QGuestAllocator *alloc;
25 
pci_config(void * obj,void * data,QGuestAllocator * t_alloc)26 static void pci_config(void *obj, void *data, QGuestAllocator *t_alloc)
27 {
28     QVirtioIOMMU *v_iommu = obj;
29     QVirtioDevice *dev = v_iommu->vdev;
30     uint64_t input_range_start = qvirtio_config_readq(dev, 8);
31     uint64_t input_range_end = qvirtio_config_readq(dev, 16);
32     uint32_t domain_range_start = qvirtio_config_readl(dev, 24);
33     uint32_t domain_range_end = qvirtio_config_readl(dev, 28);
34     uint8_t bypass = qvirtio_config_readb(dev, 36);
35 
36     g_assert_cmpint(input_range_start, ==, 0);
37     g_assert_cmphex(input_range_end, >=, UINT32_MAX);
38     g_assert_cmpint(domain_range_start, ==, 0);
39     g_assert_cmpint(domain_range_end, ==, UINT32_MAX);
40     g_assert_cmpint(bypass, ==, 1);
41 }
42 
read_tail_status(struct virtio_iommu_req_tail * buffer)43 static int read_tail_status(struct virtio_iommu_req_tail *buffer)
44 {
45     int i;
46 
47     for (i = 0; i < 3; i++) {
48         g_assert_cmpint(buffer->reserved[i], ==, 0);
49     }
50     return buffer->status;
51 }
52 
53 /**
54  * send_attach_detach - Send an attach/detach command to the device
55  * @type: VIRTIO_IOMMU_T_ATTACH/VIRTIO_IOMMU_T_DETACH
56  * @domain: domain the endpoint is attached to
57  * @ep: endpoint
58  */
send_attach_detach(QTestState * qts,QVirtioIOMMU * v_iommu,uint8_t type,uint32_t domain,uint32_t ep)59 static int send_attach_detach(QTestState *qts, QVirtioIOMMU *v_iommu,
60                               uint8_t type, uint32_t domain, uint32_t ep)
61 {
62     QVirtioDevice *dev = v_iommu->vdev;
63     QVirtQueue *vq = v_iommu->vq;
64     uint64_t ro_addr, wr_addr;
65     uint32_t free_head;
66     struct virtio_iommu_req_attach req = {}; /* same layout as detach */
67     size_t ro_size = sizeof(req) - sizeof(struct virtio_iommu_req_tail);
68     size_t wr_size = sizeof(struct virtio_iommu_req_tail);
69     struct virtio_iommu_req_tail buffer;
70     int ret;
71 
72     req.head.type = type;
73     req.domain = cpu_to_le32(domain);
74     req.endpoint = cpu_to_le32(ep);
75 
76     ro_addr = guest_alloc(alloc, ro_size);
77     wr_addr = guest_alloc(alloc, wr_size);
78 
79     qtest_memwrite(qts, ro_addr, &req, ro_size);
80     free_head = qvirtqueue_add(qts, vq, ro_addr, ro_size, false, true);
81     qvirtqueue_add(qts, vq, wr_addr, wr_size, true, false);
82     qvirtqueue_kick(qts, dev, vq, free_head);
83     qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
84                            QVIRTIO_IOMMU_TIMEOUT_US);
85     qtest_memread(qts, wr_addr, &buffer, wr_size);
86     ret = read_tail_status(&buffer);
87     guest_free(alloc, ro_addr);
88     guest_free(alloc, wr_addr);
89     return ret;
90 }
91 
92 /**
93  * send_map - Send a map command to the device
94  * @domain: domain the new mapping is attached to
95  * @virt_start: iova start
96  * @virt_end: iova end
97  * @phys_start: base physical address
98  * @flags: mapping flags
99  */
send_map(QTestState * qts,QVirtioIOMMU * v_iommu,uint32_t domain,uint64_t virt_start,uint64_t virt_end,uint64_t phys_start,uint32_t flags)100 static int send_map(QTestState *qts, QVirtioIOMMU *v_iommu,
101                     uint32_t domain, uint64_t virt_start, uint64_t virt_end,
102                     uint64_t phys_start, uint32_t flags)
103 {
104     QVirtioDevice *dev = v_iommu->vdev;
105     QVirtQueue *vq = v_iommu->vq;
106     uint64_t ro_addr, wr_addr;
107     uint32_t free_head;
108     struct virtio_iommu_req_map req;
109     size_t ro_size = sizeof(req) - sizeof(struct virtio_iommu_req_tail);
110     size_t wr_size = sizeof(struct virtio_iommu_req_tail);
111     struct virtio_iommu_req_tail buffer;
112     int ret;
113 
114     req.head.type = VIRTIO_IOMMU_T_MAP;
115     req.domain = cpu_to_le32(domain);
116     req.virt_start = cpu_to_le64(virt_start);
117     req.virt_end = cpu_to_le64(virt_end);
118     req.phys_start = cpu_to_le64(phys_start);
119     req.flags = cpu_to_le32(flags);
120 
121     ro_addr = guest_alloc(alloc, ro_size);
122     wr_addr = guest_alloc(alloc, wr_size);
123 
124     qtest_memwrite(qts, ro_addr, &req, ro_size);
125     free_head = qvirtqueue_add(qts, vq, ro_addr, ro_size, false, true);
126     qvirtqueue_add(qts, vq, wr_addr, wr_size, true, false);
127     qvirtqueue_kick(qts, dev, vq, free_head);
128     qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
129                            QVIRTIO_IOMMU_TIMEOUT_US);
130     qtest_memread(qts, wr_addr, &buffer, wr_size);
131     ret = read_tail_status(&buffer);
132     guest_free(alloc, ro_addr);
133     guest_free(alloc, wr_addr);
134     return ret;
135 }
136 
137 /**
138  * send_unmap - Send an unmap command to the device
139  * @domain: domain the new binding is attached to
140  * @virt_start: iova start
141  * @virt_end: iova end
142  */
send_unmap(QTestState * qts,QVirtioIOMMU * v_iommu,uint32_t domain,uint64_t virt_start,uint64_t virt_end)143 static int send_unmap(QTestState *qts, QVirtioIOMMU *v_iommu,
144                       uint32_t domain, uint64_t virt_start, uint64_t virt_end)
145 {
146     QVirtioDevice *dev = v_iommu->vdev;
147     QVirtQueue *vq = v_iommu->vq;
148     uint64_t ro_addr, wr_addr;
149     uint32_t free_head;
150     struct virtio_iommu_req_unmap req;
151     size_t ro_size = sizeof(req) - sizeof(struct virtio_iommu_req_tail);
152     size_t wr_size = sizeof(struct virtio_iommu_req_tail);
153     struct virtio_iommu_req_tail buffer;
154     int ret;
155 
156     req.head.type = VIRTIO_IOMMU_T_UNMAP;
157     req.domain = cpu_to_le32(domain);
158     req.virt_start = cpu_to_le64(virt_start);
159     req.virt_end = cpu_to_le64(virt_end);
160 
161     ro_addr = guest_alloc(alloc, ro_size);
162     wr_addr = guest_alloc(alloc, wr_size);
163 
164     qtest_memwrite(qts, ro_addr, &req, ro_size);
165     free_head = qvirtqueue_add(qts, vq, ro_addr, ro_size, false, true);
166     qvirtqueue_add(qts, vq, wr_addr, wr_size, true, false);
167     qvirtqueue_kick(qts, dev, vq, free_head);
168     qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
169                            QVIRTIO_IOMMU_TIMEOUT_US);
170     qtest_memread(qts, wr_addr, &buffer, wr_size);
171     ret = read_tail_status(&buffer);
172     guest_free(alloc, ro_addr);
173     guest_free(alloc, wr_addr);
174     return ret;
175 }
176 
test_attach_detach(void * obj,void * data,QGuestAllocator * t_alloc)177 static void test_attach_detach(void *obj, void *data, QGuestAllocator *t_alloc)
178 {
179     QVirtioIOMMU *v_iommu = obj;
180     QTestState *qts = global_qtest;
181     int ret;
182 
183     alloc = t_alloc;
184 
185     /* type, domain, ep */
186 
187     /* attach ep0 to domain 0 */
188     ret = send_attach_detach(qts, v_iommu, VIRTIO_IOMMU_T_ATTACH, 0, 0);
189     g_assert_cmpint(ret, ==, 0);
190 
191     /* attach a non existing device */
192     ret = send_attach_detach(qts, v_iommu, VIRTIO_IOMMU_T_ATTACH, 0, 444);
193     g_assert_cmpint(ret, ==, VIRTIO_IOMMU_S_NOENT);
194 
195     /* detach a non existing device (1) */
196     ret = send_attach_detach(qts, v_iommu, VIRTIO_IOMMU_T_DETACH, 0, 1);
197     g_assert_cmpint(ret, ==, VIRTIO_IOMMU_S_NOENT);
198 
199     /* move ep0 from domain 0 to domain 1 */
200     ret = send_attach_detach(qts, v_iommu, VIRTIO_IOMMU_T_ATTACH, 1, 0);
201     g_assert_cmpint(ret, ==, 0);
202 
203     /* detach ep0 from domain 0 */
204     ret = send_attach_detach(qts, v_iommu, VIRTIO_IOMMU_T_DETACH, 0, 0);
205     g_assert_cmpint(ret, ==, VIRTIO_IOMMU_S_INVAL);
206 
207     /* detach ep0 from domain 1 */
208     ret = send_attach_detach(qts, v_iommu, VIRTIO_IOMMU_T_DETACH, 1, 0);
209     g_assert_cmpint(ret, ==, 0);
210 
211     ret = send_attach_detach(qts, v_iommu, VIRTIO_IOMMU_T_ATTACH, 1, 0);
212     g_assert_cmpint(ret, ==, 0);
213     ret = send_map(qts, v_iommu, 1, 0x0, 0xFFF, 0xa1000,
214                    VIRTIO_IOMMU_MAP_F_READ);
215     g_assert_cmpint(ret, ==, 0);
216     ret = send_map(qts, v_iommu, 1, 0x2000, 0x2FFF, 0xb1000,
217                    VIRTIO_IOMMU_MAP_F_READ);
218     g_assert_cmpint(ret, ==, 0);
219     ret = send_attach_detach(qts, v_iommu, VIRTIO_IOMMU_T_DETACH, 1, 0);
220     g_assert_cmpint(ret, ==, 0);
221 }
222 
223 /* Test map/unmap scenari documented in the spec */
test_map_unmap(void * obj,void * data,QGuestAllocator * t_alloc)224 static void test_map_unmap(void *obj, void *data, QGuestAllocator *t_alloc)
225 {
226     QVirtioIOMMU *v_iommu = obj;
227     QTestState *qts = global_qtest;
228     int ret;
229 
230     alloc = t_alloc;
231 
232     /* attach ep0 to domain 1 */
233     ret = send_attach_detach(qts, v_iommu, VIRTIO_IOMMU_T_ATTACH, 1, 0);
234     g_assert_cmpint(ret, ==, 0);
235 
236     ret = send_map(qts, v_iommu, 0, 0, 0xFFF, 0xa1000, VIRTIO_IOMMU_MAP_F_READ);
237     g_assert_cmpint(ret, ==, VIRTIO_IOMMU_S_NOENT);
238 
239     /* domain, virt start, virt end, phys start, flags */
240     ret = send_map(qts, v_iommu, 1, 0x0, 0xFFF, 0xa1000, VIRTIO_IOMMU_MAP_F_READ);
241     g_assert_cmpint(ret, ==, 0);
242 
243     /* send a new mapping overlapping the previous one */
244     ret = send_map(qts, v_iommu, 1, 0, 0xFFFF, 0xb1000, VIRTIO_IOMMU_MAP_F_READ);
245     g_assert_cmpint(ret, ==, VIRTIO_IOMMU_S_INVAL);
246 
247     ret = send_unmap(qts, v_iommu, 4, 0x10, 0xFFF);
248     g_assert_cmpint(ret, ==, VIRTIO_IOMMU_S_NOENT);
249 
250     ret = send_unmap(qts, v_iommu, 1, 0x10, 0xFFF);
251     g_assert_cmpint(ret, ==, VIRTIO_IOMMU_S_RANGE);
252 
253     ret = send_unmap(qts, v_iommu, 1, 0, 0x1000);
254     g_assert_cmpint(ret, ==, 0); /* unmap everything */
255 
256     /* Spec example sequence */
257 
258     /* 1 */
259     ret = send_unmap(qts, v_iommu, 1, 0, 4);
260     g_assert_cmpint(ret, ==, 0); /* doesn't unmap anything */
261 
262     /* 2 */
263     ret = send_map(qts, v_iommu, 1, 0, 9, 0xa1000, VIRTIO_IOMMU_MAP_F_READ);
264     g_assert_cmpint(ret, ==, 0);
265     ret = send_unmap(qts, v_iommu, 1, 0, 9);
266     g_assert_cmpint(ret, ==, 0); /* unmaps [0,9] */
267 
268     /* 3 */
269     ret = send_map(qts, v_iommu, 1, 0, 4, 0xb1000, VIRTIO_IOMMU_MAP_F_READ);
270     g_assert_cmpint(ret, ==, 0);
271     ret = send_map(qts, v_iommu, 1, 5, 9, 0xb2000, VIRTIO_IOMMU_MAP_F_READ);
272     g_assert_cmpint(ret, ==, 0);
273     ret = send_unmap(qts, v_iommu, 1, 0, 9);
274     g_assert_cmpint(ret, ==, 0); /* unmaps [0,4] and [5,9] */
275 
276     /* 4 */
277     ret = send_map(qts, v_iommu, 1, 0, 9, 0xc1000, VIRTIO_IOMMU_MAP_F_READ);
278     g_assert_cmpint(ret, ==, 0);
279 
280     ret = send_unmap(qts, v_iommu, 1, 0, 4);
281     g_assert_cmpint(ret, ==, VIRTIO_IOMMU_S_RANGE); /* doesn't unmap anything */
282 
283     ret = send_unmap(qts, v_iommu, 1, 0, 10);
284     g_assert_cmpint(ret, ==, 0);
285 
286     /* 5 */
287     ret = send_map(qts, v_iommu, 1, 0, 4, 0xd1000, VIRTIO_IOMMU_MAP_F_READ);
288     g_assert_cmpint(ret, ==, 0);
289     ret = send_map(qts, v_iommu, 1, 5, 9, 0xd2000, VIRTIO_IOMMU_MAP_F_READ);
290     g_assert_cmpint(ret, ==, 0);
291     ret = send_unmap(qts, v_iommu, 1, 0, 4);
292     g_assert_cmpint(ret, ==, 0); /* unmaps [0,4] */
293 
294     ret = send_unmap(qts, v_iommu, 1, 5, 9);
295     g_assert_cmpint(ret, ==, 0);
296 
297     /* 6 */
298     ret = send_map(qts, v_iommu, 1, 0, 4, 0xe2000, VIRTIO_IOMMU_MAP_F_READ);
299     g_assert_cmpint(ret, ==, 0);
300     ret = send_unmap(qts, v_iommu, 1, 0, 9);
301     g_assert_cmpint(ret, ==, 0); /* unmaps [0,4] */
302 
303     /* 7 */
304     ret = send_map(qts, v_iommu, 1, 0, 4, 0xf2000, VIRTIO_IOMMU_MAP_F_READ);
305     g_assert_cmpint(ret, ==, 0);
306     ret = send_map(qts, v_iommu, 1, 10, 14, 0xf3000, VIRTIO_IOMMU_MAP_F_READ);
307     g_assert_cmpint(ret, ==, 0);
308     ret = send_unmap(qts, v_iommu, 1, 0, 14);
309     g_assert_cmpint(ret, ==, 0); /* unmaps [0,4] and [10,14] */
310 
311     ret = send_map(qts, v_iommu, 1, 10, 14, 0xf3000, VIRTIO_IOMMU_MAP_F_READ);
312     g_assert_cmpint(ret, ==, 0);
313     ret = send_map(qts, v_iommu, 1, 0, 4, 0xf2000, VIRTIO_IOMMU_MAP_F_READ);
314     g_assert_cmpint(ret, ==, 0);
315     ret = send_unmap(qts, v_iommu, 1, 0, 4);
316     g_assert_cmpint(ret, ==, 0); /* only unmaps [0,4] */
317     ret = send_map(qts, v_iommu, 1, 10, 14, 0xf3000, VIRTIO_IOMMU_MAP_F_READ);
318     g_assert_cmpint(ret, ==, VIRTIO_IOMMU_S_INVAL); /* 10-14 still is mapped */
319 }
320 
register_virtio_iommu_test(void)321 static void register_virtio_iommu_test(void)
322 {
323     qos_add_test("config", "virtio-iommu", pci_config, NULL);
324     qos_add_test("attach_detach", "virtio-iommu", test_attach_detach, NULL);
325     qos_add_test("map_unmap", "virtio-iommu", test_map_unmap, NULL);
326 }
327 
328 libqos_init(register_virtio_iommu_test);
329