qemu

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

virtio-blk-test.c (24411B)


      1 /*
      2  * QTest testcase for VirtIO Block Device
      3  *
      4  * Copyright (c) 2014 SUSE LINUX Products GmbH
      5  * Copyright (c) 2014 Marc MarĂ­
      6  *
      7  * This work is licensed under the terms of the GNU GPL, version 2 or later.
      8  * See the COPYING file in the top-level directory.
      9  */
     10 
     11 #include "qemu/osdep.h"
     12 #include "libqtest-single.h"
     13 #include "qemu/bswap.h"
     14 #include "qemu/module.h"
     15 #include "standard-headers/linux/virtio_blk.h"
     16 #include "standard-headers/linux/virtio_pci.h"
     17 #include "libqos/qgraph.h"
     18 #include "libqos/virtio-blk.h"
     19 
     20 /* TODO actually test the results and get rid of this */
     21 #define qmp_discard_response(...) qobject_unref(qmp(__VA_ARGS__))
     22 
     23 #define TEST_IMAGE_SIZE         (64 * 1024 * 1024)
     24 #define QVIRTIO_BLK_TIMEOUT_US  (30 * 1000 * 1000)
     25 #define PCI_SLOT_HP             0x06
     26 
     27 typedef struct QVirtioBlkReq {
     28     uint32_t type;
     29     uint32_t ioprio;
     30     uint64_t sector;
     31     char *data;
     32     uint8_t status;
     33 } QVirtioBlkReq;
     34 
     35 
     36 #if HOST_BIG_ENDIAN
     37 const bool host_is_big_endian = true;
     38 #else
     39 const bool host_is_big_endian; /* false */
     40 #endif
     41 
     42 static void drive_destroy(void *path)
     43 {
     44     unlink(path);
     45     g_free(path);
     46     qos_invalidate_command_line();
     47 }
     48 
     49 static char *drive_create(void)
     50 {
     51     int fd, ret;
     52     char *t_path;
     53 
     54     /* Create a temporary raw image */
     55     fd = g_file_open_tmp("qtest.XXXXXX", &t_path, NULL);
     56     g_assert_cmpint(fd, >=, 0);
     57     ret = ftruncate(fd, TEST_IMAGE_SIZE);
     58     g_assert_cmpint(ret, ==, 0);
     59     close(fd);
     60 
     61     g_test_queue_destroy(drive_destroy, t_path);
     62     return t_path;
     63 }
     64 
     65 static inline void virtio_blk_fix_request(QVirtioDevice *d, QVirtioBlkReq *req)
     66 {
     67     if (qvirtio_is_big_endian(d) != host_is_big_endian) {
     68         req->type = bswap32(req->type);
     69         req->ioprio = bswap32(req->ioprio);
     70         req->sector = bswap64(req->sector);
     71     }
     72 }
     73 
     74 
     75 static inline void virtio_blk_fix_dwz_hdr(QVirtioDevice *d,
     76     struct virtio_blk_discard_write_zeroes *dwz_hdr)
     77 {
     78     if (qvirtio_is_big_endian(d) != host_is_big_endian) {
     79         dwz_hdr->sector = bswap64(dwz_hdr->sector);
     80         dwz_hdr->num_sectors = bswap32(dwz_hdr->num_sectors);
     81         dwz_hdr->flags = bswap32(dwz_hdr->flags);
     82     }
     83 }
     84 
     85 static uint64_t virtio_blk_request(QGuestAllocator *alloc, QVirtioDevice *d,
     86                                    QVirtioBlkReq *req, uint64_t data_size)
     87 {
     88     uint64_t addr;
     89     uint8_t status = 0xFF;
     90 
     91     switch (req->type) {
     92     case VIRTIO_BLK_T_IN:
     93     case VIRTIO_BLK_T_OUT:
     94         g_assert_cmpuint(data_size % 512, ==, 0);
     95         break;
     96     case VIRTIO_BLK_T_DISCARD:
     97     case VIRTIO_BLK_T_WRITE_ZEROES:
     98         g_assert_cmpuint(data_size %
     99                          sizeof(struct virtio_blk_discard_write_zeroes), ==, 0);
    100         break;
    101     default:
    102         g_assert_cmpuint(data_size, ==, 0);
    103     }
    104 
    105     addr = guest_alloc(alloc, sizeof(*req) + data_size);
    106 
    107     virtio_blk_fix_request(d, req);
    108 
    109     memwrite(addr, req, 16);
    110     memwrite(addr + 16, req->data, data_size);
    111     memwrite(addr + 16 + data_size, &status, sizeof(status));
    112 
    113     return addr;
    114 }
    115 
    116 /* Returns the request virtqueue so the caller can perform further tests */
    117 static QVirtQueue *test_basic(QVirtioDevice *dev, QGuestAllocator *alloc)
    118 {
    119     QVirtioBlkReq req;
    120     uint64_t req_addr;
    121     uint64_t capacity;
    122     uint64_t features;
    123     uint32_t free_head;
    124     uint8_t status;
    125     char *data;
    126     QTestState *qts = global_qtest;
    127     QVirtQueue *vq;
    128 
    129     features = qvirtio_get_features(dev);
    130     features = features & ~(QVIRTIO_F_BAD_FEATURE |
    131                     (1u << VIRTIO_RING_F_INDIRECT_DESC) |
    132                     (1u << VIRTIO_RING_F_EVENT_IDX) |
    133                     (1u << VIRTIO_BLK_F_SCSI));
    134     qvirtio_set_features(dev, features);
    135 
    136     capacity = qvirtio_config_readq(dev, 0);
    137     g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
    138 
    139     vq = qvirtqueue_setup(dev, alloc, 0);
    140 
    141     qvirtio_set_driver_ok(dev);
    142 
    143     /* Write and read with 3 descriptor layout */
    144     /* Write request */
    145     req.type = VIRTIO_BLK_T_OUT;
    146     req.ioprio = 1;
    147     req.sector = 0;
    148     req.data = g_malloc0(512);
    149     strcpy(req.data, "TEST");
    150 
    151     req_addr = virtio_blk_request(alloc, dev, &req, 512);
    152 
    153     g_free(req.data);
    154 
    155     free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
    156     qvirtqueue_add(qts, vq, req_addr + 16, 512, false, true);
    157     qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
    158 
    159     qvirtqueue_kick(qts, dev, vq, free_head);
    160 
    161     qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
    162                            QVIRTIO_BLK_TIMEOUT_US);
    163     status = readb(req_addr + 528);
    164     g_assert_cmpint(status, ==, 0);
    165 
    166     guest_free(alloc, req_addr);
    167 
    168     /* Read request */
    169     req.type = VIRTIO_BLK_T_IN;
    170     req.ioprio = 1;
    171     req.sector = 0;
    172     req.data = g_malloc0(512);
    173 
    174     req_addr = virtio_blk_request(alloc, dev, &req, 512);
    175 
    176     g_free(req.data);
    177 
    178     free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
    179     qvirtqueue_add(qts, vq, req_addr + 16, 512, true, true);
    180     qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
    181 
    182     qvirtqueue_kick(qts, dev, vq, free_head);
    183 
    184     qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
    185                            QVIRTIO_BLK_TIMEOUT_US);
    186     status = readb(req_addr + 528);
    187     g_assert_cmpint(status, ==, 0);
    188 
    189     data = g_malloc0(512);
    190     memread(req_addr + 16, data, 512);
    191     g_assert_cmpstr(data, ==, "TEST");
    192     g_free(data);
    193 
    194     guest_free(alloc, req_addr);
    195 
    196     if (features & (1u << VIRTIO_BLK_F_WRITE_ZEROES)) {
    197         struct virtio_blk_discard_write_zeroes dwz_hdr;
    198         void *expected;
    199 
    200         /*
    201          * WRITE_ZEROES request on the same sector of previous test where
    202          * we wrote "TEST".
    203          */
    204         req.type = VIRTIO_BLK_T_WRITE_ZEROES;
    205         req.data = (char *) &dwz_hdr;
    206         dwz_hdr.sector = 0;
    207         dwz_hdr.num_sectors = 1;
    208         dwz_hdr.flags = 0;
    209 
    210         virtio_blk_fix_dwz_hdr(dev, &dwz_hdr);
    211 
    212         req_addr = virtio_blk_request(alloc, dev, &req, sizeof(dwz_hdr));
    213 
    214         free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
    215         qvirtqueue_add(qts, vq, req_addr + 16, sizeof(dwz_hdr), false, true);
    216         qvirtqueue_add(qts, vq, req_addr + 16 + sizeof(dwz_hdr), 1, true,
    217                        false);
    218 
    219         qvirtqueue_kick(qts, dev, vq, free_head);
    220 
    221         qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
    222                                QVIRTIO_BLK_TIMEOUT_US);
    223         status = readb(req_addr + 16 + sizeof(dwz_hdr));
    224         g_assert_cmpint(status, ==, 0);
    225 
    226         guest_free(alloc, req_addr);
    227 
    228         /* Read request to check if the sector contains all zeroes */
    229         req.type = VIRTIO_BLK_T_IN;
    230         req.ioprio = 1;
    231         req.sector = 0;
    232         req.data = g_malloc0(512);
    233 
    234         req_addr = virtio_blk_request(alloc, dev, &req, 512);
    235 
    236         g_free(req.data);
    237 
    238         free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
    239         qvirtqueue_add(qts, vq, req_addr + 16, 512, true, true);
    240         qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
    241 
    242         qvirtqueue_kick(qts, dev, vq, free_head);
    243 
    244         qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
    245                                QVIRTIO_BLK_TIMEOUT_US);
    246         status = readb(req_addr + 528);
    247         g_assert_cmpint(status, ==, 0);
    248 
    249         data = g_malloc(512);
    250         expected = g_malloc0(512);
    251         memread(req_addr + 16, data, 512);
    252         g_assert_cmpmem(data, 512, expected, 512);
    253         g_free(expected);
    254         g_free(data);
    255 
    256         guest_free(alloc, req_addr);
    257     }
    258 
    259     if (features & (1u << VIRTIO_BLK_F_DISCARD)) {
    260         struct virtio_blk_discard_write_zeroes dwz_hdr;
    261 
    262         req.type = VIRTIO_BLK_T_DISCARD;
    263         req.data = (char *) &dwz_hdr;
    264         dwz_hdr.sector = 0;
    265         dwz_hdr.num_sectors = 1;
    266         dwz_hdr.flags = 0;
    267 
    268         virtio_blk_fix_dwz_hdr(dev, &dwz_hdr);
    269 
    270         req_addr = virtio_blk_request(alloc, dev, &req, sizeof(dwz_hdr));
    271 
    272         free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
    273         qvirtqueue_add(qts, vq, req_addr + 16, sizeof(dwz_hdr), false, true);
    274         qvirtqueue_add(qts, vq, req_addr + 16 + sizeof(dwz_hdr), 1, true, false);
    275 
    276         qvirtqueue_kick(qts, dev, vq, free_head);
    277 
    278         qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
    279                                QVIRTIO_BLK_TIMEOUT_US);
    280         status = readb(req_addr + 16 + sizeof(dwz_hdr));
    281         g_assert_cmpint(status, ==, 0);
    282 
    283         guest_free(alloc, req_addr);
    284     }
    285 
    286     if (features & (1u << VIRTIO_F_ANY_LAYOUT)) {
    287         /* Write and read with 2 descriptor layout */
    288         /* Write request */
    289         req.type = VIRTIO_BLK_T_OUT;
    290         req.ioprio = 1;
    291         req.sector = 1;
    292         req.data = g_malloc0(512);
    293         strcpy(req.data, "TEST");
    294 
    295         req_addr = virtio_blk_request(alloc, dev, &req, 512);
    296 
    297         g_free(req.data);
    298 
    299         free_head = qvirtqueue_add(qts, vq, req_addr, 528, false, true);
    300         qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
    301         qvirtqueue_kick(qts, dev, vq, free_head);
    302 
    303         qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
    304                                QVIRTIO_BLK_TIMEOUT_US);
    305         status = readb(req_addr + 528);
    306         g_assert_cmpint(status, ==, 0);
    307 
    308         guest_free(alloc, req_addr);
    309 
    310         /* Read request */
    311         req.type = VIRTIO_BLK_T_IN;
    312         req.ioprio = 1;
    313         req.sector = 1;
    314         req.data = g_malloc0(512);
    315 
    316         req_addr = virtio_blk_request(alloc, dev, &req, 512);
    317 
    318         g_free(req.data);
    319 
    320         free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
    321         qvirtqueue_add(qts, vq, req_addr + 16, 513, true, false);
    322 
    323         qvirtqueue_kick(qts, dev, vq, free_head);
    324 
    325         qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
    326                                QVIRTIO_BLK_TIMEOUT_US);
    327         status = readb(req_addr + 528);
    328         g_assert_cmpint(status, ==, 0);
    329 
    330         data = g_malloc0(512);
    331         memread(req_addr + 16, data, 512);
    332         g_assert_cmpstr(data, ==, "TEST");
    333         g_free(data);
    334 
    335         guest_free(alloc, req_addr);
    336     }
    337 
    338     return vq;
    339 }
    340 
    341 static void basic(void *obj, void *data, QGuestAllocator *t_alloc)
    342 {
    343     QVirtioBlk *blk_if = obj;
    344     QVirtQueue *vq;
    345 
    346     vq = test_basic(blk_if->vdev, t_alloc);
    347     qvirtqueue_cleanup(blk_if->vdev->bus, vq, t_alloc);
    348 
    349 }
    350 
    351 static void indirect(void *obj, void *u_data, QGuestAllocator *t_alloc)
    352 {
    353     QVirtQueue *vq;
    354     QVirtioBlk *blk_if = obj;
    355     QVirtioDevice *dev = blk_if->vdev;
    356     QVirtioBlkReq req;
    357     QVRingIndirectDesc *indirect;
    358     uint64_t req_addr;
    359     uint64_t capacity;
    360     uint64_t features;
    361     uint32_t free_head;
    362     uint8_t status;
    363     char *data;
    364     QTestState *qts = global_qtest;
    365 
    366     features = qvirtio_get_features(dev);
    367     g_assert_cmphex(features & (1u << VIRTIO_RING_F_INDIRECT_DESC), !=, 0);
    368     features = features & ~(QVIRTIO_F_BAD_FEATURE |
    369                             (1u << VIRTIO_RING_F_EVENT_IDX) |
    370                             (1u << VIRTIO_BLK_F_SCSI));
    371     qvirtio_set_features(dev, features);
    372 
    373     capacity = qvirtio_config_readq(dev, 0);
    374     g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
    375 
    376     vq = qvirtqueue_setup(dev, t_alloc, 0);
    377     qvirtio_set_driver_ok(dev);
    378 
    379     /* Write request */
    380     req.type = VIRTIO_BLK_T_OUT;
    381     req.ioprio = 1;
    382     req.sector = 0;
    383     req.data = g_malloc0(512);
    384     strcpy(req.data, "TEST");
    385 
    386     req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
    387 
    388     g_free(req.data);
    389 
    390     indirect = qvring_indirect_desc_setup(qts, dev, t_alloc, 2);
    391     qvring_indirect_desc_add(dev, qts, indirect, req_addr, 528, false);
    392     qvring_indirect_desc_add(dev, qts, indirect, req_addr + 528, 1, true);
    393     free_head = qvirtqueue_add_indirect(qts, vq, indirect);
    394     qvirtqueue_kick(qts, dev, vq, free_head);
    395 
    396     qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
    397                            QVIRTIO_BLK_TIMEOUT_US);
    398     status = readb(req_addr + 528);
    399     g_assert_cmpint(status, ==, 0);
    400 
    401     g_free(indirect);
    402     guest_free(t_alloc, req_addr);
    403 
    404     /* Read request */
    405     req.type = VIRTIO_BLK_T_IN;
    406     req.ioprio = 1;
    407     req.sector = 0;
    408     req.data = g_malloc0(512);
    409     strcpy(req.data, "TEST");
    410 
    411     req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
    412 
    413     g_free(req.data);
    414 
    415     indirect = qvring_indirect_desc_setup(qts, dev, t_alloc, 2);
    416     qvring_indirect_desc_add(dev, qts, indirect, req_addr, 16, false);
    417     qvring_indirect_desc_add(dev, qts, indirect, req_addr + 16, 513, true);
    418     free_head = qvirtqueue_add_indirect(qts, vq, indirect);
    419     qvirtqueue_kick(qts, dev, vq, free_head);
    420 
    421     qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
    422                            QVIRTIO_BLK_TIMEOUT_US);
    423     status = readb(req_addr + 528);
    424     g_assert_cmpint(status, ==, 0);
    425 
    426     data = g_malloc0(512);
    427     memread(req_addr + 16, data, 512);
    428     g_assert_cmpstr(data, ==, "TEST");
    429     g_free(data);
    430 
    431     g_free(indirect);
    432     guest_free(t_alloc, req_addr);
    433     qvirtqueue_cleanup(dev->bus, vq, t_alloc);
    434 }
    435 
    436 static void config(void *obj, void *data, QGuestAllocator *t_alloc)
    437 {
    438     QVirtioBlk *blk_if = obj;
    439     QVirtioDevice *dev = blk_if->vdev;
    440     int n_size = TEST_IMAGE_SIZE / 2;
    441     uint64_t features;
    442     uint64_t capacity;
    443 
    444     features = qvirtio_get_features(dev);
    445     features = features & ~(QVIRTIO_F_BAD_FEATURE |
    446                             (1u << VIRTIO_RING_F_INDIRECT_DESC) |
    447                             (1u << VIRTIO_RING_F_EVENT_IDX) |
    448                             (1u << VIRTIO_BLK_F_SCSI));
    449     qvirtio_set_features(dev, features);
    450 
    451     capacity = qvirtio_config_readq(dev, 0);
    452     g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
    453 
    454     qvirtio_set_driver_ok(dev);
    455 
    456     qmp_discard_response("{ 'execute': 'block_resize', "
    457                          " 'arguments': { 'device': 'drive0', "
    458                          " 'size': %d } }", n_size);
    459     qvirtio_wait_config_isr(dev, QVIRTIO_BLK_TIMEOUT_US);
    460 
    461     capacity = qvirtio_config_readq(dev, 0);
    462     g_assert_cmpint(capacity, ==, n_size / 512);
    463 }
    464 
    465 static void msix(void *obj, void *u_data, QGuestAllocator *t_alloc)
    466 {
    467     QVirtQueue *vq;
    468     QVirtioBlkPCI *blk = obj;
    469     QVirtioPCIDevice *pdev = &blk->pci_vdev;
    470     QVirtioDevice *dev = &pdev->vdev;
    471     QVirtioBlkReq req;
    472     int n_size = TEST_IMAGE_SIZE / 2;
    473     uint64_t req_addr;
    474     uint64_t capacity;
    475     uint64_t features;
    476     uint32_t free_head;
    477     uint8_t status;
    478     char *data;
    479     QOSGraphObject *blk_object = obj;
    480     QPCIDevice *pci_dev = blk_object->get_driver(blk_object, "pci-device");
    481     QTestState *qts = global_qtest;
    482 
    483     if (qpci_check_buggy_msi(pci_dev)) {
    484         return;
    485     }
    486 
    487     qpci_msix_enable(pdev->pdev);
    488     qvirtio_pci_set_msix_configuration_vector(pdev, t_alloc, 0);
    489 
    490     features = qvirtio_get_features(dev);
    491     features = features & ~(QVIRTIO_F_BAD_FEATURE |
    492                             (1u << VIRTIO_RING_F_INDIRECT_DESC) |
    493                             (1u << VIRTIO_RING_F_EVENT_IDX) |
    494                             (1u << VIRTIO_BLK_F_SCSI));
    495     qvirtio_set_features(dev, features);
    496 
    497     capacity = qvirtio_config_readq(dev, 0);
    498     g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
    499 
    500     vq = qvirtqueue_setup(dev, t_alloc, 0);
    501     qvirtqueue_pci_msix_setup(pdev, (QVirtQueuePCI *)vq, t_alloc, 1);
    502 
    503     qvirtio_set_driver_ok(dev);
    504 
    505     qmp_discard_response("{ 'execute': 'block_resize', "
    506                          " 'arguments': { 'device': 'drive0', "
    507                          " 'size': %d } }", n_size);
    508 
    509     qvirtio_wait_config_isr(dev, QVIRTIO_BLK_TIMEOUT_US);
    510 
    511     capacity = qvirtio_config_readq(dev, 0);
    512     g_assert_cmpint(capacity, ==, n_size / 512);
    513 
    514     /* Write request */
    515     req.type = VIRTIO_BLK_T_OUT;
    516     req.ioprio = 1;
    517     req.sector = 0;
    518     req.data = g_malloc0(512);
    519     strcpy(req.data, "TEST");
    520 
    521     req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
    522 
    523     g_free(req.data);
    524 
    525     free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
    526     qvirtqueue_add(qts, vq, req_addr + 16, 512, false, true);
    527     qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
    528     qvirtqueue_kick(qts, dev, vq, free_head);
    529 
    530     qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
    531                            QVIRTIO_BLK_TIMEOUT_US);
    532 
    533     status = readb(req_addr + 528);
    534     g_assert_cmpint(status, ==, 0);
    535 
    536     guest_free(t_alloc, req_addr);
    537 
    538     /* Read request */
    539     req.type = VIRTIO_BLK_T_IN;
    540     req.ioprio = 1;
    541     req.sector = 0;
    542     req.data = g_malloc0(512);
    543 
    544     req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
    545 
    546     g_free(req.data);
    547 
    548     free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
    549     qvirtqueue_add(qts, vq, req_addr + 16, 512, true, true);
    550     qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
    551 
    552     qvirtqueue_kick(qts, dev, vq, free_head);
    553 
    554 
    555     qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
    556                            QVIRTIO_BLK_TIMEOUT_US);
    557 
    558     status = readb(req_addr + 528);
    559     g_assert_cmpint(status, ==, 0);
    560 
    561     data = g_malloc0(512);
    562     memread(req_addr + 16, data, 512);
    563     g_assert_cmpstr(data, ==, "TEST");
    564     g_free(data);
    565 
    566     guest_free(t_alloc, req_addr);
    567 
    568     /* End test */
    569     qpci_msix_disable(pdev->pdev);
    570     qvirtqueue_cleanup(dev->bus, vq, t_alloc);
    571 }
    572 
    573 static void idx(void *obj, void *u_data, QGuestAllocator *t_alloc)
    574 {
    575     QVirtQueue *vq;
    576     QVirtioBlkPCI *blk = obj;
    577     QVirtioPCIDevice *pdev = &blk->pci_vdev;
    578     QVirtioDevice *dev = &pdev->vdev;
    579     QVirtioBlkReq req;
    580     uint64_t req_addr;
    581     uint64_t capacity;
    582     uint64_t features;
    583     uint32_t free_head;
    584     uint32_t write_head;
    585     uint32_t desc_idx;
    586     uint8_t status;
    587     char *data;
    588     QOSGraphObject *blk_object = obj;
    589     QPCIDevice *pci_dev = blk_object->get_driver(blk_object, "pci-device");
    590     QTestState *qts = global_qtest;
    591 
    592     if (qpci_check_buggy_msi(pci_dev)) {
    593         return;
    594     }
    595 
    596     qpci_msix_enable(pdev->pdev);
    597     qvirtio_pci_set_msix_configuration_vector(pdev, t_alloc, 0);
    598 
    599     features = qvirtio_get_features(dev);
    600     features = features & ~(QVIRTIO_F_BAD_FEATURE |
    601                             (1u << VIRTIO_RING_F_INDIRECT_DESC) |
    602                             (1u << VIRTIO_F_NOTIFY_ON_EMPTY) |
    603                             (1u << VIRTIO_BLK_F_SCSI));
    604     qvirtio_set_features(dev, features);
    605 
    606     capacity = qvirtio_config_readq(dev, 0);
    607     g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
    608 
    609     vq = qvirtqueue_setup(dev, t_alloc, 0);
    610     qvirtqueue_pci_msix_setup(pdev, (QVirtQueuePCI *)vq, t_alloc, 1);
    611 
    612     qvirtio_set_driver_ok(dev);
    613 
    614     /* Write request */
    615     req.type = VIRTIO_BLK_T_OUT;
    616     req.ioprio = 1;
    617     req.sector = 0;
    618     req.data = g_malloc0(512);
    619     strcpy(req.data, "TEST");
    620 
    621     req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
    622 
    623     g_free(req.data);
    624 
    625     free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
    626     qvirtqueue_add(qts, vq, req_addr + 16, 512, false, true);
    627     qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
    628     qvirtqueue_kick(qts, dev, vq, free_head);
    629 
    630     qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
    631                            QVIRTIO_BLK_TIMEOUT_US);
    632 
    633     /* Write request */
    634     req.type = VIRTIO_BLK_T_OUT;
    635     req.ioprio = 1;
    636     req.sector = 1;
    637     req.data = g_malloc0(512);
    638     strcpy(req.data, "TEST");
    639 
    640     req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
    641 
    642     g_free(req.data);
    643 
    644     /* Notify after processing the third request */
    645     qvirtqueue_set_used_event(qts, vq, 2);
    646     free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
    647     qvirtqueue_add(qts, vq, req_addr + 16, 512, false, true);
    648     qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
    649     qvirtqueue_kick(qts, dev, vq, free_head);
    650     write_head = free_head;
    651 
    652     /* No notification expected */
    653     status = qvirtio_wait_status_byte_no_isr(qts, dev,
    654                                              vq, req_addr + 528,
    655                                              QVIRTIO_BLK_TIMEOUT_US);
    656     g_assert_cmpint(status, ==, 0);
    657 
    658     guest_free(t_alloc, req_addr);
    659 
    660     /* Read request */
    661     req.type = VIRTIO_BLK_T_IN;
    662     req.ioprio = 1;
    663     req.sector = 1;
    664     req.data = g_malloc0(512);
    665 
    666     req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
    667 
    668     g_free(req.data);
    669 
    670     free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
    671     qvirtqueue_add(qts, vq, req_addr + 16, 512, true, true);
    672     qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
    673 
    674     qvirtqueue_kick(qts, dev, vq, free_head);
    675 
    676     /* We get just one notification for both requests */
    677     qvirtio_wait_used_elem(qts, dev, vq, write_head, NULL,
    678                            QVIRTIO_BLK_TIMEOUT_US);
    679     g_assert(qvirtqueue_get_buf(qts, vq, &desc_idx, NULL));
    680     g_assert_cmpint(desc_idx, ==, free_head);
    681 
    682     status = readb(req_addr + 528);
    683     g_assert_cmpint(status, ==, 0);
    684 
    685     data = g_malloc0(512);
    686     memread(req_addr + 16, data, 512);
    687     g_assert_cmpstr(data, ==, "TEST");
    688     g_free(data);
    689 
    690     guest_free(t_alloc, req_addr);
    691 
    692     /* End test */
    693     qpci_msix_disable(pdev->pdev);
    694 
    695     qvirtqueue_cleanup(dev->bus, vq, t_alloc);
    696 }
    697 
    698 static void pci_hotplug(void *obj, void *data, QGuestAllocator *t_alloc)
    699 {
    700     QVirtioPCIDevice *dev1 = obj;
    701     QVirtioPCIDevice *dev;
    702     QTestState *qts = dev1->pdev->bus->qts;
    703 
    704     if (dev1->pdev->bus->not_hotpluggable) {
    705         g_test_skip("pci bus does not support hotplug");
    706         return;
    707     }
    708 
    709     /* plug secondary disk */
    710     qtest_qmp_device_add(qts, "virtio-blk-pci", "drv1",
    711                          "{'addr': %s, 'drive': 'drive1'}",
    712                          stringify(PCI_SLOT_HP) ".0");
    713 
    714     dev = virtio_pci_new(dev1->pdev->bus,
    715                          &(QPCIAddress) { .devfn = QPCI_DEVFN(PCI_SLOT_HP, 0) });
    716     g_assert_nonnull(dev);
    717     g_assert_cmpint(dev->vdev.device_type, ==, VIRTIO_ID_BLOCK);
    718     qvirtio_pci_device_disable(dev);
    719     qos_object_destroy((QOSGraphObject *)dev);
    720 
    721     /* unplug secondary disk */
    722     qpci_unplug_acpi_device_test(qts, "drv1", PCI_SLOT_HP);
    723 }
    724 
    725 /*
    726  * Check that setting the vring addr on a non-existent virtqueue does
    727  * not crash.
    728  */
    729 static void test_nonexistent_virtqueue(void *obj, void *data,
    730                                        QGuestAllocator *t_alloc)
    731 {
    732     QVirtioBlkPCI *blk = obj;
    733     QVirtioPCIDevice *pdev = &blk->pci_vdev;
    734     QPCIBar bar0;
    735     QPCIDevice *dev;
    736 
    737     dev = qpci_device_find(pdev->pdev->bus, QPCI_DEVFN(4, 0));
    738     g_assert(dev != NULL);
    739     qpci_device_enable(dev);
    740 
    741     bar0 = qpci_iomap(dev, 0, NULL);
    742 
    743     qpci_io_writeb(dev, bar0, VIRTIO_PCI_QUEUE_SEL, 2);
    744     qpci_io_writel(dev, bar0, VIRTIO_PCI_QUEUE_PFN, 1);
    745 
    746 
    747     g_free(dev);
    748 }
    749 
    750 static void resize(void *obj, void *data, QGuestAllocator *t_alloc)
    751 {
    752     QVirtioBlk *blk_if = obj;
    753     QVirtioDevice *dev = blk_if->vdev;
    754     int n_size = TEST_IMAGE_SIZE / 2;
    755     uint64_t capacity;
    756     QVirtQueue *vq;
    757     QTestState *qts = global_qtest;
    758 
    759     vq = test_basic(dev, t_alloc);
    760 
    761     qmp_discard_response("{ 'execute': 'block_resize', "
    762                          " 'arguments': { 'device': 'drive0', "
    763                          " 'size': %d } }", n_size);
    764 
    765     qvirtio_wait_queue_isr(qts, dev, vq, QVIRTIO_BLK_TIMEOUT_US);
    766 
    767     capacity = qvirtio_config_readq(dev, 0);
    768     g_assert_cmpint(capacity, ==, n_size / 512);
    769 
    770     qvirtqueue_cleanup(dev->bus, vq, t_alloc);
    771 
    772 }
    773 
    774 static void *virtio_blk_test_setup(GString *cmd_line, void *arg)
    775 {
    776     char *tmp_path = drive_create();
    777 
    778     g_string_append_printf(cmd_line,
    779                            " -drive if=none,id=drive0,file=%s,"
    780                            "format=raw,auto-read-only=off "
    781                            "-drive if=none,id=drive1,file=null-co://,"
    782                            "file.read-zeroes=on,format=raw ",
    783                            tmp_path);
    784 
    785     return arg;
    786 }
    787 
    788 static void register_virtio_blk_test(void)
    789 {
    790     QOSGraphTestOptions opts = {
    791         .before = virtio_blk_test_setup,
    792     };
    793 
    794     qos_add_test("indirect", "virtio-blk", indirect, &opts);
    795     qos_add_test("config", "virtio-blk", config, &opts);
    796     qos_add_test("basic", "virtio-blk", basic, &opts);
    797     qos_add_test("resize", "virtio-blk", resize, &opts);
    798 
    799     /* tests just for virtio-blk-pci */
    800     qos_add_test("msix", "virtio-blk-pci", msix, &opts);
    801     qos_add_test("idx", "virtio-blk-pci", idx, &opts);
    802     qos_add_test("nxvirtq", "virtio-blk-pci",
    803                       test_nonexistent_virtqueue, &opts);
    804     qos_add_test("hotplug", "virtio-blk-pci", pci_hotplug, &opts);
    805 }
    806 
    807 libqos_init(register_virtio_blk_test);