xref: /xnu-8020.101.4/tests/user_msrs.c (revision e7776783b89a353188416a9a346c6cdb4928faad)
1 #include <machine/cpu_capabilities.h>
2 #include <os/thread_self_restrict.h>
3 #include <libkern/OSCacheControl.h>
4 #include <sys/time.h>
5 #include <darwintest.h>
6 #include <sys/types.h>
7 #include <sys/sysctl.h>
8 #include <sys/wait.h>
9 #include <sys/mman.h>
10 #include <ptrauth.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <unistd.h>
14 #include <signal.h>
15 
16 
17 #define NUM_THREADS             256
18 
19 T_GLOBAL_META(T_META_RUN_CONCURRENTLY(true));
20 
21 
22 #if defined(__arm64__) && defined(__LP64__)
23 
24 enum msr_op_reason {
25 	msr_get_name, // -> const char *name
26 	msr_read,     // -> uint64_t val
27 	msr_write,    // <- uint64_t val
28 };
29 
30 typedef void (*msr_op)(enum msr_op_reason why, void *param);
31 
32 static pthread_mutex_t threads_please_die_lock = PTHREAD_MUTEX_INITIALIZER;
33 static volatile bool did_fault;
34 static bool threads_please_die;
35 
36 
37 static void*
thread_func(void * param)38 thread_func(void *param)
39 {
40 	bool die;
41 
42 	(void)param;
43 
44 	do {
45 		pthread_mutex_lock(&threads_please_die_lock);
46 		die = threads_please_die;
47 		pthread_mutex_unlock(&threads_please_die_lock);
48 	} while (!die);
49 
50 	return NULL;
51 }
52 
53 static void
msr_test(msr_op op)54 msr_test(msr_op op)
55 {
56 	struct timeval time_start, time_end, time_passed;
57 	uint64_t val, new_val, wrote_val;
58 	pthread_t threads[NUM_THREADS];
59 	bool readable, writeable;
60 	const char *reg_name;
61 	int i;
62 
63 	op(msr_get_name, &reg_name);
64 	T_LOG("sub-test '%s'\n", reg_name);
65 
66 	//let's see if we can read and write it
67 	did_fault = false;
68 	op(msr_read, &val);
69 	readable = !did_fault;
70 
71 	did_fault = false;
72 	op(msr_write, &val);
73 	writeable = !did_fault;
74 
75 	T_LOG("\tcan read: %s\n", readable ? "YES" : "NO");
76 	T_LOG("\tcan write: %s\n", writeable ? "YES" : "NO");
77 	if (readable) {
78 		T_LOG("\tvalue found: 0x%016llx\n", (unsigned long long)val);
79 	}
80 
81 	if (!readable || !writeable) {
82 		T_LOG("\t RW needed for more testing. no further testing will be performed\n");
83 		return;
84 	}
85 
86 	//write inverse of what we read and see if the read differs
87 	wrote_val = ~val;
88 	op(msr_write, &wrote_val);
89 	op(msr_read, &new_val);
90 
91 	if (new_val == val) {
92 		T_LOG("\t reg seems to not take writes (0x%016llx). no further testing will be performed\n", val);
93 		return;
94 	}
95 	T_LOG("\twrote 0x%016llx, saw 0x%016llx\n", (unsigned long long)wrote_val, (unsigned long long)new_val);
96 	wrote_val = new_val;
97 
98 	//verify it flips to original value at context switch or otherwise
99 	//for context switch to happen, spin up a lot of threads
100 	pthread_mutex_lock(&threads_please_die_lock);
101 	threads_please_die = false;
102 	pthread_mutex_unlock(&threads_please_die_lock);
103 	for (i = 0; i < NUM_THREADS; i++) {
104 		if (pthread_create(threads + i, NULL, thread_func, NULL)) {
105 			T_ASSERT_FAIL("cannot create thread %d\n", i);
106 		}
107 	}
108 
109 	gettimeofday(&time_start, NULL);
110 	while (1) {
111 		op(msr_read, &new_val);
112 		if (new_val != wrote_val) {
113 			T_LOG("\tvalue reverted to 0x%016llx from 0x%016llx\n", (unsigned long long)new_val, (unsigned long long)wrote_val);
114 			break;
115 		}
116 
117 		gettimeofday(&time_end, NULL);
118 		timersub(&time_end, &time_start, &time_passed);
119 		if (time_passed.tv_sec) { //wait one second at most
120 			T_FAIL("\ttoo long for register to be cleared, last read value was 0x%016llx, expected revert to 0x%016llx!", (unsigned long long)new_val, (unsigned long long)val);
121 			break;
122 		}
123 	}
124 	pthread_mutex_lock(&threads_please_die_lock);
125 	threads_please_die = true;
126 	pthread_mutex_unlock(&threads_please_die_lock);
127 	for (i = 0; i < NUM_THREADS; i++) {
128 		if (pthread_join(threads[i], NULL)) {
129 			T_ASSERT_FAIL("cannot join thread %d\n", i);
130 		}
131 	}
132 }
133 
134 static void
msr_test_1(enum msr_op_reason why,void * param)135 msr_test_1(enum msr_op_reason why, void *param)
136 {
137 	switch (why) {
138 	case msr_get_name:
139 		*(const char **)param = "S3_5_C15_C1_4";
140 		break;
141 
142 	case msr_read:
143 		*(uint64_t*)param = __builtin_arm_rsr64("S3_5_C15_C1_4");
144 		break;
145 
146 	case msr_write:
147 		__builtin_arm_wsr64("S3_5_C15_C1_4", *(const uint64_t*)param);
148 		break;
149 	}
150 }
151 
152 static void
msr_test_2(enum msr_op_reason why,void * param)153 msr_test_2(enum msr_op_reason why, void *param)
154 {
155 	switch (why) {
156 	case msr_get_name:
157 		*(const char **)param = "S3_5_C15_C10_1";
158 		break;
159 
160 	case msr_read:
161 		*(uint64_t*)param = __builtin_arm_rsr64("S3_5_C15_C10_1");
162 		break;
163 
164 	case msr_write:
165 		__builtin_arm_wsr64("S3_5_C15_C10_1", *(const uint64_t*)param);
166 		break;
167 	}
168 }
169 
170 static void
msr_test_3(enum msr_op_reason why,void * param)171 msr_test_3(enum msr_op_reason why, void *param)
172 {
173 	switch (why) {
174 	case msr_get_name:
175 		*(const char **)param = "TPIDRRO_EL0";
176 		break;
177 
178 	case msr_read:
179 		*(uint64_t*)param = __builtin_arm_rsr64("TPIDRRO_EL0");
180 		break;
181 
182 	case msr_write:
183 		__builtin_arm_wsr64("TPIDRRO_EL0", *(const uint64_t*)param);
184 		break;
185 	}
186 }
187 
188 static void
msr_test_4(enum msr_op_reason why,void * param)189 msr_test_4(enum msr_op_reason why, void *param)
190 {
191 	switch (why) {
192 	case msr_get_name:
193 		*(const char **)param = "TPIDR_EL1";
194 		break;
195 
196 	case msr_read:
197 		*(uint64_t*)param = __builtin_arm_rsr64("TPIDR_EL1");
198 		break;
199 
200 	case msr_write:
201 		__builtin_arm_wsr64("TPIDR_EL1", *(const uint64_t*)param);
202 		break;
203 	}
204 }
205 
206 static void
sig_caught(int signo,siginfo_t * sinfop,void * ucontext)207 sig_caught(int signo, siginfo_t *sinfop, void *ucontext)
208 {
209 	_STRUCT_MCONTEXT64 *ctx = ((ucontext_t *)ucontext)->uc_mcontext;
210 	void (*pc)(void);
211 	uint32_t instr;
212 
213 	(void)sinfop;
214 
215 	if (signo != SIGILL) {
216 		T_ASSERT_FAIL("We did not expect signal %d", signo);
217 	}
218 
219 	pc = (void (*)(void))__darwin_arm_thread_state64_get_pc(ctx->__ss);
220 	instr = *(uint32_t*)pc;
221 
222 	if ((instr & 0xffd00000) != 0xd5100000) {
223 		T_ASSERT_FAIL("We did not expect SIGILL on an instr that is not an MSR/MRS");
224 	}
225 
226 	pc = (void (*)(void))(((uintptr_t)pc) + 4);
227 	pc = ptrauth_sign_unauthenticated(pc, ptrauth_key_function_pointer, 0);
228 
229 	did_fault = true;
230 
231 	// skip the instruction
232 	__darwin_arm_thread_state64_set_pc_fptr(ctx->__ss, pc);
233 }
234 
235 static bool
is_release_kernel(void)236 is_release_kernel(void)
237 {
238 /*
239  *       I apologize to anyone reading this code!! I promise you that I felt
240  *       as dirty writing this as you do reading this. I asked in the proper
241  *       channels, but nobody had a good idea how to detect a release kernel
242  *       from userspace. Sadly, we need that here as the mitigations at hand
243  *       are only applied in RELEASE builds. Again: I am sorry.
244  */
245 	char ver_str[1024] = {};
246 	size_t len = sizeof(ver_str) - 1;
247 
248 	(void)sysctlbyname("kern.version", ver_str, &len, NULL, 0);
249 
250 	return !!strstr(ver_str, "/RELEASE_ARM64");
251 }
252 
253 #endif // defined(__arm64__) && defined(__LP64__)
254 
255 T_DECL(user_msrs, "Userspace MSR access test")
256 {
257 #if defined(__arm64__) && defined(__LP64__)
258 	if (is_release_kernel()) {
259 		struct sigaction sa_old, sa_new = {.__sigaction_u = { .__sa_sigaction = sig_caught, }, .sa_flags = SA_SIGINFO, };
260 
261 		sigaction(SIGILL, &sa_new, &sa_old);
262 		msr_test(msr_test_1);
263 		msr_test(msr_test_2);
264 		msr_test(msr_test_3);
265 		msr_test(msr_test_4);
266 		//todo: macro autogen all of them
267 		sigaction(SIGILL, &sa_old, NULL);
268 
269 		T_PASS("Userspace MSR access test passed");
270 	} else {
271 		T_PASS("Userspace MSR access test only runs on release kernels");
272 	}
273 #else // defined(__arm64__) && defined(__LP64__)
274 	T_SKIP("userspace MSR access test skipped - not ARM64.64");
275 #endif // defined(__arm64__) && defined(__LP64__)
276 }
277