ivshmem-test.c 12 KB

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