+/*
+ * TEMPORARYTEMPORARYTEMPORARYTEMPORARYTEMPORARYTEMPORARY
+ * Goes away after regular vm_region_recurse function migrates to
+ * 64 bits
+ * vm_region_recurse: A form of vm_region which follows the
+ * submaps in a target map
+ *
+ */
+
+kern_return_t
+vm_map_region_recurse_64(
+ vm_map_t map,
+ vm_map_offset_t *address, /* IN/OUT */
+ vm_map_size_t *size, /* OUT */
+ natural_t *nesting_depth, /* IN/OUT */
+ vm_region_submap_info_64_t submap_info, /* IN/OUT */
+ mach_msg_type_number_t *count) /* IN/OUT */
+{
+ vm_region_extended_info_data_t extended;
+ vm_map_entry_t tmp_entry;
+ vm_map_offset_t user_address;
+ unsigned int user_max_depth;
+
+ /*
+ * "curr_entry" is the VM map entry preceding or including the
+ * address we're looking for.
+ * "curr_map" is the map or sub-map containing "curr_entry".
+ * "curr_offset" is the cumulated offset of "curr_map" in the
+ * target task's address space.
+ * "curr_depth" is the depth of "curr_map" in the chain of
+ * sub-maps.
+ * "curr_max_offset" is the maximum offset we should take into
+ * account in the current map. It may be smaller than the current
+ * map's "max_offset" because we might not have mapped it all in
+ * the upper level map.
+ */
+ vm_map_entry_t curr_entry;
+ vm_map_offset_t curr_offset;
+ vm_map_t curr_map;
+ unsigned int curr_depth;
+ vm_map_offset_t curr_max_offset;
+
+ /*
+ * "next_" is the same as "curr_" but for the VM region immediately
+ * after the address we're looking for. We need to keep track of this
+ * too because we want to return info about that region if the
+ * address we're looking for is not mapped.
+ */
+ vm_map_entry_t next_entry;
+ vm_map_offset_t next_offset;
+ vm_map_t next_map;
+ unsigned int next_depth;
+ vm_map_offset_t next_max_offset;
+
+ if (map == VM_MAP_NULL) {
+ /* no address space to work on */
+ return KERN_INVALID_ARGUMENT;
+ }
+
+ if (*count < VM_REGION_SUBMAP_INFO_COUNT_64) {
+ /* "info" structure is not big enough and would overflow */
+ return KERN_INVALID_ARGUMENT;
+ }
+
+ *count = VM_REGION_SUBMAP_INFO_COUNT_64;
+
+ user_address = *address;
+ user_max_depth = *nesting_depth;
+
+ curr_entry = NULL;
+ curr_map = map;
+ curr_offset = 0;
+ curr_depth = 0;
+ curr_max_offset = curr_map->max_offset;
+
+ next_entry = NULL;
+ next_map = NULL;
+ next_offset = 0;
+ next_depth = 0;
+ next_max_offset = curr_max_offset;
+
+ if (not_in_kdp) {
+ vm_map_lock_read(curr_map);
+ }
+
+ for (;;) {
+ if (vm_map_lookup_entry(curr_map,
+ user_address - curr_offset,
+ &tmp_entry)) {
+ /* tmp_entry contains the address we're looking for */
+ curr_entry = tmp_entry;
+ } else {
+ /*
+ * The address is not mapped. "tmp_entry" is the
+ * map entry preceding the address. We want the next
+ * one, if it exists.
+ */
+ curr_entry = tmp_entry->vme_next;
+ if (curr_entry == vm_map_to_entry(curr_map) ||
+ curr_entry->vme_start >= curr_max_offset) {
+ /* no next entry at this level: stop looking */
+ if (not_in_kdp) {
+ vm_map_unlock_read(curr_map);
+ }
+ curr_entry = NULL;
+ curr_map = NULL;
+ curr_offset = 0;
+ curr_depth = 0;
+ curr_max_offset = 0;
+ break;
+ }
+ }
+
+ /*
+ * Is the next entry at this level closer to the address (or
+ * deeper in the submap chain) than the one we had
+ * so far ?
+ */
+ tmp_entry = curr_entry->vme_next;
+ if (tmp_entry == vm_map_to_entry(curr_map)) {
+ /* no next entry at this level */
+ } else if (tmp_entry->vme_start >= curr_max_offset) {
+ /*
+ * tmp_entry is beyond the scope of what we mapped of
+ * this submap in the upper level: ignore it.
+ */
+ } else if ((next_entry == NULL) ||
+ (tmp_entry->vme_start + curr_offset <=
+ next_entry->vme_start + next_offset)) {
+ /*
+ * We didn't have a "next_entry" or this one is
+ * closer to the address we're looking for:
+ * use this "tmp_entry" as the new "next_entry".
+ */
+ if (next_entry != NULL) {
+ /* unlock the last "next_map" */
+ if (next_map != curr_map && not_in_kdp) {
+ vm_map_unlock_read(next_map);
+ }
+ }
+ next_entry = tmp_entry;
+ next_map = curr_map;
+ next_offset = curr_offset;
+ next_depth = curr_depth;
+ next_max_offset = curr_max_offset;
+ }
+
+ if (!curr_entry->is_sub_map ||
+ curr_depth >= user_max_depth) {
+ /*
+ * We hit a leaf map or we reached the maximum depth
+ * we could, so stop looking. Keep the current map
+ * locked.
+ */
+ break;
+ }
+
+ /*
+ * Get down to the next submap level.
+ */
+
+ /*
+ * Lock the next level and unlock the current level,
+ * unless we need to keep it locked to access the "next_entry"
+ * later.
+ */
+ if (not_in_kdp) {
+ vm_map_lock_read(curr_entry->object.sub_map);
+ }
+ if (curr_map == next_map) {
+ /* keep "next_map" locked in case we need it */
+ } else {
+ /* release this map */
+ vm_map_unlock_read(curr_map);
+ }
+
+ /*
+ * Adjust the offset. "curr_entry" maps the submap
+ * at relative address "curr_entry->vme_start" in the
+ * curr_map but skips the first "curr_entry->offset"
+ * bytes of the submap.
+ * "curr_offset" always represents the offset of a virtual
+ * address in the curr_map relative to the absolute address
+ * space (i.e. the top-level VM map).
+ */
+ curr_offset +=
+ (curr_entry->vme_start - curr_entry->offset);
+ /* switch to the submap */
+ curr_map = curr_entry->object.sub_map;
+ curr_depth++;
+ /*
+ * "curr_max_offset" allows us to keep track of the
+ * portion of the submap that is actually mapped at this level:
+ * the rest of that submap is irrelevant to us, since it's not
+ * mapped here.
+ * The relevant portion of the map starts at
+ * "curr_entry->offset" up to the size of "curr_entry".
+ */
+ curr_max_offset =
+ curr_entry->vme_end - curr_entry->vme_start +
+ curr_entry->offset;
+ curr_entry = NULL;
+ }
+
+ if (curr_entry == NULL) {
+ /* no VM region contains the address... */
+ if (next_entry == NULL) {
+ /* ... and no VM region follows it either */
+ return KERN_INVALID_ADDRESS;
+ }
+ /* ... gather info about the next VM region */
+ curr_entry = next_entry;
+ curr_map = next_map; /* still locked ... */
+ curr_offset = next_offset;
+ curr_depth = next_depth;
+ curr_max_offset = next_max_offset;
+ } else {
+ /* we won't need "next_entry" after all */
+ if (next_entry != NULL) {
+ /* release "next_map" */
+ if (next_map != curr_map && not_in_kdp) {
+ vm_map_unlock_read(next_map);
+ }
+ }
+ }
+ next_entry = NULL;
+ next_map = NULL;
+ next_offset = 0;
+ next_depth = 0;
+ next_max_offset = 0;
+
+ *nesting_depth = curr_depth;
+ *size = curr_entry->vme_end - curr_entry->vme_start;
+ *address = curr_entry->vme_start + curr_offset;
+
+ submap_info->user_tag = curr_entry->alias;
+ submap_info->offset = curr_entry->offset;
+ submap_info->protection = curr_entry->protection;
+ submap_info->inheritance = curr_entry->inheritance;
+ submap_info->max_protection = curr_entry->max_protection;
+ submap_info->behavior = curr_entry->behavior;
+ submap_info->user_wired_count = curr_entry->user_wired_count;
+ submap_info->is_submap = curr_entry->is_sub_map;
+ submap_info->object_id = (uint32_t) curr_entry->object.vm_object;
+
+ extended.pages_resident = 0;
+ extended.pages_swapped_out = 0;
+ extended.pages_shared_now_private = 0;
+ extended.pages_dirtied = 0;
+ extended.external_pager = 0;
+ extended.shadow_depth = 0;
+
+ if (not_in_kdp) {
+ if (!curr_entry->is_sub_map) {
+ vm_map_region_walk(curr_map,
+ curr_entry->vme_start,
+ curr_entry,
+ curr_entry->offset,
+ (curr_entry->vme_end -
+ curr_entry->vme_start),
+ &extended);
+ submap_info->share_mode = extended.share_mode;
+ if (extended.external_pager &&
+ extended.ref_count == 2 &&
+ extended.share_mode == SM_SHARED) {
+ submap_info->share_mode = SM_PRIVATE;
+ }
+ submap_info->ref_count = extended.ref_count;
+ } else {
+ if (curr_entry->use_pmap) {
+ submap_info->share_mode = SM_TRUESHARED;
+ } else {
+ submap_info->share_mode = SM_PRIVATE;
+ }
+ submap_info->ref_count =
+ curr_entry->object.sub_map->ref_count;
+ }
+ }
+
+ submap_info->pages_resident = extended.pages_resident;
+ submap_info->pages_swapped_out = extended.pages_swapped_out;
+ submap_info->pages_shared_now_private =
+ extended.pages_shared_now_private;
+ submap_info->pages_dirtied = extended.pages_dirtied;
+ submap_info->external_pager = extended.external_pager;
+ submap_info->shadow_depth = extended.shadow_depth;
+
+ if (not_in_kdp) {
+ vm_map_unlock_read(curr_map);
+ }
+
+ return KERN_SUCCESS;
+}
+