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