xref: /xnu-11417.121.6/osfmk/machine/static_if.h (revision a1e26a70f38d1d7daa7b49b258e2f8538ad81650)
1 /*
2  * Copyright (c) 2021 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 _MACHINE_STATIC_IF_H
30 #define _MACHINE_STATIC_IF_H
31 
32 #include <sys/cdefs.h>
33 #include <stdbool.h>
34 #include <stdint.h>
35 #include <mach/kern_return.h>
36 #include <libkern/section_keywords.h>
37 
38 typedef const struct static_if_entry *static_if_entry_t;
39 
40 typedef struct static_if_key {
41 	short                   sik_enable_count;
42 	short                   sik_init_value;
43 	unsigned                sik_entries_count;
44 	static_if_entry_t       sik_entries_head;
45 } *static_if_key_t;
46 
47 #if defined (__x86_64__)
48 #include "x86_64/static_if.h"
49 #elif defined (__arm__)
50 #include "arm/static_if.h"
51 #elif defined (__arm64__)
52 #include "arm64/static_if.h"
53 #else
54 #error architecture not supported
55 #endif
56 
57 __BEGIN_DECLS
58 #pragma GCC visibility push(hidden)
59 
60 /*
61  * Declare/define a static jump key
62  *
63  * STATIC_IF_KEY_{DECLARE,DEFINE}_TRUE
64  *     the static jump key initial enablement count is positive,
65  *     and the key enabled.
66  *
67  * STATIC_IF_KEY_{DECLARE,DEFINE}_FALSE
68  *     the static jump key initial enablement count is negative,
69  *     and the key disabled.
70  *
71  * Enablement counts can be manipulated with @c static_if_key_{enable,disable}.
72  */
73 #define STATIC_IF_KEY_DECLARE_TRUE(name) \
74 	extern struct static_if_key_true name##_jump_key
75 
76 #define STATIC_IF_KEY_DEFINE_TRUE(name) \
77 	__used struct static_if_key_true name##_jump_key = { \
78 	        .key.sik_init_value = 0, \
79 	        .key.sik_enable_count = 0, \
80 	}
81 
82 #define STATIC_IF_KEY_DECLARE_FALSE(name) \
83 	extern struct static_if_key_false name##_jump_key
84 
85 #define STATIC_IF_KEY_DEFINE_FALSE(name) \
86 	__used struct static_if_key_false name##_jump_key = { \
87 	        .key.sik_init_value = -1, \
88 	        .key.sik_enable_count = -1, \
89 	}
90 
91 /*!
92  * @macro probable_static_if() / improbable_static_if()
93  *
94  * @brief
95  * Returns whether the static if class is true or not,
96  * encoding it in code rather than loading globals.
97  *
98  * @discussion
99  * Static "ifs" are meant to provide extremely low overhead
100  * enablement or disablement of features based on TEXT patching.
101  *
102  * It is meant for debugging features, or checkers that happen
103  * on really hot paths where loading a global will affect
104  * performance in meaningful ways (lock checks, preemption
105  * disablement tracking, etc...).
106  *
107  * @c probable_static_if() versus @c improbable_static_if()
108  * affects which direction will take a branch:
109  *
110  *   key value             |   TRUE   |  FALSE   |    outlining
111  *   ----------------------+----------+----------+----------------
112  *   probable_static_if    |  nop     |  branch  |  "FALSE" case
113  *   improbable_static_if  |  branch  |  nop     |  "TRUE"  case
114  *
115  * @c static_if() will not outline any code, and will codegen a "nop"
116  * for the initial value of the key.
117  *
118  * There usually is a STARTUP hook that pairs with those
119  * static if domains that will toggle enablement based
120  * on boot-args or various configurations.
121  */
122 #define static_if(n)  ({                                        \
123 	__label__ __l;                                          \
124 	int __result = !__static_if_key_init_value(n);          \
125 	STATIC_IF_NOP(n, __l);                                  \
126 	__result = __static_if_key_init_value(n);               \
127 __l:                                                            \
128 	__result;                                               \
129 })
130 
131 #define probable_static_if(n)  __probable(({                    \
132 	__label__ __l;                                          \
133 	int __result = 0;                                       \
134 	if (__static_if_key_init_value(n)) {                    \
135 	        STATIC_IF_NOP(n, __l);                          \
136 	} else {                                                \
137 	        STATIC_IF_BRANCH(n, __l);                       \
138 	}                                                       \
139 	__result = 1;                                           \
140 __l:                                                            \
141 	__result;                                               \
142 }))
143 
144 #define improbable_static_if(n)  __improbable(({                \
145 	__label__ __l;                                          \
146 	int __result = 1;                                       \
147 	if (__static_if_key_init_value(n)) {                    \
148 	        STATIC_IF_BRANCH(n, __l);                       \
149 	} else {                                                \
150 	        STATIC_IF_NOP(n, __l);                          \
151 	}                                                       \
152 	__result = 0;                                           \
153 __l:                                                            \
154 	__result;                                               \
155 }))
156 
157 
158 /*!
159  * @function static_if_key_enable()
160  *
161  * @brief
162  * Increases the key enablement count.
163  *
164  * @discussion
165  * The key becomes disabled when its enablement count becomes positive.
166  * This function can only be called from the context of a STATIC_IF_INIT()
167  * callout.
168  */
169 #define static_if_key_enable(n) \
170 	__static_if_key_delta(&n##_jump_key.key, 1)
171 
172 /*!
173  * @function static_if_key_disable()
174  *
175  * @brief
176  * Decreases the key enablement count.
177  *
178  * @discussion
179  * The key becomes disabled when its enablement count becomes negative.
180  * This function can only be called from the context of a STATIC_IF_INIT()
181  * callout.
182  */
183 #define static_if_key_disable(n) \
184 	__static_if_key_delta(&n##_jump_key.key, -1)
185 
186 /*!
187  * @brief
188  * Marker for functions used to setup static_if() blocks during boot.
189  */
190 #define __static_if_init_func MARK_AS_FIXUP_TEXT
191 
192 /*!
193  * @macro STATIC_IF_INIT
194  *
195  * @brief
196  * Register a function to setup a static if direction.
197  *
198  * @discussion
199  * This code runs extremly early during boot and it can only rely
200  * on extremly basic notions such as boot-args or system registers.
201  *
202  * Code running during this call must be marked with __static_if_init_func.
203  */
204 #define STATIC_IF_INIT(func) \
205 	__PLACE_IN_SECTION(STATIC_IF_SEGMENT "," STATIC_IFINIT_SECTION) \
206 	static static_if_initializer __static_if__ ## func = func
207 
208 /*!
209  * @function static_if_boot_arg_uint64()
210  *
211  * @brief
212  * Parses a boot-arg within a STATIC_IF_INIT() function.
213  *
214  * @discussion
215  * PE_parse_boot_argn() can't be used that early on SPTM devices,
216  * and TUNABLES() aren't parsed yet.
217  */
218 extern uint64_t static_if_boot_arg_uint64(
219 	const char             *args,
220 	const char             *name,
221 	uint64_t                defval);
222 
223 
224 #pragma mark implementation details
225 
226 #if KASAN
227 /*
228  * The use of weird sections that get unmapped confuse the hell out of kasan,
229  * so for KASAN leave things in regular __TEXT/__DATA segments
230  */
231 #define STATIC_IF_SEGMENT       "__DATA_CONST"
232 #elif defined(__x86_64__)
233 /* Intel doesn't have a __BOOTDATA but doesn't protect __KLD */
234 #define STATIC_IF_SEGMENT       "__KLDDATA"
235 #else
236 /* arm protects __KLD early, so use __BOOTDATA for data */
237 #define STATIC_IF_SEGMENT       "__BOOTDATA"
238 #endif
239 #define STATIC_IF_SECTION       "__static_if"
240 #define STATIC_IFINIT_SECTION   "__static_ifinit"
241 #define STATIC_IF_SEGSECT       STATIC_IF_SEGMENT "," STATIC_IF_SECTION
242 
243 typedef void (*static_if_initializer)(const char *boot_args);
244 
245 struct static_if_key_true {
246 	struct static_if_key    key;
247 #if __cplusplus
248 	static const bool       init_value = true;
249 #endif
250 };
251 
252 struct static_if_key_false {
253 	struct static_if_key    key;
254 #if __cplusplus
255 	static const bool       init_value = false;
256 #endif
257 };
258 
259 #if __cplusplus
260 #define __static_if_key_init_value(n)  decltype(n##_jump_key)::init_value
261 #else
262 #define __static_if_key_init_value(n)  _Generic(n##_jump_key, \
263 	struct static_if_key_true: 1, \
264 	struct static_if_key_false: 0)
265 #endif
266 
267 extern void __static_if_key_delta(
268 	static_if_key_t         key,
269 	int                     delta);
270 
271 #if MACH_KERNEL_PRIVATE
272 
273 __attribute__((always_inline))
274 static inline unsigned long
__static_if_entry_patch_point(static_if_entry_t sie)275 __static_if_entry_patch_point(static_if_entry_t sie)
276 {
277 #if STATIC_IF_RELATIVE
278 	return (unsigned long)&sie->sie_base + (unsigned long)(long)sie->sie_base;
279 #else
280 	return (unsigned long)sie->sie_base;
281 #endif
282 }
283 
284 extern void ml_static_if_entry_patch(
285 	static_if_entry_t     ent,
286 	int                     branch);
287 
288 extern void ml_static_if_flush_icache(void);
289 
290 extern void static_if_init(const char *args);
291 
292 #endif /* MACH_KERNEL_PRIVATE */
293 
294 #pragma GCC visibility pop
295 __END_DECLS
296 
297 #endif /* _MACHINE_STATIC_IF_H */
298