You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
830 lines
27 KiB
830 lines
27 KiB
// Copyright 2020 The Android Open Source Project
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#include "AndroidPipe.h"
|
|
#include "android_pipe_base.h"
|
|
|
|
#include "base/Optional.h"
|
|
#include "base/StringFormat.h"
|
|
#include "base/MemStream.h"
|
|
#include "base/Lock.h"
|
|
#include "android_pipe_device.h"
|
|
#include "android_pipe_host.h"
|
|
#include "DeviceContextRunner.h"
|
|
#include "VmLock.h"
|
|
|
|
#include <algorithm>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <unordered_set>
|
|
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
|
|
#define DEBUG 0
|
|
|
|
#if DEBUG >= 1
|
|
#include <stdio.h>
|
|
#define D(...) fprintf(stderr, __VA_ARGS__), fprintf(stderr, "\n")
|
|
#else
|
|
#define D(...) (void)0
|
|
#endif
|
|
|
|
#if DEBUG >= 2
|
|
#define DD(...) fprintf(stderr, __VA_ARGS__), fprintf(stderr, "\n")
|
|
#else
|
|
#define DD(...) (void)0
|
|
#endif
|
|
|
|
#define E(...) fprintf(stderr, "ERROR:" __VA_ARGS__), fprintf(stderr, "\n")
|
|
|
|
static const AndroidPipeHwFuncs* getPipeHwFuncs(const void* hwPipe) {
|
|
return *static_cast<const AndroidPipeHwFuncs* const *>(hwPipe);
|
|
}
|
|
|
|
using namespace android::base;
|
|
|
|
using AndroidPipe = android::AndroidPipe;
|
|
using BaseStream = android::base::Stream;
|
|
using CStream = ::Stream;
|
|
using OptionalString = android::base::Optional<std::string>;
|
|
using Service = android::AndroidPipe::Service;
|
|
using ServiceList = std::vector<std::unique_ptr<Service>>;
|
|
using VmLock = android::VmLock;
|
|
using android::base::MemStream;
|
|
using android::base::StringFormat;
|
|
|
|
static BaseStream* asBaseStream(CStream* stream) {
|
|
return reinterpret_cast<BaseStream*>(stream);
|
|
}
|
|
|
|
#define CHECK_VM_STATE_LOCK() (void)0
|
|
|
|
namespace android {
|
|
|
|
namespace {
|
|
|
|
static bool isPipeOptional(const std::string& name) {
|
|
return name == "OffworldPipe";
|
|
}
|
|
|
|
// Write an optional string |str| to |stream|. |str| can be null. Use
|
|
// readOptionalString() to read it back later.
|
|
static void writeOptionalString(BaseStream* stream, const char* str) {
|
|
if (str) {
|
|
stream->putByte(1);
|
|
stream->putString(str);
|
|
} else {
|
|
stream->putByte(0);
|
|
}
|
|
}
|
|
|
|
// Read an optional string from |stream|. If the result is not constructed
|
|
// (i.e. equals to false), this means the original |str| parameter was null.
|
|
static OptionalString readOptionalString(BaseStream* stream) {
|
|
if (stream->getByte()) {
|
|
return OptionalString(stream->getString());
|
|
}
|
|
return OptionalString();
|
|
}
|
|
|
|
// forward
|
|
Service* findServiceByName(const char* name);
|
|
|
|
// Implementation of a special AndroidPipe class used to model the state
|
|
// of a pipe connection before the service name has been written to the
|
|
// file descriptor by the guest. The most important method is onGuestSend()
|
|
// which will detect when this happens.
|
|
class ConnectorPipe : public AndroidPipe {
|
|
public:
|
|
ConnectorPipe(void* hwPipe, Service* service)
|
|
: AndroidPipe(hwPipe, service) {
|
|
DD("%s: Creating new ConnectorPipe hwpipe=%p", __FUNCTION__, mHwPipe);
|
|
}
|
|
|
|
virtual void onGuestClose(PipeCloseReason reason) override {
|
|
// nothing to do here
|
|
DD("%s: closing ConnectorPipe hwpipe=%p prematurily (reason=%d)!",
|
|
__func__, mHwPipe, (int)reason);
|
|
}
|
|
|
|
virtual unsigned onGuestPoll() const override {
|
|
// A connector always want to receive data.
|
|
DD("%s: polling hwpipe=%p", __FUNCTION__, mHwPipe);
|
|
return PIPE_POLL_OUT;
|
|
}
|
|
|
|
virtual int onGuestRecv(AndroidPipeBuffer* buffers,
|
|
int numBuffers) override {
|
|
// This pipe never wants to write to the guest, so getting there
|
|
// is an error since PIPE_WAKE_IN is never signaled.
|
|
DD("%s: trying to receive data from hwpipe=%p", __FUNCTION__, mHwPipe);
|
|
return PIPE_ERROR_IO;
|
|
}
|
|
|
|
// TECHNICAL NOTE: This function reads data from the guest until it
|
|
// finds a zero-terminated C-string. After that it parses it to find
|
|
// a registered service corresponding to one of the allowed formats
|
|
// (see below). In case of success, this creates a new AndroidPipe
|
|
// instance and calls AndroidPipeHwFuncs::resetPipe() to associate it with
|
|
// the current hardware-side |mHwPipe|, then *deletes* the current
|
|
// instance! In case of error (e.g. invalid service name, or error during
|
|
// initialization), PIPE_ERROR_INVAL will be returned, otherwise, the
|
|
// number of bytes accepted from the guest is returned.
|
|
virtual int onGuestSend(const AndroidPipeBuffer* buffers,
|
|
int numBuffers,
|
|
void** newPipePtr) override {
|
|
int result = 0;
|
|
size_t avail = kBufferSize - mPos;
|
|
bool foundZero = false;
|
|
for (; !foundZero && avail > 0 && numBuffers > 0;
|
|
buffers++, numBuffers--) {
|
|
const uint8_t* data = buffers[0].data;
|
|
size_t count = std::min(avail, buffers[0].size);
|
|
// Read up to |count| bytes, stopping after the first zero.
|
|
size_t n = 0;
|
|
while (n < count) {
|
|
uint8_t byte = data[n++];
|
|
mBuffer[mPos++] = (char) byte;
|
|
if (!byte) {
|
|
foundZero = true;
|
|
break;
|
|
}
|
|
}
|
|
result += static_cast<int>(n);
|
|
avail -= n;
|
|
}
|
|
DD("%s: receiving %d connection bytes from hwpipe=%p", __FUNCTION__,
|
|
result, mHwPipe);
|
|
|
|
if (!foundZero) {
|
|
if (avail == 0) {
|
|
DD("%s: service name buffer full, force-closing connection",
|
|
__FUNCTION__);
|
|
return PIPE_ERROR_IO;
|
|
}
|
|
// Still waiting for terminating zero.
|
|
DD("%s: still waiting for terminating zero!", __FUNCTION__);
|
|
return result;
|
|
}
|
|
|
|
// Acceptable formats for the connection string are:
|
|
//
|
|
// pipe:<name>
|
|
// pipe:<name>:<arguments>
|
|
//
|
|
char* pipeName;
|
|
char* pipeArgs;
|
|
|
|
D("%s: connector: '%s'", __FUNCTION__, mBuffer);
|
|
if (memcmp(mBuffer, "pipe:", 5) != 0) {
|
|
// Nope, we don't handle these for now.
|
|
D("%s: Unknown pipe connection: '%s'", __FUNCTION__, mBuffer);
|
|
return PIPE_ERROR_INVAL;
|
|
}
|
|
|
|
pipeName = mBuffer + 5;
|
|
pipeArgs = strchr(pipeName, ':');
|
|
|
|
Service* svc = nullptr;
|
|
|
|
// As a special case, if the service name is as:
|
|
// qemud:<name>
|
|
// qemud:<name>:args
|
|
//
|
|
// First look for a registered pipe service named "qemud:<name>"
|
|
// and if not found, fallback to "qemud" only.
|
|
//
|
|
// This is useful to support qemud services that are now served
|
|
// by a dedicated (and faster) pipe service, e.g. 'qemud:adb'
|
|
// as currently implemented by QEMU2 (and soon by QEMU1).
|
|
static const char kQemudPrefix[] = "qemud:";
|
|
const size_t kQemudPrefixSize = sizeof(kQemudPrefix) - 1U;
|
|
|
|
if (!::strncmp(pipeName, kQemudPrefix, kQemudPrefixSize)) {
|
|
assert(pipeArgs == pipeName + kQemudPrefixSize - 1);
|
|
char* pipeArgs2 = strchr(pipeArgs + 1, ':');
|
|
if (pipeArgs2) {
|
|
*pipeArgs2 = '\0';
|
|
}
|
|
svc = findServiceByName(pipeName);
|
|
if (svc) {
|
|
pipeArgs = pipeArgs2;
|
|
} else if (pipeArgs2) {
|
|
// Restore colon.
|
|
*pipeArgs2 = ':';
|
|
}
|
|
}
|
|
if (pipeArgs) {
|
|
*pipeArgs++ = '\0';
|
|
if (!pipeArgs) {
|
|
pipeArgs = NULL;
|
|
}
|
|
}
|
|
|
|
if (!svc) {
|
|
svc = findServiceByName(pipeName);
|
|
}
|
|
|
|
if (!svc) {
|
|
D("%s: Unknown server with name %s!", __FUNCTION__, pipeName);
|
|
return PIPE_ERROR_INVAL;
|
|
}
|
|
|
|
AndroidPipe* newPipe = svc->create(mHwPipe, pipeArgs);
|
|
if (!newPipe) {
|
|
D("%s: Initialization failed for %s pipe!", __FUNCTION__, pipeName);
|
|
return PIPE_ERROR_INVAL;
|
|
}
|
|
|
|
// Swap your host-side pipe instance with this one weird trick!
|
|
D("%s: starting new pipe %p (swapping %p) for service %s",
|
|
__FUNCTION__,
|
|
newPipe,
|
|
mHwPipe,
|
|
pipeName);
|
|
|
|
newPipe->setFlags(mFlags);
|
|
*newPipePtr = newPipe;
|
|
delete this;
|
|
|
|
return result;
|
|
}
|
|
|
|
virtual void onGuestWantWakeOn(int wakeFlags) override {
|
|
// nothing to do here
|
|
DD("%s: signaling wakeFlags=%d for hwpipe=%p", __FUNCTION__, wakeFlags,
|
|
mHwPipe);
|
|
}
|
|
|
|
virtual void onSave(BaseStream* stream) override {
|
|
DD("%s: saving connector state for hwpipe=%p", __FUNCTION__, mHwPipe);
|
|
stream->putBe32(mPos);
|
|
stream->write(mBuffer, mPos);
|
|
}
|
|
|
|
bool onLoad(BaseStream* stream) {
|
|
DD("%s: loading connector state for hwpipe=%p", __FUNCTION__, mHwPipe);
|
|
int32_t len = stream->getBe32();
|
|
if (len < 0 || len > kBufferSize) {
|
|
D("%s: invalid length %d (expected 0 <= len <= %d)", __FUNCTION__,
|
|
static_cast<int>(len), kBufferSize);
|
|
return false;
|
|
}
|
|
mPos = (int)len;
|
|
int ret = (int)stream->read(mBuffer, mPos);
|
|
DD("%s: read %d bytes (%d expected)", __FUNCTION__, ret, mPos);
|
|
return (ret == mPos);
|
|
}
|
|
|
|
private:
|
|
static constexpr int kBufferSize = 128;
|
|
char mBuffer[kBufferSize];
|
|
int mPos = 0;
|
|
};
|
|
|
|
// Associated AndroidPipe::Service class for ConnectorPipe instances.
|
|
class ConnectorService : public Service {
|
|
public:
|
|
ConnectorService() : Service("<connector>") {}
|
|
|
|
virtual AndroidPipe* create(void* hwPipe, const char* args) override {
|
|
return new ConnectorPipe(hwPipe, this);
|
|
}
|
|
|
|
virtual bool canLoad() const override { return true; }
|
|
|
|
virtual AndroidPipe* load(void* hwPipe,
|
|
const char* args,
|
|
BaseStream* stream) override {
|
|
ConnectorPipe* pipe = new ConnectorPipe(hwPipe, this);
|
|
if (!pipe->onLoad(stream)) {
|
|
delete pipe;
|
|
return nullptr;
|
|
}
|
|
return pipe;
|
|
}
|
|
};
|
|
|
|
// A helper class used to send signalWake() and closeFromHost() commands to
|
|
// the device thread, depending on the threading mode setup by the emulation
|
|
// engine.
|
|
struct PipeWakeCommand {
|
|
void* hwPipe;
|
|
int wakeFlags;
|
|
};
|
|
|
|
class PipeWaker final : public DeviceContextRunner<PipeWakeCommand> {
|
|
public:
|
|
void signalWake(void* hwPipe, int wakeFlags) {
|
|
queueDeviceOperation({ hwPipe, wakeFlags });
|
|
}
|
|
void closeFromHost(void* hwPipe) {
|
|
signalWake(hwPipe, PIPE_WAKE_CLOSED);
|
|
}
|
|
void abortPending(void* hwPipe) {
|
|
removeAllPendingOperations([hwPipe](const PipeWakeCommand& cmd) {
|
|
return cmd.hwPipe == hwPipe;
|
|
});
|
|
}
|
|
void abortAllPending() {
|
|
removeAllPendingOperations([](const PipeWakeCommand& cmd) {
|
|
return true;
|
|
});
|
|
}
|
|
|
|
int getPendingFlags(void* hwPipe) const {
|
|
int flags = 0;
|
|
forEachPendingOperation([hwPipe, &flags](const PipeWakeCommand& cmd) {
|
|
if (cmd.hwPipe == hwPipe) {
|
|
flags |= cmd.wakeFlags;
|
|
}
|
|
});
|
|
return flags;
|
|
}
|
|
|
|
private:
|
|
virtual void performDeviceOperation(const PipeWakeCommand& wake_cmd) {
|
|
void* hwPipe = wake_cmd.hwPipe;
|
|
int flags = wake_cmd.wakeFlags;
|
|
|
|
// Not used when in virtio mode.
|
|
if (flags & PIPE_WAKE_CLOSED) {
|
|
getPipeHwFuncs(hwPipe)->closeFromHost(hwPipe);
|
|
} else {
|
|
getPipeHwFuncs(hwPipe)->signalWake(hwPipe, flags);
|
|
}
|
|
}
|
|
};
|
|
|
|
struct Globals {
|
|
ServiceList services;
|
|
ConnectorService connectorService;
|
|
PipeWaker pipeWaker;
|
|
|
|
// Searches for a service position in the |services| list and returns the
|
|
// index. |startPosHint| is a _hint_ and suggests where to start from.
|
|
// Returns the index of the service or -1 if there's no |name| service.
|
|
int findServicePositionByName(const char* name,
|
|
const int startPosHint = 0) const {
|
|
const auto searchByNameFunc =
|
|
[name](const std::unique_ptr<Service>& service) {
|
|
return service->name() == name;
|
|
};
|
|
|
|
// First, try to search starting from the hint position.
|
|
auto end = services.end();
|
|
auto it = std::find_if(services.begin() + startPosHint, end,
|
|
searchByNameFunc);
|
|
|
|
// If there was a hint that didn't help, continue searching from the
|
|
// beginning of the contatiner to check the rest of it.
|
|
if (it == end && startPosHint > 0) {
|
|
end = services.begin() + startPosHint;
|
|
it = std::find_if(services.begin(), end, searchByNameFunc);
|
|
}
|
|
|
|
return it == end ? -1 : it - services.begin();
|
|
}
|
|
|
|
Service* loadServiceByName(BaseStream* stream) {
|
|
OptionalString serviceName = readOptionalString(stream);
|
|
if (!serviceName) {
|
|
DD("%s: no name (assuming connector state)", __FUNCTION__);
|
|
return &connectorService;
|
|
}
|
|
DD("%s: found [%s]", __FUNCTION__, serviceName->c_str());
|
|
return findServiceByName(serviceName->c_str());
|
|
}
|
|
};
|
|
|
|
static Globals* sGlobals() { static Globals* g = new Globals; return g; }
|
|
|
|
Service* findServiceByName(const char* name) {
|
|
const int pos = sGlobals()->findServicePositionByName(name);
|
|
return pos < 0 ? nullptr : sGlobals()->services[pos].get();
|
|
}
|
|
|
|
AndroidPipe* loadPipeFromStreamCommon(BaseStream* stream,
|
|
void* hwPipe,
|
|
Service* service,
|
|
char* pForceClose) {
|
|
*pForceClose = 0;
|
|
|
|
OptionalString args = readOptionalString(stream);
|
|
AndroidPipe* pipe = nullptr;
|
|
if (service->canLoad()) {
|
|
DD("%s: loading state for [%s] hwpipe=%p", __FUNCTION__,
|
|
service->name().c_str(), hwPipe);
|
|
pipe = service->load(hwPipe, args ? args->c_str() : nullptr, stream);
|
|
if (!pipe) {
|
|
*pForceClose = 1;
|
|
}
|
|
} else {
|
|
DD("%s: force-closing hwpipe=%p", __FUNCTION__, hwPipe);
|
|
*pForceClose = 1;
|
|
}
|
|
|
|
const int pendingFlags = stream->getBe32();
|
|
if (pendingFlags && pipe && !*pForceClose) {
|
|
if (!hwPipe) {
|
|
fprintf(stderr, "fatal: AndroidPipe::%s [%s]: hwPipe is NULL (flags = 0x%x)\n",
|
|
__func__, pipe->name(), unsigned(pendingFlags));
|
|
abort();
|
|
}
|
|
sGlobals()->pipeWaker.signalWake(hwPipe, pendingFlags);
|
|
DD("%s: singalled wake flags %d for pipe hwpipe=%p", __func__,
|
|
pendingFlags, hwPipe);
|
|
}
|
|
|
|
return pipe;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
// static
|
|
void AndroidPipe::initThreading(VmLock* vmLock) {
|
|
// TODO: Make this work in qemu with the actual goldfish pipe device.
|
|
// In virtio land, this won't be needed, so include trivial timer interface.
|
|
sGlobals()->pipeWaker.init(vmLock, {
|
|
// installFunc
|
|
[](DeviceContextRunner<PipeWakeCommand>* dcr, std::function<void()> installedFunc) {
|
|
(void)dcr;
|
|
(void)installedFunc;
|
|
},
|
|
// uninstallFunc
|
|
[](DeviceContextRunner<PipeWakeCommand>* dcr) {
|
|
(void)dcr;
|
|
},
|
|
// startWithTimeoutFunc
|
|
[](DeviceContextRunner<PipeWakeCommand>* dcr, uint64_t timeout) {
|
|
(void)dcr;
|
|
(void)timeout;
|
|
}
|
|
});
|
|
}
|
|
|
|
AndroidPipe::~AndroidPipe() {
|
|
DD("%s: for hwpipe=%p (host %p '%s')", __FUNCTION__, mHwPipe, this,
|
|
mService->name().c_str());
|
|
}
|
|
|
|
// static
|
|
void AndroidPipe::Service::add(std::unique_ptr<Service> service) {
|
|
DD("Adding new pipe service '%s' this=%p", service->name().c_str(),
|
|
service.get());
|
|
sGlobals()->services.push_back(std::move(service));
|
|
}
|
|
|
|
// static
|
|
void AndroidPipe::Service::resetAll() {
|
|
DD("Resetting all pipe services");
|
|
sGlobals()->services.clear();
|
|
}
|
|
|
|
void AndroidPipe::signalWake(int wakeFlags) {
|
|
// i.e., pipe not using normal pipe device
|
|
if (mFlags) return;
|
|
if (!mHwPipe) {
|
|
fprintf(stderr, "AndroidPipe::%s [%s]: hwPipe is NULL (flags = 0x%x)\n",
|
|
__func__, name(), (unsigned)wakeFlags);
|
|
abort();
|
|
}
|
|
sGlobals()->pipeWaker.signalWake(mHwPipe, wakeFlags);
|
|
}
|
|
|
|
void AndroidPipe::closeFromHost() {
|
|
// i.e., pipe not using normal pipe device
|
|
if (mFlags) return;
|
|
if (!mHwPipe) {
|
|
fprintf(stderr, "AndroidPipe::%s [%s]: hwPipe is NULL\n", __func__, name());
|
|
abort();
|
|
}
|
|
sGlobals()->pipeWaker.closeFromHost(mHwPipe);
|
|
}
|
|
|
|
void AndroidPipe::abortPendingOperation() {
|
|
// i.e., pipe not using normal pipe device
|
|
if (mFlags) return;
|
|
|
|
if (!mHwPipe) {
|
|
fprintf(stderr, "AndroidPipe::%s [%s]: hwPipe is NULL\n", __func__, name());
|
|
abort();
|
|
}
|
|
sGlobals()->pipeWaker.abortPending(mHwPipe);
|
|
}
|
|
|
|
void AndroidPipe::saveToStream(BaseStream* stream) {
|
|
// First, write service name.
|
|
if (mService == &sGlobals()->connectorService) {
|
|
// A connector pipe
|
|
stream->putByte(0);
|
|
} else {
|
|
// A regular service pipe.
|
|
stream->putByte(1);
|
|
stream->putString(mService->name());
|
|
}
|
|
|
|
MemStream pipeStream;
|
|
writeOptionalString(&pipeStream, mArgs.c_str());
|
|
|
|
// Save pipe-specific state now.
|
|
if (mService->canLoad()) {
|
|
mService->savePipe(this, &pipeStream);
|
|
}
|
|
|
|
// Save the pending wake or close operations as well.
|
|
const int pendingFlags = sGlobals()->pipeWaker.getPendingFlags(mHwPipe);
|
|
pipeStream.putBe32(pendingFlags);
|
|
|
|
pipeStream.save(stream);
|
|
}
|
|
|
|
// static
|
|
AndroidPipe* AndroidPipe::loadFromStream(BaseStream* stream,
|
|
void* hwPipe,
|
|
char* pForceClose) {
|
|
Service* service = sGlobals()->loadServiceByName(stream);
|
|
// Always load the pipeStream, it allows us to safely skip loading streams.
|
|
MemStream pipeStream;
|
|
pipeStream.load(stream);
|
|
|
|
if (!service) {
|
|
return nullptr;
|
|
}
|
|
return loadPipeFromStreamCommon(&pipeStream, hwPipe, service, pForceClose);
|
|
}
|
|
|
|
// static
|
|
AndroidPipe* AndroidPipe::loadFromStreamLegacy(BaseStream* stream,
|
|
void* hwPipe,
|
|
uint64_t* pChannel,
|
|
unsigned char* pWakes,
|
|
unsigned char* pClosed,
|
|
char* pForceClose) {
|
|
Service* service = sGlobals()->loadServiceByName(stream);
|
|
// Always load the pipeStream, it allows us to safely skip loading streams.
|
|
MemStream pipeStream;
|
|
pipeStream.load(stream);
|
|
|
|
if (!service) {
|
|
return nullptr;
|
|
}
|
|
*pChannel = pipeStream.getBe64();
|
|
*pWakes = pipeStream.getByte();
|
|
*pClosed = pipeStream.getByte();
|
|
|
|
return loadPipeFromStreamCommon(&pipeStream, hwPipe, service, pForceClose);
|
|
}
|
|
|
|
} // namespace android
|
|
|
|
// API for the virtual device.
|
|
|
|
void android_pipe_reset_services() {
|
|
AndroidPipe::Service::resetAll();
|
|
}
|
|
|
|
void* android_pipe_guest_open(void* hwpipe) {
|
|
CHECK_VM_STATE_LOCK();
|
|
DD("%s: Creating new connector pipe for hwpipe=%p", __FUNCTION__, hwpipe);
|
|
return android::sGlobals()->connectorService.create(hwpipe, nullptr);
|
|
}
|
|
|
|
void* android_pipe_guest_open_with_flags(void* hwpipe, uint32_t flags) {
|
|
CHECK_VM_STATE_LOCK();
|
|
DD("%s: Creating new connector pipe for hwpipe=%p", __FUNCTION__, hwpipe);
|
|
auto pipe = android::sGlobals()->connectorService.create(hwpipe, nullptr);
|
|
pipe->setFlags((AndroidPipeFlags)flags);
|
|
return pipe;
|
|
}
|
|
|
|
void android_pipe_guest_close(void* internalPipe, PipeCloseReason reason) {
|
|
CHECK_VM_STATE_LOCK();
|
|
auto pipe = static_cast<android::AndroidPipe*>(internalPipe);
|
|
if (pipe) {
|
|
D("%s: host=%p [%s] reason=%d", __FUNCTION__, pipe, pipe->name(),
|
|
(int)reason);
|
|
pipe->abortPendingOperation();
|
|
pipe->onGuestClose(reason);
|
|
}
|
|
}
|
|
|
|
template <class Func>
|
|
static void forEachServiceToStream(CStream* stream, Func&& func) {
|
|
BaseStream* const bs = asBaseStream(stream);
|
|
bs->putBe16(android::sGlobals()->services.size());
|
|
for (const auto& service : android::sGlobals()->services) {
|
|
bs->putString(service->name());
|
|
|
|
// Write to the pipeStream first so that we know the length and can
|
|
// enable skipping loading specific pipes on load, see isPipeOptional.
|
|
MemStream pipeStream;
|
|
func(service.get(), &pipeStream);
|
|
pipeStream.save(bs);
|
|
}
|
|
}
|
|
|
|
template <class Func>
|
|
static void forEachServiceFromStream(CStream* stream, Func&& func) {
|
|
const auto& services = android::sGlobals()->services;
|
|
BaseStream* const bs = asBaseStream(stream);
|
|
const int count = bs->getBe16();
|
|
int servicePos = -1;
|
|
std::unordered_set<Service*> missingServices;
|
|
for (const auto& service : services) {
|
|
missingServices.insert(service.get());
|
|
}
|
|
for (int i = 0; i < count; ++i) {
|
|
const auto name = bs->getString();
|
|
servicePos = android::sGlobals()->findServicePositionByName(
|
|
name.c_str(), servicePos + 1);
|
|
|
|
// Always load the pipeStream, so that if the pipe is missing it does
|
|
// not corrupt the next pipe.
|
|
MemStream pipeStream;
|
|
pipeStream.load(bs);
|
|
|
|
if (servicePos >= 0) {
|
|
const auto& service = services[servicePos];
|
|
func(service.get(), &pipeStream);
|
|
missingServices.erase(service.get());
|
|
} else if (android::isPipeOptional(name)) {
|
|
D("%s: Skipping optional pipe %s\n", __FUNCTION__, name.c_str());
|
|
} else {
|
|
assert(false && "Service for snapshot pipe does not exist");
|
|
E("%s: Could not load pipe %s, service does not exist\n",
|
|
__FUNCTION__, name.c_str());
|
|
}
|
|
}
|
|
|
|
// Now call the same function for all services that weren't in the snapshot.
|
|
// Pass |nullptr| instead of the stream pointer to make sure they know
|
|
// that while we're loading from a snapshot these services aren't part
|
|
// of it.
|
|
for (const auto service : missingServices) {
|
|
func(service, nullptr);
|
|
}
|
|
}
|
|
|
|
void android_pipe_guest_pre_load(CStream* stream) {
|
|
CHECK_VM_STATE_LOCK();
|
|
// We may not call qemu_set_irq() until the snapshot is loaded.
|
|
android::sGlobals()->pipeWaker.abortAllPending();
|
|
android::sGlobals()->pipeWaker.setContextRunMode(
|
|
android::ContextRunMode::DeferAlways);
|
|
forEachServiceFromStream(stream, [](Service* service, BaseStream* bs) {
|
|
if (service->canLoad()) {
|
|
service->preLoad(bs);
|
|
}
|
|
});
|
|
}
|
|
|
|
void android_pipe_guest_post_load(CStream* stream) {
|
|
CHECK_VM_STATE_LOCK();
|
|
forEachServiceFromStream(stream, [](Service* service, BaseStream* bs) {
|
|
if (service->canLoad()) {
|
|
service->postLoad(bs);
|
|
}
|
|
});
|
|
// Restore the regular handling of pipe interrupt requests.
|
|
android::sGlobals()->pipeWaker.setContextRunMode(
|
|
android::ContextRunMode::DeferIfNotLocked);
|
|
}
|
|
|
|
void android_pipe_guest_pre_save(CStream* stream) {
|
|
CHECK_VM_STATE_LOCK();
|
|
forEachServiceToStream(stream, [](Service* service, BaseStream* bs) {
|
|
if (service->canLoad()) {
|
|
service->preSave(bs);
|
|
}
|
|
});
|
|
}
|
|
|
|
void android_pipe_guest_post_save(CStream* stream) {
|
|
CHECK_VM_STATE_LOCK();
|
|
forEachServiceToStream(stream, [](Service* service, BaseStream* bs) {
|
|
if (service->canLoad()) {
|
|
service->postSave(bs);
|
|
}
|
|
});
|
|
}
|
|
|
|
void android_pipe_guest_save(void* internalPipe, CStream* stream) {
|
|
CHECK_VM_STATE_LOCK();
|
|
auto pipe = static_cast<android::AndroidPipe*>(internalPipe);
|
|
DD("%s: host=%p [%s]", __FUNCTION__, pipe, pipe->name());
|
|
pipe->saveToStream(asBaseStream(stream));
|
|
}
|
|
|
|
void* android_pipe_guest_load(CStream* stream,
|
|
void* hwPipe,
|
|
char* pForceClose) {
|
|
CHECK_VM_STATE_LOCK();
|
|
DD("%s: hwpipe=%p", __FUNCTION__, hwPipe);
|
|
return AndroidPipe::loadFromStream(asBaseStream(stream), hwPipe,
|
|
pForceClose);
|
|
}
|
|
|
|
void* android_pipe_guest_load_legacy(CStream* stream,
|
|
void* hwPipe,
|
|
uint64_t* pChannel,
|
|
unsigned char* pWakes,
|
|
unsigned char* pClosed,
|
|
char* pForceClose) {
|
|
CHECK_VM_STATE_LOCK();
|
|
DD("%s: hwpipe=%p", __FUNCTION__, hwPipe);
|
|
return android::AndroidPipe::loadFromStreamLegacy(asBaseStream(stream),
|
|
hwPipe, pChannel, pWakes,
|
|
pClosed, pForceClose);
|
|
}
|
|
|
|
unsigned android_pipe_guest_poll(void* internalPipe) {
|
|
CHECK_VM_STATE_LOCK();
|
|
auto pipe = static_cast<AndroidPipe*>(internalPipe);
|
|
DD("%s: host=%p [%s]", __FUNCTION__, pipe, pipe->name());
|
|
return pipe->onGuestPoll();
|
|
}
|
|
|
|
int android_pipe_guest_recv(void* internalPipe,
|
|
AndroidPipeBuffer* buffers,
|
|
int numBuffers) {
|
|
CHECK_VM_STATE_LOCK();
|
|
auto pipe = static_cast<AndroidPipe*>(internalPipe);
|
|
// Note that pipe may be deleted during this call, so it's not safe to
|
|
// access pipe after this point.
|
|
return pipe->onGuestRecv(buffers, numBuffers);
|
|
}
|
|
|
|
int android_pipe_guest_send(void** internalPipe,
|
|
const AndroidPipeBuffer* buffers,
|
|
int numBuffers) {
|
|
CHECK_VM_STATE_LOCK();
|
|
auto pipe = static_cast<AndroidPipe*>(*internalPipe);
|
|
// Note that pipe may be deleted during this call, so it's not safe to
|
|
// access pipe after this point.
|
|
return pipe->onGuestSend(buffers, numBuffers, internalPipe);
|
|
}
|
|
|
|
void android_pipe_guest_wake_on(void* internalPipe, unsigned wakes) {
|
|
CHECK_VM_STATE_LOCK();
|
|
auto pipe = static_cast<AndroidPipe*>(internalPipe);
|
|
pipe->onGuestWantWakeOn(wakes);
|
|
}
|
|
|
|
// API implemented by the virtual device.
|
|
void android_pipe_host_close(void* hwpipe) {
|
|
auto pipe = static_cast<android::AndroidPipe*>(hwpipe);
|
|
D("%s: host=%p [%s]", __FUNCTION__, pipe, pipe->name());
|
|
android::sGlobals()->pipeWaker.closeFromHost(pipe);
|
|
}
|
|
|
|
void android_pipe_host_signal_wake(void* hwpipe, unsigned flags) {
|
|
android::sGlobals()->pipeWaker.signalWake(hwpipe, flags);
|
|
}
|
|
|
|
// Not used when in virtio mode.
|
|
int android_pipe_get_id(void* hwpipe) {
|
|
return getPipeHwFuncs(hwpipe)->getPipeId(hwpipe);
|
|
}
|
|
|
|
static std::vector<std::pair<void*(*)(int), const char*>> lookup_by_id_callbacks;
|
|
|
|
void android_pipe_append_lookup_by_id_callback(void*(*cb)(int), const char* tag) {
|
|
lookup_by_id_callbacks.push_back({cb, tag});
|
|
}
|
|
|
|
void* android_pipe_lookup_by_id(const int id) {
|
|
void* hwPipeFound = nullptr;
|
|
const char* tagFound = "(null)";
|
|
|
|
for (const auto &cb : lookup_by_id_callbacks) {
|
|
void* hwPipe = (*cb.first)(id);
|
|
if (hwPipe) {
|
|
if (hwPipeFound) {
|
|
fprintf(stderr, "%s: Pipe id (%d) is not unique, at least two "
|
|
"pipes are found: `%s` and `%s`\n",
|
|
__func__, id, tagFound, cb.second);
|
|
abort();
|
|
} else {
|
|
hwPipeFound = hwPipe;
|
|
tagFound = cb.second;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hwPipeFound;
|
|
}
|