]>
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 LV |
65 | const char *cmd = "-drive if=none,id=drive0,file=%s,format=raw " |
66 | "-drive if=none,id=drive1,file=/dev/null,format=raw " | |
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 | |
8ac9e205 | 111 | dev = qvirtio_pci_device_find(bus, VIRTIO_ID_BLOCK); |
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 | |
6b9cdf4c | 199 | qvirtio_wait_queue_isr(dev, vq, 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 | |
6b9cdf4c | 221 | qvirtio_wait_queue_isr(dev, vq, 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 | |
6b9cdf4c | 249 | qvirtio_wait_queue_isr(dev, vq, 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 | |
6b9cdf4c | 270 | qvirtio_wait_queue_isr(dev, vq, 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); |
311e666a | 299 | g_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 | |
6b9cdf4c | 351 | qvirtio_wait_queue_isr(&dev->vdev, &vqpci->vq, |
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 | |
6b9cdf4c | 376 | qvirtio_wait_queue_isr(&dev->vdev, &vqpci->vq, |
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 MM |
391 | qvirtio_pci_device_disable(dev); |
392 | g_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 MM |
411 | |
412 | qmp("{ 'execute': 'block_resize', 'arguments': { 'device': 'drive0', " | |
413 | " 'size': %d } }", n_size); | |
6b9cdf4c | 414 | qvirtio_wait_config_isr(&dev->vdev, QVIRTIO_BLK_TIMEOUT_US); |
e1119955 | 415 | |
246fc0fb | 416 | capacity = qvirtio_config_readq(&dev->vdev, 0); |
e1119955 MM |
417 | g_assert_cmpint(capacity, ==, n_size / 512); |
418 | ||
419 | qvirtio_pci_device_disable(dev); | |
420 | g_free(dev); | |
a980f7f2 LV |
421 | |
422 | qtest_shutdown(qs); | |
e1119955 MM |
423 | } |
424 | ||
58368113 MM |
425 | static void pci_msix(void) |
426 | { | |
427 | QVirtioPCIDevice *dev; | |
a980f7f2 | 428 | QOSState *qs; |
58368113 | 429 | QVirtQueuePCI *vqpci; |
58368113 MM |
430 | QVirtioBlkReq req; |
431 | int n_size = TEST_IMAGE_SIZE / 2; | |
58368113 MM |
432 | uint64_t req_addr; |
433 | uint64_t capacity; | |
434 | uint32_t features; | |
435 | uint32_t free_head; | |
436 | uint8_t status; | |
437 | char *data; | |
438 | ||
a980f7f2 | 439 | qs = pci_test_start(); |
58368113 | 440 | |
a980f7f2 | 441 | dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT); |
58368113 MM |
442 | qpci_msix_enable(dev->pdev); |
443 | ||
a980f7f2 | 444 | qvirtio_pci_set_msix_configuration_vector(dev, qs->alloc, 0); |
58368113 | 445 | |
246fc0fb | 446 | capacity = qvirtio_config_readq(&dev->vdev, 0); |
58368113 MM |
447 | g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); |
448 | ||
6b9cdf4c | 449 | features = qvirtio_get_features(&dev->vdev); |
58368113 | 450 | features = features & ~(QVIRTIO_F_BAD_FEATURE | |
ee3b850a SH |
451 | (1u << VIRTIO_RING_F_INDIRECT_DESC) | |
452 | (1u << VIRTIO_RING_F_EVENT_IDX) | | |
4565a3e0 | 453 | (1u << VIRTIO_BLK_F_SCSI)); |
6b9cdf4c | 454 | qvirtio_set_features(&dev->vdev, features); |
58368113 | 455 | |
a980f7f2 LV |
456 | vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 0); |
457 | qvirtqueue_pci_msix_setup(dev, vqpci, qs->alloc, 1); | |
58368113 | 458 | |
6b9cdf4c | 459 | qvirtio_set_driver_ok(&dev->vdev); |
58368113 MM |
460 | |
461 | qmp("{ 'execute': 'block_resize', 'arguments': { 'device': 'drive0', " | |
462 | " 'size': %d } }", n_size); | |
463 | ||
6b9cdf4c | 464 | qvirtio_wait_config_isr(&dev->vdev, QVIRTIO_BLK_TIMEOUT_US); |
58368113 | 465 | |
246fc0fb | 466 | capacity = qvirtio_config_readq(&dev->vdev, 0); |
58368113 MM |
467 | g_assert_cmpint(capacity, ==, n_size / 512); |
468 | ||
469 | /* Write request */ | |
4565a3e0 | 470 | req.type = VIRTIO_BLK_T_OUT; |
58368113 MM |
471 | req.ioprio = 1; |
472 | req.sector = 0; | |
473 | req.data = g_malloc0(512); | |
474 | strcpy(req.data, "TEST"); | |
475 | ||
a980f7f2 | 476 | req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512); |
58368113 MM |
477 | |
478 | g_free(req.data); | |
479 | ||
9b7d2d8b MM |
480 | free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); |
481 | qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, false, true); | |
58368113 | 482 | qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); |
6b9cdf4c | 483 | qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); |
58368113 | 484 | |
6b9cdf4c | 485 | qvirtio_wait_queue_isr(&dev->vdev, &vqpci->vq, |
70556264 | 486 | QVIRTIO_BLK_TIMEOUT_US); |
58368113 MM |
487 | |
488 | status = readb(req_addr + 528); | |
489 | g_assert_cmpint(status, ==, 0); | |
490 | ||
a980f7f2 | 491 | guest_free(qs->alloc, req_addr); |
58368113 MM |
492 | |
493 | /* Read request */ | |
4565a3e0 | 494 | req.type = VIRTIO_BLK_T_IN; |
58368113 MM |
495 | req.ioprio = 1; |
496 | req.sector = 0; | |
497 | req.data = g_malloc0(512); | |
498 | ||
a980f7f2 | 499 | req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512); |
58368113 MM |
500 | |
501 | g_free(req.data); | |
502 | ||
503 | free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); | |
9b7d2d8b MM |
504 | qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, true, true); |
505 | qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); | |
58368113 | 506 | |
6b9cdf4c | 507 | qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); |
58368113 | 508 | |
1053587c | 509 | |
6b9cdf4c | 510 | qvirtio_wait_queue_isr(&dev->vdev, &vqpci->vq, |
70556264 | 511 | QVIRTIO_BLK_TIMEOUT_US); |
1053587c MM |
512 | |
513 | status = readb(req_addr + 528); | |
514 | g_assert_cmpint(status, ==, 0); | |
515 | ||
516 | data = g_malloc0(512); | |
517 | memread(req_addr + 16, data, 512); | |
518 | g_assert_cmpstr(data, ==, "TEST"); | |
519 | g_free(data); | |
520 | ||
a980f7f2 | 521 | guest_free(qs->alloc, req_addr); |
1053587c MM |
522 | |
523 | /* End test */ | |
a980f7f2 | 524 | qvirtqueue_cleanup(dev->vdev.bus, &vqpci->vq, qs->alloc); |
1053587c MM |
525 | qpci_msix_disable(dev->pdev); |
526 | qvirtio_pci_device_disable(dev); | |
527 | g_free(dev); | |
a980f7f2 | 528 | qtest_shutdown(qs); |
1053587c MM |
529 | } |
530 | ||
531 | static void pci_idx(void) | |
532 | { | |
533 | QVirtioPCIDevice *dev; | |
a980f7f2 | 534 | QOSState *qs; |
1053587c | 535 | QVirtQueuePCI *vqpci; |
1053587c | 536 | QVirtioBlkReq req; |
1053587c MM |
537 | uint64_t req_addr; |
538 | uint64_t capacity; | |
539 | uint32_t features; | |
540 | uint32_t free_head; | |
541 | uint8_t status; | |
542 | char *data; | |
543 | ||
a980f7f2 | 544 | qs = pci_test_start(); |
1053587c | 545 | |
a980f7f2 | 546 | dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT); |
1053587c MM |
547 | qpci_msix_enable(dev->pdev); |
548 | ||
a980f7f2 | 549 | qvirtio_pci_set_msix_configuration_vector(dev, qs->alloc, 0); |
1053587c | 550 | |
246fc0fb | 551 | capacity = qvirtio_config_readq(&dev->vdev, 0); |
1053587c MM |
552 | g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); |
553 | ||
6b9cdf4c | 554 | features = qvirtio_get_features(&dev->vdev); |
1053587c | 555 | features = features & ~(QVIRTIO_F_BAD_FEATURE | |
ee3b850a | 556 | (1u << VIRTIO_RING_F_INDIRECT_DESC) | |
1373a4c2 | 557 | (1u << VIRTIO_F_NOTIFY_ON_EMPTY) | |
4565a3e0 | 558 | (1u << VIRTIO_BLK_F_SCSI)); |
6b9cdf4c | 559 | qvirtio_set_features(&dev->vdev, features); |
1053587c | 560 | |
a980f7f2 LV |
561 | vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 0); |
562 | qvirtqueue_pci_msix_setup(dev, vqpci, qs->alloc, 1); | |
1053587c | 563 | |
6b9cdf4c | 564 | qvirtio_set_driver_ok(&dev->vdev); |
1053587c MM |
565 | |
566 | /* Write request */ | |
4565a3e0 | 567 | req.type = VIRTIO_BLK_T_OUT; |
1053587c MM |
568 | req.ioprio = 1; |
569 | req.sector = 0; | |
570 | req.data = g_malloc0(512); | |
571 | strcpy(req.data, "TEST"); | |
572 | ||
a980f7f2 | 573 | req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512); |
1053587c MM |
574 | |
575 | g_free(req.data); | |
576 | ||
9b7d2d8b MM |
577 | free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); |
578 | qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, false, true); | |
1053587c | 579 | qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); |
6b9cdf4c | 580 | qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); |
1053587c | 581 | |
6b9cdf4c | 582 | qvirtio_wait_queue_isr(&dev->vdev, &vqpci->vq, QVIRTIO_BLK_TIMEOUT_US); |
1053587c MM |
583 | |
584 | /* Write request */ | |
4565a3e0 | 585 | req.type = VIRTIO_BLK_T_OUT; |
1053587c MM |
586 | req.ioprio = 1; |
587 | req.sector = 1; | |
588 | req.data = g_malloc0(512); | |
589 | strcpy(req.data, "TEST"); | |
590 | ||
a980f7f2 | 591 | req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512); |
1053587c MM |
592 | |
593 | g_free(req.data); | |
594 | ||
595 | /* Notify after processing the third request */ | |
596 | qvirtqueue_set_used_event(&vqpci->vq, 2); | |
9b7d2d8b MM |
597 | free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); |
598 | qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, false, true); | |
1053587c | 599 | qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); |
6b9cdf4c | 600 | qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); |
1053587c MM |
601 | |
602 | /* No notification expected */ | |
6b9cdf4c | 603 | status = qvirtio_wait_status_byte_no_isr(&dev->vdev, |
e8c81b4d SH |
604 | &vqpci->vq, req_addr + 528, |
605 | QVIRTIO_BLK_TIMEOUT_US); | |
1053587c MM |
606 | g_assert_cmpint(status, ==, 0); |
607 | ||
a980f7f2 | 608 | guest_free(qs->alloc, req_addr); |
1053587c MM |
609 | |
610 | /* Read request */ | |
4565a3e0 | 611 | req.type = VIRTIO_BLK_T_IN; |
1053587c MM |
612 | req.ioprio = 1; |
613 | req.sector = 1; | |
614 | req.data = g_malloc0(512); | |
615 | ||
a980f7f2 | 616 | req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512); |
1053587c MM |
617 | |
618 | g_free(req.data); | |
619 | ||
620 | free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); | |
9b7d2d8b MM |
621 | qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, true, true); |
622 | qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); | |
1053587c | 623 | |
6b9cdf4c | 624 | qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); |
1053587c | 625 | |
6b9cdf4c | 626 | qvirtio_wait_queue_isr(&dev->vdev, &vqpci->vq, |
70556264 | 627 | QVIRTIO_BLK_TIMEOUT_US); |
58368113 MM |
628 | |
629 | status = readb(req_addr + 528); | |
630 | g_assert_cmpint(status, ==, 0); | |
631 | ||
632 | data = g_malloc0(512); | |
633 | memread(req_addr + 16, data, 512); | |
634 | g_assert_cmpstr(data, ==, "TEST"); | |
635 | g_free(data); | |
636 | ||
a980f7f2 | 637 | guest_free(qs->alloc, req_addr); |
58368113 MM |
638 | |
639 | /* End test */ | |
a980f7f2 | 640 | qvirtqueue_cleanup(dev->vdev.bus, &vqpci->vq, qs->alloc); |
58368113 MM |
641 | qpci_msix_disable(dev->pdev); |
642 | qvirtio_pci_device_disable(dev); | |
643 | g_free(dev); | |
a980f7f2 | 644 | qtest_shutdown(qs); |
58368113 MM |
645 | } |
646 | ||
38d8364f | 647 | static void pci_hotplug(void) |
aaf36070 | 648 | { |
aaf36070 | 649 | QVirtioPCIDevice *dev; |
a980f7f2 | 650 | QOSState *qs; |
30ca440e | 651 | const char *arch = qtest_get_arch(); |
aaf36070 | 652 | |
a980f7f2 | 653 | qs = pci_test_start(); |
aaf36070 IM |
654 | |
655 | /* plug secondary disk */ | |
656 | qpci_plug_device_test("virtio-blk-pci", "drv1", PCI_SLOT_HP, | |
657 | "'drive': 'drive1'"); | |
658 | ||
a980f7f2 | 659 | dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT_HP); |
aaf36070 IM |
660 | g_assert(dev); |
661 | qvirtio_pci_device_disable(dev); | |
662 | g_free(dev); | |
663 | ||
664 | /* unplug secondary disk */ | |
30ca440e LV |
665 | if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { |
666 | qpci_unplug_acpi_device_test("drv1", PCI_SLOT_HP); | |
667 | } | |
a980f7f2 | 668 | qtest_shutdown(qs); |
aaf36070 IM |
669 | } |
670 | ||
0a6ed700 MM |
671 | static void mmio_basic(void) |
672 | { | |
673 | QVirtioMMIODevice *dev; | |
674 | QVirtQueue *vq; | |
675 | QGuestAllocator *alloc; | |
676 | int n_size = TEST_IMAGE_SIZE / 2; | |
677 | uint64_t capacity; | |
678 | ||
679 | arm_test_start(); | |
680 | ||
681 | dev = qvirtio_mmio_init_device(MMIO_DEV_BASE_ADDR, MMIO_PAGE_SIZE); | |
682 | g_assert(dev != NULL); | |
8ac9e205 | 683 | g_assert_cmphex(dev->vdev.device_type, ==, VIRTIO_ID_BLOCK); |
0a6ed700 | 684 | |
6b9cdf4c LV |
685 | qvirtio_reset(&dev->vdev); |
686 | qvirtio_set_acknowledge(&dev->vdev); | |
687 | qvirtio_set_driver(&dev->vdev); | |
0a6ed700 MM |
688 | |
689 | alloc = generic_alloc_init(MMIO_RAM_ADDR, MMIO_RAM_SIZE, MMIO_PAGE_SIZE); | |
6b9cdf4c | 690 | vq = qvirtqueue_setup(&dev->vdev, alloc, 0); |
0a6ed700 | 691 | |
246fc0fb | 692 | test_basic(&dev->vdev, alloc, vq); |
0a6ed700 MM |
693 | |
694 | qmp("{ 'execute': 'block_resize', 'arguments': { 'device': 'drive0', " | |
695 | " 'size': %d } }", n_size); | |
696 | ||
6b9cdf4c | 697 | qvirtio_wait_queue_isr(&dev->vdev, vq, QVIRTIO_BLK_TIMEOUT_US); |
0a6ed700 | 698 | |
246fc0fb | 699 | capacity = qvirtio_config_readq(&dev->vdev, 0); |
0a6ed700 MM |
700 | g_assert_cmpint(capacity, ==, n_size / 512); |
701 | ||
702 | /* End test */ | |
6b9cdf4c | 703 | qvirtqueue_cleanup(dev->vdev.bus, vq, alloc); |
0a6ed700 | 704 | g_free(dev); |
a980f7f2 | 705 | generic_alloc_uninit(alloc); |
0a6ed700 MM |
706 | test_end(); |
707 | } | |
708 | ||
c7a59bed AF |
709 | int main(int argc, char **argv) |
710 | { | |
0a6ed700 | 711 | const char *arch = qtest_get_arch(); |
c7a59bed AF |
712 | |
713 | g_test_init(&argc, &argv, NULL); | |
c7a59bed | 714 | |
30ca440e LV |
715 | if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0 || |
716 | strcmp(arch, "ppc64") == 0) { | |
0a6ed700 MM |
717 | qtest_add_func("/virtio/blk/pci/basic", pci_basic); |
718 | qtest_add_func("/virtio/blk/pci/indirect", pci_indirect); | |
719 | qtest_add_func("/virtio/blk/pci/config", pci_config); | |
30ca440e LV |
720 | if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { |
721 | qtest_add_func("/virtio/blk/pci/msix", pci_msix); | |
722 | qtest_add_func("/virtio/blk/pci/idx", pci_idx); | |
723 | } | |
0a6ed700 MM |
724 | qtest_add_func("/virtio/blk/pci/hotplug", pci_hotplug); |
725 | } else if (strcmp(arch, "arm") == 0) { | |
726 | qtest_add_func("/virtio/blk/mmio/basic", mmio_basic); | |
727 | } | |
c7a59bed | 728 | |
9be38598 | 729 | return g_test_run(); |
c7a59bed | 730 | } |