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