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_debug.h"
42 #include "exclaves_panic.h"
43 #include "exclaves_boot.h"
44 #include "exclaves_xnuproxy.h"
45 #include "exclaves_internal.h"
46
47 /* Use the new version of xnuproxy_msg_t. */
48 #define xnuproxy_msg_t xnuproxy_msg_new_t
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 kern_return_t kr = KERN_SUCCESS;
92
93 uint64_t phys;
94 uint64_t scid;
95 kr = exclaves_xnuproxy_panic_setup(&phys, &scid);
96 if (kr != KERN_SUCCESS) {
97 exclaves_debug_printf(show_errors,
98 "exclaves: panic thread init: xnu proxy send failed.");
99 return;
100 }
101
102 /* Dont copy if the panic buffer initialisation already happended */
103 if (exclaves_panic_buffer_pages[0] == 0) {
104 copy_panic_buffer_pages(phys);
105 }
106
107 thread_t thread = current_thread();
108 thread->th_exclaves_ipc_ctx.scid = scid;
109
110 assert3u(thread->th_exclaves_state & TH_EXCLAVES_STATE_ANY, ==, 0);
111 thread->th_exclaves_state |= TH_EXCLAVES_SCHEDULER_CALL;
112
113 while (1) {
114 kr = exclaves_scheduler_resume_scheduling_context(&thread->th_exclaves_ipc_ctx, false);
115 assert3u(kr, ==, KERN_SUCCESS);
116 }
117 }
118
119 static bool
exclaves_panic_buffer_sync(void)120 exclaves_panic_buffer_sync(void)
121 {
122 char *panic_buffer = (char *)&exclaves_panic_buffer;
123 size_t len = sizeof(exclaves_panic_buffer);
124
125 /* Just return if the panic buffer initialisation hasn't happened yet. */
126 if (exclaves_panic_buffer_pages[0] == 0) {
127 return KERN_NOT_SUPPORTED;
128 }
129
130 /*
131 * Initialize next page to the first page. This is for the backwards
132 * compatibility case.
133 */
134 char *next_page = exclaves_panic_buffer_pages[0];
135 for (int i = 0; i < PANIC_BUFFER_PAGE_COUNT; i++) {
136 size_t nbytes = MIN(len, PAGE_SIZE);
137
138 next_page = exclaves_panic_buffer_pages[i] != 0 ?
139 exclaves_panic_buffer_pages[i] : next_page + PAGE_SIZE;
140
141 (void)memcpy(panic_buffer, next_page, nbytes);
142
143 panic_buffer += nbytes;
144 len -= nbytes;
145
146 if (len == 0) {
147 break;
148 }
149 }
150
151 return KERN_SUCCESS;
152 }
153
154 static void
exclaves_append_panic_backtrace(void)155 exclaves_append_panic_backtrace(void)
156 {
157 uuid_string_t uuid_string;
158 xnuproxy_panic_backtrace_word_t *words;
159
160 assert3p(exclaves_panic_buffer_pages[0], !=, NULL);
161
162 if ((exclaves_panic_buffer.panicked_thread.backtrace.frames >
163 XNUPROXY__PANIC_BACKTRACE_WORDS)) {
164 return;
165 }
166
167 words = exclaves_panic_buffer.backtrace.words;
168 paniclog_append_noflush("Exclaves backtrace:\n");
169 for (size_t i = 0; i < exclaves_panic_buffer.panicked_thread.backtrace.frames; i++) {
170 uuid_unparse_upper(
171 (const unsigned char *)exclaves_panic_buffer.backtrace.images[words[i].image].uuid,
172 uuid_string);
173 paniclog_append_noflush("\t\t%s 0x%016zx\n", uuid_string,
174 exclaves_panic_buffer.backtrace.words[i].offset);
175 }
176
177 paniclog_append_noflush("\n");
178 return;
179 }
180
181 static void
exclaves_append_panic_addl_info(xnuproxy_panicked_thread_t * ex_thread)182 exclaves_append_panic_addl_info(xnuproxy_panicked_thread_t *ex_thread)
183 {
184 char component_name[XNUPROXY_PANIC_NAME_BYTES] = {0};
185
186 strlcpy(component_name, ex_thread->component.name, sizeof(component_name));
187
188 paniclog_append_noflush(
189 "\t\tAddress space ID: 0x%llx\n"
190 "\t\tComponent:\n"
191 "\t\t\tName: %s\n"
192 "\t\t\tID: 0x%llx\n"
193 "\t\t\tSelector: 0x%llx\n"
194 "\t\tspace.component.endpoint.thread: " EXCLAVES_PANIC_FOUR_CC_FORMAT "."
195 EXCLAVES_PANIC_FOUR_CC_FORMAT "."
196 EXCLAVES_PANIC_FOUR_CC_FORMAT "."
197 EXCLAVES_PANIC_FOUR_CC_FORMAT "\n"
198 "\t\tThread Context:\n"
199 "\t\t\tAddress: 0x%zx\n"
200 "\t\t\tTSS Base: 0x%zx\n"
201 "\t\t\tIPC Buffer 0x%zx\n"
202 "\t\t\tSCID 0x%zx\n"
203 "\t\t\tECID: 0x%zx\n"
204 "\t\t\tEPID: 0x%zx\n"
205 "\t\t\tStack:\n"
206 "\t\t\t\tStart: 0x%zx\n"
207 "\t\t\t\tSize: 0x%zx\n"
208 "\t\t\t\tCall base: 0x%zx\n"
209 "\t\t\tRegisters:\n"
210 "\t\t\t\tLR: 0x%zx\n"
211 "\t\t\t\tPC: 0x%zx\n"
212 "\t\t\t\tSP: 0x%zx\n"
213 "\t\t\t\tCPSR: 0x%zx\n",
214 ex_thread->address_space_id, component_name,
215 ex_thread->component.numeric_id, ex_thread->component.selector,
216 EXCLAVES_PANIC_FOUR_CC_CHARS(ex_thread->four_cc.space),
217 EXCLAVES_PANIC_FOUR_CC_CHARS(ex_thread->four_cc.component),
218 EXCLAVES_PANIC_FOUR_CC_CHARS(ex_thread->four_cc.endpoint),
219 EXCLAVES_PANIC_FOUR_CC_CHARS(ex_thread->four_cc.thread),
220 ex_thread->thread.address, ex_thread->thread.tss_base,
221 ex_thread->thread.ipc_buffer, ex_thread->thread.scheduling_context_id,
222 ex_thread->thread.execution_context_id, ex_thread->thread.endpoint_id,
223 ex_thread->thread.stack.start, ex_thread->thread.stack.size,
224 ex_thread->thread.stack.call_base, ex_thread->thread.registers.lr,
225 ex_thread->thread.registers.pc, ex_thread->thread.registers.sp,
226 ex_thread->thread.registers.cpsr);
227 }
228
229 kern_return_t
exclaves_panic_get_string(char ** string)230 exclaves_panic_get_string(char **string)
231 {
232 uint32_t status = 0;
233 char component_name[XNUPROXY_PANIC_NAME_BYTES] = {0};
234
235 kern_return_t kr = exclaves_panic_buffer_sync();
236 if (kr != KERN_SUCCESS) {
237 return kr;
238 }
239
240 xnuproxy_panicked_thread_t *ex_thread =
241 &exclaves_panic_buffer.panicked_thread;
242
243 strlcpy(component_name, ex_thread->component.name, sizeof(component_name));
244
245 status = os_atomic_load(&ex_thread->status, seq_cst);
246 if (status == XNUPROXY_PANIC_UNSET) {
247 return KERN_FAILURE;
248 }
249
250 snprintf(exclaves_panic_string, sizeof(exclaves_panic_string),
251 "%s %s:%s at PC: 0x%zx, LR: 0x%zx", EXCLAVE_PANIC_MESSAGE_MARKER,
252 component_name, exclaves_panic_buffer.message,
253 ex_thread->thread.registers.pc, ex_thread->thread.registers.lr);
254 exclaves_panic_string[sizeof(exclaves_panic_string) - 1] = '\0';
255 *string = exclaves_panic_string;
256
257 return KERN_SUCCESS;
258 }
259
260 void
exclaves_panic_append_info(void)261 exclaves_panic_append_info(void)
262 {
263 uint32_t status = 0;
264 kern_return_t kr = 0;
265 char *status_str;
266
267 paniclog_append_noflush("Exclaves boot status: %s\n", exclaves_get_boot_status_string());
268
269 kr = exclaves_panic_buffer_sync();
270 if (kr != KERN_SUCCESS) {
271 return;
272 }
273
274 xnuproxy_panicked_thread_t *ex_thread =
275 &exclaves_panic_buffer.panicked_thread;
276
277 status = os_atomic_load(&ex_thread->status, seq_cst);
278
279 switch (status) {
280 case XNUPROXY_PANIC_PARTIAL:
281 status_str = "PARTIAL";
282 break;
283 case XNUPROXY_PANIC_COMPLETE:
284 status_str = "COMPLETE";
285 break;
286 default:
287 return;
288 }
289
290 panic_info->eph_panic_flags |= EMBEDDED_PANIC_HEADER_FLAG_EXCLAVE_PANIC;
291
292 paniclog_append_noflush("Exclaves additional info: STATUS: %s\n", status_str);
293 exclaves_append_panic_addl_info(ex_thread);
294
295 exclaves_append_panic_backtrace();
296 }
297
298 __attribute__((noinline, noreturn))
299 void
exclaves_panic_thread_wait(void)300 exclaves_panic_thread_wait(void)
301 {
302 assert_wait((event_t)&exclaves_panic_thread_wait_forever, THREAD_UNINT);
303 (void) thread_block(THREAD_CONTINUE_NULL);
304
305 /* NOT REACHABLE */
306 panic("Exclaves panic thread woken up");
307 }
308
309 void
handle_response_panic_buffer_address(pmap_paddr_t addr)310 handle_response_panic_buffer_address(pmap_paddr_t addr)
311 {
312 return copy_panic_buffer_pages(addr);
313 }
314
315 kern_return_t
exclaves_panic_thread_setup(void)316 exclaves_panic_thread_setup(void)
317 {
318 thread_t thread = THREAD_NULL;
319 kern_return_t kr = KERN_FAILURE;
320
321 kr = kernel_thread_start_priority(exclaves_xnuproxy_panic_thread, NULL,
322 BASEPRI_DEFAULT, &thread);
323 if (kr != KERN_SUCCESS) {
324 return kr;
325 }
326
327 thread_set_thread_name(thread, "EXCLAVES_PANIC_WAIT_THREAD");
328 thread_deallocate(thread);
329
330 return KERN_SUCCESS;
331 }
332
333 #endif /* CONFIG_ EXCLAVES */
334