11e8a1faeSThomas Huth /*
21e8a1faeSThomas Huth * QTest testcase for ivshmem
31e8a1faeSThomas Huth *
41e8a1faeSThomas Huth * Copyright (c) 2014 SUSE LINUX Products GmbH
51e8a1faeSThomas Huth * Copyright (c) 2015 Red Hat, Inc.
61e8a1faeSThomas Huth *
71e8a1faeSThomas Huth * This work is licensed under the terms of the GNU GPL, version 2 or later.
81e8a1faeSThomas Huth * See the COPYING file in the top-level directory.
91e8a1faeSThomas Huth */
101e8a1faeSThomas Huth
111e8a1faeSThomas Huth #include "qemu/osdep.h"
121e8a1faeSThomas Huth #include <glib/gstdio.h>
131e8a1faeSThomas Huth #include "contrib/ivshmem-server/ivshmem-server.h"
141e8a1faeSThomas Huth #include "libqos/libqos-pc.h"
151e8a1faeSThomas Huth #include "libqos/libqos-spapr.h"
16907b5105SMarc-André Lureau #include "libqtest.h"
171e8a1faeSThomas Huth
181e8a1faeSThomas Huth #define TMPSHMSIZE (1 << 20)
191e8a1faeSThomas Huth static char *tmpshm;
201e8a1faeSThomas Huth static void *tmpshmem;
211e8a1faeSThomas Huth static char *tmpdir;
221e8a1faeSThomas Huth static char *tmpserver;
231e8a1faeSThomas Huth
save_fn(QPCIDevice * dev,int devfn,void * data)241e8a1faeSThomas Huth static void save_fn(QPCIDevice *dev, int devfn, void *data)
251e8a1faeSThomas Huth {
261e8a1faeSThomas Huth QPCIDevice **pdev = (QPCIDevice **) data;
271e8a1faeSThomas Huth
281e8a1faeSThomas Huth *pdev = dev;
291e8a1faeSThomas Huth }
301e8a1faeSThomas Huth
get_device(QPCIBus * pcibus)311e8a1faeSThomas Huth static QPCIDevice *get_device(QPCIBus *pcibus)
321e8a1faeSThomas Huth {
331e8a1faeSThomas Huth QPCIDevice *dev;
341e8a1faeSThomas Huth
351e8a1faeSThomas Huth dev = NULL;
361e8a1faeSThomas Huth qpci_device_foreach(pcibus, 0x1af4, 0x1110, save_fn, &dev);
371e8a1faeSThomas Huth g_assert(dev != NULL);
381e8a1faeSThomas Huth
391e8a1faeSThomas Huth return dev;
401e8a1faeSThomas Huth }
411e8a1faeSThomas Huth
421e8a1faeSThomas Huth typedef struct _IVState {
431e8a1faeSThomas Huth QOSState *qs;
441e8a1faeSThomas Huth QPCIBar reg_bar, mem_bar;
451e8a1faeSThomas Huth QPCIDevice *dev;
461e8a1faeSThomas Huth } IVState;
471e8a1faeSThomas Huth
481e8a1faeSThomas Huth enum Reg {
491e8a1faeSThomas Huth INTRMASK = 0,
501e8a1faeSThomas Huth INTRSTATUS = 4,
511e8a1faeSThomas Huth IVPOSITION = 8,
521e8a1faeSThomas Huth DOORBELL = 12,
531e8a1faeSThomas Huth };
541e8a1faeSThomas Huth
reg2str(enum Reg reg)551e8a1faeSThomas Huth static const char* reg2str(enum Reg reg) {
561e8a1faeSThomas Huth switch (reg) {
571e8a1faeSThomas Huth case INTRMASK:
581e8a1faeSThomas Huth return "IntrMask";
591e8a1faeSThomas Huth case INTRSTATUS:
601e8a1faeSThomas Huth return "IntrStatus";
611e8a1faeSThomas Huth case IVPOSITION:
621e8a1faeSThomas Huth return "IVPosition";
631e8a1faeSThomas Huth case DOORBELL:
641e8a1faeSThomas Huth return "DoorBell";
651e8a1faeSThomas Huth default:
661e8a1faeSThomas Huth return NULL;
671e8a1faeSThomas Huth }
681e8a1faeSThomas Huth }
691e8a1faeSThomas Huth
in_reg(IVState * s,enum Reg reg)701e8a1faeSThomas Huth static inline unsigned in_reg(IVState *s, enum Reg reg)
711e8a1faeSThomas Huth {
721e8a1faeSThomas Huth const char *name = reg2str(reg);
731e8a1faeSThomas Huth unsigned res;
741e8a1faeSThomas Huth
751e8a1faeSThomas Huth res = qpci_io_readl(s->dev, s->reg_bar, reg);
761e8a1faeSThomas Huth g_test_message("*%s -> %x", name, res);
771e8a1faeSThomas Huth
781e8a1faeSThomas Huth return res;
791e8a1faeSThomas Huth }
801e8a1faeSThomas Huth
out_reg(IVState * s,enum Reg reg,unsigned v)811e8a1faeSThomas Huth static inline void out_reg(IVState *s, enum Reg reg, unsigned v)
821e8a1faeSThomas Huth {
831e8a1faeSThomas Huth const char *name = reg2str(reg);
841e8a1faeSThomas Huth
851e8a1faeSThomas Huth g_test_message("%x -> *%s", v, name);
861e8a1faeSThomas Huth qpci_io_writel(s->dev, s->reg_bar, reg, v);
871e8a1faeSThomas Huth }
881e8a1faeSThomas Huth
read_mem(IVState * s,uint64_t off,void * buf,size_t len)891e8a1faeSThomas Huth static inline void read_mem(IVState *s, uint64_t off, void *buf, size_t len)
901e8a1faeSThomas Huth {
911e8a1faeSThomas Huth qpci_memread(s->dev, s->mem_bar, off, buf, len);
921e8a1faeSThomas Huth }
931e8a1faeSThomas Huth
write_mem(IVState * s,uint64_t off,const void * buf,size_t len)941e8a1faeSThomas Huth static inline void write_mem(IVState *s, uint64_t off,
951e8a1faeSThomas Huth const void *buf, size_t len)
961e8a1faeSThomas Huth {
971e8a1faeSThomas Huth qpci_memwrite(s->dev, s->mem_bar, off, buf, len);
981e8a1faeSThomas Huth }
991e8a1faeSThomas Huth
cleanup_vm(IVState * s)1001e8a1faeSThomas Huth static void cleanup_vm(IVState *s)
1011e8a1faeSThomas Huth {
1021e8a1faeSThomas Huth g_free(s->dev);
1031e8a1faeSThomas Huth qtest_shutdown(s->qs);
1041e8a1faeSThomas Huth }
1051e8a1faeSThomas Huth
setup_vm_cmd(IVState * s,const char * cmd,bool msix)1061e8a1faeSThomas Huth static void setup_vm_cmd(IVState *s, const char *cmd, bool msix)
1071e8a1faeSThomas Huth {
1081e8a1faeSThomas Huth uint64_t barsize;
1091e8a1faeSThomas Huth const char *arch = qtest_get_arch();
1101e8a1faeSThomas Huth
1111e8a1faeSThomas Huth if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
1120472b2e5SDaniel P. Berrangé s->qs = qtest_pc_boot("%s", cmd);
1131e8a1faeSThomas Huth } else if (strcmp(arch, "ppc64") == 0) {
1140472b2e5SDaniel P. Berrangé s->qs = qtest_spapr_boot("%s", cmd);
1151e8a1faeSThomas Huth } else {
1161e8a1faeSThomas Huth g_printerr("ivshmem-test tests are only available on x86 or ppc64\n");
1171e8a1faeSThomas Huth exit(EXIT_FAILURE);
1181e8a1faeSThomas Huth }
1191e8a1faeSThomas Huth s->dev = get_device(s->qs->pcibus);
1201e8a1faeSThomas Huth
1211e8a1faeSThomas Huth s->reg_bar = qpci_iomap(s->dev, 0, &barsize);
1221e8a1faeSThomas Huth g_assert_cmpuint(barsize, ==, 256);
1231e8a1faeSThomas Huth
1241e8a1faeSThomas Huth if (msix) {
1251e8a1faeSThomas Huth qpci_msix_enable(s->dev);
1261e8a1faeSThomas Huth }
1271e8a1faeSThomas Huth
1281e8a1faeSThomas Huth s->mem_bar = qpci_iomap(s->dev, 2, &barsize);
1291e8a1faeSThomas Huth g_assert_cmpuint(barsize, ==, TMPSHMSIZE);
1301e8a1faeSThomas Huth
1311e8a1faeSThomas Huth qpci_device_enable(s->dev);
1321e8a1faeSThomas Huth }
1331e8a1faeSThomas Huth
setup_vm(IVState * s)1341e8a1faeSThomas Huth static void setup_vm(IVState *s)
1351e8a1faeSThomas Huth {
1361e8a1faeSThomas Huth char *cmd = g_strdup_printf("-object memory-backend-file"
137794b9560SPaolo Bonzini ",id=mb1,size=1M,share=on,mem-path=/dev/shm%s"
1381e8a1faeSThomas Huth " -device ivshmem-plain,memdev=mb1", tmpshm);
1391e8a1faeSThomas Huth
1401e8a1faeSThomas Huth setup_vm_cmd(s, cmd, false);
1411e8a1faeSThomas Huth
1421e8a1faeSThomas Huth g_free(cmd);
1431e8a1faeSThomas Huth }
1441e8a1faeSThomas Huth
test_ivshmem_single(void)1451e8a1faeSThomas Huth static void test_ivshmem_single(void)
1461e8a1faeSThomas Huth {
1471e8a1faeSThomas Huth IVState state, *s;
1481e8a1faeSThomas Huth uint32_t data[1024];
1491e8a1faeSThomas Huth int i;
1501e8a1faeSThomas Huth
1511e8a1faeSThomas Huth setup_vm(&state);
1521e8a1faeSThomas Huth s = &state;
1531e8a1faeSThomas Huth
1541e8a1faeSThomas Huth /* initial state of readable registers */
1551e8a1faeSThomas Huth g_assert_cmpuint(in_reg(s, INTRMASK), ==, 0);
1561e8a1faeSThomas Huth g_assert_cmpuint(in_reg(s, INTRSTATUS), ==, 0);
1571e8a1faeSThomas Huth g_assert_cmpuint(in_reg(s, IVPOSITION), ==, 0);
1581e8a1faeSThomas Huth
1591e8a1faeSThomas Huth /* trigger interrupt via registers */
1601e8a1faeSThomas Huth out_reg(s, INTRMASK, 0xffffffff);
161*58045186SInès Varhol g_assert_cmphex(in_reg(s, INTRMASK), ==, 0xffffffff);
1621e8a1faeSThomas Huth out_reg(s, INTRSTATUS, 1);
1631e8a1faeSThomas Huth /* check interrupt status */
1641e8a1faeSThomas Huth g_assert_cmpuint(in_reg(s, INTRSTATUS), ==, 1);
1651e8a1faeSThomas Huth /* reading clears */
1661e8a1faeSThomas Huth g_assert_cmpuint(in_reg(s, INTRSTATUS), ==, 0);
1671e8a1faeSThomas Huth /* TODO intercept actual interrupt (needs qtest work) */
1681e8a1faeSThomas Huth
1691e8a1faeSThomas Huth /* invalid register access */
1701e8a1faeSThomas Huth out_reg(s, IVPOSITION, 1);
1711e8a1faeSThomas Huth in_reg(s, DOORBELL);
1721e8a1faeSThomas Huth
1731e8a1faeSThomas Huth /* ring the (non-functional) doorbell */
1741e8a1faeSThomas Huth out_reg(s, DOORBELL, 8 << 16);
1751e8a1faeSThomas Huth
1761e8a1faeSThomas Huth /* write shared memory */
1771e8a1faeSThomas Huth for (i = 0; i < G_N_ELEMENTS(data); i++) {
1781e8a1faeSThomas Huth data[i] = i;
1791e8a1faeSThomas Huth }
1801e8a1faeSThomas Huth write_mem(s, 0, data, sizeof(data));
1811e8a1faeSThomas Huth
1821e8a1faeSThomas Huth /* verify write */
1831e8a1faeSThomas Huth for (i = 0; i < G_N_ELEMENTS(data); i++) {
1841e8a1faeSThomas Huth g_assert_cmpuint(((uint32_t *)tmpshmem)[i], ==, i);
1851e8a1faeSThomas Huth }
1861e8a1faeSThomas Huth
1871e8a1faeSThomas Huth /* read it back and verify read */
1881e8a1faeSThomas Huth memset(data, 0, sizeof(data));
1891e8a1faeSThomas Huth read_mem(s, 0, data, sizeof(data));
1901e8a1faeSThomas Huth for (i = 0; i < G_N_ELEMENTS(data); i++) {
1911e8a1faeSThomas Huth g_assert_cmpuint(data[i], ==, i);
1921e8a1faeSThomas Huth }
1931e8a1faeSThomas Huth
1941e8a1faeSThomas Huth cleanup_vm(s);
1951e8a1faeSThomas Huth }
1961e8a1faeSThomas Huth
test_ivshmem_pair(void)1971e8a1faeSThomas Huth static void test_ivshmem_pair(void)
1981e8a1faeSThomas Huth {
1991e8a1faeSThomas Huth IVState state1, state2, *s1, *s2;
2001e8a1faeSThomas Huth char *data;
2011e8a1faeSThomas Huth int i;
2021e8a1faeSThomas Huth
2031e8a1faeSThomas Huth setup_vm(&state1);
2041e8a1faeSThomas Huth s1 = &state1;
2051e8a1faeSThomas Huth setup_vm(&state2);
2061e8a1faeSThomas Huth s2 = &state2;
2071e8a1faeSThomas Huth
2081e8a1faeSThomas Huth data = g_malloc0(TMPSHMSIZE);
2091e8a1faeSThomas Huth
2101e8a1faeSThomas Huth /* host write, guest 1 & 2 read */
2111e8a1faeSThomas Huth memset(tmpshmem, 0x42, TMPSHMSIZE);
2121e8a1faeSThomas Huth read_mem(s1, 0, data, TMPSHMSIZE);
2131e8a1faeSThomas Huth for (i = 0; i < TMPSHMSIZE; i++) {
214*58045186SInès Varhol g_assert_cmphex(data[i], ==, 0x42);
2151e8a1faeSThomas Huth }
2161e8a1faeSThomas Huth read_mem(s2, 0, data, TMPSHMSIZE);
2171e8a1faeSThomas Huth for (i = 0; i < TMPSHMSIZE; i++) {
218*58045186SInès Varhol g_assert_cmphex(data[i], ==, 0x42);
2191e8a1faeSThomas Huth }
2201e8a1faeSThomas Huth
2211e8a1faeSThomas Huth /* guest 1 write, guest 2 read */
2221e8a1faeSThomas Huth memset(data, 0x43, TMPSHMSIZE);
2231e8a1faeSThomas Huth write_mem(s1, 0, data, TMPSHMSIZE);
2241e8a1faeSThomas Huth memset(data, 0, TMPSHMSIZE);
2251e8a1faeSThomas Huth read_mem(s2, 0, data, TMPSHMSIZE);
2261e8a1faeSThomas Huth for (i = 0; i < TMPSHMSIZE; i++) {
227*58045186SInès Varhol g_assert_cmphex(data[i], ==, 0x43);
2281e8a1faeSThomas Huth }
2291e8a1faeSThomas Huth
2301e8a1faeSThomas Huth /* guest 2 write, guest 1 read */
2311e8a1faeSThomas Huth memset(data, 0x44, TMPSHMSIZE);
2321e8a1faeSThomas Huth write_mem(s2, 0, data, TMPSHMSIZE);
2331e8a1faeSThomas Huth memset(data, 0, TMPSHMSIZE);
2341e8a1faeSThomas Huth read_mem(s1, 0, data, TMPSHMSIZE);
2351e8a1faeSThomas Huth for (i = 0; i < TMPSHMSIZE; i++) {
236*58045186SInès Varhol g_assert_cmphex(data[i], ==, 0x44);
2371e8a1faeSThomas Huth }
2381e8a1faeSThomas Huth
2391e8a1faeSThomas Huth cleanup_vm(s1);
2401e8a1faeSThomas Huth cleanup_vm(s2);
2411e8a1faeSThomas Huth g_free(data);
2421e8a1faeSThomas Huth }
2431e8a1faeSThomas Huth
2441e8a1faeSThomas Huth typedef struct ServerThread {
2451e8a1faeSThomas Huth GThread *thread;
2461e8a1faeSThomas Huth IvshmemServer *server;
2471e8a1faeSThomas Huth int pipe[2]; /* to handle quit */
2481e8a1faeSThomas Huth } ServerThread;
2491e8a1faeSThomas Huth
server_thread(void * data)2501e8a1faeSThomas Huth static void *server_thread(void *data)
2511e8a1faeSThomas Huth {
2521e8a1faeSThomas Huth ServerThread *t = data;
2531e8a1faeSThomas Huth IvshmemServer *server = t->server;
2541e8a1faeSThomas Huth
2551e8a1faeSThomas Huth while (true) {
2561e8a1faeSThomas Huth fd_set fds;
2571e8a1faeSThomas Huth int maxfd, ret;
2581e8a1faeSThomas Huth
2591e8a1faeSThomas Huth FD_ZERO(&fds);
2601e8a1faeSThomas Huth FD_SET(t->pipe[0], &fds);
2611e8a1faeSThomas Huth maxfd = t->pipe[0] + 1;
2621e8a1faeSThomas Huth
2631e8a1faeSThomas Huth ivshmem_server_get_fds(server, &fds, &maxfd);
2641e8a1faeSThomas Huth
2651e8a1faeSThomas Huth ret = select(maxfd, &fds, NULL, NULL, NULL);
2661e8a1faeSThomas Huth
2671e8a1faeSThomas Huth if (ret < 0) {
2681e8a1faeSThomas Huth if (errno == EINTR) {
2691e8a1faeSThomas Huth continue;
2701e8a1faeSThomas Huth }
2711e8a1faeSThomas Huth
2721e8a1faeSThomas Huth g_critical("select error: %s\n", strerror(errno));
2731e8a1faeSThomas Huth break;
2741e8a1faeSThomas Huth }
2751e8a1faeSThomas Huth if (ret == 0) {
2761e8a1faeSThomas Huth continue;
2771e8a1faeSThomas Huth }
2781e8a1faeSThomas Huth
2791e8a1faeSThomas Huth if (FD_ISSET(t->pipe[0], &fds)) {
2801e8a1faeSThomas Huth break;
2811e8a1faeSThomas Huth }
2821e8a1faeSThomas Huth
2831e8a1faeSThomas Huth if (ivshmem_server_handle_fds(server, &fds, maxfd) < 0) {
2841e8a1faeSThomas Huth g_critical("ivshmem_server_handle_fds() failed\n");
2851e8a1faeSThomas Huth break;
2861e8a1faeSThomas Huth }
2871e8a1faeSThomas Huth }
2881e8a1faeSThomas Huth
2891e8a1faeSThomas Huth return NULL;
2901e8a1faeSThomas Huth }
2911e8a1faeSThomas Huth
setup_vm_with_server(IVState * s,int nvectors)2921e8a1faeSThomas Huth static void setup_vm_with_server(IVState *s, int nvectors)
2931e8a1faeSThomas Huth {
2941e8a1faeSThomas Huth char *cmd;
2951e8a1faeSThomas Huth
2961e8a1faeSThomas Huth cmd = g_strdup_printf("-chardev socket,id=chr0,path=%s "
2971e8a1faeSThomas Huth "-device ivshmem-doorbell,chardev=chr0,vectors=%d",
2981e8a1faeSThomas Huth tmpserver, nvectors);
2991e8a1faeSThomas Huth
3001e8a1faeSThomas Huth setup_vm_cmd(s, cmd, true);
3011e8a1faeSThomas Huth
3021e8a1faeSThomas Huth g_free(cmd);
3031e8a1faeSThomas Huth }
3041e8a1faeSThomas Huth
test_ivshmem_server(void)3051e8a1faeSThomas Huth static void test_ivshmem_server(void)
3061e8a1faeSThomas Huth {
30789810e10SMarc-André Lureau g_autoptr(GError) err = NULL;
3081e8a1faeSThomas Huth IVState state1, state2, *s1, *s2;
3091e8a1faeSThomas Huth ServerThread thread;
3101e8a1faeSThomas Huth IvshmemServer server;
3111e8a1faeSThomas Huth int ret, vm1, vm2;
3121e8a1faeSThomas Huth int nvectors = 2;
3131e8a1faeSThomas Huth guint64 end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
3141e8a1faeSThomas Huth
3151e8a1faeSThomas Huth ret = ivshmem_server_init(&server, tmpserver, tmpshm, true,
3161e8a1faeSThomas Huth TMPSHMSIZE, nvectors,
3171e8a1faeSThomas Huth g_test_verbose());
3181e8a1faeSThomas Huth g_assert_cmpint(ret, ==, 0);
3191e8a1faeSThomas Huth
3201e8a1faeSThomas Huth ret = ivshmem_server_start(&server);
3211e8a1faeSThomas Huth g_assert_cmpint(ret, ==, 0);
3221e8a1faeSThomas Huth
3231e8a1faeSThomas Huth thread.server = &server;
32489810e10SMarc-André Lureau g_unix_open_pipe(thread.pipe, FD_CLOEXEC, &err);
32589810e10SMarc-André Lureau g_assert_no_error(err);
3261e8a1faeSThomas Huth thread.thread = g_thread_new("ivshmem-server", server_thread, &thread);
3271e8a1faeSThomas Huth g_assert(thread.thread != NULL);
3281e8a1faeSThomas Huth
3291e8a1faeSThomas Huth setup_vm_with_server(&state1, nvectors);
3301e8a1faeSThomas Huth s1 = &state1;
3311e8a1faeSThomas Huth setup_vm_with_server(&state2, nvectors);
3321e8a1faeSThomas Huth s2 = &state2;
3331e8a1faeSThomas Huth
3341e8a1faeSThomas Huth /* check got different VM ids */
3351e8a1faeSThomas Huth vm1 = in_reg(s1, IVPOSITION);
3361e8a1faeSThomas Huth vm2 = in_reg(s2, IVPOSITION);
3371e8a1faeSThomas Huth g_assert_cmpint(vm1, >=, 0);
3381e8a1faeSThomas Huth g_assert_cmpint(vm2, >=, 0);
3391e8a1faeSThomas Huth g_assert_cmpint(vm1, !=, vm2);
3401e8a1faeSThomas Huth
3411e8a1faeSThomas Huth /* check number of MSI-X vectors */
3421e8a1faeSThomas Huth ret = qpci_msix_table_size(s1->dev);
3431e8a1faeSThomas Huth g_assert_cmpuint(ret, ==, nvectors);
3441e8a1faeSThomas Huth
3451e8a1faeSThomas Huth /* TODO test behavior before MSI-X is enabled */
3461e8a1faeSThomas Huth
3471e8a1faeSThomas Huth /* ping vm2 -> vm1 on vector 0 */
3481e8a1faeSThomas Huth ret = qpci_msix_pending(s1->dev, 0);
3491e8a1faeSThomas Huth g_assert_cmpuint(ret, ==, 0);
3501e8a1faeSThomas Huth out_reg(s2, DOORBELL, vm1 << 16);
3511e8a1faeSThomas Huth do {
3521e8a1faeSThomas Huth g_usleep(10000);
3531e8a1faeSThomas Huth ret = qpci_msix_pending(s1->dev, 0);
3541e8a1faeSThomas Huth } while (ret == 0 && g_get_monotonic_time() < end_time);
3551e8a1faeSThomas Huth g_assert_cmpuint(ret, !=, 0);
3561e8a1faeSThomas Huth
3571e8a1faeSThomas Huth /* ping vm1 -> vm2 on vector 1 */
3581e8a1faeSThomas Huth ret = qpci_msix_pending(s2->dev, 1);
3591e8a1faeSThomas Huth g_assert_cmpuint(ret, ==, 0);
3601e8a1faeSThomas Huth out_reg(s1, DOORBELL, vm2 << 16 | 1);
3611e8a1faeSThomas Huth do {
3621e8a1faeSThomas Huth g_usleep(10000);
3631e8a1faeSThomas Huth ret = qpci_msix_pending(s2->dev, 1);
3641e8a1faeSThomas Huth } while (ret == 0 && g_get_monotonic_time() < end_time);
3651e8a1faeSThomas Huth g_assert_cmpuint(ret, !=, 0);
3661e8a1faeSThomas Huth
3671e8a1faeSThomas Huth cleanup_vm(s2);
3681e8a1faeSThomas Huth cleanup_vm(s1);
3691e8a1faeSThomas Huth
3701e8a1faeSThomas Huth if (qemu_write_full(thread.pipe[1], "q", 1) != 1) {
3711e8a1faeSThomas Huth g_error("qemu_write_full: %s", g_strerror(errno));
3721e8a1faeSThomas Huth }
3731e8a1faeSThomas Huth
3741e8a1faeSThomas Huth g_thread_join(thread.thread);
3751e8a1faeSThomas Huth
3761e8a1faeSThomas Huth ivshmem_server_close(&server);
3771e8a1faeSThomas Huth close(thread.pipe[1]);
3781e8a1faeSThomas Huth close(thread.pipe[0]);
3791e8a1faeSThomas Huth }
3801e8a1faeSThomas Huth
test_ivshmem_hotplug_q35(void)38146d11f9dSMichael Labiuk static void test_ivshmem_hotplug_q35(void)
38246d11f9dSMichael Labiuk {
38346d11f9dSMichael Labiuk QTestState *qts = qtest_init("-object memory-backend-ram,size=1M,id=mb1 "
38446d11f9dSMichael Labiuk "-device pcie-root-port,id=p1 "
38546d11f9dSMichael Labiuk "-device pcie-pci-bridge,bus=p1,id=b1 "
38646d11f9dSMichael Labiuk "-machine q35");
38746d11f9dSMichael Labiuk
38846d11f9dSMichael Labiuk qtest_qmp_device_add(qts, "ivshmem-plain", "iv1",
38946d11f9dSMichael Labiuk "{'memdev': 'mb1', 'bus': 'b1'}");
39046d11f9dSMichael Labiuk qtest_qmp_device_del_send(qts, "iv1");
39146d11f9dSMichael Labiuk
39246d11f9dSMichael Labiuk qtest_quit(qts);
39346d11f9dSMichael Labiuk }
39446d11f9dSMichael Labiuk
3951e8a1faeSThomas Huth #define PCI_SLOT_HP 0x06
3961e8a1faeSThomas Huth
test_ivshmem_hotplug(void)3971e8a1faeSThomas Huth static void test_ivshmem_hotplug(void)
3981e8a1faeSThomas Huth {
3991e8a1faeSThomas Huth QTestState *qts;
4001e8a1faeSThomas Huth const char *arch = qtest_get_arch();
4011e8a1faeSThomas Huth
4027b172333SDr. David Alan Gilbert if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
4037b172333SDr. David Alan Gilbert qts = qtest_init("-object memory-backend-ram,size=1M,id=mb1"
4047b172333SDr. David Alan Gilbert " -machine pc");
4057b172333SDr. David Alan Gilbert } else {
4061e8a1faeSThomas Huth qts = qtest_init("-object memory-backend-ram,size=1M,id=mb1");
4077b172333SDr. David Alan Gilbert }
4081e8a1faeSThomas Huth
4091e8a1faeSThomas Huth qtest_qmp_device_add(qts, "ivshmem-plain", "iv1",
4101e8a1faeSThomas Huth "{'addr': %s, 'memdev': 'mb1'}",
4111e8a1faeSThomas Huth stringify(PCI_SLOT_HP));
4121e8a1faeSThomas Huth if (strcmp(arch, "ppc64") != 0) {
4131e8a1faeSThomas Huth qpci_unplug_acpi_device_test(qts, "iv1", PCI_SLOT_HP);
4141e8a1faeSThomas Huth }
4151e8a1faeSThomas Huth
4161e8a1faeSThomas Huth qtest_quit(qts);
4171e8a1faeSThomas Huth }
4181e8a1faeSThomas Huth
test_ivshmem_memdev(void)4191e8a1faeSThomas Huth static void test_ivshmem_memdev(void)
4201e8a1faeSThomas Huth {
4211e8a1faeSThomas Huth IVState state;
4221e8a1faeSThomas Huth
4231e8a1faeSThomas Huth /* just for the sake of checking memory-backend property */
4241e8a1faeSThomas Huth setup_vm_cmd(&state, "-object memory-backend-ram,size=1M,id=mb1"
4251e8a1faeSThomas Huth " -device ivshmem-plain,memdev=mb1", false);
4261e8a1faeSThomas Huth
4271e8a1faeSThomas Huth cleanup_vm(&state);
4281e8a1faeSThomas Huth }
4291e8a1faeSThomas Huth
cleanup(void)4301e8a1faeSThomas Huth static void cleanup(void)
4311e8a1faeSThomas Huth {
4321e8a1faeSThomas Huth if (tmpshmem) {
4331e8a1faeSThomas Huth munmap(tmpshmem, TMPSHMSIZE);
4341e8a1faeSThomas Huth tmpshmem = NULL;
4351e8a1faeSThomas Huth }
4361e8a1faeSThomas Huth
4371e8a1faeSThomas Huth if (tmpshm) {
4381e8a1faeSThomas Huth shm_unlink(tmpshm);
4391e8a1faeSThomas Huth g_free(tmpshm);
4401e8a1faeSThomas Huth tmpshm = NULL;
4411e8a1faeSThomas Huth }
4421e8a1faeSThomas Huth
4431e8a1faeSThomas Huth if (tmpserver) {
4441e8a1faeSThomas Huth g_unlink(tmpserver);
4451e8a1faeSThomas Huth g_free(tmpserver);
4461e8a1faeSThomas Huth tmpserver = NULL;
4471e8a1faeSThomas Huth }
4481e8a1faeSThomas Huth
4491e8a1faeSThomas Huth if (tmpdir) {
4501e8a1faeSThomas Huth g_rmdir(tmpdir);
4511e8a1faeSThomas Huth tmpdir = NULL;
4521e8a1faeSThomas Huth }
4531e8a1faeSThomas Huth }
4541e8a1faeSThomas Huth
abrt_handler(void * data)4551e8a1faeSThomas Huth static void abrt_handler(void *data)
4561e8a1faeSThomas Huth {
4571e8a1faeSThomas Huth cleanup();
4581e8a1faeSThomas Huth }
4591e8a1faeSThomas Huth
mktempshm(int size,int * fd)4601e8a1faeSThomas Huth static gchar *mktempshm(int size, int *fd)
4611e8a1faeSThomas Huth {
4621e8a1faeSThomas Huth while (true) {
4631e8a1faeSThomas Huth gchar *name;
4641e8a1faeSThomas Huth
4651e8a1faeSThomas Huth name = g_strdup_printf("/qtest-%u-%u", getpid(), g_test_rand_int());
4661e8a1faeSThomas Huth *fd = shm_open(name, O_CREAT|O_RDWR|O_EXCL,
4671e8a1faeSThomas Huth S_IRWXU|S_IRWXG|S_IRWXO);
4681e8a1faeSThomas Huth if (*fd > 0) {
4691e8a1faeSThomas Huth g_assert(ftruncate(*fd, size) == 0);
4701e8a1faeSThomas Huth return name;
4711e8a1faeSThomas Huth }
4721e8a1faeSThomas Huth
4731e8a1faeSThomas Huth g_free(name);
4741e8a1faeSThomas Huth
4751e8a1faeSThomas Huth if (errno != EEXIST) {
4761e8a1faeSThomas Huth perror("shm_open");
4771e8a1faeSThomas Huth return NULL;
4781e8a1faeSThomas Huth }
4791e8a1faeSThomas Huth }
4801e8a1faeSThomas Huth }
4811e8a1faeSThomas Huth
main(int argc,char ** argv)4821e8a1faeSThomas Huth int main(int argc, char **argv)
4831e8a1faeSThomas Huth {
4841e8a1faeSThomas Huth int ret, fd;
4851e8a1faeSThomas Huth gchar dir[] = "/tmp/ivshmem-test.XXXXXX";
48646d11f9dSMichael Labiuk const char *arch = qtest_get_arch();
4871e8a1faeSThomas Huth
4881e8a1faeSThomas Huth g_test_init(&argc, &argv, NULL);
4891e8a1faeSThomas Huth
4901e8a1faeSThomas Huth qtest_add_abrt_handler(abrt_handler, NULL);
4911e8a1faeSThomas Huth /* shm */
4921e8a1faeSThomas Huth tmpshm = mktempshm(TMPSHMSIZE, &fd);
4931e8a1faeSThomas Huth if (!tmpshm) {
4941e8a1faeSThomas Huth goto out;
4951e8a1faeSThomas Huth }
4961e8a1faeSThomas Huth tmpshmem = mmap(0, TMPSHMSIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
4971e8a1faeSThomas Huth g_assert(tmpshmem != MAP_FAILED);
4981e8a1faeSThomas Huth /* server */
4993c239aa7SBin Meng if (g_mkdtemp(dir) == NULL) {
5003c239aa7SBin Meng g_error("g_mkdtemp: %s", g_strerror(errno));
5011e8a1faeSThomas Huth }
5021e8a1faeSThomas Huth tmpdir = dir;
5031e8a1faeSThomas Huth tmpserver = g_strconcat(tmpdir, "/server", NULL);
5041e8a1faeSThomas Huth
5051e8a1faeSThomas Huth qtest_add_func("/ivshmem/single", test_ivshmem_single);
5061e8a1faeSThomas Huth qtest_add_func("/ivshmem/hotplug", test_ivshmem_hotplug);
5071e8a1faeSThomas Huth qtest_add_func("/ivshmem/memdev", test_ivshmem_memdev);
5081e8a1faeSThomas Huth if (g_test_slow()) {
5091e8a1faeSThomas Huth qtest_add_func("/ivshmem/pair", test_ivshmem_pair);
5101e8a1faeSThomas Huth qtest_add_func("/ivshmem/server", test_ivshmem_server);
5111e8a1faeSThomas Huth }
51246d11f9dSMichael Labiuk if (!strcmp(arch, "x86_64") && qtest_has_machine("q35")) {
51346d11f9dSMichael Labiuk qtest_add_func("/ivshmem/hotplug-q35", test_ivshmem_hotplug_q35);
51446d11f9dSMichael Labiuk }
5151e8a1faeSThomas Huth
5161e8a1faeSThomas Huth out:
5171e8a1faeSThomas Huth ret = g_test_run();
5181e8a1faeSThomas Huth cleanup();
5191e8a1faeSThomas Huth return ret;
5201e8a1faeSThomas Huth }
521