397 lines
11 KiB
Plaintext
397 lines
11 KiB
Plaintext
// 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 "android/emulation/hostdevices/HostAddressSpace.h"
|
|
|
|
#include <memory>
|
|
|
|
#if PLATFORM_SDK_VERSION < 26
|
|
#include <cutils/log.h>
|
|
#else
|
|
#include <log/log.h>
|
|
#endif
|
|
|
|
#include <errno.h>
|
|
#include "goldfish_address_space.h"
|
|
|
|
namespace {
|
|
|
|
const int HOST_MEMORY_ALLOCATOR_COMMAND_ALLOCATE_ID = 1;
|
|
const int HOST_MEMORY_ALLOCATOR_COMMAND_UNALLOCATE_ID = 2;
|
|
|
|
} // namsepace
|
|
|
|
using android::HostAddressSpaceDevice;
|
|
using android::emulation::AddressSpaceDevicePingInfo;
|
|
|
|
GoldfishAddressSpaceBlockProvider::GoldfishAddressSpaceBlockProvider(GoldfishAddressSpaceSubdeviceType subdevice)
|
|
: m_handle(HostAddressSpaceDevice::get()->open())
|
|
{
|
|
if ((subdevice != GoldfishAddressSpaceSubdeviceType::NoSubdevice) && is_opened()) {
|
|
AddressSpaceDevicePingInfo request;
|
|
::memset(&request, 0, sizeof(request));
|
|
request.metadata = subdevice;
|
|
|
|
HostAddressSpaceDevice::get()->ping(m_handle, &request);
|
|
}
|
|
}
|
|
|
|
GoldfishAddressSpaceBlockProvider::~GoldfishAddressSpaceBlockProvider()
|
|
{
|
|
if (is_opened()) {
|
|
HostAddressSpaceDevice::get()->close(m_handle);
|
|
}
|
|
}
|
|
|
|
bool GoldfishAddressSpaceBlockProvider::is_opened() const
|
|
{
|
|
return m_handle > 0;
|
|
}
|
|
|
|
void GoldfishAddressSpaceBlockProvider::close()
|
|
{
|
|
if (is_opened()) {
|
|
HostAddressSpaceDevice::get()->close(m_handle);
|
|
m_handle = 0;
|
|
}
|
|
}
|
|
|
|
address_space_handle_t GoldfishAddressSpaceBlockProvider::release()
|
|
{
|
|
address_space_handle_t handle = m_handle;
|
|
m_handle = 0;
|
|
return handle;
|
|
}
|
|
|
|
void GoldfishAddressSpaceBlockProvider::closeHandle(address_space_handle_t handle)
|
|
{
|
|
HostAddressSpaceDevice::get()->close(handle);
|
|
}
|
|
|
|
GoldfishAddressSpaceBlock::GoldfishAddressSpaceBlock()
|
|
: m_handle(0)
|
|
, m_mmaped_ptr(NULL)
|
|
, m_phys_addr(0)
|
|
, m_host_addr(0)
|
|
, m_offset(0)
|
|
, m_size(0)
|
|
, m_is_shared_mapping(false) {}
|
|
|
|
GoldfishAddressSpaceBlock::~GoldfishAddressSpaceBlock()
|
|
{
|
|
destroy();
|
|
}
|
|
|
|
GoldfishAddressSpaceBlock &GoldfishAddressSpaceBlock::operator=(const GoldfishAddressSpaceBlock &rhs)
|
|
{
|
|
m_mmaped_ptr = rhs.m_mmaped_ptr;
|
|
m_phys_addr = rhs.m_phys_addr;
|
|
m_host_addr = rhs.m_host_addr;
|
|
m_offset = rhs.m_offset;
|
|
m_size = rhs.m_size;
|
|
m_is_shared_mapping = rhs.m_is_shared_mapping;
|
|
m_handle = rhs.m_handle;
|
|
return *this;
|
|
}
|
|
|
|
bool GoldfishAddressSpaceBlock::allocate(GoldfishAddressSpaceBlockProvider *provider, size_t size)
|
|
{
|
|
ALOGV("%s: Ask for block of size 0x%llx\n", __func__,
|
|
(unsigned long long)size);
|
|
|
|
destroy();
|
|
|
|
if (!provider->is_opened()) {
|
|
return false;
|
|
}
|
|
|
|
m_size = size;
|
|
m_offset =
|
|
HostAddressSpaceDevice::get()->allocBlock(
|
|
provider->m_handle, size, &m_phys_addr);
|
|
m_handle = provider->m_handle;
|
|
m_is_shared_mapping = false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool GoldfishAddressSpaceBlock::claimShared(GoldfishAddressSpaceBlockProvider *provider, uint64_t offset, uint64_t size)
|
|
{
|
|
ALOGD("%s: Ask to claim region [0x%llx 0x%llx]\n", __func__,
|
|
(unsigned long long)offset,
|
|
(unsigned long long)offset + size);
|
|
|
|
destroy();
|
|
|
|
if (!provider->is_opened()) {
|
|
return false;
|
|
}
|
|
|
|
int claimRes = HostAddressSpaceDevice::get()->claimShared(
|
|
provider->m_handle, offset, size);
|
|
|
|
if (claimRes) {
|
|
ALOGE("%s: failed to claim shared region. Error: %d\n", __func__, claimRes);
|
|
return false;
|
|
}
|
|
|
|
m_size = size;
|
|
m_offset = offset;
|
|
m_handle = provider->m_handle;
|
|
m_is_shared_mapping = true;
|
|
m_phys_addr = HostAddressSpaceDevice::get()->offsetToPhysAddr(m_offset);
|
|
|
|
return true;
|
|
}
|
|
|
|
uint64_t GoldfishAddressSpaceBlock::physAddr() const
|
|
{
|
|
return m_phys_addr;
|
|
}
|
|
|
|
uint64_t GoldfishAddressSpaceBlock::hostAddr() const
|
|
{
|
|
return m_host_addr;
|
|
}
|
|
|
|
// In the host implementation:
|
|
// mmap: is done by interpreting |host_addr| as the actual host address.
|
|
void *GoldfishAddressSpaceBlock::mmap(uint64_t host_addr)
|
|
{
|
|
if (m_size == 0) {
|
|
ALOGE("%s: called with zero size\n", __func__);
|
|
return NULL;
|
|
}
|
|
|
|
if (m_mmaped_ptr != nullptr) {
|
|
ALOGE("'mmap' called for an already mmaped address block 0x%llx %d", (unsigned long long)m_mmaped_ptr, nullptr == m_mmaped_ptr);
|
|
::abort();
|
|
}
|
|
|
|
m_mmaped_ptr = (void*)(uintptr_t)(host_addr & (~(PAGE_SIZE - 1)));
|
|
m_host_addr = host_addr;
|
|
|
|
return guestPtr();
|
|
}
|
|
|
|
void *GoldfishAddressSpaceBlock::guestPtr() const
|
|
{
|
|
return reinterpret_cast<char *>(m_mmaped_ptr) + (m_host_addr & (PAGE_SIZE - 1));
|
|
}
|
|
|
|
void GoldfishAddressSpaceBlock::destroy()
|
|
{
|
|
if (m_mmaped_ptr && m_size) {
|
|
m_mmaped_ptr = NULL;
|
|
}
|
|
|
|
if (m_size) {
|
|
if (m_is_shared_mapping) {
|
|
HostAddressSpaceDevice::get()->unclaimShared(m_handle, m_offset);
|
|
} else {
|
|
HostAddressSpaceDevice::get()->freeBlock(m_handle, m_offset);
|
|
}
|
|
m_phys_addr = 0;
|
|
m_host_addr = 0;
|
|
m_offset = 0;
|
|
m_size = 0;
|
|
}
|
|
}
|
|
|
|
void GoldfishAddressSpaceBlock::release()
|
|
{
|
|
m_handle = 0;
|
|
m_mmaped_ptr = NULL;
|
|
m_phys_addr = 0;
|
|
m_host_addr = 0;
|
|
m_offset = 0;
|
|
m_size = 0;
|
|
}
|
|
|
|
int GoldfishAddressSpaceBlock::memoryMap(void *addr,
|
|
size_t,
|
|
address_space_handle_t,
|
|
uint64_t,
|
|
void** dst) {
|
|
*dst = addr;
|
|
return 0;
|
|
}
|
|
|
|
void GoldfishAddressSpaceBlock::memoryUnmap(void *ptr, size_t size) {}
|
|
|
|
GoldfishAddressSpaceHostMemoryAllocator::GoldfishAddressSpaceHostMemoryAllocator(bool useSharedSlots)
|
|
: m_provider(useSharedSlots
|
|
? GoldfishAddressSpaceSubdeviceType::SharedSlotsHostMemoryAllocator
|
|
: GoldfishAddressSpaceSubdeviceType::HostMemoryAllocator),
|
|
m_useSharedSlots(useSharedSlots)
|
|
{}
|
|
|
|
bool GoldfishAddressSpaceHostMemoryAllocator::is_opened() const { return m_provider.is_opened(); }
|
|
|
|
long GoldfishAddressSpaceHostMemoryAllocator::hostMalloc(GoldfishAddressSpaceBlock *block, size_t size)
|
|
{
|
|
if (size == 0) {
|
|
return -EINVAL;
|
|
}
|
|
if (block->size() > 0) {
|
|
return -EINVAL;
|
|
}
|
|
if (!m_provider.is_opened()) {
|
|
return -ENODEV;
|
|
}
|
|
|
|
AddressSpaceDevicePingInfo request;
|
|
if (m_useSharedSlots) {
|
|
::memset(&request, 0, sizeof(request));
|
|
request.size = block->size();
|
|
request.metadata = HOST_MEMORY_ALLOCATOR_COMMAND_ALLOCATE_ID;
|
|
|
|
HostAddressSpaceDevice::get()->ping(m_provider.m_handle, &request);
|
|
|
|
block->claimShared(&m_provider, request.phys_addr, request.size);
|
|
|
|
void *hostPtr = HostAddressSpaceDevice::get()->getHostAddr(block->physAddr());
|
|
block->mmap(static_cast<uint64_t>(reinterpret_cast<uintptr_t>(hostPtr)));
|
|
} else {
|
|
if (!block->allocate(&m_provider, size)) {
|
|
return -ENOMEM;
|
|
}
|
|
|
|
::memset(&request, 0, sizeof(request));
|
|
request.phys_addr = block->physAddr();
|
|
request.size = block->size();
|
|
request.metadata = HOST_MEMORY_ALLOCATOR_COMMAND_ALLOCATE_ID;
|
|
|
|
HostAddressSpaceDevice::get()->ping(m_provider.m_handle, &request);
|
|
|
|
void *hostPtr = HostAddressSpaceDevice::get()->getHostAddr(block->physAddr());
|
|
block->mmap(static_cast<uint64_t>(reinterpret_cast<uintptr_t>(hostPtr)));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void GoldfishAddressSpaceHostMemoryAllocator::hostFree(GoldfishAddressSpaceBlock *block)
|
|
{
|
|
if (block->size() == 0) {
|
|
return;
|
|
}
|
|
|
|
if (!m_provider.is_opened()) {
|
|
ALOGE("%s: device is not available", __func__);
|
|
::abort();
|
|
}
|
|
|
|
if (block->guestPtr()) {
|
|
AddressSpaceDevicePingInfo request;
|
|
::memset(&request, 0, sizeof(request));
|
|
request.phys_addr = block->physAddr();
|
|
request.metadata = HOST_MEMORY_ALLOCATOR_COMMAND_UNALLOCATE_ID;
|
|
|
|
HostAddressSpaceDevice::get()->ping(m_provider.m_handle, &request);
|
|
}
|
|
|
|
block->replace(NULL);
|
|
}
|
|
|
|
address_space_handle_t goldfish_address_space_open() {
|
|
return HostAddressSpaceDevice::get()->open();
|
|
}
|
|
|
|
void goldfish_address_space_close(address_space_handle_t handle) {
|
|
HostAddressSpaceDevice::get()->close(handle);
|
|
}
|
|
|
|
bool goldfish_address_space_allocate(
|
|
address_space_handle_t handle,
|
|
size_t size, uint64_t* phys_addr, uint64_t* offset) {
|
|
|
|
*offset =
|
|
HostAddressSpaceDevice::get()->allocBlock(
|
|
handle, size, phys_addr);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool goldfish_address_space_free(
|
|
address_space_handle_t handle, uint64_t offset) {
|
|
HostAddressSpaceDevice::get()->freeBlock(handle, offset);
|
|
return true;
|
|
}
|
|
|
|
bool goldfish_address_space_claim_shared(
|
|
address_space_handle_t handle, uint64_t offset, uint64_t size) {
|
|
|
|
int claimRes = HostAddressSpaceDevice::get()->claimShared(
|
|
handle, offset, size);
|
|
|
|
if (claimRes) {
|
|
ALOGE("%s: failed to claim shared region. Error: %d\n", __func__, claimRes);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool goldfish_address_space_unclaim_shared(
|
|
address_space_handle_t handle, uint64_t offset) {
|
|
HostAddressSpaceDevice::get()->unclaimShared(handle, offset);
|
|
return true;
|
|
}
|
|
|
|
// pgoff is the offset into the page to return in the result
|
|
void* goldfish_address_space_map(
|
|
address_space_handle_t handle,
|
|
uint64_t offset, uint64_t size,
|
|
uint64_t pgoff) {
|
|
|
|
(void)size;
|
|
|
|
void* res = HostAddressSpaceDevice::get()->
|
|
getHostAddr(
|
|
HostAddressSpaceDevice::get()->offsetToPhysAddr(offset));
|
|
|
|
if (!res) {
|
|
ALOGE("%s: failed to map. errno: %d\n", __func__, errno);
|
|
return nullptr;
|
|
}
|
|
|
|
return (void*)(((char*)res) + (uintptr_t)(pgoff & (PAGE_SIZE - 1)));
|
|
}
|
|
|
|
// same address space
|
|
void goldfish_address_space_unmap(void*, uint64_t) { }
|
|
|
|
bool goldfish_address_space_set_subdevice_type(
|
|
address_space_handle_t handle, GoldfishAddressSpaceSubdeviceType type,
|
|
address_space_handle_t* handle_out) {
|
|
struct address_space_ping request;
|
|
request.metadata = (uint64_t)type;
|
|
*handle_out = handle;
|
|
return goldfish_address_space_ping(handle, &request);
|
|
}
|
|
|
|
bool goldfish_address_space_ping(
|
|
address_space_handle_t handle,
|
|
struct address_space_ping* ping) {
|
|
|
|
AddressSpaceDevicePingInfo* asHostPingInfo =
|
|
reinterpret_cast<AddressSpaceDevicePingInfo*>(ping);
|
|
|
|
HostAddressSpaceDevice::get()->ping(handle, asHostPingInfo);
|
|
|
|
return true;
|
|
}
|