This commit is contained in:
elwrcl committed 2026-06-06 13:25:15 +03:00
1 parent 04d4dc8b09
commit 818cab85dc
12511 files changed
+6156641

No files matched your search

@@ -0,0 +1,10 @@
/*
* Copyright 2024 Google LLC
* SPDX-License-Identifier: MIT
*/
#include "GfxStreamConnection.h"
GfxStreamConnection::GfxStreamConnection() {}
GfxStreamConnection::~GfxStreamConnection() {}
@@ -0,0 +1,17 @@
/*
* Copyright 2024 Google LLC
* SPDX-License-Identifier: MIT
*/
#ifndef GFXSTREAM_CONNECTION_H
#define GFXSTREAM_CONNECTION_H
class GfxStreamConnection {
public:
GfxStreamConnection();
virtual ~GfxStreamConnection();
virtual void* getEncoder() = 0;
};
#endif
@@ -0,0 +1,193 @@
/*
* Copyright 2024 Google LLC
* SPDX-License-Identifier: MIT
*/
#include "GfxStreamConnectionManager.h"
#include <cerrno>
#include "GoldfishAddressSpaceStream.h"
#include "QemuPipeStream.h"
#include "VirtGpu.h"
#include "VirtioGpuAddressSpaceStream.h"
#include "VirtioGpuPipeStream.h"
#include "c11/threads.h"
#include "util/log.h"
#define STREAM_BUFFER_SIZE (4 * 1024 * 1024)
static tss_t gfxstream_connection_manager_tls_key;
static bool gfxstream_connection_manager_tls_key_valid;
static void gfxstream_connection_manager_tls_free(void* tls) {
if (tls) {
delete ((GfxStreamConnectionManager*)tls);
}
}
static void gfxstream_connection_manager_tls_key_create_once(void) {
gfxstream_connection_manager_tls_key_valid =
tss_create(&gfxstream_connection_manager_tls_key, gfxstream_connection_manager_tls_free) ==
thrd_success;
if (!gfxstream_connection_manager_tls_key_valid) {
mesa_logd("WARNING: failed to create gfxstream_connection_manager_tls_key");
}
}
GfxStreamConnectionManager* GfxStreamConnectionManager::getThreadLocalInstance(
GfxStreamTransportType type, VirtGpuCapset capset) {
static once_flag once = ONCE_FLAG_INIT;
call_once(&once, gfxstream_connection_manager_tls_key_create_once);
if (unlikely(!gfxstream_connection_manager_tls_key_valid)) {
return nullptr;
}
GfxStreamConnectionManager* tls =
(GfxStreamConnectionManager*)tss_get(gfxstream_connection_manager_tls_key);
if (likely(tls)) {
return tls;
}
tls = new GfxStreamConnectionManager(type, capset);
if (!tls) {
return nullptr;
}
if (!tls->initialize()) {
delete tls;
return nullptr;
}
if (tss_set(gfxstream_connection_manager_tls_key, tls) != thrd_success) {
delete tls;
return nullptr;
}
return tls;
}
void GfxStreamConnectionManager::resetThreadLocalInstance() {
if (unlikely(!gfxstream_connection_manager_tls_key_valid)) {
return;
}
GfxStreamConnectionManager* tls =
(GfxStreamConnectionManager*)tss_get(gfxstream_connection_manager_tls_key);
if (unlikely(!tls)) {
return;
}
delete tls;
void* null_ptr = nullptr;
tss_set(gfxstream_connection_manager_tls_key, null_ptr);
}
GfxStreamConnectionManager::GfxStreamConnectionManager(GfxStreamTransportType type,
VirtGpuCapset capset)
: mTransportType(type), mCapset(capset) {}
GfxStreamConnectionManager::~GfxStreamConnectionManager() {}
bool GfxStreamConnectionManager::initialize() {
switch (mTransportType) {
#ifdef GFXSTREAM_ENABLE_GUEST_GOLDFISH
case GFXSTREAM_TRANSPORT_ADDRESS_SPACE: {
mStream = createGoldfishAddressSpaceStream(STREAM_BUFFER_SIZE);
if (!mStream) {
mesa_loge("Failed to create AddressSpaceStream for host connection\n");
return false;
}
break;
}
#endif
case GFXSTREAM_TRANSPORT_QEMU_PIPE: {
mStream = new QemuPipeStream(STREAM_BUFFER_SIZE);
if (mStream->connect() < 0) {
mesa_loge("Failed to connect to host (QemuPipeStream)\n");
return false;
}
break;
}
case GFXSTREAM_TRANSPORT_VIRTIO_GPU_PIPE: {
VirtioGpuPipeStream* pipeStream =
new VirtioGpuPipeStream(STREAM_BUFFER_SIZE, INVALID_DESCRIPTOR);
if (!pipeStream) {
mesa_loge("Failed to create VirtioGpu for host connection\n");
return false;
}
if (pipeStream->connect() < 0) {
mesa_loge("Failed to connect to host (VirtioGpu)\n");
return false;
}
mDescriptor = pipeStream->getRendernodeFd();
VirtGpuDevice::getInstance(mCapset);
mStream = pipeStream;
break;
}
case GFXSTREAM_TRANSPORT_VIRTIO_GPU_ADDRESS_SPACE: {
// Use kCapsetGfxStreamVulkan for now, Ranchu HWC needs to be modified to pass in
// right capset.
auto device = VirtGpuDevice::getInstance(kCapsetGfxStreamVulkan);
if (!device) {
mesa_logd("Failed to get VirtGpuDevice\n");
return false;
}
mDescriptor = device->getDeviceHandle();
mStream = createVirtioGpuAddressSpaceStream(kCapsetGfxStreamVulkan);
if (!mStream) {
mesa_loge("Failed to create virtgpu AddressSpaceStream\n");
return false;
}
break;
}
default:
return false;
}
// send zero 'clientFlags' to the host. This is actually part of the gfxstream protocol.
unsigned int* pClientFlags = (unsigned int*)mStream->allocBuffer(sizeof(unsigned int));
*pClientFlags = 0;
mStream->commitBuffer(sizeof(unsigned int));
return true;
}
int32_t GfxStreamConnectionManager::addConnection(GfxStreamConnectionType type,
std::unique_ptr<GfxStreamConnection> connection) {
if (mConnections.find(type) != mConnections.end()) {
return -EINVAL;
}
mConnections[type] = std::move(connection);
return 0;
}
void* GfxStreamConnectionManager::getEncoder(GfxStreamConnectionType type) {
auto iterator = mConnections.find(type);
if (iterator == mConnections.end()) {
return nullptr;
}
return iterator->second->getEncoder();
}
gfxstream::guest::IOStream* GfxStreamConnectionManager::getStream() { return mStream; }
gfxstream::guest::IOStream* GfxStreamConnectionManager::processPipeStream(
GfxStreamTransportType transportType) {
switch (transportType) {
case GFXSTREAM_TRANSPORT_ADDRESS_SPACE:
case GFXSTREAM_TRANSPORT_QEMU_PIPE:
return new QemuPipeStream(STREAM_BUFFER_SIZE);
case GFXSTREAM_TRANSPORT_VIRTIO_GPU_ADDRESS_SPACE:
case GFXSTREAM_TRANSPORT_VIRTIO_GPU_PIPE:
return new VirtioGpuPipeStream(STREAM_BUFFER_SIZE, mDescriptor);
default:
return nullptr;
}
}
@@ -0,0 +1,56 @@
/*
* Copyright 2024 Google LLC
* SPDX-License-Identifier: MIT
*/
#ifndef GFXSTREAM_CONNECTION_MANAGER_H
#define GFXSTREAM_CONNECTION_MANAGER_H
#include <memory>
#include <unordered_map>
#include "GfxStreamConnection.h"
#include "VirtGpu.h"
#include "gfxstream/guest/IOStream.h"
enum GfxStreamConnectionType {
GFXSTREAM_CONNECTION_GLES = 1,
GFXSTREAM_CONNECTION_GLES2 = 2,
GFXSTREAM_CONNECTION_RENDER_CONTROL = 3,
GFXSTREAM_CONNECTION_VULKAN = 4,
};
enum GfxStreamTransportType {
GFXSTREAM_TRANSPORT_QEMU_PIPE = 1,
GFXSTREAM_TRANSPORT_ADDRESS_SPACE = 2,
GFXSTREAM_TRANSPORT_VIRTIO_GPU_PIPE = 3,
GFXSTREAM_TRANSPORT_VIRTIO_GPU_ADDRESS_SPACE = 4,
};
class GfxStreamConnectionManager {
public:
static GfxStreamConnectionManager* getThreadLocalInstance(GfxStreamTransportType type,
VirtGpuCapset capset);
static void resetThreadLocalInstance();
GfxStreamConnectionManager(GfxStreamTransportType type, VirtGpuCapset capset);
~GfxStreamConnectionManager();
bool initialize();
int32_t addConnection(GfxStreamConnectionType type,
std::unique_ptr<GfxStreamConnection> connection);
void* getEncoder(GfxStreamConnectionType type);
gfxstream::guest::IOStream* getStream();
gfxstream::guest::IOStream* processPipeStream(GfxStreamTransportType transportType);
private:
// intrusively refcounted
gfxstream::guest::IOStream* mStream = nullptr;
int32_t mDescriptor = -1;
GfxStreamTransportType mTransportType;
VirtGpuCapset mCapset;
std::unordered_map<GfxStreamConnectionType, std::unique_ptr<GfxStreamConnection>> mConnections;
};
#endif
@@ -0,0 +1,247 @@
/*
* Copyright 2011 Google LLC
* SPDX-License-Identifier: MIT
*/
#include "QemuPipeStream.h"
#include <errno.h>
#include <qemu_pipe_bp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "util/log.h"
static const size_t kReadSize = 512 * 1024;
static const size_t kWriteOffset = kReadSize;
QemuPipeStream::QemuPipeStream(size_t bufSize)
: IOStream(bufSize), m_sock(-1), m_bufsize(bufSize), m_buf(NULL), m_read(0), m_readLeft(0) {}
QemuPipeStream::QemuPipeStream(QEMU_PIPE_HANDLE sock, size_t bufSize)
: IOStream(bufSize), m_sock(sock), m_bufsize(bufSize), m_buf(NULL), m_read(0), m_readLeft(0) {}
QemuPipeStream::~QemuPipeStream() {
if (valid()) {
flush();
qemu_pipe_close((QEMU_PIPE_HANDLE)m_sock);
}
if (m_buf != NULL) {
free(m_buf);
}
}
int QemuPipeStream::connect(const char* serviceName) {
m_sock = (int)qemu_pipe_open("opengles");
if (!valid()) {
mesa_loge("%s: failed to connect to opengles pipe", __FUNCTION__);
qemu_pipe_print_error(m_sock);
return -1;
}
return 0;
}
uint64_t QemuPipeStream::processPipeInit() {
QEMU_PIPE_HANDLE processPipe = qemu_pipe_open("GLProcessPipe");
uint64_t procUID = 0;
if (!qemu_pipe_valid(processPipe)) {
processPipe = 0;
mesa_logi("Process pipe failed");
return 0;
}
// Send a confirmation int to the host
int32_t confirmInt = 100;
if (qemu_pipe_write_fully(processPipe, &confirmInt, sizeof(confirmInt))) { // failed
qemu_pipe_close(processPipe);
processPipe = 0;
mesa_logi("Process pipe failed");
return 0;
}
// Ask the host for per-process unique ID
if (qemu_pipe_read_fully(processPipe, &procUID, sizeof(procUID))) {
qemu_pipe_close(processPipe);
processPipe = 0;
procUID = 0;
mesa_logi("Process pipe failed");
return 0;
}
return procUID;
}
void* QemuPipeStream::allocBuffer(size_t minSize) {
// Add dedicated read buffer space at the front of the buffer.
minSize += kReadSize;
size_t allocSize = (m_bufsize < minSize ? minSize : m_bufsize);
if (!m_buf) {
m_buf = (unsigned char*)malloc(allocSize);
} else if (m_bufsize < allocSize) {
unsigned char* p = (unsigned char*)realloc(m_buf, allocSize);
if (p != NULL) {
m_buf = p;
m_bufsize = allocSize;
} else {
mesa_loge("realloc (%zu) failed\n", allocSize);
free(m_buf);
m_buf = NULL;
m_bufsize = 0;
}
}
return m_buf + kWriteOffset;
};
int QemuPipeStream::commitBuffer(size_t size) {
if (size == 0) return 0;
return writeFully(m_buf + kWriteOffset, size);
}
int QemuPipeStream::writeFully(const void* buf, size_t len) {
return qemu_pipe_write_fully(m_sock, buf, len);
}
const unsigned char* QemuPipeStream::readFully(void* buf, size_t len) {
return commitBufferAndReadFully(0, buf, len);
}
const unsigned char* QemuPipeStream::commitBufferAndReadFully(size_t writeSize,
void* userReadBufPtr,
size_t totalReadSize) {
unsigned char* userReadBuf = static_cast<unsigned char*>(userReadBufPtr);
if (!valid()) return NULL;
if (!userReadBuf) {
if (totalReadSize > 0) {
mesa_loge(
"QemuPipeStream::commitBufferAndReadFully failed, userReadBuf=NULL, totalReadSize "
"%zu, lethal"
" error, exiting.",
totalReadSize);
abort();
}
if (!writeSize) {
return NULL;
}
}
// Advance buffered read if not yet consumed.
size_t remaining = totalReadSize;
size_t bufferedReadSize = m_readLeft < remaining ? m_readLeft : remaining;
if (bufferedReadSize) {
memcpy(userReadBuf, m_buf + (m_read - m_readLeft), bufferedReadSize);
remaining -= bufferedReadSize;
m_readLeft -= bufferedReadSize;
}
// Early out if nothing left to do.
if (!writeSize && !remaining) {
return userReadBuf;
}
writeFully(m_buf + kWriteOffset, writeSize);
// Now done writing. Early out if no reading left to do.
if (!remaining) {
return userReadBuf;
}
// Read up to kReadSize bytes if all buffered read has been consumed.
size_t maxRead = m_readLeft ? 0 : kReadSize;
ssize_t actual = 0;
if (maxRead) {
actual = qemu_pipe_read(m_sock, m_buf, maxRead);
// Updated buffered read size.
if (actual > 0) {
m_read = m_readLeft = actual;
}
if (actual == 0) {
mesa_logi("%s: end of pipe", __FUNCTION__);
return NULL;
}
}
// Consume buffered read and read more if necessary.
while (remaining) {
bufferedReadSize = m_readLeft < remaining ? m_readLeft : remaining;
if (bufferedReadSize) {
memcpy(userReadBuf + (totalReadSize - remaining), m_buf + (m_read - m_readLeft),
bufferedReadSize);
remaining -= bufferedReadSize;
m_readLeft -= bufferedReadSize;
continue;
}
actual = qemu_pipe_read(m_sock, m_buf, kReadSize);
if (actual == 0) {
mesa_logi("%s: Failed reading from pipe: %d", __FUNCTION__, errno);
return NULL;
}
if (actual > 0) {
m_read = m_readLeft = actual;
continue;
}
if (!qemu_pipe_try_again(actual)) {
mesa_logi("%s: Error reading from pipe: %d", __FUNCTION__, errno);
return NULL;
}
}
return userReadBuf;
}
const unsigned char* QemuPipeStream::read(void* buf, size_t* inout_len) {
if (!valid()) return NULL;
if (!buf) {
mesa_loge("QemuPipeStream::read failed, buf=NULL");
return NULL; // do not allow NULL buf in that implementation
}
int n = recv(buf, *inout_len);
if (n > 0) {
*inout_len = n;
return (const unsigned char*)buf;
}
return NULL;
}
bool QemuPipeStream::valid() { return qemu_pipe_valid(m_sock); }
int QemuPipeStream::recv(void* buf, size_t len) {
if (!valid()) return int(ERR_INVALID_SOCKET);
char* p = (char*)buf;
int ret = 0;
while (len > 0) {
int res = qemu_pipe_read(m_sock, p, len);
if (res > 0) {
p += res;
ret += res;
len -= res;
continue;
}
if (res == 0) { /* EOF */
break;
}
if (qemu_pipe_try_again(res)) {
continue;
}
/* A real error */
if (ret == 0) ret = -1;
break;
}
return ret;
}
@@ -0,0 +1,48 @@
/*
* Copyright 2011 Google LLC
* SPDX-License-Identifier: MIT
*/
#ifndef __QEMU_PIPE_STREAM_H
#define __QEMU_PIPE_STREAM_H
/* This file implements an IOStream that uses a QEMU fast-pipe
* to communicate with the emulator's 'opengles' service. See
* <hardware/qemu_pipe.h> for more details.
*/
#include <stdlib.h>
#include <memory>
#include "gfxstream/guest/IOStream.h"
class QemuPipeStream : public gfxstream::guest::IOStream {
public:
typedef enum { ERR_INVALID_SOCKET = -1000 } QemuPipeStreamError;
explicit QemuPipeStream(size_t bufsize = 10000);
~QemuPipeStream();
virtual int connect(const char* serviceName = nullptr);
virtual uint64_t processPipeInit();
virtual void* allocBuffer(size_t minSize);
virtual int commitBuffer(size_t size);
virtual const unsigned char* readFully(void* buf, size_t len);
virtual const unsigned char* commitBufferAndReadFully(size_t size, void* buf, size_t len);
virtual const unsigned char* read(void* buf, size_t* inout_len);
bool valid();
int recv(void* buf, size_t len);
virtual int writeFully(const void* buf, size_t len);
private:
int m_sock;
size_t m_bufsize;
unsigned char* m_buf;
size_t m_read;
size_t m_readLeft;
QemuPipeStream(int sock, size_t bufSize);
};
#endif
@@ -0,0 +1,62 @@
/*
* Copyright 2018 Google LLC
* SPDX-License-Identifier: MIT
*/
#include "QemuPipeStream.h"
QemuPipeStream::QemuPipeStream(size_t bufSize) : IOStream(bufSize) { (void)bufSize; }
QemuPipeStream::~QemuPipeStream() {}
int QemuPipeStream::connect(const char* serviceName) {
(void)serviceName;
return 0;
}
uint64_t QemuPipeStream::processPipeInit() { return 0; }
void* QemuPipeStream::allocBuffer(size_t minSize) {
(void)minSize;
return nullptr;
};
int QemuPipeStream::commitBuffer(size_t size) {
(void)size;
return 0;
}
int QemuPipeStream::writeFully(const void* buf, size_t len) {
(void)buf;
(void)len;
return 0;
}
const unsigned char* QemuPipeStream::readFully(void* buf, size_t len) {
(void)buf;
(void)len;
return nullptr;
}
const unsigned char* QemuPipeStream::commitBufferAndReadFully(size_t writeSize,
void* userReadBufPtr,
size_t totalReadSize) {
(void)writeSize;
(void)userReadBufPtr;
(void)totalReadSize;
return nullptr;
}
const unsigned char* QemuPipeStream::read(void* buf, size_t* inout_len) {
(void)buf;
(void)inout_len;
return nullptr;
}
bool QemuPipeStream::valid() { return false; }
int QemuPipeStream::recv(void* buf, size_t len) {
(void)buf;
(void)len;
return 0;
}
@@ -0,0 +1,335 @@
/*
* Copyright 2018 Google LLC
* SPDX-License-Identifier: MIT
*/
#include "VirtioGpuPipeStream.h"
#include <errno.h>
#include "util/detect_os.h"
#if DETECT_OS_LINUX
#include <sys/mman.h>
#include <unistd.h>
#endif
#include <sys/types.h>
#include <cstring>
#include <string>
#include "VirtGpu.h"
#include "util/log.h"
#include "virtio/virtio-gpu/virgl_hw.h"
static const size_t kTransferBufferSize = (1048576);
static const size_t kReadSize = 512 * 1024;
static const size_t kWriteOffset = kReadSize;
VirtioGpuPipeStream::VirtioGpuPipeStream(size_t bufSize, int32_t descriptor)
: IOStream(bufSize),
m_fd(descriptor),
m_virtio_mapped(nullptr),
m_bufsize(bufSize),
m_buf(nullptr),
m_writtenPos(0) {}
VirtioGpuPipeStream::~VirtioGpuPipeStream() { free(m_buf); }
bool VirtioGpuPipeStream::valid() { return m_device != nullptr; }
int VirtioGpuPipeStream::getRendernodeFd() {
if (m_device == nullptr) {
return -1;
}
return m_device->getDeviceHandle();
}
int VirtioGpuPipeStream::connect(const char* serviceName) {
if (!m_device) {
m_device.reset(createPlatformVirtGpuDevice(kCapsetNone, m_fd));
if (!m_device) {
mesa_loge("Failed to create VirtioGpuPipeStream VirtGpuDevice.");
return -1;
}
m_resource = m_device->createResource(/*width=*/kTransferBufferSize,
/*height=*/1,
/*stride=*/kTransferBufferSize,
/*size=*/kTransferBufferSize, VIRGL_FORMAT_R8_UNORM,
PIPE_BUFFER, VIRGL_BIND_CUSTOM);
if (!m_resource) {
mesa_loge("Failed to create VirtioGpuPipeStream resource.");
return -1;
}
m_resourceMapping = m_resource->createMapping();
if (!m_resourceMapping) {
mesa_loge("Failed to create VirtioGpuPipeStream resource mapping.");
return -1;
}
m_virtio_mapped = m_resourceMapping->asRawPtr();
if (!m_virtio_mapped) {
mesa_loge("Failed to create VirtioGpuPipeStream resource mapping ptr.");
return -1;
}
}
wait();
if (serviceName) {
writeFully(serviceName, strlen(serviceName) + 1);
} else {
static const char kPipeString[] = "pipe:opengles";
std::string pipeStr(kPipeString);
writeFully(kPipeString, sizeof(kPipeString));
}
return 0;
}
uint64_t VirtioGpuPipeStream::processPipeInit() {
connect("pipe:GLProcessPipe");
int32_t confirmInt = 100;
writeFully(&confirmInt, sizeof(confirmInt));
uint64_t res;
readFully(&res, sizeof(res));
return res;
}
void* VirtioGpuPipeStream::allocBuffer(size_t minSize) {
size_t allocSize = (m_bufsize < minSize ? minSize : m_bufsize);
if (!m_buf) {
m_buf = (unsigned char*)malloc(allocSize);
} else if (m_bufsize < allocSize) {
unsigned char* p = (unsigned char*)realloc(m_buf, allocSize);
if (p != NULL) {
m_buf = p;
m_bufsize = allocSize;
} else {
mesa_loge("realloc (%zu) failed\n", allocSize);
free(m_buf);
m_buf = NULL;
m_bufsize = 0;
}
}
return m_buf;
}
int VirtioGpuPipeStream::commitBuffer(size_t size) {
if (size == 0) return 0;
return writeFully(m_buf, size);
}
int VirtioGpuPipeStream::writeFully(const void* buf, size_t len) {
// DBG(">> VirtioGpuPipeStream::writeFully %d\n", len);
if (!valid()) return -1;
if (!buf) {
if (len > 0) {
// If len is non-zero, buf must not be NULL. Otherwise the pipe would be
// in a corrupted state, which is lethal for the emulator.
mesa_loge(
"VirtioGpuPipeStream::writeFully failed, buf=NULL, len %zu,"
" lethal error, exiting",
len);
abort();
}
return 0;
}
size_t res = len;
int retval = 0;
while (res > 0) {
ssize_t stat = transferToHost((const char*)(buf) + (len - res), res);
if (stat > 0) {
res -= stat;
continue;
}
if (stat == 0) { /* EOF */
mesa_loge("VirtioGpuPipeStream::writeFully failed: premature EOF\n");
retval = -1;
break;
}
if (errno == EAGAIN) {
continue;
}
retval = stat;
mesa_loge("VirtioGpuPipeStream::writeFully failed: %s, lethal error, exiting.\n",
strerror(errno));
abort();
}
// DBG("<< VirtioGpuPipeStream::writeFully %d\n", len );
return retval;
}
const unsigned char* VirtioGpuPipeStream::readFully(void* buf, size_t len) {
flush();
if (!valid()) return NULL;
if (!buf) {
if (len > 0) {
// If len is non-zero, buf must not be NULL. Otherwise the pipe would be
// in a corrupted state, which is lethal for the emulator.
mesa_loge(
"VirtioGpuPipeStream::readFully failed, buf=NULL, len %zu, lethal"
" error, exiting.",
len);
abort();
}
}
size_t res = len;
while (res > 0) {
ssize_t stat = transferFromHost((char*)(buf) + len - res, res);
if (stat == 0) {
// client shutdown;
return NULL;
} else if (stat < 0) {
if (errno == EAGAIN) {
continue;
} else {
mesa_loge(
"VirtioGpuPipeStream::readFully failed (buf %p, len %zu"
", res %zu): %s, lethal error, exiting.",
buf, len, res, strerror(errno));
abort();
}
} else {
res -= stat;
}
}
// DBG("<< VirtioGpuPipeStream::readFully %d\n", len);
return (const unsigned char*)buf;
}
const unsigned char* VirtioGpuPipeStream::commitBufferAndReadFully(size_t writeSize,
void* userReadBufPtr,
size_t totalReadSize) {
return commitBuffer(writeSize) ? nullptr : readFully(userReadBufPtr, totalReadSize);
}
const unsigned char* VirtioGpuPipeStream::read(void* buf, size_t* inout_len) {
// DBG(">> VirtioGpuPipeStream::read %d\n", *inout_len);
if (!valid()) return NULL;
if (!buf) {
mesa_loge("VirtioGpuPipeStream::read failed, buf=NULL");
return NULL; // do not allow NULL buf in that implementation
}
int n = recv(buf, *inout_len);
if (n > 0) {
*inout_len = n;
return (const unsigned char*)buf;
}
// DBG("<< VirtioGpuPipeStream::read %d\n", *inout_len);
return NULL;
}
int VirtioGpuPipeStream::recv(void* buf, size_t len) {
if (!valid()) return -EINVAL;
char* p = (char*)buf;
int ret = 0;
while (len > 0) {
int res = transferFromHost(p, len);
if (res > 0) {
p += res;
ret += res;
len -= res;
continue;
}
if (res == 0) { /* EOF */
break;
}
if (errno != EAGAIN) {
continue;
}
/* A real error */
if (ret == 0) ret = -1;
break;
}
return ret;
}
void VirtioGpuPipeStream::wait() {
int ret = m_resource->wait();
if (ret) {
mesa_loge("VirtioGpuPipeStream: DRM_IOCTL_VIRTGPU_WAIT failed with %d (%s)\n", errno,
strerror(errno));
}
m_writtenPos = 0;
}
ssize_t VirtioGpuPipeStream::transferToHost(const void* buffer, size_t len) {
size_t todo = len;
size_t done = 0;
int ret = EAGAIN;
unsigned char* virtioPtr = m_virtio_mapped;
const unsigned char* readPtr = reinterpret_cast<const unsigned char*>(buffer);
while (done < len) {
size_t toXfer = todo > kTransferBufferSize ? kTransferBufferSize : todo;
if (toXfer > (kTransferBufferSize - m_writtenPos)) {
wait();
}
memcpy(virtioPtr + m_writtenPos, readPtr, toXfer);
ret = m_resource->transferToHost(m_writtenPos, toXfer);
if (ret) {
mesa_loge("VirtioGpuPipeStream: failed to transferToHost() with errno %d (%s)\n", errno,
strerror(errno));
return (ssize_t)ret;
}
done += toXfer;
readPtr += toXfer;
todo -= toXfer;
m_writtenPos += toXfer;
}
return len;
}
ssize_t VirtioGpuPipeStream::transferFromHost(void* buffer, size_t len) {
size_t todo = len;
size_t done = 0;
int ret = EAGAIN;
const unsigned char* virtioPtr = m_virtio_mapped;
unsigned char* readPtr = reinterpret_cast<unsigned char*>(buffer);
if (m_writtenPos) {
wait();
}
while (done < len) {
size_t toXfer = todo > kTransferBufferSize ? kTransferBufferSize : todo;
ret = m_resource->transferFromHost(0, toXfer);
if (ret) {
mesa_loge("VirtioGpuPipeStream: failed to transferFromHost() with errno %d (%s)\n",
errno, strerror(errno));
return (ssize_t)ret;
}
wait();
memcpy(readPtr, virtioPtr, toXfer);
done += toXfer;
readPtr += toXfer;
todo -= toXfer;
}
return len;
}
@@ -0,0 +1,57 @@
/*
* Copyright 2018 Google LLC
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <stdlib.h>
#include "VirtGpu.h"
#include "gfxstream/guest/IOStream.h"
/* This file implements an IOStream that uses VIRTGPU TRANSFER* ioctls on a
* virtio-gpu DRM rendernode device to communicate with a goldfish-pipe
* service on the host side.
*/
class VirtioGpuPipeStream : public gfxstream::guest::IOStream {
public:
explicit VirtioGpuPipeStream(size_t bufsize, int32_t descriptor);
~VirtioGpuPipeStream();
virtual int connect(const char* serviceName = nullptr);
virtual uint64_t processPipeInit();
virtual void* allocBuffer(size_t minSize);
virtual int commitBuffer(size_t size);
virtual const unsigned char* readFully(void* buf, size_t len);
virtual const unsigned char* commitBufferAndReadFully(size_t size, void* buf, size_t len);
virtual const unsigned char* read(void* buf, size_t* inout_len);
bool valid();
int getRendernodeFd();
int recv(void* buf, size_t len);
virtual int writeFully(const void* buf, size_t len);
private:
// sync. Also resets the write position.
void wait();
// transfer to/from host ops
ssize_t transferToHost(const void* buffer, size_t len);
ssize_t transferFromHost(void* buffer, size_t len);
int32_t m_fd = -1;
std::unique_ptr<VirtGpuDevice> m_device;
VirtGpuResourcePtr m_resource;
VirtGpuResourceMappingPtr m_resourceMapping;
unsigned char* m_virtio_mapped; // user mapping of bo
// intermediate buffer
size_t m_bufsize;
unsigned char* m_buf;
size_t m_writtenPos;
};
@@ -0,0 +1,32 @@
# Copyright 2024 Android Open Source Project
# SPDX-License-Identifier: MIT
inc_connection_manager = include_directories('.')
files_libconnection_manager = files(
'GfxStreamConnection.cpp',
'GfxStreamConnectionManager.cpp',
'VirtioGpuPipeStream.cpp',
)
if with_platform_android
files_libconnection_manager += files('QemuPipeStream.cpp')
else
files_libconnection_manager += files('QemuPipeStreamStub.cpp')
endif
libconnection_manager = static_library(
'connection_manager',
files_libconnection_manager,
cpp_args: gfxstream_guest_args,
include_directories: [
inc_connection_manager,
inc_guest_iostream,
inc_goldfish_address_space,
inc_platform_virtgpu,
inc_src,
inc_aemu,
],
link_with: [lib_goldfish_address_space, libplatform_virtgpu],
dependencies: [dep_libdrm, dep_android_aemu_qemupipe, idep_mesautil],
)