* Copyright (c) 2000-2009 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
- *
+ *
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* unlawful or unlicensed copies of an Apple operating system, or to
* circumvent, violate, or enable the circumvention or violation of, any
* terms of an Apple operating system software license agreement.
- *
+ *
* Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this file.
- *
+ *
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
- *
+ *
* @APPLE_OSREFERENCE_LICENSE_HEADER_END@
*/
/*
* @OSF_COPYRIGHT@
*/
-/*
+/*
* Mach Operating System
* Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University
* All Rights Reserved.
- *
+ *
* Permission to use, copy, modify and distribute this software and its
* documentation is hereby granted, provided that both the copyright
* notice and this permission notice appear in all copies of the
* software, derivative works or modified versions, and any portions
* thereof, and that both notices appear in supporting documentation.
- *
+ *
* CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
* CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
* ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
- *
+ *
* Carnegie Mellon requests users of this software to return to
- *
+ *
* Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
* School of Computer Science
* Carnegie Mellon University
* Pittsburgh PA 15213-3890
- *
+ *
* any improvements or extensions that they make and grant Carnegie Mellon
* the rights to redistribute these changes.
*/
#include <kern/macro_help.h>
#include <kern/zalloc.h>
#include <kern/misc_protos.h>
+#include <kern/policy_internal.h>
#include <vm/vm_compressor.h>
#include <vm/vm_compressor_pager.h>
#include <vm/vm_shared_region.h>
#include <sys/codesign.h>
+#include <sys/reason.h>
+#include <sys/signalvar.h>
-#include <libsa/sys/timers.h> /* for struct timespec */
+#include <san/kasan.h>
#define VM_FAULT_CLASSIFY 0
unsigned int vm_object_pagein_throttle = 16;
/*
- * We apply a hard throttle to the demand zero rate of tasks that we believe are running out of control which
+ * We apply a hard throttle to the demand zero rate of tasks that we believe are running out of control which
* kicks in when swap space runs out. 64-bit programs have massive address spaces and can leak enormous amounts
* of memory if they're buggy and can run the system completely out of swap space. If this happens, we
* impose a hard throttle on them to prevent them from taking the last bit of memory left. This helps
- * keep the UI active so that the user has a chance to kill the offending task before the system
+ * keep the UI active so that the user has a chance to kill the offending task before the system
* completely hangs.
*
* The hard throttle is only applied when the system is nearly completely out of swap space and is only applied
extern void throttle_lowpri_io(int);
+extern struct vnode *vnode_pager_lookup_vnode(memory_object_t);
+
uint64_t vm_hard_throttle_threshold;
vm_map_t map,
vm_map_offset_t va,
vm_prot_t prot,
+ vm_tag_t wire_tag,
vm_map_entry_t entry,
pmap_t pmap,
vm_map_offset_t pmap_addr,
vm_map_offset_t vaddr,
vm_prot_t caller_prot,
boolean_t change_wiring,
+ vm_tag_t wire_tag,
int interruptible,
pmap_t pmap,
vm_map_offset_t pmap_addr,
void vm_pre_fault(vm_map_offset_t);
-extern int not_in_kdp;
extern char *kdp_compressor_decompressed_page;
extern addr64_t kdp_compressor_decompressed_page_paddr;
extern ppnum_t kdp_compressor_decompressed_page_ppnum;
if (PE_parse_boot_argn("vm_compressor", &vm_compressor_temp, sizeof (vm_compressor_temp))) {
for ( i = 0; i < VM_PAGER_MAX_MODES; i++) {
- if (vm_compressor_temp > 0 &&
+ if (vm_compressor_temp > 0 &&
((vm_compressor_temp & ( 1 << i)) == vm_compressor_temp)) {
need_default_val = FALSE;
vm_compressor_mode = vm_compressor_temp;
}
if (need_default_val)
printf("Ignoring \"vm_compressor\" boot arg %d\n", vm_compressor_temp);
- }
+ }
if (need_default_val) {
/* If no boot arg or incorrect boot arg, try device tree. */
PE_get_default("kern.vm_compressor", &vm_compressor_mode, sizeof(vm_compressor_mode));
}
PE_parse_boot_argn("vm_compressor_threads", &vm_compressor_thread_count, sizeof (vm_compressor_thread_count));
- if (PE_parse_boot_argn("vm_compressor_immediate", &vm_compressor_temp, sizeof (vm_compressor_temp)))
- vm_compressor_immediate_preferred_override = TRUE;
- else {
- if (PE_get_default("kern.vm_compressor_immediate", &vm_compressor_temp, sizeof(vm_compressor_temp)))
- vm_compressor_immediate_preferred_override = TRUE;
- }
- if (vm_compressor_immediate_preferred_override == TRUE) {
- if (vm_compressor_temp)
- vm_compressor_immediate_preferred = TRUE;
- else
- vm_compressor_immediate_preferred = FALSE;
- }
printf("\"vm_compressor_mode\" is %d\n", vm_compressor_mode);
}
*/
void
vm_fault_cleanup(
- register vm_object_t object,
- register vm_page_t top_page)
+ vm_object_t object,
+ vm_page_t top_page)
{
vm_object_paging_end(object);
vm_object_unlock(object);
if (top_page != VM_PAGE_NULL) {
- object = top_page->object;
+ object = VM_PAGE_OBJECT(top_page);
vm_object_lock(object);
VM_PAGE_FREE(top_page);
boolean_t vm_page_deactivate_behind = TRUE;
-/*
- * default sizes given VM_BEHAVIOR_DEFAULT reference behavior
+/*
+ * default sizes given VM_BEHAVIOR_DEFAULT reference behavior
*/
#define VM_DEFAULT_DEACTIVATE_BEHIND_WINDOW 128
#define VM_DEFAULT_DEACTIVATE_BEHIND_CLUSTER 16 /* don't make this too big... */
for (n = 0; n < max_pages_in_run; n++) {
m = vm_page_lookup(object, offset + run_offset + (n * pg_offset));
- if (m && !m->laundry && !m->busy && !m->no_cache && !m->throttled && !m->fictitious && !m->absent) {
+ if (m && !m->laundry && !m->busy && !m->no_cache && (m->vm_page_q_state != VM_PAGE_ON_THROTTLED_Q) && !m->fictitious && !m->absent) {
page_run[pages_in_run++] = m;
/*
* in the past (TLB caches don't hang around for very long), and of course could just as easily
* have happened before we did the deactivate_behind.
*/
- pmap_clear_refmod_options(m->phys_page, VM_MEM_REFERENCED, PMAP_OPTIONS_NOFLUSH, (void *)NULL);
+ pmap_clear_refmod_options(VM_PAGE_GET_PHYS_PAGE(m), VM_MEM_REFERENCED, PMAP_OPTIONS_NOFLUSH, (void *)NULL);
}
}
if (pages_in_run) {
clock_sec_t elapsed_sec;
clock_sec_t tv_sec;
clock_usec_t tv_usec;
-
+
thread_t thread = current_thread();
-
+
if (thread->options & TH_OPT_VMPRIV)
return (0);
if (thread->t_page_creation_throttled) {
thread->t_page_creation_throttled = 0;
-
+
if (page_kept == FALSE)
goto no_throttle;
}
return (HARD_THROTTLE_DELAY);
}
- if ((vm_page_free_count < vm_page_throttle_limit || ((COMPRESSED_PAGER_IS_ACTIVE || DEFAULT_FREEZER_COMPRESSED_PAGER_IS_ACTIVE) && SWAPPER_NEEDS_TO_UNTHROTTLE())) &&
+ if ((vm_page_free_count < vm_page_throttle_limit || (VM_CONFIG_COMPRESSOR_IS_PRESENT && SWAPPER_NEEDS_TO_UNTHROTTLE())) &&
thread->t_page_creation_count > (VM_PAGE_CREATION_THROTTLE_PERIOD_SECS * VM_PAGE_CREATION_THROTTLE_RATE_PER_SEC)) {
-
+
if (vm_page_free_wanted == 0 && vm_page_free_wanted_privileged == 0) {
#if (DEVELOPMENT || DEBUG)
OSAddAtomic64(1, &vm_page_creation_throttle_avoided);
* over a long period of time a chance to get out of
* the throttled state... we reset the counter and timestamp
* so that if it stays under the rate limit for the next second
- * it will be back in our good graces... if it exceeds it, it
+ * it will be back in our good graces... if it exceeds it, it
* will remain in the throttled state
*/
thread->t_page_creation_time = tv_sec;
thread->t_page_creation_throttled = 1;
- if ((COMPRESSED_PAGER_IS_ACTIVE || DEFAULT_FREEZER_COMPRESSED_PAGER_IS_ACTIVE) && HARD_THROTTLE_LIMIT_REACHED()) {
+ if (VM_CONFIG_COMPRESSOR_IS_PRESENT && HARD_THROTTLE_LIMIT_REACHED()) {
#if (DEVELOPMENT || DEBUG)
thread->t_page_creation_throttled_hard++;
OSAddAtomic(1, &vm_page_creation_throttled_hard);
vm_fault_zero_page(vm_page_t m, boolean_t no_zero_fill)
{
int my_fault = DBG_ZERO_FILL_FAULT;
+ vm_object_t object;
+
+ object = VM_PAGE_OBJECT(m);
/*
* This is is a zero-fill page fault...
* execution. i.e. it is the responsibility
* of higher layers to call for an instruction
* sync after changing the contents and before
- * sending a program into this area. We
+ * sending a program into this area. We
* choose this approach for performance
*/
m->pmapped = TRUE;
DTRACE_VM2(zfod, int, 1, (uint64_t *), NULL);
}
assert(!m->laundry);
- assert(m->object != kernel_object);
- //assert(m->pageq.next == NULL && m->pageq.prev == NULL);
+ assert(object != kernel_object);
+ //assert(m->pageq.next == 0 && m->pageq.prev == 0);
- if (!VM_DYNAMIC_PAGING_ENABLED(memory_manager_default) &&
- (m->object->purgable == VM_PURGABLE_DENY ||
- m->object->purgable == VM_PURGABLE_NONVOLATILE ||
- m->object->purgable == VM_PURGABLE_VOLATILE )) {
+ if (!VM_DYNAMIC_PAGING_ENABLED() &&
+ (object->purgable == VM_PURGABLE_DENY ||
+ object->purgable == VM_PURGABLE_NONVOLATILE ||
+ object->purgable == VM_PURGABLE_VOLATILE )) {
vm_page_lockspin_queues();
- if (!VM_DYNAMIC_PAGING_ENABLED(memory_manager_default)) {
+ if (!VM_DYNAMIC_PAGING_ENABLED()) {
assert(!VM_PAGE_WIRED(m));
/*
* can't be on the pageout queue since we don't
* have a pager to try and clean to
*/
- assert(!m->pageout_queue);
-
- vm_page_queues_remove(m);
+ vm_page_queues_remove(m, TRUE);
vm_page_check_pageable_safe(m);
- queue_enter(&vm_page_queue_throttled, m, vm_page_t, pageq);
- m->throttled = TRUE;
+ vm_page_queue_enter(&vm_page_queue_throttled, m, vm_page_t, pageq);
+ m->vm_page_q_state = VM_PAGE_ON_THROTTLED_Q;
vm_page_throttled_count++;
}
vm_page_unlock_queues();
* The required permissions for the page is given
* in "fault_type". Desired permissions are included
* in "protection".
- * fault_info is passed along to determine pagein cluster
+ * fault_info is passed along to determine pagein cluster
* limits... it contains the expected reference pattern,
* cluster size if available, etc...
*
* The "result_page" is also left busy. It is not removed
* from the pageout queues.
* Special Case:
- * A return value of VM_FAULT_SUCCESS_NO_PAGE means that the
+ * A return value of VM_FAULT_SUCCESS_NO_PAGE means that the
* fault succeeded but there's no VM page (i.e. the VM object
* does not actually hold VM pages, but device memory or
* large pages). The object is still locked and we still hold a
/* More arguments: */
kern_return_t *error_code, /* code if page is in error */
boolean_t no_zero_fill, /* don't zero fill absent pages */
- boolean_t data_supply, /* treat as data_supply if
+ boolean_t data_supply, /* treat as data_supply if
* it is a write fault and a full
* page is provided */
vm_object_fault_info_t fault_info)
int external_state = VM_EXTERNAL_STATE_UNKNOWN;
memory_object_t pager;
vm_fault_return_t retval;
+ int grab_options;
/*
- * MACH page map - an optional optimization where a bit map is maintained
- * by the VM subsystem for internal objects to indicate which pages of
- * the object currently reside on backing store. This existence map
- * duplicates information maintained by the vnode pager. It is
- * created at the time of the first pageout against the object, i.e.
- * at the same time pager for the object is created. The optimization
- * is designed to eliminate pager interaction overhead, if it is
- * 'known' that the page does not exist on backing store.
- *
- * MUST_ASK_PAGER() evaluates to TRUE if the page specified by object/offset is
- * either marked as paged out in the existence map for the object or no
- * existence map exists for the object. MUST_ASK_PAGER() is one of the
- * criteria in the decision to invoke the pager. It is also used as one
- * of the criteria to terminate the scan for adjacent pages in a clustered
- * pagein operation. Note that MUST_ASK_PAGER() always evaluates to TRUE for
- * permanent objects. Note also that if the pager for an internal object
- * has not been created, the pager is not invoked regardless of the value
- * of MUST_ASK_PAGER() and that clustered pagein scans are only done on an object
- * for which a pager has been created.
+ * MUST_ASK_PAGER() evaluates to TRUE if the page specified by object/offset is
+ * marked as paged out in the compressor pager or the pager doesn't exist.
+ * Note also that if the pager for an internal object
+ * has not been created, the pager is not invoked regardless of the value
+ * of MUST_ASK_PAGER().
*
* PAGED_OUT() evaluates to TRUE if the page specified by the object/offset
- * is marked as paged out in the existence map for the object. PAGED_OUT()
+ * is marked as paged out in the compressor pager.
* PAGED_OUT() is used to determine if a page has already been pushed
* into a copy object in order to avoid a redundant page out operation.
*/
-#if MACH_PAGEMAP
-#define MUST_ASK_PAGER(o, f, s) \
- ((vm_external_state_get((o)->existence_map, (f)) \
- != VM_EXTERNAL_STATE_ABSENT) && \
- (s = (VM_COMPRESSOR_PAGER_STATE_GET((o), (f)))) \
- != VM_EXTERNAL_STATE_ABSENT)
-#define PAGED_OUT(o, f) \
- ((vm_external_state_get((o)->existence_map, (f)) \
- == VM_EXTERNAL_STATE_EXISTS) || \
- (VM_COMPRESSOR_PAGER_STATE_GET((o), (f)) \
- == VM_EXTERNAL_STATE_EXISTS))
-#else /* MACH_PAGEMAP */
#define MUST_ASK_PAGER(o, f, s) \
((s = VM_COMPRESSOR_PAGER_STATE_GET((o), (f))) != VM_EXTERNAL_STATE_ABSENT)
+
#define PAGED_OUT(o, f) \
(VM_COMPRESSOR_PAGER_STATE_GET((o), (f)) == VM_EXTERNAL_STATE_EXISTS)
-#endif /* MACH_PAGEMAP */
/*
* Recovery actions
#define RELEASE_PAGE(m) \
MACRO_BEGIN \
PAGE_WAKEUP_DONE(m); \
- if (!m->active && !m->inactive && !m->throttled) { \
- vm_page_lockspin_queues(); \
- if (!m->active && !m->inactive && !m->throttled) { \
- if (COMPRESSED_PAGER_IS_ACTIVE) \
- vm_page_deactivate(m); \
- else \
- vm_page_activate(m); \
- } \
- vm_page_unlock_queues(); \
- } \
+ if ( !VM_PAGE_PAGEABLE(m)) { \
+ vm_page_lockspin_queues(); \
+ if ( !VM_PAGE_PAGEABLE(m)) { \
+ if (VM_CONFIG_COMPRESSOR_IS_ACTIVE) \
+ vm_page_deactivate(m); \
+ else \
+ vm_page_activate(m); \
+ } \
+ vm_page_unlock_queues(); \
+ } \
MACRO_END
#if TRACEFAULTPAGE
interruptible = fault_info->interruptible;
interruptible_state = thread_interrupt_level(interruptible);
-
+
/*
* INVARIANTS (through entire routine):
*
#if TRACEFAULTPAGE
dbgTrace(0xBEEF0003, (unsigned int) 0, (unsigned int) 0); /* (TEST/DEBUG) */
#endif
+
+ grab_options = 0;
+#if CONFIG_SECLUDED_MEMORY
+ if (object->can_grab_secluded) {
+ grab_options |= VM_PAGE_GRAB_SECLUDED;
+ }
+#endif /* CONFIG_SECLUDED_MEMORY */
+
if (!object->alive) {
/*
* object is no longer valid
continue;
}
if (m->laundry) {
- m->pageout = FALSE;
+ m->free_when_done = FALSE;
- if (!m->cleaning)
+ if (!m->cleaning)
vm_pageout_steal_laundry(m, FALSE);
}
- if (m->phys_page == vm_page_guard_addr) {
+ if (VM_PAGE_GET_PHYS_PAGE(m) == vm_page_guard_addr) {
/*
* Guard page: off limits !
*/
/*
* check for any conditions that prevent
* us from creating a new zero-fill page
- * vm_fault_check will do all of the
+ * vm_fault_check will do all of the
* fault cleanup in the case of an error condition
* including resetting the thread_interrupt_level
*/
vm_object_unlock(object);
/*
- * grab the original page we
+ * grab the original page we
* 'soldered' in place and
* retake lock on 'first_object'
*/
m->busy = TRUE;
vm_page_lockspin_queues();
-
- assert(!m->pageout_queue);
- vm_page_queues_remove(m);
-
+ vm_page_queues_remove(m, FALSE);
vm_page_unlock_queues();
}
XPR(XPR_VM_FAULT,
vm_object_unlock(object);
object = next_object;
vm_object_paging_begin(object);
-
+
/*
* reset to default type of fault
*/
vm_object_reference_locked(object);
vm_fault_cleanup(object, first_m);
-
+
counter(c_vm_fault_page_block_backoff_kernel++);
vm_object_lock(object);
assert(object->ref_count > 0);
return (VM_FAULT_RETRY);
}
}
- if (type_of_fault == NULL && m->speculative &&
+ if (type_of_fault == NULL && (m->vm_page_q_state == VM_PAGE_ON_SPECULATIVE_Q) &&
!(fault_info != NULL && fault_info->stealth)) {
/*
* If we were passed a non-NULL pointer for
* the page in the speculative queue.
*/
vm_page_lockspin_queues();
- if (m->speculative)
- vm_page_queues_remove(m);
+ if (m->vm_page_q_state == VM_PAGE_ON_SPECULATIVE_Q)
+ vm_page_queues_remove(m, FALSE);
vm_page_unlock_queues();
}
+ assert(object == VM_PAGE_OBJECT(m));
- if (m->encrypted) {
- /*
- * ENCRYPTED SWAP:
- * the user needs access to a page that we
- * encrypted before paging it out.
- * Decrypt the page now.
- * Keep it busy to prevent anyone from
- * accessing it during the decryption.
- */
- m->busy = TRUE;
- vm_page_decrypt(m, 0);
- assert(object == m->object);
- assert(m->busy);
- PAGE_WAKEUP_DONE(m);
-
- /*
- * Retry from the top, in case
- * something changed while we were
- * decrypting.
- */
- continue;
- }
- ASSERT_PAGE_DECRYPTED(m);
-
- if (m->object->code_signed) {
+ if (object->code_signed) {
/*
* CODE SIGNING:
* We just paged in a page from a signed
m->busy = TRUE;
break;
}
-
+
/*
* we get here when there is no page present in the object at
* this object can provide the data or we're the top object...
* object is locked; m == NULL
*/
+
if (must_be_resident) {
if (fault_type == VM_PROT_NONE &&
object == kernel_object) {
goto dont_look_for_page;
}
-#if !MACH_PAGEMAP
+ /* Don't expect to fault pages into the kernel object. */
+ assert(object != kernel_object);
+
data_supply = FALSE;
-#endif /* !MACH_PAGEMAP */
look_for_page = (object->pager_created && (MUST_ASK_PAGER(object, offset, external_state) == TRUE) && !data_supply);
-
+
#if TRACEFAULTPAGE
dbgTrace(0xBEEF000C, (unsigned int) look_for_page, (unsigned int) object); /* (TEST/DEBUG) */
#endif
/*
* Allocate a new page for this object/offset pair as a placeholder
*/
- m = vm_page_grab();
+ m = vm_page_grab_options(grab_options);
#if TRACEFAULTPAGE
dbgTrace(0xBEEF000D, (unsigned int) m, (unsigned int) object); /* (TEST/DEBUG) */
#endif
return (VM_FAULT_RETRY);
}
}
- if (object->internal &&
- (COMPRESSED_PAGER_IS_ACTIVE
- || DEFAULT_FREEZER_COMPRESSED_PAGER_IS_ACTIVE)) {
+ if (object->internal) {
int compressed_count_delta;
+ assert(VM_CONFIG_COMPRESSOR_IS_PRESENT);
+
if (m == VM_PAGE_NULL) {
/*
* Allocate a new page for this object/offset pair as a placeholder
*/
- m = vm_page_grab();
+ m = vm_page_grab_options(grab_options);
#if TRACEFAULTPAGE
dbgTrace(0xBEEF000D, (unsigned int) m, (unsigned int) object); /* (TEST/DEBUG) */
#endif
}
}
assert(m->busy);
-
+
m->absent = TRUE;
pager = object->pager;
rc = vm_compressor_pager_get(
pager,
offset + object->paging_offset,
- m->phys_page,
+ VM_PAGE_GET_PHYS_PAGE(m),
&my_fault_type,
0,
&compressed_count_delta);
case KERN_SUCCESS:
m->absent = FALSE;
m->dirty = TRUE;
- if ((m->object->wimg_bits &
+ if ((object->wimg_bits &
VM_WIMG_MASK) !=
VM_WIMG_USE_DEFAULT) {
/*
* after the decompression.
*/
pmap_sync_page_attributes_phys(
- m->phys_page);
+ VM_PAGE_GET_PHYS_PAGE(m));
} else {
m->written_by_kernel = TRUE;
}
goto data_requested;
}
my_fault_type = DBG_PAGEIN_FAULT;
-
+
if (m != VM_PAGE_NULL) {
VM_PAGE_FREE(m);
m = VM_PAGE_NULL;
/*
* It's possible someone called vm_object_destroy while we weren't
- * holding the object lock. If that has happened, then bail out
+ * holding the object lock. If that has happened, then bail out
* here.
*/
* so we can release the object lock.
*/
+ if (object->object_slid == TRUE) {
+ set_thread_rwlock_boost();
+ }
+
vm_object_unlock(object);
/*
* the fault w/o having to go through memory_object_data_request again
*/
assert(first_m != VM_PAGE_NULL);
- assert(first_m->object == first_object);
-
+ assert(VM_PAGE_OBJECT(first_m) == first_object);
+
vm_object_lock(first_object);
VM_PAGE_FREE(first_m);
vm_object_paging_end(first_object);
#endif
vm_object_lock(object);
+ if (object->object_slid == TRUE) {
+ clear_thread_rwlock_boost();
+ }
+
data_requested:
if (rc != KERN_SUCCESS) {
if (m == VM_PAGE_NULL && object->phys_contiguous) {
/*
* No page here means that the object we
- * initially looked up was "physically
+ * initially looked up was "physically
* contiguous" (i.e. device memory). However,
* with Virtual VRAM, the object might not
* be backed by that device memory anymore,
}
dont_look_for_page:
/*
- * We get here if the object has no pager, or an existence map
+ * We get here if the object has no pager, or an existence map
* exists and indicates the page isn't present on the pager
* or we're unwiring a page. If a pager exists, but there
* is no existence map, then the m->absent case above handles
vm_object_lock(object);
}
m = first_m;
- assert(m->object == object);
+ assert(VM_PAGE_OBJECT(m) == object);
first_m = VM_PAGE_NULL;
/*
* check for any conditions that prevent
* us from creating a new zero-fill page
- * vm_fault_check will do all of the
+ * vm_fault_check will do all of the
* fault cleanup in the case of an error condition
* including resetting the thread_interrupt_level
*/
return (error);
if (m == VM_PAGE_NULL) {
- m = vm_page_grab();
+ m = vm_page_grab_options(grab_options);
if (m == VM_PAGE_NULL) {
vm_fault_cleanup(object, VM_PAGE_NULL);
assert(m->busy && !m->absent);
assert((first_m == VM_PAGE_NULL) ||
(first_m->busy && !first_m->absent &&
- !first_m->active && !first_m->inactive));
+ !first_m->active && !first_m->inactive && !first_m->secluded));
#endif /* EXTRA_ASSERTIONS */
- /*
- * ENCRYPTED SWAP:
- * If we found a page, we must have decrypted it before we
- * get here...
- */
- ASSERT_PAGE_DECRYPTED(m);
-
XPR(XPR_VM_FAULT,
"vm_f_page: FOUND obj 0x%X, off 0x%X, page 0x%X, 1_obj 0x%X, 1_m 0x%X\n",
object, offset, m,
/*
* Allocate a page for the copy
*/
- copy_m = vm_page_grab();
+ copy_m = vm_page_grab_options(grab_options);
if (copy_m == VM_PAGE_NULL) {
RELEASE_PAGE(m);
* avoid the pmap_disconnect() call.
*/
if (m->pmapped)
- pmap_disconnect(m->phys_page);
+ pmap_disconnect(VM_PAGE_GET_PHYS_PAGE(m));
if (m->clustered) {
VM_PAGE_COUNT_AS_PAGEIN(m);
*/
RELEASE_PAGE(m);
+ /*
+ * This check helps with marking the object as having a sequential pattern
+ * Normally we'll miss doing this below because this fault is about COW to
+ * the first_object i.e. bring page in from disk, push to object above but
+ * don't update the file object's sequential pattern.
+ */
+ if (object->internal == FALSE) {
+ vm_fault_is_sequential(object, offset, fault_info->behavior);
+ }
+
vm_object_paging_end(object);
vm_object_unlock(object);
*/
VM_PAGE_FREE(first_m);
first_m = VM_PAGE_NULL;
-
+
/*
* and replace it with the
* page we just copied into
* way, let's try to collapse the top object.
* But we have to play ugly games with
* paging_in_progress to do that...
- */
- vm_object_paging_end(object);
+ */
+ vm_object_paging_end(object);
vm_object_collapse(object, offset, TRUE);
vm_object_paging_begin(object);
copy_object->ref_count--;
assert(copy_object->ref_count > 0);
copy_m = vm_page_lookup(copy_object, copy_offset);
- /*
- * ENCRYPTED SWAP:
- * it's OK if the "copy_m" page is encrypted,
- * because we're not moving it nor handling its
- * contents.
- */
+
if (copy_m != VM_PAGE_NULL && copy_m->busy) {
PAGE_ASSERT_WAIT(copy_m, interruptible);
* Must copy page into copy-object.
*/
vm_page_copy(m, copy_m);
-
+
/*
* If the old page was in use by any users
* of the copy-object, it must be removed
* pmaps use it.)
*/
if (m->pmapped)
- pmap_disconnect(m->phys_page);
+ pmap_disconnect(VM_PAGE_GET_PHYS_PAGE(m));
if (m->clustered) {
VM_PAGE_COUNT_AS_PAGEIN(m);
* option. Else, we use the copy.
*/
if ((!copy_object->pager_ready)
-#if MACH_PAGEMAP
- || vm_external_state_get(copy_object->existence_map, copy_offset) == VM_EXTERNAL_STATE_ABSENT
-#endif
|| VM_COMPRESSOR_PAGER_STATE_GET(copy_object, copy_offset) == VM_EXTERNAL_STATE_ABSENT
- ) {
+ ) {
vm_page_lockspin_queues();
assert(!m->cleaning);
SET_PAGE_DIRTY(copy_m, TRUE);
PAGE_WAKEUP_DONE(copy_m);
- } else if (copy_object->internal &&
- (DEFAULT_PAGER_IS_ACTIVE || DEFAULT_FREEZER_IS_ACTIVE)) {
- /*
- * For internal objects check with the pager to see
- * if the page already exists in the backing store.
- * If yes, then we can drop the copy page. If not,
- * then we'll activate it, mark it dirty and keep it
- * around.
- */
-
- kern_return_t kr = KERN_SUCCESS;
-
- memory_object_t copy_pager = copy_object->pager;
- assert(copy_pager != MEMORY_OBJECT_NULL);
- vm_object_paging_begin(copy_object);
-
- vm_object_unlock(copy_object);
-
- kr = memory_object_data_request(
- copy_pager,
- copy_offset + copy_object->paging_offset,
- 0, /* Only query the pager. */
- VM_PROT_READ,
- NULL);
-
- vm_object_lock(copy_object);
-
- vm_object_paging_end(copy_object);
-
- /*
- * Since we dropped the copy_object's lock,
- * check whether we'll have to deallocate
- * the hard way.
- */
- if ((copy_object->shadow != object) || (copy_object->ref_count == 1)) {
- vm_object_unlock(copy_object);
- vm_object_deallocate(copy_object);
- vm_object_lock(object);
-
- continue;
- }
- if (kr == KERN_SUCCESS) {
- /*
- * The pager has the page. We don't want to overwrite
- * that page by sending this one out to the backing store.
- * So we drop the copy page.
- */
- VM_PAGE_FREE(copy_m);
-
- } else {
- /*
- * The pager doesn't have the page. We'll keep this one
- * around in the copy object. It might get sent out to
- * the backing store under memory pressure.
- */
- vm_page_lockspin_queues();
- assert(!m->cleaning);
- vm_page_activate(copy_m);
- vm_page_unlock_queues();
-
- SET_PAGE_DIRTY(copy_m, TRUE);
- PAGE_WAKEUP_DONE(copy_m);
- }
} else {
-
+
assert(copy_m->busy == TRUE);
assert(!m->cleaning);
copy_object->ref_count--;
assert(copy_object->ref_count > 0);
- VM_OBJ_RES_DECR(copy_object);
+ VM_OBJ_RES_DECR(copy_object);
vm_object_unlock(copy_object);
break;
object, offset, m, first_m, 0);
if (m != VM_PAGE_NULL) {
+ assert(VM_PAGE_OBJECT(m) == object);
+
retval = VM_FAULT_SUCCESS;
if (my_fault == DBG_PAGEIN_FAULT) {
VM_PAGE_COUNT_AS_PAGEIN(m);
- if (m->object->internal)
+ if (object->internal)
my_fault = DBG_PAGEIND_FAULT;
else
my_fault = DBG_PAGEINV_FAULT;
* 3. the page belongs to a code-signed object
* 4. the page has not been validated yet or has been mapped for write.
*/
-#define VM_FAULT_NEED_CS_VALIDATION(pmap, page) \
+#define VM_FAULT_NEED_CS_VALIDATION(pmap, page, page_obj) \
((pmap) != kernel_pmap /*1*/ && \
!(page)->cs_tainted /*2*/ && \
- (page)->object->code_signed /*3*/ && \
+ (page_obj)->code_signed /*3*/ && \
(!(page)->cs_validated || (page)->wpmapped /*4*/))
* careful not to modify the VM object in any way that is not
* legal under a shared lock...
*/
+extern int panic_on_cs_killed;
extern int proc_selfpid(void);
extern char *proc_name_address(void *p);
unsigned long cs_enter_tainted_rejected = 0;
vm_prot_t caller_prot,
boolean_t wired,
boolean_t change_wiring,
+ vm_tag_t wire_tag,
boolean_t no_cache,
boolean_t cs_bypass,
__unused int user_tag,
boolean_t map_is_switched, map_is_switch_protected;
int cs_enforcement_enabled;
vm_prot_t fault_type;
-
+ vm_object_t object;
+
fault_type = change_wiring ? VM_PROT_NONE : caller_prot;
+ object = VM_PAGE_OBJECT(m);
- vm_object_lock_assert_held(m->object);
-#if DEBUG
- lck_mtx_assert(&vm_page_queue_lock, LCK_MTX_ASSERT_NOTOWNED);
-#endif /* DEBUG */
+ vm_object_lock_assert_held(object);
- if (m->phys_page == vm_page_guard_addr) {
+#if KASAN
+ if (pmap == kernel_pmap) {
+ kasan_notify_address(vaddr, PAGE_SIZE);
+ }
+#endif
+
+ LCK_MTX_ASSERT(&vm_page_queue_lock, LCK_MTX_ASSERT_NOTOWNED);
+
+ if (VM_PAGE_GET_PHYS_PAGE(m) == vm_page_guard_addr) {
assert(m->fictitious);
return KERN_SUCCESS;
}
if (*type_of_fault == DBG_ZERO_FILL_FAULT) {
- vm_object_lock_assert_exclusive(m->object);
+ vm_object_lock_assert_exclusive(object);
- } else if ((fault_type & VM_PROT_WRITE) == 0) {
+ } else if ((fault_type & VM_PROT_WRITE) == 0 && !m->wpmapped) {
/*
* This is not a "write" fault, so we
* might not have taken the object lock
* soft-fault again if we need write
* access later...
*/
- prot &= ~VM_PROT_WRITE;
+
+ /* This had better not be a JIT page. */
+ if (!pmap_has_prot_policy(prot)) {
+ prot &= ~VM_PROT_WRITE;
+ } else {
+ assert(cs_bypass);
+ }
}
if (m->pmapped == FALSE) {
* so it must have come in as part of
* a cluster... account 1 pagein against it
*/
- if (m->object->internal)
+ if (object->internal)
*type_of_fault = DBG_PAGEIND_FAULT;
else
*type_of_fault = DBG_PAGEINV_FAULT;
-
+
VM_PAGE_COUNT_AS_PAGEIN(m);
}
VM_PAGE_CONSUME_CLUSTERED(m);
}
/* Validate code signature if necessary. */
- if (VM_FAULT_NEED_CS_VALIDATION(pmap, m)) {
- vm_object_lock_assert_exclusive(m->object);
+ if (VM_FAULT_NEED_CS_VALIDATION(pmap, m, object)) {
+ vm_object_lock_assert_exclusive(object);
if (m->cs_validated) {
vm_cs_revalidates++;
}
- /* VM map is locked, so 1 ref will remain on VM object -
+ /* VM map is locked, so 1 ref will remain on VM object -
* so no harm if vm_page_validate_cs drops the object lock */
vm_page_validate_cs(m);
}
map_is_switched = ((pmap != vm_map_pmap(current_task()->map)) &&
(pmap == vm_map_pmap(current_thread()->map)));
map_is_switch_protected = current_thread()->map->switch_protect;
-
+
/* If the map is switched, and is switch-protected, we must protect
- * some pages from being write-faulted: immutable pages because by
+ * some pages from being write-faulted: immutable pages because by
* definition they may not be written, and executable pages because that
* would provide a way to inject unsigned code.
* If the page is immutable, we can simply return. However, we can't
* immediately determine whether a page is executable anywhere. But,
* we can disconnect it everywhere and remove the executable protection
- * from the current map. We do that below right before we do the
+ * from the current map. We do that below right before we do the
* PMAP_ENTER.
*/
cs_enforcement_enabled = cs_enforcement(NULL);
- if(cs_enforcement_enabled && map_is_switched &&
- map_is_switch_protected && page_immutable(m, prot) &&
+ if(cs_enforcement_enabled && map_is_switched &&
+ map_is_switch_protected && page_immutable(m, prot) &&
(prot & VM_PROT_WRITE))
{
return KERN_CODESIGN_ERROR;
return KERN_CODESIGN_ERROR;
}
+ if (cs_enforcement_enabled &&
+ !m->cs_validated &&
+ (prot & VM_PROT_EXECUTE) &&
+ !(caller_prot & VM_PROT_EXECUTE)) {
+ /*
+ * FOURK PAGER:
+ * This page has not been validated and will not be
+ * allowed to be mapped for "execute".
+ * But the caller did not request "execute" access for this
+ * fault, so we should not raise a code-signing violation
+ * (and possibly kill the process) below.
+ * Instead, let's just remove the "execute" access request.
+ *
+ * This can happen on devices with a 4K page size if a 16K
+ * page contains a mix of signed&executable and
+ * unsigned&non-executable 4K pages, making the whole 16K
+ * mapping "executable".
+ */
+ if (!pmap_has_prot_policy(prot)) {
+ prot &= ~VM_PROT_EXECUTE;
+ } else {
+ assert(cs_bypass);
+ }
+ }
+
/* A page could be tainted, or pose a risk of being tainted later.
* Check whether the receiving process wants it, and make it feel
* the consequences (that hapens in cs_invalid_page()).
- * For CS Enforcement, two other conditions will
- * cause that page to be tainted as well:
+ * For CS Enforcement, two other conditions will
+ * cause that page to be tainted as well:
* - pmapping an unsigned page executable - this means unsigned code;
* - writeable mapping of a validated page - the content of that page
* can be changed without the kernel noticing, therefore unsigned
(!m->cs_validated && (prot & VM_PROT_EXECUTE)) ||
/* The page should be immutable, but is in danger of being modified
* This is the case where we want policy from the code directory -
- * is the page immutable or not? For now we have to assume that
+ * is the page immutable or not? For now we have to assume that
* code pages will be immutable, data pages not.
- * We'll assume a page is a code page if it has a code directory
+ * We'll assume a page is a code page if it has a code directory
* and we fault for execution.
* That is good enough since if we faulted the code page for
* writing in another map before, it is wpmapped; if we fault
- * it for writing in this map later it will also be faulted for executing
+ * it for writing in this map later it will also be faulted for executing
* at the same time; and if we fault for writing in another map
* later, we will disconnect it from this pmap so we'll notice
* the change.
*/
(page_immutable(m, prot) && ((prot & VM_PROT_WRITE) || m->wpmapped))
))
- ))
+ ))
{
/* We will have a tainted page. Have to handle the special case
* of a switched map now. If the map is not switched, standard
* There is no point in invalidating the switching process since
* it will not be executing from the map. So we don't call
* cs_invalid_page() in that case. */
- boolean_t reject_page;
- if(map_is_switched) {
+ boolean_t reject_page, cs_killed;
+ if(map_is_switched) {
assert(pmap==vm_map_pmap(current_thread()->map));
assert(!(prot & VM_PROT_WRITE) || (map_is_switch_protected == FALSE));
reject_page = FALSE;
} else {
if (cs_debug > 5)
- printf("vm_fault: signed: %s validate: %s tainted: %s wpmapped: %s slid: %s prot: 0x%x\n",
- m->object->code_signed ? "yes" : "no",
+ printf("vm_fault: signed: %s validate: %s tainted: %s wpmapped: %s slid: %s prot: 0x%x\n",
+ object->code_signed ? "yes" : "no",
m->cs_validated ? "yes" : "no",
m->cs_tainted ? "yes" : "no",
m->wpmapped ? "yes" : "no",
m->slid ? "yes" : "no",
(int)prot);
- reject_page = cs_invalid_page((addr64_t) vaddr);
+ reject_page = cs_invalid_page((addr64_t) vaddr, &cs_killed);
}
-
+
if (reject_page) {
/* reject the invalid page: abort the page fault */
int pid;
boolean_t truncated_path;
#define __PATH_MAX 1024
struct timespec mtime, cs_mtime;
+ int shadow_depth;
+ os_reason_t codesigning_exit_reason = OS_REASON_NULL;
kr = KERN_CODESIGN_ERROR;
cs_enter_tainted_rejected++;
procname = proc_name_address(task->bsd_info);
/* get file's VM object */
- file_object = m->object;
+ file_object = object;
file_offset = m->offset;
- for (shadow = file_object->shadow;
+ for (shadow = file_object->shadow,
+ shadow_depth = 0;
shadow != VM_OBJECT_NULL;
- shadow = file_object->shadow) {
+ shadow = file_object->shadow,
+ shadow_depth++) {
vm_object_lock_shared(shadow);
- if (file_object != m->object) {
+ if (file_object != object) {
vm_object_unlock(file_object);
}
file_offset += file_object->vo_shadow_offset;
"rejecting invalid page at address 0x%llx "
"from offset 0x%llx in file \"%s%s%s\" "
"(cs_mtime:%lu.%ld %s mtime:%lu.%ld) "
- "(signed:%d validated:%d tainted:%d "
- "wpmapped:%d slid:%d)\n",
+ "(signed:%d validated:%d tainted:%d nx:%d "
+ "wpmapped:%d slid:%d dirty:%d depth:%d)\n",
pid, procname, (addr64_t) vaddr,
file_offset,
(pathname ? pathname : "<nil>"),
? "=="
: "!="),
mtime.tv_sec, mtime.tv_nsec,
- m->object->code_signed,
+ object->code_signed,
m->cs_validated,
m->cs_tainted,
+ m->cs_nx,
m->wpmapped,
- m->slid);
- if (file_object != m->object) {
+ m->slid,
+ m->dirty,
+ shadow_depth);
+
+ /*
+ * We currently only generate an exit reason if cs_invalid_page directly killed a process. If cs_invalid_page
+ * did not kill the process (more the case on desktop), vm_fault_enter will not satisfy the fault and whether the
+ * process dies is dependent on whether there is a signal handler registered for SIGSEGV and how that handler
+ * will deal with the segmentation fault.
+ */
+ if (cs_killed) {
+ KERNEL_DEBUG_CONSTANT(BSDDBG_CODE(DBG_BSD_PROC, BSD_PROC_EXITREASON_CREATE) | DBG_FUNC_NONE,
+ pid, OS_REASON_CODESIGNING, CODESIGNING_EXIT_REASON_INVALID_PAGE, 0, 0);
+
+ codesigning_exit_reason = os_reason_create(OS_REASON_CODESIGNING, CODESIGNING_EXIT_REASON_INVALID_PAGE);
+ if (codesigning_exit_reason == NULL) {
+ printf("vm_fault_enter: failed to allocate codesigning exit reason\n");
+ } else {
+ mach_vm_address_t data_addr = 0;
+ struct codesigning_exit_reason_info *ceri = NULL;
+ uint32_t reason_buffer_size_estimate = kcdata_estimate_required_buffer_size(1, sizeof(*ceri));
+
+ if (os_reason_alloc_buffer_noblock(codesigning_exit_reason, reason_buffer_size_estimate)) {
+ printf("vm_fault_enter: failed to allocate buffer for codesigning exit reason\n");
+ } else {
+ if (KERN_SUCCESS == kcdata_get_memory_addr(&codesigning_exit_reason->osr_kcd_descriptor,
+ EXIT_REASON_CODESIGNING_INFO, sizeof(*ceri), &data_addr)) {
+ ceri = (struct codesigning_exit_reason_info *)data_addr;
+ static_assert(__PATH_MAX == sizeof(ceri->ceri_pathname));
+
+ ceri->ceri_virt_addr = vaddr;
+ ceri->ceri_file_offset = file_offset;
+ if (pathname)
+ strncpy((char *)&ceri->ceri_pathname, pathname, sizeof(ceri->ceri_pathname));
+ else
+ ceri->ceri_pathname[0] = '\0';
+ if (filename)
+ strncpy((char *)&ceri->ceri_filename, filename, sizeof(ceri->ceri_filename));
+ else
+ ceri->ceri_filename[0] = '\0';
+ ceri->ceri_path_truncated = (truncated_path);
+ ceri->ceri_codesig_modtime_secs = cs_mtime.tv_sec;
+ ceri->ceri_codesig_modtime_nsecs = cs_mtime.tv_nsec;
+ ceri->ceri_page_modtime_secs = mtime.tv_sec;
+ ceri->ceri_page_modtime_nsecs = mtime.tv_nsec;
+ ceri->ceri_object_codesigned = (object->code_signed);
+ ceri->ceri_page_codesig_validated = (m->cs_validated);
+ ceri->ceri_page_codesig_tainted = (m->cs_tainted);
+ ceri->ceri_page_codesig_nx = (m->cs_nx);
+ ceri->ceri_page_wpmapped = (m->wpmapped);
+ ceri->ceri_page_slid = (m->slid);
+ ceri->ceri_page_dirty = (m->dirty);
+ ceri->ceri_page_shadow_depth = shadow_depth;
+ } else {
+#if DEBUG || DEVELOPMENT
+ panic("vm_fault_enter: failed to allocate kcdata for codesigning exit reason");
+#else
+ printf("vm_fault_enter: failed to allocate kcdata for codesigning exit reason\n");
+#endif /* DEBUG || DEVELOPMENT */
+ /* Free the buffer */
+ os_reason_alloc_buffer_noblock(codesigning_exit_reason, 0);
+ }
+ }
+ }
+
+ set_thread_exit_reason(current_thread(), codesigning_exit_reason, FALSE);
+ }
+ if (panic_on_cs_killed &&
+ object->object_slid) {
+ panic("CODE SIGNING: process %d[%s]: "
+ "rejecting invalid page at address 0x%llx "
+ "from offset 0x%llx in file \"%s%s%s\" "
+ "(cs_mtime:%lu.%ld %s mtime:%lu.%ld) "
+ "(signed:%d validated:%d tainted:%d nx:%d"
+ "wpmapped:%d slid:%d dirty:%d depth:%d)\n",
+ pid, procname, (addr64_t) vaddr,
+ file_offset,
+ (pathname ? pathname : "<nil>"),
+ (truncated_path ? "/.../" : ""),
+ (truncated_path ? filename : ""),
+ cs_mtime.tv_sec, cs_mtime.tv_nsec,
+ ((cs_mtime.tv_sec == mtime.tv_sec &&
+ cs_mtime.tv_nsec == mtime.tv_nsec)
+ ? "=="
+ : "!="),
+ mtime.tv_sec, mtime.tv_nsec,
+ object->code_signed,
+ m->cs_validated,
+ m->cs_tainted,
+ m->cs_nx,
+ m->wpmapped,
+ m->slid,
+ m->dirty,
+ shadow_depth);
+ }
+
+ if (file_object != object) {
vm_object_unlock(file_object);
}
if (pathname_len != 0) {
/* proceed with the invalid page */
kr = KERN_SUCCESS;
if (!m->cs_validated &&
- !m->object->code_signed) {
+ !object->code_signed) {
/*
* This page has not been (fully) validated but
* does not belong to a code-signed object
}
#endif
}
-
+
} else {
/* proceed with the valid page */
kr = KERN_SUCCESS;
* the page queues. Change wiring
* case is obvious.
*/
- assert(m->compressor || m->object != compressor_object);
- if (m->compressor) {
+ assert((m->vm_page_q_state == VM_PAGE_USED_BY_COMPRESSOR) || object != compressor_object);
+
+#if CONFIG_BACKGROUND_QUEUE
+ vm_page_update_background_state(m);
+#endif
+ if (m->vm_page_q_state == VM_PAGE_USED_BY_COMPRESSOR) {
/*
* Compressor pages are neither wired
* nor pageable and should never change.
*/
- assert(m->object == compressor_object);
+ assert(object == compressor_object);
} else if (change_wiring) {
__VM_PAGE_LOCKSPIN_QUEUES_IF_NEEDED();
if (wired) {
if (kr == KERN_SUCCESS) {
- vm_page_wire(m, VM_PROT_MEMORY_TAG(caller_prot), TRUE);
+ vm_page_wire(m, wire_tag, TRUE);
}
} else {
vm_page_unwire(m, TRUE);
/* we keep the page queues lock, if we need it later */
} else {
+ if (object->internal == TRUE) {
+ /*
+ * don't allow anonymous pages on
+ * the speculative queues
+ */
+ no_cache = FALSE;
+ }
if (kr != KERN_SUCCESS) {
__VM_PAGE_LOCKSPIN_QUEUES_IF_NEEDED();
vm_page_deactivate(m);
/* we keep the page queues lock, if we need it later */
- } else if (((!m->active && !m->inactive) ||
- m->clean_queue ||
- no_cache) &&
- !VM_PAGE_WIRED(m) && !m->throttled) {
+ } else if (((m->vm_page_q_state == VM_PAGE_NOT_ON_Q) ||
+ (m->vm_page_q_state == VM_PAGE_ON_SPECULATIVE_Q) ||
+ (m->vm_page_q_state == VM_PAGE_ON_INACTIVE_CLEANED_Q) ||
+ ((m->vm_page_q_state != VM_PAGE_ON_THROTTLED_Q) && no_cache)) &&
+ !VM_PAGE_WIRED(m)) {
if (vm_page_local_q &&
- !no_cache &&
(*type_of_fault == DBG_COW_FAULT ||
*type_of_fault == DBG_ZERO_FILL_FAULT) ) {
struct vpl *lq;
uint32_t lid;
+ assert(m->vm_page_q_state == VM_PAGE_NOT_ON_Q);
+
__VM_PAGE_UNLOCK_QUEUES_IF_NEEDED();
- vm_object_lock_assert_exclusive(m->object);
+ vm_object_lock_assert_exclusive(object);
/*
* we got a local queue to stuff this
* we'll use the current cpu number to
* select the queue note that we don't
* need to disable preemption... we're
- * going to behind the local queue's
+ * going to be behind the local queue's
* lock to do the real work
*/
lid = cpu_number();
VPL_LOCK(&lq->vpl_lock);
vm_page_check_pageable_safe(m);
- queue_enter(&lq->vpl_queue, m,
- vm_page_t, pageq);
- m->local = TRUE;
+ vm_page_queue_enter(&lq->vpl_queue, m,
+ vm_page_t, pageq);
+ m->vm_page_q_state = VM_PAGE_ON_ACTIVE_LOCAL_Q;
m->local_id = lid;
lq->vpl_count++;
-
- if (m->object->internal)
+
+ if (object->internal)
lq->vpl_internal_count++;
else
lq->vpl_external_count++;
* page queue lock
*/
if (!VM_PAGE_WIRED(m)) {
- if (m->clean_queue) {
- vm_page_queues_remove(m);
+ if (m->vm_page_q_state == VM_PAGE_ON_INACTIVE_CLEANED_Q) {
+ vm_page_queues_remove(m, FALSE);
vm_pageout_cleaned_reactivated++;
vm_pageout_cleaned_fault_reactivated++;
}
- if ((!m->active &&
- !m->inactive) ||
- no_cache) {
+ if ( !VM_PAGE_ACTIVE_OR_INACTIVE(m) ||
+ no_cache) {
/*
* If this is a no_cache mapping
* and the page has never been
* that they can be readily
* recycled if free memory runs
* low. Otherwise the page is
- * activated as normal.
+ * activated as normal.
*/
if (no_cache &&
m->no_cache)) {
m->no_cache = TRUE;
- if (!m->speculative)
+ if (m->vm_page_q_state != VM_PAGE_ON_SPECULATIVE_Q)
vm_page_speculate(m, FALSE);
- } else if (!m->active &&
- !m->inactive) {
-
+ } else if ( !VM_PAGE_ACTIVE_OR_INACTIVE(m)) {
vm_page_activate(m);
}
}
* now so those processes can take note.
*/
if (kr == KERN_SUCCESS) {
-
/*
* NOTE: we may only hold the vm_object lock SHARED
- * at this point, so we need the phys_page lock to
+ * at this point, so we need the phys_page lock to
* properly serialize updating the pmapped and
* xpmapped bits
*/
if ((prot & VM_PROT_EXECUTE) && !m->xpmapped) {
+ ppnum_t phys_page = VM_PAGE_GET_PHYS_PAGE(m);
- pmap_lock_phys_page(m->phys_page);
+ pmap_lock_phys_page(phys_page);
/*
* go ahead and take the opportunity
* to set 'pmapped' here so that we don't
* just below
*/
m->pmapped = TRUE;
-
+
if (!m->xpmapped) {
m->xpmapped = TRUE;
- pmap_unlock_phys_page(m->phys_page);
+ pmap_unlock_phys_page(phys_page);
- if (!m->object->internal)
+ if (!object->internal)
OSAddAtomic(1, &vm_page_xpmapped_external_count);
- if ((COMPRESSED_PAGER_IS_ACTIVE) &&
- m->object->internal &&
- m->object->pager != NULL) {
+#if defined(__arm__) || defined(__arm64__)
+ pmap_sync_page_data_phys(phys_page);
+#else
+ if (object->internal &&
+ object->pager != NULL) {
/*
* This page could have been
* uncompressed by the
* make sure the icache is in
* sync.
*/
- pmap_sync_page_data_phys(m->phys_page);
+ assert(VM_CONFIG_COMPRESSOR_IS_PRESENT);
+ pmap_sync_page_data_phys(phys_page);
}
+#endif
} else
- pmap_unlock_phys_page(m->phys_page);
+ pmap_unlock_phys_page(phys_page);
} else {
if (m->pmapped == FALSE) {
- pmap_lock_phys_page(m->phys_page);
+ ppnum_t phys_page = VM_PAGE_GET_PHYS_PAGE(m);
+
+ pmap_lock_phys_page(phys_page);
m->pmapped = TRUE;
- pmap_unlock_phys_page(m->phys_page);
+ pmap_unlock_phys_page(phys_page);
}
}
if (vm_page_is_slideable(m)) {
boolean_t was_busy = m->busy;
- vm_object_lock_assert_exclusive(m->object);
+ vm_object_lock_assert_exclusive(object);
m->busy = TRUE;
kr = vm_page_slide(m, 0);
if (fault_type & VM_PROT_WRITE) {
if (m->wpmapped == FALSE) {
- vm_object_lock_assert_exclusive(m->object);
- if (!m->object->internal)
- task_update_logical_writes(current_task(), PAGE_SIZE, TASK_WRITE_DEFERRED);
+ vm_object_lock_assert_exclusive(object);
+ if (!object->internal && object->pager) {
+ task_update_logical_writes(current_task(), PAGE_SIZE, TASK_WRITE_DEFERRED, vnode_pager_lookup_vnode(object->pager));
+ }
m->wpmapped = TRUE;
}
if (must_disconnect) {
/*
- * We can only get here
+ * We can only get here
* because of the CSE logic
*/
assert(cs_enforcement_enabled);
- pmap_disconnect(m->phys_page);
- /*
+ pmap_disconnect(VM_PAGE_GET_PHYS_PAGE(m));
+ /*
* If we are faulting for a write, we can clear
* the execute bit - that will ensure the page is
* checked again before being executable, which
* protects against a map switch.
* This only happens the first time the page
- * gets tainted, so we won't get stuck here
+ * gets tainted, so we won't get stuck here
* to make an already writeable page executable.
*/
if (!cs_bypass){
+ assert(!pmap_has_prot_policy(prot));
prot &= ~VM_PROT_EXECUTE;
}
}
}
+ assert(VM_PAGE_OBJECT(m) == object);
/* Prevent a deadlock by not
* holding the object lock if we need to wait for a page in
wired,
pmap_options | PMAP_OPTIONS_NOWAIT,
pe_result);
+#if __x86_64__
+ if (pe_result == KERN_INVALID_ARGUMENT &&
+ pmap == PMAP_NULL &&
+ wired) {
+ /*
+ * Wiring a page in a pmap-less VM map:
+ * VMware's "vmmon" kernel extension does this
+ * to grab pages.
+ * Let it proceed even though the PMAP_ENTER() failed.
+ */
+ pe_result = KERN_SUCCESS;
+ }
+#endif /* __x86_64__ */
if(pe_result == KERN_RESOURCE_SHORTAGE) {
* on the top-object in this chain... we can't just drop
* the lock on the object we're inserting the page into
* and recall the PMAP_ENTER since we can still cause
- * a deadlock if one of the critical paths tries to
+ * a deadlock if one of the critical paths tries to
* acquire the lock on the top-object and we're blocked
* in PMAP_ENTER waiting for memory... our only recourse
- * is to deal with it at a higher level where we can
+ * is to deal with it at a higher level where we can
* drop both locks.
*/
*need_retry = TRUE;
}
/* The nonblocking version of pmap_enter did not succeed.
* and we don't need to drop other locks and retry
- * at the level above us, so
+ * at the level above us, so
* use the blocking version instead. Requires marking
* the page busy and unlocking the object */
boolean_t was_busy = m->busy;
- vm_object_lock_assert_exclusive(m->object);
+ vm_object_lock_assert_exclusive(object);
m->busy = TRUE;
- vm_object_unlock(m->object);
-
+ vm_object_unlock(object);
+
PMAP_ENTER_OPTIONS(pmap, vaddr, m, prot, fault_type,
0, wired,
pmap_options, pe_result);
-
+
+ assert(VM_PAGE_OBJECT(m) == object);
+
/* Take the object lock again. */
- vm_object_lock(m->object);
-
+ vm_object_lock(object);
+
/* If the page was busy, someone else will wake it up.
* Otherwise, we have to do it now. */
assert(m->busy);
}
vm_pmap_enter_blocked++;
}
+
+ kr = pe_result;
}
after_the_pmap_enter:
{
if (pmap_find_phys(current_map()->pmap, vaddr) == 0) {
- vm_fault(current_map(), /* map */
- vaddr, /* vaddr */
- VM_PROT_READ, /* fault_type */
- FALSE, /* change_wiring */
- THREAD_UNINT, /* interruptible */
- NULL, /* caller_pmap */
- 0 /* caller_pmap_addr */);
+ vm_fault(current_map(), /* map */
+ vaddr, /* vaddr */
+ VM_PROT_READ, /* fault_type */
+ FALSE, /* change_wiring */
+ VM_KERN_MEMORY_NONE, /* tag - not wiring */
+ THREAD_UNINT, /* interruptible */
+ NULL, /* caller_pmap */
+ 0 /* caller_pmap_addr */);
}
}
*/
extern int _map_enter_debug;
+extern uint64_t get_current_unique_pid(void);
unsigned long vm_fault_collapse_total = 0;
unsigned long vm_fault_collapse_skipped = 0;
kern_return_t
-vm_fault(
+vm_fault_external(
vm_map_t map,
vm_map_offset_t vaddr,
vm_prot_t fault_type,
pmap_t caller_pmap,
vm_map_offset_t caller_pmap_addr)
{
- return vm_fault_internal(map, vaddr, fault_type, change_wiring,
+ return vm_fault_internal(map, vaddr, fault_type, change_wiring, vm_tag_bt(),
interruptible, caller_pmap, caller_pmap_addr,
NULL);
}
+kern_return_t
+vm_fault(
+ vm_map_t map,
+ vm_map_offset_t vaddr,
+ vm_prot_t fault_type,
+ boolean_t change_wiring,
+ vm_tag_t wire_tag, /* if wiring must pass tag != VM_KERN_MEMORY_NONE */
+ int interruptible,
+ pmap_t caller_pmap,
+ vm_map_offset_t caller_pmap_addr)
+{
+ return vm_fault_internal(map, vaddr, fault_type, change_wiring, wire_tag,
+ interruptible, caller_pmap, caller_pmap_addr,
+ NULL);
+}
kern_return_t
vm_fault_internal(
vm_map_offset_t vaddr,
vm_prot_t caller_prot,
boolean_t change_wiring,
+ vm_tag_t wire_tag, /* if wiring must pass tag != VM_KERN_MEMORY_NONE */
int interruptible,
pmap_t caller_pmap,
vm_map_offset_t caller_pmap_addr,
vm_page_t m; /* Fast access to result_page */
kern_return_t error_code;
vm_object_t cur_object;
+ vm_object_t m_object = NULL;
vm_object_offset_t cur_offset;
vm_page_t cur_m;
vm_object_t new_object;
boolean_t interruptible_state;
vm_map_t real_map = map;
vm_map_t original_map = map;
+ boolean_t object_locks_dropped = FALSE;
vm_prot_t fault_type;
vm_prot_t original_fault_type;
struct vm_object_fault_info fault_info;
vm_object_t top_object = VM_OBJECT_NULL;
int throttle_delay;
int compressed_count_delta;
+ int grab_options;
+ vm_map_offset_t trace_vaddr;
+ vm_map_offset_t trace_real_vaddr;
+#if DEVELOPMENT || DEBUG
+ vm_map_offset_t real_vaddr;
+ real_vaddr = vaddr;
+#endif /* DEVELOPMENT || DEBUG */
+ trace_real_vaddr = vaddr;
+ vaddr = vm_map_trunc_page(vaddr, PAGE_MASK);
+
+ if (map == kernel_map) {
+ trace_vaddr = VM_KERNEL_ADDRHIDE(vaddr);
+ trace_real_vaddr = VM_KERNEL_ADDRHIDE(trace_real_vaddr);
+ } else {
+ trace_vaddr = vaddr;
+ }
- KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE,
+ KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE,
(MACHDBG_CODE(DBG_MACH_VM, 2)) | DBG_FUNC_START,
- ((uint64_t)vaddr >> 32),
- vaddr,
+ ((uint64_t)trace_vaddr >> 32),
+ trace_vaddr,
(map == kernel_map),
0,
0);
if (get_preemption_level() != 0) {
- KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE,
+ KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE,
(MACHDBG_CODE(DBG_MACH_VM, 2)) | DBG_FUNC_END,
- ((uint64_t)vaddr >> 32),
- vaddr,
+ ((uint64_t)trace_vaddr >> 32),
+ trace_vaddr,
KERN_FAILURE,
0,
0);
return (KERN_FAILURE);
}
-
+
interruptible_state = thread_interrupt_level(interruptible);
fault_type = (change_wiring ? VM_PROT_NONE : caller_prot);
cur_object_lock_type = OBJECT_LOCK_SHARED;
+ if ((map == kernel_map) && (caller_prot & VM_PROT_WRITE)) {
+ if (compressor_map) {
+ if ((vaddr >= vm_map_min(compressor_map)) && (vaddr < vm_map_max(compressor_map))) {
+ panic("Write fault on compressor map, va: %p type: %u bounds: %p->%p", (void *) vaddr, caller_prot, (void *) vm_map_min(compressor_map), (void *) vm_map_max(compressor_map));
+
+ }
+ }
+ }
RetryFault:
/*
* assume we will hit a page in the cache
&fault_info,
&real_map);
-
if (kr != KERN_SUCCESS) {
vm_map_unlock_read(map);
goto done;
*
*/
+#if defined(__arm64__)
+ /*
+ * Fail if reading an execute-only page in a
+ * pmap that enforces execute-only protection.
+ */
+ if (fault_type == VM_PROT_READ &&
+ (prot & VM_PROT_EXECUTE) &&
+ !(prot & VM_PROT_READ) &&
+ pmap_enforces_execute_only(pmap)) {
+ vm_object_unlock(object);
+ vm_map_unlock_read(map);
+ if (real_map != map) {
+ vm_map_unlock(real_map);
+ }
+ kr = KERN_PROTECTION_FAILURE;
+ goto done;
+ }
+#endif
/*
* If this page is to be inserted in a copy delay object
cur_object = object;
cur_offset = offset;
+ grab_options = 0;
+#if CONFIG_SECLUDED_MEMORY
+ if (object->can_grab_secluded) {
+ grab_options |= VM_PAGE_GRAB_SECLUDED;
+ }
+#endif /* CONFIG_SECLUDED_MEMORY */
+
while (TRUE) {
if (!cur_object->pager_created &&
cur_object->phys_contiguous) /* superpage */
}
m = vm_page_lookup(cur_object, cur_offset);
+ m_object = NULL;
if (m != VM_PAGE_NULL) {
+ m_object = cur_object;
+
if (m->busy) {
wait_result_t result;
continue;
}
}
- if (m->pageout_queue && m->object->internal && COMPRESSED_PAGER_IS_ACTIVE) {
+ if ((m->vm_page_q_state == VM_PAGE_ON_PAGEOUT_Q) && m_object->internal) {
/*
* m->busy == TRUE and the object is locked exclusively
* if m->pageout_queue == TRUE after we acquire the
* NOTE: this is only true for the internal pageout queue
* in the compressor world
*/
+ assert(VM_CONFIG_COMPRESSOR_IS_PRESENT);
+
vm_page_lock_queues();
- if (m->pageout_queue) {
+ if (m->vm_page_q_state == VM_PAGE_ON_PAGEOUT_Q) {
vm_pageout_throttle_up(m);
vm_page_unlock_queues();
continue;
}
}
- m->pageout = FALSE;
-
vm_pageout_steal_laundry(m, FALSE);
}
- if (m->phys_page == vm_page_guard_addr) {
+ if (VM_PAGE_GET_PHYS_PAGE(m) == vm_page_guard_addr) {
/*
* Guard page: let the slow path deal with it
*/
*/
break;
}
- if (VM_OBJECT_PURGEABLE_FAULT_ERROR(m->object)) {
+ if (VM_OBJECT_PURGEABLE_FAULT_ERROR(m_object)) {
if (object != cur_object)
vm_object_unlock(object);
vm_map_unlock_read(map);
kr = KERN_MEMORY_ERROR;
goto done;
}
-
- if (m->encrypted) {
- /*
- * ENCRYPTED SWAP:
- * We've soft-faulted (because it's not in the page
- * table) on an encrypted page.
- * Keep the page "busy" so that no one messes with
- * it during the decryption.
- * Release the extra locks we're holding, keep only
- * the page's VM object lock.
- *
- * in order to set 'busy' on 'm', we must
- * have object that 'm' belongs to locked exclusively
- */
- if (object != cur_object) {
- vm_object_unlock(object);
-
- if (cur_object_lock_type == OBJECT_LOCK_SHARED) {
-
- cur_object_lock_type = OBJECT_LOCK_EXCLUSIVE;
-
- if (vm_object_lock_upgrade(cur_object) == FALSE) {
- /*
- * couldn't upgrade so go do a full retry
- * immediately since we've already dropped
- * the top object lock associated with this page
- * and the current one got dropped due to the
- * failed upgrade... the state is no longer valid
- */
- vm_map_unlock_read(map);
- if (real_map != map)
- vm_map_unlock(real_map);
-
- goto RetryFault;
- }
- }
- } else if (object_lock_type == OBJECT_LOCK_SHARED) {
-
- object_lock_type = OBJECT_LOCK_EXCLUSIVE;
-
- if (vm_object_lock_upgrade(object) == FALSE) {
- /*
- * couldn't upgrade, so explictly take the lock
- * exclusively and go relookup the page since we
- * will have dropped the object lock and
- * a different thread could have inserted
- * a page at this offset
- * no need for a full retry since we're
- * at the top level of the object chain
- */
- vm_object_lock(object);
-
- continue;
- }
- }
- m->busy = TRUE;
-
- vm_map_unlock_read(map);
- if (real_map != map)
- vm_map_unlock(real_map);
-
- vm_page_decrypt(m, 0);
-
- assert(m->busy);
- PAGE_WAKEUP_DONE(m);
-
- vm_object_unlock(cur_object);
- /*
- * Retry from the top, in case anything
- * changed while we were decrypting...
- */
- goto RetryFault;
- }
- ASSERT_PAGE_DECRYPTED(m);
-
- if(vm_page_is_slideable(m)) {
+ if (vm_page_is_slideable(m)) {
/*
* We might need to slide this page, and so,
* we want to hold the VM object exclusively.
goto RetryFault;
}
}
+ assert(m_object == VM_PAGE_OBJECT(m));
- if (VM_FAULT_NEED_CS_VALIDATION(map->pmap, m) ||
+ if (VM_FAULT_NEED_CS_VALIDATION(map->pmap, m, m_object) ||
(physpage_p != NULL && (prot & VM_PROT_WRITE))) {
upgrade_for_validation:
/*
}
if ((fault_type & VM_PROT_WRITE) == 0) {
+ if (!pmap_has_prot_policy(prot)) {
+ prot &= ~VM_PROT_WRITE;
+ } else {
+ /*
+ * For a protection that the pmap cares
+ * about, we must hand over the full
+ * set of protections (so that the pmap
+ * layer can apply any desired policy).
+ * This means that cs_bypass must be
+ * set, as this can force us to pass
+ * RWX.
+ */
+ assert(fault_info.cs_bypass);
+ }
if (object != cur_object) {
/*
object_lock_type = cur_object_lock_type;
}
FastPmapEnter:
+ assert(m_object == VM_PAGE_OBJECT(m));
+
/*
* prepare for the pmap_enter...
* object and map are both locked
caller_prot,
wired,
change_wiring,
+ wire_tag,
fault_info.no_cache,
fault_info.cs_bypass,
fault_info.user_tag,
caller_prot,
wired,
change_wiring,
+ wire_tag,
fault_info.no_cache,
fault_info.cs_bypass,
fault_info.user_tag,
need_retry_ptr,
&type_of_fault);
}
+#if DEVELOPMENT || DEBUG
+ {
+ int event_code = 0;
+ if (m_object->internal)
+ event_code = (MACHDBG_CODE(DBG_MACH_WORKINGSET, VM_REAL_FAULT_ADDR_INTERNAL));
+ else if (m_object->object_slid)
+ event_code = (MACHDBG_CODE(DBG_MACH_WORKINGSET, VM_REAL_FAULT_ADDR_SHAREDCACHE));
+ else
+ event_code = (MACHDBG_CODE(DBG_MACH_WORKINGSET, VM_REAL_FAULT_ADDR_EXTERNAL));
+
+ KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, event_code, trace_real_vaddr, (fault_info.user_tag << 16) | (caller_prot << 8) | type_of_fault, m->offset, get_current_unique_pid(), 0);
+
+ DTRACE_VM6(real_fault, vm_map_offset_t, real_vaddr, vm_map_offset_t, m->offset, int, event_code, int, caller_prot, int, type_of_fault, int, fault_info.user_tag);
+ }
+#endif
if (kr == KERN_SUCCESS &&
physpage_p != NULL) {
/* for vm_map_wire_and_extract() */
- *physpage_p = m->phys_page;
+ *physpage_p = VM_PAGE_GET_PHYS_PAGE(m);
if (prot & VM_PROT_WRITE) {
- vm_object_lock_assert_exclusive(
- m->object);
+ vm_object_lock_assert_exclusive(m_object);
m->dirty = TRUE;
}
}
if (need_collapse == TRUE)
vm_object_collapse(object, offset, TRUE);
-
+
if (need_retry == FALSE &&
(type_of_fault == DBG_PAGEIND_FAULT || type_of_fault == DBG_PAGEINV_FAULT || type_of_fault == DBG_CACHE_HIT_FAULT)) {
/*
(void)pmap_enter_options(
pmap, vaddr, 0, 0, 0, 0, 0,
PMAP_OPTIONS_NOENTER, NULL);
-
+
need_retry = FALSE;
goto RetryFault;
}
*/
break;
}
-
+
/*
* This is now a shadow based copy on write
* fault -- it requires a copy up the shadow
* chain.
*/
-
+ assert(m_object == VM_PAGE_OBJECT(m));
+
if ((cur_object_lock_type == OBJECT_LOCK_SHARED) &&
- VM_FAULT_NEED_CS_VALIDATION(NULL, m)) {
+ VM_FAULT_NEED_CS_VALIDATION(NULL, m, m_object)) {
goto upgrade_for_validation;
}
* need to remember current page, as it's the
* source of the copy.
*
- * at this point we hold locks on both
+ * at this point we hold locks on both
* object and cur_object... no need to take
* paging refs or mark pages BUSY since
* we don't drop either object lock until
* the page has been copied and inserted
*/
cur_m = m;
- m = vm_page_grab();
+ m = vm_page_grab_options(grab_options);
+ m_object = NULL;
if (m == VM_PAGE_NULL) {
/*
*/
vm_page_copy(cur_m, m);
vm_page_insert(m, object, offset);
+ m_object = object;
SET_PAGE_DIRTY(m, FALSE);
/*
* Now cope with the source page and object
*/
if (object->ref_count > 1 && cur_m->pmapped)
- pmap_disconnect(cur_m->phys_page);
-
+ pmap_disconnect(VM_PAGE_GET_PHYS_PAGE(cur_m));
+
if (cur_m->clustered) {
VM_PAGE_COUNT_AS_PAGEIN(cur_m);
VM_PAGE_CONSUME_CLUSTERED(cur_m);
+ vm_fault_is_sequential(cur_object, cur_offset, fault_info.behavior);
}
need_collapse = TRUE;
* at the top level of the object chain
*/
vm_object_lock(object);
-
+
continue;
}
}
- m = vm_page_grab();
+ m = vm_page_grab_options(grab_options);
+ m_object = NULL;
if (m == VM_PAGE_NULL) {
/*
cur_object->pager,
(cur_offset +
cur_object->paging_offset),
- m->phys_page,
+ VM_PAGE_GET_PHYS_PAGE(m),
&my_fault_type,
c_flags,
&compressed_count_delta);
cur_object);
if (kr != KERN_SUCCESS) {
- vm_page_release(m);
+ vm_page_release(m, FALSE);
+ m = VM_PAGE_NULL;
break;
}
m->dirty = TRUE;
if (insert_cur_object) {
vm_page_insert(m, cur_object, cur_offset);
+ m_object = cur_object;
} else {
vm_page_insert(m, object, offset);
+ m_object = object;
}
- if ((m->object->wimg_bits & VM_WIMG_MASK) != VM_WIMG_USE_DEFAULT) {
+ if ((m_object->wimg_bits & VM_WIMG_MASK) != VM_WIMG_USE_DEFAULT) {
/*
* If the page is not cacheable,
* we can't let its contents
* linger in the data cache
* after the decompression.
*/
- pmap_sync_page_attributes_phys(m->phys_page);
+ pmap_sync_page_attributes_phys(VM_PAGE_GET_PHYS_PAGE(m));
}
type_of_fault = my_fault_type;
* inserted into the original object.
*/
if (cur_object->shadow_severed ||
- VM_OBJECT_PURGEABLE_FAULT_ERROR(cur_object))
- {
+ VM_OBJECT_PURGEABLE_FAULT_ERROR(cur_object) ||
+ cur_object == compressor_object ||
+ cur_object == kernel_object ||
+ cur_object == vm_submap_object) {
if (object != cur_object)
vm_object_unlock(cur_object);
vm_object_unlock(object);
if (vm_backing_store_low) {
/*
* we are protecting the system from
- * backing store exhaustion...
+ * backing store exhaustion...
* must take the slow path if we're
* not privileged
*/
}
}
m = vm_page_alloc(object, offset);
+ m_object = NULL;
if (m == VM_PAGE_NULL) {
/*
*/
break;
}
+ m_object = object;
/*
* Now zero fill page...
- * the page is probably going to
+ * the page is probably going to
* be written soon, so don't bother
* to clear the modified bit
*
if (real_map != map)
vm_map_unlock(real_map);
+ if (__improbable(object == compressor_object ||
+ object == kernel_object ||
+ object == vm_submap_object)) {
+ /*
+ * These objects are explicitly managed and populated by the
+ * kernel. The virtual ranges backed by these objects should
+ * either have wired pages or "holes" that are not supposed to
+ * be accessed at all until they get explicitly populated.
+ * We should never have to resolve a fault on a mapping backed
+ * by one of these VM objects and providing a zero-filled page
+ * would be wrong here, so let's fail the fault and let the
+ * caller crash or recover.
+ */
+ vm_object_unlock(object);
+ kr = KERN_MEMORY_ERROR;
+ goto done;
+ }
+
+ assert(object != compressor_object);
+ assert(object != kernel_object);
+ assert(object != vm_submap_object);
+
/*
* Make a reference to this object to
* prevent its disposal while we are messing with
*
* the object is returned locked with a paging reference
*
- * if top_page != NULL, then it's BUSY and the
+ * if top_page != NULL, then it's BUSY and the
* object it belongs to has a paging reference
* but is returned unlocked
*/
*/
switch (kr) {
case VM_FAULT_MEMORY_SHORTAGE:
- if (vm_page_wait((change_wiring) ?
+ if (vm_page_wait((change_wiring) ?
THREAD_UNINT :
THREAD_ABORTSAFE))
goto RetryFault;
}
}
m = result_page;
+ m_object = NULL;
if (m != VM_PAGE_NULL) {
+ m_object = VM_PAGE_OBJECT(m);
assert((change_wiring && !wired) ?
- (top_page == VM_PAGE_NULL) :
- ((top_page == VM_PAGE_NULL) == (m->object == object)));
+ (top_page == VM_PAGE_NULL) :
+ ((top_page == VM_PAGE_NULL) == (m_object == object)));
}
/*
#define RELEASE_PAGE(m) \
MACRO_BEGIN \
PAGE_WAKEUP_DONE(m); \
- if (!m->active && !m->inactive && !m->throttled) { \
- vm_page_lockspin_queues(); \
- if (!m->active && !m->inactive && !m->throttled) \
- vm_page_activate(m); \
- vm_page_unlock_queues(); \
- } \
+ if ( !VM_PAGE_PAGEABLE(m)) { \
+ vm_page_lockspin_queues(); \
+ if ( !VM_PAGE_PAGEABLE(m)) \
+ vm_page_activate(m); \
+ vm_page_unlock_queues(); \
+ } \
MACRO_END
+
+ object_locks_dropped = FALSE;
/*
* We must verify that the maps have not changed
- * since our last lookup.
+ * since our last lookup. vm_map_verify() needs the
+ * map lock (shared) but we are holding object locks.
+ * So we do a try_lock() first and, if that fails, we
+ * drop the object locks and go in for the map lock again.
*/
- if (m != VM_PAGE_NULL) {
- old_copy_object = m->object->copy;
- vm_object_unlock(m->object);
- } else {
- old_copy_object = VM_OBJECT_NULL;
- vm_object_unlock(object);
+ if (!vm_map_try_lock_read(original_map)) {
+
+ if (m != VM_PAGE_NULL) {
+ old_copy_object = m_object->copy;
+ vm_object_unlock(m_object);
+ } else {
+ old_copy_object = VM_OBJECT_NULL;
+ vm_object_unlock(object);
+ }
+
+ object_locks_dropped = TRUE;
+
+ vm_map_lock_read(original_map);
}
- /*
- * no object locks are held at this point
- */
if ((map != original_map) || !vm_map_verify(map, &version)) {
+
+ if (object_locks_dropped == FALSE) {
+ if (m != VM_PAGE_NULL) {
+ old_copy_object = m_object->copy;
+ vm_object_unlock(m_object);
+ } else {
+ old_copy_object = VM_OBJECT_NULL;
+ vm_object_unlock(object);
+ }
+
+ object_locks_dropped = TRUE;
+ }
+
+ /*
+ * no object locks are held at this point
+ */
vm_object_t retry_object;
vm_object_offset_t retry_offset;
vm_prot_t retry_prot;
* take another fault.
*/
map = original_map;
- vm_map_lock_read(map);
kr = vm_map_lookup_locked(&map, vaddr,
fault_type & ~VM_PROT_WRITE,
vm_map_unlock_read(map);
if (m != VM_PAGE_NULL) {
+ assert(VM_PAGE_OBJECT(m) == m_object);
+
/*
* retake the lock so that
* we can drop the paging reference
* in vm_fault_cleanup and do the
* PAGE_WAKEUP_DONE in RELEASE_PAGE
*/
- vm_object_lock(m->object);
+ vm_object_lock(m_object);
RELEASE_PAGE(m);
- vm_fault_cleanup(m->object, top_page);
+ vm_fault_cleanup(m_object, top_page);
} else {
/*
* retake the lock so that
vm_map_unlock(real_map);
if (m != VM_PAGE_NULL) {
+ assert(VM_PAGE_OBJECT(m) == m_object);
+
/*
* retake the lock so that
* we can drop the paging reference
* in vm_fault_cleanup and do the
* PAGE_WAKEUP_DONE in RELEASE_PAGE
*/
- vm_object_lock(m->object);
+ vm_object_lock(m_object);
RELEASE_PAGE(m);
- vm_fault_cleanup(m->object, top_page);
+ vm_fault_cleanup(m_object, top_page);
} else {
/*
* retake the lock so that
* Check whether the protection has changed or the object
* has been copied while we left the map unlocked.
*/
- prot &= retry_prot;
+ if (pmap_has_prot_policy(retry_prot)) {
+ /* If the pmap layer cares, pass the full set. */
+ prot = retry_prot;
+ } else {
+ prot &= retry_prot;
+ }
}
- if (m != VM_PAGE_NULL) {
- vm_object_lock(m->object);
- if (m->object->copy != old_copy_object) {
- /*
- * The copy object changed while the top-level object
- * was unlocked, so take away write permission.
- */
- prot &= ~VM_PROT_WRITE;
- }
- } else
- vm_object_lock(object);
+ if (object_locks_dropped == TRUE) {
+ if (m != VM_PAGE_NULL) {
+ vm_object_lock(m_object);
+
+ if (m_object->copy != old_copy_object) {
+ /*
+ * The copy object changed while the top-level object
+ * was unlocked, so take away write permission.
+ */
+ assert(!pmap_has_prot_policy(prot));
+ prot &= ~VM_PROT_WRITE;
+ }
+ } else
+ vm_object_lock(object);
+
+ object_locks_dropped = FALSE;
+ }
/*
* If we want to wire down this page, but no longer have
*/
if (wired && (fault_type != (prot | VM_PROT_WRITE))) {
- vm_map_verify_done(map, &version);
+ vm_map_unlock_read(map);
if (real_map != map)
vm_map_unlock(real_map);
if (m != VM_PAGE_NULL) {
+ assert(VM_PAGE_OBJECT(m) == m_object);
+
RELEASE_PAGE(m);
- vm_fault_cleanup(m->object, top_page);
+ vm_fault_cleanup(m_object, top_page);
} else
vm_fault_cleanup(object, top_page);
caller_prot,
wired,
change_wiring,
+ wire_tag,
fault_info.no_cache,
fault_info.cs_bypass,
fault_info.user_tag,
caller_prot,
wired,
change_wiring,
+ wire_tag,
fault_info.no_cache,
fault_info.cs_bypass,
fault_info.user_tag,
NULL,
&type_of_fault);
}
+ assert(VM_PAGE_OBJECT(m) == m_object);
+
+#if DEVELOPMENT || DEBUG
+ {
+ int event_code = 0;
+
+ if (m_object->internal)
+ event_code = (MACHDBG_CODE(DBG_MACH_WORKINGSET, VM_REAL_FAULT_ADDR_INTERNAL));
+ else if (m_object->object_slid)
+ event_code = (MACHDBG_CODE(DBG_MACH_WORKINGSET, VM_REAL_FAULT_ADDR_SHAREDCACHE));
+ else
+ event_code = (MACHDBG_CODE(DBG_MACH_WORKINGSET, VM_REAL_FAULT_ADDR_EXTERNAL));
+
+ KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, event_code, trace_real_vaddr, (fault_info.user_tag << 16) | (caller_prot << 8) | type_of_fault, m->offset, get_current_unique_pid(), 0);
+
+ DTRACE_VM6(real_fault, vm_map_offset_t, real_vaddr, vm_map_offset_t, m->offset, int, event_code, int, caller_prot, int, type_of_fault, int, fault_info.user_tag);
+ }
+#endif
if (kr != KERN_SUCCESS) {
/* abort this page fault */
- vm_map_verify_done(map, &version);
+ vm_map_unlock_read(map);
if (real_map != map)
vm_map_unlock(real_map);
PAGE_WAKEUP_DONE(m);
- vm_fault_cleanup(m->object, top_page);
+ vm_fault_cleanup(m_object, top_page);
vm_object_deallocate(object);
goto done;
}
if (physpage_p != NULL) {
/* for vm_map_wire_and_extract() */
- *physpage_p = m->phys_page;
+ *physpage_p = VM_PAGE_GET_PHYS_PAGE(m);
if (prot & VM_PROT_WRITE) {
- vm_object_lock_assert_exclusive(m->object);
+ vm_object_lock_assert_exclusive(m_object);
m->dirty = TRUE;
}
}
vm_map_offset_t laddr;
vm_map_offset_t ldelta, hdelta;
- /*
+ /*
* do a pmap block mapping from the physical address
- * in the object
+ * in the object
*/
#ifdef ppc
if ((fault_type & VM_PROT_EXECUTE) &&
(!pmap_eligible_for_execute((ppnum_t)(object->vo_shadow_offset >> 12)))) {
- vm_map_verify_done(map, &version);
+ vm_map_unlock_read(map);
if (real_map != map)
vm_map_unlock(real_map);
if (hdelta > (entry->vme_end - laddr))
hdelta = entry->vme_end - laddr;
if (entry->is_sub_map) {
-
- laddr = ((laddr - entry->vme_start)
+
+ laddr = ((laddr - entry->vme_start)
+ VME_OFFSET(entry));
vm_map_lock_read(VME_SUBMAP(entry));
real_map = VME_SUBMAP(entry);
}
map = VME_SUBMAP(entry);
-
+
} else {
break;
}
}
- if (vm_map_lookup_entry(map, laddr, &entry) &&
+ if (vm_map_lookup_entry(map, laddr, &entry) &&
(VME_OBJECT(entry) != NULL) &&
(VME_OBJECT(entry) == object)) {
int superpage;
* Set up a block mapped area
*/
assert((uint32_t)((ldelta + hdelta) >> PAGE_SHIFT) == ((ldelta + hdelta) >> PAGE_SHIFT));
- pmap_map_block(caller_pmap,
- (addr64_t)(caller_pmap_addr - ldelta),
- (ppnum_t)((((vm_map_offset_t) (VME_OBJECT(entry)->vo_shadow_offset)) +
- VME_OFFSET(entry) + (laddr - entry->vme_start) - ldelta) >> PAGE_SHIFT),
- (uint32_t)((ldelta + hdelta) >> PAGE_SHIFT), prot,
- (VM_WIMG_MASK & (int)object->wimg_bits) | superpage, 0);
- } else {
+ kr = pmap_map_block(caller_pmap,
+ (addr64_t)(caller_pmap_addr - ldelta),
+ (ppnum_t)((((vm_map_offset_t) (VME_OBJECT(entry)->vo_shadow_offset)) +
+ VME_OFFSET(entry) + (laddr - entry->vme_start) - ldelta) >> PAGE_SHIFT),
+ (uint32_t)((ldelta + hdelta) >> PAGE_SHIFT), prot,
+ (VM_WIMG_MASK & (int)object->wimg_bits) | superpage, 0);
+
+ if (kr != KERN_SUCCESS) {
+ goto cleanup;
+ }
+ } else {
/*
* Set up a block mapped area
*/
assert((uint32_t)((ldelta + hdelta) >> PAGE_SHIFT) == ((ldelta + hdelta) >> PAGE_SHIFT));
- pmap_map_block(real_map->pmap,
- (addr64_t)(vaddr - ldelta),
- (ppnum_t)((((vm_map_offset_t)(VME_OBJECT(entry)->vo_shadow_offset)) +
- VME_OFFSET(entry) + (laddr - entry->vme_start) - ldelta) >> PAGE_SHIFT),
- (uint32_t)((ldelta + hdelta) >> PAGE_SHIFT), prot,
- (VM_WIMG_MASK & (int)object->wimg_bits) | superpage, 0);
+ kr = pmap_map_block(real_map->pmap,
+ (addr64_t)(vaddr - ldelta),
+ (ppnum_t)((((vm_map_offset_t)(VME_OBJECT(entry)->vo_shadow_offset)) +
+ VME_OFFSET(entry) + (laddr - entry->vme_start) - ldelta) >> PAGE_SHIFT),
+ (uint32_t)((ldelta + hdelta) >> PAGE_SHIFT), prot,
+ (VM_WIMG_MASK & (int)object->wimg_bits) | superpage, 0);
+
+ if (kr != KERN_SUCCESS) {
+ goto cleanup;
+ }
}
}
}
+ /*
+ * Success
+ */
+ kr = KERN_SUCCESS;
+
+ /*
+ * TODO: could most of the done cases just use cleanup?
+ */
+cleanup:
/*
* Unlock everything, and return
*/
- vm_map_verify_done(map, &version);
+ vm_map_unlock_read(map);
if (real_map != map)
vm_map_unlock(real_map);
if (m != VM_PAGE_NULL) {
+ assert(VM_PAGE_OBJECT(m) == m_object);
+
PAGE_WAKEUP_DONE(m);
- vm_fault_cleanup(m->object, top_page);
+ vm_fault_cleanup(m_object, top_page);
} else
vm_fault_cleanup(object, top_page);
#undef RELEASE_PAGE
- kr = KERN_SUCCESS;
done:
thread_interrupt_level(interruptible_state);
}
}
}
- KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE,
+ KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE,
(MACHDBG_CODE(DBG_MACH_VM, 2)) | DBG_FUNC_END,
- ((uint64_t)vaddr >> 32),
- vaddr,
+ ((uint64_t)trace_vaddr >> 32),
+ trace_vaddr,
kr,
type_of_fault,
0);
vm_map_t map,
vm_map_entry_t entry,
vm_prot_t prot,
+ vm_tag_t wire_tag,
pmap_t pmap,
vm_map_offset_t pmap_addr,
ppnum_t *physpage_p)
{
-
- register vm_map_offset_t va;
- register vm_map_offset_t end_addr = entry->vme_end;
- register kern_return_t rc;
+ vm_map_offset_t va;
+ vm_map_offset_t end_addr = entry->vme_end;
+ kern_return_t rc;
assert(entry->in_transition);
- if ((VME_OBJECT(entry) != NULL) &&
- !entry->is_sub_map &&
+ if ((VME_OBJECT(entry) != NULL) &&
+ !entry->is_sub_map &&
VME_OBJECT(entry)->phys_contiguous) {
return KERN_SUCCESS;
}
* page tables and such can be locked down as well.
*/
- pmap_pageable(pmap, pmap_addr,
+ pmap_pageable(pmap, pmap_addr,
pmap_addr + (end_addr - entry->vme_start), FALSE);
/*
*/
for (va = entry->vme_start; va < end_addr; va += PAGE_SIZE) {
- rc = vm_fault_wire_fast(map, va, prot, entry, pmap,
+ rc = vm_fault_wire_fast(map, va, prot, wire_tag, entry, pmap,
pmap_addr + (va - entry->vme_start),
physpage_p);
if (rc != KERN_SUCCESS) {
- rc = vm_fault_internal(map, va, prot, TRUE,
+ rc = vm_fault_internal(map, va, prot, TRUE, wire_tag,
((pmap == kernel_pmap)
? THREAD_UNINT
- : THREAD_ABORTSAFE),
+ : THREAD_ABORTSAFE),
pmap,
(pmap_addr +
(va - entry->vme_start)),
/* unwire wired pages */
tmp_entry.vme_end = va;
- vm_fault_unwire(map,
+ vm_fault_unwire(map,
&tmp_entry, FALSE, pmap, pmap_addr);
return rc;
pmap_t pmap,
vm_map_offset_t pmap_addr)
{
- register vm_map_offset_t va;
- register vm_map_offset_t end_addr = entry->vme_end;
+ vm_map_offset_t va;
+ vm_map_offset_t end_addr = entry->vme_end;
vm_object_t object;
struct vm_object_fault_info fault_info;
+ unsigned int unwired_pages;
object = (entry->is_sub_map) ? VM_OBJECT_NULL : VME_OBJECT(entry);
fault_info.mark_zf_absent = FALSE;
fault_info.batch_pmap_op = FALSE;
+ unwired_pages = 0;
+
/*
* Since the pages are wired down, we must be able to
* get their mappings from the physical map system.
if (object == VM_OBJECT_NULL) {
if (pmap) {
- pmap_change_wiring(pmap,
+ pmap_change_wiring(pmap,
pmap_addr + (va - entry->vme_start), FALSE);
}
- (void) vm_fault(map, va, VM_PROT_NONE,
- TRUE, THREAD_UNINT, pmap, pmap_addr);
+ (void) vm_fault(map, va, VM_PROT_NONE,
+ TRUE, VM_KERN_MEMORY_NONE, THREAD_UNINT, pmap, pmap_addr);
} else {
vm_prot_t prot;
vm_page_t result_page;
FALSE, /* page not looked up */
&prot, &result_page, &top_page,
(int *)0,
- NULL, map->no_zero_fill,
+ NULL, map->no_zero_fill,
FALSE, &fault_info);
} while (result == VM_FAULT_RETRY);
* move on to the next one in case the remaining pages are mapped from
* different objects. During a forced unmount, the object is terminated
* so the alive flag will be false if this happens. A forced unmount will
- * will occur when an external disk is unplugged before the user does an
+ * will occur when an external disk is unplugged before the user does an
* eject, so we don't want to panic in that situation.
*/
if (result != VM_FAULT_SUCCESS)
panic("vm_fault_unwire: failure");
- result_object = result_page->object;
+ result_object = VM_PAGE_OBJECT(result_page);
if (deallocate) {
- assert(result_page->phys_page !=
+ assert(VM_PAGE_GET_PHYS_PAGE(result_page) !=
vm_page_fictitious_addr);
- pmap_disconnect(result_page->phys_page);
+ pmap_disconnect(VM_PAGE_GET_PHYS_PAGE(result_page));
+ if (VM_PAGE_WIRED(result_page)) {
+ unwired_pages++;
+ }
VM_PAGE_FREE(result_page);
} else {
- if ((pmap) && (result_page->phys_page != vm_page_guard_addr))
- pmap_change_wiring(pmap,
+ if ((pmap) && (VM_PAGE_GET_PHYS_PAGE(result_page) != vm_page_guard_addr))
+ pmap_change_wiring(pmap,
pmap_addr + (va - entry->vme_start), FALSE);
vm_page_lockspin_queues();
vm_page_unwire(result_page, TRUE);
vm_page_unlock_queues();
+ unwired_pages++;
}
if(entry->zero_wired_pages) {
- pmap_zero_page(result_page->phys_page);
+ pmap_zero_page(VM_PAGE_GET_PHYS_PAGE(result_page));
entry->zero_wired_pages = FALSE;
}
* such may be unwired themselves.
*/
- pmap_pageable(pmap, pmap_addr,
+ pmap_pageable(pmap, pmap_addr,
pmap_addr + (end_addr - entry->vme_start), TRUE);
+ if (kernel_object == object) {
+ vm_tag_update_size(fault_info.user_tag, -ptoa_64(unwired_pages));
+ }
}
/*
vm_fault_wire_fast(
__unused vm_map_t map,
vm_map_offset_t va,
- vm_prot_t caller_prot,
+ __unused vm_prot_t caller_prot,
+ vm_tag_t wire_tag,
vm_map_entry_t entry,
pmap_t pmap,
vm_map_offset_t pmap_addr,
{
vm_object_t object;
vm_object_offset_t offset;
- register vm_page_t m;
+ vm_page_t m;
vm_prot_t prot;
thread_t thread = current_thread();
int type_of_fault;
/*
* Look for page in top-level object. If it's not there or
* there's something going on, give up.
- * ENCRYPTED SWAP: use the slow fault path, since we'll need to
- * decrypt the page before wiring it down.
*/
m = vm_page_lookup(object, offset);
- if ((m == VM_PAGE_NULL) || (m->busy) || (m->encrypted) ||
+ if ((m == VM_PAGE_NULL) || (m->busy) ||
(m->unusual && ( m->error || m->restart || m->absent))) {
GIVE_UP;
}
- ASSERT_PAGE_DECRYPTED(m);
-
if (m->fictitious &&
- m->phys_page == vm_page_guard_addr) {
+ VM_PAGE_GET_PHYS_PAGE(m) == vm_page_guard_addr) {
/*
* Guard pages are fictitious pages and are never
* entered into a pmap, so let's say it's been wired...
/*
* Wire the page down now. All bail outs beyond this
- * point must unwire the page.
+ * point must unwire the page.
*/
vm_page_lockspin_queues();
- vm_page_wire(m, VM_PROT_MEMORY_TAG(caller_prot), TRUE);
+ vm_page_wire(m, wire_tag, TRUE);
vm_page_unlock_queues();
/*
pmap_addr,
prot,
prot,
- TRUE,
- FALSE,
- FALSE,
- FALSE,
+ TRUE, /* wired */
+ FALSE, /* change_wiring */
+ wire_tag,
+ FALSE, /* no_cache */
+ FALSE, /* cs_bypass */
VME_ALIAS(entry),
((entry->iokit_acct ||
(!entry->is_sub_map && !entry->use_pmap))
: 0),
NULL,
&type_of_fault);
+ if (kr != KERN_SUCCESS) {
+ RELEASE_PAGE(m);
+ GIVE_UP;
+ }
done:
/*
if (physpage_p) {
/* for vm_map_wire_and_extract() */
if (kr == KERN_SUCCESS) {
- *physpage_p = m->phys_page;
+ assert(object == VM_PAGE_OBJECT(m));
+ *physpage_p = VM_PAGE_GET_PHYS_PAGE(m);
if (prot & VM_PROT_WRITE) {
- vm_object_lock_assert_exclusive(m->object);
+ vm_object_lock_assert_exclusive(object);
m->dirty = TRUE;
}
} else {
vm_page_t page,
vm_page_t top_page)
{
- vm_object_t object = page->object;
+ vm_object_t object = VM_PAGE_OBJECT(page);
vm_object_lock(object);
PAGE_WAKEUP_DONE(page);
- if (!page->active && !page->inactive && !page->throttled) {
+ if ( !VM_PAGE_PAGEABLE(page)) {
vm_page_lockspin_queues();
- if (!page->active && !page->inactive && !page->throttled)
+ if ( !VM_PAGE_PAGEABLE(page)) {
vm_page_activate(page);
+ }
vm_page_unlock_queues();
}
vm_fault_cleanup(object, top_page);
vm_object_t object;
if (page != VM_PAGE_NULL) {
- object = page->object;
+ object = VM_PAGE_OBJECT(page);
vm_object_lock(object);
vm_page_lockspin_queues();
vm_page_unwire(page, TRUE);
vm_page_unlock_queues();
- vm_object_paging_end(object);
+ vm_object_paging_end(object);
vm_object_unlock(object);
}
}
int interruptible)
{
vm_page_t result_page;
-
+
vm_page_t src_page;
vm_page_t src_top_page;
vm_prot_t src_prot;
vm_map_size_t amount_left;
vm_object_t old_copy_object;
+ vm_object_t result_page_object = NULL;
kern_return_t error = 0;
vm_fault_return_t result;
}
assert ((dst_prot & VM_PROT_WRITE) != VM_PROT_NONE);
- old_copy_object = dst_page->object->copy;
+ assert(dst_object == VM_PAGE_OBJECT(dst_page));
+ old_copy_object = dst_object->copy;
/*
* There exists the possiblity that the source and
* same, the call to vm_fault_page() for the
* destination page will deadlock. To prevent this we
* wire the page so we can drop busy without having
- * the page daemon steal the page. We clean up the
+ * the page daemon steal the page. We clean up the
* top page but keep the paging reference on the object
* holding the dest page so it doesn't go away.
*/
vm_page_wire(dst_page, VM_KERN_MEMORY_OSFMK, TRUE);
vm_page_unlock_queues();
PAGE_WAKEUP_DONE(dst_page);
- vm_object_unlock(dst_page->object);
+ vm_object_unlock(dst_object);
if (dst_top_page != VM_PAGE_NULL) {
vm_object_lock(dst_object);
0,0,0,0,0);
result_page = VM_PAGE_NULL;
result = vm_fault_page(
- src_object,
+ src_object,
vm_object_trunc_page(src_offset),
VM_PROT_READ, FALSE,
FALSE, /* page not looked up */
- &src_prot,
+ &src_prot,
&result_page, &src_top_page,
(int *)0, &error, FALSE,
FALSE, &fault_info_src);
"vm_fault_page()\n", result);
}
-
+ result_page_object = VM_PAGE_OBJECT(result_page);
assert((src_top_page == VM_PAGE_NULL) ==
- (result_page->object == src_object));
+ (result_page_object == src_object));
}
assert ((src_prot & VM_PROT_READ) != VM_PROT_NONE);
- vm_object_unlock(result_page->object);
+ vm_object_unlock(result_page_object);
}
+ vm_map_lock_read(dst_map);
+
if (!vm_map_verify(dst_map, dst_version)) {
+ vm_map_unlock_read(dst_map);
if (result_page != VM_PAGE_NULL && src_page != dst_page)
vm_fault_copy_cleanup(result_page, src_top_page);
vm_fault_copy_dst_cleanup(dst_page);
break;
}
+ assert(dst_object == VM_PAGE_OBJECT(dst_page));
- vm_object_lock(dst_page->object);
+ vm_object_lock(dst_object);
- if (dst_page->object->copy != old_copy_object) {
- vm_object_unlock(dst_page->object);
- vm_map_verify_done(dst_map, dst_version);
+ if (dst_object->copy != old_copy_object) {
+ vm_object_unlock(dst_object);
+ vm_map_unlock_read(dst_map);
if (result_page != VM_PAGE_NULL && src_page != dst_page)
vm_fault_copy_cleanup(result_page, src_top_page);
vm_fault_copy_dst_cleanup(dst_page);
break;
}
- vm_object_unlock(dst_page->object);
+ vm_object_unlock(dst_object);
/*
* Copy the page, and note that it is dirty
if(!dst_page->dirty){
vm_object_lock(dst_object);
SET_PAGE_DIRTY(dst_page, TRUE);
- vm_object_unlock(dst_page->object);
+ vm_object_unlock(dst_object);
}
}
if (result_page == VM_PAGE_NULL)
vm_page_zero_fill(dst_page);
else{
- vm_object_lock(result_page->object);
+ vm_object_lock(result_page_object);
vm_page_copy(result_page, dst_page);
- vm_object_unlock(result_page->object);
+ vm_object_unlock(result_page_object);
if(!dst_page->dirty){
vm_object_lock(dst_object);
SET_PAGE_DIRTY(dst_page, TRUE);
- vm_object_unlock(dst_page->object);
+ vm_object_unlock(dst_object);
}
}
* Unlock everything, and return
*/
- vm_map_verify_done(dst_map, dst_version);
+ vm_map_unlock_read(dst_map);
if (result_page != VM_PAGE_NULL && src_page != dst_page)
vm_fault_copy_cleanup(result_page, src_top_page);
RETURN(KERN_SUCCESS);
#undef RETURN
- /*NOTREACHED*/
+ /*NOTREACHED*/
}
#if VM_FAULT_CLASSIFY
while (TRUE) {
m = vm_page_lookup(object, offset);
- if (m != VM_PAGE_NULL) {
+ if (m != VM_PAGE_NULL) {
if (m->busy || m->error || m->restart || m->absent) {
type = VM_FAULT_TYPE_OTHER;
break;
if (((fault_type & VM_PROT_WRITE) == 0) ||
((level == 0) && object->copy == VM_OBJECT_NULL)) {
type = VM_FAULT_TYPE_MAP_IN;
- break;
+ break;
}
type = VM_FAULT_TYPE_COPY;
break;
#endif /* VM_FAULT_CLASSIFY */
vm_offset_t
-kdp_lightweight_fault(vm_map_t map, vm_offset_t cur_target_addr, uint32_t *fault_results)
+kdp_lightweight_fault(vm_map_t map, vm_offset_t cur_target_addr)
{
-#pragma unused(map, cur_target_addr, fault_results)
-
- return 0;
-#if 0
vm_map_entry_t entry;
vm_object_t object;
vm_offset_t object_offset;
int my_fault_type = VM_PROT_READ;
kern_return_t kr;
-
if (not_in_kdp) {
panic("kdp_lightweight_fault called from outside of debugger context");
}
return 0;
}
- if (m->laundry || m->busy || m->pageout || m->absent || m->error || m->cleaning ||
+ if (m->laundry || m->busy || m->free_when_done || m->absent || m->error || m->cleaning ||
m->overwriting || m->restart || m->unusual) {
return 0;
}
return 0;
}
- assert(!m->encrypted);
- if (m->encrypted) {
- return 0;
- }
-
- assert(!m->encrypted_cleaning);
- if (m->encrypted_cleaning) {
+ assert(m->vm_page_q_state != VM_PAGE_USED_BY_COMPRESSOR);
+ if (m->vm_page_q_state == VM_PAGE_USED_BY_COMPRESSOR) {
return 0;
}
- assert(!m->compressor);
- if (m->compressor) {
- return 0;
- }
-
- if (fault_results) {
- *fault_results |= kThreadFaultedBT;
- }
- return ptoa(m->phys_page);
+ return ptoa(VM_PAGE_GET_PHYS_PAGE(m));
}
compressor_external_state = VM_EXTERNAL_STATE_UNKNOWN;
kdp_compressor_decompressed_page_ppnum, &my_fault_type,
compressor_flags, &compressed_count_delta);
if (kr == KERN_SUCCESS) {
- if (fault_results) {
- *fault_results |= kThreadDecompressedBT;
- }
return kdp_compressor_decompressed_page_paddr;
} else {
return 0;
object_offset += object->vo_shadow_offset;
object = object->shadow;
}
-#endif /* 0 */
-}
+}
-#define CODE_SIGNING_CHUNK_SIZE 4096
void
vm_page_validate_cs_mapped(
vm_page_t page,
const void *kaddr)
{
vm_object_t object;
- vm_object_offset_t offset, offset_in_page;
- kern_return_t kr;
+ vm_object_offset_t offset;
memory_object_t pager;
- void *blobs;
+ struct vnode *vnode;
boolean_t validated;
unsigned tainted;
- int num_chunks, num_chunks_validated;
assert(page->busy);
- vm_object_lock_assert_exclusive(page->object);
+ object = VM_PAGE_OBJECT(page);
+ vm_object_lock_assert_exclusive(object);
if (page->wpmapped && !page->cs_tainted) {
/*
printf("CODESIGNING: vm_page_validate_cs: "
"page %p obj %p off 0x%llx "
"was modified\n",
- page, page->object, page->offset);
+ page, object, page->offset);
}
vm_cs_validated_dirtied++;
}
vm_cs_validates++;
- object = page->object;
assert(object->code_signed);
offset = page->offset;
pager = object->pager;
assert(object->paging_in_progress);
- kr = vnode_pager_get_object_cs_blobs(pager, &blobs);
- if (kr != KERN_SUCCESS) {
- blobs = NULL;
- }
+ vnode = vnode_pager_lookup_vnode(pager);
/* verify the SHA1 hash for this page */
- num_chunks_validated = 0;
- for (offset_in_page = 0, num_chunks = 0;
- offset_in_page < PAGE_SIZE_64;
- offset_in_page += CODE_SIGNING_CHUNK_SIZE, num_chunks++) {
- tainted = 0;
- validated = cs_validate_page(blobs,
- pager,
- (object->paging_offset +
- offset +
- offset_in_page),
- (const void *)((const char *)kaddr
- + offset_in_page),
- &tainted);
- if (validated) {
- num_chunks_validated++;
- }
- if (tainted & CS_VALIDATE_TAINTED) {
- page->cs_tainted = TRUE;
- }
- if (tainted & CS_VALIDATE_NX) {
- page->cs_nx = TRUE;
- }
+ tainted = 0;
+ validated = cs_validate_range(vnode,
+ pager,
+ (object->paging_offset +
+ offset),
+ (const void *)((const char *)kaddr),
+ PAGE_SIZE_64,
+ &tainted);
+
+ if (tainted & CS_VALIDATE_TAINTED) {
+ page->cs_tainted = TRUE;
}
- /* page is validated only if all its chunks are */
- if (num_chunks_validated == num_chunks) {
+ if (tainted & CS_VALIDATE_NX) {
+ page->cs_nx = TRUE;
+ }
+
+ if (validated) {
page->cs_validated = TRUE;
}
}
boolean_t busy_page;
boolean_t need_unmap;
- vm_object_lock_assert_held(page->object);
+ object = VM_PAGE_OBJECT(page);
+ vm_object_lock_assert_held(object);
if (page->wpmapped && !page->cs_tainted) {
- vm_object_lock_assert_exclusive(page->object);
+ vm_object_lock_assert_exclusive(object);
/*
* This page was mapped for "write" access sometime in the
printf("CODESIGNING: vm_page_validate_cs: "
"page %p obj %p off 0x%llx "
"was modified\n",
- page, page->object, page->offset);
+ page, object, page->offset);
}
vm_cs_validated_dirtied++;
}
}
assert(!page->slid);
-#if CHECK_CS_VALIDATION_BITMAP
- if ( vnode_pager_cs_check_validation_bitmap( page->object->pager, trunc_page(page->offset + page->object->paging_offset), CS_BITMAP_CHECK ) == KERN_SUCCESS) {
+#if CHECK_CS_VALIDATION_BITMAP
+ if ( vnode_pager_cs_check_validation_bitmap( object->pager, trunc_page(page->offset + object->paging_offset), CS_BITMAP_CHECK ) == KERN_SUCCESS) {
page->cs_validated = TRUE;
page->cs_tainted = FALSE;
vm_cs_bitmap_validated++;
return;
}
#endif
- vm_object_lock_assert_exclusive(page->object);
+ vm_object_lock_assert_exclusive(object);
- object = page->object;
assert(object->code_signed);
offset = page->offset;
/* keep page busy while we map (and unlock) the VM object */
page->busy = TRUE;
}
-
+
/*
* Take a paging reference on the VM object
* to protect it from collapse or bypass,
/* validate the mapped page */
vm_page_validate_cs_mapped(page, (const void *) kaddr);
-#if CHECK_CS_VALIDATION_BITMAP
+#if CHECK_CS_VALIDATION_BITMAP
if ( page->cs_validated == TRUE && page->cs_tainted == FALSE ) {
vnode_pager_cs_check_validation_bitmap( object->pager, trunc_page( offset + object->paging_offset), CS_BITMAP_SET );
}
#endif
assert(page->busy);
- assert(object == page->object);
+ assert(object == VM_PAGE_OBJECT(page));
vm_object_lock_assert_exclusive(object);
if (!busy_page) {
vm_page_t page,
const void *kaddr,
vm_offset_t chunk_offset,
+ vm_size_t chunk_size,
boolean_t *validated_p,
unsigned *tainted_p)
{
vm_object_t object;
vm_object_offset_t offset, offset_in_page;
- kern_return_t kr;
memory_object_t pager;
- void *blobs;
+ struct vnode *vnode;
boolean_t validated;
unsigned tainted;
*tainted_p = 0;
assert(page->busy);
- vm_object_lock_assert_exclusive(page->object);
+ object = VM_PAGE_OBJECT(page);
+ vm_object_lock_assert_exclusive(object);
- object = page->object;
assert(object->code_signed);
offset = page->offset;
pager = object->pager;
assert(object->paging_in_progress);
- kr = vnode_pager_get_object_cs_blobs(pager, &blobs);
- if (kr != KERN_SUCCESS) {
- blobs = NULL;
- }
+ vnode = vnode_pager_lookup_vnode(pager);
/* verify the signature for this chunk */
offset_in_page = chunk_offset;
assert(offset_in_page < PAGE_SIZE);
- assert((offset_in_page & (CODE_SIGNING_CHUNK_SIZE-1)) == 0);
tainted = 0;
- validated = cs_validate_page(blobs,
- pager,
- (object->paging_offset +
- offset +
- offset_in_page),
- (const void *)((const char *)kaddr
+ validated = cs_validate_range(vnode,
+ pager,
+ (object->paging_offset +
+ offset +
+ offset_in_page),
+ (const void *)((const char *)kaddr
+ offset_in_page),
- &tainted);
+ chunk_size,
+ &tainted);
if (validated) {
*validated_p = TRUE;
}