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