| OLD | NEW |
| 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 #include "vm/globals.h" | 5 #include "vm/globals.h" |
| 6 #if defined(TARGET_OS_FUCHSIA) | 6 #if defined(TARGET_OS_FUCHSIA) |
| 7 | 7 |
| 8 #include "vm/virtual_memory.h" | 8 #include "vm/virtual_memory.h" |
| 9 | 9 |
| 10 #include <magenta/status.h> | 10 #include <magenta/status.h> |
| 11 #include <magenta/syscalls.h> | 11 #include <magenta/syscalls.h> |
| 12 #include <sys/mman.h> | 12 #include <sys/mman.h> |
| 13 #include <unistd.h> | 13 #include <unistd.h> |
| 14 | 14 |
| 15 #include "platform/assert.h" | 15 #include "platform/assert.h" |
| 16 #include "vm/allocation.h" |
| 17 #include "vm/growable_array.h" |
| 16 #include "vm/isolate.h" | 18 #include "vm/isolate.h" |
| 19 #include "vm/lockers.h" |
| 17 #include "vm/memory_region.h" | 20 #include "vm/memory_region.h" |
| 18 #include "vm/os.h" | 21 #include "vm/os.h" |
| 22 #include "vm/os_thread.h" |
| 19 | 23 |
| 20 // #define VIRTUAL_MEMORY_LOGGING 1 | 24 // #define VIRTUAL_MEMORY_LOGGING 1 |
| 21 #if defined(VIRTUAL_MEMORY_LOGGING) | 25 #if defined(VIRTUAL_MEMORY_LOGGING) |
| 22 #define LOG_ERR(msg, ...) \ | 26 #define LOG_ERR(msg, ...) \ |
| 23 OS::PrintErr("VMVM: %s:%d: " msg, __FILE__, __LINE__, ##__VA_ARGS__) | 27 OS::PrintErr("VMVM: %s:%d: " msg, __FILE__, __LINE__, ##__VA_ARGS__) |
| 24 #define LOG_INFO(msg, ...) \ | 28 #define LOG_INFO(msg, ...) \ |
| 25 OS::Print("VMVM: %s:%d: " msg, __FILE__, __LINE__, ##__VA_ARGS__) | 29 OS::Print("VMVM: %s:%d: " msg, __FILE__, __LINE__, ##__VA_ARGS__) |
| 26 #else | 30 #else |
| 27 #define LOG_ERR(msg, ...) | 31 #define LOG_ERR(msg, ...) |
| 28 #define LOG_INFO(msg, ...) | 32 #define LOG_INFO(msg, ...) |
| 29 #endif // defined(VIRTUAL_MEMORY_LOGGING) | 33 #endif // defined(VIRTUAL_MEMORY_LOGGING) |
| 30 | 34 |
| 31 namespace dart { | 35 namespace dart { |
| 32 | 36 |
| 37 // The Magenta system call to protect memory regions (mx_vmar_protect) takes a |
| 38 // VM area (vmar) handle as first argument. We call VirtualMemory::Protect() |
| 39 // from the memory freelist code in vm/freelist.cc where the vmar handle is not |
| 40 // available. Additionally, there is no mx_vmar system call to retrieve a handle |
| 41 // for the leaf vmar given an address. Thus, when memory protections are |
| 42 // enabled, we maintain a sorted list of our leaf vmar handles that we can |
| 43 // query by address in calls to VirtualMemory::Protect(). |
| 44 class VmarList : public AllStatic { |
| 45 public: |
| 46 static void AddVmar(mx_handle_t vmar, uword addr, intptr_t size); |
| 47 static void RemoveVmar(uword addr); |
| 48 static mx_handle_t LookupVmar(uword addr); |
| 49 |
| 50 private: |
| 51 static intptr_t LookupVmarIndexLocked(uword addr); |
| 52 |
| 53 struct VmarListElement { |
| 54 mx_handle_t vmar; |
| 55 uword addr; |
| 56 intptr_t size; |
| 57 }; |
| 58 |
| 59 static Mutex* vmar_array_lock_; |
| 60 static MallocGrowableArray<VmarListElement> vmar_array_; |
| 61 }; |
| 62 |
| 63 Mutex* VmarList::vmar_array_lock_ = new Mutex(); |
| 64 MallocGrowableArray<VmarList::VmarListElement> VmarList::vmar_array_; |
| 65 |
| 66 void VmarList::AddVmar(mx_handle_t vmar, uword addr, intptr_t size) { |
| 67 MutexLocker ml(vmar_array_lock_); |
| 68 LOG_INFO("AddVmar(%d, %lx, %ld)\n", vmar, addr, size); |
| 69 // Sorted insert in increasing order. |
| 70 const intptr_t length = vmar_array_.length(); |
| 71 intptr_t idx; |
| 72 for (idx = 0; idx < length; idx++) { |
| 73 const VmarListElement& m = vmar_array_.At(idx); |
| 74 if (m.addr >= addr) { |
| 75 break; |
| 76 } |
| 77 } |
| 78 #if defined(DEBUG) |
| 79 if ((length > 0) && (idx < (length - 1))) { |
| 80 const VmarListElement& m = vmar_array_.At(idx); |
| 81 ASSERT(m.addr != addr); |
| 82 } |
| 83 #endif |
| 84 LOG_INFO("AddVmar(%d, %lx, %ld) at index = %ld\n", vmar, addr, size, idx); |
| 85 VmarListElement new_mapping; |
| 86 new_mapping.vmar = vmar; |
| 87 new_mapping.addr = addr; |
| 88 new_mapping.size = size; |
| 89 vmar_array_.InsertAt(idx, new_mapping); |
| 90 } |
| 91 |
| 92 |
| 93 intptr_t VmarList::LookupVmarIndexLocked(uword addr) { |
| 94 // Binary search for the vmar containing addr. |
| 95 intptr_t imin = 0; |
| 96 intptr_t imax = vmar_array_.length(); |
| 97 while (imax >= imin) { |
| 98 const intptr_t imid = ((imax - imin) / 2) + imin; |
| 99 const VmarListElement& mapping = vmar_array_.At(imid); |
| 100 if ((mapping.addr + mapping.size) <= addr) { |
| 101 imin = imid + 1; |
| 102 } else if (mapping.addr > addr) { |
| 103 imax = imid - 1; |
| 104 } else { |
| 105 return imid; |
| 106 } |
| 107 } |
| 108 return -1; |
| 109 } |
| 110 |
| 111 |
| 112 mx_handle_t VmarList::LookupVmar(uword addr) { |
| 113 MutexLocker ml(vmar_array_lock_); |
| 114 LOG_INFO("LookupVmar(%lx)\n", addr); |
| 115 const intptr_t idx = LookupVmarIndexLocked(addr); |
| 116 if (idx == -1) { |
| 117 LOG_ERR("LookupVmar(%lx) NOT FOUND\n", addr); |
| 118 return MX_HANDLE_INVALID; |
| 119 } |
| 120 LOG_INFO("LookupVmar(%lx) found at %ld\n", addr, idx); |
| 121 return vmar_array_[idx].vmar; |
| 122 } |
| 123 |
| 124 |
| 125 void VmarList::RemoveVmar(uword addr) { |
| 126 MutexLocker ml(vmar_array_lock_); |
| 127 LOG_INFO("RemoveVmar(%lx)\n", addr); |
| 128 const intptr_t idx = LookupVmarIndexLocked(addr); |
| 129 ASSERT(idx != -1); |
| 130 #if defined(DEBUG) |
| 131 mx_handle_t vmar = vmar_array_[idx].vmar; |
| 132 #endif |
| 133 // Swap idx to the end, and then RemoveLast() |
| 134 const intptr_t length = vmar_array_.length(); |
| 135 for (intptr_t i = idx; i < length - 1; i++) { |
| 136 vmar_array_.Swap(i, i + 1); |
| 137 } |
| 138 #if defined(DEBUG) |
| 139 const VmarListElement& mapping = vmar_array_.Last(); |
| 140 ASSERT(mapping.vmar == vmar); |
| 141 #endif |
| 142 vmar_array_.RemoveLast(); |
| 143 } |
| 144 |
| 145 |
| 33 uword VirtualMemory::page_size_ = 0; | 146 uword VirtualMemory::page_size_ = 0; |
| 34 | 147 |
| 35 | 148 |
| 36 void VirtualMemory::InitOnce() { | 149 void VirtualMemory::InitOnce() { |
| 37 page_size_ = getpagesize(); | 150 page_size_ = getpagesize(); |
| 38 } | 151 } |
| 39 | 152 |
| 40 | 153 |
| 41 VirtualMemory* VirtualMemory::ReserveInternal(intptr_t size) { | 154 VirtualMemory* VirtualMemory::ReserveInternal(intptr_t size) { |
| 42 mx_handle_t vmar = MX_HANDLE_INVALID; | 155 mx_handle_t vmar = MX_HANDLE_INVALID; |
| 43 uword addr = 0; | 156 uword addr = 0; |
| 44 const uint32_t flags = MX_VM_FLAG_COMPACT | MX_VM_FLAG_CAN_MAP_SPECIFIC | | 157 const uint32_t flags = MX_VM_FLAG_COMPACT | MX_VM_FLAG_CAN_MAP_SPECIFIC | |
| 45 MX_VM_FLAG_CAN_MAP_READ | MX_VM_FLAG_CAN_MAP_WRITE | | 158 MX_VM_FLAG_CAN_MAP_READ | MX_VM_FLAG_CAN_MAP_WRITE | |
| 46 MX_VM_FLAG_CAN_MAP_EXECUTE; | 159 MX_VM_FLAG_CAN_MAP_EXECUTE; |
| 47 mx_status_t status = | 160 mx_status_t status = |
| 48 mx_vmar_allocate(mx_vmar_root_self(), 0, size, flags, &vmar, &addr); | 161 mx_vmar_allocate(mx_vmar_root_self(), 0, size, flags, &vmar, &addr); |
| 49 if (status != NO_ERROR) { | 162 if (status != NO_ERROR) { |
| 50 LOG_ERR("mx_vmar_allocate(size = %ld) failed: %s\n", size, | 163 LOG_ERR("mx_vmar_allocate(size = %ld) failed: %s\n", size, |
| 51 mx_status_get_string(status)); | 164 mx_status_get_string(status)); |
| 52 return NULL; | 165 return NULL; |
| 53 } | 166 } |
| 54 | 167 VmarList::AddVmar(vmar, addr, size); |
| 55 MemoryRegion region(reinterpret_cast<void*>(addr), size); | 168 MemoryRegion region(reinterpret_cast<void*>(addr), size); |
| 56 return new VirtualMemory(region, vmar); | 169 return new VirtualMemory(region, vmar); |
| 57 } | 170 } |
| 58 | 171 |
| 59 | 172 |
| 60 VirtualMemory::~VirtualMemory() { | 173 VirtualMemory::~VirtualMemory() { |
| 61 if (!embedder_allocated()) { | 174 if (!embedder_allocated()) { |
| 62 mx_handle_t vmar = static_cast<mx_handle_t>(handle()); | 175 mx_handle_t vmar = static_cast<mx_handle_t>(handle()); |
| 63 mx_status_t status = mx_vmar_destroy(vmar); | 176 mx_status_t status = mx_vmar_destroy(vmar); |
| 64 if (status != NO_ERROR) { | 177 if (status != NO_ERROR) { |
| 65 LOG_ERR("mx_vmar_destroy failed: %s\n", mx_status_get_string(status)); | 178 LOG_ERR("mx_vmar_destroy failed: %s\n", mx_status_get_string(status)); |
| 66 } | 179 } |
| 67 status = mx_handle_close(vmar); | 180 status = mx_handle_close(vmar); |
| 68 if (status != NO_ERROR) { | 181 if (status != NO_ERROR) { |
| 69 LOG_ERR("mx_handle_close failed: %s\n", mx_status_get_string(status)); | 182 LOG_ERR("mx_handle_close failed: %s\n", mx_status_get_string(status)); |
| 70 } | 183 } |
| 184 VmarList::RemoveVmar(start()); |
| 71 } | 185 } |
| 72 } | 186 } |
| 73 | 187 |
| 74 | 188 |
| 75 bool VirtualMemory::FreeSubSegment(int32_t handle, | 189 bool VirtualMemory::FreeSubSegment(int32_t handle, |
| 76 void* address, | 190 void* address, |
| 77 intptr_t size) { | 191 intptr_t size) { |
| 78 mx_handle_t vmar = static_cast<mx_handle_t>(handle); | 192 mx_handle_t vmar = static_cast<mx_handle_t>(handle); |
| 79 mx_status_t status = | 193 mx_status_t status = |
| 80 mx_vmar_unmap(vmar, reinterpret_cast<uintptr_t>(address), size); | 194 mx_vmar_unmap(vmar, reinterpret_cast<uintptr_t>(address), size); |
| (...skipping 12 matching lines...) Expand all Loading... |
| 93 mx_status_t status = mx_vmo_create(size, 0u, &vmo); | 207 mx_status_t status = mx_vmo_create(size, 0u, &vmo); |
| 94 if (status != NO_ERROR) { | 208 if (status != NO_ERROR) { |
| 95 LOG_ERR("mx_vmo_create(%ld) failed: %s\n", size, | 209 LOG_ERR("mx_vmo_create(%ld) failed: %s\n", size, |
| 96 mx_status_get_string(status)); | 210 mx_status_get_string(status)); |
| 97 return false; | 211 return false; |
| 98 } | 212 } |
| 99 | 213 |
| 100 mx_handle_t vmar = static_cast<mx_handle_t>(handle()); | 214 mx_handle_t vmar = static_cast<mx_handle_t>(handle()); |
| 101 const size_t offset = addr - start(); | 215 const size_t offset = addr - start(); |
| 102 const uint32_t flags = MX_VM_FLAG_SPECIFIC | MX_VM_FLAG_PERM_READ | | 216 const uint32_t flags = MX_VM_FLAG_SPECIFIC | MX_VM_FLAG_PERM_READ | |
| 103 MX_VM_FLAG_PERM_WRITE | MX_VM_FLAG_PERM_EXECUTE; | 217 MX_VM_FLAG_PERM_WRITE | |
| 218 (executable ? MX_VM_FLAG_PERM_EXECUTE : 0); |
| 104 uintptr_t mapped_addr; | 219 uintptr_t mapped_addr; |
| 105 status = mx_vmar_map(vmar, offset, vmo, 0, size, flags, &mapped_addr); | 220 status = mx_vmar_map(vmar, offset, vmo, 0, size, flags, &mapped_addr); |
| 106 if (status != NO_ERROR) { | 221 if (status != NO_ERROR) { |
| 107 mx_handle_close(vmo); | 222 mx_handle_close(vmo); |
| 108 LOG_ERR("mx_vmar_map(%ld, %ld, %u) failed: %s\n", offset, size, flags, | 223 LOG_ERR("mx_vmar_map(%ld, %ld, %u) failed: %s\n", offset, size, flags, |
| 109 mx_status_get_string(status)); | 224 mx_status_get_string(status)); |
| 110 return false; | 225 return false; |
| 111 } | 226 } |
| 112 if (addr != mapped_addr) { | 227 if (addr != mapped_addr) { |
| 228 mx_handle_close(vmo); |
| 113 LOG_ERR("mx_vmar_map: addr != mapped_addr: %lx != %lx\n", addr, | 229 LOG_ERR("mx_vmar_map: addr != mapped_addr: %lx != %lx\n", addr, |
| 114 mapped_addr); | 230 mapped_addr); |
| 115 return false; | 231 return false; |
| 116 } | 232 } |
| 233 mx_handle_close(vmo); |
| 234 LOG_INFO("Commit(%lx, %ld, %s): success\n", addr, size, |
| 235 executable ? "executable" : ""); |
| 117 return true; | 236 return true; |
| 118 } | 237 } |
| 119 | 238 |
| 120 | 239 |
| 121 bool VirtualMemory::Protect(void* address, intptr_t size, Protection mode) { | 240 bool VirtualMemory::Protect(void* address, intptr_t size, Protection mode) { |
| 241 ASSERT(Thread::Current()->IsMutatorThread() || |
| 242 Isolate::Current()->mutator_thread()->IsAtSafepoint()); |
| 243 const uword start_address = reinterpret_cast<uword>(address); |
| 244 const uword end_address = start_address + size; |
| 245 const uword page_address = Utils::RoundDown(start_address, PageSize()); |
| 246 mx_handle_t vmar = VmarList::LookupVmar(page_address); |
| 247 ASSERT(vmar != MX_HANDLE_INVALID); |
| 248 uint32_t prot = 0; |
| 249 switch (mode) { |
| 250 case kNoAccess: |
| 251 // MG-426: mx_vmar_protect() requires at least on permission. |
| 252 prot = MX_VM_FLAG_PERM_READ; |
| 253 break; |
| 254 case kReadOnly: |
| 255 prot = MX_VM_FLAG_PERM_READ; |
| 256 break; |
| 257 case kReadWrite: |
| 258 prot = MX_VM_FLAG_PERM_READ | MX_VM_FLAG_PERM_WRITE; |
| 259 break; |
| 260 case kReadExecute: |
| 261 prot = MX_VM_FLAG_PERM_READ | MX_VM_FLAG_PERM_EXECUTE; |
| 262 break; |
| 263 case kReadWriteExecute: |
| 264 prot = MX_VM_FLAG_PERM_READ | MX_VM_FLAG_PERM_WRITE | |
| 265 MX_VM_FLAG_PERM_EXECUTE; |
| 266 break; |
| 267 } |
| 268 mx_status_t status = |
| 269 mx_vmar_protect(vmar, page_address, end_address - page_address, prot); |
| 270 if (status != NO_ERROR) { |
| 271 LOG_ERR("mx_vmar_protect(%lx, %lx, %x) success: %s\n", page_address, |
| 272 end_address - page_address, prot, mx_status_get_string(status)); |
| 273 return false; |
| 274 } |
| 275 LOG_INFO("mx_vmar_protect(%lx, %lx, %x) success\n", page_address, |
| 276 end_address - page_address, prot); |
| 122 return true; | 277 return true; |
| 123 } | 278 } |
| 124 | 279 |
| 125 } // namespace dart | 280 } // namespace dart |
| 126 | 281 |
| 127 #endif // defined(TARGET_OS_FUCHSIA) | 282 #endif // defined(TARGET_OS_FUCHSIA) |
| OLD | NEW |