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