1 /*
2 * Copyright 2011 Google LLC
3 * SPDX-License-Identifier: MIT
4 */
5 #include "QemuPipeStream.h"
6
7 #include <errno.h>
8 #include <qemu_pipe_bp.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <unistd.h>
13
14 #include "util/log.h"
15
16 static const size_t kReadSize = 512 * 1024;
17 static const size_t kWriteOffset = kReadSize;
18
QemuPipeStream(size_t bufSize)19 QemuPipeStream::QemuPipeStream(size_t bufSize)
20 : IOStream(bufSize), m_sock(-1), m_bufsize(bufSize), m_buf(NULL), m_read(0), m_readLeft(0) {}
21
QemuPipeStream(QEMU_PIPE_HANDLE sock,size_t bufSize)22 QemuPipeStream::QemuPipeStream(QEMU_PIPE_HANDLE sock, size_t bufSize)
23 : IOStream(bufSize), m_sock(sock), m_bufsize(bufSize), m_buf(NULL), m_read(0), m_readLeft(0) {}
24
~QemuPipeStream()25 QemuPipeStream::~QemuPipeStream() {
26 if (valid()) {
27 flush();
28 qemu_pipe_close((QEMU_PIPE_HANDLE)m_sock);
29 }
30 if (m_buf != NULL) {
31 free(m_buf);
32 }
33 }
34
connect(const char * serviceName)35 int QemuPipeStream::connect(const char* serviceName) {
36 m_sock = (int)qemu_pipe_open("opengles");
37 if (!valid()) {
38 mesa_loge("%s: failed to connect to opengles pipe", __FUNCTION__);
39 qemu_pipe_print_error(m_sock);
40 return -1;
41 }
42 return 0;
43 }
44
processPipeInit()45 uint64_t QemuPipeStream::processPipeInit() {
46 QEMU_PIPE_HANDLE processPipe = qemu_pipe_open("GLProcessPipe");
47
48 uint64_t procUID = 0;
49 if (!qemu_pipe_valid(processPipe)) {
50 processPipe = 0;
51 mesa_logi("Process pipe failed");
52 return 0;
53 }
54
55 // Send a confirmation int to the host
56 int32_t confirmInt = 100;
57 if (qemu_pipe_write_fully(processPipe, &confirmInt, sizeof(confirmInt))) { // failed
58 qemu_pipe_close(processPipe);
59 processPipe = 0;
60 mesa_logi("Process pipe failed");
61 return 0;
62 }
63
64 // Ask the host for per-process unique ID
65 if (qemu_pipe_read_fully(processPipe, &procUID, sizeof(procUID))) {
66 qemu_pipe_close(processPipe);
67 processPipe = 0;
68 procUID = 0;
69 mesa_logi("Process pipe failed");
70 return 0;
71 }
72
73 return procUID;
74 }
75
allocBuffer(size_t minSize)76 void* QemuPipeStream::allocBuffer(size_t minSize) {
77 // Add dedicated read buffer space at the front of the buffer.
78 minSize += kReadSize;
79
80 size_t allocSize = (m_bufsize < minSize ? minSize : m_bufsize);
81 if (!m_buf) {
82 m_buf = (unsigned char*)malloc(allocSize);
83 } else if (m_bufsize < allocSize) {
84 unsigned char* p = (unsigned char*)realloc(m_buf, allocSize);
85 if (p != NULL) {
86 m_buf = p;
87 m_bufsize = allocSize;
88 } else {
89 mesa_loge("realloc (%zu) failed\n", allocSize);
90 free(m_buf);
91 m_buf = NULL;
92 m_bufsize = 0;
93 }
94 }
95
96 return m_buf + kWriteOffset;
97 };
98
commitBuffer(size_t size)99 int QemuPipeStream::commitBuffer(size_t size) {
100 if (size == 0) return 0;
101 return writeFully(m_buf + kWriteOffset, size);
102 }
103
writeFully(const void * buf,size_t len)104 int QemuPipeStream::writeFully(const void* buf, size_t len) {
105 return qemu_pipe_write_fully(m_sock, buf, len);
106 }
107
readFully(void * buf,size_t len)108 const unsigned char* QemuPipeStream::readFully(void* buf, size_t len) {
109 return commitBufferAndReadFully(0, buf, len);
110 }
111
commitBufferAndReadFully(size_t writeSize,void * userReadBufPtr,size_t totalReadSize)112 const unsigned char* QemuPipeStream::commitBufferAndReadFully(size_t writeSize,
113 void* userReadBufPtr,
114 size_t totalReadSize) {
115 unsigned char* userReadBuf = static_cast<unsigned char*>(userReadBufPtr);
116
117 if (!valid()) return NULL;
118
119 if (!userReadBuf) {
120 if (totalReadSize > 0) {
121 mesa_loge(
122 "QemuPipeStream::commitBufferAndReadFully failed, userReadBuf=NULL, totalReadSize "
123 "%zu, lethal"
124 " error, exiting.",
125 totalReadSize);
126 abort();
127 }
128 if (!writeSize) {
129 return NULL;
130 }
131 }
132
133 // Advance buffered read if not yet consumed.
134 size_t remaining = totalReadSize;
135 size_t bufferedReadSize = m_readLeft < remaining ? m_readLeft : remaining;
136 if (bufferedReadSize) {
137 memcpy(userReadBuf, m_buf + (m_read - m_readLeft), bufferedReadSize);
138 remaining -= bufferedReadSize;
139 m_readLeft -= bufferedReadSize;
140 }
141
142 // Early out if nothing left to do.
143 if (!writeSize && !remaining) {
144 return userReadBuf;
145 }
146
147 writeFully(m_buf + kWriteOffset, writeSize);
148
149 // Now done writing. Early out if no reading left to do.
150 if (!remaining) {
151 return userReadBuf;
152 }
153
154 // Read up to kReadSize bytes if all buffered read has been consumed.
155 size_t maxRead = m_readLeft ? 0 : kReadSize;
156
157 ssize_t actual = 0;
158
159 if (maxRead) {
160 actual = qemu_pipe_read(m_sock, m_buf, maxRead);
161 // Updated buffered read size.
162 if (actual > 0) {
163 m_read = m_readLeft = actual;
164 }
165
166 if (actual == 0) {
167 mesa_logi("%s: end of pipe", __FUNCTION__);
168 return NULL;
169 }
170 }
171
172 // Consume buffered read and read more if necessary.
173 while (remaining) {
174 bufferedReadSize = m_readLeft < remaining ? m_readLeft : remaining;
175 if (bufferedReadSize) {
176 memcpy(userReadBuf + (totalReadSize - remaining), m_buf + (m_read - m_readLeft),
177 bufferedReadSize);
178 remaining -= bufferedReadSize;
179 m_readLeft -= bufferedReadSize;
180 continue;
181 }
182
183 actual = qemu_pipe_read(m_sock, m_buf, kReadSize);
184
185 if (actual == 0) {
186 mesa_logi("%s: Failed reading from pipe: %d", __FUNCTION__, errno);
187 return NULL;
188 }
189
190 if (actual > 0) {
191 m_read = m_readLeft = actual;
192 continue;
193 }
194
195 if (!qemu_pipe_try_again(actual)) {
196 mesa_logi("%s: Error reading from pipe: %d", __FUNCTION__, errno);
197 return NULL;
198 }
199 }
200
201 return userReadBuf;
202 }
203
read(void * buf,size_t * inout_len)204 const unsigned char* QemuPipeStream::read(void* buf, size_t* inout_len) {
205 if (!valid()) return NULL;
206 if (!buf) {
207 mesa_loge("QemuPipeStream::read failed, buf=NULL");
208 return NULL; // do not allow NULL buf in that implementation
209 }
210
211 int n = recv(buf, *inout_len);
212
213 if (n > 0) {
214 *inout_len = n;
215 return (const unsigned char*)buf;
216 }
217
218 return NULL;
219 }
220
valid()221 bool QemuPipeStream::valid() { return qemu_pipe_valid(m_sock); }
222
recv(void * buf,size_t len)223 int QemuPipeStream::recv(void* buf, size_t len) {
224 if (!valid()) return int(ERR_INVALID_SOCKET);
225 char* p = (char*)buf;
226 int ret = 0;
227 while (len > 0) {
228 int res = qemu_pipe_read(m_sock, p, len);
229 if (res > 0) {
230 p += res;
231 ret += res;
232 len -= res;
233 continue;
234 }
235 if (res == 0) { /* EOF */
236 break;
237 }
238 if (qemu_pipe_try_again(res)) {
239 continue;
240 }
241
242 /* A real error */
243 if (ret == 0) ret = -1;
244 break;
245 }
246 return ret;
247 }
248