]>
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 | |
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 | |
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); |
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 | |
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 | 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 | |
6b9cdf4c | 487 | qvirtio_wait_queue_isr(&dev->vdev, &vqpci->vq, |
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 | |
6b9cdf4c | 512 | qvirtio_wait_queue_isr(&dev->vdev, &vqpci->vq, |
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; | |
543 | uint8_t status; | |
544 | char *data; | |
545 | ||
a980f7f2 | 546 | qs = pci_test_start(); |
1053587c | 547 | |
a980f7f2 | 548 | dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT); |
1053587c MM |
549 | qpci_msix_enable(dev->pdev); |
550 | ||
a980f7f2 | 551 | qvirtio_pci_set_msix_configuration_vector(dev, qs->alloc, 0); |
1053587c | 552 | |
246fc0fb | 553 | capacity = qvirtio_config_readq(&dev->vdev, 0); |
1053587c MM |
554 | g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); |
555 | ||
6b9cdf4c | 556 | features = qvirtio_get_features(&dev->vdev); |
1053587c | 557 | features = features & ~(QVIRTIO_F_BAD_FEATURE | |
ee3b850a | 558 | (1u << VIRTIO_RING_F_INDIRECT_DESC) | |
1373a4c2 | 559 | (1u << VIRTIO_F_NOTIFY_ON_EMPTY) | |
4565a3e0 | 560 | (1u << VIRTIO_BLK_F_SCSI)); |
6b9cdf4c | 561 | qvirtio_set_features(&dev->vdev, features); |
1053587c | 562 | |
a980f7f2 LV |
563 | vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 0); |
564 | qvirtqueue_pci_msix_setup(dev, vqpci, qs->alloc, 1); | |
1053587c | 565 | |
6b9cdf4c | 566 | qvirtio_set_driver_ok(&dev->vdev); |
1053587c MM |
567 | |
568 | /* Write request */ | |
4565a3e0 | 569 | req.type = VIRTIO_BLK_T_OUT; |
1053587c MM |
570 | req.ioprio = 1; |
571 | req.sector = 0; | |
572 | req.data = g_malloc0(512); | |
573 | strcpy(req.data, "TEST"); | |
574 | ||
a980f7f2 | 575 | req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512); |
1053587c MM |
576 | |
577 | g_free(req.data); | |
578 | ||
9b7d2d8b MM |
579 | free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); |
580 | qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, false, true); | |
1053587c | 581 | qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); |
6b9cdf4c | 582 | qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); |
1053587c | 583 | |
6b9cdf4c | 584 | qvirtio_wait_queue_isr(&dev->vdev, &vqpci->vq, 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); |
1053587c MM |
603 | |
604 | /* No notification expected */ | |
6b9cdf4c | 605 | status = qvirtio_wait_status_byte_no_isr(&dev->vdev, |
e8c81b4d SH |
606 | &vqpci->vq, req_addr + 528, |
607 | QVIRTIO_BLK_TIMEOUT_US); | |
1053587c MM |
608 | g_assert_cmpint(status, ==, 0); |
609 | ||
a980f7f2 | 610 | guest_free(qs->alloc, req_addr); |
1053587c MM |
611 | |
612 | /* Read request */ | |
4565a3e0 | 613 | req.type = VIRTIO_BLK_T_IN; |
1053587c MM |
614 | req.ioprio = 1; |
615 | req.sector = 1; | |
616 | req.data = g_malloc0(512); | |
617 | ||
a980f7f2 | 618 | req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512); |
1053587c MM |
619 | |
620 | g_free(req.data); | |
621 | ||
622 | free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); | |
9b7d2d8b MM |
623 | qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, true, true); |
624 | qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); | |
1053587c | 625 | |
6b9cdf4c | 626 | qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); |
1053587c | 627 | |
6b9cdf4c | 628 | qvirtio_wait_queue_isr(&dev->vdev, &vqpci->vq, |
70556264 | 629 | QVIRTIO_BLK_TIMEOUT_US); |
58368113 MM |
630 | |
631 | status = readb(req_addr + 528); | |
632 | g_assert_cmpint(status, ==, 0); | |
633 | ||
634 | data = g_malloc0(512); | |
635 | memread(req_addr + 16, data, 512); | |
636 | g_assert_cmpstr(data, ==, "TEST"); | |
637 | g_free(data); | |
638 | ||
a980f7f2 | 639 | guest_free(qs->alloc, req_addr); |
58368113 MM |
640 | |
641 | /* End test */ | |
a980f7f2 | 642 | qvirtqueue_cleanup(dev->vdev.bus, &vqpci->vq, qs->alloc); |
58368113 MM |
643 | qpci_msix_disable(dev->pdev); |
644 | qvirtio_pci_device_disable(dev); | |
80e1eea3 | 645 | qvirtio_pci_device_free(dev); |
a980f7f2 | 646 | qtest_shutdown(qs); |
58368113 MM |
647 | } |
648 | ||
38d8364f | 649 | static void pci_hotplug(void) |
aaf36070 | 650 | { |
aaf36070 | 651 | QVirtioPCIDevice *dev; |
a980f7f2 | 652 | QOSState *qs; |
30ca440e | 653 | const char *arch = qtest_get_arch(); |
aaf36070 | 654 | |
a980f7f2 | 655 | qs = pci_test_start(); |
aaf36070 IM |
656 | |
657 | /* plug secondary disk */ | |
658 | qpci_plug_device_test("virtio-blk-pci", "drv1", PCI_SLOT_HP, | |
659 | "'drive': 'drive1'"); | |
660 | ||
a980f7f2 | 661 | dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT_HP); |
aaf36070 IM |
662 | g_assert(dev); |
663 | qvirtio_pci_device_disable(dev); | |
80e1eea3 | 664 | qvirtio_pci_device_free(dev); |
aaf36070 IM |
665 | |
666 | /* unplug secondary disk */ | |
30ca440e LV |
667 | if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { |
668 | qpci_unplug_acpi_device_test("drv1", PCI_SLOT_HP); | |
669 | } | |
a980f7f2 | 670 | qtest_shutdown(qs); |
aaf36070 IM |
671 | } |
672 | ||
0a6ed700 MM |
673 | static void mmio_basic(void) |
674 | { | |
675 | QVirtioMMIODevice *dev; | |
676 | QVirtQueue *vq; | |
677 | QGuestAllocator *alloc; | |
678 | int n_size = TEST_IMAGE_SIZE / 2; | |
679 | uint64_t capacity; | |
680 | ||
681 | arm_test_start(); | |
682 | ||
683 | dev = qvirtio_mmio_init_device(MMIO_DEV_BASE_ADDR, MMIO_PAGE_SIZE); | |
684 | g_assert(dev != NULL); | |
8ac9e205 | 685 | g_assert_cmphex(dev->vdev.device_type, ==, VIRTIO_ID_BLOCK); |
0a6ed700 | 686 | |
6b9cdf4c LV |
687 | qvirtio_reset(&dev->vdev); |
688 | qvirtio_set_acknowledge(&dev->vdev); | |
689 | qvirtio_set_driver(&dev->vdev); | |
0a6ed700 MM |
690 | |
691 | alloc = generic_alloc_init(MMIO_RAM_ADDR, MMIO_RAM_SIZE, MMIO_PAGE_SIZE); | |
6b9cdf4c | 692 | vq = qvirtqueue_setup(&dev->vdev, alloc, 0); |
0a6ed700 | 693 | |
246fc0fb | 694 | test_basic(&dev->vdev, alloc, vq); |
0a6ed700 | 695 | |
dc491fea MAL |
696 | qmp_discard_response("{ 'execute': 'block_resize', " |
697 | " 'arguments': { 'device': 'drive0', " | |
698 | " 'size': %d } }", n_size); | |
0a6ed700 | 699 | |
6b9cdf4c | 700 | qvirtio_wait_queue_isr(&dev->vdev, vq, QVIRTIO_BLK_TIMEOUT_US); |
0a6ed700 | 701 | |
246fc0fb | 702 | capacity = qvirtio_config_readq(&dev->vdev, 0); |
0a6ed700 MM |
703 | g_assert_cmpint(capacity, ==, n_size / 512); |
704 | ||
705 | /* End test */ | |
6b9cdf4c | 706 | qvirtqueue_cleanup(dev->vdev.bus, vq, alloc); |
0a6ed700 | 707 | g_free(dev); |
a980f7f2 | 708 | generic_alloc_uninit(alloc); |
0a6ed700 MM |
709 | test_end(); |
710 | } | |
711 | ||
c7a59bed AF |
712 | int main(int argc, char **argv) |
713 | { | |
0a6ed700 | 714 | const char *arch = qtest_get_arch(); |
c7a59bed AF |
715 | |
716 | g_test_init(&argc, &argv, NULL); | |
c7a59bed | 717 | |
30ca440e LV |
718 | if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0 || |
719 | strcmp(arch, "ppc64") == 0) { | |
0a6ed700 MM |
720 | qtest_add_func("/virtio/blk/pci/basic", pci_basic); |
721 | qtest_add_func("/virtio/blk/pci/indirect", pci_indirect); | |
722 | qtest_add_func("/virtio/blk/pci/config", pci_config); | |
30ca440e LV |
723 | if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { |
724 | qtest_add_func("/virtio/blk/pci/msix", pci_msix); | |
725 | qtest_add_func("/virtio/blk/pci/idx", pci_idx); | |
726 | } | |
0a6ed700 MM |
727 | qtest_add_func("/virtio/blk/pci/hotplug", pci_hotplug); |
728 | } else if (strcmp(arch, "arm") == 0) { | |
729 | qtest_add_func("/virtio/blk/mmio/basic", mmio_basic); | |
730 | } | |
c7a59bed | 731 | |
9be38598 | 732 | return g_test_run(); |
c7a59bed | 733 | } |