+ return KERN_FAILURE;
+}
+#endif /* VM_CPM */
+
+/*
+ * Clip and unnest a portion of a nested submap mapping.
+ */
+static void
+vm_map_clip_unnest(
+ vm_map_t map,
+ vm_map_entry_t entry,
+ vm_map_offset_t start_unnest,
+ vm_map_offset_t end_unnest)
+{
+ assert(entry->is_sub_map);
+ assert(entry->object.sub_map != NULL);
+
+ if (entry->vme_start > start_unnest ||
+ entry->vme_end < end_unnest) {
+ panic("vm_map_clip_unnest(0x%llx,0x%llx): "
+ "bad nested entry: start=0x%llx end=0x%llx\n",
+ (long long)start_unnest, (long long)end_unnest,
+ (long long)entry->vme_start, (long long)entry->vme_end);
+ }
+ if (start_unnest > entry->vme_start) {
+ _vm_map_clip_start(&map->hdr,
+ entry,
+ start_unnest);
+ UPDATE_FIRST_FREE(map, map->first_free);
+ }
+ if (entry->vme_end > end_unnest) {
+ _vm_map_clip_end(&map->hdr,
+ entry,
+ end_unnest);
+ UPDATE_FIRST_FREE(map, map->first_free);
+ }
+
+ pmap_unnest(map->pmap,
+ entry->vme_start,
+ entry->vme_end - entry->vme_start);
+ if ((map->mapped) && (map->ref_count)) {
+ /* clean up parent map/maps */
+ vm_map_submap_pmap_clean(
+ map, entry->vme_start,
+ entry->vme_end,
+ entry->object.sub_map,
+ entry->offset);
+ }
+ entry->use_pmap = FALSE;
+}
+
+/*
+ * vm_map_clip_start: [ internal use only ]
+ *
+ * Asserts that the given entry begins at or after
+ * the specified address; if necessary,
+ * it splits the entry into two.
+ */
+static void
+vm_map_clip_start(
+ vm_map_t map,
+ vm_map_entry_t entry,
+ vm_map_offset_t startaddr)
+{
+#ifndef NO_NESTED_PMAP
+ if (entry->use_pmap &&
+ startaddr >= entry->vme_start) {
+ vm_map_offset_t start_unnest, end_unnest;
+
+ /*
+ * Make sure "startaddr" is no longer in a nested range
+ * before we clip. Unnest only the minimum range the platform
+ * can handle.
+ */
+ start_unnest = startaddr & ~(pmap_nesting_size_min - 1);
+ end_unnest = start_unnest + pmap_nesting_size_min;
+ vm_map_clip_unnest(map, entry, start_unnest, end_unnest);
+ }
+#endif /* NO_NESTED_PMAP */
+ if (startaddr > entry->vme_start) {
+ if (entry->object.vm_object &&
+ !entry->is_sub_map &&
+ entry->object.vm_object->phys_contiguous) {
+ pmap_remove(map->pmap,
+ (addr64_t)(entry->vme_start),
+ (addr64_t)(entry->vme_end));
+ }
+ _vm_map_clip_start(&map->hdr, entry, startaddr);
+ UPDATE_FIRST_FREE(map, map->first_free);
+ }
+}
+
+
+#define vm_map_copy_clip_start(copy, entry, startaddr) \
+ MACRO_BEGIN \
+ if ((startaddr) > (entry)->vme_start) \
+ _vm_map_clip_start(&(copy)->cpy_hdr,(entry),(startaddr)); \
+ MACRO_END
+
+/*
+ * This routine is called only when it is known that
+ * the entry must be split.
+ */
+static void
+_vm_map_clip_start(
+ register struct vm_map_header *map_header,
+ register vm_map_entry_t entry,
+ register vm_map_offset_t start)
+{
+ register vm_map_entry_t new_entry;
+
+ /*
+ * Split off the front portion --
+ * note that we must insert the new
+ * entry BEFORE this one, so that
+ * this entry has the specified starting
+ * address.
+ */
+
+ new_entry = _vm_map_entry_create(map_header);
+ vm_map_entry_copy_full(new_entry, entry);
+
+ new_entry->vme_end = start;
+ entry->offset += (start - entry->vme_start);
+ entry->vme_start = start;
+
+ _vm_map_entry_link(map_header, entry->vme_prev, new_entry);
+
+ if (entry->is_sub_map)
+ vm_map_reference(new_entry->object.sub_map);
+ else
+ vm_object_reference(new_entry->object.vm_object);
+}
+
+
+/*
+ * vm_map_clip_end: [ internal use only ]
+ *
+ * Asserts that the given entry ends at or before
+ * the specified address; if necessary,
+ * it splits the entry into two.
+ */
+static void
+vm_map_clip_end(
+ vm_map_t map,
+ vm_map_entry_t entry,
+ vm_map_offset_t endaddr)
+{
+ if (endaddr > entry->vme_end) {
+ /*
+ * Within the scope of this clipping, limit "endaddr" to
+ * the end of this map entry...
+ */
+ endaddr = entry->vme_end;
+ }
+#ifndef NO_NESTED_PMAP
+ if (entry->use_pmap) {
+ vm_map_offset_t start_unnest, end_unnest;
+
+ /*
+ * Make sure the range between the start of this entry and
+ * the new "endaddr" is no longer nested before we clip.
+ * Unnest only the minimum range the platform can handle.
+ */
+ start_unnest = entry->vme_start;
+ end_unnest =
+ (endaddr + pmap_nesting_size_min - 1) &
+ ~(pmap_nesting_size_min - 1);
+ vm_map_clip_unnest(map, entry, start_unnest, end_unnest);
+ }
+#endif /* NO_NESTED_PMAP */
+ if (endaddr < entry->vme_end) {
+ if (entry->object.vm_object &&
+ !entry->is_sub_map &&
+ entry->object.vm_object->phys_contiguous) {
+ pmap_remove(map->pmap,
+ (addr64_t)(entry->vme_start),
+ (addr64_t)(entry->vme_end));
+ }
+ _vm_map_clip_end(&map->hdr, entry, endaddr);
+ UPDATE_FIRST_FREE(map, map->first_free);
+ }
+}
+
+
+#define vm_map_copy_clip_end(copy, entry, endaddr) \
+ MACRO_BEGIN \
+ if ((endaddr) < (entry)->vme_end) \
+ _vm_map_clip_end(&(copy)->cpy_hdr,(entry),(endaddr)); \
+ MACRO_END
+
+/*
+ * This routine is called only when it is known that
+ * the entry must be split.
+ */
+static void
+_vm_map_clip_end(
+ register struct vm_map_header *map_header,
+ register vm_map_entry_t entry,
+ register vm_map_offset_t end)
+{
+ register vm_map_entry_t new_entry;
+
+ /*
+ * Create a new entry and insert it
+ * AFTER the specified entry
+ */
+
+ new_entry = _vm_map_entry_create(map_header);
+ vm_map_entry_copy_full(new_entry, entry);
+
+ new_entry->vme_start = entry->vme_end = end;
+ new_entry->offset += (end - entry->vme_start);
+
+ _vm_map_entry_link(map_header, entry, new_entry);
+
+ if (entry->is_sub_map)
+ vm_map_reference(new_entry->object.sub_map);
+ else
+ vm_object_reference(new_entry->object.vm_object);
+}
+
+
+/*
+ * VM_MAP_RANGE_CHECK: [ internal use only ]
+ *
+ * Asserts that the starting and ending region
+ * addresses fall within the valid range of the map.
+ */
+#define VM_MAP_RANGE_CHECK(map, start, end) \
+ MACRO_BEGIN \
+ if (start < vm_map_min(map)) \
+ start = vm_map_min(map); \
+ if (end > vm_map_max(map)) \
+ end = vm_map_max(map); \
+ if (start > end) \
+ start = end; \
+ MACRO_END
+
+/*
+ * vm_map_range_check: [ internal use only ]
+ *
+ * Check that the region defined by the specified start and
+ * end addresses are wholly contained within a single map
+ * entry or set of adjacent map entries of the spacified map,
+ * i.e. the specified region contains no unmapped space.
+ * If any or all of the region is unmapped, FALSE is returned.
+ * Otherwise, TRUE is returned and if the output argument 'entry'
+ * is not NULL it points to the map entry containing the start
+ * of the region.
+ *
+ * The map is locked for reading on entry and is left locked.
+ */
+static boolean_t
+vm_map_range_check(
+ register vm_map_t map,
+ register vm_map_offset_t start,
+ register vm_map_offset_t end,
+ vm_map_entry_t *entry)
+{
+ vm_map_entry_t cur;
+ register vm_map_offset_t prev;
+
+ /*
+ * Basic sanity checks first
+ */
+ if (start < vm_map_min(map) || end > vm_map_max(map) || start > end)
+ return (FALSE);
+
+ /*
+ * Check first if the region starts within a valid
+ * mapping for the map.
+ */
+ if (!vm_map_lookup_entry(map, start, &cur))
+ return (FALSE);
+
+ /*
+ * Optimize for the case that the region is contained
+ * in a single map entry.
+ */
+ if (entry != (vm_map_entry_t *) NULL)
+ *entry = cur;
+ if (end <= cur->vme_end)
+ return (TRUE);
+
+ /*
+ * If the region is not wholly contained within a
+ * single entry, walk the entries looking for holes.
+ */
+ prev = cur->vme_end;
+ cur = cur->vme_next;
+ while ((cur != vm_map_to_entry(map)) && (prev == cur->vme_start)) {
+ if (end <= cur->vme_end)
+ return (TRUE);
+ prev = cur->vme_end;
+ cur = cur->vme_next;
+ }
+ return (FALSE);
+}
+
+/*
+ * vm_map_submap: [ kernel use only ]
+ *
+ * Mark the given range as handled by a subordinate map.
+ *
+ * This range must have been created with vm_map_find using
+ * the vm_submap_object, and no other operations may have been
+ * performed on this range prior to calling vm_map_submap.
+ *
+ * Only a limited number of operations can be performed
+ * within this rage after calling vm_map_submap:
+ * vm_fault
+ * [Don't try vm_map_copyin!]
+ *
+ * To remove a submapping, one must first remove the
+ * range from the superior map, and then destroy the
+ * submap (if desired). [Better yet, don't try it.]
+ */
+kern_return_t
+vm_map_submap(
+ vm_map_t map,
+ vm_map_offset_t start,
+ vm_map_offset_t end,
+ vm_map_t submap,
+ vm_map_offset_t offset,
+#ifdef NO_NESTED_PMAP
+ __unused
+#endif /* NO_NESTED_PMAP */
+ boolean_t use_pmap)
+{
+ vm_map_entry_t entry;
+ register kern_return_t result = KERN_INVALID_ARGUMENT;
+ register vm_object_t object;
+
+ vm_map_lock(map);
+
+ if (! vm_map_lookup_entry(map, start, &entry)) {
+ entry = entry->vme_next;
+ }
+
+ if (entry == vm_map_to_entry(map) ||
+ entry->is_sub_map) {
+ vm_map_unlock(map);
+ return KERN_INVALID_ARGUMENT;
+ }
+
+ assert(!entry->use_pmap); /* we don't want to unnest anything here */
+ vm_map_clip_start(map, entry, start);
+ vm_map_clip_end(map, entry, end);
+
+ if ((entry->vme_start == start) && (entry->vme_end == end) &&
+ (!entry->is_sub_map) &&
+ ((object = entry->object.vm_object) == vm_submap_object) &&
+ (object->resident_page_count == 0) &&
+ (object->copy == VM_OBJECT_NULL) &&
+ (object->shadow == VM_OBJECT_NULL) &&
+ (!object->pager_created)) {
+ entry->offset = (vm_object_offset_t)offset;
+ entry->object.vm_object = VM_OBJECT_NULL;
+ vm_object_deallocate(object);
+ entry->is_sub_map = TRUE;
+ entry->object.sub_map = submap;
+ vm_map_reference(submap);
+ submap->mapped = TRUE;
+
+#ifndef NO_NESTED_PMAP
+ if (use_pmap) {
+ /* nest if platform code will allow */
+ if(submap->pmap == NULL) {
+ submap->pmap = pmap_create((vm_map_size_t) 0, FALSE);
+ if(submap->pmap == PMAP_NULL) {
+ vm_map_unlock(map);
+ return(KERN_NO_SPACE);
+ }
+ }
+ result = pmap_nest(map->pmap,
+ (entry->object.sub_map)->pmap,
+ (addr64_t)start,
+ (addr64_t)start,
+ (uint64_t)(end - start));
+ if(result)
+ panic("vm_map_submap: pmap_nest failed, rc = %08X\n", result);
+ entry->use_pmap = TRUE;
+ }
+#else /* NO_NESTED_PMAP */
+ pmap_remove(map->pmap, (addr64_t)start, (addr64_t)end);
+#endif /* NO_NESTED_PMAP */
+ result = KERN_SUCCESS;
+ }
+ vm_map_unlock(map);
+
+ return(result);
+}
+
+/*
+ * vm_map_protect:
+ *
+ * Sets the protection of the specified address
+ * region in the target map. If "set_max" is
+ * specified, the maximum protection is to be set;
+ * otherwise, only the current protection is affected.
+ */
+kern_return_t
+vm_map_protect(
+ register vm_map_t map,
+ register vm_map_offset_t start,
+ register vm_map_offset_t end,
+ register vm_prot_t new_prot,
+ register boolean_t set_max)
+{
+ register vm_map_entry_t current;
+ register vm_map_offset_t prev;
+ vm_map_entry_t entry;
+ vm_prot_t new_max;
+
+ XPR(XPR_VM_MAP,
+ "vm_map_protect, 0x%X start 0x%X end 0x%X, new 0x%X %d",
+ (integer_t)map, start, end, new_prot, set_max);
+
+ vm_map_lock(map);
+
+ if ((new_prot & VM_PROT_COPY) && !map->prot_copy_allow) {
+ vm_map_unlock(map);
+ return(KERN_PROTECTION_FAILURE);
+ }
+
+ /* LP64todo - remove this check when vm_map_commpage64()
+ * no longer has to stuff in a map_entry for the commpage
+ * above the map's max_offset.
+ */
+ if (start >= map->max_offset) {
+ vm_map_unlock(map);
+ return(KERN_INVALID_ADDRESS);
+ }
+
+ /*
+ * Lookup the entry. If it doesn't start in a valid
+ * entry, return an error.
+ */
+ if (! vm_map_lookup_entry(map, start, &entry)) {
+ vm_map_unlock(map);
+ return(KERN_INVALID_ADDRESS);
+ }
+
+ /*
+ * Make a first pass to check for protection and address
+ * violations.
+ */
+
+ current = entry;
+ prev = current->vme_start;
+ while ((current != vm_map_to_entry(map)) &&
+ (current->vme_start < end)) {
+
+ /*
+ * If there is a hole, return an error.
+ */
+ if (current->vme_start != prev) {
+ vm_map_unlock(map);
+ return(KERN_INVALID_ADDRESS);
+ }
+
+ new_max = current->max_protection;
+ if(new_prot & VM_PROT_COPY) {
+ new_max |= VM_PROT_WRITE;
+ if ((new_prot & (new_max | VM_PROT_COPY)) != new_prot) {
+ vm_map_unlock(map);
+ return(KERN_PROTECTION_FAILURE);
+ }
+ } else {
+ if ((new_prot & new_max) != new_prot) {
+ vm_map_unlock(map);
+ return(KERN_PROTECTION_FAILURE);
+ }
+ }
+
+#if CONFIG_EMBEDDED
+ if (new_prot & VM_PROT_WRITE) {
+ if (new_prot & VM_PROT_EXECUTE) {
+ printf("EMBEDDED: %s can't have both write and exec at the same time\n", __FUNCTION__);
+ new_prot &= ~VM_PROT_EXECUTE;
+ }
+ }
+#endif
+
+ prev = current->vme_end;
+ current = current->vme_next;
+ }
+ if (end > prev) {
+ vm_map_unlock(map);
+ return(KERN_INVALID_ADDRESS);
+ }
+
+ /*
+ * Go back and fix up protections.
+ * Clip to start here if the range starts within
+ * the entry.
+ */
+
+ current = entry;
+ if (current != vm_map_to_entry(map)) {
+ /* clip and unnest if necessary */
+ vm_map_clip_start(map, current, start);
+ }
+
+ while ((current != vm_map_to_entry(map)) &&
+ (current->vme_start < end)) {
+
+ vm_prot_t old_prot;
+
+ vm_map_clip_end(map, current, end);
+
+ assert(!current->use_pmap); /* clipping did unnest if needed */
+
+ old_prot = current->protection;
+
+ if(new_prot & VM_PROT_COPY) {
+ /* caller is asking specifically to copy the */
+ /* mapped data, this implies that max protection */
+ /* will include write. Caller must be prepared */
+ /* for loss of shared memory communication in the */
+ /* target area after taking this step */
+ current->needs_copy = TRUE;
+ current->max_protection |= VM_PROT_WRITE;
+ }
+
+ if (set_max)
+ current->protection =
+ (current->max_protection =
+ new_prot & ~VM_PROT_COPY) &
+ old_prot;
+ else
+ current->protection = new_prot & ~VM_PROT_COPY;
+
+ /*
+ * Update physical map if necessary.
+ * If the request is to turn off write protection,
+ * we won't do it for real (in pmap). This is because
+ * it would cause copy-on-write to fail. We've already
+ * set, the new protection in the map, so if a
+ * write-protect fault occurred, it will be fixed up
+ * properly, COW or not.
+ */
+ if (current->protection != old_prot) {
+ /* Look one level in we support nested pmaps */
+ /* from mapped submaps which are direct entries */
+ /* in our map */
+
+ vm_prot_t prot;
+
+ prot = current->protection & ~VM_PROT_WRITE;
+
+ if (override_nx(map, current->alias) && prot)
+ prot |= VM_PROT_EXECUTE;
+
+ if (current->is_sub_map && current->use_pmap) {
+ pmap_protect(current->object.sub_map->pmap,
+ current->vme_start,
+ current->vme_end,
+ prot);
+ } else {
+ pmap_protect(map->pmap,
+ current->vme_start,
+ current->vme_end,
+ prot);
+ }
+ }
+ current = current->vme_next;
+ }
+
+ current = entry;
+ while ((current != vm_map_to_entry(map)) &&
+ (current->vme_start <= end)) {
+ vm_map_simplify_entry(map, current);
+ current = current->vme_next;
+ }
+
+ vm_map_unlock(map);
+ return(KERN_SUCCESS);
+}
+
+/*
+ * vm_map_inherit:
+ *
+ * Sets the inheritance of the specified address
+ * range in the target map. Inheritance
+ * affects how the map will be shared with
+ * child maps at the time of vm_map_fork.
+ */
+kern_return_t
+vm_map_inherit(
+ register vm_map_t map,
+ register vm_map_offset_t start,
+ register vm_map_offset_t end,
+ register vm_inherit_t new_inheritance)
+{
+ register vm_map_entry_t entry;
+ vm_map_entry_t temp_entry;