qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

virtio-iommu-test.c (11588B)


      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 
     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, ==, UINT64_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 
     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  */
     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  */
    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  */
    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 
    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 */
    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 
    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);