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