| // 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(uint64_t subdevice) |
| : m_handle(HostAddressSpaceDevice::get()->open()) |
| { |
| if ((subdevice != SUBDEVICE_TYPE_NO_SUBDEVICE_ID) && 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() |
| : m_provider(GoldfishAddressSpaceBlockProvider::SUBDEVICE_TYPE_HOST_MEMORY_ALLOCATOR_ID) {} |
| |
| 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; |
| } |
| if (!block->allocate(&m_provider, size)) { |
| return -ENOMEM; |
| } |
| |
| AddressSpaceDevicePingInfo request; |
| ::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_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; |
| } |