]>
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 | |
be3a6781 | 196 | qvirtio_wait_used_elem(dev, vq, free_head, NULL, 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 | |
be3a6781 | 218 | qvirtio_wait_used_elem(dev, vq, free_head, NULL, 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 | |
be3a6781 GK |
246 | qvirtio_wait_used_elem(dev, vq, free_head, NULL, |
247 | QVIRTIO_BLK_TIMEOUT_US); | |
9b7d2d8b MM |
248 | status = readb(req_addr + 528); |
249 | g_assert_cmpint(status, ==, 0); | |
bf3c63d2 | 250 | |
9b7d2d8b | 251 | guest_free(alloc, req_addr); |
bf3c63d2 | 252 | |
9b7d2d8b | 253 | /* Read request */ |
4565a3e0 | 254 | req.type = VIRTIO_BLK_T_IN; |
9b7d2d8b MM |
255 | req.ioprio = 1; |
256 | req.sector = 1; | |
257 | req.data = g_malloc0(512); | |
bf3c63d2 | 258 | |
8b4b80c3 | 259 | req_addr = virtio_blk_request(alloc, dev, &req, 512); |
bf3c63d2 | 260 | |
9b7d2d8b | 261 | g_free(req.data); |
bf3c63d2 | 262 | |
9b7d2d8b MM |
263 | free_head = qvirtqueue_add(vq, req_addr, 16, false, true); |
264 | qvirtqueue_add(vq, req_addr + 16, 513, true, false); | |
bf3c63d2 | 265 | |
6b9cdf4c | 266 | qvirtqueue_kick(dev, vq, free_head); |
bf3c63d2 | 267 | |
be3a6781 GK |
268 | qvirtio_wait_used_elem(dev, vq, free_head, NULL, |
269 | QVIRTIO_BLK_TIMEOUT_US); | |
9b7d2d8b MM |
270 | status = readb(req_addr + 528); |
271 | g_assert_cmpint(status, ==, 0); | |
bf3c63d2 | 272 | |
9b7d2d8b MM |
273 | data = g_malloc0(512); |
274 | memread(req_addr + 16, data, 512); | |
275 | g_assert_cmpstr(data, ==, "TEST"); | |
276 | g_free(data); | |
bf3c63d2 | 277 | |
9b7d2d8b MM |
278 | guest_free(alloc, req_addr); |
279 | } | |
38d8364f MM |
280 | } |
281 | ||
282 | static void pci_basic(void) | |
283 | { | |
284 | QVirtioPCIDevice *dev; | |
a980f7f2 | 285 | QOSState *qs; |
38d8364f | 286 | QVirtQueuePCI *vqpci; |
38d8364f | 287 | |
a980f7f2 LV |
288 | qs = pci_test_start(); |
289 | dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT); | |
38d8364f | 290 | |
a980f7f2 | 291 | vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 0); |
38d8364f | 292 | |
246fc0fb | 293 | test_basic(&dev->vdev, qs->alloc, &vqpci->vq); |
bf3c63d2 MM |
294 | |
295 | /* End test */ | |
a980f7f2 | 296 | qvirtqueue_cleanup(dev->vdev.bus, &vqpci->vq, qs->alloc); |
46e0cf76 | 297 | qvirtio_pci_device_disable(dev); |
80e1eea3 | 298 | qvirtio_pci_device_free(dev); |
a980f7f2 | 299 | qtest_shutdown(qs); |
c7a59bed AF |
300 | } |
301 | ||
f294b029 MM |
302 | static void pci_indirect(void) |
303 | { | |
304 | QVirtioPCIDevice *dev; | |
58368113 | 305 | QVirtQueuePCI *vqpci; |
a980f7f2 | 306 | QOSState *qs; |
f294b029 MM |
307 | QVirtioBlkReq req; |
308 | QVRingIndirectDesc *indirect; | |
f294b029 MM |
309 | uint64_t req_addr; |
310 | uint64_t capacity; | |
311 | uint32_t features; | |
312 | uint32_t free_head; | |
313 | uint8_t status; | |
314 | char *data; | |
315 | ||
a980f7f2 | 316 | qs = pci_test_start(); |
f294b029 | 317 | |
a980f7f2 | 318 | dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT); |
f294b029 | 319 | |
246fc0fb | 320 | capacity = qvirtio_config_readq(&dev->vdev, 0); |
f294b029 MM |
321 | g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); |
322 | ||
6b9cdf4c | 323 | features = qvirtio_get_features(&dev->vdev); |
ee3b850a SH |
324 | g_assert_cmphex(features & (1u << VIRTIO_RING_F_INDIRECT_DESC), !=, 0); |
325 | features = features & ~(QVIRTIO_F_BAD_FEATURE | | |
326 | (1u << VIRTIO_RING_F_EVENT_IDX) | | |
4565a3e0 | 327 | (1u << VIRTIO_BLK_F_SCSI)); |
6b9cdf4c | 328 | qvirtio_set_features(&dev->vdev, features); |
f294b029 | 329 | |
a980f7f2 | 330 | vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 0); |
6b9cdf4c | 331 | qvirtio_set_driver_ok(&dev->vdev); |
f294b029 MM |
332 | |
333 | /* Write request */ | |
4565a3e0 | 334 | req.type = VIRTIO_BLK_T_OUT; |
f294b029 MM |
335 | req.ioprio = 1; |
336 | req.sector = 0; | |
337 | req.data = g_malloc0(512); | |
338 | strcpy(req.data, "TEST"); | |
339 | ||
a980f7f2 | 340 | req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512); |
f294b029 MM |
341 | |
342 | g_free(req.data); | |
343 | ||
a980f7f2 | 344 | indirect = qvring_indirect_desc_setup(&dev->vdev, qs->alloc, 2); |
f294b029 MM |
345 | qvring_indirect_desc_add(indirect, req_addr, 528, false); |
346 | qvring_indirect_desc_add(indirect, req_addr + 528, 1, true); | |
58368113 | 347 | free_head = qvirtqueue_add_indirect(&vqpci->vq, indirect); |
6b9cdf4c | 348 | qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); |
f294b029 | 349 | |
be3a6781 | 350 | qvirtio_wait_used_elem(&dev->vdev, &vqpci->vq, free_head, NULL, |
70556264 | 351 | QVIRTIO_BLK_TIMEOUT_US); |
f294b029 MM |
352 | status = readb(req_addr + 528); |
353 | g_assert_cmpint(status, ==, 0); | |
354 | ||
355 | g_free(indirect); | |
a980f7f2 | 356 | guest_free(qs->alloc, req_addr); |
f294b029 MM |
357 | |
358 | /* Read request */ | |
4565a3e0 | 359 | req.type = VIRTIO_BLK_T_IN; |
f294b029 MM |
360 | req.ioprio = 1; |
361 | req.sector = 0; | |
362 | req.data = g_malloc0(512); | |
363 | strcpy(req.data, "TEST"); | |
364 | ||
a980f7f2 | 365 | req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512); |
f294b029 MM |
366 | |
367 | g_free(req.data); | |
368 | ||
a980f7f2 | 369 | indirect = qvring_indirect_desc_setup(&dev->vdev, qs->alloc, 2); |
f294b029 MM |
370 | qvring_indirect_desc_add(indirect, req_addr, 16, false); |
371 | qvring_indirect_desc_add(indirect, req_addr + 16, 513, true); | |
58368113 | 372 | free_head = qvirtqueue_add_indirect(&vqpci->vq, indirect); |
6b9cdf4c | 373 | qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); |
f294b029 | 374 | |
be3a6781 | 375 | qvirtio_wait_used_elem(&dev->vdev, &vqpci->vq, free_head, NULL, |
70556264 | 376 | QVIRTIO_BLK_TIMEOUT_US); |
f294b029 MM |
377 | status = readb(req_addr + 528); |
378 | g_assert_cmpint(status, ==, 0); | |
379 | ||
380 | data = g_malloc0(512); | |
381 | memread(req_addr + 16, data, 512); | |
382 | g_assert_cmpstr(data, ==, "TEST"); | |
383 | g_free(data); | |
384 | ||
385 | g_free(indirect); | |
a980f7f2 | 386 | guest_free(qs->alloc, req_addr); |
f294b029 MM |
387 | |
388 | /* End test */ | |
a980f7f2 | 389 | qvirtqueue_cleanup(dev->vdev.bus, &vqpci->vq, qs->alloc); |
f294b029 | 390 | qvirtio_pci_device_disable(dev); |
80e1eea3 | 391 | qvirtio_pci_device_free(dev); |
a980f7f2 | 392 | qtest_shutdown(qs); |
f294b029 MM |
393 | } |
394 | ||
e1119955 MM |
395 | static void pci_config(void) |
396 | { | |
397 | QVirtioPCIDevice *dev; | |
a980f7f2 | 398 | QOSState *qs; |
e1119955 | 399 | int n_size = TEST_IMAGE_SIZE / 2; |
e1119955 MM |
400 | uint64_t capacity; |
401 | ||
a980f7f2 | 402 | qs = pci_test_start(); |
e1119955 | 403 | |
a980f7f2 | 404 | dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT); |
e1119955 | 405 | |
246fc0fb | 406 | capacity = qvirtio_config_readq(&dev->vdev, 0); |
e1119955 MM |
407 | g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); |
408 | ||
6b9cdf4c | 409 | qvirtio_set_driver_ok(&dev->vdev); |
e1119955 | 410 | |
dc491fea MAL |
411 | qmp_discard_response("{ 'execute': 'block_resize', " |
412 | " '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); | |
80e1eea3 | 420 | qvirtio_pci_device_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 | 460 | |
dc491fea MAL |
461 | qmp_discard_response("{ 'execute': 'block_resize', " |
462 | " 'arguments': { 'device': 'drive0', " | |
463 | " 'size': %d } }", n_size); | |
58368113 | 464 | |
6b9cdf4c | 465 | qvirtio_wait_config_isr(&dev->vdev, QVIRTIO_BLK_TIMEOUT_US); |
58368113 | 466 | |
246fc0fb | 467 | capacity = qvirtio_config_readq(&dev->vdev, 0); |
58368113 MM |
468 | g_assert_cmpint(capacity, ==, n_size / 512); |
469 | ||
470 | /* Write request */ | |
4565a3e0 | 471 | req.type = VIRTIO_BLK_T_OUT; |
58368113 MM |
472 | req.ioprio = 1; |
473 | req.sector = 0; | |
474 | req.data = g_malloc0(512); | |
475 | strcpy(req.data, "TEST"); | |
476 | ||
a980f7f2 | 477 | req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512); |
58368113 MM |
478 | |
479 | g_free(req.data); | |
480 | ||
9b7d2d8b MM |
481 | free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); |
482 | qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, false, true); | |
58368113 | 483 | qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); |
6b9cdf4c | 484 | qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); |
58368113 | 485 | |
be3a6781 | 486 | qvirtio_wait_used_elem(&dev->vdev, &vqpci->vq, free_head, NULL, |
70556264 | 487 | QVIRTIO_BLK_TIMEOUT_US); |
58368113 MM |
488 | |
489 | status = readb(req_addr + 528); | |
490 | g_assert_cmpint(status, ==, 0); | |
491 | ||
a980f7f2 | 492 | guest_free(qs->alloc, req_addr); |
58368113 MM |
493 | |
494 | /* Read request */ | |
4565a3e0 | 495 | req.type = VIRTIO_BLK_T_IN; |
58368113 MM |
496 | req.ioprio = 1; |
497 | req.sector = 0; | |
498 | req.data = g_malloc0(512); | |
499 | ||
a980f7f2 | 500 | req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512); |
58368113 MM |
501 | |
502 | g_free(req.data); | |
503 | ||
504 | free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); | |
9b7d2d8b MM |
505 | qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, true, true); |
506 | qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); | |
58368113 | 507 | |
6b9cdf4c | 508 | qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); |
58368113 | 509 | |
1053587c | 510 | |
be3a6781 | 511 | qvirtio_wait_used_elem(&dev->vdev, &vqpci->vq, free_head, NULL, |
70556264 | 512 | QVIRTIO_BLK_TIMEOUT_US); |
1053587c MM |
513 | |
514 | status = readb(req_addr + 528); | |
515 | g_assert_cmpint(status, ==, 0); | |
516 | ||
517 | data = g_malloc0(512); | |
518 | memread(req_addr + 16, data, 512); | |
519 | g_assert_cmpstr(data, ==, "TEST"); | |
520 | g_free(data); | |
521 | ||
a980f7f2 | 522 | guest_free(qs->alloc, req_addr); |
1053587c MM |
523 | |
524 | /* End test */ | |
a980f7f2 | 525 | qvirtqueue_cleanup(dev->vdev.bus, &vqpci->vq, qs->alloc); |
1053587c MM |
526 | qpci_msix_disable(dev->pdev); |
527 | qvirtio_pci_device_disable(dev); | |
80e1eea3 | 528 | qvirtio_pci_device_free(dev); |
a980f7f2 | 529 | qtest_shutdown(qs); |
1053587c MM |
530 | } |
531 | ||
532 | static void pci_idx(void) | |
533 | { | |
534 | QVirtioPCIDevice *dev; | |
a980f7f2 | 535 | QOSState *qs; |
1053587c | 536 | QVirtQueuePCI *vqpci; |
1053587c | 537 | QVirtioBlkReq req; |
1053587c MM |
538 | uint64_t req_addr; |
539 | uint64_t capacity; | |
540 | uint32_t features; | |
541 | uint32_t free_head; | |
12dfbdca SH |
542 | uint32_t write_head; |
543 | uint32_t desc_idx; | |
1053587c MM |
544 | uint8_t status; |
545 | char *data; | |
546 | ||
a980f7f2 | 547 | qs = pci_test_start(); |
1053587c | 548 | |
a980f7f2 | 549 | dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT); |
1053587c MM |
550 | qpci_msix_enable(dev->pdev); |
551 | ||
a980f7f2 | 552 | qvirtio_pci_set_msix_configuration_vector(dev, qs->alloc, 0); |
1053587c | 553 | |
246fc0fb | 554 | capacity = qvirtio_config_readq(&dev->vdev, 0); |
1053587c MM |
555 | g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); |
556 | ||
6b9cdf4c | 557 | features = qvirtio_get_features(&dev->vdev); |
1053587c | 558 | features = features & ~(QVIRTIO_F_BAD_FEATURE | |
ee3b850a | 559 | (1u << VIRTIO_RING_F_INDIRECT_DESC) | |
1373a4c2 | 560 | (1u << VIRTIO_F_NOTIFY_ON_EMPTY) | |
4565a3e0 | 561 | (1u << VIRTIO_BLK_F_SCSI)); |
6b9cdf4c | 562 | qvirtio_set_features(&dev->vdev, features); |
1053587c | 563 | |
a980f7f2 LV |
564 | vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 0); |
565 | qvirtqueue_pci_msix_setup(dev, vqpci, qs->alloc, 1); | |
1053587c | 566 | |
6b9cdf4c | 567 | qvirtio_set_driver_ok(&dev->vdev); |
1053587c MM |
568 | |
569 | /* Write request */ | |
4565a3e0 | 570 | req.type = VIRTIO_BLK_T_OUT; |
1053587c MM |
571 | req.ioprio = 1; |
572 | req.sector = 0; | |
573 | req.data = g_malloc0(512); | |
574 | strcpy(req.data, "TEST"); | |
575 | ||
a980f7f2 | 576 | req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512); |
1053587c MM |
577 | |
578 | g_free(req.data); | |
579 | ||
9b7d2d8b MM |
580 | free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); |
581 | qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, false, true); | |
1053587c | 582 | qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); |
6b9cdf4c | 583 | qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); |
1053587c | 584 | |
be3a6781 | 585 | qvirtio_wait_used_elem(&dev->vdev, &vqpci->vq, free_head, NULL, |
12dfbdca | 586 | QVIRTIO_BLK_TIMEOUT_US); |
1053587c MM |
587 | |
588 | /* Write request */ | |
4565a3e0 | 589 | req.type = VIRTIO_BLK_T_OUT; |
1053587c MM |
590 | req.ioprio = 1; |
591 | req.sector = 1; | |
592 | req.data = g_malloc0(512); | |
593 | strcpy(req.data, "TEST"); | |
594 | ||
a980f7f2 | 595 | req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512); |
1053587c MM |
596 | |
597 | g_free(req.data); | |
598 | ||
599 | /* Notify after processing the third request */ | |
600 | qvirtqueue_set_used_event(&vqpci->vq, 2); | |
9b7d2d8b MM |
601 | free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); |
602 | qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, false, true); | |
1053587c | 603 | qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); |
6b9cdf4c | 604 | qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); |
12dfbdca | 605 | write_head = free_head; |
1053587c MM |
606 | |
607 | /* No notification expected */ | |
6b9cdf4c | 608 | status = qvirtio_wait_status_byte_no_isr(&dev->vdev, |
e8c81b4d SH |
609 | &vqpci->vq, req_addr + 528, |
610 | QVIRTIO_BLK_TIMEOUT_US); | |
1053587c MM |
611 | g_assert_cmpint(status, ==, 0); |
612 | ||
a980f7f2 | 613 | guest_free(qs->alloc, req_addr); |
1053587c MM |
614 | |
615 | /* Read request */ | |
4565a3e0 | 616 | req.type = VIRTIO_BLK_T_IN; |
1053587c MM |
617 | req.ioprio = 1; |
618 | req.sector = 1; | |
619 | req.data = g_malloc0(512); | |
620 | ||
a980f7f2 | 621 | req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512); |
1053587c MM |
622 | |
623 | g_free(req.data); | |
624 | ||
625 | free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); | |
9b7d2d8b MM |
626 | qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, true, true); |
627 | qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); | |
1053587c | 628 | |
6b9cdf4c | 629 | qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); |
1053587c | 630 | |
12dfbdca | 631 | /* We get just one notification for both requests */ |
be3a6781 | 632 | qvirtio_wait_used_elem(&dev->vdev, &vqpci->vq, write_head, NULL, |
70556264 | 633 | QVIRTIO_BLK_TIMEOUT_US); |
be3a6781 | 634 | g_assert(qvirtqueue_get_buf(&vqpci->vq, &desc_idx, NULL)); |
12dfbdca | 635 | g_assert_cmpint(desc_idx, ==, free_head); |
58368113 MM |
636 | |
637 | status = readb(req_addr + 528); | |
638 | g_assert_cmpint(status, ==, 0); | |
639 | ||
640 | data = g_malloc0(512); | |
641 | memread(req_addr + 16, data, 512); | |
642 | g_assert_cmpstr(data, ==, "TEST"); | |
643 | g_free(data); | |
644 | ||
a980f7f2 | 645 | guest_free(qs->alloc, req_addr); |
58368113 MM |
646 | |
647 | /* End test */ | |
a980f7f2 | 648 | qvirtqueue_cleanup(dev->vdev.bus, &vqpci->vq, qs->alloc); |
58368113 MM |
649 | qpci_msix_disable(dev->pdev); |
650 | qvirtio_pci_device_disable(dev); | |
80e1eea3 | 651 | qvirtio_pci_device_free(dev); |
a980f7f2 | 652 | qtest_shutdown(qs); |
58368113 MM |
653 | } |
654 | ||
38d8364f | 655 | static void pci_hotplug(void) |
aaf36070 | 656 | { |
aaf36070 | 657 | QVirtioPCIDevice *dev; |
a980f7f2 | 658 | QOSState *qs; |
30ca440e | 659 | const char *arch = qtest_get_arch(); |
aaf36070 | 660 | |
a980f7f2 | 661 | qs = pci_test_start(); |
aaf36070 IM |
662 | |
663 | /* plug secondary disk */ | |
664 | qpci_plug_device_test("virtio-blk-pci", "drv1", PCI_SLOT_HP, | |
665 | "'drive': 'drive1'"); | |
666 | ||
a980f7f2 | 667 | dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT_HP); |
aaf36070 IM |
668 | g_assert(dev); |
669 | qvirtio_pci_device_disable(dev); | |
80e1eea3 | 670 | qvirtio_pci_device_free(dev); |
aaf36070 IM |
671 | |
672 | /* unplug secondary disk */ | |
30ca440e LV |
673 | if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { |
674 | qpci_unplug_acpi_device_test("drv1", PCI_SLOT_HP); | |
675 | } | |
a980f7f2 | 676 | qtest_shutdown(qs); |
aaf36070 IM |
677 | } |
678 | ||
4426f061 PP |
679 | /* |
680 | * Check that setting the vring addr on a non-existent virtqueue does | |
681 | * not crash. | |
682 | */ | |
683 | static void test_nonexistent_virtqueue(void) | |
684 | { | |
685 | QPCIBar bar0; | |
686 | QOSState *qs; | |
687 | QPCIDevice *dev; | |
688 | ||
689 | qs = pci_test_start(); | |
690 | dev = qpci_device_find(qs->pcibus, QPCI_DEVFN(4, 0)); | |
691 | g_assert(dev != NULL); | |
692 | ||
693 | qpci_device_enable(dev); | |
694 | bar0 = qpci_iomap(dev, 0, NULL); | |
695 | ||
696 | qpci_io_writeb(dev, bar0, VIRTIO_PCI_QUEUE_SEL, 2); | |
697 | qpci_io_writel(dev, bar0, VIRTIO_PCI_QUEUE_PFN, 1); | |
698 | ||
699 | g_free(dev); | |
700 | qtest_shutdown(qs); | |
701 | } | |
702 | ||
0a6ed700 MM |
703 | static void mmio_basic(void) |
704 | { | |
705 | QVirtioMMIODevice *dev; | |
706 | QVirtQueue *vq; | |
707 | QGuestAllocator *alloc; | |
708 | int n_size = TEST_IMAGE_SIZE / 2; | |
709 | uint64_t capacity; | |
710 | ||
711 | arm_test_start(); | |
712 | ||
713 | dev = qvirtio_mmio_init_device(MMIO_DEV_BASE_ADDR, MMIO_PAGE_SIZE); | |
714 | g_assert(dev != NULL); | |
8ac9e205 | 715 | g_assert_cmphex(dev->vdev.device_type, ==, VIRTIO_ID_BLOCK); |
0a6ed700 | 716 | |
6b9cdf4c LV |
717 | qvirtio_reset(&dev->vdev); |
718 | qvirtio_set_acknowledge(&dev->vdev); | |
719 | qvirtio_set_driver(&dev->vdev); | |
0a6ed700 MM |
720 | |
721 | alloc = generic_alloc_init(MMIO_RAM_ADDR, MMIO_RAM_SIZE, MMIO_PAGE_SIZE); | |
6b9cdf4c | 722 | vq = qvirtqueue_setup(&dev->vdev, alloc, 0); |
0a6ed700 | 723 | |
246fc0fb | 724 | test_basic(&dev->vdev, alloc, vq); |
0a6ed700 | 725 | |
dc491fea MAL |
726 | qmp_discard_response("{ 'execute': 'block_resize', " |
727 | " 'arguments': { 'device': 'drive0', " | |
728 | " 'size': %d } }", n_size); | |
0a6ed700 | 729 | |
6b9cdf4c | 730 | qvirtio_wait_queue_isr(&dev->vdev, vq, QVIRTIO_BLK_TIMEOUT_US); |
0a6ed700 | 731 | |
246fc0fb | 732 | capacity = qvirtio_config_readq(&dev->vdev, 0); |
0a6ed700 MM |
733 | g_assert_cmpint(capacity, ==, n_size / 512); |
734 | ||
735 | /* End test */ | |
6b9cdf4c | 736 | qvirtqueue_cleanup(dev->vdev.bus, vq, alloc); |
0a6ed700 | 737 | g_free(dev); |
a980f7f2 | 738 | generic_alloc_uninit(alloc); |
0a6ed700 MM |
739 | test_end(); |
740 | } | |
741 | ||
c7a59bed AF |
742 | int main(int argc, char **argv) |
743 | { | |
0a6ed700 | 744 | const char *arch = qtest_get_arch(); |
c7a59bed AF |
745 | |
746 | g_test_init(&argc, &argv, NULL); | |
c7a59bed | 747 | |
30ca440e LV |
748 | if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0 || |
749 | strcmp(arch, "ppc64") == 0) { | |
0a6ed700 MM |
750 | qtest_add_func("/virtio/blk/pci/basic", pci_basic); |
751 | qtest_add_func("/virtio/blk/pci/indirect", pci_indirect); | |
752 | qtest_add_func("/virtio/blk/pci/config", pci_config); | |
4426f061 | 753 | qtest_add_func("/virtio/blk/pci/nxvirtq", test_nonexistent_virtqueue); |
30ca440e LV |
754 | if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { |
755 | qtest_add_func("/virtio/blk/pci/msix", pci_msix); | |
756 | qtest_add_func("/virtio/blk/pci/idx", pci_idx); | |
757 | } | |
0a6ed700 MM |
758 | qtest_add_func("/virtio/blk/pci/hotplug", pci_hotplug); |
759 | } else if (strcmp(arch, "arm") == 0) { | |
760 | qtest_add_func("/virtio/blk/mmio/basic", mmio_basic); | |
761 | } | |
c7a59bed | 762 | |
9be38598 | 763 | return g_test_run(); |
c7a59bed | 764 | } |