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