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