2 * Copyright (c) 2000-2010 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 #include <i386/pmap_internal.h>
32 * Each entry in the pv_head_table is locked by a bit in the
33 * pv_lock_table. The lock bits are accessed by the physical
34 * address of the page they lock.
37 char *pv_lock_table
; /* pointer to array of bits */
38 char *pv_hash_lock_table
;
40 pv_rooted_entry_t pv_head_table
; /* array of entries, one per
42 uint32_t pv_hashed_free_count
= 0;
43 uint32_t pv_hashed_kern_free_count
= 0;
45 pmap_pagetable_corruption_record_t pmap_pagetable_corruption_records
[PMAP_PAGETABLE_CORRUPTION_MAX_LOG
];
46 uint32_t pmap_pagetable_corruption_incidents
;
47 uint64_t pmap_pagetable_corruption_last_abstime
= (~(0ULL) >> 1);
48 uint64_t pmap_pagetable_corruption_interval_abstime
;
49 thread_call_t pmap_pagetable_corruption_log_call
;
50 static thread_call_data_t pmap_pagetable_corruption_log_call_data
;
51 boolean_t pmap_pagetable_corruption_timeout
= FALSE
;
53 volatile uint32_t mappingrecurse
= 0;
55 uint32_t pv_hashed_low_water_mark
, pv_hashed_kern_low_water_mark
, pv_hashed_alloc_chunk
, pv_hashed_kern_alloc_chunk
;
57 thread_t mapping_replenish_thread
;
58 event_t mapping_replenish_event
, pmap_user_pv_throttle_event
;
60 uint64_t pmap_pv_throttle_stat
, pmap_pv_throttled_waiters
;
62 unsigned int pmap_cache_attributes(ppnum_t pn
) {
63 if (pmap_get_cache_attributes(pn
) & INTEL_PTE_NCACHE
)
66 return (VM_WIMG_COPYBACK
);
69 void pmap_set_cache_attributes(ppnum_t pn
, unsigned int cacheattr
) {
70 unsigned int current
, template = 0;
73 if (cacheattr
& VM_MEM_NOT_CACHEABLE
) {
74 if(!(cacheattr
& VM_MEM_GUARDED
))
76 template |= PHYS_NCACHE
;
81 assert((pn
!= vm_page_fictitious_addr
) && (pn
!= vm_page_guard_addr
));
85 if (!IS_MANAGED_PAGE(pai
)) {
89 /* override cache attributes for this phys page
90 * Does not walk through existing mappings to adjust,
91 * assumes page is disconnected
96 pmap_update_cache_attributes_locked(pn
, template);
98 current
= pmap_phys_attributes
[pai
] & PHYS_CACHEABILITY_MASK
;
99 pmap_phys_attributes
[pai
] &= ~PHYS_CACHEABILITY_MASK
;
100 pmap_phys_attributes
[pai
] |= template;
104 if ((template & PHYS_NCACHE
) && !(current
& PHYS_NCACHE
)) {
105 pmap_sync_page_attributes_phys(pn
);
109 unsigned pmap_get_cache_attributes(ppnum_t pn
) {
110 if (last_managed_page
== 0)
113 if (!IS_MANAGED_PAGE(ppn_to_pai(pn
))) {
114 return INTEL_PTE_NCACHE
;
118 * The cache attributes are read locklessly for efficiency.
120 unsigned int attr
= pmap_phys_attributes
[ppn_to_pai(pn
)];
121 unsigned int template = 0;
124 template |= INTEL_PTE_PTA
;
125 if (attr
& PHYS_NCACHE
)
126 template |= INTEL_PTE_NCACHE
;
133 pmap_is_noencrypt(ppnum_t pn
)
137 pai
= ppn_to_pai(pn
);
139 if (!IS_MANAGED_PAGE(pai
))
142 if (pmap_phys_attributes
[pai
] & PHYS_NOENCRYPT
)
150 pmap_set_noencrypt(ppnum_t pn
)
154 pai
= ppn_to_pai(pn
);
156 if (IS_MANAGED_PAGE(pai
)) {
159 pmap_phys_attributes
[pai
] |= PHYS_NOENCRYPT
;
167 pmap_clear_noencrypt(ppnum_t pn
)
171 pai
= ppn_to_pai(pn
);
173 if (IS_MANAGED_PAGE(pai
)) {
175 * synchronization at VM layer prevents PHYS_NOENCRYPT
176 * from changing state, so we don't need the lock to inspect
178 if (pmap_phys_attributes
[pai
] & PHYS_NOENCRYPT
) {
181 pmap_phys_attributes
[pai
] &= ~PHYS_NOENCRYPT
;
189 compute_pmap_gc_throttle(void *arg __unused
)
195 __private_extern__
void
196 pmap_pagetable_corruption_msg_log(int (*log_func
)(const char * fmt
, ...)__printflike(1,2)) {
197 if (pmap_pagetable_corruption_incidents
> 0) {
198 int i
, e
= MIN(pmap_pagetable_corruption_incidents
, PMAP_PAGETABLE_CORRUPTION_MAX_LOG
);
199 (*log_func
)("%u pagetable corruption incident(s) detected, timeout: %u\n", pmap_pagetable_corruption_incidents
, pmap_pagetable_corruption_timeout
);
200 for (i
= 0; i
< e
; i
++) {
201 (*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
);
207 pmap_pagetable_corruption_log_setup(void) {
208 if (pmap_pagetable_corruption_log_call
== NULL
) {
209 nanotime_to_absolutetime(PMAP_PAGETABLE_CORRUPTION_INTERVAL
, 0, &pmap_pagetable_corruption_interval_abstime
);
210 thread_call_setup(&pmap_pagetable_corruption_log_call_data
,
211 (thread_call_func_t
) pmap_pagetable_corruption_msg_log
,
212 (thread_call_param_t
) &printf
);
213 pmap_pagetable_corruption_log_call
= &pmap_pagetable_corruption_log_call_data
;
218 mapping_free_prime(void)
221 pv_hashed_entry_t pvh_e
;
222 pv_hashed_entry_t pvh_eh
;
223 pv_hashed_entry_t pvh_et
;
226 /* Scale based on DRAM size */
227 pv_hashed_low_water_mark
= MAX(PV_HASHED_LOW_WATER_MARK_DEFAULT
, ((uint32_t)(sane_size
>> 30)) * 2000);
228 pv_hashed_low_water_mark
= MIN(pv_hashed_low_water_mark
, 16000);
229 /* Alterable via sysctl */
230 pv_hashed_kern_low_water_mark
= MAX(PV_HASHED_KERN_LOW_WATER_MARK_DEFAULT
, ((uint32_t)(sane_size
>> 30)) * 1000);
231 pv_hashed_kern_low_water_mark
= MIN(pv_hashed_kern_low_water_mark
, 16000);
232 pv_hashed_kern_alloc_chunk
= PV_HASHED_KERN_ALLOC_CHUNK_INITIAL
;
233 pv_hashed_alloc_chunk
= PV_HASHED_ALLOC_CHUNK_INITIAL
;
236 pvh_eh
= pvh_et
= PV_HASHED_ENTRY_NULL
;
238 for (i
= 0; i
< (5 * PV_HASHED_ALLOC_CHUNK_INITIAL
); i
++) {
239 pvh_e
= (pv_hashed_entry_t
) zalloc(pv_hashed_list_zone
);
241 pvh_e
->qlink
.next
= (queue_entry_t
)pvh_eh
;
244 if (pvh_et
== PV_HASHED_ENTRY_NULL
)
248 PV_HASHED_FREE_LIST(pvh_eh
, pvh_et
, pv_cnt
);
251 pvh_eh
= pvh_et
= PV_HASHED_ENTRY_NULL
;
252 for (i
= 0; i
< PV_HASHED_KERN_ALLOC_CHUNK_INITIAL
; i
++) {
253 pvh_e
= (pv_hashed_entry_t
) zalloc(pv_hashed_list_zone
);
255 pvh_e
->qlink
.next
= (queue_entry_t
)pvh_eh
;
258 if (pvh_et
== PV_HASHED_ENTRY_NULL
)
262 PV_HASHED_KERN_FREE_LIST(pvh_eh
, pvh_et
, pv_cnt
);
265 void mapping_replenish(void);
267 void mapping_adjust(void) {
270 pmap_pagetable_corruption_log_setup();
272 mres
= kernel_thread_start_priority((thread_continue_t
)mapping_replenish
, NULL
, MAXPRI_KERNEL
, &mapping_replenish_thread
);
273 if (mres
!= KERN_SUCCESS
) {
274 panic("pmap: mapping_replenish_thread creation failed");
276 thread_deallocate(mapping_replenish_thread
);
279 unsigned pmap_mapping_thread_wakeups
;
280 unsigned pmap_kernel_reserve_replenish_stat
;
281 unsigned pmap_user_reserve_replenish_stat
;
282 unsigned pmap_kern_reserve_alloc_stat
;
284 void mapping_replenish(void)
286 pv_hashed_entry_t pvh_e
;
287 pv_hashed_entry_t pvh_eh
;
288 pv_hashed_entry_t pvh_et
;
292 /* We qualify for VM privileges...*/
293 current_thread()->options
|= TH_OPT_VMPRIV
;
297 while (pv_hashed_kern_free_count
< pv_hashed_kern_low_water_mark
) {
299 pvh_eh
= pvh_et
= PV_HASHED_ENTRY_NULL
;
301 for (i
= 0; i
< pv_hashed_kern_alloc_chunk
; i
++) {
302 pvh_e
= (pv_hashed_entry_t
) zalloc(pv_hashed_list_zone
);
303 pvh_e
->qlink
.next
= (queue_entry_t
)pvh_eh
;
306 if (pvh_et
== PV_HASHED_ENTRY_NULL
)
310 pmap_kernel_reserve_replenish_stat
+= pv_cnt
;
311 PV_HASHED_KERN_FREE_LIST(pvh_eh
, pvh_et
, pv_cnt
);
315 pvh_eh
= pvh_et
= PV_HASHED_ENTRY_NULL
;
317 if (pv_hashed_free_count
< pv_hashed_low_water_mark
) {
318 for (i
= 0; i
< pv_hashed_alloc_chunk
; i
++) {
319 pvh_e
= (pv_hashed_entry_t
) zalloc(pv_hashed_list_zone
);
321 pvh_e
->qlink
.next
= (queue_entry_t
)pvh_eh
;
324 if (pvh_et
== PV_HASHED_ENTRY_NULL
)
328 pmap_user_reserve_replenish_stat
+= pv_cnt
;
329 PV_HASHED_FREE_LIST(pvh_eh
, pvh_et
, pv_cnt
);
331 /* Wake threads throttled while the kernel reserve was being replenished.
333 if (pmap_pv_throttled_waiters
) {
334 pmap_pv_throttled_waiters
= 0;
335 thread_wakeup(&pmap_user_pv_throttle_event
);
337 /* Check if the kernel pool has been depleted since the
338 * first pass, to reduce refill latency.
340 if (pv_hashed_kern_free_count
< pv_hashed_kern_low_water_mark
)
342 /* Block sans continuation to avoid yielding kernel stack */
343 assert_wait(&mapping_replenish_event
, THREAD_UNINT
);
345 thread_block(THREAD_CONTINUE_NULL
);
346 pmap_mapping_thread_wakeups
++;
351 * Set specified attribute bits.
362 assert(pn
!= vm_page_fictitious_addr
);
363 if (pn
== vm_page_guard_addr
)
366 pai
= ppn_to_pai(pn
);
368 if (!IS_MANAGED_PAGE(pai
)) {
369 /* Not a managed page. */
374 pmap_phys_attributes
[pai
] |= bits
;
379 * Set the modify bit on the specified physical page.
383 pmap_set_modify(ppnum_t pn
)
385 phys_attribute_set(pn
, PHYS_MODIFIED
);
389 * Clear the modify bits on the specified physical page.
393 pmap_clear_modify(ppnum_t pn
)
395 phys_attribute_clear(pn
, PHYS_MODIFIED
);
401 * Return whether or not the specified physical page is modified
402 * by any physical maps.
406 pmap_is_modified(ppnum_t pn
)
408 if (phys_attribute_test(pn
, PHYS_MODIFIED
))
415 * pmap_clear_reference:
417 * Clear the reference bit on the specified physical page.
421 pmap_clear_reference(ppnum_t pn
)
423 phys_attribute_clear(pn
, PHYS_REFERENCED
);
427 pmap_set_reference(ppnum_t pn
)
429 phys_attribute_set(pn
, PHYS_REFERENCED
);
433 * pmap_is_referenced:
435 * Return whether or not the specified physical page is referenced
436 * by any physical maps.
440 pmap_is_referenced(ppnum_t pn
)
442 if (phys_attribute_test(pn
, PHYS_REFERENCED
))
449 * pmap_get_refmod(phys)
450 * returns the referenced and modified bits of the specified
454 pmap_get_refmod(ppnum_t pn
)
457 unsigned int retval
= 0;
459 refmod
= phys_attribute_test(pn
, PHYS_MODIFIED
| PHYS_REFERENCED
);
461 if (refmod
& PHYS_MODIFIED
)
462 retval
|= VM_MEM_MODIFIED
;
463 if (refmod
& PHYS_REFERENCED
)
464 retval
|= VM_MEM_REFERENCED
;
470 * pmap_clear_refmod(phys, mask)
471 * clears the referenced and modified bits as specified by the mask
472 * of the specified physical page.
475 pmap_clear_refmod(ppnum_t pn
, unsigned int mask
)
477 unsigned int x86Mask
;
479 x86Mask
= ( ((mask
& VM_MEM_MODIFIED
)? PHYS_MODIFIED
: 0)
480 | ((mask
& VM_MEM_REFERENCED
)? PHYS_REFERENCED
: 0));
481 phys_attribute_clear(pn
, x86Mask
);
489 * Disconnect all mappings for this page and return reference and change status
494 pmap_disconnect(ppnum_t pa
)
496 unsigned refmod
, vmrefmod
= 0;
498 pmap_page_protect(pa
, 0); /* disconnect the page */
500 pmap_assert(pa
!= vm_page_fictitious_addr
);
501 if ((pa
== vm_page_guard_addr
) || !IS_MANAGED_PAGE(pa
))
503 refmod
= pmap_phys_attributes
[pa
] & (PHYS_MODIFIED
| PHYS_REFERENCED
);
505 if (refmod
& PHYS_MODIFIED
)
506 vmrefmod
|= VM_MEM_MODIFIED
;
507 if (refmod
& PHYS_REFERENCED
)
508 vmrefmod
|= VM_MEM_REFERENCED
;