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_internal.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 = (vm_page_is_fictitious(iter->pageq_pos) != 0);
153 break;
154 }
155
156 return phys_page;
157 }
158
159 #if XNU_VM_HAS_LINEAR_PAGES_ARRAY
160
161 /**
162 * Attempts to resolve the canonical VM page for the current position of a page list iter
163 *
164 * @note The behavior of this function is undefined if the iterator is already at or
165 * beyond the end of the page list.
166 *
167 * @param iter The iterator from which to extract the current page.
168 *
169 * @return The canonical vm_page_t for the current iterator position or
170 * VM_PAGE_NULL (if the page isn't managed and is part of an UPL array).
171 */
172 __attribute__((always_inline))
173 vm_page_t
unified_page_list_iterator_vm_page(const unified_page_list_iterator_t * iter)174 unified_page_list_iterator_vm_page(
175 const unified_page_list_iterator_t *iter)
176 {
177 vm_page_t page = VM_PAGE_NULL;
178 ppnum_t phys_page;
179
180 switch (iter->list->type) {
181 case UNIFIED_PAGE_LIST_TYPE_UPL_ARRAY:
182 phys_page = iter->list->upl.upl_info[iter->upl_index].phys_addr;
183 page = vm_page_find_canonical(phys_page);
184 break;
185 case UNIFIED_PAGE_LIST_TYPE_VM_PAGE_LIST:
186 case UNIFIED_PAGE_LIST_TYPE_VM_PAGE_OBJ_Q:
187 case UNIFIED_PAGE_LIST_TYPE_VM_PAGE_FIFO_Q:
188 page = iter->pageq_pos;
189 break;
190 }
191 return page;
192 }
193
194 #endif /* XNU_VM_HAS_LINEAR_PAGES_ARRAY */
195