virtio-blk-test.c 19.5 KB
Newer Older
A
Andreas Färber 已提交
1 2 3 4
/*
 * QTest testcase for VirtIO Block Device
 *
 * Copyright (c) 2014 SUSE LINUX Products GmbH
5
 * Copyright (c) 2014 Marc Marí
A
Andreas Färber 已提交
6 7 8 9 10 11 12
 *
 * This work is licensed under the terms of the GNU GPL, version 2 or later.
 * See the COPYING file in the top-level directory.
 */

#include <glib.h>
#include <string.h>
13 14 15
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
A
Andreas Färber 已提交
16
#include "libqtest.h"
17 18 19
#include "libqos/virtio.h"
#include "libqos/virtio-pci.h"
#include "libqos/pci-pc.h"
20 21 22
#include "libqos/malloc.h"
#include "libqos/malloc-pc.h"
#include "qemu/bswap.h"
A
Andreas Färber 已提交
23

24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
#define QVIRTIO_BLK_F_BARRIER       0x00000001
#define QVIRTIO_BLK_F_SIZE_MAX      0x00000002
#define QVIRTIO_BLK_F_SEG_MAX       0x00000004
#define QVIRTIO_BLK_F_GEOMETRY      0x00000010
#define QVIRTIO_BLK_F_RO            0x00000020
#define QVIRTIO_BLK_F_BLK_SIZE      0x00000040
#define QVIRTIO_BLK_F_SCSI          0x00000080
#define QVIRTIO_BLK_F_WCE           0x00000200
#define QVIRTIO_BLK_F_TOPOLOGY      0x00000400
#define QVIRTIO_BLK_F_CONFIG_WCE    0x00000800

#define QVIRTIO_BLK_T_IN            0
#define QVIRTIO_BLK_T_OUT           1
#define QVIRTIO_BLK_T_SCSI_CMD      2
#define QVIRTIO_BLK_T_SCSI_CMD_OUT  3
#define QVIRTIO_BLK_T_FLUSH         4
#define QVIRTIO_BLK_T_FLUSH_OUT     5
#define QVIRTIO_BLK_T_GET_ID        8

#define TEST_IMAGE_SIZE         (64 * 1024 * 1024)
44
#define QVIRTIO_BLK_TIMEOUT_US  (30 * 1000 * 1000)
45 46 47
#define PCI_SLOT                0x04
#define PCI_FN                  0x00

48 49
#define PCI_SLOT_HP             0x06

50 51 52 53 54 55 56
typedef struct QVirtioBlkReq {
    uint32_t type;
    uint32_t ioprio;
    uint64_t sector;
    char *data;
    uint8_t status;
} QVirtioBlkReq;
57 58

static QPCIBus *test_start(void)
A
Andreas Färber 已提交
59
{
60
    char *cmdline;
61 62 63 64 65 66 67 68 69 70
    char tmp_path[] = "/tmp/qtest.XXXXXX";
    int fd, ret;

    /* Create a temporary raw image */
    fd = mkstemp(tmp_path);
    g_assert_cmpint(fd, >=, 0);
    ret = ftruncate(fd, TEST_IMAGE_SIZE);
    g_assert_cmpint(ret, ==, 0);
    close(fd);

71 72
    cmdline = g_strdup_printf("-drive if=none,id=drive0,file=%s,format=raw "
                              "-drive if=none,id=drive1,file=/dev/null,format=raw "
73 74 75
                              "-device virtio-blk-pci,id=drv0,drive=drive0,"
                              "addr=%x.%x",
                              tmp_path, PCI_SLOT, PCI_FN);
76 77
    qtest_start(cmdline);
    unlink(tmp_path);
78
    g_free(cmdline);
79 80 81 82 83 84 85 86 87

    return qpci_init_pc();
}

static void test_end(void)
{
    qtest_end();
}

88
static QVirtioPCIDevice *virtio_blk_init(QPCIBus *bus, int slot)
89 90 91 92 93 94
{
    QVirtioPCIDevice *dev;

    dev = qvirtio_pci_device_find(bus, QVIRTIO_BLK_DEVICE_ID);
    g_assert(dev != NULL);
    g_assert_cmphex(dev->vdev.device_type, ==, QVIRTIO_BLK_DEVICE_ID);
95
    g_assert_cmphex(dev->pdev->devfn, ==, ((slot << 3) | PCI_FN));
96

97 98 99 100 101 102 103 104
    qvirtio_pci_device_enable(dev);
    qvirtio_reset(&qvirtio_pci, &dev->vdev);
    qvirtio_set_acknowledge(&qvirtio_pci, &dev->vdev);
    qvirtio_set_driver(&qvirtio_pci, &dev->vdev);

    return dev;
}

105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
static inline void virtio_blk_fix_request(QVirtioBlkReq *req)
{
#ifdef HOST_WORDS_BIGENDIAN
    bool host_endian = true;
#else
    bool host_endian = false;
#endif

    if (qtest_big_endian() != host_endian) {
        req->type = bswap32(req->type);
        req->ioprio = bswap32(req->ioprio);
        req->sector = bswap64(req->sector);
    }
}

static uint64_t virtio_blk_request(QGuestAllocator *alloc, QVirtioBlkReq *req,
                                                            uint64_t data_size)
{
    uint64_t addr;
    uint8_t status = 0xFF;

    g_assert_cmpuint(data_size % 512, ==, 0);
    addr = guest_alloc(alloc, sizeof(*req) + data_size);

    virtio_blk_fix_request(req);

    memwrite(addr, req, 16);
    memwrite(addr + 16, req->data, data_size);
    memwrite(addr + 16 + data_size, &status, sizeof(status));

    return addr;
}

138 139 140 141
static void pci_basic(void)
{
    QVirtioPCIDevice *dev;
    QPCIBus *bus;
M
Marc Marí 已提交
142
    QVirtQueuePCI *vqpci;
143 144
    QGuestAllocator *alloc;
    QVirtioBlkReq req;
145
    void *addr;
146
    uint64_t req_addr;
147
    uint64_t capacity;
148 149 150 151
    uint32_t features;
    uint32_t free_head;
    uint8_t status;
    char *data;
152 153 154

    bus = test_start();

155
    dev = virtio_blk_init(bus, PCI_SLOT);
156 157 158 159 160 161 162

    /* MSI-X is not enabled */
    addr = dev->addr + QVIRTIO_DEVICE_SPECIFIC_NO_MSIX;

    capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr);
    g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);

163 164 165 166 167 168 169
    features = qvirtio_get_features(&qvirtio_pci, &dev->vdev);
    features = features & ~(QVIRTIO_F_BAD_FEATURE |
                    QVIRTIO_F_RING_INDIRECT_DESC | QVIRTIO_F_RING_EVENT_IDX |
                            QVIRTIO_BLK_F_SCSI);
    qvirtio_set_features(&qvirtio_pci, &dev->vdev, features);

    alloc = pc_alloc_init();
M
Marc Marí 已提交
170 171
    vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev,
                                                                    alloc, 0);
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186

    qvirtio_set_driver_ok(&qvirtio_pci, &dev->vdev);

    /* Write and read with 2 descriptor layout */
    /* Write request */
    req.type = QVIRTIO_BLK_T_OUT;
    req.ioprio = 1;
    req.sector = 0;
    req.data = g_malloc0(512);
    strcpy(req.data, "TEST");

    req_addr = virtio_blk_request(alloc, &req, 512);

    g_free(req.data);

M
Marc Marí 已提交
187 188 189
    free_head = qvirtqueue_add(&vqpci->vq, req_addr, 528, false, true);
    qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false);
    qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head);
190

191 192
    qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq,
                           QVIRTIO_BLK_TIMEOUT_US);
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
    status = readb(req_addr + 528);
    g_assert_cmpint(status, ==, 0);

    guest_free(alloc, req_addr);

    /* Read request */
    req.type = QVIRTIO_BLK_T_IN;
    req.ioprio = 1;
    req.sector = 0;
    req.data = g_malloc0(512);

    req_addr = virtio_blk_request(alloc, &req, 512);

    g_free(req.data);

M
Marc Marí 已提交
208 209
    free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true);
    qvirtqueue_add(&vqpci->vq, req_addr + 16, 513, true, false);
210

M
Marc Marí 已提交
211
    qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head);
212

213 214
    qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq,
                           QVIRTIO_BLK_TIMEOUT_US);
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
    status = readb(req_addr + 528);
    g_assert_cmpint(status, ==, 0);

    data = g_malloc0(512);
    memread(req_addr + 16, data, 512);
    g_assert_cmpstr(data, ==, "TEST");
    g_free(data);

    guest_free(alloc, req_addr);

    /* Write and read with 3 descriptor layout */
    /* Write request */
    req.type = QVIRTIO_BLK_T_OUT;
    req.ioprio = 1;
    req.sector = 1;
    req.data = g_malloc0(512);
    strcpy(req.data, "TEST");

    req_addr = virtio_blk_request(alloc, &req, 512);

M
Marc Marí 已提交
235 236 237
    free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true);
    qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, false, true);
    qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false);
238

M
Marc Marí 已提交
239
    qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head);
240

241 242
    qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq,
                           QVIRTIO_BLK_TIMEOUT_US);
243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
    status = readb(req_addr + 528);
    g_assert_cmpint(status, ==, 0);

    guest_free(alloc, req_addr);

    /* Read request */
    req.type = QVIRTIO_BLK_T_IN;
    req.ioprio = 1;
    req.sector = 1;
    req.data = g_malloc0(512);

    req_addr = virtio_blk_request(alloc, &req, 512);

    g_free(req.data);

M
Marc Marí 已提交
258 259 260
    free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true);
    qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, true, true);
    qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false);
261

M
Marc Marí 已提交
262
    qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head);
263

264 265
    qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq,
                           QVIRTIO_BLK_TIMEOUT_US);
266 267 268 269 270 271 272 273 274 275 276
    status = readb(req_addr + 528);
    g_assert_cmpint(status, ==, 0);

    data = g_malloc0(512);
    memread(req_addr + 16, data, 512);
    g_assert_cmpstr(data, ==, "TEST");
    g_free(data);

    guest_free(alloc, req_addr);

    /* End test */
M
Marc Marí 已提交
277
    guest_free(alloc, vqpci->vq.desc);
278
    qvirtio_pci_device_disable(dev);
279 280
    g_free(dev);
    test_end();
A
Andreas Färber 已提交
281 282
}

283 284 285 286
static void pci_indirect(void)
{
    QVirtioPCIDevice *dev;
    QPCIBus *bus;
M
Marc Marí 已提交
287
    QVirtQueuePCI *vqpci;
288 289 290 291 292 293 294 295 296 297 298 299 300
    QGuestAllocator *alloc;
    QVirtioBlkReq req;
    QVRingIndirectDesc *indirect;
    void *addr;
    uint64_t req_addr;
    uint64_t capacity;
    uint32_t features;
    uint32_t free_head;
    uint8_t status;
    char *data;

    bus = test_start();

301
    dev = virtio_blk_init(bus, PCI_SLOT);
302 303 304 305 306 307 308 309 310 311 312 313 314 315

    /* MSI-X is not enabled */
    addr = dev->addr + QVIRTIO_DEVICE_SPECIFIC_NO_MSIX;

    capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr);
    g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);

    features = qvirtio_get_features(&qvirtio_pci, &dev->vdev);
    g_assert_cmphex(features & QVIRTIO_F_RING_INDIRECT_DESC, !=, 0);
    features = features & ~(QVIRTIO_F_BAD_FEATURE | QVIRTIO_F_RING_EVENT_IDX |
                                                            QVIRTIO_BLK_F_SCSI);
    qvirtio_set_features(&qvirtio_pci, &dev->vdev, features);

    alloc = pc_alloc_init();
M
Marc Marí 已提交
316 317
    vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev,
                                                                    alloc, 0);
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333
    qvirtio_set_driver_ok(&qvirtio_pci, &dev->vdev);

    /* Write request */
    req.type = QVIRTIO_BLK_T_OUT;
    req.ioprio = 1;
    req.sector = 0;
    req.data = g_malloc0(512);
    strcpy(req.data, "TEST");

    req_addr = virtio_blk_request(alloc, &req, 512);

    g_free(req.data);

    indirect = qvring_indirect_desc_setup(&dev->vdev, alloc, 2);
    qvring_indirect_desc_add(indirect, req_addr, 528, false);
    qvring_indirect_desc_add(indirect, req_addr + 528, 1, true);
M
Marc Marí 已提交
334 335
    free_head = qvirtqueue_add_indirect(&vqpci->vq, indirect);
    qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head);
336

337 338
    qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq,
                           QVIRTIO_BLK_TIMEOUT_US);
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358
    status = readb(req_addr + 528);
    g_assert_cmpint(status, ==, 0);

    g_free(indirect);
    guest_free(alloc, req_addr);

    /* Read request */
    req.type = QVIRTIO_BLK_T_IN;
    req.ioprio = 1;
    req.sector = 0;
    req.data = g_malloc0(512);
    strcpy(req.data, "TEST");

    req_addr = virtio_blk_request(alloc, &req, 512);

    g_free(req.data);

    indirect = qvring_indirect_desc_setup(&dev->vdev, alloc, 2);
    qvring_indirect_desc_add(indirect, req_addr, 16, false);
    qvring_indirect_desc_add(indirect, req_addr + 16, 513, true);
M
Marc Marí 已提交
359 360
    free_head = qvirtqueue_add_indirect(&vqpci->vq, indirect);
    qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head);
361

362 363
    qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq,
                           QVIRTIO_BLK_TIMEOUT_US);
364 365 366 367 368 369 370 371 372 373 374 375
    status = readb(req_addr + 528);
    g_assert_cmpint(status, ==, 0);

    data = g_malloc0(512);
    memread(req_addr + 16, data, 512);
    g_assert_cmpstr(data, ==, "TEST");
    g_free(data);

    g_free(indirect);
    guest_free(alloc, req_addr);

    /* End test */
M
Marc Marí 已提交
376
    guest_free(alloc, vqpci->vq.desc);
377 378 379 380 381
    qvirtio_pci_device_disable(dev);
    g_free(dev);
    test_end();
}

382 383 384 385 386 387 388 389 390 391
static void pci_config(void)
{
    QVirtioPCIDevice *dev;
    QPCIBus *bus;
    int n_size = TEST_IMAGE_SIZE / 2;
    void *addr;
    uint64_t capacity;

    bus = test_start();

392
    dev = virtio_blk_init(bus, PCI_SLOT);
393 394 395 396 397 398 399 400 401 402 403

    /* MSI-X is not enabled */
    addr = dev->addr + QVIRTIO_DEVICE_SPECIFIC_NO_MSIX;

    capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr);
    g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);

    qvirtio_set_driver_ok(&qvirtio_pci, &dev->vdev);

    qmp("{ 'execute': 'block_resize', 'arguments': { 'device': 'drive0', "
                                                    " 'size': %d } }", n_size);
404
    qvirtio_wait_config_isr(&qvirtio_pci, &dev->vdev, QVIRTIO_BLK_TIMEOUT_US);
405 406 407 408 409 410 411 412 413

    capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr);
    g_assert_cmpint(capacity, ==, n_size / 512);

    qvirtio_pci_device_disable(dev);
    g_free(dev);
    test_end();
}

M
Marc Marí 已提交
414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432
static void pci_msix(void)
{
    QVirtioPCIDevice *dev;
    QPCIBus *bus;
    QVirtQueuePCI *vqpci;
    QGuestAllocator *alloc;
    QVirtioBlkReq req;
    int n_size = TEST_IMAGE_SIZE / 2;
    void *addr;
    uint64_t req_addr;
    uint64_t capacity;
    uint32_t features;
    uint32_t free_head;
    uint8_t status;
    char *data;

    bus = test_start();
    alloc = pc_alloc_init();

433
    dev = virtio_blk_init(bus, PCI_SLOT);
M
Marc Marí 已提交
434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458
    qpci_msix_enable(dev->pdev);

    qvirtio_pci_set_msix_configuration_vector(dev, alloc, 0);

    /* MSI-X is enabled */
    addr = dev->addr + QVIRTIO_DEVICE_SPECIFIC_MSIX;

    capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr);
    g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);

    features = qvirtio_get_features(&qvirtio_pci, &dev->vdev);
    features = features & ~(QVIRTIO_F_BAD_FEATURE |
                            QVIRTIO_F_RING_INDIRECT_DESC |
                            QVIRTIO_F_RING_EVENT_IDX | QVIRTIO_BLK_F_SCSI);
    qvirtio_set_features(&qvirtio_pci, &dev->vdev, features);

    vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev,
                                                                    alloc, 0);
    qvirtqueue_pci_msix_setup(dev, vqpci, alloc, 1);

    qvirtio_set_driver_ok(&qvirtio_pci, &dev->vdev);

    qmp("{ 'execute': 'block_resize', 'arguments': { 'device': 'drive0', "
                                                    " 'size': %d } }", n_size);

459
    qvirtio_wait_config_isr(&qvirtio_pci, &dev->vdev, QVIRTIO_BLK_TIMEOUT_US);
M
Marc Marí 已提交
460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478

    capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr);
    g_assert_cmpint(capacity, ==, n_size / 512);

    /* Write request */
    req.type = QVIRTIO_BLK_T_OUT;
    req.ioprio = 1;
    req.sector = 0;
    req.data = g_malloc0(512);
    strcpy(req.data, "TEST");

    req_addr = virtio_blk_request(alloc, &req, 512);

    g_free(req.data);

    free_head = qvirtqueue_add(&vqpci->vq, req_addr, 528, false, true);
    qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false);
    qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head);

479 480
    qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq,
                           QVIRTIO_BLK_TIMEOUT_US);
M
Marc Marí 已提交
481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501

    status = readb(req_addr + 528);
    g_assert_cmpint(status, ==, 0);

    guest_free(alloc, req_addr);

    /* Read request */
    req.type = QVIRTIO_BLK_T_IN;
    req.ioprio = 1;
    req.sector = 0;
    req.data = g_malloc0(512);

    req_addr = virtio_blk_request(alloc, &req, 512);

    g_free(req.data);

    free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true);
    qvirtqueue_add(&vqpci->vq, req_addr + 16, 513, true, false);

    qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head);

M
Marc Marí 已提交
502

503 504
    qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq,
                           QVIRTIO_BLK_TIMEOUT_US);
M
Marc Marí 已提交
505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541

    status = readb(req_addr + 528);
    g_assert_cmpint(status, ==, 0);

    data = g_malloc0(512);
    memread(req_addr + 16, data, 512);
    g_assert_cmpstr(data, ==, "TEST");
    g_free(data);

    guest_free(alloc, req_addr);

    /* End test */
    guest_free(alloc, (uint64_t)vqpci->vq.desc);
    qpci_msix_disable(dev->pdev);
    qvirtio_pci_device_disable(dev);
    g_free(dev);
    test_end();
}

static void pci_idx(void)
{
    QVirtioPCIDevice *dev;
    QPCIBus *bus;
    QVirtQueuePCI *vqpci;
    QGuestAllocator *alloc;
    QVirtioBlkReq req;
    void *addr;
    uint64_t req_addr;
    uint64_t capacity;
    uint32_t features;
    uint32_t free_head;
    uint8_t status;
    char *data;

    bus = test_start();
    alloc = pc_alloc_init();

542
    dev = virtio_blk_init(bus, PCI_SLOT);
M
Marc Marí 已提交
543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579
    qpci_msix_enable(dev->pdev);

    qvirtio_pci_set_msix_configuration_vector(dev, alloc, 0);

    /* MSI-X is enabled */
    addr = dev->addr + QVIRTIO_DEVICE_SPECIFIC_MSIX;

    capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr);
    g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);

    features = qvirtio_get_features(&qvirtio_pci, &dev->vdev);
    features = features & ~(QVIRTIO_F_BAD_FEATURE |
                            QVIRTIO_F_RING_INDIRECT_DESC |
                            QVIRTIO_F_NOTIFY_ON_EMPTY | QVIRTIO_BLK_F_SCSI);
    qvirtio_set_features(&qvirtio_pci, &dev->vdev, features);

    vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev,
                                                                    alloc, 0);
    qvirtqueue_pci_msix_setup(dev, vqpci, alloc, 1);

    qvirtio_set_driver_ok(&qvirtio_pci, &dev->vdev);

    /* Write request */
    req.type = QVIRTIO_BLK_T_OUT;
    req.ioprio = 1;
    req.sector = 0;
    req.data = g_malloc0(512);
    strcpy(req.data, "TEST");

    req_addr = virtio_blk_request(alloc, &req, 512);

    g_free(req.data);

    free_head = qvirtqueue_add(&vqpci->vq, req_addr, 528, false, true);
    qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false);
    qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head);

580 581
    qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq,
                           QVIRTIO_BLK_TIMEOUT_US);
M
Marc Marí 已提交
582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600

    /* Write request */
    req.type = QVIRTIO_BLK_T_OUT;
    req.ioprio = 1;
    req.sector = 1;
    req.data = g_malloc0(512);
    strcpy(req.data, "TEST");

    req_addr = virtio_blk_request(alloc, &req, 512);

    g_free(req.data);

    /* Notify after processing the third request */
    qvirtqueue_set_used_event(&vqpci->vq, 2);
    free_head = qvirtqueue_add(&vqpci->vq, req_addr, 528, false, true);
    qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false);
    qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head);

    /* No notification expected */
601 602 603
    status = qvirtio_wait_status_byte_no_isr(&qvirtio_pci, &dev->vdev,
                                             &vqpci->vq, req_addr + 528,
                                             QVIRTIO_BLK_TIMEOUT_US);
M
Marc Marí 已提交
604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623
    g_assert_cmpint(status, ==, 0);

    guest_free(alloc, req_addr);

    /* Read request */
    req.type = QVIRTIO_BLK_T_IN;
    req.ioprio = 1;
    req.sector = 1;
    req.data = g_malloc0(512);

    req_addr = virtio_blk_request(alloc, &req, 512);

    g_free(req.data);

    free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true);
    qvirtqueue_add(&vqpci->vq, req_addr + 16, 513, true, false);

    qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head);


624 625
    qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq,
                           QVIRTIO_BLK_TIMEOUT_US);
M
Marc Marí 已提交
626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644

    status = readb(req_addr + 528);
    g_assert_cmpint(status, ==, 0);

    data = g_malloc0(512);
    memread(req_addr + 16, data, 512);
    g_assert_cmpstr(data, ==, "TEST");
    g_free(data);

    guest_free(alloc, req_addr);

    /* End test */
    guest_free(alloc, vqpci->vq.desc);
    qpci_msix_disable(dev->pdev);
    qvirtio_pci_device_disable(dev);
    g_free(dev);
    test_end();
}

645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665
static void hotplug(void)
{
    QPCIBus *bus;
    QVirtioPCIDevice *dev;

    bus = test_start();

    /* plug secondary disk */
    qpci_plug_device_test("virtio-blk-pci", "drv1", PCI_SLOT_HP,
                          "'drive': 'drive1'");

    dev = virtio_blk_init(bus, PCI_SLOT_HP);
    g_assert(dev);
    qvirtio_pci_device_disable(dev);
    g_free(dev);

    /* unplug secondary disk */
    qpci_unplug_acpi_device_test("drv1", PCI_SLOT_HP);
    test_end();
}

A
Andreas Färber 已提交
666 667 668 669 670 671
int main(int argc, char **argv)
{
    int ret;

    g_test_init(&argc, &argv, NULL);

672
    g_test_add_func("/virtio/blk/pci/basic", pci_basic);
673
    g_test_add_func("/virtio/blk/pci/indirect", pci_indirect);
674
    g_test_add_func("/virtio/blk/pci/config", pci_config);
M
Marc Marí 已提交
675
    g_test_add_func("/virtio/blk/pci/msix", pci_msix);
M
Marc Marí 已提交
676
    g_test_add_func("/virtio/blk/pci/idx", pci_idx);
677
    g_test_add_func("/virtio/blk/pci/hotplug", hotplug);
A
Andreas Färber 已提交
678

679
    ret = g_test_run();
A
Andreas Färber 已提交
680 681 682

    return ret;
}