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