xref: /openbmc/qemu/hw/remote/mpqemu-link.c (revision d6cd3ae0)
1 /*
2  * Communication channel between QEMU and remote device process
3  *
4  * Copyright © 2018, 2021 Oracle and/or its affiliates.
5  *
6  * This work is licensed under the terms of the GNU GPL, version 2 or later.
7  * See the COPYING file in the top-level directory.
8  *
9  */
10 
11 #include "qemu/osdep.h"
12 
13 #include "qemu/module.h"
14 #include "hw/remote/mpqemu-link.h"
15 #include "qapi/error.h"
16 #include "qemu/iov.h"
17 #include "qemu/error-report.h"
18 #include "qemu/main-loop.h"
19 #include "io/channel.h"
20 #include "sysemu/iothread.h"
21 #include "trace.h"
22 
23 /*
24  * Send message over the ioc QIOChannel.
25  * This function is safe to call from:
26  * - main loop in co-routine context. Will block the main loop if not in
27  *   co-routine context;
28  * - vCPU thread with no co-routine context and if the channel is not part
29  *   of the main loop handling;
30  * - IOThread within co-routine context, outside of co-routine context
31  *   will block IOThread;
32  * Returns true if no errors were encountered, false otherwise.
33  */
34 bool mpqemu_msg_send(MPQemuMsg *msg, QIOChannel *ioc, Error **errp)
35 {
36     bool iolock = qemu_mutex_iothread_locked();
37     bool iothread = qemu_in_iothread();
38     struct iovec send[2] = {};
39     int *fds = NULL;
40     size_t nfds = 0;
41     bool ret = false;
42 
43     send[0].iov_base = msg;
44     send[0].iov_len = MPQEMU_MSG_HDR_SIZE;
45 
46     send[1].iov_base = (void *)&msg->data;
47     send[1].iov_len = msg->size;
48 
49     if (msg->num_fds) {
50         nfds = msg->num_fds;
51         fds = msg->fds;
52     }
53 
54     /*
55      * Dont use in IOThread out of co-routine context as
56      * it will block IOThread.
57      */
58     assert(qemu_in_coroutine() || !iothread);
59 
60     /*
61      * Skip unlocking/locking iothread lock when the IOThread is running
62      * in co-routine context. Co-routine context is asserted above
63      * for IOThread case.
64      * Also skip lock handling while in a co-routine in the main context.
65      */
66     if (iolock && !iothread && !qemu_in_coroutine()) {
67         qemu_mutex_unlock_iothread();
68     }
69 
70     if (!qio_channel_writev_full_all(ioc, send, G_N_ELEMENTS(send),
71                                     fds, nfds, 0, errp)) {
72         ret = true;
73     } else {
74         trace_mpqemu_send_io_error(msg->cmd, msg->size, nfds);
75     }
76 
77     if (iolock && !iothread && !qemu_in_coroutine()) {
78         /* See above comment why skip locking here. */
79         qemu_mutex_lock_iothread();
80     }
81 
82     return ret;
83 }
84 
85 /*
86  * Read message from the ioc QIOChannel.
87  * This function is safe to call from:
88  * - From main loop in co-routine context. Will block the main loop if not in
89  *   co-routine context;
90  * - From vCPU thread with no co-routine context and if the channel is not part
91  *   of the main loop handling;
92  * - From IOThread within co-routine context, outside of co-routine context
93  *   will block IOThread;
94  */
95 static ssize_t mpqemu_read(QIOChannel *ioc, void *buf, size_t len, int **fds,
96                            size_t *nfds, Error **errp)
97 {
98     struct iovec iov = { .iov_base = buf, .iov_len = len };
99     bool iolock = qemu_mutex_iothread_locked();
100     bool iothread = qemu_in_iothread();
101     int ret = -1;
102 
103     /*
104      * Dont use in IOThread out of co-routine context as
105      * it will block IOThread.
106      */
107     assert(qemu_in_coroutine() || !iothread);
108 
109     if (iolock && !iothread && !qemu_in_coroutine()) {
110         qemu_mutex_unlock_iothread();
111     }
112 
113     ret = qio_channel_readv_full_all_eof(ioc, &iov, 1, fds, nfds, errp);
114 
115     if (iolock && !iothread && !qemu_in_coroutine()) {
116         qemu_mutex_lock_iothread();
117     }
118 
119     return (ret <= 0) ? ret : iov.iov_len;
120 }
121 
122 bool mpqemu_msg_recv(MPQemuMsg *msg, QIOChannel *ioc, Error **errp)
123 {
124     ERRP_GUARD();
125     g_autofree int *fds = NULL;
126     size_t nfds = 0;
127     ssize_t len;
128     bool ret = false;
129 
130     len = mpqemu_read(ioc, msg, MPQEMU_MSG_HDR_SIZE, &fds, &nfds, errp);
131     if (len <= 0) {
132         goto fail;
133     } else if (len != MPQEMU_MSG_HDR_SIZE) {
134         error_setg(errp, "Message header corrupted");
135         goto fail;
136     }
137 
138     if (msg->size > sizeof(msg->data)) {
139         error_setg(errp, "Invalid size for message");
140         goto fail;
141     }
142 
143     if (!msg->size) {
144         goto copy_fds;
145     }
146 
147     len = mpqemu_read(ioc, &msg->data, msg->size, NULL, NULL, errp);
148     if (len <= 0) {
149         goto fail;
150     }
151     if (len != msg->size) {
152         error_setg(errp, "Unable to read full message");
153         goto fail;
154     }
155 
156 copy_fds:
157     msg->num_fds = nfds;
158     if (nfds > G_N_ELEMENTS(msg->fds)) {
159         error_setg(errp,
160                    "Overflow error: received %zu fds, more than max of %d fds",
161                    nfds, REMOTE_MAX_FDS);
162         goto fail;
163     }
164     if (nfds) {
165         memcpy(msg->fds, fds, nfds * sizeof(int));
166     }
167 
168     ret = true;
169 
170 fail:
171     if (*errp) {
172         trace_mpqemu_recv_io_error(msg->cmd, msg->size, nfds);
173     }
174     while (*errp && nfds) {
175         close(fds[nfds - 1]);
176         nfds--;
177     }
178 
179     return ret;
180 }
181 
182 /*
183  * Send msg and wait for a reply with command code RET_MSG.
184  * Returns the message received of size u64 or UINT64_MAX
185  * on error.
186  * Called from VCPU thread in non-coroutine context.
187  * Used by the Proxy object to communicate to remote processes.
188  */
189 uint64_t mpqemu_msg_send_and_await_reply(MPQemuMsg *msg, PCIProxyDev *pdev,
190                                          Error **errp)
191 {
192     MPQemuMsg msg_reply = {0};
193     uint64_t ret = UINT64_MAX;
194 
195     assert(!qemu_in_coroutine());
196 
197     QEMU_LOCK_GUARD(&pdev->io_mutex);
198     if (!mpqemu_msg_send(msg, pdev->ioc, errp)) {
199         return ret;
200     }
201 
202     if (!mpqemu_msg_recv(&msg_reply, pdev->ioc, errp)) {
203         return ret;
204     }
205 
206     if (!mpqemu_msg_valid(&msg_reply) || msg_reply.cmd != MPQEMU_CMD_RET) {
207         error_setg(errp, "ERROR: Invalid reply received for command %d",
208                          msg->cmd);
209         return ret;
210     }
211 
212     return msg_reply.data.u64;
213 }
214 
215 bool mpqemu_msg_valid(MPQemuMsg *msg)
216 {
217     if (msg->cmd >= MPQEMU_CMD_MAX || msg->cmd < 0) {
218         return false;
219     }
220 
221     /* Verify FDs. */
222     if (msg->num_fds >= REMOTE_MAX_FDS) {
223         return false;
224     }
225 
226     if (msg->num_fds > 0) {
227         for (int i = 0; i < msg->num_fds; i++) {
228             if (fcntl(msg->fds[i], F_GETFL) == -1) {
229                 return false;
230             }
231         }
232     }
233 
234      /* Verify message specific fields. */
235     switch (msg->cmd) {
236     case MPQEMU_CMD_SYNC_SYSMEM:
237         if (msg->num_fds == 0 || msg->size != sizeof(SyncSysmemMsg)) {
238             return false;
239         }
240         break;
241     case MPQEMU_CMD_PCI_CFGWRITE:
242     case MPQEMU_CMD_PCI_CFGREAD:
243         if (msg->size != sizeof(PciConfDataMsg)) {
244             return false;
245         }
246         break;
247     case MPQEMU_CMD_BAR_WRITE:
248     case MPQEMU_CMD_BAR_READ:
249         if ((msg->size != sizeof(BarAccessMsg)) || (msg->num_fds != 0)) {
250             return false;
251         }
252         break;
253     case MPQEMU_CMD_SET_IRQFD:
254         if (msg->size || (msg->num_fds != 2)) {
255             return false;
256         }
257         break;
258     default:
259         break;
260     }
261 
262     return true;
263 }
264