Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 | /* * Copyright (c) 2024 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. The rights granted to you under the License * may not be used to create, or enable the creation or redistribution of, * unlawful or unlicensed copies of an Apple operating system, or to * circumvent, violate, or enable the circumvention or violation of, any * terms of an Apple operating system software license agreement. * * Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ #ifndef _KERN_THREAD_TEST_CONTEXT_H_ #define _KERN_THREAD_TEST_CONTEXT_H_ #include <kern/thread.h> /* * Thread-specific data for threads running kernel tests. * * A kernel test may store a "test context" in current_thread(). * The test context is used to communicate between the kernel test * and the kernel implementation code being tested. * * A "test option" is a field in the thread test context that * is intended to be accessed by convenience accessors (such as * thread_get_test_option) that do nothing in release builds. * * Example uses: * - a kernel test sets a flag that some kernel implementation * code sees to return an error instead of panicking * - some kernel implementation code increments counters as it * progresses for kernel test code to validate its execution */ __BEGIN_DECLS #ifdef MACH_KERNEL_PRIVATE #define DECLARE_TEST_IDENTITY(ident) \ extern test_identity_t const ident #define DEFINE_TEST_IDENTITY(ident) \ test_identity_t const ident = (test_identity_t)&ident typedef const struct test_identity_t *test_identity_t; typedef struct thread_test_context { /* * ttc_identity optionally names the kernel test that owns this * context structure. If you want to wrap thread_test_context_t * in some larger structure for your test, use this field. * * ttc_data is reserved for the use of the test named by ttc_identity. */ test_identity_t ttc_identity; void *ttc_data; /* * Additional fields below may be used by any kernel test or * kernel implementation code regardless of test identity. * Kernel tests that don't use a field initialize it to zero. * Any non-trivial deinitialization is in thread_test_context_deinit(). */ /* for tests of thread_test_context_t itself */ int ttc_testing_ttc_int; struct mach_vm_range ttc_testing_ttc_struct; /* prevent some panics for untagged wired memory */ bool test_option_vm_prevent_wire_tag_panic; /* allow NULL vm_map->pmap in some places? */ bool test_option_vm_map_allow_null_pmap; /* clamp virtual addresses before passing to pmap_remove? */ bool test_option_vm_map_clamp_pmap_remove; } thread_test_context_t; /* * Gets the test context of current_thread. * Returns NULL if current_thread has no test context set. * Returns NULL on release builds. */ static inline thread_test_context_t * __result_use_check thread_get_test_context(void) { #if DEBUG || DEVELOPMENT return current_thread()->th_test_ctx; #else return NULL; #endif } /* * Gets a field from the test context on current_thread. * Returns a zero-initialized value if current_thread has no test context set. * Returns a zero-initialized value on release builds. */ #define thread_get_test_option(field) \ ({ \ thread_test_context_t *_get_ctx = thread_get_test_context(); \ __improbable(_get_ctx != NULL) \ ? (_get_ctx->field) \ : (__typeof__(_get_ctx->field)){}; \ }) /* * Sets thread_test_context_t->field = new_value on current_thread. * Does nothing if current_thread has no test context set. * Does nothing on release builds; new_value is not evaluated. */ #if DEBUG || DEVELOPMENT #define thread_set_test_option(field, /* new_value */ ...) \ ({ \ thread_test_context_t *_set_ctx = thread_get_test_context(); \ if (__improbable(_set_ctx != NULL)) { \ _set_ctx->field = (__VA_ARGS__); \ } \ }) #else /* not (DEBUG || DEVELOPMENT) */ #define thread_set_test_option(field, /* new_value */ ...) \ ({ }) #endif /* not (DEBUG || DEVELOPMENT) */ #if DEBUG || DEVELOPMENT /* * Sets the test context of current_thread. * Panics if new_ctx is NULL. * Panics if current_thread's test context is already set. * Not available in release builds. */ static inline void thread_set_test_context(thread_test_context_t *new_ctx) { thread_t thread = current_thread(); assert(new_ctx); assert(thread->th_test_ctx == NULL); thread->th_test_ctx = new_ctx; } /* * Performs any deinitialization of ctx required. * The contents of *ctx are left in an indeterminate state. * ctx is not freed. * Panics if ctx is NULL. * Not available in release builds. */ extern void thread_test_context_deinit(thread_test_context_t *ctx); /* * Convenience macro for using attribute(cleanup) to disconnect and * destroy a stack-allocated thread test context at end of scope. * Not available in release builds. * * Usage: * { * thread_test_context_t ctx CLEANUP_THREAD_TEST_CONTEXT = { * .ttc_field = value, .ttc_field_2 = value2, ... * }; * thread_set_test_context(&ctx); * ... run tests ... * ... ctx is disconnected and deinited here at end of scope ... * } */ #define CLEANUP_THREAD_TEST_CONTEXT \ __attribute__((cleanup(thread_cleanup_test_context))) static inline void thread_cleanup_test_context(thread_test_context_t *ctx) { /* * No assertion that th_test_ctx is non-NULL here, * in case the caller needed to exit before setting it. * ... but if it is set, it must be set to ctx. */ thread_test_context_t *thread_ctx = current_thread()->th_test_ctx; if (thread_ctx) { assert(thread_ctx == ctx); } current_thread()->th_test_ctx = NULL; thread_test_context_deinit(ctx); /* No heap deallocation necessary here: *ctx is stored on the stack */ } #endif /* DEBUG || DEVELOPMENT */ #endif /* MACH_KERNEL_PRIVATE */ __END_DECLS #endif /* _KERN_THREAD_TEST_CONTEXT_H_ */ |