xref: /qemu/tests/qtest/ivshmem-test.c (revision 58045186)
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);
16158045186SInè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++) {
21458045186SInè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++) {
21858045186SInè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++) {
22758045186SInè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++) {
23658045186SInè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