blob: e61df31d6757015cc82c44d015910d10687a77ed [file] [log] [blame]
// 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<HostMemoryAllocatorCommand>(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<ssize_t>(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