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