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