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.
478 lines
13 KiB
478 lines
13 KiB
// Copyright (C) 2018 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 "host-common/HostGoldfishPipe.h"
|
|
|
|
#include "base/Lookup.h"
|
|
#include "base/Result.h"
|
|
#include "android/crashreport/crash-handler.h"
|
|
#include "host-common/android_pipe_device.h"
|
|
#include "host-common/AndroidPipe.h"
|
|
#include "host-common/testing/TestVmLock.h"
|
|
#include "host-common/VmLock.h"
|
|
|
|
#include <algorithm>
|
|
#include <unordered_map>
|
|
#include <unordered_set>
|
|
#include <vector>
|
|
|
|
#include <errno.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
|
|
#ifdef _MSC_VER
|
|
#ifdef ERROR
|
|
#undef ERROR
|
|
#endif
|
|
#endif
|
|
|
|
#define HOST_PIPE_DEBUG 0
|
|
|
|
#if HOST_PIPE_DEBUG
|
|
|
|
#define HOST_PIPE_DLOG(fmt,...) fprintf(stderr, "%s:%d " fmt "\n", __func__, __LINE__, ##__VA_ARGS__);
|
|
|
|
#else
|
|
|
|
#define HOST_PIPE_DLOG(fmt,...)
|
|
|
|
#endif
|
|
|
|
using android::base::Result;
|
|
using android::base::Ok;
|
|
using android::base::Err;
|
|
|
|
namespace android {
|
|
|
|
const AndroidPipeHwFuncs HostGoldfishPipeDevice::HostHwPipe::vtbl = {
|
|
&closeFromHostCallback,
|
|
&signalWakeCallback,
|
|
&getPipeIdCallback,
|
|
};
|
|
|
|
HostGoldfishPipeDevice::HostHwPipe::HostHwPipe(int fd) : vtblPtr(&vtbl), mFd(fd) {}
|
|
|
|
HostGoldfishPipeDevice::HostHwPipe::~HostHwPipe() {}
|
|
|
|
std::unique_ptr<HostGoldfishPipeDevice::HostHwPipe>
|
|
HostGoldfishPipeDevice::HostHwPipe::create(int fd) {
|
|
return std::make_unique<HostHwPipe>(fd);
|
|
}
|
|
|
|
HostGoldfishPipeDevice::HostGoldfishPipeDevice() {}
|
|
|
|
HostGoldfishPipeDevice::~HostGoldfishPipeDevice() {
|
|
clearLocked(); // no lock required in dctor
|
|
}
|
|
|
|
int HostGoldfishPipeDevice::getErrno() const {
|
|
ScopedVmLock lock;
|
|
return mErrno;
|
|
}
|
|
|
|
// Also opens.
|
|
int HostGoldfishPipeDevice::connect(const char* name) {
|
|
const auto handshake = std::string("pipe:") + name;
|
|
|
|
ScopedVmLock lock;
|
|
const int hwPipeFd = ++mFdGenerator;
|
|
std::unique_ptr<HostHwPipe> hwPipe = HostHwPipe::create(hwPipeFd);
|
|
|
|
InternalPipe* hostPipe = static_cast<InternalPipe*>(
|
|
android_pipe_guest_open(hwPipe.get()));
|
|
if (!hostPipe) {
|
|
mErrno = ENOENT;
|
|
return kNoFd;
|
|
}
|
|
|
|
const ssize_t len = static_cast<ssize_t>(handshake.size()) + 1;
|
|
const ssize_t ret = writeInternal(&hostPipe, handshake.c_str(), len);
|
|
|
|
if (ret == len) {
|
|
HostHwPipe* hwPipeWeak = associatePipes(std::move(hwPipe), hostPipe);
|
|
if (hwPipeWeak) {
|
|
HOST_PIPE_DLOG("New pipe: service: for '%s', hwpipe: %p hostpipe: %p",
|
|
name, hwPipeWeak, hostPipe);
|
|
|
|
return hwPipeWeak->getFd();
|
|
} else {
|
|
mErrno = ENOENT;
|
|
return kNoFd;
|
|
}
|
|
} else {
|
|
LOG(ERROR) << "Could not connect to goldfish pipe name: " << name;
|
|
android_pipe_guest_close(hostPipe, PIPE_CLOSE_GRACEFUL);
|
|
mErrno = EIO;
|
|
return kNoFd;
|
|
}
|
|
}
|
|
|
|
void HostGoldfishPipeDevice::close(const int fd) {
|
|
HOST_PIPE_DLOG("Close fd=%d", fd);
|
|
|
|
ScopedVmLock lock;
|
|
if (!eraseFdInfo(fd)) {
|
|
LOG(INFO) << "Could not close pipe, ENOENT.";
|
|
mErrno = ENOENT;
|
|
}
|
|
}
|
|
|
|
// Read/write/poll but for a particular pipe.
|
|
ssize_t HostGoldfishPipeDevice::read(const int fd, void* buffer, size_t len) {
|
|
ScopedVmLock lock;
|
|
|
|
FdInfo* fdInfo = lookupFdInfo(fd);
|
|
if (!fdInfo) {
|
|
LOG(ERROR) << "fd=" << fd << " is not a valid pipe fd";
|
|
mErrno = EINVAL;
|
|
return PIPE_ERROR_INVAL;
|
|
}
|
|
|
|
AndroidPipeBuffer buf = { static_cast<uint8_t*>(buffer), len };
|
|
ssize_t res = android_pipe_guest_recv(fdInfo->hostPipe, &buf, 1);
|
|
setErrno(res);
|
|
return res;
|
|
}
|
|
|
|
HostGoldfishPipeDevice::ReadResult HostGoldfishPipeDevice::read(int fd, size_t maxLength) {
|
|
std::vector<uint8_t> resultBuffer(maxLength);
|
|
ssize_t read_size = read(fd, resultBuffer.data(), maxLength);
|
|
|
|
if (read_size < 0) {
|
|
return Err(mErrno);
|
|
} else {
|
|
if (read_size < maxLength) {
|
|
resultBuffer.resize(read_size);
|
|
}
|
|
return Ok(resultBuffer);
|
|
}
|
|
}
|
|
|
|
ssize_t HostGoldfishPipeDevice::write(const int fd, const void* buffer, size_t len) {
|
|
ScopedVmLock lock;
|
|
|
|
FdInfo* fdInfo = lookupFdInfo(fd);
|
|
if (!fdInfo) {
|
|
LOG(ERROR) << "fd=" << fd << " is not a valid pipe fd";
|
|
mErrno = EINVAL;
|
|
return PIPE_ERROR_INVAL;
|
|
}
|
|
|
|
return writeInternal(&fdInfo->hostPipe, buffer, len);
|
|
}
|
|
|
|
HostGoldfishPipeDevice::WriteResult
|
|
HostGoldfishPipeDevice::write(const int fd, const std::vector<uint8_t>& data) {
|
|
ssize_t res = write(fd, data.data(), data.size());
|
|
|
|
if (res < 0) {
|
|
return Err(mErrno);
|
|
} else {
|
|
return Ok(res);
|
|
}
|
|
}
|
|
|
|
unsigned HostGoldfishPipeDevice::poll(const int fd) const {
|
|
ScopedVmLock lock;
|
|
|
|
const FdInfo* fdInfo = lookupFdInfo(fd);
|
|
if (!fdInfo) {
|
|
LOG(ERROR) << "fd=" << fd << " is not a valid pipe fd";
|
|
return 0;
|
|
}
|
|
|
|
return android_pipe_guest_poll(fdInfo->hostPipe);
|
|
}
|
|
|
|
void HostGoldfishPipeDevice::setWakeCallback(const int fd,
|
|
std::function<void(int)> callback) {
|
|
ScopedVmLock lock;
|
|
|
|
FdInfo* fdInfo = lookupFdInfo(fd);
|
|
if (fdInfo) {
|
|
fdInfo->wakeCallback = std::move(callback);
|
|
}
|
|
}
|
|
|
|
void* HostGoldfishPipeDevice::getHostPipe(const int fd) const {
|
|
ScopedVmLock lock;
|
|
|
|
const FdInfo* fdInfo = lookupFdInfo(fd);
|
|
return fdInfo ? fdInfo->hostPipe : nullptr;
|
|
}
|
|
|
|
void HostGoldfishPipeDevice::saveSnapshot(base::Stream* stream) {
|
|
HOST_PIPE_DLOG("Saving snapshot");
|
|
|
|
auto cStream = reinterpret_cast<::Stream*>(stream);
|
|
|
|
ScopedVmLock lock;
|
|
|
|
android_pipe_guest_pre_save(cStream);
|
|
stream_put_be32(cStream, mFdInfo.size());
|
|
for (const auto& kv : mFdInfo) {
|
|
HOST_PIPE_DLOG("save pipe: fd=%d hwPipe=%p hostPipe=%p",
|
|
kv.first, kv.second.hwPipe.get(), kv.second.hostPipe);
|
|
stream_put_be32(cStream, kv.first);
|
|
android_pipe_guest_save(kv.second.hostPipe, cStream);
|
|
}
|
|
android_pipe_guest_post_save(cStream);
|
|
}
|
|
|
|
void HostGoldfishPipeDevice::loadSnapshot(base::Stream* stream) {
|
|
auto cStream = reinterpret_cast<::Stream*>(stream);
|
|
|
|
ScopedVmLock lock;
|
|
clearLocked();
|
|
|
|
android_pipe_guest_pre_load(cStream);
|
|
const uint32_t pipeCount = stream_get_be32(cStream);
|
|
|
|
for (uint32_t i = 0; i < pipeCount; ++i) {
|
|
const int fd = stream_get_be32(cStream);
|
|
mFdGenerator = std::max(mFdGenerator, fd);
|
|
|
|
std::unique_ptr<HostHwPipe> hwPipe = HostHwPipe::create(fd);
|
|
|
|
HOST_PIPE_DLOG("attempt to load a host pipe");
|
|
|
|
char forceClose = 0;
|
|
InternalPipe* hostPipe = static_cast<InternalPipe*>(
|
|
android_pipe_guest_load(cStream, hwPipe.get(), &forceClose));
|
|
|
|
if (!forceClose) {
|
|
HostHwPipe* hwPipePtr = associatePipes(std::move(hwPipe), hostPipe);
|
|
HOST_PIPE_DLOG("Successfully loaded host pipe %p for hw pipe %p",
|
|
hostPipe, hwPipePtr);
|
|
} else {
|
|
HOST_PIPE_DLOG("Failed to load host pipe for hw pipe %p", hwpipe);
|
|
LOG(ERROR) << "Could not load goldfish pipe";
|
|
mErrno = EIO;
|
|
return;
|
|
}
|
|
}
|
|
|
|
android_pipe_guest_post_load(cStream);
|
|
}
|
|
|
|
void HostGoldfishPipeDevice::saveSnapshot(base::Stream* stream, const int fd) {
|
|
HOST_PIPE_DLOG("Saving snapshot for fd=%d", fd);
|
|
|
|
ScopedVmLock lock;
|
|
FdInfo* fdInfo = lookupFdInfo(fd);
|
|
if (!fdInfo) {
|
|
crashhandler_die_format("%s:%d fd=%d is a valid pipe fd",
|
|
__func__, __LINE__, fd);
|
|
}
|
|
|
|
auto cStream = reinterpret_cast<::Stream*>(stream);
|
|
|
|
android_pipe_guest_pre_save(cStream);
|
|
|
|
stream_put_be32(cStream, 1);
|
|
stream_put_be32(cStream, fd);
|
|
android_pipe_guest_save(fdInfo->hostPipe, cStream);
|
|
android_pipe_guest_post_save(cStream);
|
|
}
|
|
|
|
int HostGoldfishPipeDevice::loadSnapshotSinglePipe(base::Stream* stream) {
|
|
HOST_PIPE_DLOG("Loading snapshot for a single pipe");
|
|
|
|
auto cStream = reinterpret_cast<::Stream*>(stream);
|
|
|
|
ScopedVmLock lock;
|
|
|
|
android_pipe_guest_pre_load(cStream);
|
|
uint32_t pipeCount = stream_get_be32(cStream);
|
|
|
|
if (pipeCount != 1) {
|
|
LOG(ERROR) << "Invalid pipe count from stream";
|
|
mErrno = EIO;
|
|
return kNoFd;
|
|
}
|
|
|
|
const int fd = stream_get_be32(cStream);
|
|
eraseFdInfo(fd);
|
|
|
|
HOST_PIPE_DLOG("attempt to load a host pipe");
|
|
|
|
std::unique_ptr<HostHwPipe> hwPipe = HostHwPipe::create(fd);
|
|
|
|
char forceClose = 0;
|
|
InternalPipe* hostPipe = static_cast<InternalPipe*>(
|
|
android_pipe_guest_load(cStream, hwPipe.get(), &forceClose));
|
|
if (!forceClose) {
|
|
associatePipes(std::move(hwPipe), hostPipe);
|
|
HOST_PIPE_DLOG("Successfully loaded host pipe %p for fd=%d", hostPipe, fd);
|
|
} else {
|
|
HOST_PIPE_DLOG("Failed to load host pipe for hw pipe %p", hwPipeFromStream);
|
|
LOG(ERROR) << "Could not load goldfish pipe";
|
|
mErrno = EIO;
|
|
}
|
|
|
|
android_pipe_guest_post_load(cStream);
|
|
|
|
return fd;
|
|
}
|
|
|
|
void HostGoldfishPipeDevice::clear() {
|
|
ScopedVmLock lock;
|
|
clearLocked();
|
|
}
|
|
|
|
void HostGoldfishPipeDevice::initialize() {
|
|
if (mInitialized) return;
|
|
AndroidPipe::Service::resetAll();
|
|
AndroidPipe::initThreading(android::HostVmLock::getInstance());
|
|
mInitialized = true;
|
|
}
|
|
|
|
// locked
|
|
void HostGoldfishPipeDevice::clearLocked() {
|
|
while (true) {
|
|
const auto i = mFdInfo.begin();
|
|
if (i == mFdInfo.end()) {
|
|
break;
|
|
} else {
|
|
eraseFdInfo(i->first);
|
|
}
|
|
}
|
|
}
|
|
|
|
// locked
|
|
HostGoldfishPipeDevice::FdInfo* HostGoldfishPipeDevice::lookupFdInfo(int fd) {
|
|
const auto i = mFdInfo.find(fd);
|
|
return (i == mFdInfo.end()) ? nullptr : &i->second;
|
|
}
|
|
|
|
// locked
|
|
const HostGoldfishPipeDevice::FdInfo* HostGoldfishPipeDevice::lookupFdInfo(int fd) const {
|
|
const auto i = mFdInfo.find(fd);
|
|
return (i == mFdInfo.end()) ? nullptr : &i->second;
|
|
}
|
|
|
|
// locked
|
|
HostGoldfishPipeDevice::HostHwPipe*
|
|
HostGoldfishPipeDevice::associatePipes(std::unique_ptr<HostGoldfishPipeDevice::HostHwPipe> hwPipe,
|
|
HostGoldfishPipeDevice::InternalPipe* hostPipe) {
|
|
HostHwPipe* hwPipePtr = hwPipe.get();
|
|
const int hwPipeFd = hwPipePtr->getFd();
|
|
|
|
mPipeToHwPipe[hostPipe] = hwPipePtr;
|
|
|
|
FdInfo info;
|
|
info.hwPipe = std::move(hwPipe);
|
|
info.hostPipe = hostPipe;
|
|
|
|
if (!mFdInfo.insert({hwPipeFd, std::move(info)}).second) {
|
|
crashhandler_die_format("%s:%d hwPipeFd=%d already exists",
|
|
__func__, __LINE__, hwPipeFd);
|
|
}
|
|
|
|
return hwPipePtr;
|
|
}
|
|
|
|
// locked
|
|
bool HostGoldfishPipeDevice::eraseFdInfo(const int fd) {
|
|
const auto i = mFdInfo.find(fd);
|
|
if (i == mFdInfo.end()) {
|
|
return false;
|
|
}
|
|
|
|
if (mPipeToHwPipe.erase(i->second.hostPipe) == 0) {
|
|
crashhandler_die_format("%s:%d hostPipe=%p does not exit while fd=%d exists",
|
|
__func__, __LINE__, i->second.hostPipe, fd);
|
|
}
|
|
|
|
android_pipe_guest_close(i->second.hostPipe, PIPE_CLOSE_GRACEFUL);
|
|
mFdInfo.erase(i);
|
|
|
|
return true;
|
|
}
|
|
|
|
ssize_t HostGoldfishPipeDevice::writeInternal(InternalPipe** ppipe,
|
|
const void* buffer,
|
|
size_t len) {
|
|
AndroidPipeBuffer buf = {(uint8_t*)buffer, len};
|
|
ssize_t res = android_pipe_guest_send((void**)ppipe, &buf, 1);
|
|
setErrno(res);
|
|
return res;
|
|
}
|
|
|
|
void HostGoldfishPipeDevice::setErrno(ssize_t res) {
|
|
if (res >= 0) return;
|
|
switch (res) {
|
|
case PIPE_ERROR_INVAL:
|
|
mErrno = EINVAL;
|
|
break;
|
|
case PIPE_ERROR_AGAIN:
|
|
mErrno = EAGAIN;
|
|
break;
|
|
case PIPE_ERROR_NOMEM:
|
|
mErrno = ENOMEM;
|
|
break;
|
|
case PIPE_ERROR_IO:
|
|
mErrno = EIO;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void HostGoldfishPipeDevice::signalWake(const int fd, const int wakes) {
|
|
ScopedVmLock lock;
|
|
const FdInfo* fdInfo = lookupFdInfo(fd);
|
|
if (fdInfo) {
|
|
fdInfo->wakeCallback(wakes);
|
|
}
|
|
}
|
|
|
|
static HostGoldfishPipeDevice* sDevice() {
|
|
static HostGoldfishPipeDevice* d = new HostGoldfishPipeDevice;
|
|
return d;
|
|
}
|
|
|
|
// static
|
|
HostGoldfishPipeDevice* HostGoldfishPipeDevice::get() {
|
|
auto res = sDevice();
|
|
// Must be separate from construction
|
|
// as some initialization routines require
|
|
// the instance to be constructed.
|
|
res->initialize();
|
|
return res;
|
|
}
|
|
|
|
// Callbacks for AndroidPipeHwFuncs.
|
|
// static
|
|
void HostGoldfishPipeDevice::closeFromHostCallback(void* hwPipeRaw) {
|
|
const int fd = static_cast<const HostHwPipe*>(hwPipeRaw)->getFd();
|
|
|
|
// PIPE_WAKE_CLOSED gets translated to closeFromHostCallback.
|
|
// To simplify detecting a close-from-host, signal a wake callback so that
|
|
// the event can be detected.
|
|
HostGoldfishPipeDevice::get()->signalWake(fd, PIPE_WAKE_CLOSED);
|
|
HostGoldfishPipeDevice::get()->close(fd);
|
|
}
|
|
|
|
// static
|
|
void HostGoldfishPipeDevice::signalWakeCallback(void* hwPipeRaw, unsigned wakes) {
|
|
const int fd = static_cast<const HostHwPipe*>(hwPipeRaw)->getFd();
|
|
HostGoldfishPipeDevice::get()->signalWake(fd, wakes);
|
|
}
|
|
|
|
// static
|
|
int HostGoldfishPipeDevice::getPipeIdCallback(void* hwPipeRaw) {
|
|
return static_cast<const HostHwPipe*>(hwPipeRaw)->getFd();
|
|
}
|
|
|
|
} // namespace android
|