1 /*
2 * Copyright (c) 2020 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28 /**
29 * This header file stores the types, and prototypes used strictly by the pmap
30 * itself. The public pmap API exported to the rest of the kernel should be
31 * located in osfmk/arm64/sptm/pmap/pmap.h.
32 *
33 * This file will automatically include all of the other internal arm/pmap/
34 * headers so .c files will only need to include this one header.
35 */
36 #pragma once
37
38 #include <stdint.h>
39
40 #include <kern/debug.h>
41 #include <kern/locks.h>
42 #include <mach/vm_types.h>
43 #include <mach_assert.h>
44
45 #include <arm/cpu_data.h>
46 #include <arm64/proc_reg.h>
47 #include <arm64/sptm/sptm.h>
48
49 /**
50 * arm64/sptm/pmap/pmap.h and the other /arm/pmap/ internal header files are safe to be
51 * included in this file since they shouldn't rely on any of the internal pmap
52 * header files (so no circular dependencies). Implementation files will only
53 * need to include this one header to get all of the relevant pmap types.
54 */
55 #include <arm64/sptm/pmap/pmap.h>
56 #include <arm64/sptm/pmap/pmap_data.h>
57 #include <arm64/sptm/pmap/pmap_pt_geometry.h>
58
59 #define PMAP_SUPPORT_PROTOTYPES(__return_type, __function_name, __function_args, __function_index) \
60 extern __return_type __function_name##_internal __function_args
61
62 /**
63 * Global variables exported to the rest of the internal pmap implementation.
64 */
65 extern lck_grp_t pmap_lck_grp;
66 extern pmap_paddr_t avail_start;
67 extern pmap_paddr_t avail_end;
68 extern uint32_t pmap_max_asids;
69
70 /**
71 * SPTM TODO: The following flag is set up based on the presence and
72 * configuration of the 'sptm-stability-hacks' boot-arg; this
73 * is used in certain codepaths that do not properly function
74 * today in SPTM systems to make the system more stable and fully
75 * able to boot to user space.
76 */
77 extern bool sptm_stability_hacks;
78
79 /**
80 * Functions exported to the rest of the internal pmap implementation.
81 */
82
83 extern void pmap_remove_range_options(
84 pmap_t, vm_map_address_t, vm_map_address_t, int);
85
86 #if defined(PVH_FLAG_EXEC)
87 extern void pmap_set_ptov_ap(unsigned int, unsigned int, boolean_t);
88 #endif /* defined(PVH_FLAG_EXEC) */
89
90 extern pmap_t current_pmap(void);
91 extern void pmap_tt_ledger_credit(pmap_t, vm_size_t);
92 extern void pmap_tt_ledger_debit(pmap_t, vm_size_t);
93
94 extern void write_pte(pt_entry_t *, pt_entry_t);
95
96 /**
97 * The qsort function is used by various parts of the pmap but doesn't contain
98 * its own header file with prototype so it must be manually extern'd.
99 *
100 * The `cmpfunc_t` type is a pointer to a function that should return the
101 * following:
102 *
103 * return < 0 for a < b
104 * 0 for a == b
105 * > 0 for a > b
106 */
107 typedef int (*cmpfunc_t)(const void *a, const void *b);
108 extern void qsort(void *a, size_t n, size_t es, cmpfunc_t cmp);
109
110 /**
111 * Inline and macro functions exported for usage by other pmap modules.
112 *
113 * In an effort to not cause any performance regressions while breaking up the
114 * pmap, I'm keeping all functions originally marked as "static inline", as
115 * inline and moving them into header files to be shared across the pmap
116 * modules. In reality, many of these functions probably don't need to be inline
117 * and can be moved back into a .c file.
118 *
119 * TODO: rdar://70538514 (PMAP Cleanup: re-evaluate whether inline functions should actually be inline)
120 */
121
122 /* Helper macro for rounding an address up to a correctly aligned value. */
123 #define PMAP_ALIGN(addr, align) ((addr) + ((align) - 1) & ~((align) - 1))
124
125 /**
126 * pmap_data.h must be included before this point so that pmap_lock_mode_t is
127 * defined before the rest of the locking code.
128 */
129
130 /**
131 * Initialize a pmap object's reader/writer lock.
132 *
133 * @param pmap The pmap whose lock to initialize.
134 */
135 static inline void
pmap_lock_init(pmap_t pmap)136 pmap_lock_init(pmap_t pmap)
137 {
138 lck_rw_init(&pmap->rwlock, &pmap_lck_grp, 0);
139 }
140
141 /**
142 * Destroy a pmap object's reader/writer lock.
143 *
144 * @param pmap The pmap whose lock to destroy.
145 */
146 static inline void
pmap_lock_destroy(pmap_t pmap)147 pmap_lock_destroy(pmap_t pmap)
148 {
149 lck_rw_destroy(&pmap->rwlock, &pmap_lck_grp);
150 }
151
152 /**
153 * Initialize a pmap object's TXM reader/writer lock.
154 *
155 * @param pmap The pmap whose TXM lock to initialize.
156 */
157 static inline void
pmap_txmlock_init(pmap_t pmap)158 pmap_txmlock_init(pmap_t pmap)
159 {
160 lck_rw_init(&pmap->txm_lck, &pmap_lck_grp, 0);
161 }
162
163 /**
164 * Destroy a pmap object's TXM reader/writer lock.
165 *
166 * @param pmap The pmap whose TXM lock to destroy.
167 */
168 static inline void
pmap_txmlock_destroy(pmap_t pmap)169 pmap_txmlock_destroy(pmap_t pmap)
170 {
171 lck_rw_destroy(&pmap->txm_lck, &pmap_lck_grp);
172 }
173
174 /**
175 * Assert that the pmap lock is held in the given mode.
176 *
177 * @note See pmap_lock() below for an explanation of the special handling
178 * we do for kernel_pmap.
179 *
180 * @param pmap The pmap whose lock to assert is being held.
181 * @param mode The mode the lock should be held in.
182 */
183 static inline void
pmap_assert_locked(__unused pmap_t pmap,__unused pmap_lock_mode_t mode)184 pmap_assert_locked(__unused pmap_t pmap, __unused pmap_lock_mode_t mode)
185 {
186 #if MACH_ASSERT
187 if (pmap == kernel_pmap) {
188 return;
189 }
190 if (__improbable(sptm_stability_hacks)) {
191 mode = PMAP_LOCK_EXCLUSIVE;
192 }
193
194 switch (mode) {
195 case PMAP_LOCK_SHARED:
196 LCK_RW_ASSERT(&pmap->rwlock, LCK_RW_ASSERT_SHARED);
197 break;
198 case PMAP_LOCK_EXCLUSIVE:
199 LCK_RW_ASSERT(&pmap->rwlock, LCK_RW_ASSERT_EXCLUSIVE);
200 break;
201 case PMAP_LOCK_HELD:
202 LCK_RW_ASSERT(&pmap->rwlock, LCK_RW_ASSERT_HELD);
203 break;
204 default:
205 panic("%s: Unknown pmap_lock_mode. pmap=%p, mode=%d", __FUNCTION__, pmap, mode);
206 }
207 #endif
208 }
209
210 /**
211 * Assert that the pmap lock is held in any mode.
212 *
213 * @param pmap The pmap whose lock should be held.
214 */
215 __unused static inline void
pmap_assert_locked_any(__unused pmap_t pmap)216 pmap_assert_locked_any(__unused pmap_t pmap)
217 {
218 if (pmap != kernel_pmap) {
219 LCK_RW_ASSERT(&pmap->rwlock, LCK_RW_ASSERT_HELD);
220 }
221 }
222
223 /**
224 * Acquire a pmap object's reader/writer lock as either shared (read-only) or
225 * exclusive (read/write).
226 *
227 * @note If this function is called to request acquisition of the kernel pmap
228 * lock, the lock will not be acquired as a performance optimization. See the
229 * the explanation in the function body for why this is safe to do.
230 *
231 * @param pmap The pmap whose lock to acquire.
232 * @param mode Whether to grab the lock as shared (read-only) or exclusive (read/write).
233 */
234 static inline void
pmap_lock(pmap_t pmap,pmap_lock_mode_t mode)235 pmap_lock(pmap_t pmap, pmap_lock_mode_t mode)
236 {
237 /**
238 * The pmap lock is only held exclusive for removal of a leaf-level
239 * page table during pmap_remove(), to prevent concurrent mapping
240 * into the to-be-deleted table.
241 * The kernel pmap does not participate in the above, as table
242 * removal is only done for user pmaps.
243 * Since the kernel pmap never requires exclusive locking, it's
244 * also pointless to use shared locking and we can therefore elide
245 * any acquisition of the kernel pmap lock.
246 */
247 if (pmap == kernel_pmap) {
248 return;
249 }
250 if (__improbable(sptm_stability_hacks)) {
251 mode = PMAP_LOCK_EXCLUSIVE;
252 }
253
254 switch (mode) {
255 case PMAP_LOCK_SHARED:
256 lck_rw_lock_shared(&pmap->rwlock);
257 break;
258 case PMAP_LOCK_EXCLUSIVE:
259 lck_rw_lock_exclusive(&pmap->rwlock);
260 break;
261 default:
262 panic("%s: Unknown pmap_lock_mode. pmap=%p, mode=%d", __func__, pmap, mode);
263 }
264 }
265
266 /**
267 * Attempt to acquire the pmap lock in the specified mode. If the lock couldn't
268 * be acquired, then return immediately instead of spinning.
269 *
270 * @param pmap The pmap whose lock to attempt to acquire.
271 * @param mode Whether to grab the lock as shared (read-only) or exclusive (read/write).
272 *
273 * @return True if the lock was acquired, false otherwise.
274 */
275 static inline bool
pmap_try_lock(pmap_t pmap,pmap_lock_mode_t mode)276 pmap_try_lock(pmap_t pmap, pmap_lock_mode_t mode)
277 {
278 if (pmap == kernel_pmap) {
279 return true;
280 }
281 bool ret = false;
282
283 if (__improbable(sptm_stability_hacks)) {
284 mode = PMAP_LOCK_EXCLUSIVE;
285 }
286
287 switch (mode) {
288 case PMAP_LOCK_SHARED:
289 ret = lck_rw_try_lock_shared(&pmap->rwlock);
290 break;
291 case PMAP_LOCK_EXCLUSIVE:
292 ret = lck_rw_try_lock_exclusive(&pmap->rwlock);
293 break;
294 default:
295 panic("%s: Unknown pmap_lock_mode. pmap=%p, mode=%d", __func__, pmap, mode);
296 }
297
298 return ret;
299 }
300
301 /**
302 * Attempts to promote an already acquired pmap lock from shared to exclusive.
303 *
304 * @param pmap The pmap whose lock should be promoted from shared to exclusive.
305 *
306 * @return True if successfully promoted, otherwise false upon failure in
307 * which case the shared lock is dropped.
308 */
309 static inline bool
pmap_lock_shared_to_exclusive(pmap_t pmap)310 pmap_lock_shared_to_exclusive(pmap_t pmap)
311 {
312 pmap_assert_locked(pmap, PMAP_LOCK_SHARED);
313
314 if ((pmap == kernel_pmap) || __improbable(sptm_stability_hacks)) {
315 return true;
316 }
317
318 return lck_rw_lock_shared_to_exclusive(&pmap->rwlock);
319 }
320
321 /**
322 * Release a pmap object's reader/writer lock.
323 *
324 * @param pmap The pmap whose lock to release.
325 * @param mode Which mode the lock should be in at time of release.
326 */
327 static inline void
pmap_unlock(pmap_t pmap,pmap_lock_mode_t mode)328 pmap_unlock(pmap_t pmap, pmap_lock_mode_t mode)
329 {
330 if (pmap == kernel_pmap) {
331 return;
332 }
333 if (__improbable(sptm_stability_hacks)) {
334 mode = PMAP_LOCK_EXCLUSIVE;
335 }
336
337 switch (mode) {
338 case PMAP_LOCK_SHARED:
339 lck_rw_unlock_shared(&pmap->rwlock);
340 break;
341 case PMAP_LOCK_EXCLUSIVE:
342 lck_rw_unlock_exclusive(&pmap->rwlock);
343 break;
344 default:
345 panic("%s: Unknown pmap_lock_mode. pmap=%p, mode=%d", __func__, pmap, mode);
346 }
347 }
348