]> git.saurik.com Git - apple/xnu.git/blame - osfmk/i386/pmap_common.c
xnu-3789.70.16.tar.gz
[apple/xnu.git] / osfmk / i386 / pmap_common.c
CommitLineData
6d2010ae 1/*
39037602 2 * Copyright (c) 2000-2016 Apple Inc. All rights reserved.
6d2010ae
A
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28#include <vm/pmap.h>
316670eb 29#include <kern/ledger.h>
6d2010ae
A
30#include <i386/pmap_internal.h>
31
316670eb 32
6d2010ae
A
33/*
34 * Each entry in the pv_head_table is locked by a bit in the
35 * pv_lock_table. The lock bits are accessed by the physical
36 * address of the page they lock.
37 */
38
39char *pv_lock_table; /* pointer to array of bits */
40char *pv_hash_lock_table;
41
42pv_rooted_entry_t pv_head_table; /* array of entries, one per
43 * page */
44uint32_t pv_hashed_free_count = 0;
45uint32_t pv_hashed_kern_free_count = 0;
46
47pmap_pagetable_corruption_record_t pmap_pagetable_corruption_records[PMAP_PAGETABLE_CORRUPTION_MAX_LOG];
48uint32_t pmap_pagetable_corruption_incidents;
49uint64_t pmap_pagetable_corruption_last_abstime = (~(0ULL) >> 1);
50uint64_t pmap_pagetable_corruption_interval_abstime;
51thread_call_t pmap_pagetable_corruption_log_call;
52static thread_call_data_t pmap_pagetable_corruption_log_call_data;
53boolean_t pmap_pagetable_corruption_timeout = FALSE;
54
55volatile uint32_t mappingrecurse = 0;
56
57uint32_t pv_hashed_low_water_mark, pv_hashed_kern_low_water_mark, pv_hashed_alloc_chunk, pv_hashed_kern_alloc_chunk;
58
59thread_t mapping_replenish_thread;
60event_t mapping_replenish_event, pmap_user_pv_throttle_event;
61
62uint64_t pmap_pv_throttle_stat, pmap_pv_throttled_waiters;
63
39037602
A
64int pmap_asserts_enabled = DEBUG;
65int pmap_asserts_traced = 0;
66
6d2010ae 67unsigned int pmap_cache_attributes(ppnum_t pn) {
3e170ce0 68 if (pmap_get_cache_attributes(pn, FALSE) & INTEL_PTE_NCACHE)
6d2010ae
A
69 return (VM_WIMG_IO);
70 else
71 return (VM_WIMG_COPYBACK);
72}
73
74void pmap_set_cache_attributes(ppnum_t pn, unsigned int cacheattr) {
75 unsigned int current, template = 0;
76 int pai;
77
78 if (cacheattr & VM_MEM_NOT_CACHEABLE) {
79 if(!(cacheattr & VM_MEM_GUARDED))
80 template |= PHYS_PTA;
81 template |= PHYS_NCACHE;
82 }
83
84 pmap_intr_assert();
85
86 assert((pn != vm_page_fictitious_addr) && (pn != vm_page_guard_addr));
87
88 pai = ppn_to_pai(pn);
89
90 if (!IS_MANAGED_PAGE(pai)) {
91 return;
92 }
93
94 /* override cache attributes for this phys page
95 * Does not walk through existing mappings to adjust,
96 * assumes page is disconnected
97 */
98
99 LOCK_PVH(pai);
100
101 pmap_update_cache_attributes_locked(pn, template);
102
103 current = pmap_phys_attributes[pai] & PHYS_CACHEABILITY_MASK;
104 pmap_phys_attributes[pai] &= ~PHYS_CACHEABILITY_MASK;
105 pmap_phys_attributes[pai] |= template;
106
107 UNLOCK_PVH(pai);
108
109 if ((template & PHYS_NCACHE) && !(current & PHYS_NCACHE)) {
110 pmap_sync_page_attributes_phys(pn);
111 }
112}
113
3e170ce0 114unsigned pmap_get_cache_attributes(ppnum_t pn, boolean_t is_ept) {
6d2010ae
A
115 if (last_managed_page == 0)
116 return 0;
117
3e170ce0
A
118 if (!IS_MANAGED_PAGE(ppn_to_pai(pn)))
119 return PTE_NCACHE(is_ept);
6d2010ae
A
120
121 /*
122 * The cache attributes are read locklessly for efficiency.
123 */
124 unsigned int attr = pmap_phys_attributes[ppn_to_pai(pn)];
125 unsigned int template = 0;
3e170ce0
A
126
127 /*
128 * The PTA bit is currently unsupported for EPT PTEs.
129 */
130 if ((attr & PHYS_PTA) && !is_ept)
6d2010ae 131 template |= INTEL_PTE_PTA;
3e170ce0
A
132
133 /*
134 * If the page isn't marked as NCACHE, the default for EPT entries
135 * is WB.
136 */
6d2010ae 137 if (attr & PHYS_NCACHE)
3e170ce0
A
138 template |= PTE_NCACHE(is_ept);
139 else if (is_ept)
140 template |= INTEL_EPT_WB;
141
6d2010ae
A
142 return template;
143}
144
3e170ce0
A
145boolean_t
146pmap_has_managed_page(ppnum_t first, ppnum_t last)
147{
148 ppnum_t pn;
149 boolean_t result;
150
151 assert(last_managed_page);
152 assert(first <= last);
153
154 for (result = FALSE, pn = first;
155 !result
156 && (pn <= last)
157 && (pn <= last_managed_page);
158 pn++)
159 {
160 result = (0 != (pmap_phys_attributes[pn] & PHYS_MANAGED));
161 }
162
163 return (result);
164}
6d2010ae
A
165
166boolean_t
167pmap_is_noencrypt(ppnum_t pn)
168{
169 int pai;
170
171 pai = ppn_to_pai(pn);
172
173 if (!IS_MANAGED_PAGE(pai))
7ddcb079 174 return (FALSE);
6d2010ae
A
175
176 if (pmap_phys_attributes[pai] & PHYS_NOENCRYPT)
177 return (TRUE);
178
179 return (FALSE);
180}
181
182
183void
184pmap_set_noencrypt(ppnum_t pn)
185{
186 int pai;
187
188 pai = ppn_to_pai(pn);
189
190 if (IS_MANAGED_PAGE(pai)) {
191 LOCK_PVH(pai);
192
193 pmap_phys_attributes[pai] |= PHYS_NOENCRYPT;
194
195 UNLOCK_PVH(pai);
196 }
197}
198
199
200void
201pmap_clear_noencrypt(ppnum_t pn)
202{
203 int pai;
204
205 pai = ppn_to_pai(pn);
206
207 if (IS_MANAGED_PAGE(pai)) {
7ddcb079
A
208 /*
209 * synchronization at VM layer prevents PHYS_NOENCRYPT
210 * from changing state, so we don't need the lock to inspect
211 */
212 if (pmap_phys_attributes[pai] & PHYS_NOENCRYPT) {
213 LOCK_PVH(pai);
6d2010ae 214
7ddcb079 215 pmap_phys_attributes[pai] &= ~PHYS_NOENCRYPT;
6d2010ae 216
7ddcb079
A
217 UNLOCK_PVH(pai);
218 }
6d2010ae
A
219 }
220}
221
222void
223compute_pmap_gc_throttle(void *arg __unused)
224{
225
226}
227
228
fe8ab488
A
229void
230pmap_lock_phys_page(ppnum_t pn)
231{
232 int pai;
233
234 pai = ppn_to_pai(pn);
235
236 if (IS_MANAGED_PAGE(pai)) {
237 LOCK_PVH(pai);
238 } else
239 simple_lock(&phys_backup_lock);
240}
241
242
243void
244pmap_unlock_phys_page(ppnum_t pn)
245{
246 int pai;
247
248 pai = ppn_to_pai(pn);
249
250 if (IS_MANAGED_PAGE(pai)) {
251 UNLOCK_PVH(pai);
252 } else
253 simple_unlock(&phys_backup_lock);
254}
255
256
257
6d2010ae
A
258__private_extern__ void
259pmap_pagetable_corruption_msg_log(int (*log_func)(const char * fmt, ...)__printflike(1,2)) {
260 if (pmap_pagetable_corruption_incidents > 0) {
261 int i, e = MIN(pmap_pagetable_corruption_incidents, PMAP_PAGETABLE_CORRUPTION_MAX_LOG);
262 (*log_func)("%u pagetable corruption incident(s) detected, timeout: %u\n", pmap_pagetable_corruption_incidents, pmap_pagetable_corruption_timeout);
263 for (i = 0; i < e; i++) {
264 (*log_func)("Incident 0x%x, reason: 0x%x, action: 0x%x, time: 0x%llx\n", pmap_pagetable_corruption_records[i].incident, pmap_pagetable_corruption_records[i].reason, pmap_pagetable_corruption_records[i].action, pmap_pagetable_corruption_records[i].abstime);
265 }
266 }
267}
268
269static inline void
270pmap_pagetable_corruption_log_setup(void) {
271 if (pmap_pagetable_corruption_log_call == NULL) {
272 nanotime_to_absolutetime(PMAP_PAGETABLE_CORRUPTION_INTERVAL, 0, &pmap_pagetable_corruption_interval_abstime);
273 thread_call_setup(&pmap_pagetable_corruption_log_call_data,
274 (thread_call_func_t) pmap_pagetable_corruption_msg_log,
275 (thread_call_param_t) &printf);
276 pmap_pagetable_corruption_log_call = &pmap_pagetable_corruption_log_call_data;
277 }
278}
279
280void
281mapping_free_prime(void)
282{
283 unsigned i;
284 pv_hashed_entry_t pvh_e;
285 pv_hashed_entry_t pvh_eh;
286 pv_hashed_entry_t pvh_et;
287 int pv_cnt;
288
289 /* Scale based on DRAM size */
290 pv_hashed_low_water_mark = MAX(PV_HASHED_LOW_WATER_MARK_DEFAULT, ((uint32_t)(sane_size >> 30)) * 2000);
291 pv_hashed_low_water_mark = MIN(pv_hashed_low_water_mark, 16000);
292 /* Alterable via sysctl */
293 pv_hashed_kern_low_water_mark = MAX(PV_HASHED_KERN_LOW_WATER_MARK_DEFAULT, ((uint32_t)(sane_size >> 30)) * 1000);
294 pv_hashed_kern_low_water_mark = MIN(pv_hashed_kern_low_water_mark, 16000);
295 pv_hashed_kern_alloc_chunk = PV_HASHED_KERN_ALLOC_CHUNK_INITIAL;
296 pv_hashed_alloc_chunk = PV_HASHED_ALLOC_CHUNK_INITIAL;
297
298 pv_cnt = 0;
299 pvh_eh = pvh_et = PV_HASHED_ENTRY_NULL;
300
301 for (i = 0; i < (5 * PV_HASHED_ALLOC_CHUNK_INITIAL); i++) {
302 pvh_e = (pv_hashed_entry_t) zalloc(pv_hashed_list_zone);
303
304 pvh_e->qlink.next = (queue_entry_t)pvh_eh;
305 pvh_eh = pvh_e;
306
307 if (pvh_et == PV_HASHED_ENTRY_NULL)
308 pvh_et = pvh_e;
309 pv_cnt++;
310 }
311 PV_HASHED_FREE_LIST(pvh_eh, pvh_et, pv_cnt);
312
313 pv_cnt = 0;
314 pvh_eh = pvh_et = PV_HASHED_ENTRY_NULL;
315 for (i = 0; i < PV_HASHED_KERN_ALLOC_CHUNK_INITIAL; i++) {
316 pvh_e = (pv_hashed_entry_t) zalloc(pv_hashed_list_zone);
317
318 pvh_e->qlink.next = (queue_entry_t)pvh_eh;
319 pvh_eh = pvh_e;
320
321 if (pvh_et == PV_HASHED_ENTRY_NULL)
322 pvh_et = pvh_e;
323 pv_cnt++;
324 }
325 PV_HASHED_KERN_FREE_LIST(pvh_eh, pvh_et, pv_cnt);
326}
327
328void mapping_replenish(void);
329
330void mapping_adjust(void) {
331 kern_return_t mres;
332
333 pmap_pagetable_corruption_log_setup();
334
335 mres = kernel_thread_start_priority((thread_continue_t)mapping_replenish, NULL, MAXPRI_KERNEL, &mapping_replenish_thread);
336 if (mres != KERN_SUCCESS) {
337 panic("pmap: mapping_replenish_thread creation failed");
338 }
339 thread_deallocate(mapping_replenish_thread);
340}
341
342unsigned pmap_mapping_thread_wakeups;
343unsigned pmap_kernel_reserve_replenish_stat;
344unsigned pmap_user_reserve_replenish_stat;
345unsigned pmap_kern_reserve_alloc_stat;
346
39037602
A
347__attribute__((noreturn))
348void
349mapping_replenish(void)
6d2010ae
A
350{
351 pv_hashed_entry_t pvh_e;
352 pv_hashed_entry_t pvh_eh;
353 pv_hashed_entry_t pvh_et;
354 int pv_cnt;
355 unsigned i;
356
357 /* We qualify for VM privileges...*/
358 current_thread()->options |= TH_OPT_VMPRIV;
359
360 for (;;) {
361
362 while (pv_hashed_kern_free_count < pv_hashed_kern_low_water_mark) {
363 pv_cnt = 0;
364 pvh_eh = pvh_et = PV_HASHED_ENTRY_NULL;
365
366 for (i = 0; i < pv_hashed_kern_alloc_chunk; i++) {
367 pvh_e = (pv_hashed_entry_t) zalloc(pv_hashed_list_zone);
368 pvh_e->qlink.next = (queue_entry_t)pvh_eh;
369 pvh_eh = pvh_e;
370
371 if (pvh_et == PV_HASHED_ENTRY_NULL)
372 pvh_et = pvh_e;
373 pv_cnt++;
374 }
375 pmap_kernel_reserve_replenish_stat += pv_cnt;
376 PV_HASHED_KERN_FREE_LIST(pvh_eh, pvh_et, pv_cnt);
377 }
378
379 pv_cnt = 0;
380 pvh_eh = pvh_et = PV_HASHED_ENTRY_NULL;
381
382 if (pv_hashed_free_count < pv_hashed_low_water_mark) {
383 for (i = 0; i < pv_hashed_alloc_chunk; i++) {
384 pvh_e = (pv_hashed_entry_t) zalloc(pv_hashed_list_zone);
385
386 pvh_e->qlink.next = (queue_entry_t)pvh_eh;
387 pvh_eh = pvh_e;
388
389 if (pvh_et == PV_HASHED_ENTRY_NULL)
390 pvh_et = pvh_e;
391 pv_cnt++;
392 }
393 pmap_user_reserve_replenish_stat += pv_cnt;
394 PV_HASHED_FREE_LIST(pvh_eh, pvh_et, pv_cnt);
395 }
396/* Wake threads throttled while the kernel reserve was being replenished.
397 */
398 if (pmap_pv_throttled_waiters) {
399 pmap_pv_throttled_waiters = 0;
400 thread_wakeup(&pmap_user_pv_throttle_event);
401 }
402 /* Check if the kernel pool has been depleted since the
403 * first pass, to reduce refill latency.
404 */
405 if (pv_hashed_kern_free_count < pv_hashed_kern_low_water_mark)
406 continue;
407 /* Block sans continuation to avoid yielding kernel stack */
408 assert_wait(&mapping_replenish_event, THREAD_UNINT);
409 mappingrecurse = 0;
410 thread_block(THREAD_CONTINUE_NULL);
411 pmap_mapping_thread_wakeups++;
412 }
413}
414
415/*
416 * Set specified attribute bits.
417 */
418
419void
420phys_attribute_set(
421 ppnum_t pn,
422 int bits)
423{
424 int pai;
425
426 pmap_intr_assert();
427 assert(pn != vm_page_fictitious_addr);
428 if (pn == vm_page_guard_addr)
429 return;
430
431 pai = ppn_to_pai(pn);
432
433 if (!IS_MANAGED_PAGE(pai)) {
434 /* Not a managed page. */
435 return;
436 }
437
438 LOCK_PVH(pai);
439 pmap_phys_attributes[pai] |= bits;
440 UNLOCK_PVH(pai);
441}
442
443/*
444 * Set the modify bit on the specified physical page.
445 */
446
447void
448pmap_set_modify(ppnum_t pn)
449{
450 phys_attribute_set(pn, PHYS_MODIFIED);
451}
452
453/*
454 * Clear the modify bits on the specified physical page.
455 */
456
457void
458pmap_clear_modify(ppnum_t pn)
459{
39236c6e 460 phys_attribute_clear(pn, PHYS_MODIFIED, 0, NULL);
6d2010ae
A
461}
462
463/*
464 * pmap_is_modified:
465 *
466 * Return whether or not the specified physical page is modified
467 * by any physical maps.
468 */
469
470boolean_t
471pmap_is_modified(ppnum_t pn)
472{
473 if (phys_attribute_test(pn, PHYS_MODIFIED))
474 return TRUE;
475 return FALSE;
476}
477
478
479/*
480 * pmap_clear_reference:
481 *
482 * Clear the reference bit on the specified physical page.
483 */
484
485void
486pmap_clear_reference(ppnum_t pn)
487{
39236c6e 488 phys_attribute_clear(pn, PHYS_REFERENCED, 0, NULL);
6d2010ae
A
489}
490
491void
492pmap_set_reference(ppnum_t pn)
493{
494 phys_attribute_set(pn, PHYS_REFERENCED);
495}
496
497/*
498 * pmap_is_referenced:
499 *
500 * Return whether or not the specified physical page is referenced
501 * by any physical maps.
502 */
503
504boolean_t
505pmap_is_referenced(ppnum_t pn)
506{
507 if (phys_attribute_test(pn, PHYS_REFERENCED))
508 return TRUE;
509 return FALSE;
510}
511
512
513/*
514 * pmap_get_refmod(phys)
515 * returns the referenced and modified bits of the specified
516 * physical page.
517 */
518unsigned int
519pmap_get_refmod(ppnum_t pn)
520{
521 int refmod;
522 unsigned int retval = 0;
523
524 refmod = phys_attribute_test(pn, PHYS_MODIFIED | PHYS_REFERENCED);
525
526 if (refmod & PHYS_MODIFIED)
527 retval |= VM_MEM_MODIFIED;
528 if (refmod & PHYS_REFERENCED)
529 retval |= VM_MEM_REFERENCED;
530
531 return (retval);
532}
533
39236c6e
A
534
535void
536pmap_clear_refmod_options(ppnum_t pn, unsigned int mask, unsigned int options, void *arg)
537{
538 unsigned int x86Mask;
539
540 x86Mask = ( ((mask & VM_MEM_MODIFIED)? PHYS_MODIFIED : 0)
541 | ((mask & VM_MEM_REFERENCED)? PHYS_REFERENCED : 0));
542
543 phys_attribute_clear(pn, x86Mask, options, arg);
544}
545
6d2010ae
A
546/*
547 * pmap_clear_refmod(phys, mask)
548 * clears the referenced and modified bits as specified by the mask
549 * of the specified physical page.
550 */
551void
552pmap_clear_refmod(ppnum_t pn, unsigned int mask)
553{
554 unsigned int x86Mask;
555
556 x86Mask = ( ((mask & VM_MEM_MODIFIED)? PHYS_MODIFIED : 0)
557 | ((mask & VM_MEM_REFERENCED)? PHYS_REFERENCED : 0));
39236c6e
A
558
559 phys_attribute_clear(pn, x86Mask, 0, NULL);
560}
561
562unsigned int
563pmap_disconnect(ppnum_t pa)
564{
565 return (pmap_disconnect_options(pa, 0, NULL));
6d2010ae
A
566}
567
568/*
569 * Routine:
39236c6e 570 * pmap_disconnect_options
6d2010ae
A
571 *
572 * Function:
573 * Disconnect all mappings for this page and return reference and change status
574 * in generic format.
575 *
576 */
577unsigned int
39236c6e 578pmap_disconnect_options(ppnum_t pa, unsigned int options, void *arg)
6d2010ae
A
579{
580 unsigned refmod, vmrefmod = 0;
581
39236c6e 582 pmap_page_protect_options(pa, 0, options, arg); /* disconnect the page */
6d2010ae
A
583
584 pmap_assert(pa != vm_page_fictitious_addr);
39236c6e 585 if ((pa == vm_page_guard_addr) || !IS_MANAGED_PAGE(pa) || (options & PMAP_OPTIONS_NOREFMOD))
6d2010ae
A
586 return 0;
587 refmod = pmap_phys_attributes[pa] & (PHYS_MODIFIED | PHYS_REFERENCED);
588
589 if (refmod & PHYS_MODIFIED)
590 vmrefmod |= VM_MEM_MODIFIED;
591 if (refmod & PHYS_REFERENCED)
592 vmrefmod |= VM_MEM_REFERENCED;
593
594 return vmrefmod;
595}