/* * 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 /* * 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_ */