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