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,int32_t descriptor)31 VirtioGpuPipeStream::VirtioGpuPipeStream(size_t bufSize, int32_t descriptor)
32 : IOStream(bufSize),
33 m_fd(descriptor),
34 m_virtio_mapped(nullptr),
35 m_bufsize(bufSize),
36 m_buf(nullptr),
37 m_writtenPos(0) {}
38
~VirtioGpuPipeStream()39 VirtioGpuPipeStream::~VirtioGpuPipeStream()
40 {
41 free(m_buf);
42 }
43
valid()44 bool VirtioGpuPipeStream::valid() {
45 return m_device != nullptr;
46 }
47
getRendernodeFd()48 int VirtioGpuPipeStream::getRendernodeFd() {
49 if (m_device == nullptr) {
50 return -1;
51 }
52 return m_device->getDeviceHandle();
53 }
54
connect(const char * serviceName)55 int VirtioGpuPipeStream::connect(const char* serviceName)
56 {
57 if (!m_device) {
58 m_device.reset(createPlatformVirtGpuDevice(kCapsetNone, m_fd));
59 if (!m_device) {
60 ALOGE("Failed to create VirtioGpuPipeStream VirtGpuDevice.");
61 return -1;
62 }
63
64 m_resource = m_device->createResource(/*width=*/kTransferBufferSize,
65 /*height=*/1,
66 /*stride=*/kTransferBufferSize,
67 /*size=*/kTransferBufferSize, VIRGL_FORMAT_R8_UNORM,
68 PIPE_BUFFER, VIRGL_BIND_CUSTOM);
69 if (!m_resource) {
70 ALOGE("Failed to create VirtioGpuPipeStream resource.");
71 return -1;
72 }
73
74 m_resourceMapping = m_resource->createMapping();
75 if (!m_resourceMapping) {
76 ALOGE("Failed to create VirtioGpuPipeStream resource mapping.");
77 return -1;
78 }
79
80 m_virtio_mapped = m_resourceMapping->asRawPtr();
81 if (!m_virtio_mapped) {
82 ALOGE("Failed to create VirtioGpuPipeStream resource mapping ptr.");
83 return -1;
84 }
85 }
86
87 wait();
88
89 if (serviceName) {
90 writeFully(serviceName, strlen(serviceName) + 1);
91 } else {
92 static const char kPipeString[] = "pipe:opengles";
93 std::string pipeStr(kPipeString);
94 writeFully(kPipeString, sizeof(kPipeString));
95 }
96
97 return 0;
98 }
99
processPipeInit()100 uint64_t VirtioGpuPipeStream::processPipeInit() {
101 connect("pipe:GLProcessPipe");
102 int32_t confirmInt = 100;
103 writeFully(&confirmInt, sizeof(confirmInt));
104 uint64_t res;
105 readFully(&res, sizeof(res));
106 return res;
107 }
108
allocBuffer(size_t minSize)109 void *VirtioGpuPipeStream::allocBuffer(size_t minSize) {
110 size_t allocSize = (m_bufsize < minSize ? minSize : m_bufsize);
111 if (!m_buf) {
112 m_buf = (unsigned char *)malloc(allocSize);
113 }
114 else if (m_bufsize < allocSize) {
115 unsigned char *p = (unsigned char *)realloc(m_buf, allocSize);
116 if (p != NULL) {
117 m_buf = p;
118 m_bufsize = allocSize;
119 } else {
120 ALOGE("realloc (%zu) failed\n", allocSize);
121 free(m_buf);
122 m_buf = NULL;
123 m_bufsize = 0;
124 }
125 }
126
127 return m_buf;
128 }
129
commitBuffer(size_t size)130 int VirtioGpuPipeStream::commitBuffer(size_t size) {
131 if (size == 0) return 0;
132 return writeFully(m_buf, size);
133 }
134
writeFully(const void * buf,size_t len)135 int VirtioGpuPipeStream::writeFully(const void *buf, size_t len)
136 {
137 //DBG(">> VirtioGpuPipeStream::writeFully %d\n", len);
138 if (!valid()) return -1;
139 if (!buf) {
140 if (len>0) {
141 // If len is non-zero, buf must not be NULL. Otherwise the pipe would be
142 // in a corrupted state, which is lethal for the emulator.
143 ALOGE(
144 "VirtioGpuPipeStream::writeFully failed, buf=NULL, len %zu,"
145 " lethal error, exiting",
146 len);
147 abort();
148 }
149 return 0;
150 }
151
152 size_t res = len;
153 int retval = 0;
154
155 while (res > 0) {
156 ssize_t stat = transferToHost((const char *)(buf) + (len - res), res);
157 if (stat > 0) {
158 res -= stat;
159 continue;
160 }
161 if (stat == 0) { /* EOF */
162 ALOGE("VirtioGpuPipeStream::writeFully failed: premature EOF\n");
163 retval = -1;
164 break;
165 }
166 if (errno == EAGAIN) {
167 continue;
168 }
169 retval = stat;
170 ALOGE("VirtioGpuPipeStream::writeFully failed: %s, lethal error, exiting.\n",
171 strerror(errno));
172 abort();
173 }
174 //DBG("<< VirtioGpuPipeStream::writeFully %d\n", len );
175 return retval;
176 }
177
readFully(void * buf,size_t len)178 const unsigned char *VirtioGpuPipeStream::readFully(void *buf, size_t len)
179 {
180 flush();
181
182 if (!valid()) return NULL;
183 if (!buf) {
184 if (len > 0) {
185 // If len is non-zero, buf must not be NULL. Otherwise the pipe would be
186 // in a corrupted state, which is lethal for the emulator.
187 ALOGE(
188 "VirtioGpuPipeStream::readFully failed, buf=NULL, len %zu, lethal"
189 " error, exiting.",
190 len);
191 abort();
192 }
193 }
194
195 size_t res = len;
196 while (res > 0) {
197 ssize_t stat = transferFromHost((char *)(buf) + len - res, res);
198 if (stat == 0) {
199 // client shutdown;
200 return NULL;
201 } else if (stat < 0) {
202 if (errno == EAGAIN) {
203 continue;
204 } else {
205 ALOGE(
206 "VirtioGpuPipeStream::readFully failed (buf %p, len %zu"
207 ", res %zu): %s, lethal error, exiting.",
208 buf, len, res, 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 ALOGE("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 ALOGE("VirtioGpuPipeStream: DRM_IOCTL_VIRTGPU_WAIT failed with %d (%s)\n", errno,
277 strerror(errno));
278 }
279
280 m_writtenPos = 0;
281 }
282
transferToHost(const void * buffer,size_t len)283 ssize_t VirtioGpuPipeStream::transferToHost(const void* buffer, size_t len) {
284 size_t todo = len;
285 size_t done = 0;
286 int ret = EAGAIN;
287
288 unsigned char* virtioPtr = m_virtio_mapped;
289
290 const unsigned char* readPtr = reinterpret_cast<const unsigned char*>(buffer);
291
292 while (done < len) {
293 size_t toXfer = todo > kTransferBufferSize ? kTransferBufferSize : todo;
294
295 if (toXfer > (kTransferBufferSize - m_writtenPos)) {
296 wait();
297 }
298
299 memcpy(virtioPtr + m_writtenPos, readPtr, toXfer);
300
301 ret = m_resource->transferToHost(m_writtenPos, toXfer);
302 if (ret) {
303 ALOGE("VirtioGpuPipeStream: failed to transferToHost() with errno %d (%s)\n", errno,
304 strerror(errno));
305 return (ssize_t)ret;
306 }
307
308 done += toXfer;
309 readPtr += toXfer;
310 todo -= toXfer;
311 m_writtenPos += toXfer;
312 }
313
314 return len;
315 }
316
transferFromHost(void * buffer,size_t len)317 ssize_t VirtioGpuPipeStream::transferFromHost(void* buffer, size_t len) {
318 size_t todo = len;
319 size_t done = 0;
320 int ret = EAGAIN;
321
322 const unsigned char* virtioPtr = m_virtio_mapped;
323 unsigned char* readPtr = reinterpret_cast<unsigned char*>(buffer);
324
325 if (m_writtenPos) {
326 wait();
327 }
328
329 while (done < len) {
330 size_t toXfer = todo > kTransferBufferSize ? kTransferBufferSize : todo;
331
332 ret = m_resource->transferFromHost(0, toXfer);
333 if (ret) {
334 ALOGE("VirtioGpuPipeStream: failed to transferFromHost() with errno %d (%s)\n", errno,
335 strerror(errno));
336 return (ssize_t)ret;
337 }
338
339 wait();
340
341 memcpy(readPtr, virtioPtr, toXfer);
342
343 done += toXfer;
344 readPtr += toXfer;
345 todo -= toXfer;
346 }
347
348 return len;
349 }
350