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 #ifndef _KERN_THREAD_TEST_CONTEXT_H_
30 #define _KERN_THREAD_TEST_CONTEXT_H_
31 
32 #include <kern/thread.h>
33 
34 /*
35  * Thread-specific data for threads running kernel tests.
36  *
37  * A kernel test may store a "test context" in current_thread().
38  * The test context is used to communicate between the kernel test
39  * and the kernel implementation code being tested.
40  *
41  * A "test option" is a field in the thread test context that
42  * is intended to be accessed by convenience accessors (such as
43  * thread_get_test_option) that do nothing in release builds.
44  *
45  * Example uses:
46  * - a kernel test sets a flag that some kernel implementation
47  *   code sees to return an error instead of panicking
48  * - some kernel implementation code increments counters as it
49  *   progresses for kernel test code to validate its execution
50  */
51 
52 __BEGIN_DECLS
53 
54 #ifdef MACH_KERNEL_PRIVATE
55 
56 #define DECLARE_TEST_IDENTITY(ident) \
57 	extern test_identity_t const ident
58 #define DEFINE_TEST_IDENTITY(ident) \
59 	test_identity_t const ident = (test_identity_t)&ident
60 
61 typedef const struct test_identity_t *test_identity_t;
62 
63 typedef struct thread_test_context {
64 	/*
65 	 * ttc_identity optionally names the kernel test that owns this
66 	 * context structure. If you want to wrap thread_test_context_t
67 	 * in some larger structure for your test, use this field.
68 	 *
69 	 * ttc_data is reserved for the use of the test named by ttc_identity.
70 	 */
71 	test_identity_t ttc_identity;
72 	void *ttc_data;
73 
74 	/*
75 	 * Additional fields below may be used by any kernel test or
76 	 * kernel implementation code regardless of test identity.
77 	 * Kernel tests that don't use a field initialize it to zero.
78 	 * Any non-trivial deinitialization is in thread_test_context_deinit().
79 	 */
80 
81 	/* for tests of thread_test_context_t itself */
82 	int ttc_testing_ttc_int;
83 	struct mach_vm_range ttc_testing_ttc_struct;
84 
85 	/* prevent some panics for untagged wired memory */
86 	bool test_option_vm_prevent_wire_tag_panic;
87 	/* allow NULL vm_map->pmap in some places? */
88 	bool test_option_vm_map_allow_null_pmap;
89 	/* clamp virtual addresses before passing to pmap_remove? */
90 	bool test_option_vm_map_clamp_pmap_remove;
91 } thread_test_context_t;
92 
93 
94 /*
95  * Gets the test context of current_thread.
96  * Returns NULL if current_thread has no test context set.
97  * Returns NULL on release builds.
98  */
99 static inline thread_test_context_t * __result_use_check
thread_get_test_context(void)100 thread_get_test_context(void)
101 {
102 #if DEBUG || DEVELOPMENT
103 	return current_thread()->th_test_ctx;
104 #else
105 	return NULL;
106 #endif
107 }
108 
109 /*
110  * Gets a field from the test context on current_thread.
111  * Returns a zero-initialized value if current_thread has no test context set.
112  * Returns a zero-initialized value on release builds.
113  */
114 #define thread_get_test_option(field)                                   \
115 	({                                                              \
116 	        thread_test_context_t *_get_ctx = thread_get_test_context(); \
117 	        __improbable(_get_ctx != NULL)                          \
118 	            ? (_get_ctx->field)                                 \
119 	            : (__typeof__(_get_ctx->field)){};                  \
120 	})
121 
122 /*
123  * Sets thread_test_context_t->field = new_value on current_thread.
124  * Does nothing if current_thread has no test context set.
125  * Does nothing on release builds; new_value is not evaluated.
126  */
127 #if DEBUG || DEVELOPMENT
128 #define thread_set_test_option(field, /* new_value */ ...)          \
129 	({                                                              \
130 	        thread_test_context_t *_set_ctx = thread_get_test_context(); \
131 	        if (__improbable(_set_ctx != NULL)) {                   \
132 	                _set_ctx->field = (__VA_ARGS__);                \
133 	        }                                                       \
134 	})
135 #else /* not (DEBUG || DEVELOPMENT) */
136 #define thread_set_test_option(field, /* new_value */ ...)      \
137 	({ })
138 #endif /* not (DEBUG || DEVELOPMENT) */
139 
140 
141 #if DEBUG || DEVELOPMENT
142 
143 /*
144  * Sets the test context of current_thread.
145  * Panics if new_ctx is NULL.
146  * Panics if current_thread's test context is already set.
147  * Not available in release builds.
148  */
149 static inline void
thread_set_test_context(thread_test_context_t * new_ctx)150 thread_set_test_context(thread_test_context_t *new_ctx)
151 {
152 	thread_t thread = current_thread();
153 	assert(new_ctx);
154 	assert(thread->th_test_ctx == NULL);
155 	thread->th_test_ctx = new_ctx;
156 }
157 
158 /*
159  * Performs any deinitialization of ctx required.
160  * The contents of *ctx are left in an indeterminate state.
161  * ctx is not freed.
162  * Panics if ctx is NULL.
163  * Not available in release builds.
164  */
165 extern void
166 thread_test_context_deinit(thread_test_context_t *ctx);
167 
168 /*
169  * Convenience macro for using attribute(cleanup) to disconnect and
170  * destroy a stack-allocated thread test context at end of scope.
171  * Not available in release builds.
172  *
173  * Usage:
174  * {
175  *     thread_test_context_t ctx CLEANUP_THREAD_TEST_CONTEXT = {
176  *             .ttc_field = value, .ttc_field_2 = value2, ...
177  *     };
178  *     thread_set_test_context(&ctx);
179  *     ... run tests ...
180  *     ... ctx is disconnected and deinited here at end of scope ...
181  * }
182  */
183 
184 #define CLEANUP_THREAD_TEST_CONTEXT                             \
185 	__attribute__((cleanup(thread_cleanup_test_context)))
186 
187 static inline void
thread_cleanup_test_context(thread_test_context_t * ctx)188 thread_cleanup_test_context(thread_test_context_t *ctx)
189 {
190 	/*
191 	 * No assertion that th_test_ctx is non-NULL here,
192 	 * in case the caller needed to exit before setting it.
193 	 * ... but if it is set, it must be set to ctx.
194 	 */
195 	thread_test_context_t *thread_ctx = current_thread()->th_test_ctx;
196 	if (thread_ctx) {
197 		assert(thread_ctx == ctx);
198 	}
199 	current_thread()->th_test_ctx = NULL;
200 
201 	thread_test_context_deinit(ctx);
202 	/* No heap deallocation necessary here: *ctx is stored on the stack */
203 }
204 
205 #endif /* DEBUG || DEVELOPMENT */
206 
207 #endif /* MACH_KERNEL_PRIVATE */
208 
209 __END_DECLS
210 
211 #endif /* _KERN_THREAD_TEST_CONTEXT_H_ */
212