1 // SPDX-License-Identifier: BSD-3-Clause
2 //
3 // Copyright(c) 2019 Intel Corporation. All rights reserved.
4 //
5 // Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
6 // Author: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
7
8 /*
9 * Creates an IO bridge between two QEMU instances where messages can be passed
10 * between the parent and child instances via messages queues and shared memory.
11 *
12 * The parent is usually the QEMU instance that runs the operating system (like
13 * Linux) on the application processor whilst the child is typically a smaller
14 * processor running an embedded firmware. The parent and child do not need to
15 * be the same architecture but are expected to communicate over a local bus.
16 */
17
18 #include <mqueue.h>
19 #include <unistd.h>
20 #include <sys/mman.h>
21 #include <sys/stat.h>
22 #include <sys/types.h>
23 #include <fcntl.h>
24 #include <stdint.h>
25 #include <stdbool.h>
26 #include <stdio.h>
27 #include <glib.h>
28 #include <errno.h>
29 #include "qemu-bridge.h"
30
31 /* set to 1 to enable debug */
32 static int io_bridge_debug;//QEMU_IO_DEBUG;
33
34 /* we can either be parent or child */
35 #define ROLE_NONE 0
36 #define ROLE_PARENT 1
37 #define ROLE_CHILD 2
38
39 static int role = ROLE_NONE;
40
41 #define QEMU_IO_MAX_MSGS 8
42 #define QEMU_IO_MAX_MSG_SIZE 128
43 #define QEMU_IO_MAX_SHM_REGIONS 32
44
45 #define NAME_SIZE 64
46
47 struct io_shm {
48 int fd;
49 void *addr;
50 char name[NAME_SIZE];
51 size_t size;
52 };
53
54 struct io_mq {
55 char mq_name[NAME_SIZE];
56 char thread_name[NAME_SIZE];
57 struct mq_attr mqattr;
58 mqd_t mqdes;
59 };
60
61 struct io_bridge {
62 struct io_mq parent;
63 struct io_mq child;
64 GThread *io_thread;
65 int (*cb)(void *data, struct qemu_io_msg *msg);
66 struct io_shm shm[QEMU_IO_MAX_SHM_REGIONS];
67 void *data;
68 };
69
70 static struct io_bridge _iob;
71 static int _id;
72
73 /* parent reader Q */
parent_reader_thread(gpointer data)74 static gpointer parent_reader_thread(gpointer data)
75 {
76 struct io_bridge *io = data;
77 char buf[QEMU_IO_MAX_MSG_SIZE];
78 int i;
79
80 mq_getattr(io->parent.mqdes, &io->parent.mqattr);
81 if (io_bridge_debug)
82 fprintf(stdout, "bridge-io: %d messages are currently on parent queue.\n",
83 (int)io->parent.mqattr.mq_curmsgs);
84
85 while (mq_receive(io->parent.mqdes, buf, QEMU_IO_MAX_MSG_SIZE,
86 NULL) != -1) {
87 struct qemu_io_msg *hdr = (struct qemu_io_msg *)buf;
88
89 if (io_bridge_debug)
90 fprintf(stdout, "bridge-io: msg recv %d type %d size %d msg %d\n",
91 hdr->id, hdr->type, hdr->size, hdr->msg);
92
93 if (io->cb)
94 io->cb(io->data, hdr);
95 }
96
97 return 0;
98 }
99
100 /* child reader Q */
child_reader_thread(gpointer data)101 static gpointer child_reader_thread(gpointer data)
102 {
103 struct io_bridge *io = data;
104 char buf[QEMU_IO_MAX_MSG_SIZE];
105 int i;
106
107 mq_getattr(io->child.mqdes, &io->child.mqattr);
108 if (io_bridge_debug)
109 fprintf(stdout, "bridge-io: %d messages are currently on child queue.\n",
110 (int)io->child.mqattr.mq_curmsgs);
111
112 /* flush old messages here */
113 for (i = 0; i < io->child.mqattr.mq_curmsgs; i++) {
114 mq_receive(io->child.mqdes, buf, QEMU_IO_MAX_MSG_SIZE, NULL);
115 struct qemu_io_msg *hdr = (struct qemu_io_msg *)buf;
116
117 if (io_bridge_debug)
118 fprintf(stdout, "bridge-io: flushed %d type %d size %d msg %d\n",
119 hdr->id, hdr->type, hdr->size, hdr->msg);
120 }
121
122 while (mq_receive(io->child.mqdes, buf, QEMU_IO_MAX_MSG_SIZE,
123 NULL) != -1) {
124 struct qemu_io_msg *hdr = (struct qemu_io_msg *)buf;
125
126 if (io_bridge_debug)
127 fprintf(stdout, "bridge-io: msg recv %d type %d size %d msg %d\n",
128 hdr->id, hdr->type, hdr->size, hdr->msg);
129
130 if (io->cb)
131 io->cb(io->data, hdr);
132 }
133
134 return 0;
135 }
136
mq_init(const char * name,struct io_bridge * io)137 static int mq_init(const char *name, struct io_bridge *io)
138 {
139 int ret = 0;
140
141 io->parent.mqattr.mq_maxmsg = QEMU_IO_MAX_MSGS;
142 io->parent.mqattr.mq_msgsize = QEMU_IO_MAX_MSG_SIZE;
143 io->parent.mqattr.mq_flags = 0;
144 io->parent.mqattr.mq_curmsgs = 0;
145
146 io->child.mqattr.mq_maxmsg = QEMU_IO_MAX_MSGS;
147 io->child.mqattr.mq_msgsize = QEMU_IO_MAX_MSG_SIZE;
148 io->child.mqattr.mq_flags = 0;
149 io->child.mqattr.mq_curmsgs = 0;
150
151 if (role == ROLE_PARENT) {
152
153 /* Host */
154
155 sprintf(io->parent.thread_name, "io-bridge-%s", name);
156 io->io_thread = g_thread_new(io->parent.thread_name,
157 parent_reader_thread, io);
158
159 /* parent Rx Q */
160 sprintf(io->parent.mq_name, "/qemu-io-parent-%s", name);
161 io->parent.mqdes = mq_open(io->parent.mq_name,
162 O_RDONLY | O_CREAT,
163 0664, &io->parent.mqattr);
164 if (io->parent.mqdes < 0) {
165 fprintf(stderr, "failed to open parent Rx queue %d\n",
166 -errno);
167 ret = -errno;
168 }
169
170 /* parent Tx Q */
171 sprintf(io->child.mq_name, "/qemu-io-child-%s", name);
172 io->child.mqdes = mq_open(io->child.mq_name,
173 O_WRONLY | O_CREAT,
174 0664, &io->child.mqattr);
175 if (io->child.mqdes < 0) {
176 fprintf(stderr, "failed to open parent Tx queue %d\n",
177 -errno);
178 ret = -errno;
179 }
180 } else {
181
182 /* DSP */
183
184 sprintf(io->child.thread_name, "io-bridge-%s", name);
185 io->io_thread = g_thread_new(io->child.thread_name,
186 child_reader_thread, io);
187
188 /* child Rx Q */
189 sprintf(io->child.mq_name, "/qemu-io-child-%s", name);
190 mq_unlink(io->child.mq_name);
191 io->child.mqdes = mq_open(io->child.mq_name,
192 O_RDONLY | O_CREAT,
193 0664, &io->child.mqattr);
194 if (io->child.mqdes < 0) {
195 fprintf(stderr, "failed to open child Rx queue %d\n",
196 -errno);
197 ret = -errno;
198 }
199
200 /* child Tx Q */
201 sprintf(io->parent.mq_name, "/qemu-io-parent-%s", name);
202 mq_unlink(io->parent.mq_name);
203 io->parent.mqdes = mq_open(io->parent.mq_name,
204 O_WRONLY | O_CREAT,
205 0664, &io->parent.mqattr);
206 if (io->parent.mqdes < 0) {
207 fprintf(stderr, "failed to open child Tx queue %d\n",
208 -errno);
209 ret = -errno;
210 }
211 }
212
213 if (ret == 0 && io_bridge_debug) {
214 fprintf(stdout, "bridge-io-mq: added %s\n",
215 io->parent.mq_name);
216 fprintf(stdout, "bridge-io-mq: added %s\n", io->child.mq_name);
217 }
218
219 return ret;
220 }
221
qemu_io_register_parent(const char * name,int (* cb)(void *,struct qemu_io_msg * msg),void * data)222 int qemu_io_register_parent(const char *name,
223 int (*cb)(void *, struct qemu_io_msg *msg),
224 void *data)
225 {
226 if (role != ROLE_NONE)
227 return -EINVAL;
228
229 role = ROLE_PARENT;
230 _iob.cb = cb;
231 _iob.data = data;
232
233 mq_init(name, &_iob);
234
235 return 0;
236 }
237
qemu_io_register_child(const char * name,int (* cb)(void *,struct qemu_io_msg * msg),void * data)238 int qemu_io_register_child(const char *name,
239 int (*cb)(void *, struct qemu_io_msg *msg),
240 void *data)
241 {
242 int ret = 0;
243
244 if (role != ROLE_NONE)
245 return -EINVAL;
246
247 role = ROLE_CHILD;
248 _iob.cb = cb;
249 _iob.data = data;
250
251 mq_init(name, &_iob);
252
253 return ret;
254 }
255
qemu_io_register_shm(const char * rname,int region,size_t size,void ** addr)256 int qemu_io_register_shm(const char *rname, int region, size_t size,
257 void **addr)
258 {
259 char *name;
260 int fd, ret;
261 void *a;
262
263 /* check that region is not already in use */
264 if (_iob.shm[region].fd)
265 return -EBUSY;
266
267 name = _iob.shm[region].name;
268 sprintf(name, "qemu-bridge-%s", rname);
269
270 fd = shm_open(name, O_RDWR | O_CREAT, 0664);
271 if (fd < 0) {
272 fprintf(stderr, "bridge-io: can't open SHM %d\n", errno);
273 return -errno;
274 }
275
276 ret = ftruncate(fd, size);
277 if (ret < 0) {
278 fprintf(stderr, "bridge-io: can't truncate %d\n", errno);
279 shm_unlink(name);
280 return -errno;
281 }
282
283 a = mmap(*addr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
284 if (!a) {
285 fprintf(stderr, "bridge-io: can't open mmap %d\n", errno);
286 shm_unlink(name);
287 return -errno;
288 }
289
290 if (io_bridge_debug)
291 fprintf(stdout, "bridge-io: %s fd %d region %d at %p allocated %zu bytes\n",
292 name, fd, region, a, size);
293 _iob.shm[region].fd = fd;
294 _iob.shm[region].addr = a;
295 _iob.shm[region].size = size;
296 *addr = a;
297
298 return ret;
299 }
300
301 #define PAGE_SIZE 4096
302
qemu_io_sync(int region,unsigned int offset,size_t length)303 int qemu_io_sync(int region, unsigned int offset, size_t length)
304 {
305 if (region < 0 || region > QEMU_IO_MAX_SHM_REGIONS)
306 return -EINVAL;
307
308 /* check that region is in use */
309 if (_iob.shm[region].fd == 0)
310 return -EINVAL;
311
312 /* align offset to pagesize */
313 offset -= (offset % PAGE_SIZE);
314
315 return msync(_iob.shm[region].addr + offset, length,
316 MS_SYNC | MS_INVALIDATE);
317 }
318
qemu_io_send_msg(struct qemu_io_msg * msg)319 int qemu_io_send_msg(struct qemu_io_msg *msg)
320 {
321 int ret;
322
323 msg->id = _id++;
324
325 if (role == ROLE_PARENT)
326 ret = mq_send(_iob.child.mqdes, (const char *)msg, msg->size,
327 0);
328 else
329 ret = mq_send(_iob.parent.mqdes, (const char *)msg, msg->size,
330 0);
331
332 if (io_bridge_debug)
333 fprintf(stdout, "bridge-io: msg send: %d type %d msg %d size %d ret %d\n",
334 msg->id, msg->type, msg->msg, msg->size, ret);
335 if (ret < 0)
336 fprintf(stderr, "bridge-io: msg send failed %d\n", -errno);
337
338 return ret;
339 }
340
qemu_io_send_msg_reply(struct qemu_io_msg * msg)341 int qemu_io_send_msg_reply(struct qemu_io_msg *msg)
342 {
343 int ret;
344
345 if (role == ROLE_PARENT)
346 ret = mq_send(_iob.child.mqdes, (const char *)msg, msg->size,
347 0);
348 else
349 ret = mq_send(_iob.parent.mqdes, (const char *)msg, msg->size,
350 0);
351
352 if (io_bridge_debug)
353 fprintf(stdout, "bridge-io: repmsg send: %d type %d msg %d size %d ret %d\n",
354 msg->id, msg->type, msg->msg, msg->size, ret);
355 if (ret < 0)
356 fprintf(stderr, "bridge-io: rmsg send failed %d\n", -errno);
357
358 return ret;
359 }
360
qemu_io_free(void)361 void qemu_io_free(void)
362 {
363 int i;
364
365 for (i = 0; i < QEMU_IO_MAX_SHM_REGIONS; i++) {
366 if (_iob.shm[i].fd) {
367 munmap(_iob.shm[i].addr, _iob.shm[i].size);
368 shm_unlink(_iob.shm[i].name);
369 close(_iob.shm[i].fd);
370 }
371 }
372
373 mq_unlink(_iob.parent.mq_name);
374 mq_unlink(_iob.child.mq_name);
375
376 mq_close(_iob.parent.mqdes);
377 mq_close(_iob.child.mqdes);
378 }
379
qemu_io_free_shm(int region)380 void qemu_io_free_shm(int region)
381 {
382 int err;
383
384 if (region < QEMU_IO_MAX_SHM_REGIONS && _iob.shm[region].fd) {
385 err = munmap(_iob.shm[region].addr, _iob.shm[region].size);
386 if (err < 0)
387 fprintf(stderr, "bridge-io: munmap failed %d\n",
388 errno);
389
390 /* client or host can unlink this, so it gets done twice */
391 shm_unlink(_iob.shm[region].name);
392 close(_iob.shm[region].fd);
393 _iob.shm[region].fd = 0;
394 }
395 }
396