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