|  | // 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(DART_HOST_OS_FUCHSIA) | 
|  |  | 
|  | #include "vm/virtual_memory.h" | 
|  |  | 
|  | #include <zircon/process.h> | 
|  | #include <zircon/status.h> | 
|  | #include <zircon/syscalls.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" | 
|  | #include "vm/virtual_memory_compressed.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::PrintErr("VMVM: %s:%d: " msg, __FILE__, __LINE__, ##__VA_ARGS__) | 
|  | #else | 
|  | #define LOG_ERR(msg, ...) | 
|  | #define LOG_INFO(msg, ...) | 
|  | #endif  // defined(VIRTUAL_MEMORY_LOGGING) | 
|  |  | 
|  | namespace dart { | 
|  |  | 
|  | DECLARE_FLAG(bool, write_protect_code); | 
|  |  | 
|  | uword VirtualMemory::page_size_ = 0; | 
|  |  | 
|  | #if defined(DART_COMPRESSED_POINTERS) | 
|  | static zx_handle_t compressed_heap_vmar_ = ZX_HANDLE_INVALID; | 
|  | static uword compressed_heap_base_ = 0; | 
|  | #endif  // defined(DART_COMPRESSED_POINTERS) | 
|  | static zx_handle_t vmex_resource_ = ZX_HANDLE_INVALID; | 
|  |  | 
|  | intptr_t VirtualMemory::CalculatePageSize() { | 
|  | const intptr_t page_size = getpagesize(); | 
|  | ASSERT(page_size != 0); | 
|  | ASSERT(Utils::IsPowerOfTwo(page_size)); | 
|  | return page_size; | 
|  | } | 
|  |  | 
|  | void VirtualMemory::Init(zx_handle_t vmex_resource) { | 
|  | if (FLAG_old_gen_heap_size < 0 || FLAG_old_gen_heap_size > kMaxAddrSpaceMB) { | 
|  | OS::PrintErr( | 
|  | "warning: value specified for --old_gen_heap_size %d is larger than" | 
|  | " the physically addressable range, using 0(unlimited) instead.`\n", | 
|  | FLAG_old_gen_heap_size); | 
|  | FLAG_old_gen_heap_size = 0; | 
|  | } | 
|  | if (FLAG_new_gen_semi_max_size < 0 || | 
|  | FLAG_new_gen_semi_max_size > kMaxAddrSpaceMB) { | 
|  | OS::PrintErr( | 
|  | "warning: value specified for --new_gen_semi_max_size %d is larger" | 
|  | " than the physically addressable range, using %" Pd " instead.`\n", | 
|  | FLAG_new_gen_semi_max_size, kDefaultNewGenSemiMaxSize); | 
|  | FLAG_new_gen_semi_max_size = kDefaultNewGenSemiMaxSize; | 
|  | } | 
|  |  | 
|  | #if defined(DART_COMPRESSED_POINTERS) | 
|  | if (compressed_heap_vmar_ == ZX_HANDLE_INVALID) { | 
|  | const zx_vm_option_t align_flag = | 
|  | Utils::ShiftForPowerOfTwo(kCompressedHeapAlignment) << ZX_VM_ALIGN_BASE; | 
|  | const zx_vm_option_t options = ZX_VM_CAN_MAP_READ | ZX_VM_CAN_MAP_WRITE | | 
|  | ZX_VM_CAN_MAP_SPECIFIC | align_flag; | 
|  | zx_vaddr_t region; | 
|  | zx_status_t status = | 
|  | zx_vmar_allocate(zx_vmar_root_self(), options, 0, kCompressedHeapSize, | 
|  | &compressed_heap_vmar_, ®ion); | 
|  | if (status != ZX_OK) { | 
|  | LOG_ERR("zx_vmar_allocate(0x%lx) failed: %s\n", kCompressedHeapSize, | 
|  | zx_status_get_string(status)); | 
|  | } else { | 
|  | compressed_heap_base_ = reinterpret_cast<uword>(region); | 
|  | ASSERT(Utils::IsAligned(compressed_heap_base_, kCompressedHeapAlignment)); | 
|  | } | 
|  | } | 
|  | #endif  // defined(DART_COMPRESSED_POINTERS) | 
|  |  | 
|  | page_size_ = CalculatePageSize(); | 
|  | vmex_resource_ = vmex_resource; | 
|  | } | 
|  |  | 
|  | void VirtualMemory::Cleanup() { | 
|  | vmex_resource_ = ZX_HANDLE_INVALID; | 
|  | page_size_ = 0; | 
|  |  | 
|  | #if defined(DART_COMPRESSED_POINTERS) | 
|  | zx_vmar_destroy(compressed_heap_vmar_); | 
|  | compressed_heap_vmar_ = ZX_HANDLE_INVALID; | 
|  | compressed_heap_base_ = 0; | 
|  | #endif  // defined(DART_COMPRESSED_POINTERS) | 
|  | } | 
|  |  | 
|  | static zx_handle_t getVmarForAddress(uword address) { | 
|  | #if defined(DART_COMPRESSED_POINTERS) | 
|  | if (address - compressed_heap_base_ < kCompressedHeapSize) { | 
|  | return compressed_heap_vmar_; | 
|  | } | 
|  | #endif  // defined(DART_COMPRESSED_POINTERS) | 
|  | return zx_vmar_root_self(); | 
|  | } | 
|  |  | 
|  | static void Unmap(zx_handle_t vmar, uword start, uword end) { | 
|  | ASSERT(start <= end); | 
|  | const uword size = end - start; | 
|  | if (size == 0) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | zx_status_t status = zx_vmar_unmap(vmar, start, size); | 
|  | if (status != ZX_OK) { | 
|  | FATAL("zx_vmar_unmap failed: %s\n", zx_status_get_string(status)); | 
|  | } | 
|  | } | 
|  |  | 
|  | VirtualMemory* VirtualMemory::AllocateAligned(intptr_t size, | 
|  | intptr_t alignment, | 
|  | bool is_executable, | 
|  | bool is_compressed, | 
|  | const char* name) { | 
|  | // When FLAG_write_protect_code is active, code memory (indicated by | 
|  | // is_executable = true) is allocated as non-executable and later | 
|  | // changed to executable via VirtualMemory::Protect, which requires | 
|  | // ZX_RIGHT_EXECUTE on the underlying VMO. | 
|  | ASSERT(Utils::IsAligned(size, page_size_)); | 
|  | ASSERT(Utils::IsPowerOfTwo(alignment)); | 
|  | ASSERT(Utils::IsAligned(alignment, page_size_)); | 
|  |  | 
|  | const zx_vm_option_t align_flag = Utils::ShiftForPowerOfTwo(alignment) | 
|  | << ZX_VM_ALIGN_BASE; | 
|  | ASSERT((ZX_VM_ALIGN_1KB <= align_flag) && (align_flag <= ZX_VM_ALIGN_4GB)); | 
|  |  | 
|  | #if defined(DART_COMPRESSED_POINTERS) | 
|  | zx_handle_t vmar; | 
|  | if (is_compressed) { | 
|  | RELEASE_ASSERT(!is_executable); | 
|  | vmar = compressed_heap_vmar_; | 
|  | } else { | 
|  | vmar = zx_vmar_root_self(); | 
|  | } | 
|  | #else | 
|  | zx_handle_t vmar = zx_vmar_root_self(); | 
|  | #endif  // defined(DART_COMPRESSED_POINTERS) | 
|  | zx_handle_t vmo = ZX_HANDLE_INVALID; | 
|  | zx_status_t status = zx_vmo_create(size, 0u, &vmo); | 
|  | if (status == ZX_ERR_NO_MEMORY) { | 
|  | LOG_ERR("zx_vmo_create(0x%lx) failed: %s\n", size, | 
|  | zx_status_get_string(status)); | 
|  | return nullptr; | 
|  | } else if (status != ZX_OK) { | 
|  | FATAL("zx_vmo_create(0x%lx) failed: %s\n", size, | 
|  | zx_status_get_string(status)); | 
|  | } | 
|  |  | 
|  | if (name != nullptr) { | 
|  | zx_object_set_property(vmo, ZX_PROP_NAME, name, strlen(name)); | 
|  | } | 
|  |  | 
|  | if (is_executable) { | 
|  | // Add ZX_RIGHT_EXECUTE permission to VMO, so it can be mapped | 
|  | // into memory as executable (now or later). | 
|  | status = zx_vmo_replace_as_executable(vmo, vmex_resource_, &vmo); | 
|  | if (status == ZX_ERR_NO_MEMORY) { | 
|  | LOG_ERR("zx_vmo_replace_as_executable() failed: %s\n", | 
|  | zx_status_get_string(status)); | 
|  | zx_handle_close(vmo); | 
|  | return nullptr; | 
|  | } else if (status != ZX_OK) { | 
|  | FATAL("zx_vmo_replace_as_executable() failed: %s\n", | 
|  | zx_status_get_string(status)); | 
|  | } | 
|  | } | 
|  |  | 
|  | const zx_vm_option_t region_options = | 
|  | ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | align_flag | | 
|  | ((is_executable && !FLAG_write_protect_code) ? ZX_VM_PERM_EXECUTE : 0); | 
|  | uword base; | 
|  | status = zx_vmar_map(vmar, region_options, 0, vmo, 0u, size, &base); | 
|  | LOG_INFO("zx_vmar_map(%u, 0x%lx, 0x%lx)\n", region_options, base, size); | 
|  | if (status != ZX_OK) { | 
|  | LOG_ERR("zx_vmar_map(%u, 0x%lx, 0x%lx) failed: %s\n", region_options, base, | 
|  | size, zx_status_get_string(status)); | 
|  | zx_handle_close(vmo); | 
|  | return nullptr; | 
|  | } | 
|  | void* region_ptr = reinterpret_cast<void*>(base); | 
|  | MemoryRegion region(region_ptr, size); | 
|  | VirtualMemory* result = new VirtualMemory(region, region); | 
|  | zx_handle_close(vmo); | 
|  |  | 
|  | #if defined(DART_COMPRESSED_POINTERS) | 
|  | if (!is_executable) { | 
|  | uword offset = result->start() - compressed_heap_base_; | 
|  | ASSERT(offset < kCompressedHeapSize); | 
|  | } | 
|  | #endif  // defined(DART_COMPRESSED_POINTERS) | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  | VirtualMemory::~VirtualMemory() { | 
|  | // Reserved region may be empty due to VirtualMemory::Truncate. | 
|  | if (vm_owns_region() && reserved_.size() != 0) { | 
|  | Unmap(getVmarForAddress(reserved_.start()), reserved_.start(), | 
|  | reserved_.end()); | 
|  | LOG_INFO("zx_vmar_unmap(0x%lx, 0x%lx) success\n", reserved_.start(), | 
|  | reserved_.size()); | 
|  | } | 
|  | } | 
|  |  | 
|  | bool VirtualMemory::FreeSubSegment(void* address, intptr_t size) { | 
|  | const uword start = reinterpret_cast<uword>(address); | 
|  | Unmap(getVmarForAddress(start), start, start + size); | 
|  | LOG_INFO("zx_vmar_unmap(0x%p, 0x%lx) success\n", address, size); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void VirtualMemory::Protect(void* address, intptr_t size, Protection mode) { | 
|  | #if defined(DEBUG) | 
|  | Thread* thread = Thread::Current(); | 
|  | ASSERT(thread == nullptr || thread->IsDartMutatorThread() || | 
|  | thread->isolate() == nullptr || | 
|  | thread->isolate()->mutator_thread()->IsAtSafepoint()); | 
|  | #endif | 
|  | const uword start_address = reinterpret_cast<uword>(address); | 
|  | const uword end_address = start_address + size; | 
|  | const uword page_address = Utils::RoundDown(start_address, PageSize()); | 
|  | uint32_t prot = 0; | 
|  | switch (mode) { | 
|  | case kNoAccess: | 
|  | prot = 0; | 
|  | break; | 
|  | case kReadOnly: | 
|  | prot = ZX_VM_PERM_READ; | 
|  | break; | 
|  | case kReadWrite: | 
|  | prot = ZX_VM_PERM_READ | ZX_VM_PERM_WRITE; | 
|  | break; | 
|  | case kReadExecute: | 
|  | prot = ZX_VM_PERM_READ | ZX_VM_PERM_EXECUTE; | 
|  | break; | 
|  | case kReadWriteExecute: | 
|  | prot = ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_PERM_EXECUTE; | 
|  | break; | 
|  | } | 
|  | zx_status_t status = | 
|  | zx_vmar_protect(getVmarForAddress(page_address), prot, page_address, | 
|  | end_address - page_address); | 
|  | LOG_INFO("zx_vmar_protect(%u, 0x%lx, 0x%lx)\n", prot, page_address, | 
|  | end_address - page_address); | 
|  | if (status != ZX_OK) { | 
|  | FATAL("zx_vmar_protect(0x%lx, 0x%lx) failed: %s\n", page_address, | 
|  | end_address - page_address, zx_status_get_string(status)); | 
|  | } | 
|  | } | 
|  |  | 
|  | void VirtualMemory::DontNeed(void* address, intptr_t size) { | 
|  | uword start_address = reinterpret_cast<uword>(address); | 
|  | uword end_address = start_address + size; | 
|  | uword page_address = Utils::RoundDown(start_address, PageSize()); | 
|  | zx_status_t status = zx_vmar_op_range( | 
|  | getVmarForAddress(reinterpret_cast<uword>(address)), ZX_VMAR_OP_DONT_NEED, | 
|  | page_address, end_address - page_address, nullptr, 0); | 
|  | LOG_INFO("zx_vmar_op_range(DONTNEED, 0x%lx, 0x%lx)\n", page_address, | 
|  | end_address - page_address); | 
|  | if (status != ZX_OK) { | 
|  | FATAL("zx_vmar_op_range(DONTNEED, 0x%lx, 0x%lx) failed: %s\n", page_address, | 
|  | end_address - page_address, zx_status_get_string(status)); | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace dart | 
|  |  | 
|  | #endif  // defined(DART_HOST_OS_FUCHSIA) |