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 /*
30 * vm/configurator_mincore.c
31 *
32 * Test mincore with many different VM states.
33 */
34
35 #include <sys/types.h>
36 #include <sys/mman.h>
37 #include <stdlib.h>
38
39 #include "configurator/vm_configurator_tests.h"
40
41 T_GLOBAL_META(
42 T_META_NAMESPACE("xnu.vm.configurator"),
43 T_META_RADAR_COMPONENT_NAME("xnu"),
44 T_META_RADAR_COMPONENT_VERSION("VM"),
45 T_META_RUN_CONCURRENTLY(true),
46 T_META_ASROOT(true), /* required for vm submap sysctls */
47 T_META_ALL_VALID_ARCHS(true)
48 );
49
50 /*
51 * This implementation can model any successful call to mincore.
52 */
53 static test_result_t
successful_mincore_nested(checker_list_t * checker_list,mach_vm_address_t start,mach_vm_size_t size)54 successful_mincore_nested(
55 checker_list_t *checker_list,
56 mach_vm_address_t start,
57 mach_vm_size_t size)
58 {
59 /* mincore returns one byte per page of address range */
60 assert(size % PAGE_SIZE == 0);
61 mach_vm_size_t page_count = size / PAGE_SIZE;
62 uint8_t *page_infos = calloc(size / PAGE_SIZE, 1);
63
64 /* No checker updates. mincore has no VM side effects. */
65 int err = mincore((void *)start, size, (char *)page_infos);
66 assert(err == 0);
67
68 /* Verify that mincore's result matches the checker's expectation. */
69 for (mach_vm_size_t page_index = 0;
70 page_index < page_count;
71 page_index++) {
72 mach_vm_address_t page_address = start + page_index * PAGE_SIZE;
73 uint8_t page_info = page_infos[page_index];
74 vm_entry_checker_t *checker =
75 checker_list_find_checker(checker_list, page_address);
76
77 /* descend into submaps */
78 if (checker != NULL && checker->kind == Submap) {
79 checker_list_t *submap_checkers DEFER_UNSLIDE =
80 checker_get_and_slide_submap_checkers(checker);
81 test_result_t result = successful_mincore_nested(submap_checkers, page_address, PAGE_SIZE);
82 if (result != TestSucceeded) {
83 return result;
84 }
85 continue;
86 }
87
88 /* mappedness */
89 if (checker == NULL) {
90 /* fixme mincore sets MINCORE_ANONYMOUS in unallocated space? */
91 T_QUIET; T_EXPECT_EQ((page_info & ~MINCORE_ANONYMOUS), 0,
92 "empty space should have zero mincore state");
93 continue;
94 }
95
96 /* resident */
97 bool mincore_resident = (page_info & MINCORE_INCORE);
98 /* TODO this assumes writes affect entire entries */
99 bool checker_resident = (checker->pages_resident > 0);
100 if (mincore_resident != checker_resident) {
101 T_LOG("page residency mismatch, address 0x%llx: expected %s, "
102 "mincore reported %s (0x%02hhx & MINCORE_INCORE)",
103 page_address, name_for_bool(checker_resident),
104 name_for_bool(mincore_resident), page_info);
105
106 entry_checker_range_t range = { .head = checker, .tail = checker };
107 T_LOG("*** mincore expected ***");
108 dump_checker_range(range);
109 T_LOG("*** actual ***");
110 dump_region_info_for_entries(range);
111
112 free(page_infos);
113 return TestFailed;
114 }
115 }
116
117 free(page_infos);
118 return TestSucceeded;
119 }
120
121 static test_result_t
successful_mincore(checker_list_t * checker_list,mach_vm_address_t start,mach_vm_size_t size)122 successful_mincore(
123 checker_list_t *checker_list,
124 mach_vm_address_t start,
125 mach_vm_size_t size)
126 {
127 test_result_t result = successful_mincore_nested(checker_list, start, size);
128 if (result != TestSucceeded) {
129 return result;
130 }
131 return verify_vm_state(checker_list, "after mincore");
132 }
133
134
135 T_DECL(mincore,
136 "run mincore with various vm configurations")
137 {
138 vm_tests_t tests = {
139 .single_entry_1 = successful_mincore,
140 .single_entry_2 = successful_mincore,
141 .single_entry_3 = successful_mincore,
142 .single_entry_4 = successful_mincore,
143
144 .multiple_entries_1 = successful_mincore,
145 .multiple_entries_2 = successful_mincore,
146 .multiple_entries_3 = successful_mincore,
147 .multiple_entries_4 = successful_mincore,
148 .multiple_entries_5 = successful_mincore,
149 .multiple_entries_6 = successful_mincore,
150
151 .some_holes_1 = successful_mincore,
152 .some_holes_2 = successful_mincore,
153 .some_holes_3 = successful_mincore,
154 .some_holes_4 = successful_mincore,
155 .some_holes_5 = successful_mincore,
156 .some_holes_6 = successful_mincore,
157 .some_holes_7 = successful_mincore,
158 .some_holes_8 = successful_mincore,
159 .some_holes_9 = successful_mincore,
160 .some_holes_10 = successful_mincore,
161 .some_holes_11 = successful_mincore,
162 .some_holes_12 = successful_mincore,
163
164 .all_holes_1 = successful_mincore,
165 .all_holes_2 = successful_mincore,
166 .all_holes_3 = successful_mincore,
167 .all_holes_4 = successful_mincore,
168
169 .null_entry = successful_mincore,
170 .nonresident_entry = successful_mincore,
171 .resident_entry = successful_mincore,
172
173 .shared_entry = successful_mincore,
174 .shared_entry_discontiguous = successful_mincore,
175 .shared_entry_partial = successful_mincore,
176 .shared_entry_pairs = successful_mincore,
177 .shared_entry_x1000 = successful_mincore,
178
179 .cow_entry = successful_mincore,
180 .cow_unreferenced = successful_mincore,
181 .cow_nocow = successful_mincore,
182 .nocow_cow = successful_mincore,
183 .cow_unreadable = successful_mincore,
184 .cow_unwriteable = successful_mincore,
185
186 .permanent_entry = successful_mincore,
187 .permanent_before_permanent = successful_mincore,
188 .permanent_before_allocation = successful_mincore,
189 .permanent_before_allocation_2 = successful_mincore,
190 .permanent_before_hole = successful_mincore,
191 .permanent_after_allocation = successful_mincore,
192 .permanent_after_hole = successful_mincore,
193
194 .single_submap_single_entry = successful_mincore,
195 .single_submap_single_entry_first_pages = successful_mincore,
196 .single_submap_single_entry_last_pages = successful_mincore,
197 .single_submap_single_entry_middle_pages = successful_mincore,
198 .single_submap_oversize_entry_at_start = successful_mincore,
199 .single_submap_oversize_entry_at_end = successful_mincore,
200 .single_submap_oversize_entry_at_both = successful_mincore,
201
202 .submap_before_allocation = successful_mincore,
203 .submap_after_allocation = successful_mincore,
204 .submap_before_hole = successful_mincore,
205 .submap_after_hole = successful_mincore,
206 .submap_allocation_submap_one_entry = successful_mincore,
207 .submap_allocation_submap_two_entries = successful_mincore,
208 .submap_allocation_submap_three_entries = successful_mincore,
209
210 .submap_before_allocation_ro = successful_mincore,
211 .submap_after_allocation_ro = successful_mincore,
212 .submap_before_hole_ro = successful_mincore,
213 .submap_after_hole_ro = successful_mincore,
214 .submap_allocation_submap_one_entry_ro = successful_mincore,
215 .submap_allocation_submap_two_entries_ro = successful_mincore,
216 .submap_allocation_submap_three_entries_ro = successful_mincore,
217
218 .protection_single_000_000 = successful_mincore,
219 .protection_single_000_r00 = successful_mincore,
220 .protection_single_000_0w0 = successful_mincore,
221 .protection_single_000_rw0 = successful_mincore,
222 .protection_single_r00_r00 = successful_mincore,
223 .protection_single_r00_rw0 = successful_mincore,
224 .protection_single_0w0_0w0 = successful_mincore,
225 .protection_single_0w0_rw0 = successful_mincore,
226 .protection_single_rw0_rw0 = successful_mincore,
227
228 .protection_pairs_000_000 = successful_mincore,
229 .protection_pairs_000_r00 = successful_mincore,
230 .protection_pairs_000_0w0 = successful_mincore,
231 .protection_pairs_000_rw0 = successful_mincore,
232 .protection_pairs_r00_000 = successful_mincore,
233 .protection_pairs_r00_r00 = successful_mincore,
234 .protection_pairs_r00_0w0 = successful_mincore,
235 .protection_pairs_r00_rw0 = successful_mincore,
236 .protection_pairs_0w0_000 = successful_mincore,
237 .protection_pairs_0w0_r00 = successful_mincore,
238 .protection_pairs_0w0_0w0 = successful_mincore,
239 .protection_pairs_0w0_rw0 = successful_mincore,
240 .protection_pairs_rw0_000 = successful_mincore,
241 .protection_pairs_rw0_r00 = successful_mincore,
242 .protection_pairs_rw0_0w0 = successful_mincore,
243 .protection_pairs_rw0_rw0 = successful_mincore,
244 };
245
246 run_vm_tests("mincore", __FILE__, &tests, argc, argv);
247 }
248