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