]> git.saurik.com Git - apple/xnu.git/blobdiff - osfmk/vm/vm_user.c
xnu-2782.40.9.tar.gz
[apple/xnu.git] / osfmk / vm / vm_user.c
index 59c26ff7069c1eac5329d456aad46cd87894ba05..024140fb53562e8137fb76b73c08ee3894973366 100644 (file)
 #include <vm/memory_object.h>
 #include <vm/vm_pageout.h>
 #include <vm/vm_protos.h>
+#include <vm/vm_purgeable_internal.h>
 
 vm_size_t        upl_offset_to_pagelist = 0;
 
@@ -164,10 +165,12 @@ mach_vm_allocate(
                 */
                map_addr = vm_map_min(map);
                if (map_addr == 0)
-                       map_addr += PAGE_SIZE;
+                       map_addr += VM_MAP_PAGE_SIZE(map);
        } else
-               map_addr = vm_map_trunc_page(*addr);
-       map_size = vm_map_round_page(size);
+               map_addr = vm_map_trunc_page(*addr,
+                                            VM_MAP_PAGE_MASK(map));
+       map_size = vm_map_round_page(size,
+                                    VM_MAP_PAGE_MASK(map));
        if (map_size == 0) {
          return(KERN_INVALID_ARGUMENT);
        }
@@ -231,10 +234,12 @@ vm_allocate(
                 */
                map_addr = vm_map_min(map);
                if (map_addr == 0)
-                       map_addr += PAGE_SIZE;
+                       map_addr += VM_MAP_PAGE_SIZE(map);
        } else
-               map_addr = vm_map_trunc_page(*addr);
-       map_size = vm_map_round_page(size);
+               map_addr = vm_map_trunc_page(*addr,
+                                            VM_MAP_PAGE_MASK(map));
+       map_size = vm_map_round_page(size,
+                                    VM_MAP_PAGE_MASK(map));
        if (map_size == 0) {
          return(KERN_INVALID_ARGUMENT);
        }
@@ -273,8 +278,12 @@ mach_vm_deallocate(
        if (size == (mach_vm_offset_t) 0)
                return(KERN_SUCCESS);
 
-       return(vm_map_remove(map, vm_map_trunc_page(start),
-                            vm_map_round_page(start+size), VM_MAP_NO_FLAGS));
+       return(vm_map_remove(map,
+                            vm_map_trunc_page(start,
+                                              VM_MAP_PAGE_MASK(map)),
+                            vm_map_round_page(start+size,
+                                              VM_MAP_PAGE_MASK(map)),
+                            VM_MAP_NO_FLAGS));
 }
 
 /*
@@ -295,8 +304,12 @@ vm_deallocate(
        if (size == (vm_offset_t) 0)
                return(KERN_SUCCESS);
 
-       return(vm_map_remove(map, vm_map_trunc_page(start),
-                            vm_map_round_page(start+size), VM_MAP_NO_FLAGS));
+       return(vm_map_remove(map,
+                            vm_map_trunc_page(start,
+                                              VM_MAP_PAGE_MASK(map)),
+                            vm_map_round_page(start+size,
+                                              VM_MAP_PAGE_MASK(map)),
+                            VM_MAP_NO_FLAGS));
 }
 
 /*
@@ -319,8 +332,10 @@ mach_vm_inherit(
                return KERN_SUCCESS;
 
        return(vm_map_inherit(map,
-                             vm_map_trunc_page(start),
-                             vm_map_round_page(start+size),
+                             vm_map_trunc_page(start,
+                                               VM_MAP_PAGE_MASK(map)),
+                             vm_map_round_page(start+size,
+                                               VM_MAP_PAGE_MASK(map)),
                              new_inheritance));
 }
 
@@ -344,8 +359,10 @@ vm_inherit(
                return KERN_SUCCESS;
 
        return(vm_map_inherit(map,
-                             vm_map_trunc_page(start),
-                             vm_map_round_page(start+size),
+                             vm_map_trunc_page(start,
+                                               VM_MAP_PAGE_MASK(map)),
+                             vm_map_round_page(start+size,
+                                               VM_MAP_PAGE_MASK(map)),
                              new_inheritance));
 }
 
@@ -371,8 +388,10 @@ mach_vm_protect(
                return KERN_SUCCESS;
 
        return(vm_map_protect(map,
-                             vm_map_trunc_page(start),
-                             vm_map_round_page(start+size),
+                             vm_map_trunc_page(start,
+                                               VM_MAP_PAGE_MASK(map)),
+                             vm_map_round_page(start+size,
+                                               VM_MAP_PAGE_MASK(map)),
                              new_protection,
                              set_maximum));
 }
@@ -400,8 +419,10 @@ vm_protect(
                return KERN_SUCCESS;
 
        return(vm_map_protect(map,
-                             vm_map_trunc_page(start),
-                             vm_map_round_page(start+size),
+                             vm_map_trunc_page(start,
+                                               VM_MAP_PAGE_MASK(map)),
+                             vm_map_round_page(start+size,
+                                               VM_MAP_PAGE_MASK(map)),
                              new_protection,
                              set_maximum));
 }
@@ -425,11 +446,14 @@ mach_vm_machine_attribute(
        if (size == 0)
                return KERN_SUCCESS;
 
-       return vm_map_machine_attribute(map, 
-                               vm_map_trunc_page(addr),
-                               vm_map_round_page(addr+size),
-                               attribute,
-                               value);
+       return vm_map_machine_attribute(
+               map, 
+               vm_map_trunc_page(addr,
+                                 VM_MAP_PAGE_MASK(map)),
+               vm_map_round_page(addr+size,
+                                 VM_MAP_PAGE_MASK(map)),
+               attribute,
+               value);
 }
 
 /*
@@ -452,11 +476,14 @@ vm_machine_attribute(
        if (size == 0)
                return KERN_SUCCESS;
 
-       return vm_map_machine_attribute(map, 
-                               vm_map_trunc_page(addr),
-                               vm_map_round_page(addr+size),
-                               attribute,
-                               value);
+       return vm_map_machine_attribute(
+               map, 
+               vm_map_trunc_page(addr,
+                                 VM_MAP_PAGE_MASK(map)),
+               vm_map_round_page(addr+size,
+                                 VM_MAP_PAGE_MASK(map)),
+               attribute,
+               value);
 }
 
 /*
@@ -881,12 +908,17 @@ mach_vm_map(
        vm_prot_t               max_protection,
        vm_inherit_t            inheritance)
 {
+       kern_return_t           kr;
+       vm_map_offset_t         vmmaddr;
+
+       vmmaddr = (vm_map_offset_t) *address;
+
        /* filter out any kernel-only flags */
        if (flags & ~VM_FLAGS_USER_MAP)
                return KERN_INVALID_ARGUMENT;
 
-       return vm_map_enter_mem_object(target_map,
-                                      address,
+       kr = vm_map_enter_mem_object(target_map,
+                                      &vmmaddr,
                                       initial_size,
                                       mask,
                                       flags,
@@ -896,6 +928,9 @@ mach_vm_map(
                                       cur_protection,
                                       max_protection,
                                       inheritance);
+
+       *address = vmmaddr;
+       return kr;
 }
 
 
@@ -977,7 +1012,7 @@ mach_vm_remap(
        mach_vm_offset_t        *address,
        mach_vm_size_t  size,
        mach_vm_offset_t        mask,
-       boolean_t               anywhere,
+       int                     flags,
        vm_map_t                src_map,
        mach_vm_offset_t        memory_address,
        boolean_t               copy,
@@ -991,13 +1026,17 @@ mach_vm_remap(
        if (VM_MAP_NULL == target_map || VM_MAP_NULL == src_map)
                return KERN_INVALID_ARGUMENT;
 
+       /* filter out any kernel-only flags */
+       if (flags & ~VM_FLAGS_USER_REMAP)
+               return KERN_INVALID_ARGUMENT;
+
        map_addr = (vm_map_offset_t)*address;
 
        kr = vm_map_remap(target_map,
                          &map_addr,
                          size,
                          mask,
-                         anywhere,
+                         flags,
                          src_map,
                          memory_address,
                          copy,
@@ -1025,7 +1064,7 @@ vm_remap(
        vm_offset_t             *address,
        vm_size_t               size,
        vm_offset_t             mask,
-       boolean_t               anywhere,
+       int                     flags,
        vm_map_t                src_map,
        vm_offset_t             memory_address,
        boolean_t               copy,
@@ -1039,13 +1078,17 @@ vm_remap(
        if (VM_MAP_NULL == target_map || VM_MAP_NULL == src_map)
                return KERN_INVALID_ARGUMENT;
 
+       /* filter out any kernel-only flags */
+       if (flags & ~VM_FLAGS_USER_REMAP)
+               return KERN_INVALID_ARGUMENT;
+
        map_addr = (vm_map_offset_t)*address;
 
        kr = vm_map_remap(target_map,
                          &map_addr,
                          size,
                          mask,
-                         anywhere,
+                         flags,
                          src_map,
                          memory_address,
                          copy,
@@ -1091,11 +1134,20 @@ mach_vm_wire(
                return KERN_INVALID_ARGUMENT;
 
        if (access != VM_PROT_NONE) {
-               rc = vm_map_wire(map, vm_map_trunc_page(start),
-                                vm_map_round_page(start+size), access, TRUE);
+               rc = vm_map_wire(map,
+                                vm_map_trunc_page(start,
+                                                  VM_MAP_PAGE_MASK(map)),
+                                vm_map_round_page(start+size,
+                                                  VM_MAP_PAGE_MASK(map)),
+                                access,
+                                TRUE);
        } else {
-               rc = vm_map_unwire(map, vm_map_trunc_page(start),
-                                  vm_map_round_page(start+size), TRUE);
+               rc = vm_map_unwire(map,
+                                  vm_map_trunc_page(start,
+                                                    VM_MAP_PAGE_MASK(map)),
+                                  vm_map_round_page(start+size,
+                                                    VM_MAP_PAGE_MASK(map)),
+                                  TRUE);
        }
        return rc;
 }
@@ -1132,11 +1184,20 @@ vm_wire(
        if (size == 0) {
                rc = KERN_SUCCESS;
        } else if (access != VM_PROT_NONE) {
-               rc = vm_map_wire(map, vm_map_trunc_page(start),
-                                vm_map_round_page(start+size), access, TRUE);
+               rc = vm_map_wire(map,
+                                vm_map_trunc_page(start,
+                                                  VM_MAP_PAGE_MASK(map)),
+                                vm_map_round_page(start+size,
+                                                  VM_MAP_PAGE_MASK(map)),
+                                access,
+                                TRUE);
        } else {
-               rc = vm_map_unwire(map, vm_map_trunc_page(start),
-                                  vm_map_round_page(start+size), TRUE);
+               rc = vm_map_unwire(map,
+                                  vm_map_trunc_page(start,
+                                                    VM_MAP_PAGE_MASK(map)),
+                                  vm_map_round_page(start+size,
+                                                    VM_MAP_PAGE_MASK(map)),
+                                  TRUE);
        }
        return rc;
 }
@@ -1237,6 +1298,32 @@ vm_msync(
 }
 
 
+int
+vm_toggle_entry_reuse(int toggle, int *old_value)
+{
+       vm_map_t map = current_map();
+       
+       if(toggle == VM_TOGGLE_GETVALUE && old_value != NULL){
+               *old_value = map->disable_vmentry_reuse;
+       } else if(toggle == VM_TOGGLE_SET){
+               vm_map_lock(map);
+               map->disable_vmentry_reuse = TRUE;
+               if (map->first_free == vm_map_to_entry(map)) {
+                       map->highest_entry_end = vm_map_min(map);
+               } else {
+                       map->highest_entry_end = map->first_free->vme_end;
+               }
+               vm_map_unlock(map);
+       } else if (toggle == VM_TOGGLE_CLEAR){
+               vm_map_lock(map);
+               map->disable_vmentry_reuse = FALSE;
+               vm_map_unlock(map);
+       } else
+               return KERN_INVALID_ARGUMENT;
+
+       return KERN_SUCCESS;
+}
+
 /*
  *     mach_vm_behavior_set 
  *
@@ -1259,8 +1346,12 @@ mach_vm_behavior_set(
        if (size == 0)
                return KERN_SUCCESS;
 
-       return(vm_map_behavior_set(map, vm_map_trunc_page(start), 
-                                  vm_map_round_page(start+size), new_behavior));
+       return(vm_map_behavior_set(map,
+                                  vm_map_trunc_page(start,
+                                                    VM_MAP_PAGE_MASK(map)), 
+                                  vm_map_round_page(start+size,
+                                                    VM_MAP_PAGE_MASK(map)),
+                                  new_behavior));
 }
 
 /*
@@ -1289,8 +1380,12 @@ vm_behavior_set(
        if (size == 0)
                return KERN_SUCCESS;
 
-       return(vm_map_behavior_set(map, vm_map_trunc_page(start), 
-                                  vm_map_round_page(start+size), new_behavior));
+       return(vm_map_behavior_set(map,
+                                  vm_map_trunc_page(start,
+                                                    VM_MAP_PAGE_MASK(map)), 
+                                  vm_map_round_page(start+size,
+                                                    VM_MAP_PAGE_MASK(map)),
+                                  new_behavior));
 }
 
 /*
@@ -1567,7 +1662,7 @@ mach_vm_purgable_control(
                return KERN_INVALID_ARGUMENT;
 
        return vm_map_purgable_control(map,
-                                      vm_map_trunc_page(address),
+                                      vm_map_trunc_page(address, PAGE_MASK),
                                       control,
                                       state);
 }
@@ -1583,7 +1678,7 @@ vm_purgable_control(
                return KERN_INVALID_ARGUMENT;
 
        return vm_map_purgable_control(map,
-                                      vm_map_trunc_page(address),
+                                      vm_map_trunc_page(address, PAGE_MASK),
                                       control,
                                       state);
 }
@@ -1648,9 +1743,10 @@ mach_vm_page_query(
        if (VM_MAP_NULL == map)
                return KERN_INVALID_ARGUMENT;
 
-       return vm_map_page_query_internal(map,
-                                         vm_map_trunc_page(offset),
-                                         disposition, ref_count);
+       return vm_map_page_query_internal(
+               map,
+               vm_map_trunc_page(offset, PAGE_MASK),
+               disposition, ref_count);
 }
 
 kern_return_t
@@ -1663,9 +1759,10 @@ vm_map_page_query(
        if (VM_MAP_NULL == map)
                return KERN_INVALID_ARGUMENT;
 
-       return vm_map_page_query_internal(map,
-                                         vm_map_trunc_page(offset),
-                                         disposition, ref_count);
+       return vm_map_page_query_internal(
+               map,
+               vm_map_trunc_page(offset, PAGE_MASK),
+               disposition, ref_count);
 }
 
 kern_return_t
@@ -1794,16 +1891,28 @@ mach_make_memory_entry_64(
        vm_map_offset_t         local_offset;
        vm_object_size_t        mappable_size;
 
+       /* 
+        * Stash the offset in the page for use by vm_map_enter_mem_object()
+        * in the VM_FLAGS_RETURN_DATA_ADDR/MAP_MEM_USE_DATA_ADDR case.
+        */
+       vm_object_offset_t      offset_in_page;
+
        unsigned int            access;
        vm_prot_t               protections;
+       vm_prot_t               original_protections, mask_protections;
        unsigned int            wimg_mode;
-       boolean_t               cache_attr = FALSE;
+
+       boolean_t               force_shadow = FALSE;
+       boolean_t               use_data_addr;
 
        if (((permission & 0x00FF0000) &
             ~(MAP_MEM_ONLY |
               MAP_MEM_NAMED_CREATE |
               MAP_MEM_PURGABLE | 
-              MAP_MEM_NAMED_REUSE))) {
+              MAP_MEM_NAMED_REUSE |
+              MAP_MEM_USE_DATA_ADDR |
+              MAP_MEM_VM_COPY |
+              MAP_MEM_VM_SHARE))) {
                /*
                 * Unknown flag: reject for forward compatibility.
                 */
@@ -1817,28 +1926,37 @@ mach_make_memory_entry_64(
                parent_entry = NULL;
        }
 
-       protections = permission & VM_PROT_ALL;
+       if (parent_entry && parent_entry->is_copy) {
+               return KERN_INVALID_ARGUMENT;
+       }
+
+       original_protections = permission & VM_PROT_ALL;
+       protections = original_protections;
+       mask_protections = permission & VM_PROT_IS_MASK;
        access = GET_MAP_MEM(permission);
+       use_data_addr = ((permission & MAP_MEM_USE_DATA_ADDR) != 0);
 
        user_handle = IP_NULL;
        user_entry = NULL;
 
-       map_offset = vm_map_trunc_page(offset);
-       map_size = vm_map_round_page(*size);
+       map_offset = vm_map_trunc_page(offset, PAGE_MASK);
 
        if (permission & MAP_MEM_ONLY) {
                boolean_t               parent_is_object;
 
-               if (parent_entry == NULL) {
+               map_size = vm_map_round_page(*size, PAGE_MASK);
+               
+               if (use_data_addr || parent_entry == NULL) {
                        return KERN_INVALID_ARGUMENT;
                }
 
-               parent_is_object = !(parent_entry->is_sub_map || parent_entry->is_pager);
+               parent_is_object = !(parent_entry->is_sub_map ||
+                                    parent_entry->is_pager);
                object = parent_entry->backing.object;
                if(parent_is_object && object != VM_OBJECT_NULL)
                        wimg_mode = object->wimg_bits;
                else
-                       wimg_mode = VM_WIMG_DEFAULT;
+                       wimg_mode = VM_WIMG_USE_DEFAULT;
                if((access != GET_MAP_MEM(parent_entry->protection)) &&
                                !(parent_entry->protection & VM_PROT_WRITE)) { 
                        return KERN_INVALID_RIGHT;
@@ -1848,7 +1966,10 @@ mach_make_memory_entry_64(
                   wimg_mode = VM_WIMG_IO;
                } else if (access == MAP_MEM_COPYBACK) {
                   SET_MAP_MEM(access, parent_entry->protection);
-                  wimg_mode = VM_WIMG_DEFAULT;
+                  wimg_mode = VM_WIMG_USE_DEFAULT;
+               } else if (access == MAP_MEM_INNERWBACK) {
+                  SET_MAP_MEM(access, parent_entry->protection);
+                  wimg_mode = VM_WIMG_INNERWBACK;
                } else if (access == MAP_MEM_WTHRU) {
                   SET_MAP_MEM(access, parent_entry->protection);
                   wimg_mode = VM_WIMG_WTHRU;
@@ -1856,37 +1977,26 @@ mach_make_memory_entry_64(
                   SET_MAP_MEM(access, parent_entry->protection);
                   wimg_mode = VM_WIMG_WCOMB;
                }
-               if(parent_is_object && object &&
+               if (parent_is_object && object &&
                        (access != MAP_MEM_NOOP) && 
                        (!(object->nophyscache))) {
-                       if(object->wimg_bits != wimg_mode) {
-                          vm_page_t p;
-                          if ((wimg_mode == VM_WIMG_IO)
-                               || (wimg_mode == VM_WIMG_WCOMB))
-                               cache_attr = TRUE;
-                          else 
-                               cache_attr = FALSE;
-                          vm_object_lock(object);
-                          vm_object_paging_wait(object, THREAD_UNINT);
-                          object->wimg_bits = wimg_mode;
-                          queue_iterate(&object->memq, 
-                                               p, vm_page_t, listq) {
-                               if (!p->fictitious) {
-                                       if (p->pmapped)
-                                               pmap_disconnect(p->phys_page);
-                                       if (cache_attr)
-                                               pmap_sync_page_attributes_phys(p->phys_page);
-                               }
-                          }
-                          vm_object_unlock(object);
+
+                       if (object->wimg_bits != wimg_mode) {
+                               vm_object_lock(object);
+                               vm_object_change_wimg_mode(object, wimg_mode);
+                               vm_object_unlock(object);
                        }
                }
                if (object_handle)
                        *object_handle = IP_NULL;
                return KERN_SUCCESS;
-       }
+       } else if (permission & MAP_MEM_NAMED_CREATE) {
+               map_size = vm_map_round_page(*size, PAGE_MASK);
+
+               if (use_data_addr) {
+                       return KERN_INVALID_ARGUMENT;
+               }
 
-       if(permission & MAP_MEM_NAMED_CREATE) {
                kr = mach_memory_entry_allocate(&user_entry, &user_handle);
                if (kr != KERN_SUCCESS) {
                        return KERN_FAILURE;
@@ -1916,6 +2026,13 @@ mach_make_memory_entry_64(
                                goto make_mem_done;
                        }
                        object->purgable = VM_PURGABLE_NONVOLATILE;
+                       assert(object->vo_purgeable_owner == NULL);
+                       assert(object->resident_page_count == 0);
+                       assert(object->wired_page_count == 0);
+                       vm_object_lock(object);
+                       vm_purgeable_nonvolatile_enqueue(object,
+                                                        current_task());
+                       vm_object_unlock(object);
                }
 
                /*
@@ -1927,7 +2044,9 @@ mach_make_memory_entry_64(
                if (access == MAP_MEM_IO) {
                        wimg_mode = VM_WIMG_IO;
                } else if (access == MAP_MEM_COPYBACK) {
-                       wimg_mode = VM_WIMG_DEFAULT;
+                       wimg_mode = VM_WIMG_USE_DEFAULT;
+               } else if (access == MAP_MEM_INNERWBACK) {
+                       wimg_mode = VM_WIMG_INNERWBACK;
                } else if (access == MAP_MEM_WTHRU) {
                        wimg_mode = VM_WIMG_WTHRU;
                } else if (access == MAP_MEM_WCOMB) {
@@ -1948,12 +2067,14 @@ mach_make_memory_entry_64(
                 * shadow objects either...
                 */
                object->copy_strategy = MEMORY_OBJECT_COPY_NONE;
+               object->true_share = TRUE;
 
                user_entry->backing.object = object;
                user_entry->internal = TRUE;
                user_entry->is_sub_map = FALSE;
                user_entry->is_pager = FALSE;
                user_entry->offset = 0;
+               user_entry->data_offset = 0;
                user_entry->protection = protections;
                SET_MAP_MEM(access, user_entry->protection);
                user_entry->size = map_size;
@@ -1966,9 +2087,135 @@ mach_make_memory_entry_64(
                return KERN_SUCCESS;
        }
 
+       if (permission & MAP_MEM_VM_COPY) {
+               vm_map_copy_t   copy;
+
+               if (target_map == VM_MAP_NULL) {
+                       return KERN_INVALID_TASK;
+               }
+
+               if (use_data_addr) {
+                       map_size = (vm_map_round_page(offset + *size,
+                                                     PAGE_MASK) -
+                                   map_offset);
+                       offset_in_page = offset - map_offset;
+               } else {
+                       map_size = vm_map_round_page(*size, PAGE_MASK);
+                       offset_in_page = 0;
+               }
+
+               kr = vm_map_copyin(target_map,
+                                  map_offset,
+                                  map_size,
+                                  FALSE,
+                                  &copy);
+               if (kr != KERN_SUCCESS) {
+                       return kr;
+               }
+                                  
+               kr = mach_memory_entry_allocate(&user_entry, &user_handle);
+               if (kr != KERN_SUCCESS) {
+                       vm_map_copy_discard(copy);
+                       return KERN_FAILURE;
+               }
+
+               user_entry->backing.copy = copy;
+               user_entry->internal = FALSE;
+               user_entry->is_sub_map = FALSE;
+               user_entry->is_pager = FALSE;
+               user_entry->is_copy = TRUE;
+               user_entry->offset = 0;
+               user_entry->protection = protections;
+               user_entry->size = map_size;
+               user_entry->data_offset = offset_in_page;
+
+               *size = CAST_DOWN(vm_size_t, map_size);
+               *object_handle = user_handle;
+               return KERN_SUCCESS;
+       }
+
+       if (permission & MAP_MEM_VM_SHARE) {
+               vm_map_copy_t   copy;
+               vm_prot_t       cur_prot, max_prot;
+
+               if (target_map == VM_MAP_NULL) {
+                       return KERN_INVALID_TASK;
+               }
+
+               if (use_data_addr) {
+                       map_size = (vm_map_round_page(offset + *size,
+                                                     PAGE_MASK) -
+                                   map_offset);
+                       offset_in_page = offset - map_offset;
+               } else {
+                       map_size = vm_map_round_page(*size, PAGE_MASK);
+                       offset_in_page = 0;
+               }
+
+               kr = vm_map_copy_extract(target_map,
+                                        map_offset,
+                                        map_size,
+                                        &copy,
+                                        &cur_prot,
+                                        &max_prot);
+               if (kr != KERN_SUCCESS) {
+                       return kr;
+               }
+
+               if (mask_protections) {
+                       /*
+                        * We just want as much of "original_protections" 
+                        * as we can get out of the actual "cur_prot".
+                        */
+                       protections &= cur_prot;
+                       if (protections == VM_PROT_NONE) {
+                               /* no access at all: fail */
+                               vm_map_copy_discard(copy);
+                               return KERN_PROTECTION_FAILURE;
+                       }
+               } else {
+                       /*
+                        * We want exactly "original_protections"
+                        * out of "cur_prot".
+                        */
+                       if ((cur_prot & protections) != protections) {
+                               vm_map_copy_discard(copy);
+                               return KERN_PROTECTION_FAILURE;
+                       }
+               }
+
+               kr = mach_memory_entry_allocate(&user_entry, &user_handle);
+               if (kr != KERN_SUCCESS) {
+                       vm_map_copy_discard(copy);
+                       return KERN_FAILURE;
+               }
+
+               user_entry->backing.copy = copy;
+               user_entry->internal = FALSE;
+               user_entry->is_sub_map = FALSE;
+               user_entry->is_pager = FALSE;
+               user_entry->is_copy = TRUE;
+               user_entry->offset = 0;
+               user_entry->protection = protections;
+               user_entry->size = map_size;
+               user_entry->data_offset = offset_in_page;
+
+               *size = CAST_DOWN(vm_size_t, map_size);
+               *object_handle = user_handle;
+               return KERN_SUCCESS;
+       }
+
        if (parent_entry == NULL ||
            (permission & MAP_MEM_NAMED_REUSE)) {
 
+               if (use_data_addr) {
+                       map_size = vm_map_round_page(offset + *size, PAGE_MASK) - map_offset;
+                       offset_in_page = offset - map_offset;
+               } else {
+                       map_size = vm_map_round_page(*size, PAGE_MASK);
+                       offset_in_page = 0;
+               }
+
                /* Create a named object based on address range within the task map */
                /* Go find the object at given address */
 
@@ -1977,6 +2224,7 @@ mach_make_memory_entry_64(
                }
 
 redo_lookup:
+               protections = original_protections;
                vm_map_lock_read(target_map);
 
                /* get the object associated with the target address */
@@ -1984,14 +2232,23 @@ redo_lookup:
                /* that requested by the caller */
 
                kr = vm_map_lookup_locked(&target_map, map_offset, 
-                               protections, OBJECT_LOCK_EXCLUSIVE, &version,
-                               &object, &obj_off, &prot, &wired,
-                               &fault_info,
-                               &real_map);
+                                         protections | mask_protections,
+                                         OBJECT_LOCK_EXCLUSIVE, &version,
+                                         &object, &obj_off, &prot, &wired,
+                                         &fault_info,
+                                         &real_map);
                if (kr != KERN_SUCCESS) {
                        vm_map_unlock_read(target_map);
                        goto make_mem_done;
                }
+               if (mask_protections) {
+                       /*
+                        * The caller asked us to use the "protections" as
+                        * a mask, so restrict "protections" to what this
+                        * mapping actually allows.
+                        */
+                       protections &= prot;
+               }
                if (((prot & protections) != protections) 
                                        || (object == kernel_object)) {
                        kr = KERN_INVALID_RIGHT;
@@ -2077,6 +2334,14 @@ redo_lookup:
                         /* JMM - The check below should be reworked instead. */
                         object->true_share = TRUE;
                      }
+               if (mask_protections) {
+                       /*
+                        * The caller asked us to use the "protections" as
+                        * a mask, so restrict "protections" to what this
+                        * mapping actually allows.
+                        */
+                       protections &= map_entry->max_protection;
+               }
                if(((map_entry->max_protection) & protections) != protections) {
                         kr = KERN_INVALID_RIGHT;
                          vm_object_unlock(object);
@@ -2105,6 +2370,20 @@ redo_lookup:
                                           next_entry->vme_prev->offset + 
                                           (next_entry->vme_prev->vme_end - 
                                           next_entry->vme_prev->vme_start))) {
+                                       if (mask_protections) {
+                                               /*
+                                                * The caller asked us to use
+                                                * the "protections" as a mask,
+                                                * so restrict "protections" to
+                                                * what this mapping actually
+                                                * allows.
+                                                */
+                                               protections &= next_entry->max_protection;
+                                       }
+                                       if ((next_entry->wired_count) &&
+                                           (map_entry->wired_count == 0)) {
+                                               break;
+                                       }
                                        if(((next_entry->max_protection) 
                                                & protections) != protections) {
                                                break;
@@ -2124,16 +2403,57 @@ redo_lookup:
                        }
                }
 
-               if(object->internal) {
+               if (vm_map_entry_should_cow_for_true_share(map_entry) &&
+                   object->vo_size > map_size &&
+                   map_size != 0) {
+                       /*
+                        * Set up the targeted range for copy-on-write to
+                        * limit the impact of "true_share"/"copy_delay" to
+                        * that range instead of the entire VM object...
+                        */
+                       
+                       vm_object_unlock(object);
+                       if (vm_map_lock_read_to_write(target_map)) {
+                               vm_object_deallocate(object);
+                               target_map = original_map;
+                               goto redo_lookup;
+                       }
+
+                       vm_map_clip_start(target_map,
+                                         map_entry,
+                                         vm_map_trunc_page(offset,
+                                                           VM_MAP_PAGE_MASK(target_map)));
+                       vm_map_clip_end(target_map,
+                                       map_entry,
+                                       (vm_map_round_page(offset + map_size,
+                                                          VM_MAP_PAGE_MASK(target_map))));
+                       force_shadow = TRUE;
+
+                       if ((map_entry->vme_end - offset) < map_size) {
+                               map_size = map_entry->vme_end - offset;
+                       }
+                       total_size = map_entry->vme_end - map_entry->vme_start;
+
+                       vm_map_lock_write_to_read(target_map);
+                       vm_object_lock(object);
+               }
+
+               if (object->internal) {
                        /* vm_map_lookup_locked will create a shadow if   */
                        /* needs_copy is set but does not check for the   */
                        /* other two conditions shown. It is important to */ 
                        /* set up an object which will not be pulled from */
                        /* under us.  */
 
-                       if ((map_entry->needs_copy  || object->shadowed ||
-                            (object->size > total_size))
-                                       && !object->true_share) {
+                       if (force_shadow ||
+                           ((map_entry->needs_copy  ||
+                             object->shadowed ||
+                             (object->vo_size > total_size &&
+                              (map_entry->offset != 0 ||
+                               object->vo_size >
+                               vm_map_round_page(total_size,
+                                                 VM_MAP_PAGE_MASK(target_map)))))
+                            && !object->true_share)) {
                                /*
                                 * We have to unlock the VM object before
                                 * trying to upgrade the VM map lock, to
@@ -2158,7 +2478,9 @@ redo_lookup:
                                        target_map = original_map;
                                        goto redo_lookup;
                                }
+#if 00
                                vm_object_lock(object);
+#endif
 
                                /* 
                                 * JMM - We need to avoid coming here when the object
@@ -2169,9 +2491,11 @@ redo_lookup:
                                 
                                /* create a shadow object */
                                vm_object_shadow(&map_entry->object.vm_object,
-                                               &map_entry->offset, total_size);
+                                                &map_entry->offset, total_size);
                                shadow_object = map_entry->object.vm_object;
+#if 00
                                vm_object_unlock(object);
+#endif
 
                                prot = map_entry->protection & ~VM_PROT_WRITE;
 
@@ -2182,7 +2506,7 @@ redo_lookup:
                                        object, map_entry->offset,
                                        total_size,
                                        ((map_entry->is_shared 
-                                               || target_map->mapped)
+                                         || target_map->mapped_in_other_pmaps)
                                                        ? PMAP_NULL :
                                                        target_map->pmap),
                                        map_entry->vme_start,
@@ -2194,6 +2518,9 @@ redo_lookup:
 
                                vm_object_lock(shadow_object);
                                while (total_size) {
+                                   assert((next_entry->wired_count == 0) ||
+                                          (map_entry->wired_count));
+
                                   if(next_entry->object.vm_object == object) {
                                        vm_object_reference_locked(shadow_object);
                                        next_entry->object.vm_object 
@@ -2245,6 +2572,8 @@ redo_lookup:
                                wimg_mode = VM_WIMG_IO;
                        } else if (access == MAP_MEM_COPYBACK) {
                                wimg_mode = VM_WIMG_USE_DEFAULT;
+                       } else if (access == MAP_MEM_INNERWBACK) {
+                               wimg_mode = VM_WIMG_INNERWBACK;
                        } else if (access == MAP_MEM_WTHRU) {
                                wimg_mode = VM_WIMG_WTHRU;
                        } else if (access == MAP_MEM_WCOMB) {
@@ -2252,6 +2581,22 @@ redo_lookup:
                        }
                }
 
+#if VM_OBJECT_TRACKING_OP_TRUESHARE
+               if (!object->true_share &&
+                   vm_object_tracking_inited) {
+                       void *bt[VM_OBJECT_TRACKING_BTDEPTH];
+                       int num = 0;
+
+                       num = OSBacktrace(bt,
+                                         VM_OBJECT_TRACKING_BTDEPTH);
+                       btlog_add_entry(vm_object_tracking_btlog,
+                                       object,
+                                       VM_OBJECT_TRACKING_OP_TRUESHARE,
+                                       bt,
+                                       num);
+               }
+#endif /* VM_OBJECT_TRACKING_OP_TRUESHARE */
+
                object->true_share = TRUE;
                if (object->copy_strategy == MEMORY_OBJECT_COPY_SYMMETRIC)
                        object->copy_strategy = MEMORY_OBJECT_COPY_DELAY;
@@ -2267,28 +2612,8 @@ redo_lookup:
                if(real_map != target_map)
                        vm_map_unlock_read(real_map);
 
-               if(object->wimg_bits != wimg_mode) {
-                       vm_page_t p;
-
-                       vm_object_paging_wait(object, THREAD_UNINT);
-
-                       if ((wimg_mode == VM_WIMG_IO)
-                           || (wimg_mode == VM_WIMG_WCOMB))
-                               cache_attr = TRUE;
-                       else 
-                               cache_attr = FALSE;
-
-                       queue_iterate(&object->memq, 
-                                               p, vm_page_t, listq) {
-                               if (!p->fictitious) {
-                                       if (p->pmapped)
-                                               pmap_disconnect(p->phys_page);
-                                       if (cache_attr)
-                                               pmap_sync_page_attributes_phys(p->phys_page);
-                               }
-                       }
-                       object->wimg_bits = wimg_mode;
-               }
+               if (object->wimg_bits != wimg_mode)
+                       vm_object_change_wimg_mode(object, wimg_mode);
 
                /* the size of mapped entry that overlaps with our region */
                /* which is targeted for share.                           */
@@ -2312,7 +2637,9 @@ redo_lookup:
                            parent_entry->is_pager == FALSE &&
                            parent_entry->offset == obj_off &&
                            parent_entry->protection == protections &&
-                           parent_entry->size == map_size) {
+                           parent_entry->size == map_size &&
+                           ((!use_data_addr && (parent_entry->data_offset == 0)) ||  
+                            (use_data_addr && (parent_entry->data_offset == offset_in_page)))) {
                                /*
                                 * We have a match: re-use "parent_entry".
                                 */
@@ -2322,6 +2649,8 @@ redo_lookup:
                                /* parent_entry->ref_count++; XXX ? */
                                /* Get an extra send-right on handle */
                                ipc_port_copy_send(parent_handle);
+
+                               *size = CAST_DOWN(vm_size_t, map_size);
                                *object_handle = parent_handle;
                                return KERN_SUCCESS;
                        } else {
@@ -2345,7 +2674,9 @@ redo_lookup:
                user_entry->is_sub_map = FALSE;
                user_entry->is_pager = FALSE;
                user_entry->offset = obj_off;
-               user_entry->protection = permission;
+               user_entry->data_offset = offset_in_page;
+               user_entry->protection = protections;
+               SET_MAP_MEM(GET_MAP_MEM(permission), user_entry->protection);
                user_entry->size = map_size;
 
                /* user_object pager and internal fields are not used */
@@ -2357,16 +2688,49 @@ redo_lookup:
 
        } else {
                /* The new object will be base on an existing named object */
-
                if (parent_entry == NULL) {
                        kr = KERN_INVALID_ARGUMENT;
                        goto make_mem_done;
                }
-               if((offset + map_size) > parent_entry->size) {
-                       kr = KERN_INVALID_ARGUMENT;
-                       goto make_mem_done;
+
+               if (use_data_addr) {
+                       /*
+                        * submaps and pagers should only be accessible from within
+                        * the kernel, which shouldn't use the data address flag, so can fail here.
+                        */
+                       if (parent_entry->is_pager || parent_entry->is_sub_map) {
+                               panic("Shouldn't be using data address with a parent entry that is a submap or pager.");
+                       }
+                       /*
+                        * Account for offset to data in parent entry and
+                        * compute our own offset to data.
+                        */
+                       if((offset + *size + parent_entry->data_offset) > parent_entry->size) {
+                               kr = KERN_INVALID_ARGUMENT;
+                               goto make_mem_done;
+                       }
+
+                       map_offset = vm_map_trunc_page(offset + parent_entry->data_offset, PAGE_MASK);
+                       offset_in_page = (offset + parent_entry->data_offset) - map_offset;
+                       map_size = vm_map_round_page(offset + parent_entry->data_offset + *size, PAGE_MASK) - map_offset;
+               } else {
+                       map_size = vm_map_round_page(*size, PAGE_MASK);
+                       offset_in_page = 0;
+
+                       if((offset + map_size) > parent_entry->size) {
+                               kr = KERN_INVALID_ARGUMENT;
+                               goto make_mem_done;
+                       }
                }
 
+               if (mask_protections) {
+                       /*
+                        * The caller asked us to use the "protections" as
+                        * a mask, so restrict "protections" to what this
+                        * mapping actually allows.
+                        */
+                       protections &= parent_entry->protection;
+               }
                if((protections & parent_entry->protection) != protections) {
                        kr = KERN_PROTECTION_FAILURE;
                        goto make_mem_done;
@@ -2380,8 +2744,10 @@ redo_lookup:
 
                user_entry->size = map_size;
                user_entry->offset = parent_entry->offset + map_offset;
+               user_entry->data_offset = offset_in_page; 
                user_entry->is_sub_map = parent_entry->is_sub_map;
                user_entry->is_pager = parent_entry->is_pager;
+               user_entry->is_copy = parent_entry->is_copy;
                user_entry->internal = parent_entry->internal;
                user_entry->protection = protections;
 
@@ -2405,6 +2771,22 @@ redo_lookup:
                   /* we now point to this object, hold on */
                   vm_object_reference(object); 
                   vm_object_lock(object);
+#if VM_OBJECT_TRACKING_OP_TRUESHARE
+               if (!object->true_share &&
+                   vm_object_tracking_inited) {
+                       void *bt[VM_OBJECT_TRACKING_BTDEPTH];
+                       int num = 0;
+
+                       num = OSBacktrace(bt,
+                                         VM_OBJECT_TRACKING_BTDEPTH);
+                       btlog_add_entry(vm_object_tracking_btlog,
+                                       object,
+                                       VM_OBJECT_TRACKING_OP_TRUESHARE,
+                                       bt,
+                                       num);
+               }
+#endif /* VM_OBJECT_TRACKING_OP_TRUESHARE */
+
                   object->true_share = TRUE;
                   if (object->copy_strategy == MEMORY_OBJECT_COPY_SYMMETRIC)
                        object->copy_strategy = MEMORY_OBJECT_COPY_DELAY;
@@ -2532,9 +2914,11 @@ mach_memory_entry_allocate(
        user_entry->backing.pager = NULL;
        user_entry->is_sub_map = FALSE;
        user_entry->is_pager = FALSE;
+       user_entry->is_copy = FALSE;
        user_entry->internal = FALSE;
        user_entry->size = 0;
        user_entry->offset = 0;
+       user_entry->data_offset = 0;
        user_entry->protection = VM_PROT_NONE;
        user_entry->ref_count = 1;
 
@@ -2632,7 +3016,9 @@ mach_memory_entry_purgable_control(
 
        named_entry_lock(mem_entry);
 
-       if (mem_entry->is_sub_map || mem_entry->is_pager) {
+       if (mem_entry->is_sub_map ||
+           mem_entry->is_pager ||
+           mem_entry->is_copy) {
                named_entry_unlock(mem_entry);
                return KERN_INVALID_ARGUMENT;
        }
@@ -2646,7 +3032,7 @@ mach_memory_entry_purgable_control(
        vm_object_lock(object);
 
        /* check that named entry covers entire object ? */
-       if (mem_entry->offset != 0 || object->size != mem_entry->size) {
+       if (mem_entry->offset != 0 || object->vo_size != mem_entry->size) {
                vm_object_unlock(object);
                named_entry_unlock(mem_entry);
                return KERN_INVALID_ARGUMENT;
@@ -2661,6 +3047,54 @@ mach_memory_entry_purgable_control(
        return kr;
 }
 
+kern_return_t
+mach_memory_entry_get_page_counts(
+       ipc_port_t      entry_port,
+       unsigned int    *resident_page_count,
+       unsigned int    *dirty_page_count)
+{
+       kern_return_t           kr;
+       vm_named_entry_t        mem_entry;
+       vm_object_t             object;
+       vm_object_offset_t      offset;
+       vm_object_size_t        size;
+
+       if (entry_port == IP_NULL ||
+           ip_kotype(entry_port) != IKOT_NAMED_ENTRY) {
+               return KERN_INVALID_ARGUMENT;
+       }
+
+       mem_entry = (vm_named_entry_t) entry_port->ip_kobject;
+
+       named_entry_lock(mem_entry);
+
+       if (mem_entry->is_sub_map ||
+           mem_entry->is_pager ||
+           mem_entry->is_copy) {
+               named_entry_unlock(mem_entry);
+               return KERN_INVALID_ARGUMENT;
+       }
+
+       object = mem_entry->backing.object;
+       if (object == VM_OBJECT_NULL) {
+               named_entry_unlock(mem_entry);
+               return KERN_INVALID_ARGUMENT;
+       }
+
+       vm_object_lock(object);
+
+       offset = mem_entry->offset;
+       size = mem_entry->size;
+
+       named_entry_unlock(mem_entry);
+
+       kr = vm_object_get_page_counts(object, offset, size, resident_page_count, dirty_page_count);
+
+       vm_object_unlock(object);
+
+       return kr;
+}
+
 /*
  * mach_memory_entry_port_release:
  *
@@ -2697,22 +3131,29 @@ mach_destroy_memory_entry(
        assert(ip_kotype(port) == IKOT_NAMED_ENTRY);
 #endif /* MACH_ASSERT */
        named_entry = (vm_named_entry_t)port->ip_kobject;
-       lck_mtx_lock(&(named_entry)->Lock);
+
+       named_entry_lock(named_entry);
        named_entry->ref_count -= 1;
+
        if(named_entry->ref_count == 0) {
                if (named_entry->is_sub_map) {
                        vm_map_deallocate(named_entry->backing.map);
-               } else if (!named_entry->is_pager) { 
-                       /* release the memory object we've been pointing to */
+               } else if (named_entry->is_pager) {
+                       /* JMM - need to drop reference on pager in that case */
+               } else if (named_entry->is_copy) {
+                       vm_map_copy_discard(named_entry->backing.copy);
+               } else {
+                       /* release the VM object we've been pointing to */
                        vm_object_deallocate(named_entry->backing.object);
-               } /* else JMM - need to drop reference on pager in that case */
+               }
 
-               lck_mtx_unlock(&(named_entry)->Lock);
+               named_entry_unlock(named_entry);
+               named_entry_lock_destroy(named_entry);
 
                kfree((void *) port->ip_kobject,
                      sizeof (struct vm_named_entry));
        } else
-               lck_mtx_unlock(&(named_entry)->Lock);
+               named_entry_unlock(named_entry);
 }
 
 /* Allow manipulation of individual page state.  This is actually part of */
@@ -2739,7 +3180,9 @@ mach_memory_entry_page_op(
 
        named_entry_lock(mem_entry);
 
-       if (mem_entry->is_sub_map || mem_entry->is_pager) {
+       if (mem_entry->is_sub_map ||
+           mem_entry->is_pager ||
+           mem_entry->is_copy) {
                named_entry_unlock(mem_entry);
                return KERN_INVALID_ARGUMENT;
        }
@@ -2791,7 +3234,9 @@ mach_memory_entry_range_op(
 
        named_entry_lock(mem_entry);
 
-       if (mem_entry->is_sub_map || mem_entry->is_pager) {
+       if (mem_entry->is_sub_map ||
+           mem_entry->is_pager ||
+           mem_entry->is_copy) {
                named_entry_unlock(mem_entry);
                return KERN_INVALID_ARGUMENT;
        }
@@ -2997,7 +3442,10 @@ vm_region_object_create(
        /* Create a named object based on a submap of specified size */
 
        new_map = vm_map_create(PMAP_NULL, VM_MAP_MIN_ADDRESS,
-                               vm_map_round_page(size), TRUE);
+                               vm_map_round_page(size,
+                                                 VM_MAP_PAGE_MASK(target_map)),
+                               TRUE);
+       vm_map_set_page_shift(new_map, VM_MAP_PAGE_SHIFT(target_map));
 
        user_entry->backing.map = new_map;
        user_entry->internal = TRUE;
@@ -3027,7 +3475,7 @@ vm_map_get_phys_page(
        vm_map_entry_t          entry;
        ppnum_t                 phys_page = 0;
 
-       map_offset = vm_map_trunc_page(addr);
+       map_offset = vm_map_trunc_page(addr, PAGE_MASK);
 
        vm_map_lock(map);
        while (vm_map_lookup_entry(map, map_offset, &entry)) {
@@ -3050,7 +3498,7 @@ vm_map_get_phys_page(
                        /* If they are not present in the object they will  */
                        /* have to be picked up from the pager through the  */
                        /* fault mechanism.  */
-                       if(entry->object.vm_object->shadow_offset == 0) {
+                       if(entry->object.vm_object->vo_shadow_offset == 0) {
                                /* need to call vm_fault */
                                vm_map_unlock(map);
                                vm_fault(map, map_offset, VM_PROT_NONE, 
@@ -3060,8 +3508,8 @@ vm_map_get_phys_page(
                        }
                        offset = entry->offset + (map_offset - entry->vme_start);
                        phys_page = (ppnum_t)
-                               ((entry->object.vm_object->shadow_offset 
-                                                       + offset) >> 12);
+                               ((entry->object.vm_object->vo_shadow_offset 
+                                                       + offset) >> PAGE_SHIFT);
                        break;
                        
                }
@@ -3075,7 +3523,7 @@ vm_map_get_phys_page(
                                        vm_object_t old_object;
                                        vm_object_lock(object->shadow);
                                        old_object = object;
-                                       offset = offset + object->shadow_offset;
+                                       offset = offset + object->vo_shadow_offset;
                                        object = object->shadow;
                                        vm_object_unlock(old_object);
                                } else {
@@ -3159,8 +3607,9 @@ kernel_object_iopl_request(
        /* offset from beginning of named entry offset in object */
        offset = offset + named_entry->offset;
 
-       if(named_entry->is_sub_map) 
-               return (KERN_INVALID_ARGUMENT);
+       if (named_entry->is_sub_map ||
+           named_entry->is_copy)
+               return KERN_INVALID_ARGUMENT;
                
        named_entry_lock(named_entry);
 
@@ -3208,8 +3657,8 @@ kernel_object_iopl_request(
        }
 
        if (!object->private) {
-               if (*upl_size > (MAX_UPL_TRANSFER*PAGE_SIZE))
-                       *upl_size = (MAX_UPL_TRANSFER*PAGE_SIZE);
+               if (*upl_size > MAX_UPL_TRANSFER_BYTES)
+                       *upl_size = MAX_UPL_TRANSFER_BYTES;
                if (object->phys_contiguous) {
                        *flags = UPL_PHYS_CONTIG;
                } else {