[Clang][SME2] Enable multi-vector loads & stores for SME2 (#75821)
[llvm-project.git] / compiler-rt / lib / scudo / standalone / mem_map_fuchsia.cpp
blob0566ab0655263ee55552fcfb4b7131ab66d1769b
1 //===-- mem_map_fuchsia.cpp -------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
9 #include "mem_map_fuchsia.h"
11 #include "atomic_helpers.h"
12 #include "common.h"
13 #include "string_utils.h"
15 #if SCUDO_FUCHSIA
17 #include <zircon/process.h>
18 #include <zircon/status.h>
19 #include <zircon/syscalls.h>
21 namespace scudo {
23 static void NORETURN dieOnError(zx_status_t Status, const char *FnName,
24 uptr Size) {
25 char Error[128];
26 formatString(Error, sizeof(Error),
27 "SCUDO ERROR: %s failed with size %zuKB (%s)", FnName,
28 Size >> 10, _zx_status_get_string(Status));
29 outputRaw(Error);
30 die();
33 static void setVmoName(zx_handle_t Vmo, const char *Name) {
34 size_t Len = strlen(Name);
35 DCHECK_LT(Len, ZX_MAX_NAME_LEN);
36 zx_status_t Status = _zx_object_set_property(Vmo, ZX_PROP_NAME, Name, Len);
37 CHECK_EQ(Status, ZX_OK);
40 // Returns the (cached) base address of the root VMAR.
41 static uptr getRootVmarBase() {
42 static atomic_uptr CachedResult = {0};
44 uptr Result = atomic_load(&CachedResult, memory_order_acquire);
45 if (UNLIKELY(!Result)) {
46 zx_info_vmar_t VmarInfo;
47 zx_status_t Status =
48 _zx_object_get_info(_zx_vmar_root_self(), ZX_INFO_VMAR, &VmarInfo,
49 sizeof(VmarInfo), nullptr, nullptr);
50 CHECK_EQ(Status, ZX_OK);
51 CHECK_NE(VmarInfo.base, 0);
53 atomic_store(&CachedResult, VmarInfo.base, memory_order_release);
54 Result = VmarInfo.base;
57 return Result;
60 // Lazily creates and then always returns the same zero-sized VMO.
61 static zx_handle_t getPlaceholderVmo() {
62 static atomic_u32 StoredVmo = {ZX_HANDLE_INVALID};
64 zx_handle_t Vmo = atomic_load(&StoredVmo, memory_order_acquire);
65 if (UNLIKELY(Vmo == ZX_HANDLE_INVALID)) {
66 // Create a zero-sized placeholder VMO.
67 zx_status_t Status = _zx_vmo_create(0, 0, &Vmo);
68 if (UNLIKELY(Status != ZX_OK))
69 dieOnError(Status, "zx_vmo_create", 0);
71 setVmoName(Vmo, "scudo:reserved");
73 // Atomically store its handle. If some other thread wins the race, use its
74 // handle and discard ours.
75 zx_handle_t OldValue = atomic_compare_exchange_strong(
76 &StoredVmo, ZX_HANDLE_INVALID, Vmo, memory_order_acq_rel);
77 if (UNLIKELY(OldValue != ZX_HANDLE_INVALID)) {
78 Status = _zx_handle_close(Vmo);
79 CHECK_EQ(Status, ZX_OK);
81 Vmo = OldValue;
85 return Vmo;
88 MemMapFuchsia::MemMapFuchsia(uptr Base, uptr Capacity)
89 : MapAddr(Base), WindowBase(Base), WindowSize(Capacity) {
90 // Create the VMO.
91 zx_status_t Status = _zx_vmo_create(Capacity, 0, &Vmo);
92 if (UNLIKELY(Status != ZX_OK))
93 dieOnError(Status, "zx_vmo_create", Capacity);
96 bool MemMapFuchsia::mapImpl(UNUSED uptr Addr, uptr Size, const char *Name,
97 uptr Flags) {
98 const bool AllowNoMem = !!(Flags & MAP_ALLOWNOMEM);
99 const bool PreCommit = !!(Flags & MAP_PRECOMMIT);
100 const bool NoAccess = !!(Flags & MAP_NOACCESS);
102 // Create the VMO.
103 zx_status_t Status = _zx_vmo_create(Size, 0, &Vmo);
104 if (UNLIKELY(Status != ZX_OK)) {
105 if (Status != ZX_ERR_NO_MEMORY || !AllowNoMem)
106 dieOnError(Status, "zx_vmo_create", Size);
107 return false;
110 if (Name != nullptr)
111 setVmoName(Vmo, Name);
113 // Map it.
114 zx_vm_option_t MapFlags = ZX_VM_ALLOW_FAULTS;
115 if (!NoAccess)
116 MapFlags |= ZX_VM_PERM_READ | ZX_VM_PERM_WRITE;
117 Status =
118 _zx_vmar_map(_zx_vmar_root_self(), MapFlags, 0, Vmo, 0, Size, &MapAddr);
119 if (UNLIKELY(Status != ZX_OK)) {
120 if (Status != ZX_ERR_NO_MEMORY || !AllowNoMem)
121 dieOnError(Status, "zx_vmar_map", Size);
123 Status = _zx_handle_close(Vmo);
124 CHECK_EQ(Status, ZX_OK);
126 MapAddr = 0;
127 Vmo = ZX_HANDLE_INVALID;
128 return false;
131 if (PreCommit) {
132 Status = _zx_vmar_op_range(_zx_vmar_root_self(), ZX_VMAR_OP_COMMIT, MapAddr,
133 Size, nullptr, 0);
134 CHECK_EQ(Status, ZX_OK);
137 WindowBase = MapAddr;
138 WindowSize = Size;
139 return true;
142 void MemMapFuchsia::unmapImpl(uptr Addr, uptr Size) {
143 zx_status_t Status;
145 if (Size == WindowSize) {
146 // NOTE: Closing first and then unmapping seems slightly faster than doing
147 // the same operations in the opposite order.
148 Status = _zx_handle_close(Vmo);
149 CHECK_EQ(Status, ZX_OK);
150 Status = _zx_vmar_unmap(_zx_vmar_root_self(), Addr, Size);
151 CHECK_EQ(Status, ZX_OK);
153 MapAddr = WindowBase = WindowSize = 0;
154 Vmo = ZX_HANDLE_INVALID;
155 } else {
156 // Unmap the subrange.
157 Status = _zx_vmar_unmap(_zx_vmar_root_self(), Addr, Size);
158 CHECK_EQ(Status, ZX_OK);
160 // Decommit the pages that we just unmapped.
161 Status = _zx_vmo_op_range(Vmo, ZX_VMO_OP_DECOMMIT, Addr - MapAddr, Size,
162 nullptr, 0);
163 CHECK_EQ(Status, ZX_OK);
165 if (Addr == WindowBase)
166 WindowBase += Size;
167 WindowSize -= Size;
171 bool MemMapFuchsia::remapImpl(uptr Addr, uptr Size, const char *Name,
172 uptr Flags) {
173 const bool AllowNoMem = !!(Flags & MAP_ALLOWNOMEM);
174 const bool PreCommit = !!(Flags & MAP_PRECOMMIT);
175 const bool NoAccess = !!(Flags & MAP_NOACCESS);
177 // NOTE: This will rename the *whole* VMO, not only the requested portion of
178 // it. But we cannot do better than this given the MemMap API. In practice,
179 // the upper layers of Scudo always pass the same Name for a given MemMap.
180 if (Name != nullptr)
181 setVmoName(Vmo, Name);
183 uptr MappedAddr;
184 zx_vm_option_t MapFlags = ZX_VM_ALLOW_FAULTS | ZX_VM_SPECIFIC_OVERWRITE;
185 if (!NoAccess)
186 MapFlags |= ZX_VM_PERM_READ | ZX_VM_PERM_WRITE;
187 zx_status_t Status =
188 _zx_vmar_map(_zx_vmar_root_self(), MapFlags, Addr - getRootVmarBase(),
189 Vmo, Addr - MapAddr, Size, &MappedAddr);
190 if (UNLIKELY(Status != ZX_OK)) {
191 if (Status != ZX_ERR_NO_MEMORY || !AllowNoMem)
192 dieOnError(Status, "zx_vmar_map", Size);
193 return false;
195 DCHECK_EQ(Addr, MappedAddr);
197 if (PreCommit) {
198 Status = _zx_vmar_op_range(_zx_vmar_root_self(), ZX_VMAR_OP_COMMIT, MapAddr,
199 Size, nullptr, 0);
200 CHECK_EQ(Status, ZX_OK);
203 return true;
206 void MemMapFuchsia::releaseAndZeroPagesToOSImpl(uptr From, uptr Size) {
207 zx_status_t Status = _zx_vmo_op_range(Vmo, ZX_VMO_OP_DECOMMIT, From - MapAddr,
208 Size, nullptr, 0);
209 CHECK_EQ(Status, ZX_OK);
212 void MemMapFuchsia::setMemoryPermissionImpl(uptr Addr, uptr Size, uptr Flags) {
213 const bool NoAccess = !!(Flags & MAP_NOACCESS);
215 zx_vm_option_t MapFlags = 0;
216 if (!NoAccess)
217 MapFlags |= ZX_VM_PERM_READ | ZX_VM_PERM_WRITE;
218 zx_status_t Status =
219 _zx_vmar_protect(_zx_vmar_root_self(), MapFlags, Addr, Size);
220 CHECK_EQ(Status, ZX_OK);
223 bool ReservedMemoryFuchsia::createImpl(UNUSED uptr Addr, uptr Size,
224 UNUSED const char *Name, uptr Flags) {
225 const bool AllowNoMem = !!(Flags & MAP_ALLOWNOMEM);
227 // Reserve memory by mapping the placeholder VMO without any permission.
228 zx_status_t Status = _zx_vmar_map(_zx_vmar_root_self(), ZX_VM_ALLOW_FAULTS, 0,
229 getPlaceholderVmo(), 0, Size, &Base);
230 if (UNLIKELY(Status != ZX_OK)) {
231 if (Status != ZX_ERR_NO_MEMORY || !AllowNoMem)
232 dieOnError(Status, "zx_vmar_map", Size);
233 return false;
236 Capacity = Size;
237 return true;
240 void ReservedMemoryFuchsia::releaseImpl() {
241 zx_status_t Status = _zx_vmar_unmap(_zx_vmar_root_self(), Base, Capacity);
242 CHECK_EQ(Status, ZX_OK);
245 ReservedMemoryFuchsia::MemMapT ReservedMemoryFuchsia::dispatchImpl(uptr Addr,
246 uptr Size) {
247 return ReservedMemoryFuchsia::MemMapT(Addr, Size);
250 } // namespace scudo
252 #endif // SCUDO_FUCHSIA