• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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