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 #include <vm/pmap.h>
39
40 #include <xnuproxy/messages.h>
41 #include <xnuproxy/panic.h>
42
43 #include "exclaves_debug.h"
44 #include "exclaves_panic.h"
45
46 /* Use the new version of xnuproxy_msg_t. */
47 #define xnuproxy_msg_t xnuproxy_msg_new_t
48
49 #define EXCLAVES_PANIC_FOUR_CC_FORMAT "%c%c%c%c"
50 #define EXCLAVES_PANIC_FOUR_CC_CHARS(c) \
51 (((c) >> 24) & 0xFF), \
52 (((c) >> 16) & 0xFF), \
53 (((c) >> 8) & 0xFF), \
54 (((c) >> 0) & 0xFF)
55 #define EXCLAVE_PANIC_MESSAGE_MARKER "[Exclaves]"
56
57 // Adding 64 bytes here to accommodate the PC and LR in the panic string
58 #define EXCLAVES_PANIC_STRING_SIZE \
59 (sizeof(EXCLAVE_PANIC_MESSAGE_MARKER) + XNUPROXY_PANIC_MESSAGE_LEN + XNUPROXY_PANIC_NAME_BYTES + 64)
60
61 static char exclaves_panic_string[EXCLAVES_PANIC_STRING_SIZE];
62 static xnuproxy_panic_buffer_t *exclaves_panic_buffer;
63 static int exclaves_panic_thread_wait_forever;
64
65 static void
exclaves_xnu_proxy_panic_thread(void * arg __unused,wait_result_t wr __unused)66 exclaves_xnu_proxy_panic_thread(void *arg __unused, wait_result_t wr __unused)
67 {
68 kern_return_t kr = KERN_SUCCESS;
69 Exclaves_L4_Word_t spawned_scid = 0;
70 thread_t thread;
71
72 xnuproxy_msg_t msg = {
73 .cmd = XNUPROXY_CMD_PANIC_SETUP,
74 };
75
76 extern kern_return_t exclaves_xnu_proxy_send(xnuproxy_msg_t *,
77 Exclaves_L4_Word_t *);
78 kr = exclaves_xnu_proxy_send(&msg, &spawned_scid);
79 if (kr != KERN_SUCCESS) {
80 exclaves_debug_printf(show_errors,
81 "exclaves: panic thread init: xnu proxy send failed.");
82 return;
83 }
84
85 exclaves_panic_buffer = (xnuproxy_panic_buffer_t *)
86 phystokv(msg.cmd_panic_setup.response.physical_address);
87
88 thread = current_thread();
89 thread->th_exclaves_scheduling_context_id = spawned_scid;
90
91 while (1) {
92 extern kern_return_t exclaves_scheduler_resume_scheduling_context(Exclaves_L4_Word_t,
93 Exclaves_L4_Word_t *);
94 kr = exclaves_scheduler_resume_scheduling_context(spawned_scid, NULL);
95 assert3u(kr, ==, KERN_SUCCESS);
96 }
97 }
98
99 static void
exclaves_append_panic_backtrace(void)100 exclaves_append_panic_backtrace(void)
101 {
102 uuid_string_t uuid_string;
103 xnuproxy_panic_backtrace_word_t *words;
104
105 if ((exclaves_panic_buffer->panicked_thread.backtrace.frames >
106 XNUPROXY__PANIC_BACKTRACE_WORDS)) {
107 return;
108 }
109
110 words = exclaves_panic_buffer->backtrace.words;
111 paniclog_append_noflush("Exclaves backtrace:\n");
112 for (size_t i = 0; i < exclaves_panic_buffer->panicked_thread.backtrace.frames; i++) {
113 uuid_unparse_upper(
114 (const unsigned char *)exclaves_panic_buffer->backtrace.images[words[i].image].uuid,
115 uuid_string);
116 paniclog_append_noflush("\t\t%s 0x%016zx\n", uuid_string,
117 exclaves_panic_buffer->backtrace.words[i].offset);
118 }
119
120 paniclog_append_noflush("\n");
121 return;
122 }
123
124 static void
exclaves_append_panic_addl_info(xnuproxy_panicked_thread_t * ex_thread)125 exclaves_append_panic_addl_info(xnuproxy_panicked_thread_t *ex_thread)
126 {
127 char component_name[XNUPROXY_PANIC_NAME_BYTES] = {0};
128
129 strlcpy(component_name, ex_thread->component.name, sizeof(component_name));
130
131 paniclog_append_noflush(
132 "\t\tAddress space ID: 0x%llx\n"
133 "\t\tComponent:\n"
134 "\t\t\tName: %s\n"
135 "\t\t\tID: 0x%llx\n"
136 "\t\t\tSelector: 0x%llx\n"
137 "\t\tspace.component.endpoint.thread: " EXCLAVES_PANIC_FOUR_CC_FORMAT "."
138 EXCLAVES_PANIC_FOUR_CC_FORMAT "."
139 EXCLAVES_PANIC_FOUR_CC_FORMAT "."
140 EXCLAVES_PANIC_FOUR_CC_FORMAT "\n"
141 "\t\tThread Context:\n"
142 "\t\t\tAddress: 0x%zx\n"
143 "\t\t\tTSS Base: 0x%zx\n"
144 "\t\t\tIPC Buffer 0x%zx\n"
145 "\t\t\tSCID 0x%zx\n"
146 "\t\t\tECID: 0x%zx\n"
147 "\t\t\tEPID: 0x%zx\n"
148 "\t\t\tStack:\n"
149 "\t\t\t\tStart: 0x%zx\n"
150 "\t\t\t\tSize: 0x%zx\n"
151 "\t\t\t\tCall base: 0x%zx\n"
152 "\t\t\tRegisters:\n"
153 "\t\t\t\tLR: 0x%zx\n"
154 "\t\t\t\tPC: 0x%zx\n"
155 "\t\t\t\tSP: 0x%zx\n"
156 "\t\t\t\tCPSR: 0x%zx\n",
157 ex_thread->address_space_id, component_name,
158 ex_thread->component.numeric_id, ex_thread->component.selector,
159 EXCLAVES_PANIC_FOUR_CC_CHARS(ex_thread->four_cc.space),
160 EXCLAVES_PANIC_FOUR_CC_CHARS(ex_thread->four_cc.component),
161 EXCLAVES_PANIC_FOUR_CC_CHARS(ex_thread->four_cc.endpoint),
162 EXCLAVES_PANIC_FOUR_CC_CHARS(ex_thread->four_cc.thread),
163 ex_thread->thread.address, ex_thread->thread.tss_base,
164 ex_thread->thread.ipc_buffer, ex_thread->thread.scheduling_context_id,
165 ex_thread->thread.execution_context_id, ex_thread->thread.endpoint_id,
166 ex_thread->thread.stack.start, ex_thread->thread.stack.size,
167 ex_thread->thread.stack.call_base, ex_thread->thread.registers.lr,
168 ex_thread->thread.registers.pc, ex_thread->thread.registers.sp,
169 ex_thread->thread.registers.cpsr);
170 }
171
172 kern_return_t
exclaves_panic_get_string(char ** string)173 exclaves_panic_get_string(char **string)
174 {
175 uint32_t status = 0;
176 char component_name[XNUPROXY_PANIC_NAME_BYTES] = {0};
177
178 if (exclaves_panic_buffer == NULL) {
179 return KERN_FAILURE;
180 }
181
182 xnuproxy_panicked_thread_t *ex_thread =
183 &exclaves_panic_buffer->panicked_thread;
184
185 strlcpy(component_name, ex_thread->component.name, sizeof(component_name));
186
187 status = os_atomic_load(&ex_thread->status, seq_cst);
188 if (status == XNUPROXY_PANIC_UNSET) {
189 return KERN_FAILURE;
190 }
191
192 snprintf(exclaves_panic_string, sizeof(exclaves_panic_string),
193 "%s %s:%s at PC: 0x%zx, LR: 0x%zx", EXCLAVE_PANIC_MESSAGE_MARKER,
194 component_name, exclaves_panic_buffer->message,
195 ex_thread->thread.registers.pc, ex_thread->thread.registers.lr);
196 exclaves_panic_string[sizeof(exclaves_panic_string) - 1] = '\0';
197 *string = exclaves_panic_string;
198
199 return KERN_SUCCESS;
200 }
201
202 void
exclaves_panic_append_info(void)203 exclaves_panic_append_info(void)
204 {
205 uint32_t status = 0;
206 char *status_str;
207 if (exclaves_panic_buffer == NULL) {
208 return;
209 }
210
211 xnuproxy_panicked_thread_t *ex_thread =
212 &exclaves_panic_buffer->panicked_thread;
213
214 status = os_atomic_load(&ex_thread->status, seq_cst);
215
216 switch (status) {
217 case XNUPROXY_PANIC_PARTIAL:
218 status_str = "PARTIAL";
219 break;
220 case XNUPROXY_PANIC_COMPLETE:
221 status_str = "COMPLETE";
222 break;
223 default:
224 return;
225 }
226
227 panic_info->eph_panic_flags |= EMBEDDED_PANIC_HEADER_FLAG_EXCLAVE_PANIC;
228
229 paniclog_append_noflush("Exclaves additional info: STATUS: %s\n", status_str);
230 exclaves_append_panic_addl_info(ex_thread);
231
232 exclaves_append_panic_backtrace();
233 }
234
235 __attribute__((noinline, noreturn))
236 void
exclaves_panic_thread_wait(void)237 exclaves_panic_thread_wait(void)
238 {
239 assert_wait((event_t)&exclaves_panic_thread_wait_forever, THREAD_UNINT);
240 (void) thread_block(THREAD_CONTINUE_NULL);
241
242 /* NOT REACHABLE */
243 panic("Exclaves panic thread woken up");
244 }
245
246 kern_return_t
exclaves_panic_thread_setup(void)247 exclaves_panic_thread_setup(void)
248 {
249 thread_t thread = THREAD_NULL;
250 kern_return_t kr = KERN_FAILURE;
251
252 kr = kernel_thread_start_priority(exclaves_xnu_proxy_panic_thread, NULL,
253 BASEPRI_DEFAULT, &thread);
254 if (kr != KERN_SUCCESS) {
255 return kr;
256 }
257
258 thread_set_thread_name(thread, "EXCLAVES_PANIC_WAIT_THREAD");
259 thread_deallocate(thread);
260
261 return KERN_SUCCESS;
262 }
263
264 #endif /* CONFIG_ EXCLAVES */
265