• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 //
5 // This file implements memory allocation primitives for PageAllocator using
6 // Fuchsia's VMOs (Virtual Memory Objects). VMO API is documented in
7 // https://fuchsia.dev/fuchsia-src/zircon/objects/vm_object . A VMO is a kernel
8 // object that corresponds to a set of memory pages. VMO pages may be mapped
9 // to an address space. The code below creates VMOs for each memory allocations
10 // and maps them to the default address space of the current process.
11 
12 #ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNALS_FUCHSIA_H_
13 #define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNALS_FUCHSIA_H_
14 
15 #include <lib/zx/vmar.h>
16 #include <lib/zx/vmo.h>
17 
18 #include <cstdint>
19 
20 #include "base/allocator/partition_allocator/page_allocator.h"
21 #include "base/allocator/partition_allocator/partition_alloc_base/fuchsia/fuchsia_logging.h"
22 #include "base/allocator/partition_allocator/partition_alloc_check.h"
23 #include "base/allocator/partition_allocator/partition_alloc_notreached.h"
24 
25 namespace partition_alloc::internal {
26 
27 namespace {
28 
29 // Returns VMO name for a PageTag.
PageTagToName(PageTag tag)30 const char* PageTagToName(PageTag tag) {
31   switch (tag) {
32     case PageTag::kBlinkGC:
33       return "cr_blink_gc";
34     case PageTag::kPartitionAlloc:
35       return "cr_partition_alloc";
36     case PageTag::kChromium:
37       return "cr_chromium";
38     case PageTag::kV8:
39       return "cr_v8";
40     default:
41       PA_DCHECK(false);
42       return "";
43   }
44 }
45 
PageAccessibilityToZxVmOptions(PageAccessibilityConfiguration accessibility)46 zx_vm_option_t PageAccessibilityToZxVmOptions(
47     PageAccessibilityConfiguration accessibility) {
48   switch (accessibility.permissions) {
49     case PageAccessibilityConfiguration::kRead:
50       return ZX_VM_PERM_READ;
51     case PageAccessibilityConfiguration::kReadWrite:
52     case PageAccessibilityConfiguration::kReadWriteTagged:
53       return ZX_VM_PERM_READ | ZX_VM_PERM_WRITE;
54     case PageAccessibilityConfiguration::kReadExecuteProtected:
55     case PageAccessibilityConfiguration::kReadExecute:
56       return ZX_VM_PERM_READ | ZX_VM_PERM_EXECUTE;
57     case PageAccessibilityConfiguration::kReadWriteExecute:
58       return ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_PERM_EXECUTE;
59     default:
60       PA_NOTREACHED();
61       [[fallthrough]];
62     case PageAccessibilityConfiguration::kInaccessible:
63       return 0;
64   }
65 }
66 
67 }  // namespace
68 
69 // zx_vmar_map() will fail if the VMO cannot be mapped at |vmar_offset|, i.e.
70 // |hint| is not advisory.
71 constexpr bool kHintIsAdvisory = false;
72 
73 std::atomic<int32_t> s_allocPageErrorCode{0};
74 
SystemAllocPagesInternal(uintptr_t hint,size_t length,PageAccessibilityConfiguration accessibility,PageTag page_tag,int file_descriptor_for_shared_alloc)75 uintptr_t SystemAllocPagesInternal(
76     uintptr_t hint,
77     size_t length,
78     PageAccessibilityConfiguration accessibility,
79     PageTag page_tag,
80     [[maybe_unused]] int file_descriptor_for_shared_alloc) {
81   zx::vmo vmo;
82   zx_status_t status = zx::vmo::create(length, 0, &vmo);
83   if (status != ZX_OK) {
84     PA_ZX_DLOG(INFO, status) << "zx_vmo_create";
85     return 0;
86   }
87 
88   const char* vmo_name = PageTagToName(page_tag);
89   status = vmo.set_property(ZX_PROP_NAME, vmo_name, strlen(vmo_name));
90 
91   // VMO names are used only for debugging, so failure to set a name is not
92   // fatal.
93   PA_ZX_DCHECK(status == ZX_OK, status);
94 
95   if (page_tag == PageTag::kV8) {
96     // V8 uses JIT. Call zx_vmo_replace_as_executable() to allow code execution
97     // in the new VMO.
98     status = vmo.replace_as_executable(zx::resource(), &vmo);
99     if (status != ZX_OK) {
100       PA_ZX_DLOG(INFO, status) << "zx_vmo_replace_as_executable";
101       return 0;
102     }
103   }
104 
105   zx_vm_option_t options = PageAccessibilityToZxVmOptions(accessibility);
106 
107   uint64_t vmar_offset = 0;
108   if (hint) {
109     vmar_offset = hint;
110     options |= ZX_VM_SPECIFIC;
111   }
112 
113   uint64_t address;
114   status = zx::vmar::root_self()->map(options, vmar_offset, vmo,
115                                       /*vmo_offset=*/0, length, &address);
116   if (status != ZX_OK) {
117     // map() is expected to fail if |hint| is set to an already-in-use location.
118     if (!hint) {
119       PA_ZX_DLOG(ERROR, status) << "zx_vmar_map";
120     }
121     return 0;
122   }
123 
124   return address;
125 }
126 
TrimMappingInternal(uintptr_t base_address,size_t base_length,size_t trim_length,PageAccessibilityConfiguration accessibility,size_t pre_slack,size_t post_slack)127 uintptr_t TrimMappingInternal(uintptr_t base_address,
128                               size_t base_length,
129                               size_t trim_length,
130                               PageAccessibilityConfiguration accessibility,
131                               size_t pre_slack,
132                               size_t post_slack) {
133   PA_DCHECK(base_length == trim_length + pre_slack + post_slack);
134 
135   // Unmap head if necessary.
136   if (pre_slack) {
137     zx_status_t status = zx::vmar::root_self()->unmap(base_address, pre_slack);
138     PA_ZX_CHECK(status == ZX_OK, status);
139   }
140 
141   // Unmap tail if necessary.
142   if (post_slack) {
143     zx_status_t status = zx::vmar::root_self()->unmap(
144         base_address + pre_slack + trim_length, post_slack);
145     PA_ZX_CHECK(status == ZX_OK, status);
146   }
147 
148   return base_address + pre_slack;
149 }
150 
TrySetSystemPagesAccessInternal(uint64_t address,size_t length,PageAccessibilityConfiguration accessibility)151 bool TrySetSystemPagesAccessInternal(
152     uint64_t address,
153     size_t length,
154     PageAccessibilityConfiguration accessibility) {
155   zx_status_t status = zx::vmar::root_self()->protect(
156       PageAccessibilityToZxVmOptions(accessibility), address, length);
157   return status == ZX_OK;
158 }
159 
SetSystemPagesAccessInternal(uint64_t address,size_t length,PageAccessibilityConfiguration accessibility)160 void SetSystemPagesAccessInternal(
161     uint64_t address,
162     size_t length,
163     PageAccessibilityConfiguration accessibility) {
164   zx_status_t status = zx::vmar::root_self()->protect(
165       PageAccessibilityToZxVmOptions(accessibility), address, length);
166   PA_ZX_CHECK(status == ZX_OK, status);
167 }
168 
FreePagesInternal(uint64_t address,size_t length)169 void FreePagesInternal(uint64_t address, size_t length) {
170   zx_status_t status = zx::vmar::root_self()->unmap(address, length);
171   PA_ZX_CHECK(status == ZX_OK, status);
172 }
173 
DiscardSystemPagesInternal(uint64_t address,size_t length)174 void DiscardSystemPagesInternal(uint64_t address, size_t length) {
175   zx_status_t status = zx::vmar::root_self()->op_range(
176       ZX_VMO_OP_DECOMMIT, address, length, nullptr, 0);
177   PA_ZX_CHECK(status == ZX_OK, status);
178 }
179 
DecommitSystemPagesInternal(uint64_t address,size_t length,PageAccessibilityDisposition accessibility_disposition)180 void DecommitSystemPagesInternal(
181     uint64_t address,
182     size_t length,
183     PageAccessibilityDisposition accessibility_disposition) {
184   if (accessibility_disposition ==
185       PageAccessibilityDisposition::kRequireUpdate) {
186     SetSystemPagesAccess(address, length,
187                          PageAccessibilityConfiguration(
188                              PageAccessibilityConfiguration::kInaccessible));
189   }
190 
191   DiscardSystemPagesInternal(address, length);
192 }
193 
DecommitAndZeroSystemPagesInternal(uintptr_t address,size_t length)194 void DecommitAndZeroSystemPagesInternal(uintptr_t address, size_t length) {
195   SetSystemPagesAccess(address, length,
196                        PageAccessibilityConfiguration(
197                            PageAccessibilityConfiguration::kInaccessible));
198 
199   DiscardSystemPagesInternal(address, length);
200 }
201 
RecommitSystemPagesInternal(uintptr_t address,size_t length,PageAccessibilityConfiguration accessibility,PageAccessibilityDisposition accessibility_disposition)202 void RecommitSystemPagesInternal(
203     uintptr_t address,
204     size_t length,
205     PageAccessibilityConfiguration accessibility,
206     PageAccessibilityDisposition accessibility_disposition) {
207   // On Fuchsia systems, the caller needs to simply read the memory to recommit
208   // it. However, if decommit changed the permissions, recommit has to change
209   // them back.
210   if (accessibility_disposition ==
211       PageAccessibilityDisposition::kRequireUpdate) {
212     SetSystemPagesAccess(address, length, accessibility);
213   }
214 }
215 
TryRecommitSystemPagesInternal(uintptr_t address,size_t length,PageAccessibilityConfiguration accessibility,PageAccessibilityDisposition accessibility_disposition)216 bool TryRecommitSystemPagesInternal(
217     uintptr_t address,
218     size_t length,
219     PageAccessibilityConfiguration accessibility,
220     PageAccessibilityDisposition accessibility_disposition) {
221   // On Fuchsia systems, the caller needs to simply read the memory to recommit
222   // it. However, if decommit changed the permissions, recommit has to change
223   // them back.
224   if (accessibility_disposition ==
225       PageAccessibilityDisposition::kRequireUpdate) {
226     return TrySetSystemPagesAccess(address, length, accessibility);
227   }
228   return true;
229 }
230 
231 }  // namespace partition_alloc::internal
232 
233 #endif  // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNALS_FUCHSIA_H_
234