1 /*
2 * Copyright (C) 2018 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
17 #include "VirtioGpuPipeStream.h"
18
19 #include <errno.h>
20 #include <sys/mman.h>
21 #include <sys/types.h>
22 #include <unistd.h>
23
24 #include "VirtGpu.h"
25
26 static const size_t kTransferBufferSize = (1048576);
27
28 static const size_t kReadSize = 512 * 1024;
29 static const size_t kWriteOffset = kReadSize;
30
VirtioGpuPipeStream(size_t bufSize)31 VirtioGpuPipeStream::VirtioGpuPipeStream(size_t bufSize) :
32 IOStream(bufSize),
33 m_virtio_mapped(nullptr),
34 m_bufsize(bufSize),
35 m_buf(nullptr),
36 m_writtenPos(0) { }
37
VirtioGpuPipeStream(size_t bufSize,int fd)38 VirtioGpuPipeStream::VirtioGpuPipeStream(size_t bufSize, int fd) :
39 IOStream(bufSize),
40 m_fd(fd),
41 m_virtio_mapped(nullptr),
42 m_bufsize(bufSize),
43 m_buf(nullptr),
44 m_writtenPos(0) { }
45
~VirtioGpuPipeStream()46 VirtioGpuPipeStream::~VirtioGpuPipeStream()
47 {
48 free(m_buf);
49 }
50
valid()51 bool VirtioGpuPipeStream::valid() {
52 return m_device != nullptr;
53 }
54
getRendernodeFd()55 int VirtioGpuPipeStream::getRendernodeFd() {
56 if (m_device == nullptr) {
57 return -1;
58 }
59 return m_device->getDeviceHandle();
60 }
61
connect(const char * serviceName)62 int VirtioGpuPipeStream::connect(const char* serviceName)
63 {
64 if (!m_device) {
65 m_device.reset(createPlatformVirtGpuDevice(kCapsetNone, m_fd));
66 if (!m_device) {
67 ALOGE("Failed to create VirtioGpuPipeStream VirtGpuDevice.");
68 return -1;
69 }
70
71 m_resource =
72 m_device->createResource(kTransferBufferSize, 1, kTransferBufferSize,
73 VIRGL_FORMAT_R8_UNORM, PIPE_BUFFER, VIRGL_BIND_CUSTOM);
74 if (!m_resource) {
75 ALOGE("Failed to create VirtioGpuPipeStream resource.");
76 return -1;
77 }
78
79 m_resourceMapping = m_resource->createMapping();
80 if (!m_resourceMapping) {
81 ALOGE("Failed to create VirtioGpuPipeStream resource mapping.");
82 return -1;
83 }
84
85 m_virtio_mapped = m_resourceMapping->asRawPtr();
86 if (!m_virtio_mapped) {
87 ALOGE("Failed to create VirtioGpuPipeStream resource mapping ptr.");
88 return -1;
89 }
90 }
91
92 wait();
93
94 if (serviceName) {
95 writeFully(serviceName, strlen(serviceName) + 1);
96 } else {
97 static const char kPipeString[] = "pipe:opengles";
98 std::string pipeStr(kPipeString);
99 writeFully(kPipeString, sizeof(kPipeString));
100 }
101
102 return 0;
103 }
104
initProcessPipe()105 uint64_t VirtioGpuPipeStream::initProcessPipe() {
106 connect("pipe:GLProcessPipe");
107 int32_t confirmInt = 100;
108 writeFully(&confirmInt, sizeof(confirmInt));
109 uint64_t res;
110 readFully(&res, sizeof(res));
111 return res;
112 }
113
allocBuffer(size_t minSize)114 void *VirtioGpuPipeStream::allocBuffer(size_t minSize) {
115 size_t allocSize = (m_bufsize < minSize ? minSize : m_bufsize);
116 if (!m_buf) {
117 m_buf = (unsigned char *)malloc(allocSize);
118 }
119 else if (m_bufsize < allocSize) {
120 unsigned char *p = (unsigned char *)realloc(m_buf, allocSize);
121 if (p != NULL) {
122 m_buf = p;
123 m_bufsize = allocSize;
124 } else {
125 ERR("realloc (%zu) failed\n", allocSize);
126 free(m_buf);
127 m_buf = NULL;
128 m_bufsize = 0;
129 }
130 }
131
132 return m_buf;
133 }
134
commitBuffer(size_t size)135 int VirtioGpuPipeStream::commitBuffer(size_t size) {
136 if (size == 0) return 0;
137 return writeFully(m_buf, size);
138 }
139
writeFully(const void * buf,size_t len)140 int VirtioGpuPipeStream::writeFully(const void *buf, size_t len)
141 {
142 //DBG(">> VirtioGpuPipeStream::writeFully %d\n", len);
143 if (!valid()) return -1;
144 if (!buf) {
145 if (len>0) {
146 // If len is non-zero, buf must not be NULL. Otherwise the pipe would be
147 // in a corrupted state, which is lethal for the emulator.
148 ERR("VirtioGpuPipeStream::writeFully failed, buf=NULL, len %zu,"
149 " lethal error, exiting", len);
150 abort();
151 }
152 return 0;
153 }
154
155 size_t res = len;
156 int retval = 0;
157
158 while (res > 0) {
159 ssize_t stat = transferToHost((const char *)(buf) + (len - res), res);
160 if (stat > 0) {
161 res -= stat;
162 continue;
163 }
164 if (stat == 0) { /* EOF */
165 ERR("VirtioGpuPipeStream::writeFully failed: premature EOF\n");
166 retval = -1;
167 break;
168 }
169 if (errno == EAGAIN) {
170 continue;
171 }
172 retval = stat;
173 ERR("VirtioGpuPipeStream::writeFully failed: %s, lethal error, exiting.\n",
174 strerror(errno));
175 abort();
176 }
177 //DBG("<< VirtioGpuPipeStream::writeFully %d\n", len );
178 return retval;
179 }
180
readFully(void * buf,size_t len)181 const unsigned char *VirtioGpuPipeStream::readFully(void *buf, size_t len)
182 {
183 flush();
184
185 if (!valid()) return NULL;
186 if (!buf) {
187 if (len > 0) {
188 // If len is non-zero, buf must not be NULL. Otherwise the pipe would be
189 // in a corrupted state, which is lethal for the emulator.
190 ERR("VirtioGpuPipeStream::readFully failed, buf=NULL, len %zu, lethal"
191 " error, exiting.", len);
192 abort();
193 }
194 }
195
196 size_t res = len;
197 while (res > 0) {
198 ssize_t stat = transferFromHost((char *)(buf) + len - res, res);
199 if (stat == 0) {
200 // client shutdown;
201 return NULL;
202 } else if (stat < 0) {
203 if (errno == EAGAIN) {
204 continue;
205 } else {
206 ERR("VirtioGpuPipeStream::readFully failed (buf %p, len %zu"
207 ", res %zu): %s, lethal error, exiting.", buf, len, res,
208 strerror(errno));
209 abort();
210 }
211 } else {
212 res -= stat;
213 }
214 }
215 //DBG("<< VirtioGpuPipeStream::readFully %d\n", len);
216 return (const unsigned char *)buf;
217 }
218
commitBufferAndReadFully(size_t writeSize,void * userReadBufPtr,size_t totalReadSize)219 const unsigned char *VirtioGpuPipeStream::commitBufferAndReadFully(
220 size_t writeSize, void *userReadBufPtr, size_t totalReadSize)
221 {
222 return commitBuffer(writeSize) ? nullptr : readFully(userReadBufPtr, totalReadSize);
223 }
224
read(void * buf,size_t * inout_len)225 const unsigned char *VirtioGpuPipeStream::read( void *buf, size_t *inout_len)
226 {
227 //DBG(">> VirtioGpuPipeStream::read %d\n", *inout_len);
228 if (!valid()) return NULL;
229 if (!buf) {
230 ERR("VirtioGpuPipeStream::read failed, buf=NULL");
231 return NULL; // do not allow NULL buf in that implementation
232 }
233
234 int n = recv(buf, *inout_len);
235
236 if (n > 0) {
237 *inout_len = n;
238 return (const unsigned char *)buf;
239 }
240
241 //DBG("<< VirtioGpuPipeStream::read %d\n", *inout_len);
242 return NULL;
243 }
244
recv(void * buf,size_t len)245 int VirtioGpuPipeStream::recv(void *buf, size_t len)
246 {
247 if (!valid()) return -EINVAL;
248 char* p = (char *)buf;
249 int ret = 0;
250 while(len > 0) {
251 int res = transferFromHost(p, len);
252 if (res > 0) {
253 p += res;
254 ret += res;
255 len -= res;
256 continue;
257 }
258 if (res == 0) { /* EOF */
259 break;
260 }
261 if (errno != EAGAIN) {
262 continue;
263 }
264
265 /* A real error */
266 if (ret == 0)
267 ret = -1;
268 break;
269 }
270 return ret;
271 }
272
wait()273 void VirtioGpuPipeStream::wait() {
274 int ret = m_resource->wait();
275 if (ret) {
276 ERR("VirtioGpuPipeStream: DRM_IOCTL_VIRTGPU_WAIT failed with %d (%s)\n", errno, strerror(errno));
277 }
278
279 m_writtenPos = 0;
280 }
281
transferToHost(const void * buffer,size_t len)282 ssize_t VirtioGpuPipeStream::transferToHost(const void* buffer, size_t len) {
283 size_t todo = len;
284 size_t done = 0;
285 int ret = EAGAIN;
286
287 unsigned char* virtioPtr = m_virtio_mapped;
288
289 const unsigned char* readPtr = reinterpret_cast<const unsigned char*>(buffer);
290
291 while (done < len) {
292 size_t toXfer = todo > kTransferBufferSize ? kTransferBufferSize : todo;
293
294 if (toXfer > (kTransferBufferSize - m_writtenPos)) {
295 wait();
296 }
297
298 memcpy(virtioPtr + m_writtenPos, readPtr, toXfer);
299
300 ret = m_resource->transferToHost(m_writtenPos, toXfer);
301 if (ret) {
302 ERR("VirtioGpuPipeStream: failed to transferToHost() with errno %d (%s)\n", errno, strerror(errno));
303 return (ssize_t)ret;
304 }
305
306 done += toXfer;
307 readPtr += toXfer;
308 todo -= toXfer;
309 m_writtenPos += toXfer;
310 }
311
312 return len;
313 }
314
transferFromHost(void * buffer,size_t len)315 ssize_t VirtioGpuPipeStream::transferFromHost(void* buffer, size_t len) {
316 size_t todo = len;
317 size_t done = 0;
318 int ret = EAGAIN;
319
320 const unsigned char* virtioPtr = m_virtio_mapped;
321 unsigned char* readPtr = reinterpret_cast<unsigned char*>(buffer);
322
323 if (m_writtenPos) {
324 wait();
325 }
326
327 while (done < len) {
328 size_t toXfer = todo > kTransferBufferSize ? kTransferBufferSize : todo;
329
330 ret = m_resource->transferFromHost(0, toXfer);
331 if (ret) {
332 ERR("VirtioGpuPipeStream: failed to transferFromHost() with errno %d (%s)\n", errno, strerror(errno));
333 return (ssize_t)ret;
334 }
335
336 wait();
337
338 memcpy(readPtr, virtioPtr, toXfer);
339
340 done += toXfer;
341 readPtr += toXfer;
342 todo -= toXfer;
343 }
344
345 return len;
346 }
347