ivshmem-flat.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  1. /*
  2. * Inter-VM Shared Memory Flat Device
  3. *
  4. * SPDX-FileCopyrightText: 2023 Linaro Ltd.
  5. * SPDX-FileContributor: Gustavo Romero <gustavo.romero@linaro.org>
  6. * SPDX-License-Identifier: GPL-2.0-or-later
  7. *
  8. */
  9. #include "qemu/osdep.h"
  10. #include "qemu/units.h"
  11. #include "qemu/error-report.h"
  12. #include "qemu/module.h"
  13. #include "qapi/error.h"
  14. #include "hw/irq.h"
  15. #include "hw/qdev-properties-system.h"
  16. #include "hw/sysbus.h"
  17. #include "chardev/char-fe.h"
  18. #include "exec/address-spaces.h"
  19. #include "trace.h"
  20. #include "hw/misc/ivshmem-flat.h"
  21. static int64_t ivshmem_flat_recv_msg(IvshmemFTState *s, int *pfd)
  22. {
  23. int64_t msg;
  24. int n, ret;
  25. n = 0;
  26. do {
  27. ret = qemu_chr_fe_read_all(&s->server_chr, (uint8_t *)&msg + n,
  28. sizeof(msg) - n);
  29. if (ret < 0) {
  30. if (ret == -EINTR) {
  31. continue;
  32. }
  33. exit(1);
  34. }
  35. n += ret;
  36. } while (n < sizeof(msg));
  37. if (pfd) {
  38. *pfd = qemu_chr_fe_get_msgfd(&s->server_chr);
  39. }
  40. return le64_to_cpu(msg);
  41. }
  42. static void ivshmem_flat_irq_handler(void *opaque)
  43. {
  44. VectorInfo *vi = opaque;
  45. EventNotifier *e = &vi->event_notifier;
  46. uint16_t vector_id;
  47. const VectorInfo (*v)[64];
  48. assert(e->initialized);
  49. vector_id = vi->id;
  50. /*
  51. * The vector info struct is passed to the handler via the 'opaque' pointer.
  52. * This struct pointer allows the retrieval of the vector ID and its
  53. * associated event notifier. However, for triggering an interrupt using
  54. * qemu_set_irq, it's necessary to also have a pointer to the device state,
  55. * i.e., a pointer to the IvshmemFTState struct. Since the vector info
  56. * struct is contained within the IvshmemFTState struct, its pointer can be
  57. * used to obtain the pointer to IvshmemFTState through simple pointer math.
  58. */
  59. v = (void *)(vi - vector_id); /* v = &IvshmemPeer->vector[0] */
  60. IvshmemPeer *own_peer = container_of(v, IvshmemPeer, vector);
  61. IvshmemFTState *s = container_of(own_peer, IvshmemFTState, own);
  62. /* Clear event */
  63. if (!event_notifier_test_and_clear(e)) {
  64. return;
  65. }
  66. trace_ivshmem_flat_irq_handler(vector_id);
  67. /*
  68. * Toggle device's output line, which is connected to interrupt controller,
  69. * generating an interrupt request to the CPU.
  70. */
  71. qemu_irq_pulse(s->irq);
  72. }
  73. static IvshmemPeer *ivshmem_flat_find_peer(IvshmemFTState *s, uint16_t peer_id)
  74. {
  75. IvshmemPeer *peer;
  76. /* Own ID */
  77. if (s->own.id == peer_id) {
  78. return &s->own;
  79. }
  80. /* Peer ID */
  81. QTAILQ_FOREACH(peer, &s->peer, next) {
  82. if (peer->id == peer_id) {
  83. return peer;
  84. }
  85. }
  86. return NULL;
  87. }
  88. static IvshmemPeer *ivshmem_flat_add_peer(IvshmemFTState *s, uint16_t peer_id)
  89. {
  90. IvshmemPeer *new_peer;
  91. new_peer = g_malloc0(sizeof(*new_peer));
  92. new_peer->id = peer_id;
  93. new_peer->vector_counter = 0;
  94. QTAILQ_INSERT_TAIL(&s->peer, new_peer, next);
  95. trace_ivshmem_flat_new_peer(peer_id);
  96. return new_peer;
  97. }
  98. static void ivshmem_flat_remove_peer(IvshmemFTState *s, uint16_t peer_id)
  99. {
  100. IvshmemPeer *peer;
  101. peer = ivshmem_flat_find_peer(s, peer_id);
  102. assert(peer);
  103. QTAILQ_REMOVE(&s->peer, peer, next);
  104. for (int n = 0; n < peer->vector_counter; n++) {
  105. int efd;
  106. efd = event_notifier_get_fd(&(peer->vector[n].event_notifier));
  107. close(efd);
  108. }
  109. g_free(peer);
  110. }
  111. static void ivshmem_flat_add_vector(IvshmemFTState *s, IvshmemPeer *peer,
  112. int vector_fd)
  113. {
  114. if (peer->vector_counter >= IVSHMEM_MAX_VECTOR_NUM) {
  115. trace_ivshmem_flat_add_vector_failure(peer->vector_counter,
  116. vector_fd, peer->id);
  117. close(vector_fd);
  118. return;
  119. }
  120. trace_ivshmem_flat_add_vector_success(peer->vector_counter,
  121. vector_fd, peer->id);
  122. /*
  123. * Set vector ID and its associated eventfd notifier and add them to the
  124. * peer.
  125. */
  126. peer->vector[peer->vector_counter].id = peer->vector_counter;
  127. g_unix_set_fd_nonblocking(vector_fd, true, NULL);
  128. event_notifier_init_fd(&peer->vector[peer->vector_counter].event_notifier,
  129. vector_fd);
  130. /*
  131. * If it's the device's own ID, register also the handler for the eventfd
  132. * so the device can be notified by the other peers.
  133. */
  134. if (peer == &s->own) {
  135. qemu_set_fd_handler(vector_fd, ivshmem_flat_irq_handler, NULL,
  136. &peer->vector);
  137. }
  138. peer->vector_counter++;
  139. }
  140. static void ivshmem_flat_process_msg(IvshmemFTState *s, uint64_t msg, int fd)
  141. {
  142. uint16_t peer_id;
  143. IvshmemPeer *peer;
  144. peer_id = msg & 0xFFFF;
  145. peer = ivshmem_flat_find_peer(s, peer_id);
  146. if (!peer) {
  147. peer = ivshmem_flat_add_peer(s, peer_id);
  148. }
  149. if (fd >= 0) {
  150. ivshmem_flat_add_vector(s, peer, fd);
  151. } else { /* fd == -1, which is received when peers disconnect. */
  152. ivshmem_flat_remove_peer(s, peer_id);
  153. }
  154. }
  155. static int ivshmem_flat_can_receive_data(void *opaque)
  156. {
  157. IvshmemFTState *s = opaque;
  158. assert(s->msg_buffered_bytes < sizeof(s->msg_buf));
  159. return sizeof(s->msg_buf) - s->msg_buffered_bytes;
  160. }
  161. static void ivshmem_flat_read_msg(void *opaque, const uint8_t *buf, int size)
  162. {
  163. IvshmemFTState *s = opaque;
  164. int fd;
  165. int64_t msg;
  166. assert(size >= 0 && s->msg_buffered_bytes + size <= sizeof(s->msg_buf));
  167. memcpy((unsigned char *)&s->msg_buf + s->msg_buffered_bytes, buf, size);
  168. s->msg_buffered_bytes += size;
  169. if (s->msg_buffered_bytes < sizeof(s->msg_buf)) {
  170. return;
  171. }
  172. msg = le64_to_cpu(s->msg_buf);
  173. s->msg_buffered_bytes = 0;
  174. fd = qemu_chr_fe_get_msgfd(&s->server_chr);
  175. ivshmem_flat_process_msg(s, msg, fd);
  176. }
  177. static uint64_t ivshmem_flat_iomem_read(void *opaque,
  178. hwaddr offset, unsigned size)
  179. {
  180. IvshmemFTState *s = opaque;
  181. uint32_t ret;
  182. trace_ivshmem_flat_read_mmr(offset);
  183. switch (offset) {
  184. case INTMASK:
  185. ret = 0; /* Ignore read since all bits are reserved in rev 1. */
  186. break;
  187. case INTSTATUS:
  188. ret = 0; /* Ignore read since all bits are reserved in rev 1. */
  189. break;
  190. case IVPOSITION:
  191. ret = s->own.id;
  192. break;
  193. case DOORBELL:
  194. trace_ivshmem_flat_read_mmr_doorbell(); /* DOORBELL is write-only */
  195. ret = 0;
  196. break;
  197. default:
  198. /* Should never reach out here due to iomem map range being exact */
  199. trace_ivshmem_flat_read_write_mmr_invalid(offset);
  200. ret = 0;
  201. }
  202. return ret;
  203. }
  204. static int ivshmem_flat_interrupt_peer(IvshmemFTState *s,
  205. uint16_t peer_id, uint16_t vector_id)
  206. {
  207. IvshmemPeer *peer;
  208. peer = ivshmem_flat_find_peer(s, peer_id);
  209. if (!peer) {
  210. trace_ivshmem_flat_interrupt_invalid_peer(peer_id);
  211. return 1;
  212. }
  213. event_notifier_set(&(peer->vector[vector_id].event_notifier));
  214. return 0;
  215. }
  216. static void ivshmem_flat_iomem_write(void *opaque, hwaddr offset,
  217. uint64_t value, unsigned size)
  218. {
  219. IvshmemFTState *s = opaque;
  220. uint16_t peer_id = (value >> 16) & 0xFFFF;
  221. uint16_t vector_id = value & 0xFFFF;
  222. trace_ivshmem_flat_write_mmr(offset);
  223. switch (offset) {
  224. case INTMASK:
  225. break;
  226. case INTSTATUS:
  227. break;
  228. case IVPOSITION:
  229. break;
  230. case DOORBELL:
  231. trace_ivshmem_flat_interrupt_peer(peer_id, vector_id);
  232. ivshmem_flat_interrupt_peer(s, peer_id, vector_id);
  233. break;
  234. default:
  235. /* Should never reach out here due to iomem map range being exact. */
  236. trace_ivshmem_flat_read_write_mmr_invalid(offset);
  237. break;
  238. }
  239. return;
  240. }
  241. static const MemoryRegionOps ivshmem_flat_ops = {
  242. .read = ivshmem_flat_iomem_read,
  243. .write = ivshmem_flat_iomem_write,
  244. .endianness = DEVICE_LITTLE_ENDIAN,
  245. .impl = { /* Read/write aligned at 32 bits. */
  246. .min_access_size = 4,
  247. .max_access_size = 4,
  248. },
  249. };
  250. static void ivshmem_flat_instance_init(Object *obj)
  251. {
  252. SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
  253. IvshmemFTState *s = IVSHMEM_FLAT(obj);
  254. /*
  255. * Init mem region for 4 MMRs (ivshmem_registers),
  256. * 32 bits each => 16 bytes (0x10).
  257. */
  258. memory_region_init_io(&s->iomem, obj, &ivshmem_flat_ops, s,
  259. "ivshmem-mmio", 0x10);
  260. sysbus_init_mmio(sbd, &s->iomem);
  261. /*
  262. * Create one output IRQ that will be connect to the
  263. * machine's interrupt controller.
  264. */
  265. sysbus_init_irq(sbd, &s->irq);
  266. QTAILQ_INIT(&s->peer);
  267. }
  268. static bool ivshmem_flat_connect_server(DeviceState *dev, Error **errp)
  269. {
  270. IvshmemFTState *s = IVSHMEM_FLAT(dev);
  271. SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
  272. int64_t protocol_version, msg;
  273. int shmem_fd;
  274. uint16_t peer_id;
  275. struct stat fdstat;
  276. /* Check ivshmem server connection. */
  277. if (!qemu_chr_fe_backend_connected(&s->server_chr)) {
  278. error_setg(errp, "ivshmem server socket not specified or incorret."
  279. " Can't create device.");
  280. return false;
  281. }
  282. /*
  283. * Message sequence from server on new connection:
  284. * _____________________________________
  285. * |STEP| uint64_t msg | int fd |
  286. * -------------------------------------
  287. *
  288. * 0 PROTOCOL -1 \
  289. * 1 OWN PEER ID -1 |-- Header/Greeting
  290. * 2 -1 shmem fd /
  291. *
  292. * 3 PEER IDx Other peer's Vector 0 eventfd
  293. * 4 PEER IDx Other peer's Vector 1 eventfd
  294. * . .
  295. * . .
  296. * . .
  297. * N PEER IDy Other peer's Vector 0 eventfd
  298. * N+1 PEER IDy Other peer's Vector 1 eventfd
  299. * . .
  300. * . .
  301. * . .
  302. *
  303. * ivshmem_flat_recv_msg() calls return 'msg' and 'fd'.
  304. *
  305. * See ./docs/specs/ivshmem-spec.txt for details on the protocol.
  306. */
  307. /* Step 0 */
  308. protocol_version = ivshmem_flat_recv_msg(s, NULL);
  309. /* Step 1 */
  310. msg = ivshmem_flat_recv_msg(s, NULL);
  311. peer_id = 0xFFFF & msg;
  312. s->own.id = peer_id;
  313. s->own.vector_counter = 0;
  314. trace_ivshmem_flat_proto_ver_own_id(protocol_version, s->own.id);
  315. /* Step 2 */
  316. msg = ivshmem_flat_recv_msg(s, &shmem_fd);
  317. /* Map shmem fd and MMRs into memory regions. */
  318. if (msg != -1 || shmem_fd < 0) {
  319. error_setg(errp, "Could not receive valid shmem fd."
  320. " Can't create device!");
  321. return false;
  322. }
  323. if (fstat(shmem_fd, &fdstat) != 0) {
  324. error_setg(errp, "Could not determine shmem fd size."
  325. " Can't create device!");
  326. return false;
  327. }
  328. trace_ivshmem_flat_shmem_size(shmem_fd, fdstat.st_size);
  329. /*
  330. * Shmem size provided by the ivshmem server must be equal to
  331. * device's shmem size.
  332. */
  333. if (fdstat.st_size != s->shmem_size) {
  334. error_setg(errp, "Can't map shmem fd: shmem size different"
  335. " from device size!");
  336. return false;
  337. }
  338. /*
  339. * Beyond step 2 ivshmem_process_msg, called by ivshmem_flat_read_msg
  340. * handler -- when data is available on the server socket -- will handle
  341. * the additional messages that will be generated by the server as peers
  342. * connect or disconnect.
  343. */
  344. qemu_chr_fe_set_handlers(&s->server_chr, ivshmem_flat_can_receive_data,
  345. ivshmem_flat_read_msg, NULL, NULL, s, NULL, true);
  346. memory_region_init_ram_from_fd(&s->shmem, OBJECT(s),
  347. "ivshmem-shmem", s->shmem_size,
  348. RAM_SHARED, shmem_fd, 0, NULL);
  349. sysbus_init_mmio(sbd, &s->shmem);
  350. return true;
  351. }
  352. static void ivshmem_flat_realize(DeviceState *dev, Error **errp)
  353. {
  354. if (!ivshmem_flat_connect_server(dev, errp)) {
  355. return;
  356. }
  357. }
  358. static const Property ivshmem_flat_props[] = {
  359. DEFINE_PROP_CHR("chardev", IvshmemFTState, server_chr),
  360. DEFINE_PROP_UINT32("shmem-size", IvshmemFTState, shmem_size, 4 * MiB),
  361. };
  362. static void ivshmem_flat_class_init(ObjectClass *klass, void *data)
  363. {
  364. DeviceClass *dc = DEVICE_CLASS(klass);
  365. dc->hotpluggable = true;
  366. dc->realize = ivshmem_flat_realize;
  367. set_bit(DEVICE_CATEGORY_MISC, dc->categories);
  368. device_class_set_props(dc, ivshmem_flat_props);
  369. /* Reason: Must be wired up in code (sysbus MRs and IRQ) */
  370. dc->user_creatable = false;
  371. }
  372. static const TypeInfo ivshmem_flat_types[] = {
  373. {
  374. .name = TYPE_IVSHMEM_FLAT,
  375. .parent = TYPE_SYS_BUS_DEVICE,
  376. .instance_size = sizeof(IvshmemFTState),
  377. .instance_init = ivshmem_flat_instance_init,
  378. .class_init = ivshmem_flat_class_init,
  379. },
  380. };
  381. DEFINE_TYPES(ivshmem_flat_types)