Files
sdk/runtime/vm/virtual_memory_fuchsia.cc
T
Ryan Macnak 877284947b Rename TARGET_OS_* to HOST_OS_*.
Like HOST_ARCH_*, HOST_OS_* describes the OS the VM is running on, which may be different from the OS the VM is generating code for during AOT compilation.

Currently we conflate the two when emitting AOT as assembly, and we get away with it because Flutter only uses assembly for targeting iOS and one can only target iOS from a Mac, but we expect to use assembly for Android as well so native tools can unwind Dart frames.

R=zra@google.com

Review-Url: https://codereview.chromium.org/2750843003 .
2017-03-15 13:11:05 -07:00

284 lines
8.8 KiB
C++

// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "vm/globals.h"
#if defined(HOST_OS_FUCHSIA)
#include "vm/virtual_memory.h"
#include <magenta/process.h>
#include <magenta/status.h>
#include <magenta/syscalls.h>
#include <sys/mman.h>
#include <unistd.h>
#include "platform/assert.h"
#include "vm/allocation.h"
#include "vm/growable_array.h"
#include "vm/isolate.h"
#include "vm/lockers.h"
#include "vm/memory_region.h"
#include "vm/os.h"
#include "vm/os_thread.h"
// #define VIRTUAL_MEMORY_LOGGING 1
#if defined(VIRTUAL_MEMORY_LOGGING)
#define LOG_ERR(msg, ...) \
OS::PrintErr("VMVM: %s:%d: " msg, __FILE__, __LINE__, ##__VA_ARGS__)
#define LOG_INFO(msg, ...) \
OS::Print("VMVM: %s:%d: " msg, __FILE__, __LINE__, ##__VA_ARGS__)
#else
#define LOG_ERR(msg, ...)
#define LOG_INFO(msg, ...)
#endif // defined(VIRTUAL_MEMORY_LOGGING)
namespace dart {
// The Magenta system call to protect memory regions (mx_vmar_protect) takes a
// VM area (vmar) handle as first argument. We call VirtualMemory::Protect()
// from the memory freelist code in vm/freelist.cc where the vmar handle is not
// available. Additionally, there is no mx_vmar system call to retrieve a handle
// for the leaf vmar given an address. Thus, when memory protections are
// enabled, we maintain a sorted list of our leaf vmar handles that we can
// query by address in calls to VirtualMemory::Protect().
class VmarList : public AllStatic {
public:
static void AddVmar(mx_handle_t vmar, uword addr, intptr_t size);
static void RemoveVmar(uword addr);
static mx_handle_t LookupVmar(uword addr);
private:
static intptr_t LookupVmarIndexLocked(uword addr);
struct VmarListElement {
mx_handle_t vmar;
uword addr;
intptr_t size;
};
static Mutex* vmar_array_lock_;
static MallocGrowableArray<VmarListElement> vmar_array_;
};
Mutex* VmarList::vmar_array_lock_ = new Mutex();
MallocGrowableArray<VmarList::VmarListElement> VmarList::vmar_array_;
void VmarList::AddVmar(mx_handle_t vmar, uword addr, intptr_t size) {
MutexLocker ml(vmar_array_lock_);
LOG_INFO("AddVmar(%d, %lx, %ld)\n", vmar, addr, size);
// Sorted insert in increasing order.
const intptr_t length = vmar_array_.length();
intptr_t idx;
for (idx = 0; idx < length; idx++) {
const VmarListElement& m = vmar_array_.At(idx);
if (m.addr >= addr) {
break;
}
}
#if defined(DEBUG)
if ((length > 0) && (idx < (length - 1))) {
const VmarListElement& m = vmar_array_.At(idx);
ASSERT(m.addr != addr);
}
#endif
LOG_INFO("AddVmar(%d, %lx, %ld) at index = %ld\n", vmar, addr, size, idx);
VmarListElement new_mapping;
new_mapping.vmar = vmar;
new_mapping.addr = addr;
new_mapping.size = size;
vmar_array_.InsertAt(idx, new_mapping);
}
intptr_t VmarList::LookupVmarIndexLocked(uword addr) {
// Binary search for the vmar containing addr.
intptr_t imin = 0;
intptr_t imax = vmar_array_.length();
while (imax >= imin) {
const intptr_t imid = ((imax - imin) / 2) + imin;
const VmarListElement& mapping = vmar_array_.At(imid);
if ((mapping.addr + mapping.size) <= addr) {
imin = imid + 1;
} else if (mapping.addr > addr) {
imax = imid - 1;
} else {
return imid;
}
}
return -1;
}
mx_handle_t VmarList::LookupVmar(uword addr) {
MutexLocker ml(vmar_array_lock_);
LOG_INFO("LookupVmar(%lx)\n", addr);
const intptr_t idx = LookupVmarIndexLocked(addr);
if (idx == -1) {
LOG_ERR("LookupVmar(%lx) NOT FOUND\n", addr);
return MX_HANDLE_INVALID;
}
LOG_INFO("LookupVmar(%lx) found at %ld\n", addr, idx);
return vmar_array_[idx].vmar;
}
void VmarList::RemoveVmar(uword addr) {
MutexLocker ml(vmar_array_lock_);
LOG_INFO("RemoveVmar(%lx)\n", addr);
const intptr_t idx = LookupVmarIndexLocked(addr);
ASSERT(idx != -1);
#if defined(DEBUG)
mx_handle_t vmar = vmar_array_[idx].vmar;
#endif
// Swap idx to the end, and then RemoveLast()
const intptr_t length = vmar_array_.length();
for (intptr_t i = idx; i < length - 1; i++) {
vmar_array_.Swap(i, i + 1);
}
#if defined(DEBUG)
const VmarListElement& mapping = vmar_array_.Last();
ASSERT(mapping.vmar == vmar);
#endif
vmar_array_.RemoveLast();
}
uword VirtualMemory::page_size_ = 0;
void VirtualMemory::InitOnce() {
page_size_ = getpagesize();
}
VirtualMemory* VirtualMemory::ReserveInternal(intptr_t size) {
mx_handle_t vmar = MX_HANDLE_INVALID;
uword addr = 0;
const uint32_t flags = MX_VM_FLAG_COMPACT | MX_VM_FLAG_CAN_MAP_SPECIFIC |
MX_VM_FLAG_CAN_MAP_READ | MX_VM_FLAG_CAN_MAP_WRITE |
MX_VM_FLAG_CAN_MAP_EXECUTE;
mx_status_t status =
mx_vmar_allocate(mx_vmar_root_self(), 0, size, flags, &vmar, &addr);
if (status != NO_ERROR) {
LOG_ERR("mx_vmar_allocate(size = %ld) failed: %s\n", size,
mx_status_get_string(status));
return NULL;
}
VmarList::AddVmar(vmar, addr, size);
MemoryRegion region(reinterpret_cast<void*>(addr), size);
return new VirtualMemory(region, vmar);
}
VirtualMemory::~VirtualMemory() {
if (vm_owns_region()) {
mx_handle_t vmar = static_cast<mx_handle_t>(handle());
mx_status_t status = mx_vmar_destroy(vmar);
if (status != NO_ERROR) {
LOG_ERR("mx_vmar_destroy failed: %s\n", mx_status_get_string(status));
}
status = mx_handle_close(vmar);
if (status != NO_ERROR) {
LOG_ERR("mx_handle_close failed: %s\n", mx_status_get_string(status));
}
VmarList::RemoveVmar(start());
}
}
bool VirtualMemory::FreeSubSegment(int32_t handle,
void* address,
intptr_t size) {
mx_handle_t vmar = static_cast<mx_handle_t>(handle);
mx_status_t status =
mx_vmar_unmap(vmar, reinterpret_cast<uintptr_t>(address), size);
if (status != NO_ERROR) {
LOG_ERR("mx_vmar_unmap failed: %s\n", mx_status_get_string(status));
return false;
}
return true;
}
bool VirtualMemory::Commit(uword addr, intptr_t size, bool executable) {
ASSERT(Contains(addr));
ASSERT(Contains(addr + size) || (addr + size == end()));
mx_handle_t vmo = MX_HANDLE_INVALID;
mx_status_t status = mx_vmo_create(size, 0u, &vmo);
if (status != NO_ERROR) {
LOG_ERR("mx_vmo_create(%ld) failed: %s\n", size,
mx_status_get_string(status));
return false;
}
mx_handle_t vmar = static_cast<mx_handle_t>(handle());
const size_t offset = addr - start();
const uint32_t flags = MX_VM_FLAG_SPECIFIC | MX_VM_FLAG_PERM_READ |
MX_VM_FLAG_PERM_WRITE |
(executable ? MX_VM_FLAG_PERM_EXECUTE : 0);
uintptr_t mapped_addr;
status = mx_vmar_map(vmar, offset, vmo, 0, size, flags, &mapped_addr);
if (status != NO_ERROR) {
mx_handle_close(vmo);
LOG_ERR("mx_vmar_map(%ld, %ld, %u) failed: %s\n", offset, size, flags,
mx_status_get_string(status));
return false;
}
if (addr != mapped_addr) {
mx_handle_close(vmo);
LOG_ERR("mx_vmar_map: addr != mapped_addr: %lx != %lx\n", addr,
mapped_addr);
return false;
}
mx_handle_close(vmo);
LOG_INFO("Commit(%lx, %ld, %s): success\n", addr, size,
executable ? "executable" : "");
return true;
}
bool VirtualMemory::Protect(void* address, intptr_t size, Protection mode) {
ASSERT(Thread::Current()->IsMutatorThread() ||
Isolate::Current()->mutator_thread()->IsAtSafepoint());
const uword start_address = reinterpret_cast<uword>(address);
const uword end_address = start_address + size;
const uword page_address = Utils::RoundDown(start_address, PageSize());
mx_handle_t vmar = VmarList::LookupVmar(page_address);
ASSERT(vmar != MX_HANDLE_INVALID);
uint32_t prot = 0;
switch (mode) {
case kNoAccess:
// MG-426: mx_vmar_protect() requires at least on permission.
prot = MX_VM_FLAG_PERM_READ;
break;
case kReadOnly:
prot = MX_VM_FLAG_PERM_READ;
break;
case kReadWrite:
prot = MX_VM_FLAG_PERM_READ | MX_VM_FLAG_PERM_WRITE;
break;
case kReadExecute:
prot = MX_VM_FLAG_PERM_READ | MX_VM_FLAG_PERM_EXECUTE;
break;
case kReadWriteExecute:
prot = MX_VM_FLAG_PERM_READ | MX_VM_FLAG_PERM_WRITE |
MX_VM_FLAG_PERM_EXECUTE;
break;
}
mx_status_t status =
mx_vmar_protect(vmar, page_address, end_address - page_address, prot);
if (status != NO_ERROR) {
LOG_ERR("mx_vmar_protect(%lx, %lx, %x) success: %s\n", page_address,
end_address - page_address, prot, mx_status_get_string(status));
return false;
}
LOG_INFO("mx_vmar_protect(%lx, %lx, %x) success\n", page_address,
end_address - page_address, prot);
return true;
}
} // namespace dart
#endif // defined(HOST_OS_FUCHSIA)