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