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