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 <fuchsia/hardware/goldfish/pipe/c/fidl.h>
21 #include <lib/fdio/fdio.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <zircon/process.h>
27
28 #include <utility>
29
QemuPipeStream(size_t bufSize)30 QemuPipeStream::QemuPipeStream(size_t bufSize) :
31 IOStream(bufSize),
32 m_sock(-1),
33 m_bufsize(bufSize),
34 m_buf(nullptr)
35 {
36 }
37
QemuPipeStream(QEMU_PIPE_HANDLE sock,size_t bufSize)38 QemuPipeStream::QemuPipeStream(QEMU_PIPE_HANDLE sock, size_t bufSize) :
39 IOStream(bufSize),
40 m_sock(sock),
41 m_bufsize(bufSize),
42 m_buf(nullptr)
43 {
44 }
45
~QemuPipeStream()46 QemuPipeStream::~QemuPipeStream()
47 {
48 if (m_channel.is_valid()) {
49 flush();
50 }
51 if (m_buf) {
52 zx_status_t status = zx_vmar_unmap(zx_vmar_root_self(),
53 reinterpret_cast<zx_vaddr_t>(m_buf),
54 m_bufsize);
55 if (status != ZX_OK) {
56 ALOGE("zx_vmar_unmap failed: %d\n", status);
57 abort();
58 }
59 }
60 }
61
connect(void)62 int QemuPipeStream::connect(void)
63 {
64 int fd = TEMP_FAILURE_RETRY(open(QEMU_PIPE_PATH, O_RDWR));
65 if (fd < 0) {
66 ALOGE("%s: failed to open " QEMU_PIPE_PATH ": %s",
67 __FUNCTION__, strerror(errno));
68 return -1;
69 }
70
71 zx::channel channel;
72 zx_status_t status = fdio_get_service_handle(
73 fd, channel.reset_and_get_address());
74 if (status != ZX_OK) {
75 ALOGE("%s: failed to get service handle for " QEMU_PIPE_PATH ": %d",
76 __FUNCTION__, status);
77 close(fd);
78 return -1;
79 }
80
81 zx::event event;
82 status = zx::event::create(0, &event);
83 if (status != ZX_OK) {
84 ALOGE("%s: failed to create event: %d", __FUNCTION__, status);
85 return -1;
86 }
87 zx::event event_copy;
88 status = event.duplicate(ZX_RIGHT_SAME_RIGHTS, &event_copy);
89 if (status != ZX_OK) {
90 ALOGE("%s: failed to duplicate event: %d", __FUNCTION__, status);
91 return -1;
92 }
93
94 status = fuchsia_hardware_goldfish_pipe_DeviceSetEvent(
95 channel.get(), event_copy.release());
96 if (status != ZX_OK) {
97 ALOGE("%s: failed to set event: %d:%d", __FUNCTION__, status);
98 return -1;
99 }
100
101 zx_status_t status2 = ZX_OK;
102 zx::vmo vmo;
103 status = fuchsia_hardware_goldfish_pipe_DeviceGetBuffer(
104 channel.get(), &status2, vmo.reset_and_get_address());
105 if (status != ZX_OK || status2 != ZX_OK) {
106 ALOGE("%s: failed to get buffer: %d:%d", __FUNCTION__, status, status2);
107 return -1;
108 }
109
110 size_t len = strlen("pipe:opengles");
111 status = vmo.write("pipe:opengles", 0, len + 1);
112 if (status != ZX_OK) {
113 ALOGE("%s: failed write pipe name", __FUNCTION__);
114 return -1;
115 }
116
117 uint64_t actual;
118 status = fuchsia_hardware_goldfish_pipe_DeviceWrite(
119 channel.get(), len + 1, 0, &status2, &actual);
120 if (status != ZX_OK || status2 != ZX_OK) {
121 ALOGD("%s: connecting to pipe service failed: %d:%d", __FUNCTION__,
122 status, status2);
123 return -1;
124 }
125
126 m_channel = std::move(channel);
127 m_event = std::move(event);
128 m_vmo = std::move(vmo);
129 return 0;
130 }
131
allocBuffer(size_t minSize)132 void *QemuPipeStream::allocBuffer(size_t minSize)
133 {
134 zx_status_t status;
135 if (m_buf) {
136 if (minSize <= m_bufsize) {
137 return m_buf;
138 }
139 status = zx_vmar_unmap(zx_vmar_root_self(),
140 reinterpret_cast<zx_vaddr_t>(m_buf),
141 m_bufsize);
142 if (status != ZX_OK) {
143 ALOGE("zx_vmar_unmap failed: %d\n", status);
144 abort();
145 }
146 m_buf = nullptr;
147 }
148
149 size_t allocSize = m_bufsize < minSize ? minSize : m_bufsize;
150
151 zx_status_t status2 = ZX_OK;
152 status = fuchsia_hardware_goldfish_pipe_DeviceSetBufferSize(
153 m_channel.get(), allocSize, &status2);
154 if (status != ZX_OK || status2 != ZX_OK) {
155 ALOGE("%s: failed to get buffer: %d:%d", __FUNCTION__, status, status2);
156 return nullptr;
157 }
158
159 zx::vmo vmo;
160 status = fuchsia_hardware_goldfish_pipe_DeviceGetBuffer(
161 m_channel.get(), &status2, vmo.reset_and_get_address());
162 if (status != ZX_OK || status2 != ZX_OK) {
163 ALOGE("%s: failed to get buffer: %d:%d", __FUNCTION__, status, status2);
164 return nullptr;
165 }
166
167 zx_vaddr_t mapped_addr;
168 status = zx_vmar_map(zx_vmar_root_self(),
169 ZX_VM_PERM_READ | ZX_VM_PERM_WRITE,
170 0, vmo.get(), 0, allocSize, &mapped_addr);
171 if (status != ZX_OK) {
172 ALOGE("%s: failed to map buffer: %d:%d", __FUNCTION__, status);
173 return nullptr;
174 }
175
176 m_buf = reinterpret_cast<unsigned char*>(mapped_addr);
177 m_bufsize = allocSize;
178 m_vmo = std::move(vmo);
179 return m_buf;
180 }
181
commitBuffer(size_t size)182 int QemuPipeStream::commitBuffer(size_t size)
183 {
184 if (size == 0) return 0;
185
186 size_t remaining = size;
187 while (remaining) {
188 zx_status_t status2 = ZX_OK;
189 uint64_t actual = 0;
190 zx_status_t status = fuchsia_hardware_goldfish_pipe_DeviceWrite(
191 m_channel.get(), remaining, size - remaining, &status2, &actual);
192 if (status != ZX_OK) {
193 ALOGD("%s: Failed writing to pipe: %d", __FUNCTION__, status);
194 return -1;
195 }
196 if (actual) {
197 remaining -= actual;
198 continue;
199 }
200 if (status2 != ZX_ERR_SHOULD_WAIT) {
201 ALOGD("%s: Error writing to pipe: %d", __FUNCTION__, status2);
202 return -1;
203 }
204 zx_signals_t observed = ZX_SIGNAL_NONE;
205 status = m_event.wait_one(
206 fuchsia_hardware_goldfish_pipe_SIGNAL_WRITABLE |
207 fuchsia_hardware_goldfish_pipe_SIGNAL_HANGUP,
208 zx::time::infinite(), &observed);
209 if (status != ZX_OK) {
210 ALOGD("%s: wait_one failed: %d", __FUNCTION__, status);
211 return -1;
212 }
213 if (observed & fuchsia_hardware_goldfish_pipe_SIGNAL_HANGUP) {
214 ALOGD("%s: Remote end hungup", __FUNCTION__);
215 return -1;
216 }
217 }
218
219 return 0;
220 }
221
writeFully(const void * buf,size_t len)222 int QemuPipeStream::writeFully(const void *buf, size_t len)
223 {
224 ALOGE("%s: unsupported", __FUNCTION__);
225 abort();
226 return -1;
227 }
228
getSocket() const229 QEMU_PIPE_HANDLE QemuPipeStream::getSocket() const {
230 return m_sock;
231 }
232
readFully(void * buf,size_t len)233 const unsigned char *QemuPipeStream::readFully(void *buf, size_t len)
234 {
235 if (!m_channel.is_valid()) return nullptr;
236
237 if (!buf) {
238 if (len > 0) {
239 ALOGE("QemuPipeStream::readFully failed, buf=NULL, len %zu, lethal"
240 " error, exiting.", len);
241 abort();
242 }
243 return nullptr;
244 }
245
246 size_t remaining = len;
247 while (remaining) {
248 size_t readSize = m_bufsize < remaining ? m_bufsize : remaining;
249 zx_status_t status2 = ZX_OK;
250 uint64_t actual = 0;
251 zx_status_t status = fuchsia_hardware_goldfish_pipe_DeviceRead(
252 m_channel.get(), readSize, 0, &status2, &actual);
253 if (status != ZX_OK) {
254 ALOGD("%s: Failed reading from pipe: %d", __FUNCTION__, status);
255 return nullptr;
256 }
257 if (actual) {
258 m_vmo.read(static_cast<char *>(buf) + (len - remaining), 0, actual);
259 remaining -= actual;
260 continue;
261 }
262 if (status2 != ZX_ERR_SHOULD_WAIT) {
263 ALOGD("%s: Error reading from pipe: %d", __FUNCTION__, status2);
264 return nullptr;
265 }
266 zx_signals_t observed = ZX_SIGNAL_NONE;
267 status = m_event.wait_one(
268 fuchsia_hardware_goldfish_pipe_SIGNAL_READABLE |
269 fuchsia_hardware_goldfish_pipe_SIGNAL_HANGUP,
270 zx::time::infinite(), &observed);
271 if (status != ZX_OK) {
272 ALOGD("%s: wait_one failed: %d", __FUNCTION__, status);
273 return nullptr;
274 }
275 if (observed & fuchsia_hardware_goldfish_pipe_SIGNAL_HANGUP) {
276 ALOGD("%s: Remote end hungup", __FUNCTION__);
277 return nullptr;
278 }
279 }
280
281 return static_cast<const unsigned char *>(buf);
282 }
283
read(void * buf,size_t * inout_len)284 const unsigned char *QemuPipeStream::read(void *buf, size_t *inout_len)
285 {
286 ALOGE("%s: unsupported", __FUNCTION__);
287 abort();
288 return nullptr;
289 }
290
recv(void * buf,size_t len)291 int QemuPipeStream::recv(void *buf, size_t len)
292 {
293 ALOGE("%s: unsupported", __FUNCTION__);
294 abort();
295 return -1;
296 }
297