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