libqos: extend feature bits to 64-bit
[qemu/ar7.git] / tests / virtio-blk-test.c
blob31680cc15916cf2a8510b0080b70caa9a9e58f2d
1 /*
2 * QTest testcase for VirtIO Block Device
4 * Copyright (c) 2014 SUSE LINUX Products GmbH
5 * Copyright (c) 2014 Marc MarĂ­
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 */
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"
20 /* TODO actually test the results and get rid of this */
21 #define qmp_discard_response(...) qobject_unref(qmp(__VA_ARGS__))
23 #define TEST_IMAGE_SIZE (64 * 1024 * 1024)
24 #define QVIRTIO_BLK_TIMEOUT_US (30 * 1000 * 1000)
25 #define PCI_SLOT_HP 0x06
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;
36 #ifdef HOST_WORDS_BIGENDIAN
37 const bool host_is_big_endian = true;
38 #else
39 const bool host_is_big_endian; /* false */
40 #endif
42 static void drive_destroy(void *path)
44 unlink(path);
45 g_free(path);
46 qos_invalidate_command_line();
49 static char *drive_create(void)
51 int fd, ret;
52 char *t_path = g_strdup("/tmp/qtest.XXXXXX");
54 /* Create a temporary raw image */
55 fd = mkstemp(t_path);
56 g_assert_cmpint(fd, >=, 0);
57 ret = ftruncate(fd, TEST_IMAGE_SIZE);
58 g_assert_cmpint(ret, ==, 0);
59 close(fd);
61 g_test_queue_destroy(drive_destroy, t_path);
62 return t_path;
65 static inline void virtio_blk_fix_request(QVirtioDevice *d, QVirtioBlkReq *req)
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);
75 static inline void virtio_blk_fix_dwz_hdr(QVirtioDevice *d,
76 struct virtio_blk_discard_write_zeroes *dwz_hdr)
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);
85 static uint64_t virtio_blk_request(QGuestAllocator *alloc, QVirtioDevice *d,
86 QVirtioBlkReq *req, uint64_t data_size)
88 uint64_t addr;
89 uint8_t status = 0xFF;
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);
105 addr = guest_alloc(alloc, sizeof(*req) + data_size);
107 virtio_blk_fix_request(d, req);
109 memwrite(addr, req, 16);
110 memwrite(addr + 16, req->data, data_size);
111 memwrite(addr + 16 + data_size, &status, sizeof(status));
113 return addr;
116 static void test_basic(QVirtioDevice *dev, QGuestAllocator *alloc,
117 QVirtQueue *vq)
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;
128 features = qvirtio_get_features(dev);
129 features = features & ~(QVIRTIO_F_BAD_FEATURE |
130 (1u << VIRTIO_RING_F_INDIRECT_DESC) |
131 (1u << VIRTIO_RING_F_EVENT_IDX) |
132 (1u << VIRTIO_BLK_F_SCSI));
133 qvirtio_set_features(dev, features);
135 capacity = qvirtio_config_readq(dev, 0);
136 g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
138 qvirtio_set_driver_ok(dev);
140 /* Write and read with 3 descriptor layout */
141 /* Write request */
142 req.type = VIRTIO_BLK_T_OUT;
143 req.ioprio = 1;
144 req.sector = 0;
145 req.data = g_malloc0(512);
146 strcpy(req.data, "TEST");
148 req_addr = virtio_blk_request(alloc, dev, &req, 512);
150 g_free(req.data);
152 free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
153 qvirtqueue_add(qts, vq, req_addr + 16, 512, false, true);
154 qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
156 qvirtqueue_kick(qts, dev, vq, free_head);
158 qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
159 QVIRTIO_BLK_TIMEOUT_US);
160 status = readb(req_addr + 528);
161 g_assert_cmpint(status, ==, 0);
163 guest_free(alloc, req_addr);
165 /* Read request */
166 req.type = VIRTIO_BLK_T_IN;
167 req.ioprio = 1;
168 req.sector = 0;
169 req.data = g_malloc0(512);
171 req_addr = virtio_blk_request(alloc, dev, &req, 512);
173 g_free(req.data);
175 free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
176 qvirtqueue_add(qts, vq, req_addr + 16, 512, true, true);
177 qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
179 qvirtqueue_kick(qts, dev, vq, free_head);
181 qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
182 QVIRTIO_BLK_TIMEOUT_US);
183 status = readb(req_addr + 528);
184 g_assert_cmpint(status, ==, 0);
186 data = g_malloc0(512);
187 memread(req_addr + 16, data, 512);
188 g_assert_cmpstr(data, ==, "TEST");
189 g_free(data);
191 guest_free(alloc, req_addr);
193 if (features & (1u << VIRTIO_BLK_F_WRITE_ZEROES)) {
194 struct virtio_blk_discard_write_zeroes dwz_hdr;
195 void *expected;
198 * WRITE_ZEROES request on the same sector of previous test where
199 * we wrote "TEST".
201 req.type = VIRTIO_BLK_T_WRITE_ZEROES;
202 req.data = (char *) &dwz_hdr;
203 dwz_hdr.sector = 0;
204 dwz_hdr.num_sectors = 1;
205 dwz_hdr.flags = 0;
207 virtio_blk_fix_dwz_hdr(dev, &dwz_hdr);
209 req_addr = virtio_blk_request(alloc, dev, &req, sizeof(dwz_hdr));
211 free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
212 qvirtqueue_add(qts, vq, req_addr + 16, sizeof(dwz_hdr), false, true);
213 qvirtqueue_add(qts, vq, req_addr + 16 + sizeof(dwz_hdr), 1, true,
214 false);
216 qvirtqueue_kick(qts, dev, vq, free_head);
218 qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
219 QVIRTIO_BLK_TIMEOUT_US);
220 status = readb(req_addr + 16 + sizeof(dwz_hdr));
221 g_assert_cmpint(status, ==, 0);
223 guest_free(alloc, req_addr);
225 /* Read request to check if the sector contains all zeroes */
226 req.type = VIRTIO_BLK_T_IN;
227 req.ioprio = 1;
228 req.sector = 0;
229 req.data = g_malloc0(512);
231 req_addr = virtio_blk_request(alloc, dev, &req, 512);
233 g_free(req.data);
235 free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
236 qvirtqueue_add(qts, vq, req_addr + 16, 512, true, true);
237 qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
239 qvirtqueue_kick(qts, dev, vq, free_head);
241 qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
242 QVIRTIO_BLK_TIMEOUT_US);
243 status = readb(req_addr + 528);
244 g_assert_cmpint(status, ==, 0);
246 data = g_malloc(512);
247 expected = g_malloc0(512);
248 memread(req_addr + 16, data, 512);
249 g_assert_cmpmem(data, 512, expected, 512);
250 g_free(expected);
251 g_free(data);
253 guest_free(alloc, req_addr);
256 if (features & (1u << VIRTIO_BLK_F_DISCARD)) {
257 struct virtio_blk_discard_write_zeroes dwz_hdr;
259 req.type = VIRTIO_BLK_T_DISCARD;
260 req.data = (char *) &dwz_hdr;
261 dwz_hdr.sector = 0;
262 dwz_hdr.num_sectors = 1;
263 dwz_hdr.flags = 0;
265 virtio_blk_fix_dwz_hdr(dev, &dwz_hdr);
267 req_addr = virtio_blk_request(alloc, dev, &req, sizeof(dwz_hdr));
269 free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
270 qvirtqueue_add(qts, vq, req_addr + 16, sizeof(dwz_hdr), false, true);
271 qvirtqueue_add(qts, vq, req_addr + 16 + sizeof(dwz_hdr), 1, true, false);
273 qvirtqueue_kick(qts, dev, vq, free_head);
275 qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
276 QVIRTIO_BLK_TIMEOUT_US);
277 status = readb(req_addr + 16 + sizeof(dwz_hdr));
278 g_assert_cmpint(status, ==, 0);
280 guest_free(alloc, req_addr);
283 if (features & (1u << VIRTIO_F_ANY_LAYOUT)) {
284 /* Write and read with 2 descriptor layout */
285 /* Write request */
286 req.type = VIRTIO_BLK_T_OUT;
287 req.ioprio = 1;
288 req.sector = 1;
289 req.data = g_malloc0(512);
290 strcpy(req.data, "TEST");
292 req_addr = virtio_blk_request(alloc, dev, &req, 512);
294 g_free(req.data);
296 free_head = qvirtqueue_add(qts, vq, req_addr, 528, false, true);
297 qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
298 qvirtqueue_kick(qts, dev, vq, free_head);
300 qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
301 QVIRTIO_BLK_TIMEOUT_US);
302 status = readb(req_addr + 528);
303 g_assert_cmpint(status, ==, 0);
305 guest_free(alloc, req_addr);
307 /* Read request */
308 req.type = VIRTIO_BLK_T_IN;
309 req.ioprio = 1;
310 req.sector = 1;
311 req.data = g_malloc0(512);
313 req_addr = virtio_blk_request(alloc, dev, &req, 512);
315 g_free(req.data);
317 free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
318 qvirtqueue_add(qts, vq, req_addr + 16, 513, true, false);
320 qvirtqueue_kick(qts, dev, vq, free_head);
322 qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
323 QVIRTIO_BLK_TIMEOUT_US);
324 status = readb(req_addr + 528);
325 g_assert_cmpint(status, ==, 0);
327 data = g_malloc0(512);
328 memread(req_addr + 16, data, 512);
329 g_assert_cmpstr(data, ==, "TEST");
330 g_free(data);
332 guest_free(alloc, req_addr);
336 static void basic(void *obj, void *data, QGuestAllocator *t_alloc)
338 QVirtioBlk *blk_if = obj;
339 QVirtQueue *vq;
340 vq = qvirtqueue_setup(blk_if->vdev, t_alloc, 0);
341 test_basic(blk_if->vdev, t_alloc, vq);
342 qvirtqueue_cleanup(blk_if->vdev->bus, vq, t_alloc);
346 static void indirect(void *obj, void *u_data, QGuestAllocator *t_alloc)
348 QVirtQueue *vq;
349 QVirtioBlk *blk_if = obj;
350 QVirtioDevice *dev = blk_if->vdev;
351 QVirtioBlkReq req;
352 QVRingIndirectDesc *indirect;
353 uint64_t req_addr;
354 uint64_t capacity;
355 uint64_t features;
356 uint32_t free_head;
357 uint8_t status;
358 char *data;
359 QTestState *qts = global_qtest;
361 features = qvirtio_get_features(dev);
362 g_assert_cmphex(features & (1u << VIRTIO_RING_F_INDIRECT_DESC), !=, 0);
363 features = features & ~(QVIRTIO_F_BAD_FEATURE |
364 (1u << VIRTIO_RING_F_EVENT_IDX) |
365 (1u << VIRTIO_BLK_F_SCSI));
366 qvirtio_set_features(dev, features);
368 capacity = qvirtio_config_readq(dev, 0);
369 g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
371 vq = qvirtqueue_setup(dev, t_alloc, 0);
372 qvirtio_set_driver_ok(dev);
374 /* Write request */
375 req.type = VIRTIO_BLK_T_OUT;
376 req.ioprio = 1;
377 req.sector = 0;
378 req.data = g_malloc0(512);
379 strcpy(req.data, "TEST");
381 req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
383 g_free(req.data);
385 indirect = qvring_indirect_desc_setup(qts, dev, t_alloc, 2);
386 qvring_indirect_desc_add(qts, indirect, req_addr, 528, false);
387 qvring_indirect_desc_add(qts, indirect, req_addr + 528, 1, true);
388 free_head = qvirtqueue_add_indirect(qts, vq, indirect);
389 qvirtqueue_kick(qts, dev, vq, free_head);
391 qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
392 QVIRTIO_BLK_TIMEOUT_US);
393 status = readb(req_addr + 528);
394 g_assert_cmpint(status, ==, 0);
396 g_free(indirect);
397 guest_free(t_alloc, req_addr);
399 /* Read request */
400 req.type = VIRTIO_BLK_T_IN;
401 req.ioprio = 1;
402 req.sector = 0;
403 req.data = g_malloc0(512);
404 strcpy(req.data, "TEST");
406 req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
408 g_free(req.data);
410 indirect = qvring_indirect_desc_setup(qts, dev, t_alloc, 2);
411 qvring_indirect_desc_add(qts, indirect, req_addr, 16, false);
412 qvring_indirect_desc_add(qts, indirect, req_addr + 16, 513, true);
413 free_head = qvirtqueue_add_indirect(qts, vq, indirect);
414 qvirtqueue_kick(qts, dev, vq, free_head);
416 qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
417 QVIRTIO_BLK_TIMEOUT_US);
418 status = readb(req_addr + 528);
419 g_assert_cmpint(status, ==, 0);
421 data = g_malloc0(512);
422 memread(req_addr + 16, data, 512);
423 g_assert_cmpstr(data, ==, "TEST");
424 g_free(data);
426 g_free(indirect);
427 guest_free(t_alloc, req_addr);
428 qvirtqueue_cleanup(dev->bus, vq, t_alloc);
431 static void config(void *obj, void *data, QGuestAllocator *t_alloc)
433 QVirtioBlk *blk_if = obj;
434 QVirtioDevice *dev = blk_if->vdev;
435 int n_size = TEST_IMAGE_SIZE / 2;
436 uint64_t features;
437 uint64_t capacity;
439 features = qvirtio_get_features(dev);
440 features = features & ~(QVIRTIO_F_BAD_FEATURE |
441 (1u << VIRTIO_RING_F_INDIRECT_DESC) |
442 (1u << VIRTIO_RING_F_EVENT_IDX) |
443 (1u << VIRTIO_BLK_F_SCSI));
444 qvirtio_set_features(dev, features);
446 capacity = qvirtio_config_readq(dev, 0);
447 g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
449 qvirtio_set_driver_ok(dev);
451 qmp_discard_response("{ 'execute': 'block_resize', "
452 " 'arguments': { 'device': 'drive0', "
453 " 'size': %d } }", n_size);
454 qvirtio_wait_config_isr(dev, QVIRTIO_BLK_TIMEOUT_US);
456 capacity = qvirtio_config_readq(dev, 0);
457 g_assert_cmpint(capacity, ==, n_size / 512);
460 static void msix(void *obj, void *u_data, QGuestAllocator *t_alloc)
462 QVirtQueue *vq;
463 QVirtioBlkPCI *blk = obj;
464 QVirtioPCIDevice *pdev = &blk->pci_vdev;
465 QVirtioDevice *dev = &pdev->vdev;
466 QVirtioBlkReq req;
467 int n_size = TEST_IMAGE_SIZE / 2;
468 uint64_t req_addr;
469 uint64_t capacity;
470 uint64_t features;
471 uint32_t free_head;
472 uint8_t status;
473 char *data;
474 QOSGraphObject *blk_object = obj;
475 QPCIDevice *pci_dev = blk_object->get_driver(blk_object, "pci-device");
476 QTestState *qts = global_qtest;
478 if (qpci_check_buggy_msi(pci_dev)) {
479 return;
482 qpci_msix_enable(pdev->pdev);
483 qvirtio_pci_set_msix_configuration_vector(pdev, t_alloc, 0);
485 features = qvirtio_get_features(dev);
486 features = features & ~(QVIRTIO_F_BAD_FEATURE |
487 (1u << VIRTIO_RING_F_INDIRECT_DESC) |
488 (1u << VIRTIO_RING_F_EVENT_IDX) |
489 (1u << VIRTIO_BLK_F_SCSI));
490 qvirtio_set_features(dev, features);
492 capacity = qvirtio_config_readq(dev, 0);
493 g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
495 vq = qvirtqueue_setup(dev, t_alloc, 0);
496 qvirtqueue_pci_msix_setup(pdev, (QVirtQueuePCI *)vq, t_alloc, 1);
498 qvirtio_set_driver_ok(dev);
500 qmp_discard_response("{ 'execute': 'block_resize', "
501 " 'arguments': { 'device': 'drive0', "
502 " 'size': %d } }", n_size);
504 qvirtio_wait_config_isr(dev, QVIRTIO_BLK_TIMEOUT_US);
506 capacity = qvirtio_config_readq(dev, 0);
507 g_assert_cmpint(capacity, ==, n_size / 512);
509 /* Write request */
510 req.type = VIRTIO_BLK_T_OUT;
511 req.ioprio = 1;
512 req.sector = 0;
513 req.data = g_malloc0(512);
514 strcpy(req.data, "TEST");
516 req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
518 g_free(req.data);
520 free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
521 qvirtqueue_add(qts, vq, req_addr + 16, 512, false, true);
522 qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
523 qvirtqueue_kick(qts, dev, vq, free_head);
525 qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
526 QVIRTIO_BLK_TIMEOUT_US);
528 status = readb(req_addr + 528);
529 g_assert_cmpint(status, ==, 0);
531 guest_free(t_alloc, req_addr);
533 /* Read request */
534 req.type = VIRTIO_BLK_T_IN;
535 req.ioprio = 1;
536 req.sector = 0;
537 req.data = g_malloc0(512);
539 req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
541 g_free(req.data);
543 free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
544 qvirtqueue_add(qts, vq, req_addr + 16, 512, true, true);
545 qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
547 qvirtqueue_kick(qts, dev, vq, free_head);
550 qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
551 QVIRTIO_BLK_TIMEOUT_US);
553 status = readb(req_addr + 528);
554 g_assert_cmpint(status, ==, 0);
556 data = g_malloc0(512);
557 memread(req_addr + 16, data, 512);
558 g_assert_cmpstr(data, ==, "TEST");
559 g_free(data);
561 guest_free(t_alloc, req_addr);
563 /* End test */
564 qpci_msix_disable(pdev->pdev);
565 qvirtqueue_cleanup(dev->bus, vq, t_alloc);
568 static void idx(void *obj, void *u_data, QGuestAllocator *t_alloc)
570 QVirtQueue *vq;
571 QVirtioBlkPCI *blk = obj;
572 QVirtioPCIDevice *pdev = &blk->pci_vdev;
573 QVirtioDevice *dev = &pdev->vdev;
574 QVirtioBlkReq req;
575 uint64_t req_addr;
576 uint64_t capacity;
577 uint64_t features;
578 uint32_t free_head;
579 uint32_t write_head;
580 uint32_t desc_idx;
581 uint8_t status;
582 char *data;
583 QOSGraphObject *blk_object = obj;
584 QPCIDevice *pci_dev = blk_object->get_driver(blk_object, "pci-device");
585 QTestState *qts = global_qtest;
587 if (qpci_check_buggy_msi(pci_dev)) {
588 return;
591 qpci_msix_enable(pdev->pdev);
592 qvirtio_pci_set_msix_configuration_vector(pdev, t_alloc, 0);
594 features = qvirtio_get_features(dev);
595 features = features & ~(QVIRTIO_F_BAD_FEATURE |
596 (1u << VIRTIO_RING_F_INDIRECT_DESC) |
597 (1u << VIRTIO_F_NOTIFY_ON_EMPTY) |
598 (1u << VIRTIO_BLK_F_SCSI));
599 qvirtio_set_features(dev, features);
601 capacity = qvirtio_config_readq(dev, 0);
602 g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
604 vq = qvirtqueue_setup(dev, t_alloc, 0);
605 qvirtqueue_pci_msix_setup(pdev, (QVirtQueuePCI *)vq, t_alloc, 1);
607 qvirtio_set_driver_ok(dev);
609 /* Write request */
610 req.type = VIRTIO_BLK_T_OUT;
611 req.ioprio = 1;
612 req.sector = 0;
613 req.data = g_malloc0(512);
614 strcpy(req.data, "TEST");
616 req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
618 g_free(req.data);
620 free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
621 qvirtqueue_add(qts, vq, req_addr + 16, 512, false, true);
622 qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
623 qvirtqueue_kick(qts, dev, vq, free_head);
625 qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
626 QVIRTIO_BLK_TIMEOUT_US);
628 /* Write request */
629 req.type = VIRTIO_BLK_T_OUT;
630 req.ioprio = 1;
631 req.sector = 1;
632 req.data = g_malloc0(512);
633 strcpy(req.data, "TEST");
635 req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
637 g_free(req.data);
639 /* Notify after processing the third request */
640 qvirtqueue_set_used_event(qts, vq, 2);
641 free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
642 qvirtqueue_add(qts, vq, req_addr + 16, 512, false, true);
643 qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
644 qvirtqueue_kick(qts, dev, vq, free_head);
645 write_head = free_head;
647 /* No notification expected */
648 status = qvirtio_wait_status_byte_no_isr(qts, dev,
649 vq, req_addr + 528,
650 QVIRTIO_BLK_TIMEOUT_US);
651 g_assert_cmpint(status, ==, 0);
653 guest_free(t_alloc, req_addr);
655 /* Read request */
656 req.type = VIRTIO_BLK_T_IN;
657 req.ioprio = 1;
658 req.sector = 1;
659 req.data = g_malloc0(512);
661 req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
663 g_free(req.data);
665 free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
666 qvirtqueue_add(qts, vq, req_addr + 16, 512, true, true);
667 qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
669 qvirtqueue_kick(qts, dev, vq, free_head);
671 /* We get just one notification for both requests */
672 qvirtio_wait_used_elem(qts, dev, vq, write_head, NULL,
673 QVIRTIO_BLK_TIMEOUT_US);
674 g_assert(qvirtqueue_get_buf(qts, vq, &desc_idx, NULL));
675 g_assert_cmpint(desc_idx, ==, free_head);
677 status = readb(req_addr + 528);
678 g_assert_cmpint(status, ==, 0);
680 data = g_malloc0(512);
681 memread(req_addr + 16, data, 512);
682 g_assert_cmpstr(data, ==, "TEST");
683 g_free(data);
685 guest_free(t_alloc, req_addr);
687 /* End test */
688 qpci_msix_disable(pdev->pdev);
690 qvirtqueue_cleanup(dev->bus, vq, t_alloc);
693 static void pci_hotplug(void *obj, void *data, QGuestAllocator *t_alloc)
695 QVirtioPCIDevice *dev1 = obj;
696 QVirtioPCIDevice *dev;
697 QTestState *qts = dev1->pdev->bus->qts;
699 /* plug secondary disk */
700 qtest_qmp_device_add(qts, "virtio-blk-pci", "drv1",
701 "{'addr': %s, 'drive': 'drive1'}",
702 stringify(PCI_SLOT_HP) ".0");
704 dev = virtio_pci_new(dev1->pdev->bus,
705 &(QPCIAddress) { .devfn = QPCI_DEVFN(PCI_SLOT_HP, 0) });
706 g_assert_nonnull(dev);
707 g_assert_cmpint(dev->vdev.device_type, ==, VIRTIO_ID_BLOCK);
708 qvirtio_pci_device_disable(dev);
709 qos_object_destroy((QOSGraphObject *)dev);
711 /* unplug secondary disk */
712 qpci_unplug_acpi_device_test(qts, "drv1", PCI_SLOT_HP);
716 * Check that setting the vring addr on a non-existent virtqueue does
717 * not crash.
719 static void test_nonexistent_virtqueue(void *obj, void *data,
720 QGuestAllocator *t_alloc)
722 QVirtioBlkPCI *blk = obj;
723 QVirtioPCIDevice *pdev = &blk->pci_vdev;
724 QPCIBar bar0;
725 QPCIDevice *dev;
727 dev = qpci_device_find(pdev->pdev->bus, QPCI_DEVFN(4, 0));
728 g_assert(dev != NULL);
729 qpci_device_enable(dev);
731 bar0 = qpci_iomap(dev, 0, NULL);
733 qpci_io_writeb(dev, bar0, VIRTIO_PCI_QUEUE_SEL, 2);
734 qpci_io_writel(dev, bar0, VIRTIO_PCI_QUEUE_PFN, 1);
737 g_free(dev);
740 static void resize(void *obj, void *data, QGuestAllocator *t_alloc)
742 QVirtioBlk *blk_if = obj;
743 QVirtioDevice *dev = blk_if->vdev;
744 int n_size = TEST_IMAGE_SIZE / 2;
745 uint64_t capacity;
746 QVirtQueue *vq;
747 QTestState *qts = global_qtest;
749 vq = qvirtqueue_setup(dev, t_alloc, 0);
751 test_basic(dev, t_alloc, vq);
753 qmp_discard_response("{ 'execute': 'block_resize', "
754 " 'arguments': { 'device': 'drive0', "
755 " 'size': %d } }", n_size);
757 qvirtio_wait_queue_isr(qts, dev, vq, QVIRTIO_BLK_TIMEOUT_US);
759 capacity = qvirtio_config_readq(dev, 0);
760 g_assert_cmpint(capacity, ==, n_size / 512);
762 qvirtqueue_cleanup(dev->bus, vq, t_alloc);
766 static void *virtio_blk_test_setup(GString *cmd_line, void *arg)
768 char *tmp_path = drive_create();
770 g_string_append_printf(cmd_line,
771 " -drive if=none,id=drive0,file=%s,"
772 "format=raw,auto-read-only=off "
773 "-drive if=none,id=drive1,file=null-co://,"
774 "file.read-zeroes=on,format=raw ",
775 tmp_path);
777 return arg;
780 static void register_virtio_blk_test(void)
782 QOSGraphTestOptions opts = {
783 .before = virtio_blk_test_setup,
786 qos_add_test("indirect", "virtio-blk", indirect, &opts);
787 qos_add_test("config", "virtio-blk", config, &opts);
788 qos_add_test("basic", "virtio-blk", basic, &opts);
789 qos_add_test("resize", "virtio-blk", resize, &opts);
791 /* tests just for virtio-blk-pci */
792 qos_add_test("msix", "virtio-blk-pci", msix, &opts);
793 qos_add_test("idx", "virtio-blk-pci", idx, &opts);
794 qos_add_test("nxvirtq", "virtio-blk-pci",
795 test_nonexistent_virtqueue, &opts);
796 qos_add_test("hotplug", "virtio-blk-pci", pci_hotplug, &opts);
799 libqos_init(register_virtio_blk_test);