]>
Commit | Line | Data |
---|---|---|
2d21ac55 A |
1 | /* |
2 | * Copyright (c) 2007 Apple Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
0a7de745 | 5 | * |
2d21ac55 A |
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. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
0a7de745 | 12 | * |
2d21ac55 A |
13 | * The Original Code and all software distributed under the License are |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
0a7de745 | 20 | * |
2d21ac55 A |
21 | * @APPLE_LICENSE_HEADER_END@ |
22 | */ | |
23 | ||
24 | /* | |
25 | * Shared region (... and comm page) | |
26 | * | |
27 | * This file handles the VM shared region and comm page. | |
28 | * | |
29 | */ | |
30 | /* | |
31 | * SHARED REGIONS | |
32 | * -------------- | |
33 | * | |
34 | * A shared region is a submap that contains the most common system shared | |
35 | * libraries for a given environment. | |
36 | * An environment is defined by (cpu-type, 64-bitness, root directory). | |
37 | * | |
38 | * The point of a shared region is to reduce the setup overhead when exec'ing | |
39 | * a new process. | |
40 | * A shared region uses a shared VM submap that gets mapped automatically | |
41 | * at exec() time (see vm_map_exec()). The first process of a given | |
42 | * environment sets up the shared region and all further processes in that | |
43 | * environment can re-use that shared region without having to re-create | |
44 | * the same mappings in their VM map. All they need is contained in the shared | |
45 | * region. | |
46 | * It can also shared a pmap (mostly for read-only parts but also for the | |
0a7de745 | 47 | * initial version of some writable parts), which gets "nested" into the |
2d21ac55 A |
48 | * process's pmap. This reduces the number of soft faults: once one process |
49 | * brings in a page in the shared region, all the other processes can access | |
50 | * it without having to enter it in their own pmap. | |
51 | * | |
52 | * | |
53 | * When a process is being exec'ed, vm_map_exec() calls vm_shared_region_enter() | |
54 | * to map the appropriate shared region in the process's address space. | |
55 | * We look up the appropriate shared region for the process's environment. | |
56 | * If we can't find one, we create a new (empty) one and add it to the list. | |
57 | * Otherwise, we just take an extra reference on the shared region we found. | |
58 | * | |
59 | * The "dyld" runtime (mapped into the process's address space at exec() time) | |
60 | * will then use the shared_region_check_np() and shared_region_map_np() | |
61 | * system call to validate and/or populate the shared region with the | |
62 | * appropriate dyld_shared_cache file. | |
63 | * | |
64 | * The shared region is inherited on fork() and the child simply takes an | |
65 | * extra reference on its parent's shared region. | |
66 | * | |
67 | * When the task terminates, we release a reference on its shared region. | |
68 | * When the last reference is released, we destroy the shared region. | |
69 | * | |
70 | * After a chroot(), the calling process keeps using its original shared region, | |
71 | * since that's what was mapped when it was started. But its children | |
72 | * will use a different shared region, because they need to use the shared | |
73 | * cache that's relative to the new root directory. | |
74 | */ | |
75 | /* | |
76 | * COMM PAGE | |
77 | * | |
78 | * A "comm page" is an area of memory that is populated by the kernel with | |
79 | * the appropriate platform-specific version of some commonly used code. | |
80 | * There is one "comm page" per platform (cpu-type, 64-bitness) but only | |
81 | * for the native cpu-type. No need to overly optimize translated code | |
82 | * for hardware that is not really there ! | |
83 | * | |
84 | * The comm pages are created and populated at boot time. | |
85 | * | |
86 | * The appropriate comm page is mapped into a process's address space | |
87 | * at exec() time, in vm_map_exec(). | |
88 | * It is then inherited on fork(). | |
89 | * | |
90 | * The comm page is shared between the kernel and all applications of | |
91 | * a given platform. Only the kernel can modify it. | |
92 | * | |
93 | * Applications just branch to fixed addresses in the comm page and find | |
94 | * the right version of the code for the platform. There is also some | |
95 | * data provided and updated by the kernel for processes to retrieve easily | |
96 | * without having to do a system call. | |
97 | */ | |
98 | ||
99 | #include <debug.h> | |
100 | ||
101 | #include <kern/ipc_tt.h> | |
102 | #include <kern/kalloc.h> | |
b0d623f7 | 103 | #include <kern/thread_call.h> |
2d21ac55 | 104 | |
4a3eedf9 A |
105 | #include <mach/mach_vm.h> |
106 | ||
2d21ac55 A |
107 | #include <vm/vm_map.h> |
108 | #include <vm/vm_shared_region.h> | |
109 | ||
110 | #include <vm/vm_protos.h> | |
111 | ||
112 | #include <machine/commpage.h> | |
113 | #include <machine/cpu_capabilities.h> | |
114 | ||
d9a64523 A |
115 | #if defined (__arm__) || defined(__arm64__) |
116 | #include <arm/cpu_data_internal.h> | |
117 | #endif | |
118 | ||
119 | /* | |
120 | * the following codes are used in the subclass | |
121 | * of the DBG_MACH_SHAREDREGION class | |
122 | */ | |
0a7de745 | 123 | #define PROCESS_SHARED_CACHE_LAYOUT 0x00 |
d9a64523 | 124 | |
cb323159 A |
125 | #if defined(HAS_APPLE_PAC) |
126 | #include <ptrauth.h> | |
127 | #endif /* HAS_APPLE_PAC */ | |
d9a64523 | 128 | |
2d21ac55 A |
129 | /* "dyld" uses this to figure out what the kernel supports */ |
130 | int shared_region_version = 3; | |
131 | ||
2d21ac55 A |
132 | /* trace level, output is sent to the system log file */ |
133 | int shared_region_trace_level = SHARED_REGION_TRACE_ERROR_LVL; | |
134 | ||
b0d623f7 | 135 | /* should local (non-chroot) shared regions persist when no task uses them ? */ |
0a7de745 | 136 | int shared_region_persistence = 0; /* no by default */ |
b0d623f7 A |
137 | |
138 | /* delay before reclaiming an unused shared region */ | |
139 | int shared_region_destroy_delay = 120; /* in seconds */ | |
140 | ||
d9a64523 A |
141 | struct vm_shared_region *init_task_shared_region = NULL; |
142 | ||
5ba3f43e | 143 | #ifndef CONFIG_EMBEDDED |
0a7de745 | 144 | /* |
39236c6e | 145 | * Only one cache gets to slide on Desktop, since we can't |
0a7de745 | 146 | * tear down slide info properly today and the desktop actually |
39236c6e A |
147 | * produces lots of shared caches. |
148 | */ | |
6d2010ae | 149 | boolean_t shared_region_completed_slide = FALSE; |
5ba3f43e | 150 | #endif |
6d2010ae | 151 | |
2d21ac55 A |
152 | /* this lock protects all the shared region data structures */ |
153 | lck_grp_t *vm_shared_region_lck_grp; | |
154 | lck_mtx_t vm_shared_region_lock; | |
155 | ||
156 | #define vm_shared_region_lock() lck_mtx_lock(&vm_shared_region_lock) | |
157 | #define vm_shared_region_unlock() lck_mtx_unlock(&vm_shared_region_lock) | |
0a7de745 A |
158 | #define vm_shared_region_sleep(event, interruptible) \ |
159 | lck_mtx_sleep(&vm_shared_region_lock, \ | |
160 | LCK_SLEEP_DEFAULT, \ | |
161 | (event_t) (event), \ | |
162 | (interruptible)) | |
2d21ac55 A |
163 | |
164 | /* the list of currently available shared regions (one per environment) */ | |
0a7de745 | 165 | queue_head_t vm_shared_region_queue; |
2d21ac55 A |
166 | |
167 | static void vm_shared_region_reference_locked(vm_shared_region_t shared_region); | |
168 | static vm_shared_region_t vm_shared_region_create( | |
0a7de745 A |
169 | void *root_dir, |
170 | cpu_type_t cputype, | |
171 | cpu_subtype_t cpu_subtype, | |
172 | boolean_t is_64bit); | |
2d21ac55 A |
173 | static void vm_shared_region_destroy(vm_shared_region_t shared_region); |
174 | ||
b0d623f7 | 175 | static void vm_shared_region_timeout(thread_call_param_t param0, |
0a7de745 | 176 | thread_call_param_t param1); |
d9a64523 A |
177 | kern_return_t vm_shared_region_slide_mapping( |
178 | vm_shared_region_t sr, | |
179 | mach_vm_size_t slide_info_size, | |
180 | mach_vm_offset_t start, | |
181 | mach_vm_size_t size, | |
182 | mach_vm_offset_t slid_mapping, | |
183 | uint32_t slide, | |
184 | memory_object_control_t); /* forward */ | |
b0d623f7 A |
185 | |
186 | static int __commpage_setup = 0; | |
cb323159 | 187 | #if !CONFIG_EMBEDDED |
0a7de745 | 188 | static int __system_power_source = 1; /* init to extrnal power source */ |
b0d623f7 | 189 | static void post_sys_powersource_internal(int i, int internal); |
cb323159 | 190 | #endif |
b0d623f7 A |
191 | |
192 | ||
2d21ac55 A |
193 | /* |
194 | * Initialize the module... | |
195 | */ | |
196 | void | |
197 | vm_shared_region_init(void) | |
198 | { | |
199 | SHARED_REGION_TRACE_DEBUG( | |
200 | ("shared_region: -> init\n")); | |
201 | ||
202 | vm_shared_region_lck_grp = lck_grp_alloc_init("vm shared region", | |
0a7de745 | 203 | LCK_GRP_ATTR_NULL); |
2d21ac55 | 204 | lck_mtx_init(&vm_shared_region_lock, |
0a7de745 A |
205 | vm_shared_region_lck_grp, |
206 | LCK_ATTR_NULL); | |
2d21ac55 A |
207 | |
208 | queue_init(&vm_shared_region_queue); | |
209 | ||
210 | SHARED_REGION_TRACE_DEBUG( | |
211 | ("shared_region: <- init\n")); | |
212 | } | |
213 | ||
214 | /* | |
0a7de745 A |
215 | * Retrieve a task's shared region and grab an extra reference to |
216 | * make sure it doesn't disappear while the caller is using it. | |
2d21ac55 A |
217 | * The caller is responsible for consuming that extra reference if |
218 | * necessary. | |
219 | */ | |
220 | vm_shared_region_t | |
221 | vm_shared_region_get( | |
0a7de745 | 222 | task_t task) |
2d21ac55 | 223 | { |
0a7de745 | 224 | vm_shared_region_t shared_region; |
2d21ac55 A |
225 | |
226 | SHARED_REGION_TRACE_DEBUG( | |
227 | ("shared_region: -> get(%p)\n", | |
0a7de745 | 228 | (void *)VM_KERNEL_ADDRPERM(task))); |
2d21ac55 A |
229 | |
230 | task_lock(task); | |
231 | vm_shared_region_lock(); | |
232 | shared_region = task->shared_region; | |
233 | if (shared_region) { | |
234 | assert(shared_region->sr_ref_count > 0); | |
235 | vm_shared_region_reference_locked(shared_region); | |
236 | } | |
237 | vm_shared_region_unlock(); | |
238 | task_unlock(task); | |
239 | ||
240 | SHARED_REGION_TRACE_DEBUG( | |
241 | ("shared_region: get(%p) <- %p\n", | |
0a7de745 A |
242 | (void *)VM_KERNEL_ADDRPERM(task), |
243 | (void *)VM_KERNEL_ADDRPERM(shared_region))); | |
2d21ac55 A |
244 | |
245 | return shared_region; | |
246 | } | |
247 | ||
248 | /* | |
249 | * Get the base address of the shared region. | |
250 | * That's the address at which it needs to be mapped in the process's address | |
251 | * space. | |
252 | * No need to lock since this data is set when the shared region is | |
253 | * created and is never modified after that. The caller must hold an extra | |
254 | * reference on the shared region to prevent it from being destroyed. | |
255 | */ | |
256 | mach_vm_offset_t | |
257 | vm_shared_region_base_address( | |
0a7de745 | 258 | vm_shared_region_t shared_region) |
2d21ac55 A |
259 | { |
260 | SHARED_REGION_TRACE_DEBUG( | |
261 | ("shared_region: -> base_address(%p)\n", | |
0a7de745 | 262 | (void *)VM_KERNEL_ADDRPERM(shared_region))); |
2d21ac55 A |
263 | assert(shared_region->sr_ref_count > 1); |
264 | SHARED_REGION_TRACE_DEBUG( | |
265 | ("shared_region: base_address(%p) <- 0x%llx\n", | |
0a7de745 A |
266 | (void *)VM_KERNEL_ADDRPERM(shared_region), |
267 | (long long)shared_region->sr_base_address)); | |
2d21ac55 A |
268 | return shared_region->sr_base_address; |
269 | } | |
270 | ||
271 | /* | |
272 | * Get the size of the shared region. | |
273 | * That's the size that needs to be mapped in the process's address | |
274 | * space. | |
275 | * No need to lock since this data is set when the shared region is | |
276 | * created and is never modified after that. The caller must hold an extra | |
277 | * reference on the shared region to prevent it from being destroyed. | |
278 | */ | |
279 | mach_vm_size_t | |
280 | vm_shared_region_size( | |
0a7de745 | 281 | vm_shared_region_t shared_region) |
2d21ac55 A |
282 | { |
283 | SHARED_REGION_TRACE_DEBUG( | |
284 | ("shared_region: -> size(%p)\n", | |
0a7de745 | 285 | (void *)VM_KERNEL_ADDRPERM(shared_region))); |
2d21ac55 A |
286 | assert(shared_region->sr_ref_count > 1); |
287 | SHARED_REGION_TRACE_DEBUG( | |
288 | ("shared_region: size(%p) <- 0x%llx\n", | |
0a7de745 A |
289 | (void *)VM_KERNEL_ADDRPERM(shared_region), |
290 | (long long)shared_region->sr_size)); | |
2d21ac55 A |
291 | return shared_region->sr_size; |
292 | } | |
293 | ||
294 | /* | |
295 | * Get the memory entry of the shared region. | |
296 | * That's the "memory object" that needs to be mapped in the process's address | |
297 | * space. | |
298 | * No need to lock since this data is set when the shared region is | |
299 | * created and is never modified after that. The caller must hold an extra | |
300 | * reference on the shared region to prevent it from being destroyed. | |
301 | */ | |
302 | ipc_port_t | |
303 | vm_shared_region_mem_entry( | |
0a7de745 | 304 | vm_shared_region_t shared_region) |
2d21ac55 A |
305 | { |
306 | SHARED_REGION_TRACE_DEBUG( | |
307 | ("shared_region: -> mem_entry(%p)\n", | |
0a7de745 | 308 | (void *)VM_KERNEL_ADDRPERM(shared_region))); |
2d21ac55 A |
309 | assert(shared_region->sr_ref_count > 1); |
310 | SHARED_REGION_TRACE_DEBUG( | |
311 | ("shared_region: mem_entry(%p) <- %p\n", | |
0a7de745 A |
312 | (void *)VM_KERNEL_ADDRPERM(shared_region), |
313 | (void *)VM_KERNEL_ADDRPERM(shared_region->sr_mem_entry))); | |
2d21ac55 A |
314 | return shared_region->sr_mem_entry; |
315 | } | |
316 | ||
d9a64523 A |
317 | vm_map_t |
318 | vm_shared_region_vm_map( | |
0a7de745 | 319 | vm_shared_region_t shared_region) |
d9a64523 | 320 | { |
0a7de745 A |
321 | ipc_port_t sr_handle; |
322 | vm_named_entry_t sr_mem_entry; | |
323 | vm_map_t sr_map; | |
d9a64523 A |
324 | |
325 | SHARED_REGION_TRACE_DEBUG( | |
326 | ("shared_region: -> vm_map(%p)\n", | |
0a7de745 | 327 | (void *)VM_KERNEL_ADDRPERM(shared_region))); |
d9a64523 A |
328 | assert(shared_region->sr_ref_count > 1); |
329 | ||
330 | sr_handle = shared_region->sr_mem_entry; | |
331 | sr_mem_entry = (vm_named_entry_t) sr_handle->ip_kobject; | |
332 | sr_map = sr_mem_entry->backing.map; | |
333 | assert(sr_mem_entry->is_sub_map); | |
334 | ||
335 | SHARED_REGION_TRACE_DEBUG( | |
336 | ("shared_region: vm_map(%p) <- %p\n", | |
0a7de745 A |
337 | (void *)VM_KERNEL_ADDRPERM(shared_region), |
338 | (void *)VM_KERNEL_ADDRPERM(sr_map))); | |
d9a64523 A |
339 | return sr_map; |
340 | } | |
39236c6e A |
341 | uint32_t |
342 | vm_shared_region_get_slide( | |
0a7de745 | 343 | vm_shared_region_t shared_region) |
39236c6e A |
344 | { |
345 | SHARED_REGION_TRACE_DEBUG( | |
346 | ("shared_region: -> vm_shared_region_get_slide(%p)\n", | |
0a7de745 | 347 | (void *)VM_KERNEL_ADDRPERM(shared_region))); |
39236c6e A |
348 | assert(shared_region->sr_ref_count > 1); |
349 | SHARED_REGION_TRACE_DEBUG( | |
350 | ("shared_region: vm_shared_region_get_slide(%p) <- %u\n", | |
0a7de745 A |
351 | (void *)VM_KERNEL_ADDRPERM(shared_region), |
352 | shared_region->sr_slide_info.slide)); | |
39236c6e A |
353 | |
354 | /* 0 if we haven't slid */ | |
0a7de745 A |
355 | assert(shared_region->sr_slide_info.slide_object != NULL || |
356 | shared_region->sr_slide_info.slide == 0); | |
39236c6e | 357 | |
0a7de745 | 358 | return shared_region->sr_slide_info.slide; |
39236c6e A |
359 | } |
360 | ||
361 | vm_shared_region_slide_info_t | |
362 | vm_shared_region_get_slide_info( | |
0a7de745 | 363 | vm_shared_region_t shared_region) |
39236c6e A |
364 | { |
365 | SHARED_REGION_TRACE_DEBUG( | |
366 | ("shared_region: -> vm_shared_region_get_slide_info(%p)\n", | |
0a7de745 | 367 | (void *)VM_KERNEL_ADDRPERM(shared_region))); |
39236c6e A |
368 | assert(shared_region->sr_ref_count > 1); |
369 | SHARED_REGION_TRACE_DEBUG( | |
370 | ("shared_region: vm_shared_region_get_slide_info(%p) <- %p\n", | |
0a7de745 A |
371 | (void *)VM_KERNEL_ADDRPERM(shared_region), |
372 | (void *)VM_KERNEL_ADDRPERM(&shared_region->sr_slide_info))); | |
39236c6e A |
373 | return &shared_region->sr_slide_info; |
374 | } | |
375 | ||
2d21ac55 A |
376 | /* |
377 | * Set the shared region the process should use. | |
378 | * A NULL new shared region means that we just want to release the old | |
379 | * shared region. | |
380 | * The caller should already have an extra reference on the new shared region | |
381 | * (if any). We release a reference on the old shared region (if any). | |
382 | */ | |
383 | void | |
384 | vm_shared_region_set( | |
0a7de745 A |
385 | task_t task, |
386 | vm_shared_region_t new_shared_region) | |
2d21ac55 | 387 | { |
0a7de745 | 388 | vm_shared_region_t old_shared_region; |
2d21ac55 A |
389 | |
390 | SHARED_REGION_TRACE_DEBUG( | |
391 | ("shared_region: -> set(%p, %p)\n", | |
0a7de745 A |
392 | (void *)VM_KERNEL_ADDRPERM(task), |
393 | (void *)VM_KERNEL_ADDRPERM(new_shared_region))); | |
2d21ac55 A |
394 | |
395 | task_lock(task); | |
396 | vm_shared_region_lock(); | |
397 | ||
398 | old_shared_region = task->shared_region; | |
399 | if (new_shared_region) { | |
400 | assert(new_shared_region->sr_ref_count > 0); | |
401 | } | |
402 | ||
403 | task->shared_region = new_shared_region; | |
404 | ||
405 | vm_shared_region_unlock(); | |
406 | task_unlock(task); | |
407 | ||
408 | if (old_shared_region) { | |
409 | assert(old_shared_region->sr_ref_count > 0); | |
410 | vm_shared_region_deallocate(old_shared_region); | |
411 | } | |
412 | ||
413 | SHARED_REGION_TRACE_DEBUG( | |
414 | ("shared_region: set(%p) <- old=%p new=%p\n", | |
0a7de745 A |
415 | (void *)VM_KERNEL_ADDRPERM(task), |
416 | (void *)VM_KERNEL_ADDRPERM(old_shared_region), | |
417 | (void *)VM_KERNEL_ADDRPERM(new_shared_region))); | |
2d21ac55 A |
418 | } |
419 | ||
420 | /* | |
421 | * Lookup up the shared region for the desired environment. | |
422 | * If none is found, create a new (empty) one. | |
423 | * Grab an extra reference on the returned shared region, to make sure | |
424 | * it doesn't get destroyed before the caller is done with it. The caller | |
425 | * is responsible for consuming that extra reference if necessary. | |
426 | */ | |
427 | vm_shared_region_t | |
428 | vm_shared_region_lookup( | |
0a7de745 A |
429 | void *root_dir, |
430 | cpu_type_t cputype, | |
431 | cpu_subtype_t cpu_subtype, | |
432 | boolean_t is_64bit) | |
2d21ac55 | 433 | { |
0a7de745 A |
434 | vm_shared_region_t shared_region; |
435 | vm_shared_region_t new_shared_region; | |
2d21ac55 A |
436 | |
437 | SHARED_REGION_TRACE_DEBUG( | |
d9a64523 | 438 | ("shared_region: -> lookup(root=%p,cpu=<%d,%d>,64bit=%d)\n", |
fe8ab488 | 439 | |
0a7de745 A |
440 | (void *)VM_KERNEL_ADDRPERM(root_dir), |
441 | cputype, cpu_subtype, is_64bit)); | |
2d21ac55 A |
442 | |
443 | shared_region = NULL; | |
444 | new_shared_region = NULL; | |
445 | ||
446 | vm_shared_region_lock(); | |
447 | for (;;) { | |
448 | queue_iterate(&vm_shared_region_queue, | |
0a7de745 A |
449 | shared_region, |
450 | vm_shared_region_t, | |
451 | sr_q) { | |
2d21ac55 A |
452 | assert(shared_region->sr_ref_count > 0); |
453 | if (shared_region->sr_cpu_type == cputype && | |
d9a64523 | 454 | shared_region->sr_cpu_subtype == cpu_subtype && |
2d21ac55 A |
455 | shared_region->sr_root_dir == root_dir && |
456 | shared_region->sr_64bit == is_64bit) { | |
457 | /* found a match ! */ | |
458 | vm_shared_region_reference_locked(shared_region); | |
459 | goto done; | |
460 | } | |
461 | } | |
462 | if (new_shared_region == NULL) { | |
463 | /* no match: create a new one */ | |
464 | vm_shared_region_unlock(); | |
465 | new_shared_region = vm_shared_region_create(root_dir, | |
0a7de745 A |
466 | cputype, |
467 | cpu_subtype, | |
468 | is_64bit); | |
2d21ac55 A |
469 | /* do the lookup again, in case we lost a race */ |
470 | vm_shared_region_lock(); | |
471 | continue; | |
472 | } | |
473 | /* still no match: use our new one */ | |
474 | shared_region = new_shared_region; | |
475 | new_shared_region = NULL; | |
476 | queue_enter(&vm_shared_region_queue, | |
0a7de745 A |
477 | shared_region, |
478 | vm_shared_region_t, | |
479 | sr_q); | |
2d21ac55 A |
480 | break; |
481 | } | |
482 | ||
483 | done: | |
484 | vm_shared_region_unlock(); | |
485 | ||
486 | if (new_shared_region) { | |
487 | /* | |
488 | * We lost a race with someone else to create a new shared | |
489 | * region for that environment. Get rid of our unused one. | |
490 | */ | |
491 | assert(new_shared_region->sr_ref_count == 1); | |
492 | new_shared_region->sr_ref_count--; | |
493 | vm_shared_region_destroy(new_shared_region); | |
494 | new_shared_region = NULL; | |
495 | } | |
496 | ||
497 | SHARED_REGION_TRACE_DEBUG( | |
d9a64523 | 498 | ("shared_region: lookup(root=%p,cpu=<%d,%d>,64bit=%d) <- %p\n", |
0a7de745 A |
499 | (void *)VM_KERNEL_ADDRPERM(root_dir), |
500 | cputype, cpu_subtype, is_64bit, | |
501 | (void *)VM_KERNEL_ADDRPERM(shared_region))); | |
2d21ac55 A |
502 | |
503 | assert(shared_region->sr_ref_count > 0); | |
504 | return shared_region; | |
505 | } | |
506 | ||
507 | /* | |
508 | * Take an extra reference on a shared region. | |
509 | * The vm_shared_region_lock should already be held by the caller. | |
510 | */ | |
511 | static void | |
512 | vm_shared_region_reference_locked( | |
0a7de745 | 513 | vm_shared_region_t shared_region) |
2d21ac55 | 514 | { |
39037602 | 515 | LCK_MTX_ASSERT(&vm_shared_region_lock, LCK_MTX_ASSERT_OWNED); |
2d21ac55 A |
516 | |
517 | SHARED_REGION_TRACE_DEBUG( | |
518 | ("shared_region: -> reference_locked(%p)\n", | |
0a7de745 | 519 | (void *)VM_KERNEL_ADDRPERM(shared_region))); |
2d21ac55 A |
520 | assert(shared_region->sr_ref_count > 0); |
521 | shared_region->sr_ref_count++; | |
b0d623f7 A |
522 | |
523 | if (shared_region->sr_timer_call != NULL) { | |
524 | boolean_t cancelled; | |
525 | ||
526 | /* cancel and free any pending timeout */ | |
527 | cancelled = thread_call_cancel(shared_region->sr_timer_call); | |
528 | if (cancelled) { | |
529 | thread_call_free(shared_region->sr_timer_call); | |
530 | shared_region->sr_timer_call = NULL; | |
531 | /* release the reference held by the cancelled timer */ | |
532 | shared_region->sr_ref_count--; | |
533 | } else { | |
534 | /* the timer will drop the reference and free itself */ | |
535 | } | |
536 | } | |
537 | ||
2d21ac55 A |
538 | SHARED_REGION_TRACE_DEBUG( |
539 | ("shared_region: reference_locked(%p) <- %d\n", | |
0a7de745 A |
540 | (void *)VM_KERNEL_ADDRPERM(shared_region), |
541 | shared_region->sr_ref_count)); | |
2d21ac55 A |
542 | } |
543 | ||
544 | /* | |
545 | * Release a reference on the shared region. | |
546 | * Destroy it if there are no references left. | |
547 | */ | |
548 | void | |
549 | vm_shared_region_deallocate( | |
0a7de745 | 550 | vm_shared_region_t shared_region) |
2d21ac55 A |
551 | { |
552 | SHARED_REGION_TRACE_DEBUG( | |
553 | ("shared_region: -> deallocate(%p)\n", | |
0a7de745 | 554 | (void *)VM_KERNEL_ADDRPERM(shared_region))); |
2d21ac55 A |
555 | |
556 | vm_shared_region_lock(); | |
0a7de745 | 557 | |
2d21ac55 A |
558 | assert(shared_region->sr_ref_count > 0); |
559 | ||
560 | if (shared_region->sr_root_dir == NULL) { | |
561 | /* | |
562 | * Local (i.e. based on the boot volume) shared regions | |
563 | * can persist or not based on the "shared_region_persistence" | |
564 | * sysctl. | |
565 | * Make sure that this one complies. | |
39236c6e A |
566 | * |
567 | * See comments in vm_shared_region_slide() for notes about | |
568 | * shared regions we have slid (which are not torn down currently). | |
2d21ac55 A |
569 | */ |
570 | if (shared_region_persistence && | |
571 | !shared_region->sr_persists) { | |
572 | /* make this one persistent */ | |
573 | shared_region->sr_ref_count++; | |
574 | shared_region->sr_persists = TRUE; | |
575 | } else if (!shared_region_persistence && | |
0a7de745 | 576 | shared_region->sr_persists) { |
2d21ac55 A |
577 | /* make this one no longer persistent */ |
578 | assert(shared_region->sr_ref_count > 1); | |
579 | shared_region->sr_ref_count--; | |
580 | shared_region->sr_persists = FALSE; | |
581 | } | |
582 | } | |
583 | ||
584 | assert(shared_region->sr_ref_count > 0); | |
585 | shared_region->sr_ref_count--; | |
586 | SHARED_REGION_TRACE_DEBUG( | |
587 | ("shared_region: deallocate(%p): ref now %d\n", | |
0a7de745 A |
588 | (void *)VM_KERNEL_ADDRPERM(shared_region), |
589 | shared_region->sr_ref_count)); | |
2d21ac55 A |
590 | |
591 | if (shared_region->sr_ref_count == 0) { | |
b0d623f7 A |
592 | uint64_t deadline; |
593 | ||
39236c6e A |
594 | assert(!shared_region->sr_slid); |
595 | ||
b0d623f7 A |
596 | if (shared_region->sr_timer_call == NULL) { |
597 | /* hold one reference for the timer */ | |
0a7de745 | 598 | assert(!shared_region->sr_mapping_in_progress); |
b0d623f7 A |
599 | shared_region->sr_ref_count++; |
600 | ||
601 | /* set up the timer */ | |
602 | shared_region->sr_timer_call = thread_call_allocate( | |
603 | (thread_call_func_t) vm_shared_region_timeout, | |
604 | (thread_call_param_t) shared_region); | |
605 | ||
606 | /* schedule the timer */ | |
607 | clock_interval_to_deadline(shared_region_destroy_delay, | |
0a7de745 A |
608 | 1000 * 1000 * 1000, |
609 | &deadline); | |
b0d623f7 | 610 | thread_call_enter_delayed(shared_region->sr_timer_call, |
0a7de745 | 611 | deadline); |
b0d623f7 A |
612 | |
613 | SHARED_REGION_TRACE_DEBUG( | |
614 | ("shared_region: deallocate(%p): armed timer\n", | |
0a7de745 | 615 | (void *)VM_KERNEL_ADDRPERM(shared_region))); |
b0d623f7 A |
616 | |
617 | vm_shared_region_unlock(); | |
618 | } else { | |
619 | /* timer expired: let go of this shared region */ | |
620 | ||
0a7de745 | 621 | /* |
39236c6e A |
622 | * We can't properly handle teardown of a slid object today. |
623 | */ | |
624 | assert(!shared_region->sr_slid); | |
625 | ||
b0d623f7 A |
626 | /* |
627 | * Remove it from the queue first, so no one can find | |
628 | * it... | |
629 | */ | |
630 | queue_remove(&vm_shared_region_queue, | |
0a7de745 A |
631 | shared_region, |
632 | vm_shared_region_t, | |
633 | sr_q); | |
b0d623f7 | 634 | vm_shared_region_unlock(); |
39236c6e | 635 | |
b0d623f7 A |
636 | /* ... and destroy it */ |
637 | vm_shared_region_destroy(shared_region); | |
638 | shared_region = NULL; | |
639 | } | |
2d21ac55 A |
640 | } else { |
641 | vm_shared_region_unlock(); | |
642 | } | |
643 | ||
644 | SHARED_REGION_TRACE_DEBUG( | |
645 | ("shared_region: deallocate(%p) <-\n", | |
0a7de745 | 646 | (void *)VM_KERNEL_ADDRPERM(shared_region))); |
2d21ac55 A |
647 | } |
648 | ||
b0d623f7 A |
649 | void |
650 | vm_shared_region_timeout( | |
0a7de745 A |
651 | thread_call_param_t param0, |
652 | __unused thread_call_param_t param1) | |
b0d623f7 | 653 | { |
0a7de745 | 654 | vm_shared_region_t shared_region; |
b0d623f7 A |
655 | |
656 | shared_region = (vm_shared_region_t) param0; | |
657 | ||
658 | vm_shared_region_deallocate(shared_region); | |
659 | } | |
660 | ||
2d21ac55 A |
661 | /* |
662 | * Create a new (empty) shared region for a new environment. | |
663 | */ | |
664 | static vm_shared_region_t | |
665 | vm_shared_region_create( | |
0a7de745 A |
666 | void *root_dir, |
667 | cpu_type_t cputype, | |
668 | cpu_subtype_t cpu_subtype, | |
669 | boolean_t is_64bit) | |
2d21ac55 | 670 | { |
0a7de745 A |
671 | kern_return_t kr; |
672 | vm_named_entry_t mem_entry; | |
673 | ipc_port_t mem_entry_port; | |
674 | vm_shared_region_t shared_region; | |
39236c6e | 675 | vm_shared_region_slide_info_t si; |
0a7de745 A |
676 | vm_map_t sub_map; |
677 | mach_vm_offset_t base_address, pmap_nesting_start; | |
678 | mach_vm_size_t size, pmap_nesting_size; | |
2d21ac55 | 679 | |
d9a64523 A |
680 | SHARED_REGION_TRACE_INFO( |
681 | ("shared_region: -> create(root=%p,cpu=<%d,%d>,64bit=%d)\n", | |
0a7de745 A |
682 | (void *)VM_KERNEL_ADDRPERM(root_dir), |
683 | cputype, cpu_subtype, is_64bit)); | |
2d21ac55 A |
684 | |
685 | base_address = 0; | |
686 | size = 0; | |
687 | mem_entry = NULL; | |
688 | mem_entry_port = IPC_PORT_NULL; | |
689 | sub_map = VM_MAP_NULL; | |
690 | ||
691 | /* create a new shared region structure... */ | |
0a7de745 | 692 | shared_region = kalloc(sizeof(*shared_region)); |
2d21ac55 A |
693 | if (shared_region == NULL) { |
694 | SHARED_REGION_TRACE_ERROR( | |
695 | ("shared_region: create: couldn't allocate\n")); | |
696 | goto done; | |
697 | } | |
698 | ||
699 | /* figure out the correct settings for the desired environment */ | |
700 | if (is_64bit) { | |
701 | switch (cputype) { | |
5ba3f43e A |
702 | #if defined(__arm64__) |
703 | case CPU_TYPE_ARM64: | |
704 | base_address = SHARED_REGION_BASE_ARM64; | |
705 | size = SHARED_REGION_SIZE_ARM64; | |
706 | pmap_nesting_start = SHARED_REGION_NESTING_BASE_ARM64; | |
707 | pmap_nesting_size = SHARED_REGION_NESTING_SIZE_ARM64; | |
708 | break; | |
709 | #elif !defined(__arm__) | |
2d21ac55 A |
710 | case CPU_TYPE_I386: |
711 | base_address = SHARED_REGION_BASE_X86_64; | |
712 | size = SHARED_REGION_SIZE_X86_64; | |
713 | pmap_nesting_start = SHARED_REGION_NESTING_BASE_X86_64; | |
714 | pmap_nesting_size = SHARED_REGION_NESTING_SIZE_X86_64; | |
715 | break; | |
716 | case CPU_TYPE_POWERPC: | |
717 | base_address = SHARED_REGION_BASE_PPC64; | |
718 | size = SHARED_REGION_SIZE_PPC64; | |
719 | pmap_nesting_start = SHARED_REGION_NESTING_BASE_PPC64; | |
720 | pmap_nesting_size = SHARED_REGION_NESTING_SIZE_PPC64; | |
721 | break; | |
5ba3f43e | 722 | #endif |
2d21ac55 A |
723 | default: |
724 | SHARED_REGION_TRACE_ERROR( | |
725 | ("shared_region: create: unknown cpu type %d\n", | |
0a7de745 A |
726 | cputype)); |
727 | kfree(shared_region, sizeof(*shared_region)); | |
2d21ac55 A |
728 | shared_region = NULL; |
729 | goto done; | |
730 | } | |
731 | } else { | |
732 | switch (cputype) { | |
5ba3f43e A |
733 | #if defined(__arm__) || defined(__arm64__) |
734 | case CPU_TYPE_ARM: | |
5ba3f43e A |
735 | base_address = SHARED_REGION_BASE_ARM; |
736 | size = SHARED_REGION_SIZE_ARM; | |
737 | pmap_nesting_start = SHARED_REGION_NESTING_BASE_ARM; | |
738 | pmap_nesting_size = SHARED_REGION_NESTING_SIZE_ARM; | |
739 | break; | |
740 | #else | |
2d21ac55 A |
741 | case CPU_TYPE_I386: |
742 | base_address = SHARED_REGION_BASE_I386; | |
743 | size = SHARED_REGION_SIZE_I386; | |
744 | pmap_nesting_start = SHARED_REGION_NESTING_BASE_I386; | |
745 | pmap_nesting_size = SHARED_REGION_NESTING_SIZE_I386; | |
746 | break; | |
747 | case CPU_TYPE_POWERPC: | |
748 | base_address = SHARED_REGION_BASE_PPC; | |
749 | size = SHARED_REGION_SIZE_PPC; | |
750 | pmap_nesting_start = SHARED_REGION_NESTING_BASE_PPC; | |
751 | pmap_nesting_size = SHARED_REGION_NESTING_SIZE_PPC; | |
752 | break; | |
5ba3f43e | 753 | #endif |
2d21ac55 A |
754 | default: |
755 | SHARED_REGION_TRACE_ERROR( | |
756 | ("shared_region: create: unknown cpu type %d\n", | |
0a7de745 A |
757 | cputype)); |
758 | kfree(shared_region, sizeof(*shared_region)); | |
2d21ac55 A |
759 | shared_region = NULL; |
760 | goto done; | |
2d21ac55 A |
761 | } |
762 | } | |
763 | ||
764 | /* create a memory entry structure and a Mach port handle */ | |
765 | kr = mach_memory_entry_allocate(&mem_entry, | |
0a7de745 | 766 | &mem_entry_port); |
2d21ac55 | 767 | if (kr != KERN_SUCCESS) { |
0a7de745 | 768 | kfree(shared_region, sizeof(*shared_region)); |
2d21ac55 A |
769 | shared_region = NULL; |
770 | SHARED_REGION_TRACE_ERROR( | |
771 | ("shared_region: create: " | |
0a7de745 | 772 | "couldn't allocate mem_entry\n")); |
2d21ac55 A |
773 | goto done; |
774 | } | |
775 | ||
0a7de745 | 776 | #if defined(__arm__) || defined(__arm64__) |
5ba3f43e A |
777 | { |
778 | struct pmap *pmap_nested; | |
779 | ||
cb323159 | 780 | pmap_nested = pmap_create_options(NULL, 0, is_64bit ? PMAP_CREATE_64BIT : 0); |
5ba3f43e A |
781 | if (pmap_nested != PMAP_NULL) { |
782 | pmap_set_nested(pmap_nested); | |
783 | sub_map = vm_map_create(pmap_nested, 0, size, TRUE); | |
784 | #if defined(__arm64__) | |
785 | if (is_64bit || | |
786 | page_shift_user32 == SIXTEENK_PAGE_SHIFT) { | |
787 | /* enforce 16KB alignment of VM map entries */ | |
788 | vm_map_set_page_shift(sub_map, | |
0a7de745 | 789 | SIXTEENK_PAGE_SHIFT); |
5ba3f43e | 790 | } |
cb323159 | 791 | #elif (__ARM_ARCH_7K__ >= 2) |
5ba3f43e A |
792 | /* enforce 16KB alignment for watch targets with new ABI */ |
793 | vm_map_set_page_shift(sub_map, SIXTEENK_PAGE_SHIFT); | |
794 | #endif /* __arm64__ */ | |
795 | } else { | |
796 | sub_map = VM_MAP_NULL; | |
797 | } | |
798 | } | |
799 | #else | |
2d21ac55 | 800 | /* create a VM sub map and its pmap */ |
cb323159 | 801 | sub_map = vm_map_create(pmap_create_options(NULL, 0, is_64bit), |
0a7de745 A |
802 | 0, size, |
803 | TRUE); | |
5ba3f43e | 804 | #endif |
2d21ac55 A |
805 | if (sub_map == VM_MAP_NULL) { |
806 | ipc_port_release_send(mem_entry_port); | |
0a7de745 | 807 | kfree(shared_region, sizeof(*shared_region)); |
2d21ac55 A |
808 | shared_region = NULL; |
809 | SHARED_REGION_TRACE_ERROR( | |
810 | ("shared_region: create: " | |
0a7de745 | 811 | "couldn't allocate map\n")); |
2d21ac55 A |
812 | goto done; |
813 | } | |
814 | ||
39037602 A |
815 | assert(!sub_map->disable_vmentry_reuse); |
816 | sub_map->is_nested_map = TRUE; | |
817 | ||
2d21ac55 A |
818 | /* make the memory entry point to the VM sub map */ |
819 | mem_entry->is_sub_map = TRUE; | |
820 | mem_entry->backing.map = sub_map; | |
821 | mem_entry->size = size; | |
822 | mem_entry->protection = VM_PROT_ALL; | |
823 | ||
824 | /* make the shared region point at the memory entry */ | |
825 | shared_region->sr_mem_entry = mem_entry_port; | |
826 | ||
827 | /* fill in the shared region's environment and settings */ | |
828 | shared_region->sr_base_address = base_address; | |
829 | shared_region->sr_size = size; | |
830 | shared_region->sr_pmap_nesting_start = pmap_nesting_start; | |
831 | shared_region->sr_pmap_nesting_size = pmap_nesting_size; | |
832 | shared_region->sr_cpu_type = cputype; | |
d9a64523 | 833 | shared_region->sr_cpu_subtype = cpu_subtype; |
2d21ac55 A |
834 | shared_region->sr_64bit = is_64bit; |
835 | shared_region->sr_root_dir = root_dir; | |
836 | ||
837 | queue_init(&shared_region->sr_q); | |
838 | shared_region->sr_mapping_in_progress = FALSE; | |
39236c6e | 839 | shared_region->sr_slide_in_progress = FALSE; |
2d21ac55 | 840 | shared_region->sr_persists = FALSE; |
39236c6e | 841 | shared_region->sr_slid = FALSE; |
b0d623f7 | 842 | shared_region->sr_timer_call = NULL; |
2d21ac55 A |
843 | shared_region->sr_first_mapping = (mach_vm_offset_t) -1; |
844 | ||
845 | /* grab a reference for the caller */ | |
846 | shared_region->sr_ref_count = 1; | |
847 | ||
39236c6e A |
848 | /* And set up slide info */ |
849 | si = &shared_region->sr_slide_info; | |
850 | si->start = 0; | |
851 | si->end = 0; | |
852 | si->slide = 0; | |
cb323159 A |
853 | #if defined(HAS_APPLE_PAC) |
854 | si->si_ptrauth = FALSE; /* no pointer authentication by default */ | |
855 | #endif /* HAS_APPLE_PAC */ | |
39236c6e A |
856 | si->slide_object = NULL; |
857 | si->slide_info_size = 0; | |
858 | si->slide_info_entry = NULL; | |
859 | ||
d9a64523 | 860 | /* Initialize UUID and other metadata */ |
5ba3f43e A |
861 | memset(&shared_region->sr_uuid, '\0', sizeof(shared_region->sr_uuid)); |
862 | shared_region->sr_uuid_copied = FALSE; | |
d9a64523 A |
863 | shared_region->sr_images_count = 0; |
864 | shared_region->sr_images = NULL; | |
2d21ac55 A |
865 | done: |
866 | if (shared_region) { | |
867 | SHARED_REGION_TRACE_INFO( | |
d9a64523 | 868 | ("shared_region: create(root=%p,cpu=<%d,%d>,64bit=%d," |
0a7de745 A |
869 | "base=0x%llx,size=0x%llx) <- " |
870 | "%p mem=(%p,%p) map=%p pmap=%p\n", | |
871 | (void *)VM_KERNEL_ADDRPERM(root_dir), | |
872 | cputype, cpu_subtype, is_64bit, | |
873 | (long long)base_address, | |
874 | (long long)size, | |
875 | (void *)VM_KERNEL_ADDRPERM(shared_region), | |
876 | (void *)VM_KERNEL_ADDRPERM(mem_entry_port), | |
877 | (void *)VM_KERNEL_ADDRPERM(mem_entry), | |
878 | (void *)VM_KERNEL_ADDRPERM(sub_map), | |
879 | (void *)VM_KERNEL_ADDRPERM(sub_map->pmap))); | |
2d21ac55 A |
880 | } else { |
881 | SHARED_REGION_TRACE_INFO( | |
d9a64523 | 882 | ("shared_region: create(root=%p,cpu=<%d,%d>,64bit=%d," |
0a7de745 A |
883 | "base=0x%llx,size=0x%llx) <- NULL", |
884 | (void *)VM_KERNEL_ADDRPERM(root_dir), | |
885 | cputype, cpu_subtype, is_64bit, | |
886 | (long long)base_address, | |
887 | (long long)size)); | |
2d21ac55 A |
888 | } |
889 | return shared_region; | |
890 | } | |
891 | ||
892 | /* | |
893 | * Destroy a now-unused shared region. | |
894 | * The shared region is no longer in the queue and can not be looked up. | |
895 | */ | |
896 | static void | |
897 | vm_shared_region_destroy( | |
0a7de745 | 898 | vm_shared_region_t shared_region) |
2d21ac55 | 899 | { |
0a7de745 A |
900 | vm_named_entry_t mem_entry; |
901 | vm_map_t map; | |
2d21ac55 A |
902 | |
903 | SHARED_REGION_TRACE_INFO( | |
d9a64523 | 904 | ("shared_region: -> destroy(%p) (root=%p,cpu=<%d,%d>,64bit=%d)\n", |
0a7de745 A |
905 | (void *)VM_KERNEL_ADDRPERM(shared_region), |
906 | (void *)VM_KERNEL_ADDRPERM(shared_region->sr_root_dir), | |
907 | shared_region->sr_cpu_type, | |
908 | shared_region->sr_cpu_subtype, | |
909 | shared_region->sr_64bit)); | |
2d21ac55 A |
910 | |
911 | assert(shared_region->sr_ref_count == 0); | |
912 | assert(!shared_region->sr_persists); | |
39236c6e | 913 | assert(!shared_region->sr_slid); |
2d21ac55 A |
914 | |
915 | mem_entry = (vm_named_entry_t) shared_region->sr_mem_entry->ip_kobject; | |
916 | assert(mem_entry->is_sub_map); | |
917 | assert(!mem_entry->internal); | |
39236c6e | 918 | assert(!mem_entry->is_copy); |
2d21ac55 A |
919 | map = mem_entry->backing.map; |
920 | ||
921 | /* | |
922 | * Clean up the pmap first. The virtual addresses that were | |
923 | * entered in this possibly "nested" pmap may have different values | |
924 | * than the VM map's min and max offsets, if the VM sub map was | |
925 | * mapped at a non-zero offset in the processes' main VM maps, which | |
926 | * is usually the case, so the clean-up we do in vm_map_destroy() would | |
927 | * not be enough. | |
928 | */ | |
929 | if (map->pmap) { | |
930 | pmap_remove(map->pmap, | |
0a7de745 A |
931 | shared_region->sr_base_address, |
932 | (shared_region->sr_base_address + | |
933 | shared_region->sr_size)); | |
2d21ac55 A |
934 | } |
935 | ||
936 | /* | |
937 | * Release our (one and only) handle on the memory entry. | |
938 | * This will generate a no-senders notification, which will be processed | |
939 | * by ipc_kobject_notify(), which will release the one and only | |
940 | * reference on the memory entry and cause it to be destroyed, along | |
941 | * with the VM sub map and its pmap. | |
942 | */ | |
943 | mach_memory_entry_port_release(shared_region->sr_mem_entry); | |
944 | mem_entry = NULL; | |
945 | shared_region->sr_mem_entry = IPC_PORT_NULL; | |
946 | ||
b0d623f7 A |
947 | if (shared_region->sr_timer_call) { |
948 | thread_call_free(shared_region->sr_timer_call); | |
949 | } | |
950 | ||
39236c6e | 951 | #if 0 |
0a7de745 | 952 | /* |
39236c6e A |
953 | * If slid, free those resources. We'll want this eventually, |
954 | * but can't handle it properly today. | |
955 | */ | |
956 | si = &shared_region->sr_slide_info; | |
957 | if (si->slide_info_entry) { | |
6d2010ae | 958 | kmem_free(kernel_map, |
0a7de745 A |
959 | (vm_offset_t) si->slide_info_entry, |
960 | (vm_size_t) si->slide_info_size); | |
39236c6e | 961 | vm_object_deallocate(si->slide_object); |
6d2010ae | 962 | } |
0a7de745 | 963 | #endif |
6d2010ae | 964 | |
2d21ac55 | 965 | /* release the shared region structure... */ |
0a7de745 | 966 | kfree(shared_region, sizeof(*shared_region)); |
6d2010ae | 967 | |
2d21ac55 A |
968 | SHARED_REGION_TRACE_DEBUG( |
969 | ("shared_region: destroy(%p) <-\n", | |
0a7de745 | 970 | (void *)VM_KERNEL_ADDRPERM(shared_region))); |
2d21ac55 | 971 | shared_region = NULL; |
2d21ac55 A |
972 | } |
973 | ||
974 | /* | |
975 | * Gets the address of the first (in time) mapping in the shared region. | |
976 | */ | |
977 | kern_return_t | |
978 | vm_shared_region_start_address( | |
0a7de745 A |
979 | vm_shared_region_t shared_region, |
980 | mach_vm_offset_t *start_address) | |
2d21ac55 | 981 | { |
0a7de745 A |
982 | kern_return_t kr; |
983 | mach_vm_offset_t sr_base_address; | |
984 | mach_vm_offset_t sr_first_mapping; | |
2d21ac55 A |
985 | |
986 | SHARED_REGION_TRACE_DEBUG( | |
987 | ("shared_region: -> start_address(%p)\n", | |
0a7de745 | 988 | (void *)VM_KERNEL_ADDRPERM(shared_region))); |
2d21ac55 A |
989 | assert(shared_region->sr_ref_count > 1); |
990 | ||
991 | vm_shared_region_lock(); | |
992 | ||
993 | /* | |
994 | * Wait if there's another thread establishing a mapping | |
995 | * in this shared region right when we're looking at it. | |
996 | * We want a consistent view of the map... | |
997 | */ | |
998 | while (shared_region->sr_mapping_in_progress) { | |
999 | /* wait for our turn... */ | |
1000 | assert(shared_region->sr_ref_count > 1); | |
1001 | vm_shared_region_sleep(&shared_region->sr_mapping_in_progress, | |
0a7de745 | 1002 | THREAD_UNINT); |
2d21ac55 | 1003 | } |
0a7de745 | 1004 | assert(!shared_region->sr_mapping_in_progress); |
2d21ac55 | 1005 | assert(shared_region->sr_ref_count > 1); |
0a7de745 | 1006 | |
2d21ac55 A |
1007 | sr_base_address = shared_region->sr_base_address; |
1008 | sr_first_mapping = shared_region->sr_first_mapping; | |
1009 | ||
1010 | if (sr_first_mapping == (mach_vm_offset_t) -1) { | |
1011 | /* shared region is empty */ | |
1012 | kr = KERN_INVALID_ADDRESS; | |
1013 | } else { | |
1014 | kr = KERN_SUCCESS; | |
1015 | *start_address = sr_base_address + sr_first_mapping; | |
1016 | } | |
1017 | ||
1018 | vm_shared_region_unlock(); | |
0a7de745 | 1019 | |
2d21ac55 A |
1020 | SHARED_REGION_TRACE_DEBUG( |
1021 | ("shared_region: start_address(%p) <- 0x%llx\n", | |
0a7de745 A |
1022 | (void *)VM_KERNEL_ADDRPERM(shared_region), |
1023 | (long long)shared_region->sr_base_address)); | |
2d21ac55 A |
1024 | |
1025 | return kr; | |
1026 | } | |
6d2010ae A |
1027 | |
1028 | void | |
1029 | vm_shared_region_undo_mappings( | |
fe8ab488 A |
1030 | vm_map_t sr_map, |
1031 | mach_vm_offset_t sr_base_address, | |
1032 | struct shared_file_mapping_np *mappings, | |
1033 | unsigned int mappings_count) | |
6d2010ae | 1034 | { |
0a7de745 A |
1035 | unsigned int j = 0; |
1036 | vm_shared_region_t shared_region = NULL; | |
1037 | boolean_t reset_shared_region_state = FALSE; | |
316670eb | 1038 | |
6d2010ae A |
1039 | shared_region = vm_shared_region_get(current_task()); |
1040 | if (shared_region == NULL) { | |
316670eb | 1041 | printf("Failed to undo mappings because of NULL shared region.\n"); |
6d2010ae A |
1042 | return; |
1043 | } | |
0a7de745 | 1044 | |
6d2010ae A |
1045 | |
1046 | if (sr_map == NULL) { | |
0a7de745 A |
1047 | ipc_port_t sr_handle; |
1048 | vm_named_entry_t sr_mem_entry; | |
6d2010ae A |
1049 | |
1050 | vm_shared_region_lock(); | |
1051 | assert(shared_region->sr_ref_count > 1); | |
1052 | ||
1053 | while (shared_region->sr_mapping_in_progress) { | |
1054 | /* wait for our turn... */ | |
1055 | vm_shared_region_sleep(&shared_region->sr_mapping_in_progress, | |
0a7de745 | 1056 | THREAD_UNINT); |
6d2010ae | 1057 | } |
0a7de745 | 1058 | assert(!shared_region->sr_mapping_in_progress); |
6d2010ae A |
1059 | assert(shared_region->sr_ref_count > 1); |
1060 | /* let others know we're working in this shared region */ | |
1061 | shared_region->sr_mapping_in_progress = TRUE; | |
1062 | ||
1063 | vm_shared_region_unlock(); | |
1064 | ||
1065 | reset_shared_region_state = TRUE; | |
1066 | ||
1067 | /* no need to lock because this data is never modified... */ | |
1068 | sr_handle = shared_region->sr_mem_entry; | |
1069 | sr_mem_entry = (vm_named_entry_t) sr_handle->ip_kobject; | |
1070 | sr_map = sr_mem_entry->backing.map; | |
1071 | sr_base_address = shared_region->sr_base_address; | |
1072 | } | |
1073 | /* | |
1074 | * Undo the mappings we've established so far. | |
1075 | */ | |
1076 | for (j = 0; j < mappings_count; j++) { | |
1077 | kern_return_t kr2; | |
1078 | ||
1079 | if (mappings[j].sfm_size == 0) { | |
1080 | /* | |
1081 | * We didn't establish this | |
1082 | * mapping, so nothing to undo. | |
1083 | */ | |
1084 | continue; | |
1085 | } | |
1086 | SHARED_REGION_TRACE_INFO( | |
1087 | ("shared_region: mapping[%d]: " | |
0a7de745 A |
1088 | "address:0x%016llx " |
1089 | "size:0x%016llx " | |
1090 | "offset:0x%016llx " | |
1091 | "maxprot:0x%x prot:0x%x: " | |
1092 | "undoing...\n", | |
1093 | j, | |
1094 | (long long)mappings[j].sfm_address, | |
1095 | (long long)mappings[j].sfm_size, | |
1096 | (long long)mappings[j].sfm_file_offset, | |
1097 | mappings[j].sfm_max_prot, | |
1098 | mappings[j].sfm_init_prot)); | |
6d2010ae A |
1099 | kr2 = mach_vm_deallocate( |
1100 | sr_map, | |
1101 | (mappings[j].sfm_address - | |
0a7de745 | 1102 | sr_base_address), |
6d2010ae A |
1103 | mappings[j].sfm_size); |
1104 | assert(kr2 == KERN_SUCCESS); | |
1105 | } | |
1106 | ||
6d2010ae A |
1107 | if (reset_shared_region_state) { |
1108 | vm_shared_region_lock(); | |
1109 | assert(shared_region->sr_ref_count > 1); | |
1110 | assert(shared_region->sr_mapping_in_progress); | |
1111 | /* we're done working on that shared region */ | |
1112 | shared_region->sr_mapping_in_progress = FALSE; | |
1113 | thread_wakeup((event_t) &shared_region->sr_mapping_in_progress); | |
1114 | vm_shared_region_unlock(); | |
1115 | reset_shared_region_state = FALSE; | |
1116 | } | |
1117 | ||
1118 | vm_shared_region_deallocate(shared_region); | |
1119 | } | |
1120 | ||
2d21ac55 A |
1121 | /* |
1122 | * Establish some mappings of a file in the shared region. | |
1123 | * This is used by "dyld" via the shared_region_map_np() system call | |
1124 | * to populate the shared region with the appropriate shared cache. | |
1125 | * | |
1126 | * One could also call it several times to incrementally load several | |
0a7de745 | 1127 | * libraries, as long as they do not overlap. |
2d21ac55 A |
1128 | * It will return KERN_SUCCESS if the mappings were successfully established |
1129 | * or if they were already established identically by another process. | |
1130 | */ | |
1131 | kern_return_t | |
1132 | vm_shared_region_map_file( | |
0a7de745 A |
1133 | vm_shared_region_t shared_region, |
1134 | unsigned int mappings_count, | |
1135 | struct shared_file_mapping_np *mappings, | |
1136 | memory_object_control_t file_control, | |
1137 | memory_object_size_t file_size, | |
1138 | void *root_dir, | |
1139 | uint32_t slide, | |
1140 | user_addr_t slide_start, | |
1141 | user_addr_t slide_size) | |
2d21ac55 | 1142 | { |
0a7de745 A |
1143 | kern_return_t kr; |
1144 | vm_object_t file_object; | |
1145 | ipc_port_t sr_handle; | |
1146 | vm_named_entry_t sr_mem_entry; | |
1147 | vm_map_t sr_map; | |
1148 | mach_vm_offset_t sr_base_address; | |
1149 | unsigned int i; | |
1150 | mach_port_t map_port; | |
1151 | vm_map_offset_t target_address; | |
1152 | vm_object_t object; | |
1153 | vm_object_size_t obj_size; | |
1154 | struct shared_file_mapping_np *mapping_to_slide = NULL; | |
1155 | mach_vm_offset_t first_mapping = (mach_vm_offset_t) -1; | |
1156 | mach_vm_offset_t slid_mapping = (mach_vm_offset_t) -1; | |
1157 | vm_map_offset_t lowest_unnestable_addr = 0; | |
1158 | vm_map_kernel_flags_t vmk_flags; | |
1159 | mach_vm_offset_t sfm_min_address = ~0; | |
1160 | mach_vm_offset_t sfm_max_address = 0; | |
cb323159 | 1161 | mach_vm_offset_t sfm_end; |
d9a64523 | 1162 | struct _dyld_cache_header sr_cache_header; |
2d21ac55 | 1163 | |
5ba3f43e A |
1164 | #if __arm64__ |
1165 | if ((shared_region->sr_64bit || | |
0a7de745 | 1166 | page_shift_user32 == SIXTEENK_PAGE_SHIFT) && |
5ba3f43e A |
1167 | ((slide & SIXTEENK_PAGE_MASK) != 0)) { |
1168 | printf("FOURK_COMPAT: %s: rejecting mis-aligned slide 0x%x\n", | |
0a7de745 | 1169 | __FUNCTION__, slide); |
5ba3f43e A |
1170 | kr = KERN_INVALID_ARGUMENT; |
1171 | goto done; | |
1172 | } | |
1173 | #endif /* __arm64__ */ | |
3e170ce0 | 1174 | |
2d21ac55 A |
1175 | kr = KERN_SUCCESS; |
1176 | ||
1177 | vm_shared_region_lock(); | |
1178 | assert(shared_region->sr_ref_count > 1); | |
1179 | ||
1180 | if (shared_region->sr_root_dir != root_dir) { | |
1181 | /* | |
1182 | * This shared region doesn't match the current root | |
1183 | * directory of this process. Deny the mapping to | |
0a7de745 | 1184 | * avoid tainting the shared region with something that |
2d21ac55 A |
1185 | * doesn't quite belong into it. |
1186 | */ | |
1187 | vm_shared_region_unlock(); | |
1188 | kr = KERN_PROTECTION_FAILURE; | |
1189 | goto done; | |
1190 | } | |
1191 | ||
1192 | /* | |
1193 | * Make sure we handle only one mapping at a time in a given | |
1194 | * shared region, to avoid race conditions. This should not | |
1195 | * happen frequently... | |
1196 | */ | |
1197 | while (shared_region->sr_mapping_in_progress) { | |
1198 | /* wait for our turn... */ | |
1199 | vm_shared_region_sleep(&shared_region->sr_mapping_in_progress, | |
0a7de745 | 1200 | THREAD_UNINT); |
2d21ac55 | 1201 | } |
0a7de745 | 1202 | assert(!shared_region->sr_mapping_in_progress); |
2d21ac55 A |
1203 | assert(shared_region->sr_ref_count > 1); |
1204 | /* let others know we're working in this shared region */ | |
1205 | shared_region->sr_mapping_in_progress = TRUE; | |
1206 | ||
1207 | vm_shared_region_unlock(); | |
1208 | ||
1209 | /* no need to lock because this data is never modified... */ | |
1210 | sr_handle = shared_region->sr_mem_entry; | |
1211 | sr_mem_entry = (vm_named_entry_t) sr_handle->ip_kobject; | |
1212 | sr_map = sr_mem_entry->backing.map; | |
1213 | sr_base_address = shared_region->sr_base_address; | |
1214 | ||
1215 | SHARED_REGION_TRACE_DEBUG( | |
1216 | ("shared_region: -> map(%p,%d,%p,%p,0x%llx)\n", | |
0a7de745 A |
1217 | (void *)VM_KERNEL_ADDRPERM(shared_region), mappings_count, |
1218 | (void *)VM_KERNEL_ADDRPERM(mappings), | |
1219 | (void *)VM_KERNEL_ADDRPERM(file_control), file_size)); | |
2d21ac55 A |
1220 | |
1221 | /* get the VM object associated with the file to be mapped */ | |
1222 | file_object = memory_object_control_to_vm_object(file_control); | |
1223 | ||
5ba3f43e A |
1224 | assert(file_object); |
1225 | ||
2d21ac55 A |
1226 | /* establish the mappings */ |
1227 | for (i = 0; i < mappings_count; i++) { | |
1228 | SHARED_REGION_TRACE_INFO( | |
1229 | ("shared_region: mapping[%d]: " | |
0a7de745 A |
1230 | "address:0x%016llx size:0x%016llx offset:0x%016llx " |
1231 | "maxprot:0x%x prot:0x%x\n", | |
1232 | i, | |
1233 | (long long)mappings[i].sfm_address, | |
1234 | (long long)mappings[i].sfm_size, | |
1235 | (long long)mappings[i].sfm_file_offset, | |
1236 | mappings[i].sfm_max_prot, | |
1237 | mappings[i].sfm_init_prot)); | |
2d21ac55 | 1238 | |
d9a64523 A |
1239 | if (mappings[i].sfm_address < sfm_min_address) { |
1240 | sfm_min_address = mappings[i].sfm_address; | |
1241 | } | |
1242 | ||
cb323159 A |
1243 | if (os_add_overflow(mappings[i].sfm_address, |
1244 | mappings[i].sfm_size, | |
1245 | &sfm_end) || | |
1246 | (vm_map_round_page(sfm_end, VM_MAP_PAGE_MASK(sr_map)) < | |
1247 | mappings[i].sfm_address)) { | |
1248 | /* overflow */ | |
1249 | kr = KERN_INVALID_ARGUMENT; | |
1250 | break; | |
1251 | } | |
1252 | if (sfm_end > sfm_max_address) { | |
1253 | sfm_max_address = sfm_end; | |
d9a64523 A |
1254 | } |
1255 | ||
2d21ac55 A |
1256 | if (mappings[i].sfm_init_prot & VM_PROT_ZF) { |
1257 | /* zero-filled memory */ | |
1258 | map_port = MACH_PORT_NULL; | |
1259 | } else { | |
1260 | /* file-backed memory */ | |
3e170ce0 | 1261 | __IGNORE_WCASTALIGN(map_port = (ipc_port_t) file_object->pager); |
2d21ac55 | 1262 | } |
0a7de745 | 1263 | |
6d2010ae A |
1264 | if (mappings[i].sfm_init_prot & VM_PROT_SLIDE) { |
1265 | /* | |
1266 | * This is the mapping that needs to be slid. | |
1267 | */ | |
15129b1c | 1268 | if (mapping_to_slide != NULL) { |
6d2010ae A |
1269 | SHARED_REGION_TRACE_INFO( |
1270 | ("shared_region: mapping[%d]: " | |
0a7de745 A |
1271 | "address:0x%016llx size:0x%016llx " |
1272 | "offset:0x%016llx " | |
1273 | "maxprot:0x%x prot:0x%x " | |
1274 | "will not be slid as only one such mapping is allowed...\n", | |
1275 | i, | |
1276 | (long long)mappings[i].sfm_address, | |
1277 | (long long)mappings[i].sfm_size, | |
1278 | (long long)mappings[i].sfm_file_offset, | |
1279 | mappings[i].sfm_max_prot, | |
1280 | mappings[i].sfm_init_prot)); | |
6d2010ae | 1281 | } else { |
15129b1c | 1282 | mapping_to_slide = &mappings[i]; |
6d2010ae A |
1283 | } |
1284 | } | |
2d21ac55 A |
1285 | |
1286 | /* mapping's address is relative to the shared region base */ | |
1287 | target_address = | |
0a7de745 | 1288 | mappings[i].sfm_address - sr_base_address; |
2d21ac55 | 1289 | |
5ba3f43e A |
1290 | vmk_flags = VM_MAP_KERNEL_FLAGS_NONE; |
1291 | vmk_flags.vmkf_already = TRUE; | |
cb323159 A |
1292 | /* no copy-on-read for mapped binaries */ |
1293 | vmk_flags.vmkf_no_copy_on_read = 1; | |
5ba3f43e | 1294 | |
4a3eedf9 A |
1295 | /* establish that mapping, OK if it's "already" there */ |
1296 | if (map_port == MACH_PORT_NULL) { | |
1297 | /* | |
1298 | * We want to map some anonymous memory in a | |
1299 | * shared region. | |
1300 | * We have to create the VM object now, so that it | |
1301 | * can be mapped "copy-on-write". | |
1302 | */ | |
39236c6e | 1303 | obj_size = vm_map_round_page(mappings[i].sfm_size, |
0a7de745 | 1304 | VM_MAP_PAGE_MASK(sr_map)); |
4a3eedf9 A |
1305 | object = vm_object_allocate(obj_size); |
1306 | if (object == VM_OBJECT_NULL) { | |
1307 | kr = KERN_RESOURCE_SHORTAGE; | |
1308 | } else { | |
1309 | kr = vm_map_enter( | |
1310 | sr_map, | |
1311 | &target_address, | |
39236c6e | 1312 | vm_map_round_page(mappings[i].sfm_size, |
0a7de745 | 1313 | VM_MAP_PAGE_MASK(sr_map)), |
4a3eedf9 | 1314 | 0, |
5ba3f43e A |
1315 | VM_FLAGS_FIXED, |
1316 | vmk_flags, | |
1317 | VM_KERN_MEMORY_NONE, | |
4a3eedf9 A |
1318 | object, |
1319 | 0, | |
1320 | TRUE, | |
1321 | mappings[i].sfm_init_prot & VM_PROT_ALL, | |
1322 | mappings[i].sfm_max_prot & VM_PROT_ALL, | |
1323 | VM_INHERIT_DEFAULT); | |
1324 | } | |
1325 | } else { | |
1326 | object = VM_OBJECT_NULL; /* no anonymous memory here */ | |
1327 | kr = vm_map_enter_mem_object( | |
1328 | sr_map, | |
1329 | &target_address, | |
39236c6e | 1330 | vm_map_round_page(mappings[i].sfm_size, |
0a7de745 | 1331 | VM_MAP_PAGE_MASK(sr_map)), |
4a3eedf9 | 1332 | 0, |
5ba3f43e A |
1333 | VM_FLAGS_FIXED, |
1334 | vmk_flags, | |
1335 | VM_KERN_MEMORY_NONE, | |
4a3eedf9 A |
1336 | map_port, |
1337 | mappings[i].sfm_file_offset, | |
1338 | TRUE, | |
1339 | mappings[i].sfm_init_prot & VM_PROT_ALL, | |
1340 | mappings[i].sfm_max_prot & VM_PROT_ALL, | |
1341 | VM_INHERIT_DEFAULT); | |
2d21ac55 A |
1342 | } |
1343 | ||
15129b1c A |
1344 | if (kr == KERN_SUCCESS) { |
1345 | /* | |
1346 | * Record the first (chronologically) successful | |
1347 | * mapping in this shared region. | |
1348 | * We're protected by "sr_mapping_in_progress" here, | |
1349 | * so no need to lock "shared_region". | |
1350 | */ | |
1351 | if (first_mapping == (mach_vm_offset_t) -1) { | |
1352 | first_mapping = target_address; | |
1353 | } | |
39037602 | 1354 | |
cb323159 A |
1355 | #if defined(HAS_APPLE_PAC) |
1356 | /* | |
1357 | * Set "sr_slid_mapping" | |
1358 | * it is used to get the userland address for address authentication. | |
1359 | */ | |
1360 | #endif | |
d9a64523 | 1361 | if ((slid_mapping == (mach_vm_offset_t) -1) && |
0a7de745 | 1362 | (mapping_to_slide == &mappings[i])) { |
d9a64523 A |
1363 | slid_mapping = target_address; |
1364 | } | |
1365 | ||
39037602 A |
1366 | /* |
1367 | * Record the lowest writable address in this | |
1368 | * sub map, to log any unexpected unnesting below | |
1369 | * that address (see log_unnest_badness()). | |
1370 | */ | |
1371 | if ((mappings[i].sfm_init_prot & VM_PROT_WRITE) && | |
1372 | sr_map->is_nested_map && | |
1373 | (lowest_unnestable_addr == 0 || | |
0a7de745 | 1374 | (target_address < lowest_unnestable_addr))) { |
39037602 A |
1375 | lowest_unnestable_addr = target_address; |
1376 | } | |
15129b1c | 1377 | } else { |
4a3eedf9 A |
1378 | if (map_port == MACH_PORT_NULL) { |
1379 | /* | |
1380 | * Get rid of the VM object we just created | |
1381 | * but failed to map. | |
1382 | */ | |
1383 | vm_object_deallocate(object); | |
1384 | object = VM_OBJECT_NULL; | |
1385 | } | |
1386 | if (kr == KERN_MEMORY_PRESENT) { | |
1387 | /* | |
1388 | * This exact mapping was already there: | |
1389 | * that's fine. | |
1390 | */ | |
1391 | SHARED_REGION_TRACE_INFO( | |
1392 | ("shared_region: mapping[%d]: " | |
0a7de745 A |
1393 | "address:0x%016llx size:0x%016llx " |
1394 | "offset:0x%016llx " | |
1395 | "maxprot:0x%x prot:0x%x " | |
1396 | "already mapped...\n", | |
1397 | i, | |
1398 | (long long)mappings[i].sfm_address, | |
1399 | (long long)mappings[i].sfm_size, | |
1400 | (long long)mappings[i].sfm_file_offset, | |
1401 | mappings[i].sfm_max_prot, | |
1402 | mappings[i].sfm_init_prot)); | |
4a3eedf9 A |
1403 | /* |
1404 | * We didn't establish this mapping ourselves; | |
1405 | * let's reset its size, so that we do not | |
1406 | * attempt to undo it if an error occurs later. | |
1407 | */ | |
1408 | mappings[i].sfm_size = 0; | |
1409 | kr = KERN_SUCCESS; | |
1410 | } else { | |
4a3eedf9 A |
1411 | break; |
1412 | } | |
4a3eedf9 | 1413 | } |
15129b1c A |
1414 | } |
1415 | ||
cb323159 A |
1416 | if (kr != KERN_SUCCESS) { |
1417 | /* the last mapping we tried (mappings[i]) failed ! */ | |
1418 | assert(i < mappings_count); | |
1419 | SHARED_REGION_TRACE_ERROR( | |
1420 | ("shared_region: mapping[%d]: " | |
1421 | "address:0x%016llx size:0x%016llx " | |
1422 | "offset:0x%016llx " | |
1423 | "maxprot:0x%x prot:0x%x failed 0x%x\n", | |
1424 | i, | |
1425 | (long long)mappings[i].sfm_address, | |
1426 | (long long)mappings[i].sfm_size, | |
1427 | (long long)mappings[i].sfm_file_offset, | |
1428 | mappings[i].sfm_max_prot, | |
1429 | mappings[i].sfm_init_prot, | |
1430 | kr)); | |
1431 | /* undo all the previous mappings */ | |
1432 | vm_shared_region_undo_mappings(sr_map, sr_base_address, mappings, i); | |
1433 | } | |
1434 | ||
15129b1c | 1435 | if (kr == KERN_SUCCESS && |
39037602 | 1436 | slide_size != 0 && |
15129b1c | 1437 | mapping_to_slide != NULL) { |
0a7de745 A |
1438 | kr = vm_shared_region_slide(slide, |
1439 | mapping_to_slide->sfm_file_offset, | |
1440 | mapping_to_slide->sfm_size, | |
1441 | slide_start, | |
1442 | slide_size, | |
1443 | slid_mapping, | |
1444 | file_control); | |
1445 | if (kr != KERN_SUCCESS) { | |
15129b1c A |
1446 | SHARED_REGION_TRACE_ERROR( |
1447 | ("shared_region: region_slide(" | |
0a7de745 A |
1448 | "slide:0x%x start:0x%016llx " |
1449 | "size:0x%016llx) failed 0x%x\n", | |
1450 | slide, | |
1451 | (long long)slide_start, | |
1452 | (long long)slide_size, | |
1453 | kr)); | |
fe8ab488 | 1454 | vm_shared_region_undo_mappings(sr_map, |
0a7de745 A |
1455 | sr_base_address, |
1456 | mappings, | |
1457 | mappings_count); | |
2d21ac55 A |
1458 | } |
1459 | } | |
1460 | ||
39037602 A |
1461 | if (kr == KERN_SUCCESS) { |
1462 | /* adjust the map's "lowest_unnestable_start" */ | |
0a7de745 | 1463 | lowest_unnestable_addr &= ~(pmap_nesting_size_min - 1); |
39037602 A |
1464 | if (lowest_unnestable_addr != |
1465 | sr_map->lowest_unnestable_start) { | |
1466 | vm_map_lock(sr_map); | |
1467 | sr_map->lowest_unnestable_start = | |
0a7de745 | 1468 | lowest_unnestable_addr; |
39037602 A |
1469 | vm_map_unlock(sr_map); |
1470 | } | |
1471 | } | |
1472 | ||
2d21ac55 A |
1473 | vm_shared_region_lock(); |
1474 | assert(shared_region->sr_ref_count > 1); | |
1475 | assert(shared_region->sr_mapping_in_progress); | |
d9a64523 | 1476 | |
0a7de745 | 1477 | /* set "sr_first_mapping"; dyld uses it to validate the shared cache */ |
15129b1c A |
1478 | if (kr == KERN_SUCCESS && |
1479 | shared_region->sr_first_mapping == (mach_vm_offset_t) -1) { | |
1480 | shared_region->sr_first_mapping = first_mapping; | |
1481 | } | |
5ba3f43e | 1482 | |
d9a64523 A |
1483 | /* |
1484 | * copy in the shared region UUID to the shared region structure. | |
1485 | * we do this indirectly by first copying in the shared cache header | |
1486 | * and then copying the UUID from there because we'll need to look | |
1487 | * at other content from the shared cache header. | |
1488 | */ | |
5ba3f43e | 1489 | if (kr == KERN_SUCCESS && !shared_region->sr_uuid_copied) { |
d9a64523 | 1490 | int error = copyin((shared_region->sr_base_address + shared_region->sr_first_mapping), |
0a7de745 A |
1491 | (char *)&sr_cache_header, |
1492 | sizeof(sr_cache_header)); | |
d9a64523 A |
1493 | if (error == 0) { |
1494 | memcpy(&shared_region->sr_uuid, &sr_cache_header.uuid, sizeof(shared_region->sr_uuid)); | |
5ba3f43e | 1495 | shared_region->sr_uuid_copied = TRUE; |
d9a64523 | 1496 | } else { |
5ba3f43e | 1497 | #if DEVELOPMENT || DEBUG |
d9a64523 | 1498 | panic("shared_region: copyin shared_cache_header(sr_base_addr:0x%016llx sr_first_mapping:0x%016llx " |
0a7de745 A |
1499 | "offset:0 size:0x%016llx) failed with %d\n", |
1500 | (long long)shared_region->sr_base_address, | |
1501 | (long long)shared_region->sr_first_mapping, | |
1502 | (long long)sizeof(sr_cache_header), | |
1503 | error); | |
5ba3f43e A |
1504 | #endif /* DEVELOPMENT || DEBUG */ |
1505 | shared_region->sr_uuid_copied = FALSE; | |
0a7de745 | 1506 | } |
5ba3f43e A |
1507 | } |
1508 | ||
d9a64523 A |
1509 | /* |
1510 | * If the shared cache is associated with the init task (and is therefore the system shared cache), | |
1511 | * check whether it is a custom built shared cache and copy in the shared cache layout accordingly. | |
1512 | */ | |
1513 | boolean_t is_init_task = (task_pid(current_task()) == 1); | |
1514 | if (shared_region->sr_uuid_copied && is_init_task) { | |
1515 | /* Copy in the shared cache layout if we're running with a locally built shared cache */ | |
1516 | if (sr_cache_header.locallyBuiltCache) { | |
1517 | KDBG((MACHDBG_CODE(DBG_MACH_SHAREDREGION, PROCESS_SHARED_CACHE_LAYOUT)) | DBG_FUNC_START); | |
1518 | size_t image_array_length = (sr_cache_header.imagesTextCount * sizeof(struct _dyld_cache_image_text_info)); | |
1519 | struct _dyld_cache_image_text_info *sr_image_layout = kalloc(image_array_length); | |
1520 | int error = copyin((shared_region->sr_base_address + shared_region->sr_first_mapping + | |
0a7de745 | 1521 | sr_cache_header.imagesTextOffset), (char *)sr_image_layout, image_array_length); |
d9a64523 A |
1522 | if (error == 0) { |
1523 | shared_region->sr_images = kalloc(sr_cache_header.imagesTextCount * sizeof(struct dyld_uuid_info_64)); | |
1524 | for (size_t index = 0; index < sr_cache_header.imagesTextCount; index++) { | |
1525 | memcpy((char *)&shared_region->sr_images[index].imageUUID, (char *)&sr_image_layout[index].uuid, | |
0a7de745 | 1526 | sizeof(shared_region->sr_images[index].imageUUID)); |
d9a64523 A |
1527 | shared_region->sr_images[index].imageLoadAddress = sr_image_layout[index].loadAddress; |
1528 | } | |
1529 | ||
1530 | assert(sr_cache_header.imagesTextCount < UINT32_MAX); | |
1531 | shared_region->sr_images_count = (uint32_t) sr_cache_header.imagesTextCount; | |
1532 | } else { | |
1533 | #if DEVELOPMENT || DEBUG | |
1534 | panic("shared_region: copyin shared_cache_layout(sr_base_addr:0x%016llx sr_first_mapping:0x%016llx " | |
0a7de745 A |
1535 | "offset:0x%016llx size:0x%016llx) failed with %d\n", |
1536 | (long long)shared_region->sr_base_address, | |
1537 | (long long)shared_region->sr_first_mapping, | |
1538 | (long long)sr_cache_header.imagesTextOffset, | |
1539 | (long long)image_array_length, | |
1540 | error); | |
d9a64523 A |
1541 | #endif /* DEVELOPMENT || DEBUG */ |
1542 | } | |
1543 | KDBG((MACHDBG_CODE(DBG_MACH_SHAREDREGION, PROCESS_SHARED_CACHE_LAYOUT)) | DBG_FUNC_END, shared_region->sr_images_count); | |
1544 | kfree(sr_image_layout, image_array_length); | |
1545 | sr_image_layout = NULL; | |
1546 | } | |
1547 | init_task_shared_region = shared_region; | |
1548 | } | |
1549 | ||
1550 | if (kr == KERN_SUCCESS) { | |
1551 | /* | |
1552 | * If we succeeded, we know the bounds of the shared region. | |
1553 | * Trim our pmaps to only cover this range (if applicable to | |
1554 | * this platform). | |
1555 | */ | |
1556 | pmap_trim(current_map()->pmap, sr_map->pmap, sfm_min_address, sfm_min_address, sfm_max_address - sfm_min_address); | |
1557 | } | |
1558 | ||
2d21ac55 A |
1559 | /* we're done working on that shared region */ |
1560 | shared_region->sr_mapping_in_progress = FALSE; | |
1561 | thread_wakeup((event_t) &shared_region->sr_mapping_in_progress); | |
1562 | vm_shared_region_unlock(); | |
1563 | ||
1564 | done: | |
1565 | SHARED_REGION_TRACE_DEBUG( | |
1566 | ("shared_region: map(%p,%d,%p,%p,0x%llx) <- 0x%x \n", | |
0a7de745 A |
1567 | (void *)VM_KERNEL_ADDRPERM(shared_region), mappings_count, |
1568 | (void *)VM_KERNEL_ADDRPERM(mappings), | |
1569 | (void *)VM_KERNEL_ADDRPERM(file_control), file_size, kr)); | |
2d21ac55 A |
1570 | return kr; |
1571 | } | |
1572 | ||
d9a64523 A |
1573 | /* |
1574 | * Retrieve a task's shared region and grab an extra reference to | |
1575 | * make sure it doesn't disappear while the caller is using it. | |
1576 | * The caller is responsible for consuming that extra reference if | |
1577 | * necessary. | |
1578 | * | |
1579 | * This also tries to trim the pmap for the shared region. | |
1580 | */ | |
1581 | vm_shared_region_t | |
1582 | vm_shared_region_trim_and_get(task_t task) | |
1583 | { | |
1584 | vm_shared_region_t shared_region; | |
1585 | ipc_port_t sr_handle; | |
1586 | vm_named_entry_t sr_mem_entry; | |
1587 | vm_map_t sr_map; | |
1588 | ||
1589 | /* Get the shared region and the map. */ | |
1590 | shared_region = vm_shared_region_get(task); | |
1591 | if (shared_region == NULL) { | |
1592 | return NULL; | |
1593 | } | |
1594 | ||
1595 | sr_handle = shared_region->sr_mem_entry; | |
1596 | sr_mem_entry = (vm_named_entry_t) sr_handle->ip_kobject; | |
1597 | sr_map = sr_mem_entry->backing.map; | |
1598 | ||
1599 | /* Trim the pmap if possible. */ | |
1600 | pmap_trim(task->map->pmap, sr_map->pmap, 0, 0, 0); | |
1601 | ||
1602 | return shared_region; | |
1603 | } | |
1604 | ||
2d21ac55 A |
1605 | /* |
1606 | * Enter the appropriate shared region into "map" for "task". | |
1607 | * This involves looking up the shared region (and possibly creating a new | |
1608 | * one) for the desired environment, then mapping the VM sub map into the | |
1609 | * task's VM "map", with the appropriate level of pmap-nesting. | |
1610 | */ | |
1611 | kern_return_t | |
1612 | vm_shared_region_enter( | |
0a7de745 A |
1613 | struct _vm_map *map, |
1614 | struct task *task, | |
1615 | boolean_t is_64bit, | |
1616 | void *fsroot, | |
1617 | cpu_type_t cpu, | |
1618 | cpu_subtype_t cpu_subtype) | |
2d21ac55 | 1619 | { |
0a7de745 A |
1620 | kern_return_t kr; |
1621 | vm_shared_region_t shared_region; | |
1622 | vm_map_offset_t sr_address, sr_offset, target_address; | |
1623 | vm_map_size_t sr_size, mapping_size; | |
1624 | vm_map_offset_t sr_pmap_nesting_start; | |
1625 | vm_map_size_t sr_pmap_nesting_size; | |
1626 | ipc_port_t sr_handle; | |
1627 | vm_prot_t cur_prot, max_prot; | |
2d21ac55 A |
1628 | |
1629 | SHARED_REGION_TRACE_DEBUG( | |
1630 | ("shared_region: -> " | |
0a7de745 A |
1631 | "enter(map=%p,task=%p,root=%p,cpu=<%d,%d>,64bit=%d)\n", |
1632 | (void *)VM_KERNEL_ADDRPERM(map), | |
1633 | (void *)VM_KERNEL_ADDRPERM(task), | |
1634 | (void *)VM_KERNEL_ADDRPERM(fsroot), | |
1635 | cpu, cpu_subtype, is_64bit)); | |
2d21ac55 A |
1636 | |
1637 | /* lookup (create if needed) the shared region for this environment */ | |
d9a64523 | 1638 | shared_region = vm_shared_region_lookup(fsroot, cpu, cpu_subtype, is_64bit); |
2d21ac55 A |
1639 | if (shared_region == NULL) { |
1640 | /* this should not happen ! */ | |
1641 | SHARED_REGION_TRACE_ERROR( | |
1642 | ("shared_region: -> " | |
0a7de745 A |
1643 | "enter(map=%p,task=%p,root=%p,cpu=<%d,%d>,64bit=%d): " |
1644 | "lookup failed !\n", | |
1645 | (void *)VM_KERNEL_ADDRPERM(map), | |
1646 | (void *)VM_KERNEL_ADDRPERM(task), | |
1647 | (void *)VM_KERNEL_ADDRPERM(fsroot), | |
1648 | cpu, cpu_subtype, is_64bit)); | |
2d21ac55 A |
1649 | //panic("shared_region_enter: lookup failed\n"); |
1650 | return KERN_FAILURE; | |
1651 | } | |
0a7de745 | 1652 | |
2d21ac55 A |
1653 | kr = KERN_SUCCESS; |
1654 | /* no need to lock since this data is never modified */ | |
1655 | sr_address = shared_region->sr_base_address; | |
1656 | sr_size = shared_region->sr_size; | |
1657 | sr_handle = shared_region->sr_mem_entry; | |
1658 | sr_pmap_nesting_start = shared_region->sr_pmap_nesting_start; | |
1659 | sr_pmap_nesting_size = shared_region->sr_pmap_nesting_size; | |
1660 | ||
39037602 A |
1661 | cur_prot = VM_PROT_READ; |
1662 | #if __x86_64__ | |
1663 | /* | |
1664 | * XXX BINARY COMPATIBILITY | |
1665 | * java6 apparently needs to modify some code in the | |
1666 | * dyld shared cache and needs to be allowed to add | |
1667 | * write access... | |
1668 | */ | |
1669 | max_prot = VM_PROT_ALL; | |
1670 | #else /* __x86_64__ */ | |
1671 | max_prot = VM_PROT_READ; | |
1672 | #endif /* __x86_64__ */ | |
2d21ac55 A |
1673 | /* |
1674 | * Start mapping the shared region's VM sub map into the task's VM map. | |
1675 | */ | |
1676 | sr_offset = 0; | |
1677 | ||
1678 | if (sr_pmap_nesting_start > sr_address) { | |
1679 | /* we need to map a range without pmap-nesting first */ | |
1680 | target_address = sr_address; | |
1681 | mapping_size = sr_pmap_nesting_start - sr_address; | |
1682 | kr = vm_map_enter_mem_object( | |
1683 | map, | |
1684 | &target_address, | |
1685 | mapping_size, | |
1686 | 0, | |
1687 | VM_FLAGS_FIXED, | |
5ba3f43e A |
1688 | VM_MAP_KERNEL_FLAGS_NONE, |
1689 | VM_KERN_MEMORY_NONE, | |
2d21ac55 A |
1690 | sr_handle, |
1691 | sr_offset, | |
1692 | TRUE, | |
39037602 A |
1693 | cur_prot, |
1694 | max_prot, | |
2d21ac55 A |
1695 | VM_INHERIT_SHARE); |
1696 | if (kr != KERN_SUCCESS) { | |
1697 | SHARED_REGION_TRACE_ERROR( | |
d9a64523 | 1698 | ("shared_region: enter(%p,%p,%p,%d,%d,%d): " |
0a7de745 A |
1699 | "vm_map_enter(0x%llx,0x%llx,%p) error 0x%x\n", |
1700 | (void *)VM_KERNEL_ADDRPERM(map), | |
1701 | (void *)VM_KERNEL_ADDRPERM(task), | |
1702 | (void *)VM_KERNEL_ADDRPERM(fsroot), | |
1703 | cpu, cpu_subtype, is_64bit, | |
1704 | (long long)target_address, | |
1705 | (long long)mapping_size, | |
1706 | (void *)VM_KERNEL_ADDRPERM(sr_handle), kr)); | |
2d21ac55 A |
1707 | goto done; |
1708 | } | |
1709 | SHARED_REGION_TRACE_DEBUG( | |
d9a64523 | 1710 | ("shared_region: enter(%p,%p,%p,%d,%d,%d): " |
0a7de745 A |
1711 | "vm_map_enter(0x%llx,0x%llx,%p) error 0x%x\n", |
1712 | (void *)VM_KERNEL_ADDRPERM(map), | |
1713 | (void *)VM_KERNEL_ADDRPERM(task), | |
1714 | (void *)VM_KERNEL_ADDRPERM(fsroot), | |
1715 | cpu, cpu_subtype, is_64bit, | |
1716 | (long long)target_address, (long long)mapping_size, | |
1717 | (void *)VM_KERNEL_ADDRPERM(sr_handle), kr)); | |
2d21ac55 A |
1718 | sr_offset += mapping_size; |
1719 | sr_size -= mapping_size; | |
1720 | } | |
1721 | /* | |
1722 | * We may need to map several pmap-nested portions, due to platform | |
1723 | * specific restrictions on pmap nesting. | |
cb323159 | 1724 | * The pmap-nesting is triggered by the "vmkf_nested_pmap" flag... |
2d21ac55 A |
1725 | */ |
1726 | for (; | |
0a7de745 A |
1727 | sr_pmap_nesting_size > 0; |
1728 | sr_offset += mapping_size, | |
1729 | sr_size -= mapping_size, | |
1730 | sr_pmap_nesting_size -= mapping_size) { | |
cb323159 A |
1731 | vm_map_kernel_flags_t vmk_flags; |
1732 | ||
2d21ac55 A |
1733 | target_address = sr_address + sr_offset; |
1734 | mapping_size = sr_pmap_nesting_size; | |
1735 | if (mapping_size > pmap_nesting_size_max) { | |
1736 | mapping_size = (vm_map_offset_t) pmap_nesting_size_max; | |
1737 | } | |
cb323159 A |
1738 | vmk_flags = VM_MAP_KERNEL_FLAGS_NONE; |
1739 | vmk_flags.vmkf_nested_pmap = TRUE; | |
2d21ac55 A |
1740 | kr = vm_map_enter_mem_object( |
1741 | map, | |
1742 | &target_address, | |
1743 | mapping_size, | |
1744 | 0, | |
5ba3f43e | 1745 | VM_FLAGS_FIXED, |
cb323159 | 1746 | vmk_flags, |
5ba3f43e | 1747 | VM_MEMORY_SHARED_PMAP, |
2d21ac55 A |
1748 | sr_handle, |
1749 | sr_offset, | |
1750 | TRUE, | |
39037602 A |
1751 | cur_prot, |
1752 | max_prot, | |
2d21ac55 A |
1753 | VM_INHERIT_SHARE); |
1754 | if (kr != KERN_SUCCESS) { | |
1755 | SHARED_REGION_TRACE_ERROR( | |
d9a64523 | 1756 | ("shared_region: enter(%p,%p,%p,%d,%d,%d): " |
0a7de745 A |
1757 | "vm_map_enter(0x%llx,0x%llx,%p) error 0x%x\n", |
1758 | (void *)VM_KERNEL_ADDRPERM(map), | |
1759 | (void *)VM_KERNEL_ADDRPERM(task), | |
1760 | (void *)VM_KERNEL_ADDRPERM(fsroot), | |
1761 | cpu, cpu_subtype, is_64bit, | |
1762 | (long long)target_address, | |
1763 | (long long)mapping_size, | |
1764 | (void *)VM_KERNEL_ADDRPERM(sr_handle), kr)); | |
2d21ac55 A |
1765 | goto done; |
1766 | } | |
1767 | SHARED_REGION_TRACE_DEBUG( | |
d9a64523 | 1768 | ("shared_region: enter(%p,%p,%p,%d,%d,%d): " |
0a7de745 A |
1769 | "nested vm_map_enter(0x%llx,0x%llx,%p) error 0x%x\n", |
1770 | (void *)VM_KERNEL_ADDRPERM(map), | |
1771 | (void *)VM_KERNEL_ADDRPERM(task), | |
1772 | (void *)VM_KERNEL_ADDRPERM(fsroot), | |
1773 | cpu, cpu_subtype, is_64bit, | |
1774 | (long long)target_address, (long long)mapping_size, | |
1775 | (void *)VM_KERNEL_ADDRPERM(sr_handle), kr)); | |
2d21ac55 A |
1776 | } |
1777 | if (sr_size > 0) { | |
1778 | /* and there's some left to be mapped without pmap-nesting */ | |
1779 | target_address = sr_address + sr_offset; | |
1780 | mapping_size = sr_size; | |
1781 | kr = vm_map_enter_mem_object( | |
1782 | map, | |
1783 | &target_address, | |
1784 | mapping_size, | |
1785 | 0, | |
1786 | VM_FLAGS_FIXED, | |
5ba3f43e A |
1787 | VM_MAP_KERNEL_FLAGS_NONE, |
1788 | VM_KERN_MEMORY_NONE, | |
2d21ac55 A |
1789 | sr_handle, |
1790 | sr_offset, | |
1791 | TRUE, | |
39037602 A |
1792 | cur_prot, |
1793 | max_prot, | |
2d21ac55 A |
1794 | VM_INHERIT_SHARE); |
1795 | if (kr != KERN_SUCCESS) { | |
1796 | SHARED_REGION_TRACE_ERROR( | |
d9a64523 | 1797 | ("shared_region: enter(%p,%p,%p,%d,%d,%d): " |
0a7de745 A |
1798 | "vm_map_enter(0x%llx,0x%llx,%p) error 0x%x\n", |
1799 | (void *)VM_KERNEL_ADDRPERM(map), | |
1800 | (void *)VM_KERNEL_ADDRPERM(task), | |
1801 | (void *)VM_KERNEL_ADDRPERM(fsroot), | |
1802 | cpu, cpu_subtype, is_64bit, | |
1803 | (long long)target_address, | |
1804 | (long long)mapping_size, | |
1805 | (void *)VM_KERNEL_ADDRPERM(sr_handle), kr)); | |
2d21ac55 A |
1806 | goto done; |
1807 | } | |
1808 | SHARED_REGION_TRACE_DEBUG( | |
d9a64523 | 1809 | ("shared_region: enter(%p,%p,%p,%d,%d,%d): " |
0a7de745 A |
1810 | "vm_map_enter(0x%llx,0x%llx,%p) error 0x%x\n", |
1811 | (void *)VM_KERNEL_ADDRPERM(map), | |
1812 | (void *)VM_KERNEL_ADDRPERM(task), | |
1813 | (void *)VM_KERNEL_ADDRPERM(fsroot), | |
1814 | cpu, cpu_subtype, is_64bit, | |
1815 | (long long)target_address, (long long)mapping_size, | |
1816 | (void *)VM_KERNEL_ADDRPERM(sr_handle), kr)); | |
2d21ac55 A |
1817 | sr_offset += mapping_size; |
1818 | sr_size -= mapping_size; | |
1819 | } | |
1820 | assert(sr_size == 0); | |
1821 | ||
1822 | done: | |
d9a64523 A |
1823 | if (kr == KERN_SUCCESS) { |
1824 | /* let the task use that shared region */ | |
1825 | vm_shared_region_set(task, shared_region); | |
1826 | } else { | |
1827 | /* drop our reference since we're not using it */ | |
1828 | vm_shared_region_deallocate(shared_region); | |
1829 | vm_shared_region_set(task, NULL); | |
1830 | } | |
1831 | ||
2d21ac55 | 1832 | SHARED_REGION_TRACE_DEBUG( |
d9a64523 | 1833 | ("shared_region: enter(%p,%p,%p,%d,%d,%d) <- 0x%x\n", |
0a7de745 A |
1834 | (void *)VM_KERNEL_ADDRPERM(map), |
1835 | (void *)VM_KERNEL_ADDRPERM(task), | |
1836 | (void *)VM_KERNEL_ADDRPERM(fsroot), | |
1837 | cpu, cpu_subtype, is_64bit, kr)); | |
2d21ac55 A |
1838 | return kr; |
1839 | } | |
1840 | ||
0a7de745 A |
1841 | #define SANE_SLIDE_INFO_SIZE (2560*1024) /*Can be changed if needed*/ |
1842 | struct vm_shared_region_slide_info slide_info; | |
6d2010ae A |
1843 | |
1844 | kern_return_t | |
0a7de745 | 1845 | vm_shared_region_sliding_valid(uint32_t slide) |
39236c6e | 1846 | { |
6d2010ae | 1847 | kern_return_t kr = KERN_SUCCESS; |
39236c6e | 1848 | vm_shared_region_t sr = vm_shared_region_get(current_task()); |
6d2010ae | 1849 | |
39236c6e A |
1850 | /* No region yet? we're fine. */ |
1851 | if (sr == NULL) { | |
1852 | return kr; | |
1853 | } | |
1854 | ||
1855 | if ((sr->sr_slid == TRUE) && slide) { | |
0a7de745 | 1856 | if (slide != vm_shared_region_get_slide_info(sr)->slide) { |
316670eb | 1857 | printf("Only one shared region can be slid\n"); |
0a7de745 | 1858 | kr = KERN_FAILURE; |
39236c6e | 1859 | } else { |
6d2010ae A |
1860 | /* |
1861 | * Request for sliding when we've | |
1862 | * already done it with exactly the | |
1863 | * same slide value before. | |
1864 | * This isn't wrong technically but | |
1865 | * we don't want to slide again and | |
1866 | * so we return this value. | |
1867 | */ | |
0a7de745 | 1868 | kr = KERN_INVALID_ARGUMENT; |
6d2010ae A |
1869 | } |
1870 | } | |
39236c6e | 1871 | vm_shared_region_deallocate(sr); |
6d2010ae A |
1872 | return kr; |
1873 | } | |
1874 | ||
1875 | kern_return_t | |
d9a64523 | 1876 | vm_shared_region_slide_mapping( |
0a7de745 A |
1877 | vm_shared_region_t sr, |
1878 | mach_vm_size_t slide_info_size, | |
1879 | mach_vm_offset_t start, | |
1880 | mach_vm_size_t size, | |
1881 | mach_vm_offset_t slid_mapping, | |
1882 | uint32_t slide, | |
1883 | memory_object_control_t sr_file_control) | |
6d2010ae | 1884 | { |
0a7de745 A |
1885 | kern_return_t kr; |
1886 | vm_object_t object; | |
d9a64523 | 1887 | vm_shared_region_slide_info_t si; |
0a7de745 A |
1888 | vm_offset_t slide_info_entry; |
1889 | vm_map_entry_t slid_entry, tmp_entry; | |
1890 | struct vm_map_entry tmp_entry_store; | |
1891 | memory_object_t sr_pager; | |
1892 | vm_map_t sr_map; | |
1893 | int vm_flags; | |
1894 | vm_map_kernel_flags_t vmk_flags; | |
1895 | vm_map_offset_t map_addr; | |
d9a64523 A |
1896 | |
1897 | tmp_entry = VM_MAP_ENTRY_NULL; | |
1898 | sr_pager = MEMORY_OBJECT_NULL; | |
1899 | object = VM_OBJECT_NULL; | |
1900 | slide_info_entry = 0; | |
6d2010ae | 1901 | |
39236c6e A |
1902 | assert(sr->sr_slide_in_progress); |
1903 | assert(!sr->sr_slid); | |
d9a64523 A |
1904 | |
1905 | si = vm_shared_region_get_slide_info(sr); | |
1906 | assert(si->slide_object == VM_OBJECT_NULL); | |
39236c6e | 1907 | assert(si->slide_info_entry == NULL); |
6d2010ae | 1908 | |
d9a64523 A |
1909 | if (sr_file_control == MEMORY_OBJECT_CONTROL_NULL) { |
1910 | return KERN_INVALID_ARGUMENT; | |
1911 | } | |
6d2010ae | 1912 | if (slide_info_size > SANE_SLIDE_INFO_SIZE) { |
316670eb | 1913 | printf("Slide_info_size too large: %lx\n", (uintptr_t)slide_info_size); |
d9a64523 | 1914 | return KERN_FAILURE; |
6d2010ae A |
1915 | } |
1916 | ||
39236c6e | 1917 | kr = kmem_alloc(kernel_map, |
0a7de745 A |
1918 | (vm_offset_t *) &slide_info_entry, |
1919 | (vm_size_t) slide_info_size, VM_KERN_MEMORY_OSFMK); | |
39236c6e A |
1920 | if (kr != KERN_SUCCESS) { |
1921 | return kr; | |
1922 | } | |
1923 | ||
d9a64523 A |
1924 | object = memory_object_control_to_vm_object(sr_file_control); |
1925 | if (object == VM_OBJECT_NULL || object->internal) { | |
1926 | object = VM_OBJECT_NULL; | |
1927 | kr = KERN_INVALID_ADDRESS; | |
1928 | goto done; | |
1929 | } | |
6d2010ae | 1930 | |
d9a64523 | 1931 | vm_object_lock(object); |
0a7de745 | 1932 | vm_object_reference_locked(object); /* for si->slide_object */ |
d9a64523 A |
1933 | object->object_is_shared_cache = TRUE; |
1934 | vm_object_unlock(object); | |
6d2010ae | 1935 | |
d9a64523 A |
1936 | si->slide_info_entry = (vm_shared_region_slide_info_entry_t)slide_info_entry; |
1937 | si->slide_info_size = slide_info_size; | |
1938 | ||
1939 | assert(slid_mapping != (mach_vm_offset_t) -1); | |
1940 | si->slid_address = slid_mapping + sr->sr_base_address; | |
1941 | si->slide_object = object; | |
1942 | si->start = start; | |
1943 | si->end = si->start + size; | |
1944 | si->slide = slide; | |
cb323159 A |
1945 | #if defined(HAS_APPLE_PAC) |
1946 | if (sr->sr_cpu_type == CPU_TYPE_ARM64 && | |
1947 | sr->sr_cpu_subtype == CPU_SUBTYPE_ARM64E) { | |
1948 | /* arm64e has pointer authentication */ | |
1949 | si->si_ptrauth = TRUE; | |
1950 | } | |
1951 | #endif /* HAS_APPLE_PAC */ | |
d9a64523 A |
1952 | |
1953 | /* find the shared region's map entry to slide */ | |
1954 | sr_map = vm_shared_region_vm_map(sr); | |
1955 | vm_map_lock_read(sr_map); | |
1956 | if (!vm_map_lookup_entry(sr_map, | |
0a7de745 A |
1957 | slid_mapping, |
1958 | &slid_entry)) { | |
d9a64523 A |
1959 | /* no mapping there */ |
1960 | vm_map_unlock(sr_map); | |
1961 | kr = KERN_INVALID_ARGUMENT; | |
1962 | goto done; | |
1963 | } | |
1964 | /* | |
1965 | * We might want to clip the entry to cover only the portion that | |
1966 | * needs sliding (offsets si->start to si->end in the shared cache | |
1967 | * file at the bottom of the shadow chain). | |
1968 | * In practice, it seems to cover the entire DATA segment... | |
1969 | */ | |
1970 | tmp_entry_store = *slid_entry; | |
1971 | tmp_entry = &tmp_entry_store; | |
1972 | slid_entry = VM_MAP_ENTRY_NULL; | |
1973 | /* extra ref to keep object alive while map is unlocked */ | |
1974 | vm_object_reference(VME_OBJECT(tmp_entry)); | |
1975 | vm_map_unlock_read(sr_map); | |
1976 | ||
1977 | /* create a "shared_region" sliding pager */ | |
1978 | sr_pager = shared_region_pager_setup(VME_OBJECT(tmp_entry), | |
0a7de745 A |
1979 | VME_OFFSET(tmp_entry), |
1980 | si); | |
d9a64523 A |
1981 | if (sr_pager == NULL) { |
1982 | kr = KERN_RESOURCE_SHORTAGE; | |
1983 | goto done; | |
6d2010ae | 1984 | } |
39236c6e | 1985 | |
d9a64523 A |
1986 | /* map that pager over the portion of the mapping that needs sliding */ |
1987 | vm_flags = VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE; | |
1988 | vmk_flags = VM_MAP_KERNEL_FLAGS_NONE; | |
1989 | vmk_flags.vmkf_overwrite_immutable = TRUE; | |
1990 | map_addr = tmp_entry->vme_start; | |
1991 | kr = vm_map_enter_mem_object(sr_map, | |
0a7de745 A |
1992 | &map_addr, |
1993 | (tmp_entry->vme_end - | |
1994 | tmp_entry->vme_start), | |
1995 | (mach_vm_offset_t) 0, | |
1996 | vm_flags, | |
1997 | vmk_flags, | |
1998 | VM_KERN_MEMORY_NONE, | |
1999 | (ipc_port_t)(uintptr_t) sr_pager, | |
2000 | 0, | |
2001 | TRUE, | |
2002 | tmp_entry->protection, | |
2003 | tmp_entry->max_protection, | |
2004 | tmp_entry->inheritance); | |
d9a64523 A |
2005 | assertf(kr == KERN_SUCCESS, "kr = 0x%x\n", kr); |
2006 | assertf(map_addr == tmp_entry->vme_start, | |
0a7de745 A |
2007 | "map_addr=0x%llx vme_start=0x%llx tmp_entry=%p\n", |
2008 | (uint64_t)map_addr, | |
2009 | (uint64_t) tmp_entry->vme_start, | |
2010 | tmp_entry); | |
d9a64523 A |
2011 | |
2012 | /* success! */ | |
2013 | kr = KERN_SUCCESS; | |
39236c6e | 2014 | |
d9a64523 A |
2015 | done: |
2016 | if (sr_pager) { | |
39236c6e | 2017 | /* |
d9a64523 A |
2018 | * Release the sr_pager reference obtained by |
2019 | * shared_region_pager_setup(). | |
2020 | * The mapping (if it succeeded) is now holding a reference on | |
2021 | * the memory object. | |
39236c6e | 2022 | */ |
d9a64523 A |
2023 | memory_object_deallocate(sr_pager); |
2024 | sr_pager = MEMORY_OBJECT_NULL; | |
6d2010ae | 2025 | } |
d9a64523 A |
2026 | if (tmp_entry) { |
2027 | /* release extra ref on tmp_entry's VM object */ | |
2028 | vm_object_deallocate(VME_OBJECT(tmp_entry)); | |
2029 | tmp_entry = VM_MAP_ENTRY_NULL; | |
6d2010ae | 2030 | } |
6d2010ae | 2031 | |
39236c6e | 2032 | if (kr != KERN_SUCCESS) { |
d9a64523 A |
2033 | /* cleanup */ |
2034 | if (slide_info_entry) { | |
2035 | kmem_free(kernel_map, slide_info_entry, slide_info_size); | |
2036 | slide_info_entry = 0; | |
2037 | } | |
2038 | if (si->slide_object) { | |
2039 | vm_object_deallocate(si->slide_object); | |
2040 | si->slide_object = VM_OBJECT_NULL; | |
2041 | } | |
39236c6e A |
2042 | } |
2043 | return kr; | |
6d2010ae A |
2044 | } |
2045 | ||
0a7de745 A |
2046 | void* |
2047 | vm_shared_region_get_slide_info_entry(vm_shared_region_t sr) | |
2048 | { | |
39236c6e | 2049 | return (void*)sr->sr_slide_info.slide_info_entry; |
6d2010ae A |
2050 | } |
2051 | ||
39037602 A |
2052 | static kern_return_t |
2053 | vm_shared_region_slide_sanity_check_v1(vm_shared_region_slide_info_entry_v1_t s_info) | |
6d2010ae | 2054 | { |
0a7de745 A |
2055 | uint32_t pageIndex = 0; |
2056 | uint16_t entryIndex = 0; | |
6d2010ae | 2057 | uint16_t *toc = NULL; |
39037602 A |
2058 | |
2059 | toc = (uint16_t*)((uintptr_t)s_info + s_info->toc_offset); | |
0a7de745 | 2060 | for (; pageIndex < s_info->toc_count; pageIndex++) { |
39037602 A |
2061 | entryIndex = (uint16_t)(toc[pageIndex]); |
2062 | ||
2063 | if (entryIndex >= s_info->entry_count) { | |
2064 | printf("No sliding bitmap entry for pageIndex: %d at entryIndex: %d amongst %d entries\n", pageIndex, entryIndex, s_info->entry_count); | |
2065 | return KERN_FAILURE; | |
2066 | } | |
39037602 A |
2067 | } |
2068 | return KERN_SUCCESS; | |
2069 | } | |
2070 | ||
2071 | static kern_return_t | |
2072 | vm_shared_region_slide_sanity_check_v2(vm_shared_region_slide_info_entry_v2_t s_info, mach_vm_size_t slide_info_size) | |
2073 | { | |
2074 | if (s_info->page_size != PAGE_SIZE_FOR_SR_SLIDE) { | |
2075 | return KERN_FAILURE; | |
2076 | } | |
2077 | ||
2078 | /* Ensure that the slide info doesn't reference any data outside of its bounds. */ | |
2079 | ||
2080 | uint32_t page_starts_count = s_info->page_starts_count; | |
2081 | uint32_t page_extras_count = s_info->page_extras_count; | |
2082 | mach_vm_size_t num_trailing_entries = page_starts_count + page_extras_count; | |
2083 | if (num_trailing_entries < page_starts_count) { | |
2084 | return KERN_FAILURE; | |
2085 | } | |
2086 | ||
2087 | /* Scale by sizeof(uint16_t). Hard-coding the size simplifies the overflow check. */ | |
2088 | mach_vm_size_t trailing_size = num_trailing_entries << 1; | |
2089 | if (trailing_size >> 1 != num_trailing_entries) { | |
2090 | return KERN_FAILURE; | |
2091 | } | |
2092 | ||
2093 | mach_vm_size_t required_size = sizeof(*s_info) + trailing_size; | |
2094 | if (required_size < sizeof(*s_info)) { | |
2095 | return KERN_FAILURE; | |
2096 | } | |
2097 | ||
2098 | if (required_size > slide_info_size) { | |
2099 | return KERN_FAILURE; | |
2100 | } | |
2101 | ||
2102 | return KERN_SUCCESS; | |
2103 | } | |
2104 | ||
d9a64523 A |
2105 | static kern_return_t |
2106 | vm_shared_region_slide_sanity_check_v3(vm_shared_region_slide_info_entry_v3_t s_info, mach_vm_size_t slide_info_size) | |
2107 | { | |
2108 | if (s_info->page_size != PAGE_SIZE_FOR_SR_SLIDE) { | |
2109 | printf("vm_shared_region_slide_sanity_check_v3: s_info->page_size != PAGE_SIZE_FOR_SR_SL 0x%llx != 0x%llx\n", (uint64_t)s_info->page_size, (uint64_t)PAGE_SIZE_FOR_SR_SLIDE); | |
2110 | return KERN_FAILURE; | |
2111 | } | |
2112 | ||
2113 | uint32_t page_starts_count = s_info->page_starts_count; | |
2114 | mach_vm_size_t num_trailing_entries = page_starts_count; | |
2115 | mach_vm_size_t trailing_size = num_trailing_entries << 1; | |
2116 | mach_vm_size_t required_size = sizeof(*s_info) + trailing_size; | |
2117 | if (required_size < sizeof(*s_info)) { | |
2118 | printf("vm_shared_region_slide_sanity_check_v3: required_size != sizeof(*s_info) 0x%llx != 0x%llx\n", (uint64_t)required_size, (uint64_t)sizeof(*s_info)); | |
2119 | return KERN_FAILURE; | |
2120 | } | |
2121 | ||
2122 | if (required_size > slide_info_size) { | |
2123 | printf("vm_shared_region_slide_sanity_check_v3: required_size != slide_info_size 0x%llx != 0x%llx\n", (uint64_t)required_size, (uint64_t)slide_info_size); | |
2124 | return KERN_FAILURE; | |
2125 | } | |
2126 | ||
2127 | return KERN_SUCCESS; | |
2128 | } | |
2129 | ||
2130 | static kern_return_t | |
2131 | vm_shared_region_slide_sanity_check_v4(vm_shared_region_slide_info_entry_v4_t s_info, mach_vm_size_t slide_info_size) | |
2132 | { | |
0a7de745 A |
2133 | if (s_info->page_size != PAGE_SIZE_FOR_SR_SLIDE) { |
2134 | return KERN_FAILURE; | |
2135 | } | |
2136 | ||
2137 | /* Ensure that the slide info doesn't reference any data outside of its bounds. */ | |
2138 | ||
2139 | uint32_t page_starts_count = s_info->page_starts_count; | |
2140 | uint32_t page_extras_count = s_info->page_extras_count; | |
2141 | mach_vm_size_t num_trailing_entries = page_starts_count + page_extras_count; | |
2142 | if (num_trailing_entries < page_starts_count) { | |
2143 | return KERN_FAILURE; | |
2144 | } | |
2145 | ||
2146 | /* Scale by sizeof(uint16_t). Hard-coding the size simplifies the overflow check. */ | |
2147 | mach_vm_size_t trailing_size = num_trailing_entries << 1; | |
2148 | if (trailing_size >> 1 != num_trailing_entries) { | |
2149 | return KERN_FAILURE; | |
2150 | } | |
2151 | ||
2152 | mach_vm_size_t required_size = sizeof(*s_info) + trailing_size; | |
2153 | if (required_size < sizeof(*s_info)) { | |
2154 | return KERN_FAILURE; | |
2155 | } | |
2156 | ||
2157 | if (required_size > slide_info_size) { | |
2158 | return KERN_FAILURE; | |
2159 | } | |
2160 | ||
2161 | return KERN_SUCCESS; | |
d9a64523 A |
2162 | } |
2163 | ||
2164 | ||
39037602 A |
2165 | kern_return_t |
2166 | vm_shared_region_slide_sanity_check(vm_shared_region_t sr) | |
2167 | { | |
39236c6e | 2168 | vm_shared_region_slide_info_t si; |
6d2010ae A |
2169 | vm_shared_region_slide_info_entry_t s_info; |
2170 | kern_return_t kr; | |
2171 | ||
39236c6e A |
2172 | si = vm_shared_region_get_slide_info(sr); |
2173 | s_info = si->slide_info_entry; | |
6d2010ae A |
2174 | |
2175 | kr = mach_vm_protect(kernel_map, | |
0a7de745 A |
2176 | (mach_vm_offset_t)(vm_offset_t)s_info, |
2177 | (mach_vm_size_t) si->slide_info_size, | |
2178 | TRUE, VM_PROT_READ); | |
6d2010ae A |
2179 | if (kr != KERN_SUCCESS) { |
2180 | panic("vm_shared_region_slide_sanity_check: vm_protect() error 0x%x\n", kr); | |
2181 | } | |
2182 | ||
39037602 A |
2183 | if (s_info->version == 1) { |
2184 | kr = vm_shared_region_slide_sanity_check_v1(&s_info->v1); | |
2185 | } else if (s_info->version == 2) { | |
2186 | kr = vm_shared_region_slide_sanity_check_v2(&s_info->v2, si->slide_info_size); | |
d9a64523 A |
2187 | } else if (s_info->version == 3) { |
2188 | kr = vm_shared_region_slide_sanity_check_v3(&s_info->v3, si->slide_info_size); | |
0a7de745 A |
2189 | } else if (s_info->version == 4) { |
2190 | kr = vm_shared_region_slide_sanity_check_v4(&s_info->v4, si->slide_info_size); | |
39037602 A |
2191 | } else { |
2192 | goto fail; | |
6d2010ae | 2193 | } |
39037602 A |
2194 | if (kr != KERN_SUCCESS) { |
2195 | goto fail; | |
2196 | } | |
2197 | ||
6d2010ae A |
2198 | return KERN_SUCCESS; |
2199 | fail: | |
39236c6e | 2200 | if (si->slide_info_entry != NULL) { |
6d2010ae | 2201 | kmem_free(kernel_map, |
0a7de745 A |
2202 | (vm_offset_t) si->slide_info_entry, |
2203 | (vm_size_t) si->slide_info_size); | |
2204 | ||
39236c6e | 2205 | vm_object_deallocate(si->slide_object); |
0a7de745 | 2206 | si->slide_object = NULL; |
39236c6e | 2207 | si->start = 0; |
0a7de745 | 2208 | si->end = 0; |
39236c6e A |
2209 | si->slide = 0; |
2210 | si->slide_info_entry = NULL; | |
2211 | si->slide_info_size = 0; | |
6d2010ae A |
2212 | } |
2213 | return KERN_FAILURE; | |
2214 | } | |
2215 | ||
39037602 A |
2216 | static kern_return_t |
2217 | vm_shared_region_slide_page_v1(vm_shared_region_slide_info_t si, vm_offset_t vaddr, uint32_t pageIndex) | |
6d2010ae A |
2218 | { |
2219 | uint16_t *toc = NULL; | |
2220 | slide_info_entry_toc_t bitmap = NULL; | |
0a7de745 | 2221 | uint32_t i = 0, j = 0; |
6d2010ae | 2222 | uint8_t b = 0; |
39236c6e | 2223 | uint32_t slide = si->slide; |
d9a64523 | 2224 | int is_64 = task_has_64Bit_addr(current_task()); |
6d2010ae | 2225 | |
39037602 | 2226 | vm_shared_region_slide_info_entry_v1_t s_info = &si->slide_info_entry->v1; |
6d2010ae | 2227 | toc = (uint16_t*)((uintptr_t)s_info + s_info->toc_offset); |
0a7de745 | 2228 | |
6d2010ae A |
2229 | if (pageIndex >= s_info->toc_count) { |
2230 | printf("No slide entry for this page in toc. PageIndex: %d Toc Count: %d\n", pageIndex, s_info->toc_count); | |
2231 | } else { | |
2232 | uint16_t entryIndex = (uint16_t)(toc[pageIndex]); | |
2233 | slide_info_entry_toc_t slide_info_entries = (slide_info_entry_toc_t)((uintptr_t)s_info + s_info->entry_offset); | |
0a7de745 | 2234 | |
6d2010ae A |
2235 | if (entryIndex >= s_info->entry_count) { |
2236 | printf("No sliding bitmap entry for entryIndex: %d amongst %d entries\n", entryIndex, s_info->entry_count); | |
2237 | } else { | |
2238 | bitmap = &slide_info_entries[entryIndex]; | |
2239 | ||
0a7de745 | 2240 | for (i = 0; i < NUM_SLIDING_BITMAPS_PER_PAGE; ++i) { |
6d2010ae | 2241 | b = bitmap->entry[i]; |
0a7de745 A |
2242 | if (b != 0) { |
2243 | for (j = 0; j < 8; ++j) { | |
2244 | if (b & (1 << j)) { | |
6d2010ae A |
2245 | uint32_t *ptr_to_slide; |
2246 | uint32_t old_value; | |
2247 | ||
0a7de745 | 2248 | ptr_to_slide = (uint32_t*)((uintptr_t)(vaddr) + (sizeof(uint32_t) * (i * 8 + j))); |
6d2010ae A |
2249 | old_value = *ptr_to_slide; |
2250 | *ptr_to_slide += slide; | |
2251 | if (is_64 && *ptr_to_slide < old_value) { | |
2252 | /* | |
2253 | * We just slid the low 32 bits of a 64-bit pointer | |
2254 | * and it looks like there should have been a carry-over | |
2255 | * to the upper 32 bits. | |
2256 | * The sliding failed... | |
2257 | */ | |
316670eb | 2258 | printf("vm_shared_region_slide() carry over: i=%d j=%d b=0x%x slide=0x%x old=0x%x new=0x%x\n", |
0a7de745 | 2259 | i, j, b, slide, old_value, *ptr_to_slide); |
6d2010ae A |
2260 | return KERN_FAILURE; |
2261 | } | |
2262 | } | |
2263 | } | |
2264 | } | |
2265 | } | |
2266 | } | |
2267 | } | |
2268 | ||
2269 | return KERN_SUCCESS; | |
2270 | } | |
2271 | ||
39037602 A |
2272 | static kern_return_t |
2273 | rebase_chain_32( | |
2274 | uint8_t *page_content, | |
2275 | uint16_t start_offset, | |
2276 | uint32_t slide_amount, | |
2277 | vm_shared_region_slide_info_entry_v2_t s_info) | |
2278 | { | |
2279 | const uint32_t last_page_offset = PAGE_SIZE_FOR_SR_SLIDE - sizeof(uint32_t); | |
2280 | ||
2281 | const uint32_t delta_mask = (uint32_t)(s_info->delta_mask); | |
2282 | const uint32_t value_mask = ~delta_mask; | |
2283 | const uint32_t value_add = (uint32_t)(s_info->value_add); | |
2284 | const uint32_t delta_shift = __builtin_ctzll(delta_mask) - 2; | |
2285 | ||
2286 | uint32_t page_offset = start_offset; | |
2287 | uint32_t delta = 1; | |
2288 | ||
2289 | while (delta != 0 && page_offset <= last_page_offset) { | |
2290 | uint8_t *loc; | |
2291 | uint32_t value; | |
2292 | ||
2293 | loc = page_content + page_offset; | |
2294 | memcpy(&value, loc, sizeof(value)); | |
2295 | delta = (value & delta_mask) >> delta_shift; | |
2296 | value &= value_mask; | |
2297 | ||
2298 | if (value != 0) { | |
2299 | value += value_add; | |
2300 | value += slide_amount; | |
2301 | } | |
2302 | memcpy(loc, &value, sizeof(value)); | |
2303 | page_offset += delta; | |
2304 | } | |
2305 | ||
2306 | /* If the offset went past the end of the page, then the slide data is invalid. */ | |
2307 | if (page_offset > last_page_offset) { | |
2308 | return KERN_FAILURE; | |
2309 | } | |
2310 | return KERN_SUCCESS; | |
2311 | } | |
2312 | ||
2313 | static kern_return_t | |
2314 | rebase_chain_64( | |
2315 | uint8_t *page_content, | |
2316 | uint16_t start_offset, | |
2317 | uint32_t slide_amount, | |
2318 | vm_shared_region_slide_info_entry_v2_t s_info) | |
2319 | { | |
2320 | const uint32_t last_page_offset = PAGE_SIZE_FOR_SR_SLIDE - sizeof(uint64_t); | |
2321 | ||
2322 | const uint64_t delta_mask = s_info->delta_mask; | |
2323 | const uint64_t value_mask = ~delta_mask; | |
2324 | const uint64_t value_add = s_info->value_add; | |
2325 | const uint64_t delta_shift = __builtin_ctzll(delta_mask) - 2; | |
2326 | ||
2327 | uint32_t page_offset = start_offset; | |
2328 | uint32_t delta = 1; | |
2329 | ||
2330 | while (delta != 0 && page_offset <= last_page_offset) { | |
2331 | uint8_t *loc; | |
2332 | uint64_t value; | |
2333 | ||
2334 | loc = page_content + page_offset; | |
2335 | memcpy(&value, loc, sizeof(value)); | |
2336 | delta = (uint32_t)((value & delta_mask) >> delta_shift); | |
2337 | value &= value_mask; | |
2338 | ||
2339 | if (value != 0) { | |
2340 | value += value_add; | |
2341 | value += slide_amount; | |
2342 | } | |
2343 | memcpy(loc, &value, sizeof(value)); | |
2344 | page_offset += delta; | |
2345 | } | |
2346 | ||
2347 | if (page_offset + sizeof(uint32_t) == PAGE_SIZE_FOR_SR_SLIDE) { | |
2348 | /* If a pointer straddling the page boundary needs to be adjusted, then | |
2349 | * add the slide to the lower half. The encoding guarantees that the upper | |
2350 | * half on the next page will need no masking. | |
2351 | * | |
2352 | * This assumes a little-endian machine and that the region being slid | |
2353 | * never crosses a 4 GB boundary. */ | |
2354 | ||
2355 | uint8_t *loc = page_content + page_offset; | |
2356 | uint32_t value; | |
2357 | ||
2358 | memcpy(&value, loc, sizeof(value)); | |
2359 | value += slide_amount; | |
2360 | memcpy(loc, &value, sizeof(value)); | |
2361 | } else if (page_offset > last_page_offset) { | |
2362 | return KERN_FAILURE; | |
2363 | } | |
2364 | ||
2365 | return KERN_SUCCESS; | |
2366 | } | |
2367 | ||
2368 | static kern_return_t | |
2369 | rebase_chain( | |
2370 | boolean_t is_64, | |
2371 | uint32_t pageIndex, | |
2372 | uint8_t *page_content, | |
2373 | uint16_t start_offset, | |
2374 | uint32_t slide_amount, | |
2375 | vm_shared_region_slide_info_entry_v2_t s_info) | |
2376 | { | |
2377 | kern_return_t kr; | |
2378 | if (is_64) { | |
2379 | kr = rebase_chain_64(page_content, start_offset, slide_amount, s_info); | |
2380 | } else { | |
2381 | kr = rebase_chain_32(page_content, start_offset, slide_amount, s_info); | |
2382 | } | |
2383 | ||
2384 | if (kr != KERN_SUCCESS) { | |
2385 | printf("vm_shared_region_slide_page() offset overflow: pageIndex=%u, start_offset=%u, slide_amount=%u\n", | |
0a7de745 | 2386 | pageIndex, start_offset, slide_amount); |
39037602 A |
2387 | } |
2388 | return kr; | |
2389 | } | |
2390 | ||
2391 | static kern_return_t | |
2392 | vm_shared_region_slide_page_v2(vm_shared_region_slide_info_t si, vm_offset_t vaddr, uint32_t pageIndex) | |
2393 | { | |
2394 | vm_shared_region_slide_info_entry_v2_t s_info = &si->slide_info_entry->v2; | |
2395 | const uint32_t slide_amount = si->slide; | |
2396 | ||
2397 | /* The high bits of the delta_mask field are nonzero precisely when the shared | |
2398 | * cache is 64-bit. */ | |
2399 | const boolean_t is_64 = (s_info->delta_mask >> 32) != 0; | |
2400 | ||
2401 | const uint16_t *page_starts = (uint16_t *)((uintptr_t)s_info + s_info->page_starts_offset); | |
2402 | const uint16_t *page_extras = (uint16_t *)((uintptr_t)s_info + s_info->page_extras_offset); | |
2403 | ||
2404 | uint8_t *page_content = (uint8_t *)vaddr; | |
2405 | uint16_t page_entry; | |
2406 | ||
2407 | if (pageIndex >= s_info->page_starts_count) { | |
2408 | printf("vm_shared_region_slide_page() did not find page start in slide info: pageIndex=%u, count=%u\n", | |
0a7de745 | 2409 | pageIndex, s_info->page_starts_count); |
39037602 A |
2410 | return KERN_FAILURE; |
2411 | } | |
2412 | page_entry = page_starts[pageIndex]; | |
2413 | ||
2414 | if (page_entry == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE) { | |
2415 | return KERN_SUCCESS; | |
2416 | } | |
2417 | ||
2418 | if (page_entry & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA) { | |
2419 | uint16_t chain_index = page_entry & DYLD_CACHE_SLIDE_PAGE_VALUE; | |
2420 | uint16_t info; | |
2421 | ||
2422 | do { | |
2423 | uint16_t page_start_offset; | |
2424 | kern_return_t kr; | |
2425 | ||
2426 | if (chain_index >= s_info->page_extras_count) { | |
2427 | printf("vm_shared_region_slide_page() out-of-bounds extras index: index=%u, count=%u\n", | |
0a7de745 | 2428 | chain_index, s_info->page_extras_count); |
39037602 A |
2429 | return KERN_FAILURE; |
2430 | } | |
2431 | info = page_extras[chain_index]; | |
2432 | page_start_offset = (info & DYLD_CACHE_SLIDE_PAGE_VALUE) << DYLD_CACHE_SLIDE_PAGE_OFFSET_SHIFT; | |
2433 | ||
2434 | kr = rebase_chain(is_64, pageIndex, page_content, page_start_offset, slide_amount, s_info); | |
2435 | if (kr != KERN_SUCCESS) { | |
2436 | return KERN_FAILURE; | |
2437 | } | |
2438 | ||
2439 | chain_index++; | |
2440 | } while (!(info & DYLD_CACHE_SLIDE_PAGE_ATTR_END)); | |
2441 | } else { | |
2442 | const uint32_t page_start_offset = page_entry << DYLD_CACHE_SLIDE_PAGE_OFFSET_SHIFT; | |
2443 | kern_return_t kr; | |
2444 | ||
2445 | kr = rebase_chain(is_64, pageIndex, page_content, page_start_offset, slide_amount, s_info); | |
2446 | if (kr != KERN_SUCCESS) { | |
2447 | return KERN_FAILURE; | |
2448 | } | |
2449 | } | |
2450 | ||
2451 | return KERN_SUCCESS; | |
2452 | } | |
2453 | ||
d9a64523 A |
2454 | |
2455 | static kern_return_t | |
2456 | vm_shared_region_slide_page_v3(vm_shared_region_slide_info_t si, vm_offset_t vaddr, __unused mach_vm_offset_t uservaddr, uint32_t pageIndex) | |
2457 | { | |
2458 | vm_shared_region_slide_info_entry_v3_t s_info = &si->slide_info_entry->v3; | |
2459 | const uint32_t slide_amount = si->slide; | |
2460 | ||
2461 | uint8_t *page_content = (uint8_t *)vaddr; | |
2462 | uint16_t page_entry; | |
2463 | ||
2464 | if (pageIndex >= s_info->page_starts_count) { | |
2465 | printf("vm_shared_region_slide_page() did not find page start in slide info: pageIndex=%u, count=%u\n", | |
0a7de745 | 2466 | pageIndex, s_info->page_starts_count); |
d9a64523 A |
2467 | return KERN_FAILURE; |
2468 | } | |
2469 | page_entry = s_info->page_starts[pageIndex]; | |
2470 | ||
2471 | if (page_entry == DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE) { | |
2472 | return KERN_SUCCESS; | |
2473 | } | |
2474 | ||
2475 | uint8_t* rebaseLocation = page_content; | |
2476 | uint64_t delta = page_entry; | |
2477 | do { | |
2478 | rebaseLocation += delta; | |
2479 | uint64_t value; | |
2480 | memcpy(&value, rebaseLocation, sizeof(value)); | |
0a7de745 | 2481 | delta = ((value & 0x3FF8000000000000) >> 51) * sizeof(uint64_t); |
d9a64523 A |
2482 | |
2483 | // A pointer is one of : | |
2484 | // { | |
2485 | // uint64_t pointerValue : 51; | |
2486 | // uint64_t offsetToNextPointer : 11; | |
2487 | // uint64_t isBind : 1 = 0; | |
2488 | // uint64_t authenticated : 1 = 0; | |
2489 | // } | |
2490 | // { | |
2491 | // uint32_t offsetFromSharedCacheBase; | |
2492 | // uint16_t diversityData; | |
2493 | // uint16_t hasAddressDiversity : 1; | |
2494 | // uint16_t hasDKey : 1; | |
2495 | // uint16_t hasBKey : 1; | |
2496 | // uint16_t offsetToNextPointer : 11; | |
2497 | // uint16_t isBind : 1; | |
2498 | // uint16_t authenticated : 1 = 1; | |
2499 | // } | |
2500 | ||
2501 | bool isBind = (value & (1ULL << 62)) == 1; | |
2502 | if (isBind) { | |
2503 | return KERN_FAILURE; | |
2504 | } | |
2505 | ||
cb323159 A |
2506 | #if defined(HAS_APPLE_PAC) |
2507 | uint16_t diversity_data = (uint16_t)(value >> 32); | |
2508 | bool hasAddressDiversity = (value & (1ULL << 48)) != 0; | |
2509 | ptrauth_key key = (ptrauth_key)((value >> 49) & 0x3); | |
2510 | #endif /* HAS_APPLE_PAC */ | |
d9a64523 A |
2511 | bool isAuthenticated = (value & (1ULL << 63)) != 0; |
2512 | ||
2513 | if (isAuthenticated) { | |
2514 | // The new value for a rebase is the low 32-bits of the threaded value plus the slide. | |
2515 | value = (value & 0xFFFFFFFF) + slide_amount; | |
2516 | // Add in the offset from the mach_header | |
2517 | const uint64_t value_add = s_info->value_add; | |
2518 | value += value_add; | |
2519 | ||
cb323159 A |
2520 | #if defined(HAS_APPLE_PAC) |
2521 | uint64_t discriminator = diversity_data; | |
2522 | if (hasAddressDiversity) { | |
2523 | // First calculate a new discriminator using the address of where we are trying to store the value | |
2524 | uintptr_t pageOffset = rebaseLocation - page_content; | |
2525 | discriminator = __builtin_ptrauth_blend_discriminator((void*)(((uintptr_t)uservaddr) + pageOffset), discriminator); | |
2526 | } | |
2527 | ||
2528 | if (si->si_ptrauth && | |
2529 | !(BootArgs->bootFlags & kBootFlagsDisableUserJOP)) { | |
2530 | /* | |
2531 | * these pointers are used in user mode. disable the kernel key diversification | |
2532 | * so we can sign them for use in user mode. | |
2533 | */ | |
2534 | value = (uintptr_t)pmap_sign_user_ptr((void *)value, key, discriminator); | |
2535 | } | |
2536 | #endif /* HAS_APPLE_PAC */ | |
d9a64523 A |
2537 | } else { |
2538 | // The new value for a rebase is the low 51-bits of the threaded value plus the slide. | |
2539 | // Regular pointer which needs to fit in 51-bits of value. | |
2540 | // C++ RTTI uses the top bit, so we'll allow the whole top-byte | |
2541 | // and the bottom 43-bits to be fit in to 51-bits. | |
2542 | uint64_t top8Bits = value & 0x0007F80000000000ULL; | |
2543 | uint64_t bottom43Bits = value & 0x000007FFFFFFFFFFULL; | |
0a7de745 | 2544 | uint64_t targetValue = (top8Bits << 13) | bottom43Bits; |
d9a64523 A |
2545 | value = targetValue + slide_amount; |
2546 | } | |
2547 | ||
2548 | memcpy(rebaseLocation, &value, sizeof(value)); | |
2549 | } while (delta != 0); | |
2550 | ||
2551 | return KERN_SUCCESS; | |
2552 | } | |
2553 | ||
2554 | static kern_return_t | |
2555 | rebase_chainv4( | |
0a7de745 A |
2556 | uint8_t *page_content, |
2557 | uint16_t start_offset, | |
2558 | uint32_t slide_amount, | |
2559 | vm_shared_region_slide_info_entry_v4_t s_info) | |
d9a64523 | 2560 | { |
0a7de745 A |
2561 | const uint32_t last_page_offset = PAGE_SIZE_FOR_SR_SLIDE - sizeof(uint32_t); |
2562 | ||
2563 | const uint32_t delta_mask = (uint32_t)(s_info->delta_mask); | |
2564 | const uint32_t value_mask = ~delta_mask; | |
2565 | const uint32_t value_add = (uint32_t)(s_info->value_add); | |
2566 | const uint32_t delta_shift = __builtin_ctzll(delta_mask) - 2; | |
2567 | ||
2568 | uint32_t page_offset = start_offset; | |
2569 | uint32_t delta = 1; | |
2570 | ||
2571 | while (delta != 0 && page_offset <= last_page_offset) { | |
2572 | uint8_t *loc; | |
2573 | uint32_t value; | |
2574 | ||
2575 | loc = page_content + page_offset; | |
2576 | memcpy(&value, loc, sizeof(value)); | |
2577 | delta = (value & delta_mask) >> delta_shift; | |
2578 | value &= value_mask; | |
2579 | ||
2580 | if ((value & 0xFFFF8000) == 0) { | |
2581 | // small positive non-pointer, use as-is | |
2582 | } else if ((value & 0x3FFF8000) == 0x3FFF8000) { | |
2583 | // small negative non-pointer | |
2584 | value |= 0xC0000000; | |
2585 | } else { | |
2586 | // pointer that needs rebasing | |
2587 | value += value_add; | |
2588 | value += slide_amount; | |
2589 | } | |
2590 | memcpy(loc, &value, sizeof(value)); | |
2591 | page_offset += delta; | |
2592 | } | |
2593 | ||
2594 | /* If the offset went past the end of the page, then the slide data is invalid. */ | |
2595 | if (page_offset > last_page_offset) { | |
2596 | return KERN_FAILURE; | |
2597 | } | |
2598 | return KERN_SUCCESS; | |
d9a64523 A |
2599 | } |
2600 | ||
2601 | static kern_return_t | |
2602 | vm_shared_region_slide_page_v4(vm_shared_region_slide_info_t si, vm_offset_t vaddr, uint32_t pageIndex) | |
2603 | { | |
0a7de745 A |
2604 | vm_shared_region_slide_info_entry_v4_t s_info = &si->slide_info_entry->v4; |
2605 | const uint32_t slide_amount = si->slide; | |
2606 | ||
2607 | const uint16_t *page_starts = (uint16_t *)((uintptr_t)s_info + s_info->page_starts_offset); | |
2608 | const uint16_t *page_extras = (uint16_t *)((uintptr_t)s_info + s_info->page_extras_offset); | |
2609 | ||
2610 | uint8_t *page_content = (uint8_t *)vaddr; | |
2611 | uint16_t page_entry; | |
2612 | ||
2613 | if (pageIndex >= s_info->page_starts_count) { | |
2614 | printf("vm_shared_region_slide_page() did not find page start in slide info: pageIndex=%u, count=%u\n", | |
2615 | pageIndex, s_info->page_starts_count); | |
2616 | return KERN_FAILURE; | |
2617 | } | |
2618 | page_entry = page_starts[pageIndex]; | |
2619 | ||
2620 | if (page_entry == DYLD_CACHE_SLIDE4_PAGE_NO_REBASE) { | |
2621 | return KERN_SUCCESS; | |
2622 | } | |
2623 | ||
2624 | if (page_entry & DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA) { | |
2625 | uint16_t chain_index = page_entry & DYLD_CACHE_SLIDE4_PAGE_INDEX; | |
2626 | uint16_t info; | |
2627 | ||
2628 | do { | |
2629 | uint16_t page_start_offset; | |
2630 | kern_return_t kr; | |
2631 | ||
2632 | if (chain_index >= s_info->page_extras_count) { | |
2633 | printf("vm_shared_region_slide_page() out-of-bounds extras index: index=%u, count=%u\n", | |
2634 | chain_index, s_info->page_extras_count); | |
2635 | return KERN_FAILURE; | |
2636 | } | |
2637 | info = page_extras[chain_index]; | |
2638 | page_start_offset = (info & DYLD_CACHE_SLIDE4_PAGE_INDEX) << DYLD_CACHE_SLIDE_PAGE_OFFSET_SHIFT; | |
2639 | ||
2640 | kr = rebase_chainv4(page_content, page_start_offset, slide_amount, s_info); | |
2641 | if (kr != KERN_SUCCESS) { | |
2642 | return KERN_FAILURE; | |
2643 | } | |
2644 | ||
2645 | chain_index++; | |
2646 | } while (!(info & DYLD_CACHE_SLIDE4_PAGE_EXTRA_END)); | |
2647 | } else { | |
2648 | const uint32_t page_start_offset = page_entry << DYLD_CACHE_SLIDE_PAGE_OFFSET_SHIFT; | |
2649 | kern_return_t kr; | |
2650 | ||
2651 | kr = rebase_chainv4(page_content, page_start_offset, slide_amount, s_info); | |
2652 | if (kr != KERN_SUCCESS) { | |
2653 | return KERN_FAILURE; | |
2654 | } | |
2655 | } | |
2656 | ||
2657 | return KERN_SUCCESS; | |
d9a64523 A |
2658 | } |
2659 | ||
2660 | ||
2661 | ||
39037602 | 2662 | kern_return_t |
d9a64523 | 2663 | vm_shared_region_slide_page(vm_shared_region_slide_info_t si, vm_offset_t vaddr, mach_vm_offset_t uservaddr, uint32_t pageIndex) |
39037602 A |
2664 | { |
2665 | if (si->slide_info_entry->version == 1) { | |
2666 | return vm_shared_region_slide_page_v1(si, vaddr, pageIndex); | |
d9a64523 | 2667 | } else if (si->slide_info_entry->version == 2) { |
39037602 | 2668 | return vm_shared_region_slide_page_v2(si, vaddr, pageIndex); |
0a7de745 | 2669 | } else if (si->slide_info_entry->version == 3) { |
d9a64523 | 2670 | return vm_shared_region_slide_page_v3(si, vaddr, uservaddr, pageIndex); |
0a7de745 A |
2671 | } else if (si->slide_info_entry->version == 4) { |
2672 | return vm_shared_region_slide_page_v4(si, vaddr, pageIndex); | |
d9a64523 | 2673 | } else { |
0a7de745 A |
2674 | return KERN_FAILURE; |
2675 | } | |
39037602 A |
2676 | } |
2677 | ||
2d21ac55 A |
2678 | /******************************************************************************/ |
2679 | /* Comm page support */ | |
2680 | /******************************************************************************/ | |
2681 | ||
2682 | ipc_port_t commpage32_handle = IPC_PORT_NULL; | |
2683 | ipc_port_t commpage64_handle = IPC_PORT_NULL; | |
2684 | vm_named_entry_t commpage32_entry = NULL; | |
2685 | vm_named_entry_t commpage64_entry = NULL; | |
2686 | vm_map_t commpage32_map = VM_MAP_NULL; | |
2687 | vm_map_t commpage64_map = VM_MAP_NULL; | |
2688 | ||
316670eb A |
2689 | ipc_port_t commpage_text32_handle = IPC_PORT_NULL; |
2690 | ipc_port_t commpage_text64_handle = IPC_PORT_NULL; | |
2691 | vm_named_entry_t commpage_text32_entry = NULL; | |
2692 | vm_named_entry_t commpage_text64_entry = NULL; | |
2693 | vm_map_t commpage_text32_map = VM_MAP_NULL; | |
2694 | vm_map_t commpage_text64_map = VM_MAP_NULL; | |
2695 | ||
2696 | user32_addr_t commpage_text32_location = (user32_addr_t) _COMM_PAGE32_TEXT_START; | |
2697 | user64_addr_t commpage_text64_location = (user64_addr_t) _COMM_PAGE64_TEXT_START; | |
2698 | ||
2699 | #if defined(__i386__) || defined(__x86_64__) | |
2d21ac55 A |
2700 | /* |
2701 | * Create a memory entry, VM submap and pmap for one commpage. | |
2702 | */ | |
2703 | static void | |
2704 | _vm_commpage_init( | |
0a7de745 A |
2705 | ipc_port_t *handlep, |
2706 | vm_map_size_t size) | |
2d21ac55 | 2707 | { |
0a7de745 A |
2708 | kern_return_t kr; |
2709 | vm_named_entry_t mem_entry; | |
2710 | vm_map_t new_map; | |
2d21ac55 A |
2711 | |
2712 | SHARED_REGION_TRACE_DEBUG( | |
2713 | ("commpage: -> _init(0x%llx)\n", | |
0a7de745 | 2714 | (long long)size)); |
2d21ac55 A |
2715 | |
2716 | kr = mach_memory_entry_allocate(&mem_entry, | |
0a7de745 | 2717 | handlep); |
2d21ac55 A |
2718 | if (kr != KERN_SUCCESS) { |
2719 | panic("_vm_commpage_init: could not allocate mem_entry"); | |
2720 | } | |
cb323159 | 2721 | new_map = vm_map_create(pmap_create_options(NULL, 0, 0), 0, size, PMAP_CREATE_64BIT); |
2d21ac55 A |
2722 | if (new_map == VM_MAP_NULL) { |
2723 | panic("_vm_commpage_init: could not allocate VM map"); | |
2724 | } | |
2725 | mem_entry->backing.map = new_map; | |
2726 | mem_entry->internal = TRUE; | |
2727 | mem_entry->is_sub_map = TRUE; | |
2728 | mem_entry->offset = 0; | |
2729 | mem_entry->protection = VM_PROT_ALL; | |
2730 | mem_entry->size = size; | |
2731 | ||
2732 | SHARED_REGION_TRACE_DEBUG( | |
2733 | ("commpage: _init(0x%llx) <- %p\n", | |
0a7de745 | 2734 | (long long)size, (void *)VM_KERNEL_ADDRPERM(*handlep))); |
2d21ac55 | 2735 | } |
316670eb A |
2736 | #endif |
2737 | ||
2738 | ||
2739 | /* | |
0a7de745 | 2740 | * Initialize the comm text pages at boot time |
316670eb | 2741 | */ |
0a7de745 A |
2742 | extern u_int32_t random(void); |
2743 | void | |
316670eb A |
2744 | vm_commpage_text_init(void) |
2745 | { | |
2746 | SHARED_REGION_TRACE_DEBUG( | |
2747 | ("commpage text: ->init()\n")); | |
2748 | #if defined(__i386__) || defined(__x86_64__) | |
2749 | /* create the 32 bit comm text page */ | |
2750 | unsigned int offset = (random() % _PFZ32_SLIDE_RANGE) << PAGE_SHIFT; /* restricting to 32bMAX-2PAGE */ | |
2751 | _vm_commpage_init(&commpage_text32_handle, _COMM_PAGE_TEXT_AREA_LENGTH); | |
2752 | commpage_text32_entry = (vm_named_entry_t) commpage_text32_handle->ip_kobject; | |
2753 | commpage_text32_map = commpage_text32_entry->backing.map; | |
2754 | commpage_text32_location = (user32_addr_t) (_COMM_PAGE32_TEXT_START + offset); | |
2755 | /* XXX if (cpu_is_64bit_capable()) ? */ | |
0a7de745 | 2756 | /* create the 64-bit comm page */ |
316670eb | 2757 | offset = (random() % _PFZ64_SLIDE_RANGE) << PAGE_SHIFT; /* restricting sliding upto 2Mb range */ |
0a7de745 A |
2758 | _vm_commpage_init(&commpage_text64_handle, _COMM_PAGE_TEXT_AREA_LENGTH); |
2759 | commpage_text64_entry = (vm_named_entry_t) commpage_text64_handle->ip_kobject; | |
2760 | commpage_text64_map = commpage_text64_entry->backing.map; | |
316670eb A |
2761 | commpage_text64_location = (user64_addr_t) (_COMM_PAGE64_TEXT_START + offset); |
2762 | ||
2763 | commpage_text_populate(); | |
5ba3f43e | 2764 | #elif defined(__arm64__) || defined(__arm__) |
316670eb A |
2765 | #else |
2766 | #error Unknown architecture. | |
2767 | #endif /* __i386__ || __x86_64__ */ | |
2768 | /* populate the routines in here */ | |
2769 | SHARED_REGION_TRACE_DEBUG( | |
0a7de745 | 2770 | ("commpage text: init() <-\n")); |
316670eb | 2771 | } |
2d21ac55 A |
2772 | |
2773 | /* | |
2774 | * Initialize the comm pages at boot time. | |
2775 | */ | |
2776 | void | |
2777 | vm_commpage_init(void) | |
2778 | { | |
2779 | SHARED_REGION_TRACE_DEBUG( | |
2780 | ("commpage: -> init()\n")); | |
2781 | ||
316670eb | 2782 | #if defined(__i386__) || defined(__x86_64__) |
2d21ac55 A |
2783 | /* create the 32-bit comm page */ |
2784 | _vm_commpage_init(&commpage32_handle, _COMM_PAGE32_AREA_LENGTH); | |
2785 | commpage32_entry = (vm_named_entry_t) commpage32_handle->ip_kobject; | |
2786 | commpage32_map = commpage32_entry->backing.map; | |
2787 | ||
2788 | /* XXX if (cpu_is_64bit_capable()) ? */ | |
2789 | /* create the 64-bit comm page */ | |
2790 | _vm_commpage_init(&commpage64_handle, _COMM_PAGE64_AREA_LENGTH); | |
2791 | commpage64_entry = (vm_named_entry_t) commpage64_handle->ip_kobject; | |
2792 | commpage64_map = commpage64_entry->backing.map; | |
2793 | ||
316670eb A |
2794 | #endif /* __i386__ || __x86_64__ */ |
2795 | ||
2d21ac55 A |
2796 | /* populate them according to this specific platform */ |
2797 | commpage_populate(); | |
b0d623f7 | 2798 | __commpage_setup = 1; |
cb323159 | 2799 | #if !CONFIG_EMBEDDED |
b0d623f7 A |
2800 | if (__system_power_source == 0) { |
2801 | post_sys_powersource_internal(0, 1); | |
2802 | } | |
cb323159 | 2803 | #endif |
2d21ac55 A |
2804 | |
2805 | SHARED_REGION_TRACE_DEBUG( | |
2806 | ("commpage: init() <-\n")); | |
2807 | } | |
2808 | ||
2809 | /* | |
2810 | * Enter the appropriate comm page into the task's address space. | |
2811 | * This is called at exec() time via vm_map_exec(). | |
2812 | */ | |
2813 | kern_return_t | |
2814 | vm_commpage_enter( | |
0a7de745 A |
2815 | vm_map_t map, |
2816 | task_t task, | |
2817 | boolean_t is64bit) | |
2d21ac55 | 2818 | { |
0a7de745 | 2819 | #if defined(__arm__) |
5ba3f43e A |
2820 | #pragma unused(is64bit) |
2821 | (void)task; | |
2822 | (void)map; | |
2823 | return KERN_SUCCESS; | |
0a7de745 | 2824 | #elif defined(__arm64__) |
5ba3f43e A |
2825 | #pragma unused(is64bit) |
2826 | (void)task; | |
2827 | (void)map; | |
2828 | pmap_insert_sharedpage(vm_map_pmap(map)); | |
2829 | return KERN_SUCCESS; | |
2830 | #else | |
0a7de745 A |
2831 | ipc_port_t commpage_handle, commpage_text_handle; |
2832 | vm_map_offset_t commpage_address, objc_address, commpage_text_address; | |
2833 | vm_map_size_t commpage_size, objc_size, commpage_text_size; | |
2834 | int vm_flags; | |
2835 | vm_map_kernel_flags_t vmk_flags; | |
2836 | kern_return_t kr; | |
2d21ac55 A |
2837 | |
2838 | SHARED_REGION_TRACE_DEBUG( | |
2839 | ("commpage: -> enter(%p,%p)\n", | |
0a7de745 A |
2840 | (void *)VM_KERNEL_ADDRPERM(map), |
2841 | (void *)VM_KERNEL_ADDRPERM(task))); | |
2d21ac55 | 2842 | |
316670eb | 2843 | commpage_text_size = _COMM_PAGE_TEXT_AREA_LENGTH; |
2d21ac55 | 2844 | /* the comm page is likely to be beyond the actual end of the VM map */ |
5ba3f43e A |
2845 | vm_flags = VM_FLAGS_FIXED; |
2846 | vmk_flags = VM_MAP_KERNEL_FLAGS_NONE; | |
2847 | vmk_flags.vmkf_beyond_max = TRUE; | |
2d21ac55 A |
2848 | |
2849 | /* select the appropriate comm page for this task */ | |
0a7de745 | 2850 | assert(!(is64bit ^ vm_map_is_64bit(map))); |
39037602 | 2851 | if (is64bit) { |
2d21ac55 A |
2852 | commpage_handle = commpage64_handle; |
2853 | commpage_address = (vm_map_offset_t) _COMM_PAGE64_BASE_ADDRESS; | |
2854 | commpage_size = _COMM_PAGE64_AREA_LENGTH; | |
2855 | objc_size = _COMM_PAGE64_OBJC_SIZE; | |
2856 | objc_address = _COMM_PAGE64_OBJC_BASE; | |
316670eb A |
2857 | commpage_text_handle = commpage_text64_handle; |
2858 | commpage_text_address = (vm_map_offset_t) commpage_text64_location; | |
2d21ac55 A |
2859 | } else { |
2860 | commpage_handle = commpage32_handle; | |
2861 | commpage_address = | |
0a7de745 | 2862 | (vm_map_offset_t)(unsigned) _COMM_PAGE32_BASE_ADDRESS; |
2d21ac55 A |
2863 | commpage_size = _COMM_PAGE32_AREA_LENGTH; |
2864 | objc_size = _COMM_PAGE32_OBJC_SIZE; | |
2865 | objc_address = _COMM_PAGE32_OBJC_BASE; | |
316670eb A |
2866 | commpage_text_handle = commpage_text32_handle; |
2867 | commpage_text_address = (vm_map_offset_t) commpage_text32_location; | |
2d21ac55 A |
2868 | } |
2869 | ||
0a7de745 | 2870 | vm_tag_t tag = VM_KERN_MEMORY_NONE; |
2d21ac55 A |
2871 | if ((commpage_address & (pmap_nesting_size_min - 1)) == 0 && |
2872 | (commpage_size & (pmap_nesting_size_min - 1)) == 0) { | |
2873 | /* the commpage is properly aligned or sized for pmap-nesting */ | |
5ba3f43e | 2874 | tag = VM_MEMORY_SHARED_PMAP; |
cb323159 | 2875 | vmk_flags.vmkf_nested_pmap = TRUE; |
2d21ac55 | 2876 | } |
2d21ac55 A |
2877 | /* map the comm page in the task's address space */ |
2878 | assert(commpage_handle != IPC_PORT_NULL); | |
2879 | kr = vm_map_enter_mem_object( | |
2880 | map, | |
2881 | &commpage_address, | |
2882 | commpage_size, | |
2883 | 0, | |
2884 | vm_flags, | |
5ba3f43e A |
2885 | vmk_flags, |
2886 | tag, | |
2d21ac55 A |
2887 | commpage_handle, |
2888 | 0, | |
2889 | FALSE, | |
316670eb A |
2890 | VM_PROT_READ, |
2891 | VM_PROT_READ, | |
2d21ac55 A |
2892 | VM_INHERIT_SHARE); |
2893 | if (kr != KERN_SUCCESS) { | |
2894 | SHARED_REGION_TRACE_ERROR( | |
2895 | ("commpage: enter(%p,0x%llx,0x%llx) " | |
0a7de745 A |
2896 | "commpage %p mapping failed 0x%x\n", |
2897 | (void *)VM_KERNEL_ADDRPERM(map), | |
2898 | (long long)commpage_address, | |
2899 | (long long)commpage_size, | |
2900 | (void *)VM_KERNEL_ADDRPERM(commpage_handle), kr)); | |
2d21ac55 A |
2901 | } |
2902 | ||
316670eb A |
2903 | /* map the comm text page in the task's address space */ |
2904 | assert(commpage_text_handle != IPC_PORT_NULL); | |
2905 | kr = vm_map_enter_mem_object( | |
2906 | map, | |
2907 | &commpage_text_address, | |
2908 | commpage_text_size, | |
2909 | 0, | |
2910 | vm_flags, | |
5ba3f43e A |
2911 | vmk_flags, |
2912 | tag, | |
316670eb A |
2913 | commpage_text_handle, |
2914 | 0, | |
2915 | FALSE, | |
0a7de745 A |
2916 | VM_PROT_READ | VM_PROT_EXECUTE, |
2917 | VM_PROT_READ | VM_PROT_EXECUTE, | |
316670eb A |
2918 | VM_INHERIT_SHARE); |
2919 | if (kr != KERN_SUCCESS) { | |
2920 | SHARED_REGION_TRACE_ERROR( | |
2921 | ("commpage text: enter(%p,0x%llx,0x%llx) " | |
0a7de745 A |
2922 | "commpage text %p mapping failed 0x%x\n", |
2923 | (void *)VM_KERNEL_ADDRPERM(map), | |
2924 | (long long)commpage_text_address, | |
2925 | (long long)commpage_text_size, | |
2926 | (void *)VM_KERNEL_ADDRPERM(commpage_text_handle), kr)); | |
316670eb A |
2927 | } |
2928 | ||
2d21ac55 A |
2929 | /* |
2930 | * Since we're here, we also pre-allocate some virtual space for the | |
2931 | * Objective-C run-time, if needed... | |
2932 | */ | |
2933 | if (objc_size != 0) { | |
2934 | kr = vm_map_enter_mem_object( | |
2935 | map, | |
2936 | &objc_address, | |
2937 | objc_size, | |
2938 | 0, | |
5ba3f43e A |
2939 | VM_FLAGS_FIXED, |
2940 | vmk_flags, | |
2941 | tag, | |
2d21ac55 A |
2942 | IPC_PORT_NULL, |
2943 | 0, | |
2944 | FALSE, | |
2945 | VM_PROT_ALL, | |
2946 | VM_PROT_ALL, | |
2947 | VM_INHERIT_DEFAULT); | |
2948 | if (kr != KERN_SUCCESS) { | |
2949 | SHARED_REGION_TRACE_ERROR( | |
2950 | ("commpage: enter(%p,0x%llx,0x%llx) " | |
0a7de745 A |
2951 | "objc mapping failed 0x%x\n", |
2952 | (void *)VM_KERNEL_ADDRPERM(map), | |
2953 | (long long)objc_address, | |
2954 | (long long)objc_size, kr)); | |
2d21ac55 A |
2955 | } |
2956 | } | |
2957 | ||
2958 | SHARED_REGION_TRACE_DEBUG( | |
2959 | ("commpage: enter(%p,%p) <- 0x%x\n", | |
0a7de745 A |
2960 | (void *)VM_KERNEL_ADDRPERM(map), |
2961 | (void *)VM_KERNEL_ADDRPERM(task), kr)); | |
2d21ac55 | 2962 | return kr; |
5ba3f43e | 2963 | #endif |
2d21ac55 | 2964 | } |
b0d623f7 | 2965 | |
39236c6e A |
2966 | int |
2967 | vm_shared_region_slide(uint32_t slide, | |
0a7de745 A |
2968 | mach_vm_offset_t entry_start_address, |
2969 | mach_vm_size_t entry_size, | |
2970 | mach_vm_offset_t slide_start, | |
2971 | mach_vm_size_t slide_size, | |
2972 | mach_vm_offset_t slid_mapping, | |
2973 | memory_object_control_t sr_file_control) | |
39236c6e A |
2974 | { |
2975 | void *slide_info_entry = NULL; | |
0a7de745 A |
2976 | int error; |
2977 | vm_shared_region_t sr; | |
39236c6e A |
2978 | |
2979 | SHARED_REGION_TRACE_DEBUG( | |
2980 | ("vm_shared_region_slide: -> slide %#x, entry_start %#llx, entry_size %#llx, slide_start %#llx, slide_size %#llx\n", | |
0a7de745 | 2981 | slide, entry_start_address, entry_size, slide_start, slide_size)); |
39236c6e A |
2982 | |
2983 | sr = vm_shared_region_get(current_task()); | |
2984 | if (sr == NULL) { | |
2985 | printf("%s: no shared region?\n", __FUNCTION__); | |
2986 | SHARED_REGION_TRACE_DEBUG( | |
2987 | ("vm_shared_region_slide: <- %d (no shared region)\n", | |
0a7de745 | 2988 | KERN_FAILURE)); |
39236c6e A |
2989 | return KERN_FAILURE; |
2990 | } | |
2991 | ||
2992 | /* | |
2993 | * Protect from concurrent access. | |
2994 | */ | |
2995 | vm_shared_region_lock(); | |
0a7de745 | 2996 | while (sr->sr_slide_in_progress) { |
39236c6e A |
2997 | vm_shared_region_sleep(&sr->sr_slide_in_progress, THREAD_UNINT); |
2998 | } | |
2999 | if (sr->sr_slid | |
5ba3f43e | 3000 | #ifndef CONFIG_EMBEDDED |
0a7de745 | 3001 | || shared_region_completed_slide |
5ba3f43e | 3002 | #endif |
0a7de745 | 3003 | ) { |
39236c6e A |
3004 | vm_shared_region_unlock(); |
3005 | ||
3006 | vm_shared_region_deallocate(sr); | |
3007 | printf("%s: shared region already slid?\n", __FUNCTION__); | |
3008 | SHARED_REGION_TRACE_DEBUG( | |
3009 | ("vm_shared_region_slide: <- %d (already slid)\n", | |
0a7de745 | 3010 | KERN_FAILURE)); |
39236c6e A |
3011 | return KERN_FAILURE; |
3012 | } | |
3013 | ||
3014 | sr->sr_slide_in_progress = TRUE; | |
3015 | vm_shared_region_unlock(); | |
3016 | ||
d9a64523 | 3017 | error = vm_shared_region_slide_mapping(sr, |
0a7de745 A |
3018 | slide_size, |
3019 | entry_start_address, | |
3020 | entry_size, | |
3021 | slid_mapping, | |
3022 | slide, | |
3023 | sr_file_control); | |
d9a64523 | 3024 | if (error) { |
39236c6e A |
3025 | printf("slide_info initialization failed with kr=%d\n", error); |
3026 | goto done; | |
3027 | } | |
3028 | ||
3029 | slide_info_entry = vm_shared_region_get_slide_info_entry(sr); | |
0a7de745 | 3030 | if (slide_info_entry == NULL) { |
39236c6e | 3031 | error = KERN_FAILURE; |
0a7de745 | 3032 | } else { |
39236c6e | 3033 | error = copyin((user_addr_t)slide_start, |
0a7de745 A |
3034 | slide_info_entry, |
3035 | (vm_size_t)slide_size); | |
3036 | if (error) { | |
39236c6e A |
3037 | error = KERN_INVALID_ADDRESS; |
3038 | } | |
3039 | } | |
3040 | if (error) { | |
3041 | goto done; | |
3042 | } | |
0a7de745 | 3043 | |
39236c6e | 3044 | if (vm_shared_region_slide_sanity_check(sr) != KERN_SUCCESS) { |
0a7de745 A |
3045 | error = KERN_INVALID_ARGUMENT; |
3046 | printf("Sanity Check failed for slide_info\n"); | |
3047 | } else { | |
39236c6e A |
3048 | #if DEBUG |
3049 | printf("Succesfully init slide_info with start_address: %p region_size: %ld slide_header_size: %ld\n", | |
0a7de745 A |
3050 | (void*)(uintptr_t)entry_start_address, |
3051 | (unsigned long)entry_size, | |
3052 | (unsigned long)slide_size); | |
39236c6e A |
3053 | #endif |
3054 | } | |
3055 | done: | |
3056 | vm_shared_region_lock(); | |
3057 | ||
3058 | assert(sr->sr_slide_in_progress); | |
3059 | assert(sr->sr_slid == FALSE); | |
3060 | sr->sr_slide_in_progress = FALSE; | |
3061 | thread_wakeup(&sr->sr_slide_in_progress); | |
3062 | ||
3063 | if (error == KERN_SUCCESS) { | |
3064 | sr->sr_slid = TRUE; | |
3065 | ||
3066 | /* | |
3067 | * We don't know how to tear down a slid shared region today, because | |
3068 | * we would have to invalidate all the pages that have been slid | |
0a7de745 A |
3069 | * atomically with respect to anyone mapping the shared region afresh. |
3070 | * Therefore, take a dangling reference to prevent teardown. | |
39236c6e | 3071 | */ |
0a7de745 | 3072 | sr->sr_ref_count++; |
5ba3f43e | 3073 | #ifndef CONFIG_EMBEDDED |
39236c6e | 3074 | shared_region_completed_slide = TRUE; |
5ba3f43e | 3075 | #endif |
39236c6e A |
3076 | } |
3077 | vm_shared_region_unlock(); | |
3078 | ||
3079 | vm_shared_region_deallocate(sr); | |
3080 | ||
3081 | SHARED_REGION_TRACE_DEBUG( | |
3082 | ("vm_shared_region_slide: <- %d\n", | |
0a7de745 | 3083 | error)); |
39236c6e A |
3084 | |
3085 | return error; | |
3086 | } | |
b0d623f7 | 3087 | |
0a7de745 | 3088 | /* |
b0d623f7 A |
3089 | * This is called from powermanagement code to let kernel know the current source of power. |
3090 | * 0 if it is external source (connected to power ) | |
3091 | * 1 if it is internal power source ie battery | |
3092 | */ | |
3093 | void | |
cb323159 | 3094 | #if !CONFIG_EMBEDDED |
b0d623f7 A |
3095 | post_sys_powersource(int i) |
3096 | #else | |
3097 | post_sys_powersource(__unused int i) | |
3098 | #endif | |
3099 | { | |
cb323159 | 3100 | #if !CONFIG_EMBEDDED |
b0d623f7 | 3101 | post_sys_powersource_internal(i, 0); |
cb323159 | 3102 | #endif |
b0d623f7 A |
3103 | } |
3104 | ||
3105 | ||
cb323159 | 3106 | #if !CONFIG_EMBEDDED |
b0d623f7 A |
3107 | static void |
3108 | post_sys_powersource_internal(int i, int internal) | |
3109 | { | |
0a7de745 | 3110 | if (internal == 0) { |
b0d623f7 | 3111 | __system_power_source = i; |
0a7de745 | 3112 | } |
b0d623f7 A |
3113 | |
3114 | if (__commpage_setup != 0) { | |
0a7de745 | 3115 | if (__system_power_source != 0) { |
b0d623f7 | 3116 | commpage_set_spin_count(0); |
0a7de745 | 3117 | } else { |
b0d623f7 | 3118 | commpage_set_spin_count(MP_SPIN_TRIES); |
0a7de745 | 3119 | } |
b0d623f7 A |
3120 | } |
3121 | } | |
cb323159 | 3122 | #endif |