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