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