]>
Commit | Line | Data |
---|---|---|
c7a59bed AF |
1 | /* |
2 | * QTest testcase for VirtIO Block Device | |
3 | * | |
4 | * Copyright (c) 2014 SUSE LINUX Products GmbH | |
311e666a | 5 | * Copyright (c) 2014 Marc Marí |
c7a59bed AF |
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 | ||
681c28a3 | 11 | #include "qemu/osdep.h" |
c7a59bed | 12 | #include "libqtest.h" |
a980f7f2 | 13 | #include "libqos/libqos-pc.h" |
30ca440e | 14 | #include "libqos/libqos-spapr.h" |
311e666a MM |
15 | #include "libqos/virtio.h" |
16 | #include "libqos/virtio-pci.h" | |
0a6ed700 | 17 | #include "libqos/virtio-mmio.h" |
0a6ed700 | 18 | #include "libqos/malloc-generic.h" |
bf3c63d2 | 19 | #include "qemu/bswap.h" |
8ac9e205 | 20 | #include "standard-headers/linux/virtio_ids.h" |
1373a4c2 | 21 | #include "standard-headers/linux/virtio_config.h" |
ee3b850a | 22 | #include "standard-headers/linux/virtio_ring.h" |
4565a3e0 | 23 | #include "standard-headers/linux/virtio_blk.h" |
c75f4c06 | 24 | #include "standard-headers/linux/virtio_pci.h" |
bf3c63d2 MM |
25 | |
26 | #define TEST_IMAGE_SIZE (64 * 1024 * 1024) | |
e8c81b4d | 27 | #define QVIRTIO_BLK_TIMEOUT_US (30 * 1000 * 1000) |
0a6ed700 | 28 | #define PCI_SLOT_HP 0x06 |
bf3c63d2 MM |
29 | #define PCI_SLOT 0x04 |
30 | #define PCI_FN 0x00 | |
31 | ||
0a6ed700 MM |
32 | #define MMIO_PAGE_SIZE 4096 |
33 | #define MMIO_DEV_BASE_ADDR 0x0A003E00 | |
34 | #define MMIO_RAM_ADDR 0x40000000 | |
35 | #define MMIO_RAM_SIZE 0x20000000 | |
aaf36070 | 36 | |
bf3c63d2 MM |
37 | typedef struct QVirtioBlkReq { |
38 | uint32_t type; | |
39 | uint32_t ioprio; | |
40 | uint64_t sector; | |
41 | char *data; | |
42 | uint8_t status; | |
43 | } QVirtioBlkReq; | |
311e666a | 44 | |
38d8364f | 45 | static char *drive_create(void) |
c7a59bed | 46 | { |
311e666a | 47 | int fd, ret; |
38d8364f | 48 | char *tmp_path = g_strdup("/tmp/qtest.XXXXXX"); |
311e666a MM |
49 | |
50 | /* Create a temporary raw image */ | |
51 | fd = mkstemp(tmp_path); | |
52 | g_assert_cmpint(fd, >=, 0); | |
53 | ret = ftruncate(fd, TEST_IMAGE_SIZE); | |
54 | g_assert_cmpint(ret, ==, 0); | |
55 | close(fd); | |
56 | ||
38d8364f MM |
57 | return tmp_path; |
58 | } | |
59 | ||
a980f7f2 | 60 | static QOSState *pci_test_start(void) |
38d8364f | 61 | { |
a980f7f2 | 62 | QOSState *qs; |
30ca440e | 63 | const char *arch = qtest_get_arch(); |
38d8364f | 64 | char *tmp_path; |
a980f7f2 | 65 | const char *cmd = "-drive if=none,id=drive0,file=%s,format=raw " |
2420d369 | 66 | "-drive if=none,id=drive1,file=null-co://,format=raw " |
a980f7f2 LV |
67 | "-device virtio-blk-pci,id=drv0,drive=drive0," |
68 | "addr=%x.%x"; | |
38d8364f MM |
69 | |
70 | tmp_path = drive_create(); | |
71 | ||
30ca440e LV |
72 | if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { |
73 | qs = qtest_pc_boot(cmd, tmp_path, PCI_SLOT, PCI_FN); | |
74 | } else if (strcmp(arch, "ppc64") == 0) { | |
75 | qs = qtest_spapr_boot(cmd, tmp_path, PCI_SLOT, PCI_FN); | |
76 | } else { | |
77 | g_printerr("virtio-blk tests are only available on x86 or ppc64\n"); | |
78 | exit(EXIT_FAILURE); | |
79 | } | |
311e666a | 80 | unlink(tmp_path); |
38d8364f | 81 | g_free(tmp_path); |
a980f7f2 | 82 | return qs; |
311e666a MM |
83 | } |
84 | ||
0a6ed700 MM |
85 | static void arm_test_start(void) |
86 | { | |
0a6ed700 MM |
87 | char *tmp_path; |
88 | ||
89 | tmp_path = drive_create(); | |
90 | ||
78b27bad | 91 | global_qtest = qtest_startf("-machine virt " |
0a6ed700 MM |
92 | "-drive if=none,id=drive0,file=%s,format=raw " |
93 | "-device virtio-blk-device,drive=drive0", | |
94 | tmp_path); | |
0a6ed700 MM |
95 | unlink(tmp_path); |
96 | g_free(tmp_path); | |
0a6ed700 MM |
97 | } |
98 | ||
311e666a MM |
99 | static void test_end(void) |
100 | { | |
101 | qtest_end(); | |
102 | } | |
103 | ||
38d8364f | 104 | static QVirtioPCIDevice *virtio_blk_pci_init(QPCIBus *bus, int slot) |
311e666a MM |
105 | { |
106 | QVirtioPCIDevice *dev; | |
311e666a | 107 | |
80e1eea3 | 108 | dev = qvirtio_pci_device_find_slot(bus, VIRTIO_ID_BLOCK, slot); |
311e666a | 109 | g_assert(dev != NULL); |
8ac9e205 | 110 | g_assert_cmphex(dev->vdev.device_type, ==, VIRTIO_ID_BLOCK); |
aaf36070 | 111 | g_assert_cmphex(dev->pdev->devfn, ==, ((slot << 3) | PCI_FN)); |
311e666a | 112 | |
46e0cf76 | 113 | qvirtio_pci_device_enable(dev); |
6b9cdf4c LV |
114 | qvirtio_reset(&dev->vdev); |
115 | qvirtio_set_acknowledge(&dev->vdev); | |
116 | qvirtio_set_driver(&dev->vdev); | |
46e0cf76 MM |
117 | |
118 | return dev; | |
119 | } | |
120 | ||
8b4b80c3 | 121 | static inline void virtio_blk_fix_request(QVirtioDevice *d, QVirtioBlkReq *req) |
bf3c63d2 MM |
122 | { |
123 | #ifdef HOST_WORDS_BIGENDIAN | |
8b4b80c3 | 124 | const bool host_is_big_endian = true; |
bf3c63d2 | 125 | #else |
8b4b80c3 | 126 | const bool host_is_big_endian = false; |
bf3c63d2 MM |
127 | #endif |
128 | ||
8b4b80c3 | 129 | if (qvirtio_is_big_endian(d) != host_is_big_endian) { |
bf3c63d2 MM |
130 | req->type = bswap32(req->type); |
131 | req->ioprio = bswap32(req->ioprio); | |
132 | req->sector = bswap64(req->sector); | |
133 | } | |
134 | } | |
135 | ||
8b4b80c3 LV |
136 | static uint64_t virtio_blk_request(QGuestAllocator *alloc, QVirtioDevice *d, |
137 | QVirtioBlkReq *req, uint64_t data_size) | |
bf3c63d2 MM |
138 | { |
139 | uint64_t addr; | |
140 | uint8_t status = 0xFF; | |
141 | ||
142 | g_assert_cmpuint(data_size % 512, ==, 0); | |
143 | addr = guest_alloc(alloc, sizeof(*req) + data_size); | |
144 | ||
8b4b80c3 | 145 | virtio_blk_fix_request(d, req); |
bf3c63d2 MM |
146 | |
147 | memwrite(addr, req, 16); | |
148 | memwrite(addr + 16, req->data, data_size); | |
149 | memwrite(addr + 16 + data_size, &status, sizeof(status)); | |
150 | ||
151 | return addr; | |
152 | } | |
153 | ||
6b9cdf4c | 154 | static void test_basic(QVirtioDevice *dev, QGuestAllocator *alloc, |
246fc0fb | 155 | QVirtQueue *vq) |
46e0cf76 | 156 | { |
bf3c63d2 | 157 | QVirtioBlkReq req; |
bf3c63d2 | 158 | uint64_t req_addr; |
46e0cf76 | 159 | uint64_t capacity; |
bf3c63d2 MM |
160 | uint32_t features; |
161 | uint32_t free_head; | |
162 | uint8_t status; | |
163 | char *data; | |
46e0cf76 | 164 | |
246fc0fb | 165 | capacity = qvirtio_config_readq(dev, 0); |
50311a81 | 166 | |
46e0cf76 MM |
167 | g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); |
168 | ||
6b9cdf4c | 169 | features = qvirtio_get_features(dev); |
bf3c63d2 | 170 | features = features & ~(QVIRTIO_F_BAD_FEATURE | |
ee3b850a SH |
171 | (1u << VIRTIO_RING_F_INDIRECT_DESC) | |
172 | (1u << VIRTIO_RING_F_EVENT_IDX) | | |
4565a3e0 | 173 | (1u << VIRTIO_BLK_F_SCSI)); |
6b9cdf4c | 174 | qvirtio_set_features(dev, features); |
bf3c63d2 | 175 | |
6b9cdf4c | 176 | qvirtio_set_driver_ok(dev); |
bf3c63d2 | 177 | |
9b7d2d8b | 178 | /* Write and read with 3 descriptor layout */ |
bf3c63d2 | 179 | /* Write request */ |
4565a3e0 | 180 | req.type = VIRTIO_BLK_T_OUT; |
bf3c63d2 MM |
181 | req.ioprio = 1; |
182 | req.sector = 0; | |
183 | req.data = g_malloc0(512); | |
184 | strcpy(req.data, "TEST"); | |
185 | ||
8b4b80c3 | 186 | req_addr = virtio_blk_request(alloc, dev, &req, 512); |
bf3c63d2 MM |
187 | |
188 | g_free(req.data); | |
189 | ||
9b7d2d8b MM |
190 | free_head = qvirtqueue_add(vq, req_addr, 16, false, true); |
191 | qvirtqueue_add(vq, req_addr + 16, 512, false, true); | |
38d8364f | 192 | qvirtqueue_add(vq, req_addr + 528, 1, true, false); |
9b7d2d8b | 193 | |
6b9cdf4c | 194 | qvirtqueue_kick(dev, vq, free_head); |
bf3c63d2 | 195 | |
12dfbdca | 196 | qvirtio_wait_used_elem(dev, vq, free_head, QVIRTIO_BLK_TIMEOUT_US); |
bf3c63d2 MM |
197 | status = readb(req_addr + 528); |
198 | g_assert_cmpint(status, ==, 0); | |
199 | ||
200 | guest_free(alloc, req_addr); | |
201 | ||
202 | /* Read request */ | |
4565a3e0 | 203 | req.type = VIRTIO_BLK_T_IN; |
bf3c63d2 MM |
204 | req.ioprio = 1; |
205 | req.sector = 0; | |
206 | req.data = g_malloc0(512); | |
207 | ||
8b4b80c3 | 208 | req_addr = virtio_blk_request(alloc, dev, &req, 512); |
bf3c63d2 MM |
209 | |
210 | g_free(req.data); | |
211 | ||
38d8364f | 212 | free_head = qvirtqueue_add(vq, req_addr, 16, false, true); |
9b7d2d8b MM |
213 | qvirtqueue_add(vq, req_addr + 16, 512, true, true); |
214 | qvirtqueue_add(vq, req_addr + 528, 1, true, false); | |
bf3c63d2 | 215 | |
6b9cdf4c | 216 | qvirtqueue_kick(dev, vq, free_head); |
bf3c63d2 | 217 | |
12dfbdca | 218 | qvirtio_wait_used_elem(dev, vq, free_head, QVIRTIO_BLK_TIMEOUT_US); |
bf3c63d2 MM |
219 | status = readb(req_addr + 528); |
220 | g_assert_cmpint(status, ==, 0); | |
221 | ||
222 | data = g_malloc0(512); | |
223 | memread(req_addr + 16, data, 512); | |
224 | g_assert_cmpstr(data, ==, "TEST"); | |
225 | g_free(data); | |
226 | ||
227 | guest_free(alloc, req_addr); | |
228 | ||
1373a4c2 | 229 | if (features & (1u << VIRTIO_F_ANY_LAYOUT)) { |
9b7d2d8b MM |
230 | /* Write and read with 2 descriptor layout */ |
231 | /* Write request */ | |
4565a3e0 | 232 | req.type = VIRTIO_BLK_T_OUT; |
9b7d2d8b MM |
233 | req.ioprio = 1; |
234 | req.sector = 1; | |
235 | req.data = g_malloc0(512); | |
236 | strcpy(req.data, "TEST"); | |
bf3c63d2 | 237 | |
8b4b80c3 | 238 | req_addr = virtio_blk_request(alloc, dev, &req, 512); |
bf3c63d2 | 239 | |
9b7d2d8b | 240 | g_free(req.data); |
bf3c63d2 | 241 | |
9b7d2d8b MM |
242 | free_head = qvirtqueue_add(vq, req_addr, 528, false, true); |
243 | qvirtqueue_add(vq, req_addr + 528, 1, true, false); | |
6b9cdf4c | 244 | qvirtqueue_kick(dev, vq, free_head); |
38d8364f | 245 | |
12dfbdca | 246 | qvirtio_wait_used_elem(dev, vq, free_head, QVIRTIO_BLK_TIMEOUT_US); |
9b7d2d8b MM |
247 | status = readb(req_addr + 528); |
248 | g_assert_cmpint(status, ==, 0); | |
bf3c63d2 | 249 | |
9b7d2d8b | 250 | guest_free(alloc, req_addr); |
bf3c63d2 | 251 | |
9b7d2d8b | 252 | /* Read request */ |
4565a3e0 | 253 | req.type = VIRTIO_BLK_T_IN; |
9b7d2d8b MM |
254 | req.ioprio = 1; |
255 | req.sector = 1; | |
256 | req.data = g_malloc0(512); | |
bf3c63d2 | 257 | |
8b4b80c3 | 258 | req_addr = virtio_blk_request(alloc, dev, &req, 512); |
bf3c63d2 | 259 | |
9b7d2d8b | 260 | g_free(req.data); |
bf3c63d2 | 261 | |
9b7d2d8b MM |
262 | free_head = qvirtqueue_add(vq, req_addr, 16, false, true); |
263 | qvirtqueue_add(vq, req_addr + 16, 513, true, false); | |
bf3c63d2 | 264 | |
6b9cdf4c | 265 | qvirtqueue_kick(dev, vq, free_head); |
bf3c63d2 | 266 | |
12dfbdca | 267 | qvirtio_wait_used_elem(dev, vq, free_head, QVIRTIO_BLK_TIMEOUT_US); |
9b7d2d8b MM |
268 | status = readb(req_addr + 528); |
269 | g_assert_cmpint(status, ==, 0); | |
bf3c63d2 | 270 | |
9b7d2d8b MM |
271 | data = g_malloc0(512); |
272 | memread(req_addr + 16, data, 512); | |
273 | g_assert_cmpstr(data, ==, "TEST"); | |
274 | g_free(data); | |
bf3c63d2 | 275 | |
9b7d2d8b MM |
276 | guest_free(alloc, req_addr); |
277 | } | |
38d8364f MM |
278 | } |
279 | ||
280 | static void pci_basic(void) | |
281 | { | |
282 | QVirtioPCIDevice *dev; | |
a980f7f2 | 283 | QOSState *qs; |
38d8364f | 284 | QVirtQueuePCI *vqpci; |
38d8364f | 285 | |
a980f7f2 LV |
286 | qs = pci_test_start(); |
287 | dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT); | |
38d8364f | 288 | |
a980f7f2 | 289 | vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 0); |
38d8364f | 290 | |
246fc0fb | 291 | test_basic(&dev->vdev, qs->alloc, &vqpci->vq); |
bf3c63d2 MM |
292 | |
293 | /* End test */ | |
a980f7f2 | 294 | qvirtqueue_cleanup(dev->vdev.bus, &vqpci->vq, qs->alloc); |
46e0cf76 | 295 | qvirtio_pci_device_disable(dev); |
80e1eea3 | 296 | qvirtio_pci_device_free(dev); |
a980f7f2 | 297 | qtest_shutdown(qs); |
c7a59bed AF |
298 | } |
299 | ||
f294b029 MM |
300 | static void pci_indirect(void) |
301 | { | |
302 | QVirtioPCIDevice *dev; | |
58368113 | 303 | QVirtQueuePCI *vqpci; |
a980f7f2 | 304 | QOSState *qs; |
f294b029 MM |
305 | QVirtioBlkReq req; |
306 | QVRingIndirectDesc *indirect; | |
f294b029 MM |
307 | uint64_t req_addr; |
308 | uint64_t capacity; | |
309 | uint32_t features; | |
310 | uint32_t free_head; | |
311 | uint8_t status; | |
312 | char *data; | |
313 | ||
a980f7f2 | 314 | qs = pci_test_start(); |
f294b029 | 315 | |
a980f7f2 | 316 | dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT); |
f294b029 | 317 | |
246fc0fb | 318 | capacity = qvirtio_config_readq(&dev->vdev, 0); |
f294b029 MM |
319 | g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); |
320 | ||
6b9cdf4c | 321 | features = qvirtio_get_features(&dev->vdev); |
ee3b850a SH |
322 | g_assert_cmphex(features & (1u << VIRTIO_RING_F_INDIRECT_DESC), !=, 0); |
323 | features = features & ~(QVIRTIO_F_BAD_FEATURE | | |
324 | (1u << VIRTIO_RING_F_EVENT_IDX) | | |
4565a3e0 | 325 | (1u << VIRTIO_BLK_F_SCSI)); |
6b9cdf4c | 326 | qvirtio_set_features(&dev->vdev, features); |
f294b029 | 327 | |
a980f7f2 | 328 | vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 0); |
6b9cdf4c | 329 | qvirtio_set_driver_ok(&dev->vdev); |
f294b029 MM |
330 | |
331 | /* Write request */ | |
4565a3e0 | 332 | req.type = VIRTIO_BLK_T_OUT; |
f294b029 MM |
333 | req.ioprio = 1; |
334 | req.sector = 0; | |
335 | req.data = g_malloc0(512); | |
336 | strcpy(req.data, "TEST"); | |
337 | ||
a980f7f2 | 338 | req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512); |
f294b029 MM |
339 | |
340 | g_free(req.data); | |
341 | ||
a980f7f2 | 342 | indirect = qvring_indirect_desc_setup(&dev->vdev, qs->alloc, 2); |
f294b029 MM |
343 | qvring_indirect_desc_add(indirect, req_addr, 528, false); |
344 | qvring_indirect_desc_add(indirect, req_addr + 528, 1, true); | |
58368113 | 345 | free_head = qvirtqueue_add_indirect(&vqpci->vq, indirect); |
6b9cdf4c | 346 | qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); |
f294b029 | 347 | |
12dfbdca | 348 | qvirtio_wait_used_elem(&dev->vdev, &vqpci->vq, free_head, |
70556264 | 349 | QVIRTIO_BLK_TIMEOUT_US); |
f294b029 MM |
350 | status = readb(req_addr + 528); |
351 | g_assert_cmpint(status, ==, 0); | |
352 | ||
353 | g_free(indirect); | |
a980f7f2 | 354 | guest_free(qs->alloc, req_addr); |
f294b029 MM |
355 | |
356 | /* Read request */ | |
4565a3e0 | 357 | req.type = VIRTIO_BLK_T_IN; |
f294b029 MM |
358 | req.ioprio = 1; |
359 | req.sector = 0; | |
360 | req.data = g_malloc0(512); | |
361 | strcpy(req.data, "TEST"); | |
362 | ||
a980f7f2 | 363 | req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512); |
f294b029 MM |
364 | |
365 | g_free(req.data); | |
366 | ||
a980f7f2 | 367 | indirect = qvring_indirect_desc_setup(&dev->vdev, qs->alloc, 2); |
f294b029 MM |
368 | qvring_indirect_desc_add(indirect, req_addr, 16, false); |
369 | qvring_indirect_desc_add(indirect, req_addr + 16, 513, true); | |
58368113 | 370 | free_head = qvirtqueue_add_indirect(&vqpci->vq, indirect); |
6b9cdf4c | 371 | qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); |
f294b029 | 372 | |
12dfbdca | 373 | qvirtio_wait_used_elem(&dev->vdev, &vqpci->vq, free_head, |
70556264 | 374 | QVIRTIO_BLK_TIMEOUT_US); |
f294b029 MM |
375 | status = readb(req_addr + 528); |
376 | g_assert_cmpint(status, ==, 0); | |
377 | ||
378 | data = g_malloc0(512); | |
379 | memread(req_addr + 16, data, 512); | |
380 | g_assert_cmpstr(data, ==, "TEST"); | |
381 | g_free(data); | |
382 | ||
383 | g_free(indirect); | |
a980f7f2 | 384 | guest_free(qs->alloc, req_addr); |
f294b029 MM |
385 | |
386 | /* End test */ | |
a980f7f2 | 387 | qvirtqueue_cleanup(dev->vdev.bus, &vqpci->vq, qs->alloc); |
f294b029 | 388 | qvirtio_pci_device_disable(dev); |
80e1eea3 | 389 | qvirtio_pci_device_free(dev); |
a980f7f2 | 390 | qtest_shutdown(qs); |
f294b029 MM |
391 | } |
392 | ||
e1119955 MM |
393 | static void pci_config(void) |
394 | { | |
395 | QVirtioPCIDevice *dev; | |
a980f7f2 | 396 | QOSState *qs; |
e1119955 | 397 | int n_size = TEST_IMAGE_SIZE / 2; |
e1119955 MM |
398 | uint64_t capacity; |
399 | ||
a980f7f2 | 400 | qs = pci_test_start(); |
e1119955 | 401 | |
a980f7f2 | 402 | dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT); |
e1119955 | 403 | |
246fc0fb | 404 | capacity = qvirtio_config_readq(&dev->vdev, 0); |
e1119955 MM |
405 | g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); |
406 | ||
6b9cdf4c | 407 | qvirtio_set_driver_ok(&dev->vdev); |
e1119955 | 408 | |
dc491fea MAL |
409 | qmp_discard_response("{ 'execute': 'block_resize', " |
410 | " 'arguments': { 'device': 'drive0', " | |
411 | " 'size': %d } }", n_size); | |
6b9cdf4c | 412 | qvirtio_wait_config_isr(&dev->vdev, QVIRTIO_BLK_TIMEOUT_US); |
e1119955 | 413 | |
246fc0fb | 414 | capacity = qvirtio_config_readq(&dev->vdev, 0); |
e1119955 MM |
415 | g_assert_cmpint(capacity, ==, n_size / 512); |
416 | ||
417 | qvirtio_pci_device_disable(dev); | |
80e1eea3 | 418 | qvirtio_pci_device_free(dev); |
a980f7f2 LV |
419 | |
420 | qtest_shutdown(qs); | |
e1119955 MM |
421 | } |
422 | ||
58368113 MM |
423 | static void pci_msix(void) |
424 | { | |
425 | QVirtioPCIDevice *dev; | |
a980f7f2 | 426 | QOSState *qs; |
58368113 | 427 | QVirtQueuePCI *vqpci; |
58368113 MM |
428 | QVirtioBlkReq req; |
429 | int n_size = TEST_IMAGE_SIZE / 2; | |
58368113 MM |
430 | uint64_t req_addr; |
431 | uint64_t capacity; | |
432 | uint32_t features; | |
433 | uint32_t free_head; | |
434 | uint8_t status; | |
435 | char *data; | |
436 | ||
a980f7f2 | 437 | qs = pci_test_start(); |
58368113 | 438 | |
a980f7f2 | 439 | dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT); |
58368113 MM |
440 | qpci_msix_enable(dev->pdev); |
441 | ||
a980f7f2 | 442 | qvirtio_pci_set_msix_configuration_vector(dev, qs->alloc, 0); |
58368113 | 443 | |
246fc0fb | 444 | capacity = qvirtio_config_readq(&dev->vdev, 0); |
58368113 MM |
445 | g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); |
446 | ||
6b9cdf4c | 447 | features = qvirtio_get_features(&dev->vdev); |
58368113 | 448 | features = features & ~(QVIRTIO_F_BAD_FEATURE | |
ee3b850a SH |
449 | (1u << VIRTIO_RING_F_INDIRECT_DESC) | |
450 | (1u << VIRTIO_RING_F_EVENT_IDX) | | |
4565a3e0 | 451 | (1u << VIRTIO_BLK_F_SCSI)); |
6b9cdf4c | 452 | qvirtio_set_features(&dev->vdev, features); |
58368113 | 453 | |
a980f7f2 LV |
454 | vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 0); |
455 | qvirtqueue_pci_msix_setup(dev, vqpci, qs->alloc, 1); | |
58368113 | 456 | |
6b9cdf4c | 457 | qvirtio_set_driver_ok(&dev->vdev); |
58368113 | 458 | |
dc491fea MAL |
459 | qmp_discard_response("{ 'execute': 'block_resize', " |
460 | " 'arguments': { 'device': 'drive0', " | |
461 | " 'size': %d } }", n_size); | |
58368113 | 462 | |
6b9cdf4c | 463 | qvirtio_wait_config_isr(&dev->vdev, QVIRTIO_BLK_TIMEOUT_US); |
58368113 | 464 | |
246fc0fb | 465 | capacity = qvirtio_config_readq(&dev->vdev, 0); |
58368113 MM |
466 | g_assert_cmpint(capacity, ==, n_size / 512); |
467 | ||
468 | /* Write request */ | |
4565a3e0 | 469 | req.type = VIRTIO_BLK_T_OUT; |
58368113 MM |
470 | req.ioprio = 1; |
471 | req.sector = 0; | |
472 | req.data = g_malloc0(512); | |
473 | strcpy(req.data, "TEST"); | |
474 | ||
a980f7f2 | 475 | req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512); |
58368113 MM |
476 | |
477 | g_free(req.data); | |
478 | ||
9b7d2d8b MM |
479 | free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); |
480 | qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, false, true); | |
58368113 | 481 | qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); |
6b9cdf4c | 482 | qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); |
58368113 | 483 | |
12dfbdca | 484 | qvirtio_wait_used_elem(&dev->vdev, &vqpci->vq, free_head, |
70556264 | 485 | QVIRTIO_BLK_TIMEOUT_US); |
58368113 MM |
486 | |
487 | status = readb(req_addr + 528); | |
488 | g_assert_cmpint(status, ==, 0); | |
489 | ||
a980f7f2 | 490 | guest_free(qs->alloc, req_addr); |
58368113 MM |
491 | |
492 | /* Read request */ | |
4565a3e0 | 493 | req.type = VIRTIO_BLK_T_IN; |
58368113 MM |
494 | req.ioprio = 1; |
495 | req.sector = 0; | |
496 | req.data = g_malloc0(512); | |
497 | ||
a980f7f2 | 498 | req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512); |
58368113 MM |
499 | |
500 | g_free(req.data); | |
501 | ||
502 | free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); | |
9b7d2d8b MM |
503 | qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, true, true); |
504 | qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); | |
58368113 | 505 | |
6b9cdf4c | 506 | qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); |
58368113 | 507 | |
1053587c | 508 | |
12dfbdca | 509 | qvirtio_wait_used_elem(&dev->vdev, &vqpci->vq, free_head, |
70556264 | 510 | QVIRTIO_BLK_TIMEOUT_US); |
1053587c MM |
511 | |
512 | status = readb(req_addr + 528); | |
513 | g_assert_cmpint(status, ==, 0); | |
514 | ||
515 | data = g_malloc0(512); | |
516 | memread(req_addr + 16, data, 512); | |
517 | g_assert_cmpstr(data, ==, "TEST"); | |
518 | g_free(data); | |
519 | ||
a980f7f2 | 520 | guest_free(qs->alloc, req_addr); |
1053587c MM |
521 | |
522 | /* End test */ | |
a980f7f2 | 523 | qvirtqueue_cleanup(dev->vdev.bus, &vqpci->vq, qs->alloc); |
1053587c MM |
524 | qpci_msix_disable(dev->pdev); |
525 | qvirtio_pci_device_disable(dev); | |
80e1eea3 | 526 | qvirtio_pci_device_free(dev); |
a980f7f2 | 527 | qtest_shutdown(qs); |
1053587c MM |
528 | } |
529 | ||
530 | static void pci_idx(void) | |
531 | { | |
532 | QVirtioPCIDevice *dev; | |
a980f7f2 | 533 | QOSState *qs; |
1053587c | 534 | QVirtQueuePCI *vqpci; |
1053587c | 535 | QVirtioBlkReq req; |
1053587c MM |
536 | uint64_t req_addr; |
537 | uint64_t capacity; | |
538 | uint32_t features; | |
539 | uint32_t free_head; | |
12dfbdca SH |
540 | uint32_t write_head; |
541 | uint32_t desc_idx; | |
1053587c MM |
542 | uint8_t status; |
543 | char *data; | |
544 | ||
a980f7f2 | 545 | qs = pci_test_start(); |
1053587c | 546 | |
a980f7f2 | 547 | dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT); |
1053587c MM |
548 | qpci_msix_enable(dev->pdev); |
549 | ||
a980f7f2 | 550 | qvirtio_pci_set_msix_configuration_vector(dev, qs->alloc, 0); |
1053587c | 551 | |
246fc0fb | 552 | capacity = qvirtio_config_readq(&dev->vdev, 0); |
1053587c MM |
553 | g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); |
554 | ||
6b9cdf4c | 555 | features = qvirtio_get_features(&dev->vdev); |
1053587c | 556 | features = features & ~(QVIRTIO_F_BAD_FEATURE | |
ee3b850a | 557 | (1u << VIRTIO_RING_F_INDIRECT_DESC) | |
1373a4c2 | 558 | (1u << VIRTIO_F_NOTIFY_ON_EMPTY) | |
4565a3e0 | 559 | (1u << VIRTIO_BLK_F_SCSI)); |
6b9cdf4c | 560 | qvirtio_set_features(&dev->vdev, features); |
1053587c | 561 | |
a980f7f2 LV |
562 | vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 0); |
563 | qvirtqueue_pci_msix_setup(dev, vqpci, qs->alloc, 1); | |
1053587c | 564 | |
6b9cdf4c | 565 | qvirtio_set_driver_ok(&dev->vdev); |
1053587c MM |
566 | |
567 | /* Write request */ | |
4565a3e0 | 568 | req.type = VIRTIO_BLK_T_OUT; |
1053587c MM |
569 | req.ioprio = 1; |
570 | req.sector = 0; | |
571 | req.data = g_malloc0(512); | |
572 | strcpy(req.data, "TEST"); | |
573 | ||
a980f7f2 | 574 | req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512); |
1053587c MM |
575 | |
576 | g_free(req.data); | |
577 | ||
9b7d2d8b MM |
578 | free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); |
579 | qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, false, true); | |
1053587c | 580 | qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); |
6b9cdf4c | 581 | qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); |
1053587c | 582 | |
12dfbdca SH |
583 | qvirtio_wait_used_elem(&dev->vdev, &vqpci->vq, free_head, |
584 | QVIRTIO_BLK_TIMEOUT_US); | |
1053587c MM |
585 | |
586 | /* Write request */ | |
4565a3e0 | 587 | req.type = VIRTIO_BLK_T_OUT; |
1053587c MM |
588 | req.ioprio = 1; |
589 | req.sector = 1; | |
590 | req.data = g_malloc0(512); | |
591 | strcpy(req.data, "TEST"); | |
592 | ||
a980f7f2 | 593 | req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512); |
1053587c MM |
594 | |
595 | g_free(req.data); | |
596 | ||
597 | /* Notify after processing the third request */ | |
598 | qvirtqueue_set_used_event(&vqpci->vq, 2); | |
9b7d2d8b MM |
599 | free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); |
600 | qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, false, true); | |
1053587c | 601 | qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); |
6b9cdf4c | 602 | qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); |
12dfbdca | 603 | write_head = free_head; |
1053587c MM |
604 | |
605 | /* No notification expected */ | |
6b9cdf4c | 606 | status = qvirtio_wait_status_byte_no_isr(&dev->vdev, |
e8c81b4d SH |
607 | &vqpci->vq, req_addr + 528, |
608 | QVIRTIO_BLK_TIMEOUT_US); | |
1053587c MM |
609 | g_assert_cmpint(status, ==, 0); |
610 | ||
a980f7f2 | 611 | guest_free(qs->alloc, req_addr); |
1053587c MM |
612 | |
613 | /* Read request */ | |
4565a3e0 | 614 | req.type = VIRTIO_BLK_T_IN; |
1053587c MM |
615 | req.ioprio = 1; |
616 | req.sector = 1; | |
617 | req.data = g_malloc0(512); | |
618 | ||
a980f7f2 | 619 | req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512); |
1053587c MM |
620 | |
621 | g_free(req.data); | |
622 | ||
623 | free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); | |
9b7d2d8b MM |
624 | qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, true, true); |
625 | qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); | |
1053587c | 626 | |
6b9cdf4c | 627 | qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); |
1053587c | 628 | |
12dfbdca SH |
629 | /* We get just one notification for both requests */ |
630 | qvirtio_wait_used_elem(&dev->vdev, &vqpci->vq, write_head, | |
70556264 | 631 | QVIRTIO_BLK_TIMEOUT_US); |
12dfbdca SH |
632 | g_assert(qvirtqueue_get_buf(&vqpci->vq, &desc_idx)); |
633 | g_assert_cmpint(desc_idx, ==, free_head); | |
58368113 MM |
634 | |
635 | status = readb(req_addr + 528); | |
636 | g_assert_cmpint(status, ==, 0); | |
637 | ||
638 | data = g_malloc0(512); | |
639 | memread(req_addr + 16, data, 512); | |
640 | g_assert_cmpstr(data, ==, "TEST"); | |
641 | g_free(data); | |
642 | ||
a980f7f2 | 643 | guest_free(qs->alloc, req_addr); |
58368113 MM |
644 | |
645 | /* End test */ | |
a980f7f2 | 646 | qvirtqueue_cleanup(dev->vdev.bus, &vqpci->vq, qs->alloc); |
58368113 MM |
647 | qpci_msix_disable(dev->pdev); |
648 | qvirtio_pci_device_disable(dev); | |
80e1eea3 | 649 | qvirtio_pci_device_free(dev); |
a980f7f2 | 650 | qtest_shutdown(qs); |
58368113 MM |
651 | } |
652 | ||
38d8364f | 653 | static void pci_hotplug(void) |
aaf36070 | 654 | { |
aaf36070 | 655 | QVirtioPCIDevice *dev; |
a980f7f2 | 656 | QOSState *qs; |
30ca440e | 657 | const char *arch = qtest_get_arch(); |
aaf36070 | 658 | |
a980f7f2 | 659 | qs = pci_test_start(); |
aaf36070 IM |
660 | |
661 | /* plug secondary disk */ | |
662 | qpci_plug_device_test("virtio-blk-pci", "drv1", PCI_SLOT_HP, | |
663 | "'drive': 'drive1'"); | |
664 | ||
a980f7f2 | 665 | dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT_HP); |
aaf36070 IM |
666 | g_assert(dev); |
667 | qvirtio_pci_device_disable(dev); | |
80e1eea3 | 668 | qvirtio_pci_device_free(dev); |
aaf36070 IM |
669 | |
670 | /* unplug secondary disk */ | |
30ca440e LV |
671 | if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { |
672 | qpci_unplug_acpi_device_test("drv1", PCI_SLOT_HP); | |
673 | } | |
a980f7f2 | 674 | qtest_shutdown(qs); |
aaf36070 IM |
675 | } |
676 | ||
0a6ed700 MM |
677 | static void mmio_basic(void) |
678 | { | |
679 | QVirtioMMIODevice *dev; | |
680 | QVirtQueue *vq; | |
681 | QGuestAllocator *alloc; | |
682 | int n_size = TEST_IMAGE_SIZE / 2; | |
683 | uint64_t capacity; | |
684 | ||
685 | arm_test_start(); | |
686 | ||
687 | dev = qvirtio_mmio_init_device(MMIO_DEV_BASE_ADDR, MMIO_PAGE_SIZE); | |
688 | g_assert(dev != NULL); | |
8ac9e205 | 689 | g_assert_cmphex(dev->vdev.device_type, ==, VIRTIO_ID_BLOCK); |
0a6ed700 | 690 | |
6b9cdf4c LV |
691 | qvirtio_reset(&dev->vdev); |
692 | qvirtio_set_acknowledge(&dev->vdev); | |
693 | qvirtio_set_driver(&dev->vdev); | |
0a6ed700 MM |
694 | |
695 | alloc = generic_alloc_init(MMIO_RAM_ADDR, MMIO_RAM_SIZE, MMIO_PAGE_SIZE); | |
6b9cdf4c | 696 | vq = qvirtqueue_setup(&dev->vdev, alloc, 0); |
0a6ed700 | 697 | |
246fc0fb | 698 | test_basic(&dev->vdev, alloc, vq); |
0a6ed700 | 699 | |
dc491fea MAL |
700 | qmp_discard_response("{ 'execute': 'block_resize', " |
701 | " 'arguments': { 'device': 'drive0', " | |
702 | " 'size': %d } }", n_size); | |
0a6ed700 | 703 | |
6b9cdf4c | 704 | qvirtio_wait_queue_isr(&dev->vdev, vq, QVIRTIO_BLK_TIMEOUT_US); |
0a6ed700 | 705 | |
246fc0fb | 706 | capacity = qvirtio_config_readq(&dev->vdev, 0); |
0a6ed700 MM |
707 | g_assert_cmpint(capacity, ==, n_size / 512); |
708 | ||
709 | /* End test */ | |
6b9cdf4c | 710 | qvirtqueue_cleanup(dev->vdev.bus, vq, alloc); |
0a6ed700 | 711 | g_free(dev); |
a980f7f2 | 712 | generic_alloc_uninit(alloc); |
0a6ed700 MM |
713 | test_end(); |
714 | } | |
715 | ||
c7a59bed AF |
716 | int main(int argc, char **argv) |
717 | { | |
0a6ed700 | 718 | const char *arch = qtest_get_arch(); |
c7a59bed AF |
719 | |
720 | g_test_init(&argc, &argv, NULL); | |
c7a59bed | 721 | |
30ca440e LV |
722 | if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0 || |
723 | strcmp(arch, "ppc64") == 0) { | |
0a6ed700 MM |
724 | qtest_add_func("/virtio/blk/pci/basic", pci_basic); |
725 | qtest_add_func("/virtio/blk/pci/indirect", pci_indirect); | |
726 | qtest_add_func("/virtio/blk/pci/config", pci_config); | |
30ca440e LV |
727 | if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { |
728 | qtest_add_func("/virtio/blk/pci/msix", pci_msix); | |
729 | qtest_add_func("/virtio/blk/pci/idx", pci_idx); | |
730 | } | |
0a6ed700 MM |
731 | qtest_add_func("/virtio/blk/pci/hotplug", pci_hotplug); |
732 | } else if (strcmp(arch, "arm") == 0) { | |
733 | qtest_add_func("/virtio/blk/mmio/basic", mmio_basic); | |
734 | } | |
c7a59bed | 735 | |
9be38598 | 736 | return g_test_run(); |
c7a59bed | 737 | } |