blob: a401150c176aedfa2a6bd6fc677faa071da67f00 [file] [log] [blame]
// Copyright (C) 2019 The Android Open Source Project
// Copyright (C) 2019 Google Inc.
//
// 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 "HostAddressSpace.h"
#include "aemu/base/SubAllocator.h"
#include "aemu/base/synchronization/Lock.h"
#include "host-common/address_space_device.h"
#include "host-common/address_space_device.hpp"
#include <unordered_map>
#include <vector>
#include <inttypes.h>
#define HASD_DEBUG 0
#if HASD_DEBUG
#define HASD_LOG(fmt,...) printf("%s:%d " fmt "\n", __func__, __LINE__, ##__VA_ARGS__);
#else
#define HASD_LOG(fmt,...)
#endif
using android::base::AutoLock;
using android::base::Lock;
using android::base::SubAllocator;
using android::emulation::AddressSpaceDevicePingInfo;
namespace android {
class HostAddressSpaceDevice::Impl {
public:
Impl() : mControlOps(get_address_space_device_control_ops()),
mPhysicalOffsetAllocator(0, 16ULL * 1024ULL * 1048576ULL, 4096) { }
void clear() {
std::vector<uint32_t> handlesToClose;
for (auto it : mEntries) {
handlesToClose.push_back(it.first);
}
for (auto handle : handlesToClose) {
close(handle);
}
mSharedRegions.clear();
mPhysicalOffsetAllocator.freeAll();
mControlOps->clear();
}
uint32_t open() {
uint32_t handle = mControlOps->gen_handle();
AutoLock lock(mLock);
auto& entry = mEntries[handle];
entry.pingInfo = new AddressSpaceDevicePingInfo;
lock.unlock();
mControlOps->tell_ping_info(handle, (uint64_t)(uintptr_t)entry.pingInfo);
return handle;
}
void close(uint32_t handle) {
mControlOps->destroy_handle(handle);
AutoLock lock(mLock);
auto& entry = mEntries[handle];
delete entry.pingInfo;
mEntries.erase(handle);
}
uint64_t allocBlock(uint32_t handle, size_t size, uint64_t* physAddr) {
AutoLock lock(mLock);
return allocBlockLocked(handle, size, physAddr);
}
void freeBlock(uint32_t handle, uint64_t off) {
// mirror hw/pci/goldfish_address_space.c:
// first run deallocation callbacks, then update the state
mControlOps->run_deallocation_callbacks(kPciStart + off);
AutoLock lock(mLock);
freeBlockLocked(handle, off);
}
void setHostAddr(uint32_t handle, size_t off, void* hva) {
AutoLock lock(mLock);
auto& entry = mEntries[handle];
auto& mem = entry.blocks[off];
mem.hva = hva;
}
void setHostAddrByPhysAddr(uint64_t physAddr, void* hva) {
if (!physAddr) return;
const uint64_t off = physAddr - kPciStart;
AutoLock lock(mLock);
for (auto &it : mEntries) {
for (auto &it2 : it.second.blocks) {
if (it2.first == off) {
it2.second.hva = hva;
}
}
}
for (auto &it : mSharedRegions) {
if (it.first == off) {
it.second.hva = hva;
}
}
}
void unsetHostAddrByPhysAddr(uint64_t physAddr) {
if (!physAddr) return;
const uint64_t off = physAddr - kPciStart;
AutoLock lock(mLock);
for (auto &it : mEntries) {
for (auto &it2 : it.second.blocks) {
if (it2.first == off) {
it2.second.hva = nullptr;
}
}
}
for (auto &it : mSharedRegions) {
if (it.first == off) {
it.second.hva = nullptr;
}
}
}
void* getHostAddr(uint64_t physAddr) {
HASD_LOG("get hva of 0x%llx", (unsigned long long)physAddr);
if (!physAddr) return nullptr;
const uint64_t off = physAddr - kPciStart;
AutoLock lock(mLock);
HASD_LOG("get hva of off 0x%llx", (unsigned long long)off);
void* res = 0;
// First check ping infos
for (const auto &it : mEntries) {
if ((uint64_t)(uintptr_t)it.second.pingInfo == physAddr) return it.second.pingInfo;
}
for (const auto &it : mEntries) {
for (const auto &it2 : it.second.blocks) {
if (blockContainsOffset(it2.first, it2.second, off)) {
HASD_LOG("entry [0x%llx 0x%llx] contains. hva: %p",
(unsigned long long)it2.first,
(unsigned long long)it2.first + it2.second.size,
it2.second.hva);
res = ((char*)it2.second.hva) +
offsetIntoBlock(it2.first, it2.second, off);
}
}
}
for (auto &it : mSharedRegions) {
if (blockContainsOffset(it.first, it.second, off)) {
HASD_LOG("shared region [0x%llx 0x%llx] contains. hva: %p",
(unsigned long long)it.first,
(unsigned long long)it.first + it.second.size,
it.second.hva);
res = ((char*)it.second.hva) +
offsetIntoBlock(it.first, it.second, off);
}
}
return res;
}
static uint64_t offsetToPhysAddr(uint64_t offset) {
return kPciStart + offset;
}
void ping(uint32_t handle, AddressSpaceDevicePingInfo* pingInfo) {
AutoLock lock(mLock);
auto& entry = mEntries[handle];
memcpy(entry.pingInfo, pingInfo, sizeof(AddressSpaceDevicePingInfo));
lock.unlock();
mControlOps->ping(handle);
lock.lock();
memcpy(pingInfo, entry.pingInfo, sizeof(AddressSpaceDevicePingInfo));
}
int claimShared(uint32_t handle, uint64_t off, uint64_t size) {
auto& entry = mEntries[handle];
if (entry.blocks.find(off) != entry.blocks.end()) {
fprintf(stderr, "%s: failed, entry already owns offset 0x%llx\n", __func__,
(unsigned long long)off);
return -EINVAL;
}
if (!enclosingSharedRegionExists(mSharedRegions, off, size)) {
fprintf(stderr, "%s: failed, no shared region enclosing [0x%llx 0x%llx]\n", __func__,
(unsigned long long)off,
(unsigned long long)off + size);
return -EINVAL;
}
auto& entryBlock = entry.blocks[off];
entryBlock.size = size;
return 0;
}
int unclaimShared(uint32_t handle, uint64_t off) {
auto& entry = mEntries[handle];
if (entry.blocks.find(off) == entry.blocks.end()) {
fprintf(stderr, "%s: failed, entry does not own offset 0x%llx\n", __func__,
(unsigned long long)off);
return -EINVAL;
}
if (!enclosingSharedRegionExists(mSharedRegions, off, entry.blocks[off].size)) {
fprintf(stderr, "%s: failed, no shared region enclosing [0x%llx 0x%llx]\n", __func__,
(unsigned long long)off,
(unsigned long long)off + entry.blocks[off].size);
return -EINVAL;
}
entry.blocks.erase(off);
return 0;
}
void saveSnapshot(base::Stream* stream) {
emulation::goldfish_address_space_memory_state_save(stream);
}
void loadSnapshot(base::Stream* stream) {
emulation::goldfish_address_space_memory_state_load(stream);
}
// Simulated host interface
int allocSharedHostRegion(uint64_t page_aligned_size, uint64_t* offset) {
if (!offset) return -EINVAL;
AutoLock lock(mLock);
return allocSharedHostRegionLocked(page_aligned_size, offset);
}
int allocSharedHostRegionLocked(uint64_t page_aligned_size, uint64_t* offset) {
if (!offset) return -EINVAL;
uint64_t off = (uint64_t)(uintptr_t)mPhysicalOffsetAllocator.alloc(page_aligned_size);
auto& block = mSharedRegions[off];
block.size = page_aligned_size;
(void)block;
*offset = off;
HASD_LOG("new shared region: [0x%llx 0x%llx]",
(unsigned long long)off,
(unsigned long long)off + page_aligned_size);
return 0;
}
int allocSharedHostRegionFixedLocked(uint64_t page_aligned_size, uint64_t offset) {
mPhysicalOffsetAllocator.allocFixed(page_aligned_size, offset);
auto& block = mSharedRegions[offset];
block.size = page_aligned_size;
(void)block;
HASD_LOG("new shared region: [0x%llx 0x%llx]",
(unsigned long long)offset,
(unsigned long long)offset + page_aligned_size);
return 0;
}
int freeSharedHostRegion(uint64_t offset) {
AutoLock lock(mLock);
return freeSharedHostRegionLocked(offset);
}
int freeSharedHostRegionLocked(uint64_t offset) {
if (mSharedRegions.find(offset) == mSharedRegions.end()) {
fprintf(stderr, "%s: could not free shared region, offset 0x%llx is not a start\n", __func__,
(unsigned long long)offset);
return -EINVAL;
}
HASD_LOG("free shared region @ 0x%llx",
(unsigned long long)offset);
mSharedRegions.erase(offset);
mPhysicalOffsetAllocator.free((void*)(uintptr_t)offset);
return 0;
}
static uint64_t getPhysAddrStart() {
return kPciStart;
}
private:
struct BlockMemory {
size_t size = 0;
void* hva = nullptr;
};
using MemoryMap = std::unordered_map<uint64_t, BlockMemory>;
uint64_t allocBlockLocked(uint32_t handle, size_t size, uint64_t* physAddr) {
uint64_t off = (uint64_t)(uintptr_t)mPhysicalOffsetAllocator.alloc(size);
auto& entry = mEntries[handle];
auto& block = entry.blocks[off];
block.size = size;
(void)block;
*physAddr = kPciStart + off;
return off;
}
void freeBlockLocked(uint32_t handle, uint64_t off) {
auto& entry = mEntries[handle];
entry.blocks.erase(off);
mPhysicalOffsetAllocator.free((void*)(uintptr_t)off);
}
bool blockContainsOffset(
uint64_t offset,
const BlockMemory& block,
uint64_t physAddr) const {
return offset <= physAddr &&
offset + block.size > physAddr;
}
uint64_t offsetIntoBlock(
uint64_t offset,
const BlockMemory& block,
uint64_t physAddr) const {
if (!blockContainsOffset(offset, block, physAddr)) {
fprintf(stderr, "%s: block at [0x%" PRIx64 " 0x%" PRIx64"] does not contain 0x%" PRIx64 "!\n", __func__,
offset,
offset + block.size,
physAddr);
abort();
}
return physAddr - offset;
}
bool enclosingSharedRegionExists(
const MemoryMap& memoryMap, uint64_t offset, uint64_t size) const {
for (const auto& it : memoryMap) {
if (it.first <= offset &&
it.first + it.second.size >= offset + size)
return true;
}
return false;
}
static const uint64_t kPciStart = 0x0101010100000000;
Lock mLock;
address_space_device_control_ops* mControlOps = nullptr;
android::base::SubAllocator mPhysicalOffsetAllocator;
struct Entry {
AddressSpaceDevicePingInfo* pingInfo = nullptr;
MemoryMap blocks;
};
std::unordered_map<uint32_t, Entry> mEntries;
MemoryMap mSharedRegions;
};
static HostAddressSpaceDevice* sHostAddressSpace() {
static HostAddressSpaceDevice* h = new HostAddressSpaceDevice;
return h;
}
HostAddressSpaceDevice::HostAddressSpaceDevice() :
mImpl(new HostAddressSpaceDevice::Impl()) { }
// static
HostAddressSpaceDevice* HostAddressSpaceDevice::get() {
auto res = sHostAddressSpace();
res->initialize();
return res;
}
uint32_t HostAddressSpaceDevice::open() {
return mImpl->open();
}
void HostAddressSpaceDevice::close(uint32_t handle) {
mImpl->close(handle);
}
uint64_t HostAddressSpaceDevice::allocBlock(uint32_t handle, size_t size, uint64_t* physAddr) {
return mImpl->allocBlock(handle, size, physAddr);
}
void HostAddressSpaceDevice::freeBlock(uint32_t handle, uint64_t off) {
return mImpl->freeBlock(handle, off);
}
void HostAddressSpaceDevice::setHostAddr(uint32_t handle, size_t off, void* hva) {
return mImpl->setHostAddr(handle, off, hva);
}
void HostAddressSpaceDevice::setHostAddrByPhysAddr(uint64_t physAddr, void* hva) {
mImpl->setHostAddrByPhysAddr(physAddr, hva);
}
void HostAddressSpaceDevice::unsetHostAddrByPhysAddr(uint64_t physAddr) {
mImpl->unsetHostAddrByPhysAddr(physAddr);
}
void* HostAddressSpaceDevice::getHostAddr(uint64_t physAddr) {
return mImpl->getHostAddr(physAddr);
}
uint64_t HostAddressSpaceDevice::offsetToPhysAddr(uint64_t offset) const {
return mImpl->offsetToPhysAddr(offset);
}
void HostAddressSpaceDevice::ping(uint32_t handle, AddressSpaceDevicePingInfo* pingInfo) {
mImpl->ping(handle, pingInfo);
}
int HostAddressSpaceDevice::claimShared(uint32_t handle, uint64_t off, uint64_t size) {
return mImpl->claimShared(handle, off, size);
}
int HostAddressSpaceDevice::unclaimShared(uint32_t handle, uint64_t off) {
return mImpl->unclaimShared(handle, off);
}
void HostAddressSpaceDevice::saveSnapshot(base::Stream* stream) {
mImpl->saveSnapshot(stream);
}
void HostAddressSpaceDevice::loadSnapshot(base::Stream* stream) {
mImpl->loadSnapshot(stream);
}
// static
HostAddressSpaceDevice::Impl* HostAddressSpaceDevice::getImpl() {
return HostAddressSpaceDevice::get()->mImpl.get();
}
int HostAddressSpaceDevice::allocSharedHostRegion(uint64_t page_aligned_size, uint64_t* offset) {
return HostAddressSpaceDevice::getImpl()->allocSharedHostRegion(page_aligned_size, offset);
}
int HostAddressSpaceDevice::freeSharedHostRegion(uint64_t offset) {
return HostAddressSpaceDevice::getImpl()->freeSharedHostRegion(offset);
}
int HostAddressSpaceDevice::allocSharedHostRegionLocked(uint64_t page_aligned_size, uint64_t* offset) {
return HostAddressSpaceDevice::getImpl()->allocSharedHostRegionLocked(page_aligned_size, offset);
}
int HostAddressSpaceDevice::freeSharedHostRegionLocked(uint64_t offset) {
return HostAddressSpaceDevice::getImpl()->freeSharedHostRegionLocked( offset);
}
uint64_t HostAddressSpaceDevice::getPhysAddrStart() {
return HostAddressSpaceDevice::getImpl()->getPhysAddrStart();
}
int HostAddressSpaceDevice::allocSharedHostRegionFixedLocked(uint64_t page_aligned_size, uint64_t offset) {
return HostAddressSpaceDevice::getImpl()->allocSharedHostRegionFixedLocked(page_aligned_size, offset);
}
static const AddressSpaceHwFuncs sAddressSpaceHwFuncs = {
&HostAddressSpaceDevice::allocSharedHostRegion,
&HostAddressSpaceDevice::freeSharedHostRegion,
&HostAddressSpaceDevice::allocSharedHostRegionLocked,
&HostAddressSpaceDevice::freeSharedHostRegionLocked,
&HostAddressSpaceDevice::getPhysAddrStart,
&HostAddressSpaceDevice::getPhysAddrStart,
&HostAddressSpaceDevice::allocSharedHostRegionFixedLocked,
};
void HostAddressSpaceDevice::initialize() {
if (mInitialized) return;
address_space_set_hw_funcs(&sAddressSpaceHwFuncs);
mInitialized = true;
}
void HostAddressSpaceDevice::clear() {
mImpl->clear();
}
} // namespace android