blob: 8995c6bbebfc3f2e1d897ebfd77a4b1347ad503c [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 "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)
{
ALOGD("%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 goldfish_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 goldfish_address_space_ping* ping) {
AddressSpaceDevicePingInfo* asHostPingInfo =
reinterpret_cast<AddressSpaceDevicePingInfo*>(ping);
HostAddressSpaceDevice::get()->ping(handle, asHostPingInfo);
return true;
}