1 /*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #include "QemuPipeStream.h"
17
18 #include <cutils/log.h>
19 #include <errno.h>
20 #include <lib/zx/channel.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <zircon/process.h>
26
27 #include <utility>
28
29 #include "services/service_connector.h"
30
31 #define GET_STATUS_SAFE(result, member) \
32 ((result).ok() ? ((result)->member) : ZX_OK)
33
34 constexpr size_t kReadSize = 512 * 1024;
35 constexpr size_t kWriteOffset = kReadSize;
36
QemuPipeStream(size_t bufSize)37 QemuPipeStream::QemuPipeStream(size_t bufSize) :
38 IOStream(bufSize),
39 m_sock(-1),
40 m_bufsize(bufSize),
41 m_buf(nullptr),
42 m_read(0),
43 m_readLeft(0)
44 {
45 }
46
QemuPipeStream(QEMU_PIPE_HANDLE sock,size_t bufSize)47 QemuPipeStream::QemuPipeStream(QEMU_PIPE_HANDLE sock, size_t bufSize) :
48 IOStream(bufSize),
49 m_sock(sock),
50 m_bufsize(bufSize),
51 m_buf(nullptr),
52 m_read(0),
53 m_readLeft(0)
54 {
55 }
56
~QemuPipeStream()57 QemuPipeStream::~QemuPipeStream()
58 {
59 if (m_device) {
60 flush();
61 }
62 if (m_buf) {
63 zx_status_t status = zx_vmar_unmap(zx_vmar_root_self(),
64 reinterpret_cast<zx_vaddr_t>(m_buf),
65 m_bufsize);
66 if (status != ZX_OK) {
67 ALOGE("zx_vmar_unmap failed: %d\n", status);
68 abort();
69 }
70 }
71 }
72
connect(void)73 int QemuPipeStream::connect(void)
74 {
75 fidl::ClientEnd<fuchsia_hardware_goldfish::Controller> controller_channel{
76 zx::channel(GetConnectToServiceFunction()(QEMU_PIPE_PATH))};
77 if (!controller_channel) {
78 ALOGE("%s: failed to open " QEMU_PIPE_PATH,
79 __FUNCTION__);
80 return -1;
81 }
82 fidl::WireSyncClient controller(std::move(controller_channel));
83 zx::result pipe_device_ends =
84 fidl::CreateEndpoints<fuchsia_hardware_goldfish::PipeDevice>();
85 if (pipe_device_ends.is_error()) {
86 ALOGE("%s: zx_channel_create failed: %s", __FUNCTION__, pipe_device_ends.status_string());
87 return -1;
88 }
89
90 if (fidl::Status result = controller->OpenSession(std::move(pipe_device_ends->server));
91 !result.ok()) {
92 ALOGE("%s: failed to open session: %s", __FUNCTION__, result.status_string());
93 return -1;
94 }
95
96 m_device = std::make_unique<
97 fidl::WireSyncClient<fuchsia_hardware_goldfish::PipeDevice>>(
98 std::move(pipe_device_ends->client));
99
100 auto pipe_ends =
101 fidl::CreateEndpoints<::fuchsia_hardware_goldfish::Pipe>();
102 if (!pipe_ends.is_ok()) {
103 ALOGE("zx::channel::create failed: %d", pipe_ends.status_value());
104 return ZX_HANDLE_INVALID;
105 }
106 (*m_device)->OpenPipe(std::move(pipe_ends->server));
107 m_pipe =
108 fidl::WireSyncClient<fuchsia_hardware_goldfish::Pipe>(
109 std::move(pipe_ends->client));
110
111 zx::event event;
112 zx_status_t status = zx::event::create(0, &event);
113 if (status != ZX_OK) {
114 ALOGE("%s: failed to create event: %d", __FUNCTION__, status);
115 return -1;
116 }
117 zx::event event_copy;
118 status = event.duplicate(ZX_RIGHT_SAME_RIGHTS, &event_copy);
119 if (status != ZX_OK) {
120 ALOGE("%s: failed to duplicate event: %d", __FUNCTION__, status);
121 return -1;
122 }
123
124 {
125 auto result = m_pipe->SetEvent(std::move(event_copy));
126 if (!result.ok()) {
127 ALOGE("%s: failed to set event: %d:%d", __FUNCTION__,
128 result.status());
129 return -1;
130 }
131 }
132
133 if (!allocBuffer(m_bufsize)) {
134 ALOGE("%s: failed allocate initial buffer", __FUNCTION__);
135 return -1;
136 }
137
138 size_t len = strlen("pipe:opengles");
139 status = m_vmo.write("pipe:opengles", 0, len + 1);
140 if (status != ZX_OK) {
141 ALOGE("%s: failed write pipe name", __FUNCTION__);
142 return -1;
143 }
144
145 {
146 auto result = m_pipe->Write(len + 1, 0);
147 if (!result.ok() || result->res != ZX_OK) {
148 ALOGD("%s: connecting to pipe service failed: %d:%d", __FUNCTION__,
149 result.status(), GET_STATUS_SAFE(result, res));
150 return -1;
151 }
152 }
153
154 m_event = std::move(event);
155 return 0;
156 }
157
allocBuffer(size_t minSize)158 void *QemuPipeStream::allocBuffer(size_t minSize)
159 {
160 // Add dedicated read buffer space at the front of buffer.
161 minSize += kReadSize;
162
163 zx_status_t status;
164 if (m_buf) {
165 if (minSize <= m_bufsize) {
166 return m_buf + kWriteOffset;
167 }
168 status = zx_vmar_unmap(zx_vmar_root_self(),
169 reinterpret_cast<zx_vaddr_t>(m_buf),
170 m_bufsize);
171 if (status != ZX_OK) {
172 ALOGE("zx_vmar_unmap failed: %d\n", status);
173 abort();
174 }
175 m_buf = nullptr;
176 }
177
178 size_t allocSize = m_bufsize < minSize ? minSize : m_bufsize;
179
180 {
181 auto result = m_pipe->SetBufferSize(allocSize);
182 if (!result.ok() || result->res != ZX_OK) {
183 ALOGE("%s: failed to get buffer: %d:%d", __FUNCTION__,
184 result.status(), GET_STATUS_SAFE(result, res));
185 return nullptr;
186 }
187 }
188
189 zx::vmo vmo;
190 {
191 auto result = m_pipe->GetBuffer();
192 if (!result.ok() || result->res != ZX_OK) {
193 ALOGE("%s: failed to get buffer: %d:%d", __FUNCTION__,
194 result.status(), GET_STATUS_SAFE(result, res));
195 return nullptr;
196 }
197 vmo = std::move(result->vmo);
198 }
199
200 zx_vaddr_t mapped_addr;
201 status =
202 zx_vmar_map(zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0,
203 vmo.get(), 0, allocSize, &mapped_addr);
204 if (status != ZX_OK) {
205 ALOGE("%s: failed to map buffer: %d", __FUNCTION__, status);
206 return nullptr;
207 }
208
209 m_buf = reinterpret_cast<unsigned char*>(mapped_addr);
210 m_bufsize = allocSize;
211 m_vmo = std::move(vmo);
212 return m_buf + kWriteOffset;
213 }
214
commitBuffer(size_t size)215 int QemuPipeStream::commitBuffer(size_t size)
216 {
217 if (size == 0) return 0;
218
219 auto result = m_pipe->DoCall(size, kWriteOffset, 0, 0);
220 if (!result.ok() || result->res != ZX_OK) {
221 ALOGD("%s: Pipe call failed: %d:%d", __FUNCTION__, result.status(),
222 GET_STATUS_SAFE(result, res));
223 return -1;
224 }
225
226 return 0;
227 }
228
writeFully(const void * buf,size_t len)229 int QemuPipeStream::writeFully(const void *buf, size_t len)
230 {
231 ALOGE("%s: unsupported", __FUNCTION__);
232 abort();
233 return -1;
234 }
235
getSocket() const236 QEMU_PIPE_HANDLE QemuPipeStream::getSocket() const {
237 return m_sock;
238 }
239
readFully(void * buf,size_t len)240 const unsigned char *QemuPipeStream::readFully(void *buf, size_t len)
241 {
242 return commitBufferAndReadFully(0, buf, len);
243 }
244
commitBufferAndReadFully(size_t size,void * buf,size_t len)245 const unsigned char *QemuPipeStream::commitBufferAndReadFully(size_t size, void *buf, size_t len)
246 {
247 if (!m_device)
248 return nullptr;
249
250 if (!buf) {
251 if (len > 0) {
252 ALOGE("QemuPipeStream::commitBufferAndReadFully failed, buf=NULL, len %zu, lethal"
253 " error, exiting.", len);
254 abort();
255 }
256 if (!size) {
257 return nullptr;
258 }
259 }
260
261 // Advance buffered read if not yet consumed.
262 size_t remaining = len;
263 size_t readSize = m_readLeft < remaining ? m_readLeft : remaining;
264 if (readSize) {
265 memcpy(static_cast<char*>(buf), m_buf + (m_read - m_readLeft), readSize);
266 remaining -= readSize;
267 m_readLeft -= readSize;
268 }
269
270 // Early out if nothing left to do.
271 if (!size && !remaining) {
272 return static_cast<const unsigned char *>(buf);
273 }
274
275 // Read up to kReadSize bytes if all buffered read has been consumed.
276 size_t maxRead = (m_readLeft || !remaining) ? 0 : kReadSize;
277
278 auto result = m_pipe->DoCall(size, kWriteOffset, maxRead, 0);
279 if (!result.ok()) {
280 ALOGD("%s: Pipe call failed: %d", __FUNCTION__, result.status());
281 return nullptr;
282 }
283
284 // Updated buffered read size.
285 if (result->actual) {
286 m_read = m_readLeft = result->actual;
287 }
288
289 // Consume buffered read and read more if neccessary.
290 while (remaining) {
291 readSize = m_readLeft < remaining ? m_readLeft : remaining;
292 if (readSize) {
293 memcpy(static_cast<char*>(buf) + (len - remaining),
294 m_buf + (m_read - m_readLeft),
295 readSize);
296 remaining -= readSize;
297 m_readLeft -= readSize;
298 continue;
299 }
300
301 auto result = m_pipe->Read(kReadSize, 0);
302 if (!result.ok()) {
303 ALOGD("%s: Failed reading from pipe: %d:%d", __FUNCTION__,
304 result.status());
305 return nullptr;
306 }
307
308 if (result->actual) {
309 m_read = m_readLeft = result->actual;
310 continue;
311 }
312 if (result->res != ZX_ERR_SHOULD_WAIT) {
313 ALOGD("%s: Error reading from pipe: %d", __FUNCTION__,
314 result->res);
315 return nullptr;
316 }
317
318 zx_signals_t observed = ZX_SIGNAL_NONE;
319 zx_status_t status = m_event.wait_one(
320 fuchsia_hardware_goldfish::wire::kSignalReadable |
321 fuchsia_hardware_goldfish::wire::kSignalHangup,
322 zx::time::infinite(), &observed);
323 if (status != ZX_OK) {
324 ALOGD("%s: wait_one failed: %d", __FUNCTION__, status);
325 return nullptr;
326 }
327 if (observed & fuchsia_hardware_goldfish::wire::kSignalHangup) {
328 ALOGD("%s: Remote end hungup", __FUNCTION__);
329 return nullptr;
330 }
331 }
332
333 return static_cast<const unsigned char *>(buf);
334 }
335
read(void * buf,size_t * inout_len)336 const unsigned char *QemuPipeStream::read(void *buf, size_t *inout_len)
337 {
338 ALOGE("%s: unsupported", __FUNCTION__);
339 abort();
340 return nullptr;
341 }
342
recv(void * buf,size_t len)343 int QemuPipeStream::recv(void *buf, size_t len)
344 {
345 ALOGE("%s: unsupported", __FUNCTION__);
346 abort();
347 return -1;
348 }
349