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).Unwrap()->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::PipeDevice> channel{
76 zx::channel(GetConnectToServiceFunction()(QEMU_PIPE_PATH))};
77 if (!channel) {
78 ALOGE("%s: failed to get service handle for " QEMU_PIPE_PATH,
79 __FUNCTION__);
80 return -1;
81 }
82
83 m_device = std::make_unique<
84 fidl::WireSyncClient<fuchsia_hardware_goldfish::PipeDevice>>(
85 std::move(channel));
86
87 auto pipe_ends =
88 fidl::CreateEndpoints<::fuchsia_hardware_goldfish::Pipe>();
89 if (!pipe_ends.is_ok()) {
90 ALOGE("zx::channel::create failed: %d", pipe_ends.status_value());
91 return ZX_HANDLE_INVALID;
92 }
93 m_device->OpenPipe(std::move(pipe_ends->server));
94 m_pipe =
95 std::make_unique<fidl::WireSyncClient<fuchsia_hardware_goldfish::Pipe>>(
96 std::move(pipe_ends->client));
97
98 zx::event event;
99 zx_status_t status = zx::event::create(0, &event);
100 if (status != ZX_OK) {
101 ALOGE("%s: failed to create event: %d", __FUNCTION__, status);
102 return -1;
103 }
104 zx::event event_copy;
105 status = event.duplicate(ZX_RIGHT_SAME_RIGHTS, &event_copy);
106 if (status != ZX_OK) {
107 ALOGE("%s: failed to duplicate event: %d", __FUNCTION__, status);
108 return -1;
109 }
110
111 {
112 auto result = m_pipe->SetEvent(std::move(event_copy));
113 if (!result.ok()) {
114 ALOGE("%s: failed to set event: %d:%d", __FUNCTION__,
115 result.status());
116 return -1;
117 }
118 }
119
120 if (!allocBuffer(m_bufsize)) {
121 ALOGE("%s: failed allocate initial buffer", __FUNCTION__);
122 return -1;
123 }
124
125 size_t len = strlen("pipe:opengles");
126 status = m_vmo.write("pipe:opengles", 0, len + 1);
127 if (status != ZX_OK) {
128 ALOGE("%s: failed write pipe name", __FUNCTION__);
129 return -1;
130 }
131
132 {
133 auto result = m_pipe->Write(len + 1, 0);
134 if (!result.ok() || result.Unwrap()->res != ZX_OK) {
135 ALOGD("%s: connecting to pipe service failed: %d:%d", __FUNCTION__,
136 result.status(), GET_STATUS_SAFE(result, res));
137 return -1;
138 }
139 }
140
141 m_event = std::move(event);
142 return 0;
143 }
144
allocBuffer(size_t minSize)145 void *QemuPipeStream::allocBuffer(size_t minSize)
146 {
147 // Add dedicated read buffer space at the front of buffer.
148 minSize += kReadSize;
149
150 zx_status_t status;
151 if (m_buf) {
152 if (minSize <= m_bufsize) {
153 return m_buf + kWriteOffset;
154 }
155 status = zx_vmar_unmap(zx_vmar_root_self(),
156 reinterpret_cast<zx_vaddr_t>(m_buf),
157 m_bufsize);
158 if (status != ZX_OK) {
159 ALOGE("zx_vmar_unmap failed: %d\n", status);
160 abort();
161 }
162 m_buf = nullptr;
163 }
164
165 size_t allocSize = m_bufsize < minSize ? minSize : m_bufsize;
166
167 {
168 auto result = m_pipe->SetBufferSize(allocSize);
169 if (!result.ok() || result.Unwrap()->res != ZX_OK) {
170 ALOGE("%s: failed to get buffer: %d:%d", __FUNCTION__,
171 result.status(), GET_STATUS_SAFE(result, res));
172 return nullptr;
173 }
174 }
175
176 zx::vmo vmo;
177 {
178 auto result = m_pipe->GetBuffer();
179 if (!result.ok() || result.Unwrap()->res != ZX_OK) {
180 ALOGE("%s: failed to get buffer: %d:%d", __FUNCTION__,
181 result.status(), GET_STATUS_SAFE(result, res));
182 return nullptr;
183 }
184 vmo = std::move(result.Unwrap()->vmo);
185 }
186
187 zx_vaddr_t mapped_addr;
188 status =
189 zx_vmar_map(zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0,
190 vmo.get(), 0, allocSize, &mapped_addr);
191 if (status != ZX_OK) {
192 ALOGE("%s: failed to map buffer: %d", __FUNCTION__, status);
193 return nullptr;
194 }
195
196 m_buf = reinterpret_cast<unsigned char*>(mapped_addr);
197 m_bufsize = allocSize;
198 m_vmo = std::move(vmo);
199 return m_buf + kWriteOffset;
200 }
201
commitBuffer(size_t size)202 int QemuPipeStream::commitBuffer(size_t size)
203 {
204 if (size == 0) return 0;
205
206 auto result = m_pipe->DoCall(size, kWriteOffset, 0, 0);
207 if (!result.ok() || result.Unwrap()->res != ZX_OK) {
208 ALOGD("%s: Pipe call failed: %d:%d", __FUNCTION__, result.status(),
209 GET_STATUS_SAFE(result, res));
210 return -1;
211 }
212
213 return 0;
214 }
215
writeFully(const void * buf,size_t len)216 int QemuPipeStream::writeFully(const void *buf, size_t len)
217 {
218 ALOGE("%s: unsupported", __FUNCTION__);
219 abort();
220 return -1;
221 }
222
getSocket() const223 QEMU_PIPE_HANDLE QemuPipeStream::getSocket() const {
224 return m_sock;
225 }
226
readFully(void * buf,size_t len)227 const unsigned char *QemuPipeStream::readFully(void *buf, size_t len)
228 {
229 return commitBufferAndReadFully(0, buf, len);
230 }
231
commitBufferAndReadFully(size_t size,void * buf,size_t len)232 const unsigned char *QemuPipeStream::commitBufferAndReadFully(size_t size, void *buf, size_t len)
233 {
234 if (!m_device)
235 return nullptr;
236
237 if (!buf) {
238 if (len > 0) {
239 ALOGE("QemuPipeStream::commitBufferAndReadFully failed, buf=NULL, len %zu, lethal"
240 " error, exiting.", len);
241 abort();
242 }
243 if (!size) {
244 return nullptr;
245 }
246 }
247
248 // Advance buffered read if not yet consumed.
249 size_t remaining = len;
250 size_t readSize = m_readLeft < remaining ? m_readLeft : remaining;
251 if (readSize) {
252 memcpy(static_cast<char*>(buf), m_buf + (m_read - m_readLeft), readSize);
253 remaining -= readSize;
254 m_readLeft -= readSize;
255 }
256
257 // Early out if nothing left to do.
258 if (!size && !remaining) {
259 return static_cast<const unsigned char *>(buf);
260 }
261
262 // Read up to kReadSize bytes if all buffered read has been consumed.
263 size_t maxRead = (m_readLeft || !remaining) ? 0 : kReadSize;
264
265 auto result = m_pipe->DoCall(size, kWriteOffset, maxRead, 0);
266 if (!result.ok()) {
267 ALOGD("%s: Pipe call failed: %d", __FUNCTION__, result.status());
268 return nullptr;
269 }
270
271 // Updated buffered read size.
272 if (result.Unwrap()->actual) {
273 m_read = m_readLeft = result.Unwrap()->actual;
274 }
275
276 // Consume buffered read and read more if neccessary.
277 while (remaining) {
278 readSize = m_readLeft < remaining ? m_readLeft : remaining;
279 if (readSize) {
280 memcpy(static_cast<char*>(buf) + (len - remaining),
281 m_buf + (m_read - m_readLeft),
282 readSize);
283 remaining -= readSize;
284 m_readLeft -= readSize;
285 continue;
286 }
287
288 auto result = m_pipe->Read(kReadSize, 0);
289 if (!result.ok()) {
290 ALOGD("%s: Failed reading from pipe: %d:%d", __FUNCTION__,
291 result.status());
292 return nullptr;
293 }
294
295 if (result.Unwrap()->actual) {
296 m_read = m_readLeft = result.Unwrap()->actual;
297 continue;
298 }
299 if (result.Unwrap()->res != ZX_ERR_SHOULD_WAIT) {
300 ALOGD("%s: Error reading from pipe: %d", __FUNCTION__,
301 result.Unwrap()->res);
302 return nullptr;
303 }
304
305 zx_signals_t observed = ZX_SIGNAL_NONE;
306 zx_status_t status = m_event.wait_one(
307 fuchsia_hardware_goldfish::wire::kSignalReadable |
308 fuchsia_hardware_goldfish::wire::kSignalHangup,
309 zx::time::infinite(), &observed);
310 if (status != ZX_OK) {
311 ALOGD("%s: wait_one failed: %d", __FUNCTION__, status);
312 return nullptr;
313 }
314 if (observed & fuchsia_hardware_goldfish::wire::kSignalHangup) {
315 ALOGD("%s: Remote end hungup", __FUNCTION__);
316 return nullptr;
317 }
318 }
319
320 return static_cast<const unsigned char *>(buf);
321 }
322
read(void * buf,size_t * inout_len)323 const unsigned char *QemuPipeStream::read(void *buf, size_t *inout_len)
324 {
325 ALOGE("%s: unsupported", __FUNCTION__);
326 abort();
327 return nullptr;
328 }
329
recv(void * buf,size_t len)330 int QemuPipeStream::recv(void *buf, size_t len)
331 {
332 ALOGE("%s: unsupported", __FUNCTION__);
333 abort();
334 return -1;
335 }
336