xref: /xnu-10063.141.1/osfmk/kern/exclaves_panic.c (revision d8b80295118ef25ac3a784134bcf95cd8e88109f)
1 /*
2  * Copyright (c) 2023 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 #if CONFIG_EXCLAVES
30 
31 #include <kern/assert.h>
32 #include <kern/misc_protos.h>
33 #include <kern/thread.h>
34 
35 #include <mach/exclaves_l4.h>
36 
37 #include <uuid/uuid.h>
38 
39 #include <xnuproxy/messages.h>
40 #include <xnuproxy/panic.h>
41 
42 #include "exclaves_debug.h"
43 #include "exclaves_panic.h"
44 
45 /* Use the new version of xnuproxy_msg_t. */
46 #define xnuproxy_msg_t xnuproxy_msg_new_t
47 
48 #define EXCLAVES_PANIC_FOUR_CC_FORMAT   "%c%c%c%c"
49 #define EXCLAVES_PANIC_FOUR_CC_CHARS(c) \
50     (((c) >> 24) & 0xFF),               \
51     (((c) >> 16) & 0xFF),               \
52     (((c) >>  8) & 0xFF),               \
53     (((c) >>  0) & 0xFF)
54 #define EXCLAVE_PANIC_MESSAGE_MARKER    "[Exclaves]"
55 
56 // Adding 64 bytes here to accommodate the PC and LR in the panic string
57 #define EXCLAVES_PANIC_STRING_SIZE     \
58     (sizeof(EXCLAVE_PANIC_MESSAGE_MARKER) + XNUPROXY_PANIC_MESSAGE_LEN + XNUPROXY_PANIC_NAME_BYTES + 64)
59 
60 #define P2ROUNDUP(x, align) (-(-(x) & -(align)))
61 #define PANIC_BUFFER_PAGE_COUNT  (P2ROUNDUP(sizeof (xnuproxy_panic_buffer_t), PAGE_SIZE) / PAGE_SIZE)
62 
63 static char exclaves_panic_string[EXCLAVES_PANIC_STRING_SIZE];
64 static char *exclaves_panic_buffer_pages[PANIC_BUFFER_PAGE_COUNT];
65 static xnuproxy_panic_buffer_t exclaves_panic_buffer;
66 static int exclaves_panic_thread_wait_forever;
67 
68 static void
copy_panic_buffer_pages(pmap_paddr_t addr)69 copy_panic_buffer_pages(pmap_paddr_t addr)
70 {
71 	uint64_t *pages = (uint64_t *)phystokv(addr);
72 
73 	for (int i = 0; i < PANIC_BUFFER_PAGE_COUNT; i++) {
74 		exclaves_panic_buffer_pages[i] = (char *)phystokv(pages[i]);
75 	}
76 
77 	/*
78 	 * For backwards compat always use the base page even if not listed
79 	 * explicitly.
80 	 */
81 	exclaves_panic_buffer_pages[0] = (char *)pages;
82 
83 	return;
84 }
85 
86 static void
exclaves_xnu_proxy_panic_thread(void * arg __unused,wait_result_t wr __unused)87 exclaves_xnu_proxy_panic_thread(void *arg __unused, wait_result_t wr __unused)
88 {
89 	kern_return_t kr = KERN_SUCCESS;
90 	Exclaves_L4_Word_t spawned_scid = 0;
91 	thread_t thread;
92 
93 	xnuproxy_msg_t msg = {
94 		.cmd = XNUPROXY_CMD_PANIC_SETUP,
95 	};
96 
97 	extern kern_return_t exclaves_xnu_proxy_send(xnuproxy_msg_t *,
98 	    Exclaves_L4_Word_t *);
99 	kr = exclaves_xnu_proxy_send(&msg, &spawned_scid);
100 	if (kr != KERN_SUCCESS) {
101 		exclaves_debug_printf(show_errors,
102 		    "exclaves: panic thread init: xnu proxy send failed.");
103 		return;
104 	}
105 
106 	/* Dont copy if the panic buffer initialisation already happended */
107 	if (exclaves_panic_buffer_pages[0] == 0) {
108 		copy_panic_buffer_pages(msg.cmd_panic_setup.response.physical_address);
109 	}
110 
111 	thread = current_thread();
112 	thread->th_exclaves_scheduling_context_id = spawned_scid;
113 
114 	assert3u(thread->th_exclaves_state & TH_EXCLAVES_STATE_ANY, ==, 0);
115 	thread->th_exclaves_state |= TH_EXCLAVES_SCHEDULER_CALL;
116 
117 	while (1) {
118 		extern kern_return_t exclaves_scheduler_resume_scheduling_context(Exclaves_L4_Word_t,
119 		    Exclaves_L4_Word_t *);
120 		kr = exclaves_scheduler_resume_scheduling_context(spawned_scid, NULL);
121 		assert3u(kr, ==, KERN_SUCCESS);
122 	}
123 }
124 
125 static bool
exclaves_panic_buffer_sync(void)126 exclaves_panic_buffer_sync(void)
127 {
128 	char *panic_buffer = (char *)&exclaves_panic_buffer;
129 	size_t len = sizeof(exclaves_panic_buffer);
130 
131 	/* Just return if the panic buffer initialisation hasn't happened yet.  */
132 	if (exclaves_panic_buffer_pages[0] == 0) {
133 		return KERN_NOT_SUPPORTED;
134 	}
135 
136 	/*
137 	 * Initialize next page to the first page. This is for the backwards
138 	 * compatibility case.
139 	 */
140 	char *next_page = exclaves_panic_buffer_pages[0];
141 	for (int i = 0; i < PANIC_BUFFER_PAGE_COUNT; i++) {
142 		size_t nbytes = MIN(len, PAGE_SIZE);
143 
144 		next_page = exclaves_panic_buffer_pages[i] != 0 ?
145 		    exclaves_panic_buffer_pages[i] : next_page + PAGE_SIZE;
146 
147 		(void)memcpy(panic_buffer, next_page, nbytes);
148 
149 		panic_buffer += nbytes;
150 		len -= nbytes;
151 
152 		if (len == 0) {
153 			break;
154 		}
155 	}
156 
157 	return KERN_SUCCESS;
158 }
159 
160 static void
exclaves_append_panic_backtrace(void)161 exclaves_append_panic_backtrace(void)
162 {
163 	uuid_string_t uuid_string;
164 	xnuproxy_panic_backtrace_word_t *words;
165 
166 	assert3p(exclaves_panic_buffer_pages[0], !=, NULL);
167 
168 	if ((exclaves_panic_buffer.panicked_thread.backtrace.frames >
169 	    XNUPROXY__PANIC_BACKTRACE_WORDS)) {
170 		return;
171 	}
172 
173 	words = exclaves_panic_buffer.backtrace.words;
174 	paniclog_append_noflush("Exclaves backtrace:\n");
175 	for (size_t i = 0; i < exclaves_panic_buffer.panicked_thread.backtrace.frames; i++) {
176 		uuid_unparse_upper(
177 			(const unsigned char *)exclaves_panic_buffer.backtrace.images[words[i].image].uuid,
178 			uuid_string);
179 		paniclog_append_noflush("\t\t%s 0x%016zx\n", uuid_string,
180 		    exclaves_panic_buffer.backtrace.words[i].offset);
181 	}
182 
183 	paniclog_append_noflush("\n");
184 	return;
185 }
186 
187 static void
exclaves_append_panic_addl_info(xnuproxy_panicked_thread_t * ex_thread)188 exclaves_append_panic_addl_info(xnuproxy_panicked_thread_t *ex_thread)
189 {
190 	char component_name[XNUPROXY_PANIC_NAME_BYTES] = {0};
191 
192 	strlcpy(component_name, ex_thread->component.name, sizeof(component_name));
193 
194 	paniclog_append_noflush(
195 		"\t\tAddress space ID: 0x%llx\n"
196 		"\t\tComponent:\n"
197 		"\t\t\tName: %s\n"
198 		"\t\t\tID: 0x%llx\n"
199 		"\t\t\tSelector: 0x%llx\n"
200 		"\t\tspace.component.endpoint.thread: " EXCLAVES_PANIC_FOUR_CC_FORMAT "."
201 		EXCLAVES_PANIC_FOUR_CC_FORMAT "."
202 		EXCLAVES_PANIC_FOUR_CC_FORMAT "."
203 		EXCLAVES_PANIC_FOUR_CC_FORMAT "\n"
204 		"\t\tThread Context:\n"
205 		"\t\t\tAddress: 0x%zx\n"
206 		"\t\t\tTSS Base: 0x%zx\n"
207 		"\t\t\tIPC Buffer 0x%zx\n"
208 		"\t\t\tSCID 0x%zx\n"
209 		"\t\t\tECID: 0x%zx\n"
210 		"\t\t\tEPID: 0x%zx\n"
211 		"\t\t\tStack:\n"
212 		"\t\t\t\tStart: 0x%zx\n"
213 		"\t\t\t\tSize: 0x%zx\n"
214 		"\t\t\t\tCall base: 0x%zx\n"
215 		"\t\t\tRegisters:\n"
216 		"\t\t\t\tLR: 0x%zx\n"
217 		"\t\t\t\tPC: 0x%zx\n"
218 		"\t\t\t\tSP: 0x%zx\n"
219 		"\t\t\t\tCPSR: 0x%zx\n",
220 		ex_thread->address_space_id, component_name,
221 		ex_thread->component.numeric_id, ex_thread->component.selector,
222 		EXCLAVES_PANIC_FOUR_CC_CHARS(ex_thread->four_cc.space),
223 		EXCLAVES_PANIC_FOUR_CC_CHARS(ex_thread->four_cc.component),
224 		EXCLAVES_PANIC_FOUR_CC_CHARS(ex_thread->four_cc.endpoint),
225 		EXCLAVES_PANIC_FOUR_CC_CHARS(ex_thread->four_cc.thread),
226 		ex_thread->thread.address, ex_thread->thread.tss_base,
227 		ex_thread->thread.ipc_buffer, ex_thread->thread.scheduling_context_id,
228 		ex_thread->thread.execution_context_id, ex_thread->thread.endpoint_id,
229 		ex_thread->thread.stack.start, ex_thread->thread.stack.size,
230 		ex_thread->thread.stack.call_base, ex_thread->thread.registers.lr,
231 		ex_thread->thread.registers.pc, ex_thread->thread.registers.sp,
232 		ex_thread->thread.registers.cpsr);
233 }
234 
235 kern_return_t
exclaves_panic_get_string(char ** string)236 exclaves_panic_get_string(char **string)
237 {
238 	uint32_t status = 0;
239 	char component_name[XNUPROXY_PANIC_NAME_BYTES] = {0};
240 
241 	kern_return_t kr = exclaves_panic_buffer_sync();
242 	if (kr != KERN_SUCCESS) {
243 		return kr;
244 	}
245 
246 	xnuproxy_panicked_thread_t *ex_thread =
247 	    &exclaves_panic_buffer.panicked_thread;
248 
249 	strlcpy(component_name, ex_thread->component.name, sizeof(component_name));
250 
251 	status = os_atomic_load(&ex_thread->status, seq_cst);
252 	if (status == XNUPROXY_PANIC_UNSET) {
253 		return KERN_FAILURE;
254 	}
255 
256 	snprintf(exclaves_panic_string, sizeof(exclaves_panic_string),
257 	    "%s %s:%s at PC: 0x%zx, LR: 0x%zx", EXCLAVE_PANIC_MESSAGE_MARKER,
258 	    component_name, exclaves_panic_buffer.message,
259 	    ex_thread->thread.registers.pc, ex_thread->thread.registers.lr);
260 	exclaves_panic_string[sizeof(exclaves_panic_string) - 1] = '\0';
261 	*string = exclaves_panic_string;
262 
263 	return KERN_SUCCESS;
264 }
265 
266 void
exclaves_panic_append_info(void)267 exclaves_panic_append_info(void)
268 {
269 	uint32_t status = 0;
270 	char *status_str;
271 
272 	kern_return_t kr = exclaves_panic_buffer_sync();
273 	if (kr != KERN_SUCCESS) {
274 		return;
275 	}
276 
277 	xnuproxy_panicked_thread_t *ex_thread =
278 	    &exclaves_panic_buffer.panicked_thread;
279 
280 	status = os_atomic_load(&ex_thread->status, seq_cst);
281 
282 	switch (status) {
283 	case XNUPROXY_PANIC_PARTIAL:
284 		status_str = "PARTIAL";
285 		break;
286 	case XNUPROXY_PANIC_COMPLETE:
287 		status_str = "COMPLETE";
288 		break;
289 	default:
290 		return;
291 	}
292 
293 	panic_info->eph_panic_flags |= EMBEDDED_PANIC_HEADER_FLAG_EXCLAVE_PANIC;
294 
295 	paniclog_append_noflush("Exclaves additional info: STATUS: %s\n", status_str);
296 	exclaves_append_panic_addl_info(ex_thread);
297 
298 	exclaves_append_panic_backtrace();
299 }
300 
301 __attribute__((noinline, noreturn))
302 void
exclaves_panic_thread_wait(void)303 exclaves_panic_thread_wait(void)
304 {
305 	assert_wait((event_t)&exclaves_panic_thread_wait_forever, THREAD_UNINT);
306 	(void) thread_block(THREAD_CONTINUE_NULL);
307 
308 	/* NOT REACHABLE */
309 	panic("Exclaves panic thread woken up");
310 }
311 
312 void
handle_response_panic_buffer_address(pmap_paddr_t addr)313 handle_response_panic_buffer_address(pmap_paddr_t addr)
314 {
315 	return copy_panic_buffer_pages(addr);
316 }
317 
318 kern_return_t
exclaves_panic_thread_setup(void)319 exclaves_panic_thread_setup(void)
320 {
321 	thread_t thread = THREAD_NULL;
322 	kern_return_t kr = KERN_FAILURE;
323 
324 	kr = kernel_thread_start_priority(exclaves_xnu_proxy_panic_thread, NULL,
325 	    BASEPRI_DEFAULT, &thread);
326 	if (kr != KERN_SUCCESS) {
327 		return kr;
328 	}
329 
330 	thread_set_thread_name(thread, "EXCLAVES_PANIC_WAIT_THREAD");
331 	thread_deallocate(thread);
332 
333 	return KERN_SUCCESS;
334 }
335 
336 #endif /* CONFIG_ EXCLAVES */
337