xref: /openbmc/qemu/tests/qtest/virtio-blk-test.c (revision 855436db)
11e8a1faeSThomas Huth /*
21e8a1faeSThomas Huth  * QTest testcase for VirtIO Block Device
31e8a1faeSThomas Huth  *
41e8a1faeSThomas Huth  * Copyright (c) 2014 SUSE LINUX Products GmbH
51e8a1faeSThomas Huth  * Copyright (c) 2014 Marc Marí
61e8a1faeSThomas Huth  *
71e8a1faeSThomas Huth  * This work is licensed under the terms of the GNU GPL, version 2 or later.
81e8a1faeSThomas Huth  * See the COPYING file in the top-level directory.
91e8a1faeSThomas Huth  */
101e8a1faeSThomas Huth 
111e8a1faeSThomas Huth #include "qemu/osdep.h"
121e8a1faeSThomas Huth #include "libqtest-single.h"
131e8a1faeSThomas Huth #include "qemu/bswap.h"
141e8a1faeSThomas Huth #include "qemu/module.h"
151e8a1faeSThomas Huth #include "standard-headers/linux/virtio_blk.h"
161e8a1faeSThomas Huth #include "standard-headers/linux/virtio_pci.h"
171e8a1faeSThomas Huth #include "libqos/qgraph.h"
181e8a1faeSThomas Huth #include "libqos/virtio-blk.h"
191e8a1faeSThomas Huth 
201e8a1faeSThomas Huth #define TEST_IMAGE_SIZE         (64 * 1024 * 1024)
211e8a1faeSThomas Huth #define QVIRTIO_BLK_TIMEOUT_US  (30 * 1000 * 1000)
221e8a1faeSThomas Huth #define PCI_SLOT_HP             0x06
231e8a1faeSThomas Huth 
241e8a1faeSThomas Huth typedef struct QVirtioBlkReq {
251e8a1faeSThomas Huth     uint32_t type;
261e8a1faeSThomas Huth     uint32_t ioprio;
271e8a1faeSThomas Huth     uint64_t sector;
281e8a1faeSThomas Huth     char *data;
291e8a1faeSThomas Huth     uint8_t status;
301e8a1faeSThomas Huth } QVirtioBlkReq;
311e8a1faeSThomas Huth 
321e8a1faeSThomas Huth 
33e03b5686SMarc-André Lureau #if HOST_BIG_ENDIAN
341e8a1faeSThomas Huth const bool host_is_big_endian = true;
351e8a1faeSThomas Huth #else
361e8a1faeSThomas Huth const bool host_is_big_endian; /* false */
371e8a1faeSThomas Huth #endif
381e8a1faeSThomas Huth 
drive_destroy(void * path)391e8a1faeSThomas Huth static void drive_destroy(void *path)
401e8a1faeSThomas Huth {
411e8a1faeSThomas Huth     unlink(path);
421e8a1faeSThomas Huth     g_free(path);
431e8a1faeSThomas Huth     qos_invalidate_command_line();
441e8a1faeSThomas Huth }
451e8a1faeSThomas Huth 
drive_create(void)461e8a1faeSThomas Huth static char *drive_create(void)
471e8a1faeSThomas Huth {
481e8a1faeSThomas Huth     int fd, ret;
49bc989a2bSBin Meng     char *t_path;
501e8a1faeSThomas Huth 
511e8a1faeSThomas Huth     /* Create a temporary raw image */
52bc989a2bSBin Meng     fd = g_file_open_tmp("qtest.XXXXXX", &t_path, NULL);
531e8a1faeSThomas Huth     g_assert_cmpint(fd, >=, 0);
541e8a1faeSThomas Huth     ret = ftruncate(fd, TEST_IMAGE_SIZE);
551e8a1faeSThomas Huth     g_assert_cmpint(ret, ==, 0);
561e8a1faeSThomas Huth     close(fd);
571e8a1faeSThomas Huth 
581e8a1faeSThomas Huth     g_test_queue_destroy(drive_destroy, t_path);
591e8a1faeSThomas Huth     return t_path;
601e8a1faeSThomas Huth }
611e8a1faeSThomas Huth 
virtio_blk_fix_request(QVirtioDevice * d,QVirtioBlkReq * req)621e8a1faeSThomas Huth static inline void virtio_blk_fix_request(QVirtioDevice *d, QVirtioBlkReq *req)
631e8a1faeSThomas Huth {
641e8a1faeSThomas Huth     if (qvirtio_is_big_endian(d) != host_is_big_endian) {
651e8a1faeSThomas Huth         req->type = bswap32(req->type);
661e8a1faeSThomas Huth         req->ioprio = bswap32(req->ioprio);
671e8a1faeSThomas Huth         req->sector = bswap64(req->sector);
681e8a1faeSThomas Huth     }
691e8a1faeSThomas Huth }
701e8a1faeSThomas Huth 
711e8a1faeSThomas Huth 
virtio_blk_fix_dwz_hdr(QVirtioDevice * d,struct virtio_blk_discard_write_zeroes * dwz_hdr)721e8a1faeSThomas Huth static inline void virtio_blk_fix_dwz_hdr(QVirtioDevice *d,
731e8a1faeSThomas Huth     struct virtio_blk_discard_write_zeroes *dwz_hdr)
741e8a1faeSThomas Huth {
751e8a1faeSThomas Huth     if (qvirtio_is_big_endian(d) != host_is_big_endian) {
761e8a1faeSThomas Huth         dwz_hdr->sector = bswap64(dwz_hdr->sector);
771e8a1faeSThomas Huth         dwz_hdr->num_sectors = bswap32(dwz_hdr->num_sectors);
781e8a1faeSThomas Huth         dwz_hdr->flags = bswap32(dwz_hdr->flags);
791e8a1faeSThomas Huth     }
801e8a1faeSThomas Huth }
811e8a1faeSThomas Huth 
virtio_blk_request(QGuestAllocator * alloc,QVirtioDevice * d,QVirtioBlkReq * req,uint64_t data_size)821e8a1faeSThomas Huth static uint64_t virtio_blk_request(QGuestAllocator *alloc, QVirtioDevice *d,
831e8a1faeSThomas Huth                                    QVirtioBlkReq *req, uint64_t data_size)
841e8a1faeSThomas Huth {
851e8a1faeSThomas Huth     uint64_t addr;
861e8a1faeSThomas Huth     uint8_t status = 0xFF;
871e8a1faeSThomas Huth 
881e8a1faeSThomas Huth     switch (req->type) {
891e8a1faeSThomas Huth     case VIRTIO_BLK_T_IN:
901e8a1faeSThomas Huth     case VIRTIO_BLK_T_OUT:
911e8a1faeSThomas Huth         g_assert_cmpuint(data_size % 512, ==, 0);
921e8a1faeSThomas Huth         break;
931e8a1faeSThomas Huth     case VIRTIO_BLK_T_DISCARD:
941e8a1faeSThomas Huth     case VIRTIO_BLK_T_WRITE_ZEROES:
951e8a1faeSThomas Huth         g_assert_cmpuint(data_size %
961e8a1faeSThomas Huth                          sizeof(struct virtio_blk_discard_write_zeroes), ==, 0);
971e8a1faeSThomas Huth         break;
981e8a1faeSThomas Huth     default:
991e8a1faeSThomas Huth         g_assert_cmpuint(data_size, ==, 0);
1001e8a1faeSThomas Huth     }
1011e8a1faeSThomas Huth 
1021e8a1faeSThomas Huth     addr = guest_alloc(alloc, sizeof(*req) + data_size);
1031e8a1faeSThomas Huth 
1041e8a1faeSThomas Huth     virtio_blk_fix_request(d, req);
1051e8a1faeSThomas Huth 
1061e8a1faeSThomas Huth     memwrite(addr, req, 16);
1071e8a1faeSThomas Huth     memwrite(addr + 16, req->data, data_size);
1081e8a1faeSThomas Huth     memwrite(addr + 16 + data_size, &status, sizeof(status));
1091e8a1faeSThomas Huth 
1101e8a1faeSThomas Huth     return addr;
1111e8a1faeSThomas Huth }
1121e8a1faeSThomas Huth 
1131e8a1faeSThomas Huth /* Returns the request virtqueue so the caller can perform further tests */
test_basic(QVirtioDevice * dev,QGuestAllocator * alloc)1141e8a1faeSThomas Huth static QVirtQueue *test_basic(QVirtioDevice *dev, QGuestAllocator *alloc)
1151e8a1faeSThomas Huth {
1161e8a1faeSThomas Huth     QVirtioBlkReq req;
1171e8a1faeSThomas Huth     uint64_t req_addr;
1181e8a1faeSThomas Huth     uint64_t capacity;
1191e8a1faeSThomas Huth     uint64_t features;
1201e8a1faeSThomas Huth     uint32_t free_head;
1211e8a1faeSThomas Huth     uint8_t status;
1221e8a1faeSThomas Huth     char *data;
1231e8a1faeSThomas Huth     QTestState *qts = global_qtest;
1241e8a1faeSThomas Huth     QVirtQueue *vq;
1251e8a1faeSThomas Huth 
1261e8a1faeSThomas Huth     features = qvirtio_get_features(dev);
1271e8a1faeSThomas Huth     features = features & ~(QVIRTIO_F_BAD_FEATURE |
1281e8a1faeSThomas Huth                     (1u << VIRTIO_RING_F_INDIRECT_DESC) |
1291e8a1faeSThomas Huth                     (1u << VIRTIO_RING_F_EVENT_IDX) |
1301e8a1faeSThomas Huth                     (1u << VIRTIO_BLK_F_SCSI));
1311e8a1faeSThomas Huth     qvirtio_set_features(dev, features);
1321e8a1faeSThomas Huth 
1331e8a1faeSThomas Huth     capacity = qvirtio_config_readq(dev, 0);
1341e8a1faeSThomas Huth     g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
1351e8a1faeSThomas Huth 
1361e8a1faeSThomas Huth     vq = qvirtqueue_setup(dev, alloc, 0);
1371e8a1faeSThomas Huth 
1381e8a1faeSThomas Huth     qvirtio_set_driver_ok(dev);
1391e8a1faeSThomas Huth 
1401e8a1faeSThomas Huth     /* Write and read with 3 descriptor layout */
1411e8a1faeSThomas Huth     /* Write request */
1421e8a1faeSThomas Huth     req.type = VIRTIO_BLK_T_OUT;
1431e8a1faeSThomas Huth     req.ioprio = 1;
1441e8a1faeSThomas Huth     req.sector = 0;
1451e8a1faeSThomas Huth     req.data = g_malloc0(512);
1461e8a1faeSThomas Huth     strcpy(req.data, "TEST");
1471e8a1faeSThomas Huth 
1481e8a1faeSThomas Huth     req_addr = virtio_blk_request(alloc, dev, &req, 512);
1491e8a1faeSThomas Huth 
1501e8a1faeSThomas Huth     g_free(req.data);
1511e8a1faeSThomas Huth 
1521e8a1faeSThomas Huth     free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
1531e8a1faeSThomas Huth     qvirtqueue_add(qts, vq, req_addr + 16, 512, false, true);
1541e8a1faeSThomas Huth     qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
1551e8a1faeSThomas Huth 
1561e8a1faeSThomas Huth     qvirtqueue_kick(qts, dev, vq, free_head);
1571e8a1faeSThomas Huth 
1581e8a1faeSThomas Huth     qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
1591e8a1faeSThomas Huth                            QVIRTIO_BLK_TIMEOUT_US);
1601e8a1faeSThomas Huth     status = readb(req_addr + 528);
1611e8a1faeSThomas Huth     g_assert_cmpint(status, ==, 0);
1621e8a1faeSThomas Huth 
1631e8a1faeSThomas Huth     guest_free(alloc, req_addr);
1641e8a1faeSThomas Huth 
1651e8a1faeSThomas Huth     /* Read request */
1661e8a1faeSThomas Huth     req.type = VIRTIO_BLK_T_IN;
1671e8a1faeSThomas Huth     req.ioprio = 1;
1681e8a1faeSThomas Huth     req.sector = 0;
1691e8a1faeSThomas Huth     req.data = g_malloc0(512);
1701e8a1faeSThomas Huth 
1711e8a1faeSThomas Huth     req_addr = virtio_blk_request(alloc, dev, &req, 512);
1721e8a1faeSThomas Huth 
1731e8a1faeSThomas Huth     g_free(req.data);
1741e8a1faeSThomas Huth 
1751e8a1faeSThomas Huth     free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
1761e8a1faeSThomas Huth     qvirtqueue_add(qts, vq, req_addr + 16, 512, true, true);
1771e8a1faeSThomas Huth     qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
1781e8a1faeSThomas Huth 
1791e8a1faeSThomas Huth     qvirtqueue_kick(qts, dev, vq, free_head);
1801e8a1faeSThomas Huth 
1811e8a1faeSThomas Huth     qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
1821e8a1faeSThomas Huth                            QVIRTIO_BLK_TIMEOUT_US);
1831e8a1faeSThomas Huth     status = readb(req_addr + 528);
1841e8a1faeSThomas Huth     g_assert_cmpint(status, ==, 0);
1851e8a1faeSThomas Huth 
1861e8a1faeSThomas Huth     data = g_malloc0(512);
1871e8a1faeSThomas Huth     memread(req_addr + 16, data, 512);
1881e8a1faeSThomas Huth     g_assert_cmpstr(data, ==, "TEST");
1891e8a1faeSThomas Huth     g_free(data);
1901e8a1faeSThomas Huth 
1911e8a1faeSThomas Huth     guest_free(alloc, req_addr);
1921e8a1faeSThomas Huth 
1931e8a1faeSThomas Huth     if (features & (1u << VIRTIO_BLK_F_WRITE_ZEROES)) {
1941e8a1faeSThomas Huth         struct virtio_blk_discard_write_zeroes dwz_hdr;
1951e8a1faeSThomas Huth         void *expected;
1961e8a1faeSThomas Huth 
1971e8a1faeSThomas Huth         /*
1981e8a1faeSThomas Huth          * WRITE_ZEROES request on the same sector of previous test where
1991e8a1faeSThomas Huth          * we wrote "TEST".
2001e8a1faeSThomas Huth          */
2011e8a1faeSThomas Huth         req.type = VIRTIO_BLK_T_WRITE_ZEROES;
2021e8a1faeSThomas Huth         req.data = (char *) &dwz_hdr;
2031e8a1faeSThomas Huth         dwz_hdr.sector = 0;
2041e8a1faeSThomas Huth         dwz_hdr.num_sectors = 1;
2051e8a1faeSThomas Huth         dwz_hdr.flags = 0;
2061e8a1faeSThomas Huth 
2071e8a1faeSThomas Huth         virtio_blk_fix_dwz_hdr(dev, &dwz_hdr);
2081e8a1faeSThomas Huth 
2091e8a1faeSThomas Huth         req_addr = virtio_blk_request(alloc, dev, &req, sizeof(dwz_hdr));
2101e8a1faeSThomas Huth 
2111e8a1faeSThomas Huth         free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
2121e8a1faeSThomas Huth         qvirtqueue_add(qts, vq, req_addr + 16, sizeof(dwz_hdr), false, true);
2131e8a1faeSThomas Huth         qvirtqueue_add(qts, vq, req_addr + 16 + sizeof(dwz_hdr), 1, true,
2141e8a1faeSThomas Huth                        false);
2151e8a1faeSThomas Huth 
2161e8a1faeSThomas Huth         qvirtqueue_kick(qts, dev, vq, free_head);
2171e8a1faeSThomas Huth 
2181e8a1faeSThomas Huth         qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
2191e8a1faeSThomas Huth                                QVIRTIO_BLK_TIMEOUT_US);
2201e8a1faeSThomas Huth         status = readb(req_addr + 16 + sizeof(dwz_hdr));
2211e8a1faeSThomas Huth         g_assert_cmpint(status, ==, 0);
2221e8a1faeSThomas Huth 
2231e8a1faeSThomas Huth         guest_free(alloc, req_addr);
2241e8a1faeSThomas Huth 
2251e8a1faeSThomas Huth         /* Read request to check if the sector contains all zeroes */
2261e8a1faeSThomas Huth         req.type = VIRTIO_BLK_T_IN;
2271e8a1faeSThomas Huth         req.ioprio = 1;
2281e8a1faeSThomas Huth         req.sector = 0;
2291e8a1faeSThomas Huth         req.data = g_malloc0(512);
2301e8a1faeSThomas Huth 
2311e8a1faeSThomas Huth         req_addr = virtio_blk_request(alloc, dev, &req, 512);
2321e8a1faeSThomas Huth 
2331e8a1faeSThomas Huth         g_free(req.data);
2341e8a1faeSThomas Huth 
2351e8a1faeSThomas Huth         free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
2361e8a1faeSThomas Huth         qvirtqueue_add(qts, vq, req_addr + 16, 512, true, true);
2371e8a1faeSThomas Huth         qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
2381e8a1faeSThomas Huth 
2391e8a1faeSThomas Huth         qvirtqueue_kick(qts, dev, vq, free_head);
2401e8a1faeSThomas Huth 
2411e8a1faeSThomas Huth         qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
2421e8a1faeSThomas Huth                                QVIRTIO_BLK_TIMEOUT_US);
2431e8a1faeSThomas Huth         status = readb(req_addr + 528);
2441e8a1faeSThomas Huth         g_assert_cmpint(status, ==, 0);
2451e8a1faeSThomas Huth 
2461e8a1faeSThomas Huth         data = g_malloc(512);
2471e8a1faeSThomas Huth         expected = g_malloc0(512);
2481e8a1faeSThomas Huth         memread(req_addr + 16, data, 512);
2491e8a1faeSThomas Huth         g_assert_cmpmem(data, 512, expected, 512);
2501e8a1faeSThomas Huth         g_free(expected);
2511e8a1faeSThomas Huth         g_free(data);
2521e8a1faeSThomas Huth 
2531e8a1faeSThomas Huth         guest_free(alloc, req_addr);
2541e8a1faeSThomas Huth     }
2551e8a1faeSThomas Huth 
2561e8a1faeSThomas Huth     if (features & (1u << VIRTIO_BLK_F_DISCARD)) {
2571e8a1faeSThomas Huth         struct virtio_blk_discard_write_zeroes dwz_hdr;
2581e8a1faeSThomas Huth 
2591e8a1faeSThomas Huth         req.type = VIRTIO_BLK_T_DISCARD;
2601e8a1faeSThomas Huth         req.data = (char *) &dwz_hdr;
2611e8a1faeSThomas Huth         dwz_hdr.sector = 0;
2621e8a1faeSThomas Huth         dwz_hdr.num_sectors = 1;
2631e8a1faeSThomas Huth         dwz_hdr.flags = 0;
2641e8a1faeSThomas Huth 
2651e8a1faeSThomas Huth         virtio_blk_fix_dwz_hdr(dev, &dwz_hdr);
2661e8a1faeSThomas Huth 
2671e8a1faeSThomas Huth         req_addr = virtio_blk_request(alloc, dev, &req, sizeof(dwz_hdr));
2681e8a1faeSThomas Huth 
2691e8a1faeSThomas Huth         free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
2701e8a1faeSThomas Huth         qvirtqueue_add(qts, vq, req_addr + 16, sizeof(dwz_hdr), false, true);
2711e8a1faeSThomas Huth         qvirtqueue_add(qts, vq, req_addr + 16 + sizeof(dwz_hdr), 1, true, false);
2721e8a1faeSThomas Huth 
2731e8a1faeSThomas Huth         qvirtqueue_kick(qts, dev, vq, free_head);
2741e8a1faeSThomas Huth 
2751e8a1faeSThomas Huth         qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
2761e8a1faeSThomas Huth                                QVIRTIO_BLK_TIMEOUT_US);
2771e8a1faeSThomas Huth         status = readb(req_addr + 16 + sizeof(dwz_hdr));
2781e8a1faeSThomas Huth         g_assert_cmpint(status, ==, 0);
2791e8a1faeSThomas Huth 
2801e8a1faeSThomas Huth         guest_free(alloc, req_addr);
2811e8a1faeSThomas Huth     }
2821e8a1faeSThomas Huth 
2831e8a1faeSThomas Huth     if (features & (1u << VIRTIO_F_ANY_LAYOUT)) {
2841e8a1faeSThomas Huth         /* Write and read with 2 descriptor layout */
2851e8a1faeSThomas Huth         /* Write request */
2861e8a1faeSThomas Huth         req.type = VIRTIO_BLK_T_OUT;
2871e8a1faeSThomas Huth         req.ioprio = 1;
2881e8a1faeSThomas Huth         req.sector = 1;
2891e8a1faeSThomas Huth         req.data = g_malloc0(512);
2901e8a1faeSThomas Huth         strcpy(req.data, "TEST");
2911e8a1faeSThomas Huth 
2921e8a1faeSThomas Huth         req_addr = virtio_blk_request(alloc, dev, &req, 512);
2931e8a1faeSThomas Huth 
2941e8a1faeSThomas Huth         g_free(req.data);
2951e8a1faeSThomas Huth 
2961e8a1faeSThomas Huth         free_head = qvirtqueue_add(qts, vq, req_addr, 528, false, true);
2971e8a1faeSThomas Huth         qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
2981e8a1faeSThomas Huth         qvirtqueue_kick(qts, dev, vq, free_head);
2991e8a1faeSThomas Huth 
3001e8a1faeSThomas Huth         qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
3011e8a1faeSThomas Huth                                QVIRTIO_BLK_TIMEOUT_US);
3021e8a1faeSThomas Huth         status = readb(req_addr + 528);
3031e8a1faeSThomas Huth         g_assert_cmpint(status, ==, 0);
3041e8a1faeSThomas Huth 
3051e8a1faeSThomas Huth         guest_free(alloc, req_addr);
3061e8a1faeSThomas Huth 
3071e8a1faeSThomas Huth         /* Read request */
3081e8a1faeSThomas Huth         req.type = VIRTIO_BLK_T_IN;
3091e8a1faeSThomas Huth         req.ioprio = 1;
3101e8a1faeSThomas Huth         req.sector = 1;
3111e8a1faeSThomas Huth         req.data = g_malloc0(512);
3121e8a1faeSThomas Huth 
3131e8a1faeSThomas Huth         req_addr = virtio_blk_request(alloc, dev, &req, 512);
3141e8a1faeSThomas Huth 
3151e8a1faeSThomas Huth         g_free(req.data);
3161e8a1faeSThomas Huth 
3171e8a1faeSThomas Huth         free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
3181e8a1faeSThomas Huth         qvirtqueue_add(qts, vq, req_addr + 16, 513, true, false);
3191e8a1faeSThomas Huth 
3201e8a1faeSThomas Huth         qvirtqueue_kick(qts, dev, vq, free_head);
3211e8a1faeSThomas Huth 
3221e8a1faeSThomas Huth         qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
3231e8a1faeSThomas Huth                                QVIRTIO_BLK_TIMEOUT_US);
3241e8a1faeSThomas Huth         status = readb(req_addr + 528);
3251e8a1faeSThomas Huth         g_assert_cmpint(status, ==, 0);
3261e8a1faeSThomas Huth 
3271e8a1faeSThomas Huth         data = g_malloc0(512);
3281e8a1faeSThomas Huth         memread(req_addr + 16, data, 512);
3291e8a1faeSThomas Huth         g_assert_cmpstr(data, ==, "TEST");
3301e8a1faeSThomas Huth         g_free(data);
3311e8a1faeSThomas Huth 
3321e8a1faeSThomas Huth         guest_free(alloc, req_addr);
3331e8a1faeSThomas Huth     }
3341e8a1faeSThomas Huth 
3351e8a1faeSThomas Huth     return vq;
3361e8a1faeSThomas Huth }
3371e8a1faeSThomas Huth 
basic(void * obj,void * data,QGuestAllocator * t_alloc)3381e8a1faeSThomas Huth static void basic(void *obj, void *data, QGuestAllocator *t_alloc)
3391e8a1faeSThomas Huth {
3401e8a1faeSThomas Huth     QVirtioBlk *blk_if = obj;
3411e8a1faeSThomas Huth     QVirtQueue *vq;
3421e8a1faeSThomas Huth 
3431e8a1faeSThomas Huth     vq = test_basic(blk_if->vdev, t_alloc);
3441e8a1faeSThomas Huth     qvirtqueue_cleanup(blk_if->vdev->bus, vq, t_alloc);
3451e8a1faeSThomas Huth 
3461e8a1faeSThomas Huth }
3471e8a1faeSThomas Huth 
indirect(void * obj,void * u_data,QGuestAllocator * t_alloc)3481e8a1faeSThomas Huth static void indirect(void *obj, void *u_data, QGuestAllocator *t_alloc)
3491e8a1faeSThomas Huth {
3501e8a1faeSThomas Huth     QVirtQueue *vq;
3511e8a1faeSThomas Huth     QVirtioBlk *blk_if = obj;
3521e8a1faeSThomas Huth     QVirtioDevice *dev = blk_if->vdev;
3531e8a1faeSThomas Huth     QVirtioBlkReq req;
3541e8a1faeSThomas Huth     QVRingIndirectDesc *indirect;
3551e8a1faeSThomas Huth     uint64_t req_addr;
3561e8a1faeSThomas Huth     uint64_t capacity;
3571e8a1faeSThomas Huth     uint64_t features;
3581e8a1faeSThomas Huth     uint32_t free_head;
3591e8a1faeSThomas Huth     uint8_t status;
3601e8a1faeSThomas Huth     char *data;
3611e8a1faeSThomas Huth     QTestState *qts = global_qtest;
3621e8a1faeSThomas Huth 
3631e8a1faeSThomas Huth     features = qvirtio_get_features(dev);
3641e8a1faeSThomas Huth     g_assert_cmphex(features & (1u << VIRTIO_RING_F_INDIRECT_DESC), !=, 0);
3651e8a1faeSThomas Huth     features = features & ~(QVIRTIO_F_BAD_FEATURE |
3661e8a1faeSThomas Huth                             (1u << VIRTIO_RING_F_EVENT_IDX) |
3671e8a1faeSThomas Huth                             (1u << VIRTIO_BLK_F_SCSI));
3681e8a1faeSThomas Huth     qvirtio_set_features(dev, features);
3691e8a1faeSThomas Huth 
3701e8a1faeSThomas Huth     capacity = qvirtio_config_readq(dev, 0);
3711e8a1faeSThomas Huth     g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
3721e8a1faeSThomas Huth 
3731e8a1faeSThomas Huth     vq = qvirtqueue_setup(dev, t_alloc, 0);
3741e8a1faeSThomas Huth     qvirtio_set_driver_ok(dev);
3751e8a1faeSThomas Huth 
3761e8a1faeSThomas Huth     /* Write request */
3771e8a1faeSThomas Huth     req.type = VIRTIO_BLK_T_OUT;
3781e8a1faeSThomas Huth     req.ioprio = 1;
3791e8a1faeSThomas Huth     req.sector = 0;
3801e8a1faeSThomas Huth     req.data = g_malloc0(512);
3811e8a1faeSThomas Huth     strcpy(req.data, "TEST");
3821e8a1faeSThomas Huth 
3831e8a1faeSThomas Huth     req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
3841e8a1faeSThomas Huth 
3851e8a1faeSThomas Huth     g_free(req.data);
3861e8a1faeSThomas Huth 
3871e8a1faeSThomas Huth     indirect = qvring_indirect_desc_setup(qts, dev, t_alloc, 2);
3881e8a1faeSThomas Huth     qvring_indirect_desc_add(dev, qts, indirect, req_addr, 528, false);
3891e8a1faeSThomas Huth     qvring_indirect_desc_add(dev, qts, indirect, req_addr + 528, 1, true);
3901e8a1faeSThomas Huth     free_head = qvirtqueue_add_indirect(qts, vq, indirect);
3911e8a1faeSThomas Huth     qvirtqueue_kick(qts, dev, vq, free_head);
3921e8a1faeSThomas Huth 
3931e8a1faeSThomas Huth     qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
3941e8a1faeSThomas Huth                            QVIRTIO_BLK_TIMEOUT_US);
3951e8a1faeSThomas Huth     status = readb(req_addr + 528);
3961e8a1faeSThomas Huth     g_assert_cmpint(status, ==, 0);
3971e8a1faeSThomas Huth 
3981e8a1faeSThomas Huth     g_free(indirect);
3991e8a1faeSThomas Huth     guest_free(t_alloc, req_addr);
4001e8a1faeSThomas Huth 
4011e8a1faeSThomas Huth     /* Read request */
4021e8a1faeSThomas Huth     req.type = VIRTIO_BLK_T_IN;
4031e8a1faeSThomas Huth     req.ioprio = 1;
4041e8a1faeSThomas Huth     req.sector = 0;
4051e8a1faeSThomas Huth     req.data = g_malloc0(512);
4061e8a1faeSThomas Huth     strcpy(req.data, "TEST");
4071e8a1faeSThomas Huth 
4081e8a1faeSThomas Huth     req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
4091e8a1faeSThomas Huth 
4101e8a1faeSThomas Huth     g_free(req.data);
4111e8a1faeSThomas Huth 
4121e8a1faeSThomas Huth     indirect = qvring_indirect_desc_setup(qts, dev, t_alloc, 2);
4131e8a1faeSThomas Huth     qvring_indirect_desc_add(dev, qts, indirect, req_addr, 16, false);
4141e8a1faeSThomas Huth     qvring_indirect_desc_add(dev, qts, indirect, req_addr + 16, 513, true);
4151e8a1faeSThomas Huth     free_head = qvirtqueue_add_indirect(qts, vq, indirect);
4161e8a1faeSThomas Huth     qvirtqueue_kick(qts, dev, vq, free_head);
4171e8a1faeSThomas Huth 
4181e8a1faeSThomas Huth     qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
4191e8a1faeSThomas Huth                            QVIRTIO_BLK_TIMEOUT_US);
4201e8a1faeSThomas Huth     status = readb(req_addr + 528);
4211e8a1faeSThomas Huth     g_assert_cmpint(status, ==, 0);
4221e8a1faeSThomas Huth 
4231e8a1faeSThomas Huth     data = g_malloc0(512);
4241e8a1faeSThomas Huth     memread(req_addr + 16, data, 512);
4251e8a1faeSThomas Huth     g_assert_cmpstr(data, ==, "TEST");
4261e8a1faeSThomas Huth     g_free(data);
4271e8a1faeSThomas Huth 
4281e8a1faeSThomas Huth     g_free(indirect);
4291e8a1faeSThomas Huth     guest_free(t_alloc, req_addr);
4301e8a1faeSThomas Huth     qvirtqueue_cleanup(dev->bus, vq, t_alloc);
4311e8a1faeSThomas Huth }
4321e8a1faeSThomas Huth 
config(void * obj,void * data,QGuestAllocator * t_alloc)4331e8a1faeSThomas Huth static void config(void *obj, void *data, QGuestAllocator *t_alloc)
4341e8a1faeSThomas Huth {
4351e8a1faeSThomas Huth     QVirtioBlk *blk_if = obj;
4361e8a1faeSThomas Huth     QVirtioDevice *dev = blk_if->vdev;
4371e8a1faeSThomas Huth     int n_size = TEST_IMAGE_SIZE / 2;
4381e8a1faeSThomas Huth     uint64_t features;
4391e8a1faeSThomas Huth     uint64_t capacity;
4401e8a1faeSThomas Huth 
4411e8a1faeSThomas Huth     features = qvirtio_get_features(dev);
4421e8a1faeSThomas Huth     features = features & ~(QVIRTIO_F_BAD_FEATURE |
4431e8a1faeSThomas Huth                             (1u << VIRTIO_RING_F_INDIRECT_DESC) |
4441e8a1faeSThomas Huth                             (1u << VIRTIO_RING_F_EVENT_IDX) |
4451e8a1faeSThomas Huth                             (1u << VIRTIO_BLK_F_SCSI));
4461e8a1faeSThomas Huth     qvirtio_set_features(dev, features);
4471e8a1faeSThomas Huth 
4481e8a1faeSThomas Huth     capacity = qvirtio_config_readq(dev, 0);
4491e8a1faeSThomas Huth     g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
4501e8a1faeSThomas Huth 
4511e8a1faeSThomas Huth     qvirtio_set_driver_ok(dev);
4521e8a1faeSThomas Huth 
453*855436dbSDaniel P. Berrangé     qtest_qmp_assert_success(global_qtest,
454*855436dbSDaniel P. Berrangé                              "{ 'execute': 'block_resize', "
4551e8a1faeSThomas Huth                              " 'arguments': { 'device': 'drive0', "
4561e8a1faeSThomas Huth                              " 'size': %d } }", n_size);
4571e8a1faeSThomas Huth     qvirtio_wait_config_isr(dev, QVIRTIO_BLK_TIMEOUT_US);
4581e8a1faeSThomas Huth 
4591e8a1faeSThomas Huth     capacity = qvirtio_config_readq(dev, 0);
4601e8a1faeSThomas Huth     g_assert_cmpint(capacity, ==, n_size / 512);
4611e8a1faeSThomas Huth }
4621e8a1faeSThomas Huth 
msix(void * obj,void * u_data,QGuestAllocator * t_alloc)4631e8a1faeSThomas Huth static void msix(void *obj, void *u_data, QGuestAllocator *t_alloc)
4641e8a1faeSThomas Huth {
4651e8a1faeSThomas Huth     QVirtQueue *vq;
4661e8a1faeSThomas Huth     QVirtioBlkPCI *blk = obj;
4671e8a1faeSThomas Huth     QVirtioPCIDevice *pdev = &blk->pci_vdev;
4681e8a1faeSThomas Huth     QVirtioDevice *dev = &pdev->vdev;
4691e8a1faeSThomas Huth     QVirtioBlkReq req;
4701e8a1faeSThomas Huth     int n_size = TEST_IMAGE_SIZE / 2;
4711e8a1faeSThomas Huth     uint64_t req_addr;
4721e8a1faeSThomas Huth     uint64_t capacity;
4731e8a1faeSThomas Huth     uint64_t features;
4741e8a1faeSThomas Huth     uint32_t free_head;
4751e8a1faeSThomas Huth     uint8_t status;
4761e8a1faeSThomas Huth     char *data;
4771e8a1faeSThomas Huth     QOSGraphObject *blk_object = obj;
4781e8a1faeSThomas Huth     QPCIDevice *pci_dev = blk_object->get_driver(blk_object, "pci-device");
4791e8a1faeSThomas Huth     QTestState *qts = global_qtest;
4801e8a1faeSThomas Huth 
4811e8a1faeSThomas Huth     if (qpci_check_buggy_msi(pci_dev)) {
4821e8a1faeSThomas Huth         return;
4831e8a1faeSThomas Huth     }
4841e8a1faeSThomas Huth 
4851e8a1faeSThomas Huth     qpci_msix_enable(pdev->pdev);
4861e8a1faeSThomas Huth     qvirtio_pci_set_msix_configuration_vector(pdev, t_alloc, 0);
4871e8a1faeSThomas Huth 
4881e8a1faeSThomas Huth     features = qvirtio_get_features(dev);
4891e8a1faeSThomas Huth     features = features & ~(QVIRTIO_F_BAD_FEATURE |
4901e8a1faeSThomas Huth                             (1u << VIRTIO_RING_F_INDIRECT_DESC) |
4911e8a1faeSThomas Huth                             (1u << VIRTIO_RING_F_EVENT_IDX) |
4921e8a1faeSThomas Huth                             (1u << VIRTIO_BLK_F_SCSI));
4931e8a1faeSThomas Huth     qvirtio_set_features(dev, features);
4941e8a1faeSThomas Huth 
4951e8a1faeSThomas Huth     capacity = qvirtio_config_readq(dev, 0);
4961e8a1faeSThomas Huth     g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
4971e8a1faeSThomas Huth 
4981e8a1faeSThomas Huth     vq = qvirtqueue_setup(dev, t_alloc, 0);
4991e8a1faeSThomas Huth     qvirtqueue_pci_msix_setup(pdev, (QVirtQueuePCI *)vq, t_alloc, 1);
5001e8a1faeSThomas Huth 
5011e8a1faeSThomas Huth     qvirtio_set_driver_ok(dev);
5021e8a1faeSThomas Huth 
503*855436dbSDaniel P. Berrangé     qtest_qmp_assert_success(global_qtest,
504*855436dbSDaniel P. Berrangé                              "{ 'execute': 'block_resize', "
5051e8a1faeSThomas Huth                              " 'arguments': { 'device': 'drive0', "
5061e8a1faeSThomas Huth                              " 'size': %d } }", n_size);
5071e8a1faeSThomas Huth 
5081e8a1faeSThomas Huth     qvirtio_wait_config_isr(dev, QVIRTIO_BLK_TIMEOUT_US);
5091e8a1faeSThomas Huth 
5101e8a1faeSThomas Huth     capacity = qvirtio_config_readq(dev, 0);
5111e8a1faeSThomas Huth     g_assert_cmpint(capacity, ==, n_size / 512);
5121e8a1faeSThomas Huth 
5131e8a1faeSThomas Huth     /* Write request */
5141e8a1faeSThomas Huth     req.type = VIRTIO_BLK_T_OUT;
5151e8a1faeSThomas Huth     req.ioprio = 1;
5161e8a1faeSThomas Huth     req.sector = 0;
5171e8a1faeSThomas Huth     req.data = g_malloc0(512);
5181e8a1faeSThomas Huth     strcpy(req.data, "TEST");
5191e8a1faeSThomas Huth 
5201e8a1faeSThomas Huth     req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
5211e8a1faeSThomas Huth 
5221e8a1faeSThomas Huth     g_free(req.data);
5231e8a1faeSThomas Huth 
5241e8a1faeSThomas Huth     free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
5251e8a1faeSThomas Huth     qvirtqueue_add(qts, vq, req_addr + 16, 512, false, true);
5261e8a1faeSThomas Huth     qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
5271e8a1faeSThomas Huth     qvirtqueue_kick(qts, dev, vq, free_head);
5281e8a1faeSThomas Huth 
5291e8a1faeSThomas Huth     qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
5301e8a1faeSThomas Huth                            QVIRTIO_BLK_TIMEOUT_US);
5311e8a1faeSThomas Huth 
5321e8a1faeSThomas Huth     status = readb(req_addr + 528);
5331e8a1faeSThomas Huth     g_assert_cmpint(status, ==, 0);
5341e8a1faeSThomas Huth 
5351e8a1faeSThomas Huth     guest_free(t_alloc, req_addr);
5361e8a1faeSThomas Huth 
5371e8a1faeSThomas Huth     /* Read request */
5381e8a1faeSThomas Huth     req.type = VIRTIO_BLK_T_IN;
5391e8a1faeSThomas Huth     req.ioprio = 1;
5401e8a1faeSThomas Huth     req.sector = 0;
5411e8a1faeSThomas Huth     req.data = g_malloc0(512);
5421e8a1faeSThomas Huth 
5431e8a1faeSThomas Huth     req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
5441e8a1faeSThomas Huth 
5451e8a1faeSThomas Huth     g_free(req.data);
5461e8a1faeSThomas Huth 
5471e8a1faeSThomas Huth     free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
5481e8a1faeSThomas Huth     qvirtqueue_add(qts, vq, req_addr + 16, 512, true, true);
5491e8a1faeSThomas Huth     qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
5501e8a1faeSThomas Huth 
5511e8a1faeSThomas Huth     qvirtqueue_kick(qts, dev, vq, free_head);
5521e8a1faeSThomas Huth 
5531e8a1faeSThomas Huth 
5541e8a1faeSThomas Huth     qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
5551e8a1faeSThomas Huth                            QVIRTIO_BLK_TIMEOUT_US);
5561e8a1faeSThomas Huth 
5571e8a1faeSThomas Huth     status = readb(req_addr + 528);
5581e8a1faeSThomas Huth     g_assert_cmpint(status, ==, 0);
5591e8a1faeSThomas Huth 
5601e8a1faeSThomas Huth     data = g_malloc0(512);
5611e8a1faeSThomas Huth     memread(req_addr + 16, data, 512);
5621e8a1faeSThomas Huth     g_assert_cmpstr(data, ==, "TEST");
5631e8a1faeSThomas Huth     g_free(data);
5641e8a1faeSThomas Huth 
5651e8a1faeSThomas Huth     guest_free(t_alloc, req_addr);
5661e8a1faeSThomas Huth 
5671e8a1faeSThomas Huth     /* End test */
5681e8a1faeSThomas Huth     qpci_msix_disable(pdev->pdev);
5691e8a1faeSThomas Huth     qvirtqueue_cleanup(dev->bus, vq, t_alloc);
5701e8a1faeSThomas Huth }
5711e8a1faeSThomas Huth 
idx(void * obj,void * u_data,QGuestAllocator * t_alloc)5721e8a1faeSThomas Huth static void idx(void *obj, void *u_data, QGuestAllocator *t_alloc)
5731e8a1faeSThomas Huth {
5741e8a1faeSThomas Huth     QVirtQueue *vq;
5751e8a1faeSThomas Huth     QVirtioBlkPCI *blk = obj;
5761e8a1faeSThomas Huth     QVirtioPCIDevice *pdev = &blk->pci_vdev;
5771e8a1faeSThomas Huth     QVirtioDevice *dev = &pdev->vdev;
5781e8a1faeSThomas Huth     QVirtioBlkReq req;
5791e8a1faeSThomas Huth     uint64_t req_addr;
5801e8a1faeSThomas Huth     uint64_t capacity;
5811e8a1faeSThomas Huth     uint64_t features;
5821e8a1faeSThomas Huth     uint32_t free_head;
5831e8a1faeSThomas Huth     uint32_t write_head;
5841e8a1faeSThomas Huth     uint32_t desc_idx;
5851e8a1faeSThomas Huth     uint8_t status;
5861e8a1faeSThomas Huth     char *data;
5871e8a1faeSThomas Huth     QOSGraphObject *blk_object = obj;
5881e8a1faeSThomas Huth     QPCIDevice *pci_dev = blk_object->get_driver(blk_object, "pci-device");
5891e8a1faeSThomas Huth     QTestState *qts = global_qtest;
5901e8a1faeSThomas Huth 
5911e8a1faeSThomas Huth     if (qpci_check_buggy_msi(pci_dev)) {
5921e8a1faeSThomas Huth         return;
5931e8a1faeSThomas Huth     }
5941e8a1faeSThomas Huth 
5951e8a1faeSThomas Huth     qpci_msix_enable(pdev->pdev);
5961e8a1faeSThomas Huth     qvirtio_pci_set_msix_configuration_vector(pdev, t_alloc, 0);
5971e8a1faeSThomas Huth 
5981e8a1faeSThomas Huth     features = qvirtio_get_features(dev);
5991e8a1faeSThomas Huth     features = features & ~(QVIRTIO_F_BAD_FEATURE |
6001e8a1faeSThomas Huth                             (1u << VIRTIO_RING_F_INDIRECT_DESC) |
6011e8a1faeSThomas Huth                             (1u << VIRTIO_F_NOTIFY_ON_EMPTY) |
6021e8a1faeSThomas Huth                             (1u << VIRTIO_BLK_F_SCSI));
6031e8a1faeSThomas Huth     qvirtio_set_features(dev, features);
6041e8a1faeSThomas Huth 
6051e8a1faeSThomas Huth     capacity = qvirtio_config_readq(dev, 0);
6061e8a1faeSThomas Huth     g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
6071e8a1faeSThomas Huth 
6081e8a1faeSThomas Huth     vq = qvirtqueue_setup(dev, t_alloc, 0);
6091e8a1faeSThomas Huth     qvirtqueue_pci_msix_setup(pdev, (QVirtQueuePCI *)vq, t_alloc, 1);
6101e8a1faeSThomas Huth 
6111e8a1faeSThomas Huth     qvirtio_set_driver_ok(dev);
6121e8a1faeSThomas Huth 
6131e8a1faeSThomas Huth     /* Write request */
6141e8a1faeSThomas Huth     req.type = VIRTIO_BLK_T_OUT;
6151e8a1faeSThomas Huth     req.ioprio = 1;
6161e8a1faeSThomas Huth     req.sector = 0;
6171e8a1faeSThomas Huth     req.data = g_malloc0(512);
6181e8a1faeSThomas Huth     strcpy(req.data, "TEST");
6191e8a1faeSThomas Huth 
6201e8a1faeSThomas Huth     req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
6211e8a1faeSThomas Huth 
6221e8a1faeSThomas Huth     g_free(req.data);
6231e8a1faeSThomas Huth 
6241e8a1faeSThomas Huth     free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
6251e8a1faeSThomas Huth     qvirtqueue_add(qts, vq, req_addr + 16, 512, false, true);
6261e8a1faeSThomas Huth     qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
6271e8a1faeSThomas Huth     qvirtqueue_kick(qts, dev, vq, free_head);
6281e8a1faeSThomas Huth 
6291e8a1faeSThomas Huth     qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
6301e8a1faeSThomas Huth                            QVIRTIO_BLK_TIMEOUT_US);
6311e8a1faeSThomas Huth 
6321e8a1faeSThomas Huth     /* Write request */
6331e8a1faeSThomas Huth     req.type = VIRTIO_BLK_T_OUT;
6341e8a1faeSThomas Huth     req.ioprio = 1;
6351e8a1faeSThomas Huth     req.sector = 1;
6361e8a1faeSThomas Huth     req.data = g_malloc0(512);
6371e8a1faeSThomas Huth     strcpy(req.data, "TEST");
6381e8a1faeSThomas Huth 
6391e8a1faeSThomas Huth     req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
6401e8a1faeSThomas Huth 
6411e8a1faeSThomas Huth     g_free(req.data);
6421e8a1faeSThomas Huth 
6431e8a1faeSThomas Huth     /* Notify after processing the third request */
6441e8a1faeSThomas Huth     qvirtqueue_set_used_event(qts, vq, 2);
6451e8a1faeSThomas Huth     free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
6461e8a1faeSThomas Huth     qvirtqueue_add(qts, vq, req_addr + 16, 512, false, true);
6471e8a1faeSThomas Huth     qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
6481e8a1faeSThomas Huth     qvirtqueue_kick(qts, dev, vq, free_head);
6491e8a1faeSThomas Huth     write_head = free_head;
6501e8a1faeSThomas Huth 
6511e8a1faeSThomas Huth     /* No notification expected */
6521e8a1faeSThomas Huth     status = qvirtio_wait_status_byte_no_isr(qts, dev,
6531e8a1faeSThomas Huth                                              vq, req_addr + 528,
6541e8a1faeSThomas Huth                                              QVIRTIO_BLK_TIMEOUT_US);
6551e8a1faeSThomas Huth     g_assert_cmpint(status, ==, 0);
6561e8a1faeSThomas Huth 
6571e8a1faeSThomas Huth     guest_free(t_alloc, req_addr);
6581e8a1faeSThomas Huth 
6591e8a1faeSThomas Huth     /* Read request */
6601e8a1faeSThomas Huth     req.type = VIRTIO_BLK_T_IN;
6611e8a1faeSThomas Huth     req.ioprio = 1;
6621e8a1faeSThomas Huth     req.sector = 1;
6631e8a1faeSThomas Huth     req.data = g_malloc0(512);
6641e8a1faeSThomas Huth 
6651e8a1faeSThomas Huth     req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
6661e8a1faeSThomas Huth 
6671e8a1faeSThomas Huth     g_free(req.data);
6681e8a1faeSThomas Huth 
6691e8a1faeSThomas Huth     free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
6701e8a1faeSThomas Huth     qvirtqueue_add(qts, vq, req_addr + 16, 512, true, true);
6711e8a1faeSThomas Huth     qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
6721e8a1faeSThomas Huth 
6731e8a1faeSThomas Huth     qvirtqueue_kick(qts, dev, vq, free_head);
6741e8a1faeSThomas Huth 
6751e8a1faeSThomas Huth     /* We get just one notification for both requests */
6761e8a1faeSThomas Huth     qvirtio_wait_used_elem(qts, dev, vq, write_head, NULL,
6771e8a1faeSThomas Huth                            QVIRTIO_BLK_TIMEOUT_US);
6781e8a1faeSThomas Huth     g_assert(qvirtqueue_get_buf(qts, vq, &desc_idx, NULL));
6791e8a1faeSThomas Huth     g_assert_cmpint(desc_idx, ==, free_head);
6801e8a1faeSThomas Huth 
6811e8a1faeSThomas Huth     status = readb(req_addr + 528);
6821e8a1faeSThomas Huth     g_assert_cmpint(status, ==, 0);
6831e8a1faeSThomas Huth 
6841e8a1faeSThomas Huth     data = g_malloc0(512);
6851e8a1faeSThomas Huth     memread(req_addr + 16, data, 512);
6861e8a1faeSThomas Huth     g_assert_cmpstr(data, ==, "TEST");
6871e8a1faeSThomas Huth     g_free(data);
6881e8a1faeSThomas Huth 
6891e8a1faeSThomas Huth     guest_free(t_alloc, req_addr);
6901e8a1faeSThomas Huth 
6911e8a1faeSThomas Huth     /* End test */
6921e8a1faeSThomas Huth     qpci_msix_disable(pdev->pdev);
6931e8a1faeSThomas Huth 
6941e8a1faeSThomas Huth     qvirtqueue_cleanup(dev->bus, vq, t_alloc);
6951e8a1faeSThomas Huth }
6961e8a1faeSThomas Huth 
pci_hotplug(void * obj,void * data,QGuestAllocator * t_alloc)6971e8a1faeSThomas Huth static void pci_hotplug(void *obj, void *data, QGuestAllocator *t_alloc)
6981e8a1faeSThomas Huth {
6991e8a1faeSThomas Huth     QVirtioPCIDevice *dev1 = obj;
7001e8a1faeSThomas Huth     QVirtioPCIDevice *dev;
7011e8a1faeSThomas Huth     QTestState *qts = dev1->pdev->bus->qts;
7021e8a1faeSThomas Huth 
70302ee7a8aSEric Auger     if (dev1->pdev->bus->not_hotpluggable) {
70402ee7a8aSEric Auger         g_test_skip("pci bus does not support hotplug");
70502ee7a8aSEric Auger         return;
70602ee7a8aSEric Auger     }
70702ee7a8aSEric Auger 
7081e8a1faeSThomas Huth     /* plug secondary disk */
7091e8a1faeSThomas Huth     qtest_qmp_device_add(qts, "virtio-blk-pci", "drv1",
7101e8a1faeSThomas Huth                          "{'addr': %s, 'drive': 'drive1'}",
7111e8a1faeSThomas Huth                          stringify(PCI_SLOT_HP) ".0");
7121e8a1faeSThomas Huth 
7131e8a1faeSThomas Huth     dev = virtio_pci_new(dev1->pdev->bus,
7141e8a1faeSThomas Huth                          &(QPCIAddress) { .devfn = QPCI_DEVFN(PCI_SLOT_HP, 0) });
7151e8a1faeSThomas Huth     g_assert_nonnull(dev);
7161e8a1faeSThomas Huth     g_assert_cmpint(dev->vdev.device_type, ==, VIRTIO_ID_BLOCK);
7171e8a1faeSThomas Huth     qvirtio_pci_device_disable(dev);
7181e8a1faeSThomas Huth     qos_object_destroy((QOSGraphObject *)dev);
7191e8a1faeSThomas Huth 
7201e8a1faeSThomas Huth     /* unplug secondary disk */
7211e8a1faeSThomas Huth     qpci_unplug_acpi_device_test(qts, "drv1", PCI_SLOT_HP);
7221e8a1faeSThomas Huth }
7231e8a1faeSThomas Huth 
7241e8a1faeSThomas Huth /*
7251e8a1faeSThomas Huth  * Check that setting the vring addr on a non-existent virtqueue does
7261e8a1faeSThomas Huth  * not crash.
7271e8a1faeSThomas Huth  */
test_nonexistent_virtqueue(void * obj,void * data,QGuestAllocator * t_alloc)7281e8a1faeSThomas Huth static void test_nonexistent_virtqueue(void *obj, void *data,
7291e8a1faeSThomas Huth                                        QGuestAllocator *t_alloc)
7301e8a1faeSThomas Huth {
7311e8a1faeSThomas Huth     QVirtioBlkPCI *blk = obj;
7321e8a1faeSThomas Huth     QVirtioPCIDevice *pdev = &blk->pci_vdev;
7331e8a1faeSThomas Huth     QPCIBar bar0;
7341e8a1faeSThomas Huth     QPCIDevice *dev;
7351e8a1faeSThomas Huth 
7361e8a1faeSThomas Huth     dev = qpci_device_find(pdev->pdev->bus, QPCI_DEVFN(4, 0));
7371e8a1faeSThomas Huth     g_assert(dev != NULL);
7381e8a1faeSThomas Huth     qpci_device_enable(dev);
7391e8a1faeSThomas Huth 
7401e8a1faeSThomas Huth     bar0 = qpci_iomap(dev, 0, NULL);
7411e8a1faeSThomas Huth 
7421e8a1faeSThomas Huth     qpci_io_writeb(dev, bar0, VIRTIO_PCI_QUEUE_SEL, 2);
7431e8a1faeSThomas Huth     qpci_io_writel(dev, bar0, VIRTIO_PCI_QUEUE_PFN, 1);
7441e8a1faeSThomas Huth 
7451e8a1faeSThomas Huth 
7461e8a1faeSThomas Huth     g_free(dev);
7471e8a1faeSThomas Huth }
7481e8a1faeSThomas Huth 
resize(void * obj,void * data,QGuestAllocator * t_alloc)7491e8a1faeSThomas Huth static void resize(void *obj, void *data, QGuestAllocator *t_alloc)
7501e8a1faeSThomas Huth {
7511e8a1faeSThomas Huth     QVirtioBlk *blk_if = obj;
7521e8a1faeSThomas Huth     QVirtioDevice *dev = blk_if->vdev;
7531e8a1faeSThomas Huth     int n_size = TEST_IMAGE_SIZE / 2;
7541e8a1faeSThomas Huth     uint64_t capacity;
7551e8a1faeSThomas Huth     QVirtQueue *vq;
7561e8a1faeSThomas Huth     QTestState *qts = global_qtest;
7571e8a1faeSThomas Huth 
7581e8a1faeSThomas Huth     vq = test_basic(dev, t_alloc);
7591e8a1faeSThomas Huth 
760*855436dbSDaniel P. Berrangé     qtest_qmp_assert_success(global_qtest,
761*855436dbSDaniel P. Berrangé                              "{ 'execute': 'block_resize', "
7621e8a1faeSThomas Huth                              " 'arguments': { 'device': 'drive0', "
7631e8a1faeSThomas Huth                              " 'size': %d } }", n_size);
7641e8a1faeSThomas Huth 
7651e8a1faeSThomas Huth     qvirtio_wait_queue_isr(qts, dev, vq, QVIRTIO_BLK_TIMEOUT_US);
7661e8a1faeSThomas Huth 
7671e8a1faeSThomas Huth     capacity = qvirtio_config_readq(dev, 0);
7681e8a1faeSThomas Huth     g_assert_cmpint(capacity, ==, n_size / 512);
7691e8a1faeSThomas Huth 
7701e8a1faeSThomas Huth     qvirtqueue_cleanup(dev->bus, vq, t_alloc);
7711e8a1faeSThomas Huth 
7721e8a1faeSThomas Huth }
7731e8a1faeSThomas Huth 
virtio_blk_test_setup(GString * cmd_line,void * arg)7741e8a1faeSThomas Huth static void *virtio_blk_test_setup(GString *cmd_line, void *arg)
7751e8a1faeSThomas Huth {
7761e8a1faeSThomas Huth     char *tmp_path = drive_create();
7771e8a1faeSThomas Huth 
7781e8a1faeSThomas Huth     g_string_append_printf(cmd_line,
7791e8a1faeSThomas Huth                            " -drive if=none,id=drive0,file=%s,"
7801e8a1faeSThomas Huth                            "format=raw,auto-read-only=off "
7811e8a1faeSThomas Huth                            "-drive if=none,id=drive1,file=null-co://,"
7821e8a1faeSThomas Huth                            "file.read-zeroes=on,format=raw ",
7831e8a1faeSThomas Huth                            tmp_path);
7841e8a1faeSThomas Huth 
7851e8a1faeSThomas Huth     return arg;
7861e8a1faeSThomas Huth }
7871e8a1faeSThomas Huth 
register_virtio_blk_test(void)7881e8a1faeSThomas Huth static void register_virtio_blk_test(void)
7891e8a1faeSThomas Huth {
7901e8a1faeSThomas Huth     QOSGraphTestOptions opts = {
7911e8a1faeSThomas Huth         .before = virtio_blk_test_setup,
7921e8a1faeSThomas Huth     };
7931e8a1faeSThomas Huth 
7941e8a1faeSThomas Huth     qos_add_test("indirect", "virtio-blk", indirect, &opts);
7951e8a1faeSThomas Huth     qos_add_test("config", "virtio-blk", config, &opts);
7961e8a1faeSThomas Huth     qos_add_test("basic", "virtio-blk", basic, &opts);
7971e8a1faeSThomas Huth     qos_add_test("resize", "virtio-blk", resize, &opts);
7981e8a1faeSThomas Huth 
7991e8a1faeSThomas Huth     /* tests just for virtio-blk-pci */
8001e8a1faeSThomas Huth     qos_add_test("msix", "virtio-blk-pci", msix, &opts);
8011e8a1faeSThomas Huth     qos_add_test("idx", "virtio-blk-pci", idx, &opts);
8021e8a1faeSThomas Huth     qos_add_test("nxvirtq", "virtio-blk-pci",
8031e8a1faeSThomas Huth                       test_nonexistent_virtqueue, &opts);
8041e8a1faeSThomas Huth     qos_add_test("hotplug", "virtio-blk-pci", pci_hotplug, &opts);
8051e8a1faeSThomas Huth }
8061e8a1faeSThomas Huth 
8071e8a1faeSThomas Huth libqos_init(register_virtio_blk_test);
808