]>
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" | |
ad22c308 EU |
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 | { | |
ad22c308 EU |
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, 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 | { | |
ad22c308 EU |
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 | ||
e7b2c9ea EU |
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 | { | |
e7b2c9ea EU |
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 | ||
11ab8725 | 206 | if (!mpqemu_msg_valid(&msg_reply) || msg_reply.cmd != MPQEMU_CMD_RET) { |
e7b2c9ea EU |
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 | ||
ad22c308 EU |
215 | bool mpqemu_msg_valid(MPQemuMsg *msg) |
216 | { | |
dcf20655 | 217 | if (msg->cmd >= MPQEMU_CMD_MAX || msg->cmd < 0) { |
ad22c308 EU |
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 | ||
ed5d0019 JR |
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; | |
11ab8725 EU |
241 | case MPQEMU_CMD_PCI_CFGWRITE: |
242 | case MPQEMU_CMD_PCI_CFGREAD: | |
243 | if (msg->size != sizeof(PciConfDataMsg)) { | |
244 | return false; | |
245 | } | |
246 | break; | |
7ee3f823 JR |
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; | |
bd36adb8 JR |
253 | case MPQEMU_CMD_SET_IRQFD: |
254 | if (msg->size || (msg->num_fds != 2)) { | |
255 | return false; | |
256 | } | |
257 | break; | |
ed5d0019 JR |
258 | default: |
259 | break; | |
260 | } | |
261 | ||
ad22c308 EU |
262 | return true; |
263 | } |