// Copyright 2019 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/address_space_host_memory_allocator.h" #include "host-common/address_space_device.hpp" #include "host-common/vm_operations.h" #include "host-common/crash-handler.h" #include "host-common/crash_reporter.h" #include "aemu/base/AlignedBuf.h" namespace android { namespace emulation { namespace { size_t align(size_t value, size_t alignment) { return (value + alignment - 1) & (~(alignment - 1)); } } AddressSpaceHostMemoryAllocatorContext::AddressSpaceHostMemoryAllocatorContext( const address_space_device_control_ops *ops, const AddressSpaceHwFuncs* hw) : m_ops(ops), m_hw(hw) {} AddressSpaceHostMemoryAllocatorContext::~AddressSpaceHostMemoryAllocatorContext() { clear(); } void AddressSpaceHostMemoryAllocatorContext::perform(AddressSpaceDevicePingInfo *info) { uint64_t result; switch (static_cast(info->metadata)) { case HostMemoryAllocatorCommand::Allocate: result = allocate(info); break; case HostMemoryAllocatorCommand::Unallocate: result = unallocate(info); break; default: result = -1; break; } info->metadata = result; } void *AddressSpaceHostMemoryAllocatorContext::allocate_impl(const uint64_t phys_addr, const uint64_t size) { #if defined(__APPLE__) && defined(__arm64__) constexpr uint64_t k_alloc_alignment = 16384; #else constexpr uint64_t k_alloc_alignment = 4096; #endif const uint64_t aligned_size = align(size, (*m_hw->getGuestPageSize)()); void *host_ptr = android::aligned_buf_alloc(k_alloc_alignment, aligned_size); if (host_ptr) { auto r = m_paddr2ptr.insert({phys_addr, {host_ptr, aligned_size}}); if (r.second) { if (m_ops->add_memory_mapping(phys_addr, host_ptr, aligned_size)) { return host_ptr; } else { m_paddr2ptr.erase(r.first); android::aligned_buf_free(host_ptr); return nullptr; } } else { android::aligned_buf_free(host_ptr); return nullptr; } } else { return nullptr; } } uint64_t AddressSpaceHostMemoryAllocatorContext::allocate(AddressSpaceDevicePingInfo *info) { void* host_ptr = allocate_impl(info->phys_addr, info->size); if (host_ptr) { return 0; } else { return -1; } } uint64_t AddressSpaceHostMemoryAllocatorContext::unallocate(AddressSpaceDevicePingInfo *info) { const uint64_t phys_addr = info->phys_addr; const auto i = m_paddr2ptr.find(phys_addr); if (i != m_paddr2ptr.end()) { void* host_ptr = i->second.first; const uint64_t size = i->second.second; if (m_ops->remove_memory_mapping(phys_addr, host_ptr, size)) { android::aligned_buf_free(host_ptr); m_paddr2ptr.erase(i); return 0; } else { crashhandler_die("Failed remove a memory mapping {phys_addr=%lx, host_ptr=%p, size=%lu}", phys_addr, host_ptr, size); } } else { return -1; } } AddressSpaceDeviceType AddressSpaceHostMemoryAllocatorContext::getDeviceType() const { return AddressSpaceDeviceType::HostMemoryAllocator; } void AddressSpaceHostMemoryAllocatorContext::save(base::Stream* stream) const { stream->putBe32(m_paddr2ptr.size()); for (const auto &kv : m_paddr2ptr) { const uint64_t phys_addr = kv.first; const uint64_t size = kv.second.second; const void *mem = kv.second.first; stream->putBe64(phys_addr); stream->putBe64(size); stream->write(mem, size); } } bool AddressSpaceHostMemoryAllocatorContext::load(base::Stream* stream) { clear(); size_t size = stream->getBe32(); for (size_t i = 0; i < size; ++i) { uint64_t phys_addr = stream->getBe64(); uint64_t size = stream->getBe64(); void *mem = allocate_impl(phys_addr, size); if (mem) { if (stream->read(mem, size) != static_cast(size)) { return false; } } else { return false; } } return true; } void AddressSpaceHostMemoryAllocatorContext::clear() { for (const auto& kv : m_paddr2ptr) { uint64_t phys_addr = kv.first; void *host_ptr = kv.second.first; size_t size = kv.second.second; if (m_ops->remove_memory_mapping(phys_addr, host_ptr, size)) { android::aligned_buf_free(host_ptr); } else { crashhandler_die("Failed remove a memory mapping {phys_addr=%lx, host_ptr=%p, size=%lu}", phys_addr, host_ptr, size); } } m_paddr2ptr.clear(); } } // namespace emulation } // namespace android