xref: /xnu-12377.41.6/tests/vm/configurator_mincore.c (revision bbb1b6f9e71b8cdde6e5cd6f4841f207dee3d828)
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