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