qemu/tests/qtest/virtio-iommu-test.c
<<
>>
Prefs
   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
  24static QGuestAllocator *alloc;
  25
  26static 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
  43static 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 */
  59static 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 */
 100static 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 */
 143static 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
 177static 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 */
 224static 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
 321static 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
 328libqos_init(register_virtio_iommu_test);
 329