]> git.proxmox.com Git - mirror_qemu.git/blame - tests/qtest/ivshmem-test.c
Remove qemu-common.h include from most units
[mirror_qemu.git] / tests / qtest / ivshmem-test.c
CommitLineData
bbfc2efe
AF
1/*
2 * QTest testcase for ivshmem
3 *
4 * Copyright (c) 2014 SUSE LINUX Products GmbH
ddef6a0d 5 * Copyright (c) 2015 Red Hat, Inc.
bbfc2efe
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"
ddef6a0d 12#include <glib/gstdio.h>
ddef6a0d 13#include "contrib/ivshmem-server/ivshmem-server.h"
d69487d5 14#include "libqos/libqos-pc.h"
2bf25e07 15#include "libqos/libqos-spapr.h"
a2ce7dbd 16#include "libqos/libqtest.h"
bbfc2efe 17
ddef6a0d
MAL
18#define TMPSHMSIZE (1 << 20)
19static char *tmpshm;
20static void *tmpshmem;
21static char *tmpdir;
22static char *tmpserver;
bbfc2efe 23
ddef6a0d 24static void save_fn(QPCIDevice *dev, int devfn, void *data)
bbfc2efe 25{
ddef6a0d
MAL
26 QPCIDevice **pdev = (QPCIDevice **) data;
27
28 *pdev = dev;
29}
30
1760048a 31static QPCIDevice *get_device(QPCIBus *pcibus)
ddef6a0d
MAL
32{
33 QPCIDevice *dev;
ddef6a0d 34
16130947 35 dev = NULL;
ddef6a0d
MAL
36 qpci_device_foreach(pcibus, 0x1af4, 0x1110, save_fn, &dev);
37 g_assert(dev != NULL);
38
39 return dev;
40}
41
42typedef struct _IVState {
d69487d5 43 QOSState *qs;
b4ba67d9 44 QPCIBar reg_bar, mem_bar;
ddef6a0d
MAL
45 QPCIDevice *dev;
46} IVState;
47
48enum Reg {
49 INTRMASK = 0,
50 INTRSTATUS = 4,
51 IVPOSITION = 8,
52 DOORBELL = 12,
53};
54
55static const char* reg2str(enum Reg reg) {
56 switch (reg) {
57 case INTRMASK:
58 return "IntrMask";
59 case INTRSTATUS:
60 return "IntrStatus";
61 case IVPOSITION:
62 return "IVPosition";
63 case DOORBELL:
64 return "DoorBell";
65 default:
66 return NULL;
67 }
68}
69
70static inline unsigned in_reg(IVState *s, enum Reg reg)
71{
72 const char *name = reg2str(reg);
ddef6a0d
MAL
73 unsigned res;
74
b4ba67d9 75 res = qpci_io_readl(s->dev, s->reg_bar, reg);
13ee9e30 76 g_test_message("*%s -> %x", name, res);
ddef6a0d
MAL
77
78 return res;
79}
80
81static inline void out_reg(IVState *s, enum Reg reg, unsigned v)
82{
83 const char *name = reg2str(reg);
ddef6a0d 84
13ee9e30 85 g_test_message("%x -> *%s", v, name);
b4ba67d9 86 qpci_io_writel(s->dev, s->reg_bar, reg, v);
ddef6a0d
MAL
87}
88
204e54b8
DG
89static inline void read_mem(IVState *s, uint64_t off, void *buf, size_t len)
90{
b4ba67d9 91 qpci_memread(s->dev, s->mem_bar, off, buf, len);
204e54b8
DG
92}
93
94static inline void write_mem(IVState *s, uint64_t off,
95 const void *buf, size_t len)
96{
b4ba67d9 97 qpci_memwrite(s->dev, s->mem_bar, off, buf, len);
204e54b8
DG
98}
99
1760048a
MAL
100static void cleanup_vm(IVState *s)
101{
102 g_free(s->dev);
d69487d5 103 qtest_shutdown(s->qs);
1760048a
MAL
104}
105
ddef6a0d
MAL
106static void setup_vm_cmd(IVState *s, const char *cmd, bool msix)
107{
108 uint64_t barsize;
d69487d5 109 const char *arch = qtest_get_arch();
ddef6a0d 110
d69487d5
LV
111 if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
112 s->qs = qtest_pc_boot(cmd);
2bf25e07
LV
113 } else if (strcmp(arch, "ppc64") == 0) {
114 s->qs = qtest_spapr_boot(cmd);
d69487d5 115 } else {
2bf25e07 116 g_printerr("ivshmem-test tests are only available on x86 or ppc64\n");
d69487d5
LV
117 exit(EXIT_FAILURE);
118 }
119 s->dev = get_device(s->qs->pcibus);
ddef6a0d 120
b4ba67d9 121 s->reg_bar = qpci_iomap(s->dev, 0, &barsize);
99826172 122 g_assert_cmpuint(barsize, ==, 256);
ddef6a0d
MAL
123
124 if (msix) {
125 qpci_msix_enable(s->dev);
126 }
127
b4ba67d9 128 s->mem_bar = qpci_iomap(s->dev, 2, &barsize);
99826172 129 g_assert_cmpuint(barsize, ==, TMPSHMSIZE);
ddef6a0d
MAL
130
131 qpci_device_enable(s->dev);
132}
133
134static void setup_vm(IVState *s)
135{
5400c02b 136 char *cmd = g_strdup_printf("-object memory-backend-file"
794b9560 137 ",id=mb1,size=1M,share=on,mem-path=/dev/shm%s"
5400c02b 138 " -device ivshmem-plain,memdev=mb1", tmpshm);
ddef6a0d
MAL
139
140 setup_vm_cmd(s, cmd, false);
141
142 g_free(cmd);
143}
144
145static void test_ivshmem_single(void)
146{
147 IVState state, *s;
148 uint32_t data[1024];
149 int i;
150
151 setup_vm(&state);
152 s = &state;
153
4958fe5d
MA
154 /* initial state of readable registers */
155 g_assert_cmpuint(in_reg(s, INTRMASK), ==, 0);
156 g_assert_cmpuint(in_reg(s, INTRSTATUS), ==, 0);
157 g_assert_cmpuint(in_reg(s, IVPOSITION), ==, 0);
ddef6a0d 158
4958fe5d 159 /* trigger interrupt via registers */
ddef6a0d
MAL
160 out_reg(s, INTRMASK, 0xffffffff);
161 g_assert_cmpuint(in_reg(s, INTRMASK), ==, 0xffffffff);
162 out_reg(s, INTRSTATUS, 1);
4958fe5d 163 /* check interrupt status */
ddef6a0d 164 g_assert_cmpuint(in_reg(s, INTRSTATUS), ==, 1);
4958fe5d
MA
165 /* reading clears */
166 g_assert_cmpuint(in_reg(s, INTRSTATUS), ==, 0);
167 /* TODO intercept actual interrupt (needs qtest work) */
ddef6a0d 168
4958fe5d 169 /* invalid register access */
ddef6a0d 170 out_reg(s, IVPOSITION, 1);
4958fe5d
MA
171 in_reg(s, DOORBELL);
172
173 /* ring the (non-functional) doorbell */
ddef6a0d
MAL
174 out_reg(s, DOORBELL, 8 << 16);
175
4958fe5d 176 /* write shared memory */
ddef6a0d
MAL
177 for (i = 0; i < G_N_ELEMENTS(data); i++) {
178 data[i] = i;
179 }
204e54b8 180 write_mem(s, 0, data, sizeof(data));
ddef6a0d 181
4958fe5d 182 /* verify write */
ddef6a0d
MAL
183 for (i = 0; i < G_N_ELEMENTS(data); i++) {
184 g_assert_cmpuint(((uint32_t *)tmpshmem)[i], ==, i);
185 }
186
4958fe5d 187 /* read it back and verify read */
ddef6a0d 188 memset(data, 0, sizeof(data));
204e54b8 189 read_mem(s, 0, data, sizeof(data));
ddef6a0d
MAL
190 for (i = 0; i < G_N_ELEMENTS(data); i++) {
191 g_assert_cmpuint(data[i], ==, i);
192 }
193
1760048a 194 cleanup_vm(s);
ddef6a0d
MAL
195}
196
197static void test_ivshmem_pair(void)
198{
199 IVState state1, state2, *s1, *s2;
200 char *data;
201 int i;
202
203 setup_vm(&state1);
204 s1 = &state1;
205 setup_vm(&state2);
206 s2 = &state2;
207
208 data = g_malloc0(TMPSHMSIZE);
209
210 /* host write, guest 1 & 2 read */
211 memset(tmpshmem, 0x42, TMPSHMSIZE);
204e54b8 212 read_mem(s1, 0, data, TMPSHMSIZE);
ddef6a0d
MAL
213 for (i = 0; i < TMPSHMSIZE; i++) {
214 g_assert_cmpuint(data[i], ==, 0x42);
215 }
204e54b8 216 read_mem(s2, 0, data, TMPSHMSIZE);
ddef6a0d
MAL
217 for (i = 0; i < TMPSHMSIZE; i++) {
218 g_assert_cmpuint(data[i], ==, 0x42);
219 }
220
221 /* guest 1 write, guest 2 read */
222 memset(data, 0x43, TMPSHMSIZE);
204e54b8 223 write_mem(s1, 0, data, TMPSHMSIZE);
ddef6a0d 224 memset(data, 0, TMPSHMSIZE);
204e54b8 225 read_mem(s2, 0, data, TMPSHMSIZE);
ddef6a0d
MAL
226 for (i = 0; i < TMPSHMSIZE; i++) {
227 g_assert_cmpuint(data[i], ==, 0x43);
228 }
229
230 /* guest 2 write, guest 1 read */
231 memset(data, 0x44, TMPSHMSIZE);
204e54b8 232 write_mem(s2, 0, data, TMPSHMSIZE);
ddef6a0d 233 memset(data, 0, TMPSHMSIZE);
204e54b8 234 read_mem(s1, 0, data, TMPSHMSIZE);
ddef6a0d
MAL
235 for (i = 0; i < TMPSHMSIZE; i++) {
236 g_assert_cmpuint(data[i], ==, 0x44);
237 }
238
1760048a
MAL
239 cleanup_vm(s1);
240 cleanup_vm(s2);
ddef6a0d
MAL
241 g_free(data);
242}
243
244typedef struct ServerThread {
245 GThread *thread;
246 IvshmemServer *server;
247 int pipe[2]; /* to handle quit */
248} ServerThread;
249
250static void *server_thread(void *data)
251{
252 ServerThread *t = data;
253 IvshmemServer *server = t->server;
254
255 while (true) {
256 fd_set fds;
257 int maxfd, ret;
258
259 FD_ZERO(&fds);
260 FD_SET(t->pipe[0], &fds);
261 maxfd = t->pipe[0] + 1;
262
263 ivshmem_server_get_fds(server, &fds, &maxfd);
264
265 ret = select(maxfd, &fds, NULL, NULL, NULL);
266
267 if (ret < 0) {
268 if (errno == EINTR) {
269 continue;
270 }
271
272 g_critical("select error: %s\n", strerror(errno));
273 break;
274 }
275 if (ret == 0) {
276 continue;
277 }
278
279 if (FD_ISSET(t->pipe[0], &fds)) {
280 break;
281 }
282
283 if (ivshmem_server_handle_fds(server, &fds, maxfd) < 0) {
284 g_critical("ivshmem_server_handle_fds() failed\n");
285 break;
286 }
287 }
288
289 return NULL;
290}
291
5a0e75f0 292static void setup_vm_with_server(IVState *s, int nvectors)
ddef6a0d 293{
5a0e75f0 294 char *cmd;
ddef6a0d 295
767abe7f 296 cmd = g_strdup_printf("-chardev socket,id=chr0,path=%s "
5a0e75f0
TH
297 "-device ivshmem-doorbell,chardev=chr0,vectors=%d",
298 tmpserver, nvectors);
299
300 setup_vm_cmd(s, cmd, true);
ddef6a0d
MAL
301
302 g_free(cmd);
303}
304
5a0e75f0 305static void test_ivshmem_server(void)
ddef6a0d
MAL
306{
307 IVState state1, state2, *s1, *s2;
308 ServerThread thread;
309 IvshmemServer server;
310 int ret, vm1, vm2;
311 int nvectors = 2;
312 guint64 end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
313
3625c739 314 ret = ivshmem_server_init(&server, tmpserver, tmpshm, true,
ddef6a0d
MAL
315 TMPSHMSIZE, nvectors,
316 g_test_verbose());
317 g_assert_cmpint(ret, ==, 0);
318
319 ret = ivshmem_server_start(&server);
320 g_assert_cmpint(ret, ==, 0);
321
ddef6a0d
MAL
322 thread.server = &server;
323 ret = pipe(thread.pipe);
324 g_assert_cmpint(ret, ==, 0);
325 thread.thread = g_thread_new("ivshmem-server", server_thread, &thread);
326 g_assert(thread.thread != NULL);
327
5a0e75f0 328 setup_vm_with_server(&state1, nvectors);
3a55fc0f 329 s1 = &state1;
5a0e75f0 330 setup_vm_with_server(&state2, nvectors);
3a55fc0f 331 s2 = &state2;
ddef6a0d
MAL
332
333 /* check got different VM ids */
334 vm1 = in_reg(s1, IVPOSITION);
335 vm2 = in_reg(s2, IVPOSITION);
3a55fc0f
MA
336 g_assert_cmpint(vm1, >=, 0);
337 g_assert_cmpint(vm2, >=, 0);
338 g_assert_cmpint(vm1, !=, vm2);
ddef6a0d 339
41b65e5e 340 /* check number of MSI-X vectors */
5a0e75f0
TH
341 ret = qpci_msix_table_size(s1->dev);
342 g_assert_cmpuint(ret, ==, nvectors);
ddef6a0d 343
41b65e5e
MA
344 /* TODO test behavior before MSI-X is enabled */
345
346 /* ping vm2 -> vm1 on vector 0 */
5a0e75f0
TH
347 ret = qpci_msix_pending(s1->dev, 0);
348 g_assert_cmpuint(ret, ==, 0);
ddef6a0d
MAL
349 out_reg(s2, DOORBELL, vm1 << 16);
350 do {
351 g_usleep(10000);
5a0e75f0 352 ret = qpci_msix_pending(s1->dev, 0);
ddef6a0d
MAL
353 } while (ret == 0 && g_get_monotonic_time() < end_time);
354 g_assert_cmpuint(ret, !=, 0);
355
41b65e5e 356 /* ping vm1 -> vm2 on vector 1 */
5a0e75f0
TH
357 ret = qpci_msix_pending(s2->dev, 1);
358 g_assert_cmpuint(ret, ==, 0);
41b65e5e 359 out_reg(s1, DOORBELL, vm2 << 16 | 1);
ddef6a0d
MAL
360 do {
361 g_usleep(10000);
5a0e75f0 362 ret = qpci_msix_pending(s2->dev, 1);
ddef6a0d
MAL
363 } while (ret == 0 && g_get_monotonic_time() < end_time);
364 g_assert_cmpuint(ret, !=, 0);
365
1760048a
MAL
366 cleanup_vm(s2);
367 cleanup_vm(s1);
ddef6a0d
MAL
368
369 if (qemu_write_full(thread.pipe[1], "q", 1) != 1) {
370 g_error("qemu_write_full: %s", g_strerror(errno));
371 }
372
373 g_thread_join(thread.thread);
374
375 ivshmem_server_close(&server);
376 close(thread.pipe[1]);
377 close(thread.pipe[0]);
378}
379
380#define PCI_SLOT_HP 0x06
381
382static void test_ivshmem_hotplug(void)
383{
6ebb8d2a 384 QTestState *qts;
2bf25e07 385 const char *arch = qtest_get_arch();
ddef6a0d 386
7b172333
DDAG
387 if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
388 qts = qtest_init("-object memory-backend-ram,size=1M,id=mb1"
389 " -machine pc");
390 } else {
391 qts = qtest_init("-object memory-backend-ram,size=1M,id=mb1");
392 }
ddef6a0d 393
e5758de4 394 qtest_qmp_device_add(qts, "ivshmem-plain", "iv1",
5a0e75f0
TH
395 "{'addr': %s, 'memdev': 'mb1'}",
396 stringify(PCI_SLOT_HP));
2bf25e07 397 if (strcmp(arch, "ppc64") != 0) {
6ebb8d2a 398 qpci_unplug_acpi_device_test(qts, "iv1", PCI_SLOT_HP);
2bf25e07 399 }
ddef6a0d 400
6ebb8d2a 401 qtest_quit(qts);
ddef6a0d
MAL
402}
403
d9453c93
MAL
404static void test_ivshmem_memdev(void)
405{
406 IVState state;
407
408 /* just for the sake of checking memory-backend property */
409 setup_vm_cmd(&state, "-object memory-backend-ram,size=1M,id=mb1"
5400c02b 410 " -device ivshmem-plain,memdev=mb1", false);
d9453c93 411
1760048a 412 cleanup_vm(&state);
d9453c93
MAL
413}
414
ddef6a0d
MAL
415static void cleanup(void)
416{
417 if (tmpshmem) {
418 munmap(tmpshmem, TMPSHMSIZE);
419 tmpshmem = NULL;
420 }
421
422 if (tmpshm) {
423 shm_unlink(tmpshm);
424 g_free(tmpshm);
425 tmpshm = NULL;
426 }
427
428 if (tmpserver) {
429 g_unlink(tmpserver);
430 g_free(tmpserver);
431 tmpserver = NULL;
432 }
433
434 if (tmpdir) {
435 g_rmdir(tmpdir);
436 tmpdir = NULL;
437 }
438}
439
440static void abrt_handler(void *data)
441{
442 cleanup();
443}
444
445static gchar *mktempshm(int size, int *fd)
446{
447 while (true) {
448 gchar *name;
449
0f555602 450 name = g_strdup_printf("/qtest-%u-%u", getpid(), g_test_rand_int());
ddef6a0d
MAL
451 *fd = shm_open(name, O_CREAT|O_RDWR|O_EXCL,
452 S_IRWXU|S_IRWXG|S_IRWXO);
453 if (*fd > 0) {
454 g_assert(ftruncate(*fd, size) == 0);
455 return name;
456 }
457
458 g_free(name);
459
460 if (errno != EEXIST) {
461 perror("shm_open");
462 return NULL;
463 }
464 }
bbfc2efe
AF
465}
466
467int main(int argc, char **argv)
468{
bbfc2efe 469 int ret, fd;
ddef6a0d
MAL
470 gchar dir[] = "/tmp/ivshmem-test.XXXXXX";
471
bbfc2efe 472 g_test_init(&argc, &argv, NULL);
bbfc2efe 473
ddef6a0d
MAL
474 qtest_add_abrt_handler(abrt_handler, NULL);
475 /* shm */
476 tmpshm = mktempshm(TMPSHMSIZE, &fd);
477 if (!tmpshm) {
4848cb3d 478 goto out;
ddef6a0d
MAL
479 }
480 tmpshmem = mmap(0, TMPSHMSIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
481 g_assert(tmpshmem != MAP_FAILED);
482 /* server */
483 if (mkdtemp(dir) == NULL) {
484 g_error("mkdtemp: %s", g_strerror(errno));
485 }
486 tmpdir = dir;
487 tmpserver = g_strconcat(tmpdir, "/server", NULL);
bbfc2efe 488
ddef6a0d 489 qtest_add_func("/ivshmem/single", test_ivshmem_single);
ddef6a0d 490 qtest_add_func("/ivshmem/hotplug", test_ivshmem_hotplug);
d9453c93 491 qtest_add_func("/ivshmem/memdev", test_ivshmem_memdev);
2048a2a4
MAL
492 if (g_test_slow()) {
493 qtest_add_func("/ivshmem/pair", test_ivshmem_pair);
d04aeb68 494 qtest_add_func("/ivshmem/server", test_ivshmem_server);
2048a2a4 495 }
bbfc2efe 496
4848cb3d 497out:
bbfc2efe 498 ret = g_test_run();
ddef6a0d 499 cleanup();
bbfc2efe
AF
500 return ret;
501}