blob: 2f9cc2b7e7afb144b67d7ccc57993a794311a835 [file] [log] [blame]
Andreas Färberc7a59be2014-02-09 04:32:55 +01001/*
2 * QTest testcase for VirtIO Block Device
3 *
4 * Copyright (c) 2014 SUSE LINUX Products GmbH
Marc Marí311e6662014-09-01 12:07:54 +02005 * Copyright (c) 2014 Marc Marí
Andreas Färberc7a59be2014-02-09 04:32:55 +01006 *
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>
Marc Marí311e6662014-09-01 12:07:54 +020013#include <stdlib.h>
14#include <unistd.h>
15#include <stdio.h>
Andreas Färberc7a59be2014-02-09 04:32:55 +010016#include "libqtest.h"
Marc Marí311e6662014-09-01 12:07:54 +020017#include "libqos/virtio.h"
18#include "libqos/virtio-pci.h"
19#include "libqos/pci-pc.h"
Marc Maríbf3c63d2014-09-01 12:07:56 +020020#include "libqos/malloc.h"
21#include "libqos/malloc-pc.h"
22#include "qemu/bswap.h"
Andreas Färberc7a59be2014-02-09 04:32:55 +010023
Marc Maríbf3c63d2014-09-01 12:07:56 +020024#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)
44#define QVIRTIO_BLK_TIMEOUT 100
45#define PCI_SLOT 0x04
46#define PCI_FN 0x00
47
48typedef struct QVirtioBlkReq {
49 uint32_t type;
50 uint32_t ioprio;
51 uint64_t sector;
52 char *data;
53 uint8_t status;
54} QVirtioBlkReq;
Marc Marí311e6662014-09-01 12:07:54 +020055
56static QPCIBus *test_start(void)
Andreas Färberc7a59be2014-02-09 04:32:55 +010057{
Marc Marí311e6662014-09-01 12:07:54 +020058 char cmdline[100];
59 char tmp_path[] = "/tmp/qtest.XXXXXX";
60 int fd, ret;
61
62 /* Create a temporary raw image */
63 fd = mkstemp(tmp_path);
64 g_assert_cmpint(fd, >=, 0);
65 ret = ftruncate(fd, TEST_IMAGE_SIZE);
66 g_assert_cmpint(ret, ==, 0);
67 close(fd);
68
69 snprintf(cmdline, 100, "-drive if=none,id=drive0,file=%s "
70 "-device virtio-blk-pci,drive=drive0,addr=%x.%x",
71 tmp_path, PCI_SLOT, PCI_FN);
72 qtest_start(cmdline);
73 unlink(tmp_path);
74
75 return qpci_init_pc();
76}
77
78static void test_end(void)
79{
80 qtest_end();
81}
82
Marc Marí46e0cf72014-09-01 12:07:55 +020083static QVirtioPCIDevice *virtio_blk_init(QPCIBus *bus)
Marc Marí311e6662014-09-01 12:07:54 +020084{
85 QVirtioPCIDevice *dev;
Marc Marí311e6662014-09-01 12:07:54 +020086
87 dev = qvirtio_pci_device_find(bus, QVIRTIO_BLK_DEVICE_ID);
88 g_assert(dev != NULL);
89 g_assert_cmphex(dev->vdev.device_type, ==, QVIRTIO_BLK_DEVICE_ID);
90 g_assert_cmphex(dev->pdev->devfn, ==, ((PCI_SLOT << 3) | PCI_FN));
91
Marc Marí46e0cf72014-09-01 12:07:55 +020092 qvirtio_pci_device_enable(dev);
93 qvirtio_reset(&qvirtio_pci, &dev->vdev);
94 qvirtio_set_acknowledge(&qvirtio_pci, &dev->vdev);
95 qvirtio_set_driver(&qvirtio_pci, &dev->vdev);
96
97 return dev;
98}
99
Marc Maríbf3c63d2014-09-01 12:07:56 +0200100static inline void virtio_blk_fix_request(QVirtioBlkReq *req)
101{
102#ifdef HOST_WORDS_BIGENDIAN
103 bool host_endian = true;
104#else
105 bool host_endian = false;
106#endif
107
108 if (qtest_big_endian() != host_endian) {
109 req->type = bswap32(req->type);
110 req->ioprio = bswap32(req->ioprio);
111 req->sector = bswap64(req->sector);
112 }
113}
114
115static uint64_t virtio_blk_request(QGuestAllocator *alloc, QVirtioBlkReq *req,
116 uint64_t data_size)
117{
118 uint64_t addr;
119 uint8_t status = 0xFF;
120
121 g_assert_cmpuint(data_size % 512, ==, 0);
122 addr = guest_alloc(alloc, sizeof(*req) + data_size);
123
124 virtio_blk_fix_request(req);
125
126 memwrite(addr, req, 16);
127 memwrite(addr + 16, req->data, data_size);
128 memwrite(addr + 16 + data_size, &status, sizeof(status));
129
130 return addr;
131}
132
Marc Marí46e0cf72014-09-01 12:07:55 +0200133static void pci_basic(void)
134{
135 QVirtioPCIDevice *dev;
136 QPCIBus *bus;
Marc Maríbf3c63d2014-09-01 12:07:56 +0200137 QVirtQueue *vq;
138 QGuestAllocator *alloc;
139 QVirtioBlkReq req;
Marc Marí46e0cf72014-09-01 12:07:55 +0200140 void *addr;
Marc Maríbf3c63d2014-09-01 12:07:56 +0200141 uint64_t req_addr;
Marc Marí46e0cf72014-09-01 12:07:55 +0200142 uint64_t capacity;
Marc Maríbf3c63d2014-09-01 12:07:56 +0200143 uint32_t features;
144 uint32_t free_head;
145 uint8_t status;
146 char *data;
Marc Marí46e0cf72014-09-01 12:07:55 +0200147
148 bus = test_start();
149
150 dev = virtio_blk_init(bus);
151
152 /* MSI-X is not enabled */
153 addr = dev->addr + QVIRTIO_DEVICE_SPECIFIC_NO_MSIX;
154
155 capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr);
156 g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
157
Marc Maríbf3c63d2014-09-01 12:07:56 +0200158 features = qvirtio_get_features(&qvirtio_pci, &dev->vdev);
159 features = features & ~(QVIRTIO_F_BAD_FEATURE |
160 QVIRTIO_F_RING_INDIRECT_DESC | QVIRTIO_F_RING_EVENT_IDX |
161 QVIRTIO_BLK_F_SCSI);
162 qvirtio_set_features(&qvirtio_pci, &dev->vdev, features);
163
164 alloc = pc_alloc_init();
165 vq = qvirtqueue_setup(&qvirtio_pci, &dev->vdev, alloc, 0);
166
167 qvirtio_set_driver_ok(&qvirtio_pci, &dev->vdev);
168
169 /* Write and read with 2 descriptor layout */
170 /* Write request */
171 req.type = QVIRTIO_BLK_T_OUT;
172 req.ioprio = 1;
173 req.sector = 0;
174 req.data = g_malloc0(512);
175 strcpy(req.data, "TEST");
176
177 req_addr = virtio_blk_request(alloc, &req, 512);
178
179 g_free(req.data);
180
181 free_head = qvirtqueue_add(vq, req_addr, 528, false, true);
182 qvirtqueue_add(vq, req_addr + 528, 1, true, false);
183 qvirtqueue_kick(&qvirtio_pci, &dev->vdev, vq, free_head);
184
185 g_assert(qvirtio_wait_isr(&qvirtio_pci, &dev->vdev, 0x1,
186 QVIRTIO_BLK_TIMEOUT));
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
202 free_head = qvirtqueue_add(vq, req_addr, 16, false, true);
203 qvirtqueue_add(vq, req_addr + 16, 513, true, false);
204
205 qvirtqueue_kick(&qvirtio_pci, &dev->vdev, vq, free_head);
206
207 g_assert(qvirtio_wait_isr(&qvirtio_pci, &dev->vdev, 0x1,
208 QVIRTIO_BLK_TIMEOUT));
209 status = readb(req_addr + 528);
210 g_assert_cmpint(status, ==, 0);
211
212 data = g_malloc0(512);
213 memread(req_addr + 16, data, 512);
214 g_assert_cmpstr(data, ==, "TEST");
215 g_free(data);
216
217 guest_free(alloc, req_addr);
218
219 /* Write and read with 3 descriptor layout */
220 /* Write request */
221 req.type = QVIRTIO_BLK_T_OUT;
222 req.ioprio = 1;
223 req.sector = 1;
224 req.data = g_malloc0(512);
225 strcpy(req.data, "TEST");
226
227 req_addr = virtio_blk_request(alloc, &req, 512);
228
229 g_free(req.data);
230
231 free_head = qvirtqueue_add(vq, req_addr, 16, false, true);
232 qvirtqueue_add(vq, req_addr + 16, 512, false, true);
233 qvirtqueue_add(vq, req_addr + 528, 1, true, false);
234
235 qvirtqueue_kick(&qvirtio_pci, &dev->vdev, vq, free_head);
236
237 g_assert(qvirtio_wait_isr(&qvirtio_pci, &dev->vdev, 0x1,
238 QVIRTIO_BLK_TIMEOUT));
239 status = readb(req_addr + 528);
240 g_assert_cmpint(status, ==, 0);
241
242 guest_free(alloc, req_addr);
243
244 /* Read request */
245 req.type = QVIRTIO_BLK_T_IN;
246 req.ioprio = 1;
247 req.sector = 1;
248 req.data = g_malloc0(512);
249
250 req_addr = virtio_blk_request(alloc, &req, 512);
251
252 g_free(req.data);
253
254 free_head = qvirtqueue_add(vq, req_addr, 16, false, true);
255 qvirtqueue_add(vq, req_addr + 16, 512, true, true);
256 qvirtqueue_add(vq, req_addr + 528, 1, true, false);
257
258 qvirtqueue_kick(&qvirtio_pci, &dev->vdev, vq, free_head);
259
260 g_assert(qvirtio_wait_isr(&qvirtio_pci, &dev->vdev, 0x1,
261 QVIRTIO_BLK_TIMEOUT));
262 status = readb(req_addr + 528);
263 g_assert_cmpint(status, ==, 0);
264
265 guest_free(alloc, req_addr);
266
267 data = g_malloc0(512);
268 memread(req_addr + 16, data, 512);
269 g_assert_cmpstr(data, ==, "TEST");
270 g_free(data);
271
272 guest_free(alloc, req_addr);
273
274 /* End test */
275 guest_free(alloc, vq->desc);
Marc Marí46e0cf72014-09-01 12:07:55 +0200276 qvirtio_pci_device_disable(dev);
Marc Marí311e6662014-09-01 12:07:54 +0200277 g_free(dev);
278 test_end();
Andreas Färberc7a59be2014-02-09 04:32:55 +0100279}
280
Marc Maríf294b022014-09-01 12:07:57 +0200281static void pci_indirect(void)
282{
283 QVirtioPCIDevice *dev;
284 QPCIBus *bus;
285 QVirtQueue *vq;
286 QGuestAllocator *alloc;
287 QVirtioBlkReq req;
288 QVRingIndirectDesc *indirect;
289 void *addr;
290 uint64_t req_addr;
291 uint64_t capacity;
292 uint32_t features;
293 uint32_t free_head;
294 uint8_t status;
295 char *data;
296
297 bus = test_start();
298
299 dev = virtio_blk_init(bus);
300
301 /* MSI-X is not enabled */
302 addr = dev->addr + QVIRTIO_DEVICE_SPECIFIC_NO_MSIX;
303
304 capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, addr);
305 g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
306
307 features = qvirtio_get_features(&qvirtio_pci, &dev->vdev);
308 g_assert_cmphex(features & QVIRTIO_F_RING_INDIRECT_DESC, !=, 0);
309 features = features & ~(QVIRTIO_F_BAD_FEATURE | QVIRTIO_F_RING_EVENT_IDX |
310 QVIRTIO_BLK_F_SCSI);
311 qvirtio_set_features(&qvirtio_pci, &dev->vdev, features);
312
313 alloc = pc_alloc_init();
314 vq = qvirtqueue_setup(&qvirtio_pci, &dev->vdev, alloc, 0);
315
316 qvirtio_set_driver_ok(&qvirtio_pci, &dev->vdev);
317
318 /* Write request */
319 req.type = QVIRTIO_BLK_T_OUT;
320 req.ioprio = 1;
321 req.sector = 0;
322 req.data = g_malloc0(512);
323 strcpy(req.data, "TEST");
324
325 req_addr = virtio_blk_request(alloc, &req, 512);
326
327 g_free(req.data);
328
329 indirect = qvring_indirect_desc_setup(&dev->vdev, alloc, 2);
330 qvring_indirect_desc_add(indirect, req_addr, 528, false);
331 qvring_indirect_desc_add(indirect, req_addr + 528, 1, true);
332 free_head = qvirtqueue_add_indirect(vq, indirect);
333 qvirtqueue_kick(&qvirtio_pci, &dev->vdev, vq, free_head);
334
335 g_assert(qvirtio_wait_isr(&qvirtio_pci, &dev->vdev, 0x1,
336 QVIRTIO_BLK_TIMEOUT));
337 status = readb(req_addr + 528);
338 g_assert_cmpint(status, ==, 0);
339
340 g_free(indirect);
341 guest_free(alloc, req_addr);
342
343 /* Read request */
344 req.type = QVIRTIO_BLK_T_IN;
345 req.ioprio = 1;
346 req.sector = 0;
347 req.data = g_malloc0(512);
348 strcpy(req.data, "TEST");
349
350 req_addr = virtio_blk_request(alloc, &req, 512);
351
352 g_free(req.data);
353
354 indirect = qvring_indirect_desc_setup(&dev->vdev, alloc, 2);
355 qvring_indirect_desc_add(indirect, req_addr, 16, false);
356 qvring_indirect_desc_add(indirect, req_addr + 16, 513, true);
357 free_head = qvirtqueue_add_indirect(vq, indirect);
358 qvirtqueue_kick(&qvirtio_pci, &dev->vdev, vq, free_head);
359
360 g_assert(qvirtio_wait_isr(&qvirtio_pci, &dev->vdev, 0x1,
361 QVIRTIO_BLK_TIMEOUT));
362 status = readb(req_addr + 528);
363 g_assert_cmpint(status, ==, 0);
364
365 data = g_malloc0(512);
366 memread(req_addr + 16, data, 512);
367 g_assert_cmpstr(data, ==, "TEST");
368 g_free(data);
369
370 g_free(indirect);
371 guest_free(alloc, req_addr);
372
373 /* End test */
374 guest_free(alloc, vq->desc);
375 qvirtio_pci_device_disable(dev);
376 g_free(dev);
377 test_end();
378}
379
Andreas Färberc7a59be2014-02-09 04:32:55 +0100380int main(int argc, char **argv)
381{
382 int ret;
383
384 g_test_init(&argc, &argv, NULL);
Andreas Färberc7a59be2014-02-09 04:32:55 +0100385
Marc Marí311e6662014-09-01 12:07:54 +0200386 g_test_add_func("/virtio/blk/pci/basic", pci_basic);
Marc Maríf294b022014-09-01 12:07:57 +0200387 g_test_add_func("/virtio/blk/pci/indirect", pci_indirect);
Marc Marí311e6662014-09-01 12:07:54 +0200388
Andreas Färberc7a59be2014-02-09 04:32:55 +0100389 ret = g_test_run();
390
Andreas Färberc7a59be2014-02-09 04:32:55 +0100391 return ret;
392}