xref: /xnu-11215.41.3/osfmk/vm/vm_pmap.c (revision 33de042d024d46de5ff4e89f2471de6608e37fa4)
1 /*
2  * Copyright (c) 2024 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 
29 #include <vm/pmap.h>
30 #include <vm/vm_page.h>
31 
32 /**
33  * Initialize a unified page list iterator object from a source list of pages.
34  * The created iterator will point to the beginning of the list.
35  *
36  * @note Where applicable, we expect the calling VM code to hold locks that prevent
37  *       the underlying page lists from being concurrently changed from underneath
38  *       the page list and page list iterator.  In particular, for
39  *       UNIFIED_PAGE_LIST_TYPE_VM_PAGE_OBJ_Q this means holding the VM object lock,
40  *       and for UNIFIED_PAGE_LIST_TYPE_VM_PAGE_FIFO_Q this means holding any
41  *       relevant global page queue lock.
42  *
43  * @param page_list The list of pages to serve as the starting point of the iterator.
44  * @param iter Output parameter to store the initialized iterator.
45  */
46 __attribute__((always_inline))
47 void
unified_page_list_iterator_init(const unified_page_list_t * page_list,unified_page_list_iterator_t * iter)48 unified_page_list_iterator_init(
49 	const unified_page_list_t *page_list,
50 	unified_page_list_iterator_t *iter)
51 {
52 	switch (page_list->type) {
53 	case UNIFIED_PAGE_LIST_TYPE_UPL_ARRAY:
54 		iter->upl_index = 0;
55 		break;
56 	case UNIFIED_PAGE_LIST_TYPE_VM_PAGE_LIST:
57 		iter->pageq_pos = page_list->page_slist;
58 		break;
59 	case UNIFIED_PAGE_LIST_TYPE_VM_PAGE_OBJ_Q:
60 	case UNIFIED_PAGE_LIST_TYPE_VM_PAGE_FIFO_Q:
61 		iter->pageq_pos = (vm_page_t)vm_page_queue_first((vm_page_queue_head_t*)page_list->pageq);
62 		break;
63 	default:
64 		panic("%s: Unrecognized page list type %hhu", __func__, page_list->type);
65 	}
66 	iter->list = page_list;
67 }
68 
69 /**
70  * Move to the next element within a page list iterator.
71  *
72  * @note unified_page_list_iterator_end() should be used to avoid iterating
73  *       past the end of the list.
74  *
75  * @param iter The iterator to advance.
76  */
77 __attribute__((always_inline))
78 void
unified_page_list_iterator_next(unified_page_list_iterator_t * iter)79 unified_page_list_iterator_next(unified_page_list_iterator_t *iter)
80 {
81 	switch (iter->list->type) {
82 	case UNIFIED_PAGE_LIST_TYPE_UPL_ARRAY:
83 		iter->upl_index++;
84 		break;
85 	case UNIFIED_PAGE_LIST_TYPE_VM_PAGE_LIST:
86 		iter->pageq_pos = NEXT_PAGE(iter->pageq_pos);
87 		break;
88 	case UNIFIED_PAGE_LIST_TYPE_VM_PAGE_OBJ_Q:
89 		iter->pageq_pos = (vm_page_t)vm_page_queue_next(&iter->pageq_pos->vmp_listq);
90 		break;
91 	case UNIFIED_PAGE_LIST_TYPE_VM_PAGE_FIFO_Q:
92 		iter->pageq_pos = (vm_page_t)vm_page_queue_next(&iter->pageq_pos->vmp_pageq);
93 		break;
94 	}
95 }
96 
97 /**
98  * Determine whether the current position of a page list iterator is at the
99  * end of the list.
100  *
101  * @note The behavior of this function is undefined if the caller has already advanced
102  *       the iterator beyond the end of the list.
103  *
104  * @param iter The iterator to check.
105  *
106  * @return True if the iterator has reached the end of the list, false otherwise.
107  */
108 __attribute__((always_inline))
109 bool
unified_page_list_iterator_end(const unified_page_list_iterator_t * iter)110 unified_page_list_iterator_end(const unified_page_list_iterator_t *iter)
111 {
112 	switch (iter->list->type) {
113 	case UNIFIED_PAGE_LIST_TYPE_UPL_ARRAY:
114 		return iter->upl_index >= iter->list->upl.upl_size;
115 	case UNIFIED_PAGE_LIST_TYPE_VM_PAGE_LIST:
116 		return iter->pageq_pos == NULL;
117 	case UNIFIED_PAGE_LIST_TYPE_VM_PAGE_OBJ_Q:
118 	case UNIFIED_PAGE_LIST_TYPE_VM_PAGE_FIFO_Q:
119 		return vm_page_queue_end((vm_page_queue_head_t*)iter->list->pageq, (vm_page_queue_entry_t)iter->pageq_pos);
120 	}
121 }
122 
123 /**
124  * Extract the physical page number from the current position of a page list iter.
125  *
126  * @note The behavior of this function is undefined if the iterator is already at or
127  *       beyond the end of the page list.
128  *
129  * @param iter The iterator from which to extract the current page.
130  * @param is_fictitious Output parameter indicating whether the current iterator position
131  *        represents a fictitious page.  Useful for pmap functions that are meant to only
132  *        operate on real physical pages.
133  *
134  * @return The physical page number of the current iterator position.
135  */
136 __attribute__((always_inline))
137 ppnum_t
unified_page_list_iterator_page(const unified_page_list_iterator_t * iter,bool * is_fictitious)138 unified_page_list_iterator_page(
139 	const unified_page_list_iterator_t *iter,
140 	bool *is_fictitious)
141 {
142 	ppnum_t phys_page;
143 	switch (iter->list->type) {
144 	case UNIFIED_PAGE_LIST_TYPE_UPL_ARRAY:
145 		phys_page = iter->list->upl.upl_info[iter->upl_index].phys_addr;
146 		*is_fictitious = false;
147 		break;
148 	case UNIFIED_PAGE_LIST_TYPE_VM_PAGE_LIST:
149 	case UNIFIED_PAGE_LIST_TYPE_VM_PAGE_OBJ_Q:
150 	case UNIFIED_PAGE_LIST_TYPE_VM_PAGE_FIFO_Q:
151 		phys_page = VM_PAGE_GET_PHYS_PAGE(iter->pageq_pos);
152 		*is_fictitious = (iter->pageq_pos->vmp_fictitious != 0);
153 		break;
154 	}
155 
156 	return phys_page;
157 }
158