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