1 // Copyright 2016 The Fuchsia Authors
2 // Copyright (c) 2014 Travis Geiselbrecht
3 //
4 // Use of this source code is governed by a MIT-style
5 // license that can be found in the LICENSE file or at
6 // https://opensource.org/licenses/MIT
7
8 #include <arch/arch_ops.h>
9 #include <arch/arm64.h>
10 #include <arch/arm64/exceptions.h>
11 #include <arch/exception.h>
12 #include <arch/user_copy.h>
13
14 #include <bits.h>
15 #include <debug.h>
16 #include <inttypes.h>
17
18 #include <kernel/interrupt.h>
19 #include <kernel/thread.h>
20
21 #include <platform.h>
22 #include <stdio.h>
23 #include <trace.h>
24 #include <vm/fault.h>
25 #include <vm/vm.h>
26
27 #include <lib/counters.h>
28 #include <lib/crashlog.h>
29
30 #include <zircon/syscalls/exception.h>
31 #include <zircon/types.h>
32
33 #define LOCAL_TRACE 0
34
35 #define DFSC_ALIGNMENT_FAULT 0b100001
36
dump_iframe(const struct arm64_iframe_long * iframe)37 static void dump_iframe(const struct arm64_iframe_long* iframe) {
38 printf("iframe %p:\n", iframe);
39 printf("x0 %#18" PRIx64 " x1 %#18" PRIx64 " x2 %#18" PRIx64 " x3 %#18" PRIx64 "\n", iframe->r[0], iframe->r[1], iframe->r[2], iframe->r[3]);
40 printf("x4 %#18" PRIx64 " x5 %#18" PRIx64 " x6 %#18" PRIx64 " x7 %#18" PRIx64 "\n", iframe->r[4], iframe->r[5], iframe->r[6], iframe->r[7]);
41 printf("x8 %#18" PRIx64 " x9 %#18" PRIx64 " x10 %#18" PRIx64 " x11 %#18" PRIx64 "\n", iframe->r[8], iframe->r[9], iframe->r[10], iframe->r[11]);
42 printf("x12 %#18" PRIx64 " x13 %#18" PRIx64 " x14 %#18" PRIx64 " x15 %#18" PRIx64 "\n", iframe->r[12], iframe->r[13], iframe->r[14], iframe->r[15]);
43 printf("x16 %#18" PRIx64 " x17 %#18" PRIx64 " x18 %#18" PRIx64 " x19 %#18" PRIx64 "\n", iframe->r[16], iframe->r[17], iframe->r[18], iframe->r[19]);
44 printf("x20 %#18" PRIx64 " x21 %#18" PRIx64 " x22 %#18" PRIx64 " x23 %#18" PRIx64 "\n", iframe->r[20], iframe->r[21], iframe->r[22], iframe->r[23]);
45 printf("x24 %#18" PRIx64 " x25 %#18" PRIx64 " x26 %#18" PRIx64 " x27 %#18" PRIx64 "\n", iframe->r[24], iframe->r[25], iframe->r[26], iframe->r[27]);
46 printf("x28 %#18" PRIx64 " x29 %#18" PRIx64 " lr %#18" PRIx64 " usp %#18" PRIx64 "\n", iframe->r[28], iframe->r[29], iframe->lr, iframe->usp);
47 printf("elr %#18" PRIx64 "\n", iframe->elr);
48 printf("spsr %#18" PRIx64 "\n", iframe->spsr);
49 }
50
51 KCOUNTER(exceptions_brkpt, "kernel.exceptions.breakpoint");
52 KCOUNTER(exceptions_fpu, "kernel.exceptions.fpu");
53 KCOUNTER(exceptions_page, "kernel.exceptions.page_fault");
54 KCOUNTER(exceptions_irq, "kernel.exceptions.irq");
55 KCOUNTER(exceptions_unhandled, "kernel.exceptions.unhandled");
56 KCOUNTER(exceptions_user, "kernel.exceptions.user");
57 KCOUNTER(exceptions_unknown, "kernel.exceptions.unknown");
58
try_dispatch_user_data_fault_exception(zx_excp_type_t type,struct arm64_iframe_long * iframe,uint32_t esr,uint64_t far)59 static zx_status_t try_dispatch_user_data_fault_exception(
60 zx_excp_type_t type, struct arm64_iframe_long* iframe,
61 uint32_t esr, uint64_t far) {
62 thread_t* thread = get_current_thread();
63 arch_exception_context_t context = {};
64 DEBUG_ASSERT(iframe != nullptr);
65 context.frame = iframe;
66 context.esr = esr;
67 context.far = far;
68
69 arch_enable_ints();
70 DEBUG_ASSERT(thread->arch.suspended_general_regs == nullptr);
71 thread->arch.suspended_general_regs = iframe;
72 zx_status_t status = dispatch_user_exception(type, &context);
73 thread->arch.suspended_general_regs = nullptr;
74 arch_disable_ints();
75 return status;
76 }
77
try_dispatch_user_exception(zx_excp_type_t type,struct arm64_iframe_long * iframe,uint32_t esr)78 static zx_status_t try_dispatch_user_exception(
79 zx_excp_type_t type, struct arm64_iframe_long* iframe, uint32_t esr) {
80 return try_dispatch_user_data_fault_exception(type, iframe, esr, 0);
81 }
82
exception_die(struct arm64_iframe_long * iframe,uint32_t esr)83 __NO_RETURN static void exception_die(struct arm64_iframe_long* iframe, uint32_t esr) {
84 platform_panic_start();
85
86 uint32_t ec = BITS_SHIFT(esr, 31, 26);
87 uint32_t il = BIT(esr, 25);
88 uint32_t iss = BITS(esr, 24, 0);
89
90 /* fatal exception, die here */
91 printf("ESR 0x%x: ec 0x%x, il 0x%x, iss 0x%x\n", esr, ec, il, iss);
92 dump_iframe(iframe);
93 crashlog.iframe = iframe;
94
95 platform_halt(HALT_ACTION_HALT, HALT_REASON_SW_PANIC);
96 }
97
arm64_unknown_handler(struct arm64_iframe_long * iframe,uint exception_flags,uint32_t esr)98 static void arm64_unknown_handler(struct arm64_iframe_long* iframe, uint exception_flags,
99 uint32_t esr) {
100 /* this is for a lot of reasons, but most of them are undefined instructions */
101 if (unlikely((exception_flags & ARM64_EXCEPTION_FLAG_LOWER_EL) == 0)) {
102 /* trapped inside the kernel, this is bad */
103 printf("unknown exception in kernel: PC at %#" PRIx64 "\n", iframe->elr);
104 exception_die(iframe, esr);
105 }
106 try_dispatch_user_exception(ZX_EXCP_UNDEFINED_INSTRUCTION, iframe, esr);
107 }
108
arm64_brk_handler(struct arm64_iframe_long * iframe,uint exception_flags,uint32_t esr)109 static void arm64_brk_handler(struct arm64_iframe_long* iframe, uint exception_flags,
110 uint32_t esr) {
111 if (unlikely((exception_flags & ARM64_EXCEPTION_FLAG_LOWER_EL) == 0)) {
112 /* trapped inside the kernel, this is bad */
113 printf("BRK in kernel: PC at %#" PRIx64 "\n", iframe->elr);
114 exception_die(iframe, esr);
115 }
116 try_dispatch_user_exception(ZX_EXCP_SW_BREAKPOINT, iframe, esr);
117 }
118
arm64_hw_breakpoint_handler(struct arm64_iframe_long * iframe,uint exception_flags,uint32_t esr)119 static void arm64_hw_breakpoint_handler(struct arm64_iframe_long* iframe, uint exception_flags,
120 uint32_t esr) {
121 if (unlikely((exception_flags & ARM64_EXCEPTION_FLAG_LOWER_EL) == 0)) {
122 /* trapped inside the kernel, this is bad */
123 printf("HW breakpoint in kernel: PC at %#" PRIx64 "\n", iframe->elr);
124 exception_die(iframe, esr);
125 }
126
127 // We don't need to save the debug state because it doesn't change by an exception. The only
128 // way to change the debug state is through the thread write syscall.
129
130 // NOTE: ARM64 Doesn't provide a good way to comunicate exception status (without exposing ESR
131 // to userspace). This means a debugger will have to compare the registers with the PC
132 // on the exceptions to find out which breakpoint triggered the exception.
133 try_dispatch_user_exception(ZX_EXCP_HW_BREAKPOINT, iframe, esr);
134 }
135
arm64_step_handler(struct arm64_iframe_long * iframe,uint exception_flags,uint32_t esr)136 static void arm64_step_handler(struct arm64_iframe_long* iframe, uint exception_flags,
137 uint32_t esr) {
138 if (unlikely((exception_flags & ARM64_EXCEPTION_FLAG_LOWER_EL) == 0)) {
139 /* trapped inside the kernel, this is bad */
140 printf("software step in kernel: PC at %#" PRIx64 "\n", iframe->elr);
141 exception_die(iframe, esr);
142 }
143 // TODO(ZX-3037): Is it worth separating this into two separate exceptions?
144 try_dispatch_user_exception(ZX_EXCP_HW_BREAKPOINT, iframe, esr);
145 }
146
arm64_fpu_handler(struct arm64_iframe_long * iframe,uint exception_flags,uint32_t esr)147 static void arm64_fpu_handler(struct arm64_iframe_long* iframe, uint exception_flags,
148 uint32_t esr) {
149 if (unlikely((exception_flags & ARM64_EXCEPTION_FLAG_LOWER_EL) == 0)) {
150 /* we trapped a floating point instruction inside our own EL, this is bad */
151 printf("invalid fpu use in kernel: PC at %#" PRIx64 "\n",
152 iframe->elr);
153 exception_die(iframe, esr);
154 }
155 arm64_fpu_exception(iframe, exception_flags);
156 }
157
arm64_instruction_abort_handler(struct arm64_iframe_long * iframe,uint exception_flags,uint32_t esr)158 static void arm64_instruction_abort_handler(struct arm64_iframe_long* iframe, uint exception_flags,
159 uint32_t esr) {
160 /* read the FAR register */
161 uint64_t far = __arm_rsr64("far_el1");
162 uint32_t ec = BITS_SHIFT(esr, 31, 26);
163 uint32_t iss = BITS(esr, 24, 0);
164 bool is_user = !BIT(ec, 0);
165
166 uint pf_flags = VMM_PF_FLAG_INSTRUCTION;
167 pf_flags |= is_user ? VMM_PF_FLAG_USER : 0;
168 /* Check if this was not permission fault */
169 if ((iss & 0b111100) != 0b001100) {
170 pf_flags |= VMM_PF_FLAG_NOT_PRESENT;
171 }
172
173 LTRACEF("instruction abort: PC at %#" PRIx64
174 ", is_user %d, FAR %" PRIx64 ", esr 0x%x, iss 0x%x\n",
175 iframe->elr, is_user, far, esr, iss);
176
177 arch_enable_ints();
178 kcounter_add(exceptions_page, 1);
179 CPU_STATS_INC(page_faults);
180 zx_status_t err = vmm_page_fault_handler(far, pf_flags);
181 arch_disable_ints();
182 if (err >= 0)
183 return;
184
185 // If this is from user space, let the user exception handler
186 // get a shot at it.
187 if (is_user) {
188 kcounter_add(exceptions_user, 1);
189 if (try_dispatch_user_data_fault_exception(ZX_EXCP_FATAL_PAGE_FAULT, iframe, esr, far) == ZX_OK)
190 return;
191 }
192
193 printf("instruction abort: PC at %#" PRIx64 ", is_user %d, FAR %" PRIx64 "\n",
194 iframe->elr, is_user, far);
195 exception_die(iframe, esr);
196 }
197
arm64_data_abort_handler(struct arm64_iframe_long * iframe,uint exception_flags,uint32_t esr)198 static void arm64_data_abort_handler(struct arm64_iframe_long* iframe, uint exception_flags,
199 uint32_t esr) {
200 /* read the FAR register */
201 uint64_t far = __arm_rsr64("far_el1");
202 uint32_t ec = BITS_SHIFT(esr, 31, 26);
203 uint32_t iss = BITS(esr, 24, 0);
204 bool is_user = !BIT(ec, 0);
205 bool WnR = BIT(iss, 6); // Write not Read
206 bool CM = BIT(iss, 8); // cache maintenance op
207
208 uint pf_flags = 0;
209 // if it was marked Write but the cache maintenance bit was set, treat it as read
210 pf_flags |= (WnR && !CM) ? VMM_PF_FLAG_WRITE : 0;
211 pf_flags |= is_user ? VMM_PF_FLAG_USER : 0;
212 /* Check if this was not permission fault */
213 if ((iss & 0b111100) != 0b001100) {
214 pf_flags |= VMM_PF_FLAG_NOT_PRESENT;
215 }
216
217 LTRACEF("data fault: PC at %#" PRIx64
218 ", is_user %d, FAR %#" PRIx64 ", esr 0x%x, iss 0x%x\n",
219 iframe->elr, is_user, far, esr, iss);
220
221 uint32_t dfsc = BITS(iss, 5, 0);
222 if (likely(dfsc != DFSC_ALIGNMENT_FAULT)) {
223 arch_enable_ints();
224 kcounter_add(exceptions_page, 1);
225 zx_status_t err = vmm_page_fault_handler(far, pf_flags);
226 arch_disable_ints();
227 if (err >= 0) {
228 return;
229 }
230 }
231
232 // Check if the current thread was expecting a data fault and
233 // we should return to its handler.
234 thread_t* thr = get_current_thread();
235 if (thr->arch.data_fault_resume != NULL && is_user_address(far)) {
236 iframe->elr = (uintptr_t)thr->arch.data_fault_resume;
237 return;
238 }
239
240 // If this is from user space, let the user exception handler
241 // get a shot at it.
242 if (is_user) {
243 kcounter_add(exceptions_user, 1);
244 zx_excp_type_t excp_type = ZX_EXCP_FATAL_PAGE_FAULT;
245 if (unlikely(dfsc == DFSC_ALIGNMENT_FAULT)) {
246 excp_type = ZX_EXCP_UNALIGNED_ACCESS;
247 }
248 if (try_dispatch_user_data_fault_exception(excp_type, iframe, esr, far) == ZX_OK)
249 return;
250 }
251
252 /* decode the iss */
253 if (BIT(iss, 24)) { /* ISV bit */
254 printf("data fault: PC at %#" PRIx64
255 ", FAR %#" PRIx64 ", iss %#x (DFSC %#x)\n",
256 iframe->elr, far, iss, BITS(iss, 5, 0));
257 } else {
258 printf("data fault: PC at %#" PRIx64
259 ", FAR %#" PRIx64 ", iss 0x%x\n",
260 iframe->elr, far, iss);
261 }
262
263 exception_die(iframe, esr);
264 }
265
arm64_restore_percpu_pointer()266 static inline void arm64_restore_percpu_pointer() {
267 arm64_write_percpu_ptr(get_current_thread()->arch.current_percpu_ptr);
268 }
269
270 /* called from assembly */
arm64_sync_exception(struct arm64_iframe_long * iframe,uint exception_flags,uint32_t esr)271 extern "C" void arm64_sync_exception(
272 struct arm64_iframe_long* iframe, uint exception_flags, uint32_t esr) {
273 uint32_t ec = BITS_SHIFT(esr, 31, 26);
274
275 if (exception_flags & ARM64_EXCEPTION_FLAG_LOWER_EL) {
276 // if we came from a lower level, restore the per cpu pointer
277 arm64_restore_percpu_pointer();
278 }
279
280 switch (ec) {
281 case 0b000000: /* unknown reason */
282 kcounter_add(exceptions_unknown, 1);
283 arm64_unknown_handler(iframe, exception_flags, esr);
284 break;
285 case 0b111000: /* BRK from arm32 */
286 case 0b111100: /* BRK from arm64 */
287 kcounter_add(exceptions_brkpt, 1);
288 arm64_brk_handler(iframe, exception_flags, esr);
289 break;
290 case 0b000111: /* floating point */
291 kcounter_add(exceptions_fpu, 1);
292 arm64_fpu_handler(iframe, exception_flags, esr);
293 break;
294 case 0b010001: /* syscall from arm32 */
295 case 0b010101: /* syscall from arm64 */
296 printf("syscalls should be handled in assembly\n");
297 exception_die(iframe, esr);
298 break;
299 case 0b100000: /* instruction abort from lower level */
300 case 0b100001: /* instruction abort from same level */
301 arm64_instruction_abort_handler(iframe, exception_flags, esr);
302 break;
303 case 0b100100: /* data abort from lower level */
304 case 0b100101: /* data abort from same level */
305 arm64_data_abort_handler(iframe, exception_flags, esr);
306 break;
307 case 0b110000: /* HW breakpoint from a lower level */
308 case 0b110001: /* HW breakpoint from same level */
309 arm64_hw_breakpoint_handler(iframe, exception_flags, esr);
310 break;
311 case 0b110010: /* software step from lower level */
312 case 0b110011: /* software step from same level */
313 arm64_step_handler(iframe, exception_flags, esr);
314 break;
315 default: {
316 /* TODO: properly decode more of these */
317 if (unlikely((exception_flags & ARM64_EXCEPTION_FLAG_LOWER_EL) == 0)) {
318 /* trapped inside the kernel, this is bad */
319 printf("unhandled exception in kernel: PC at %#" PRIx64 "\n", iframe->elr);
320 exception_die(iframe, esr);
321 }
322 /* let the user exception handler get a shot at it */
323 kcounter_add(exceptions_unhandled, 1);
324 if (try_dispatch_user_exception(ZX_EXCP_GENERAL, iframe, esr) == ZX_OK)
325 break;
326 printf("unhandled synchronous exception\n");
327 exception_die(iframe, esr);
328 }
329 }
330
331 /* if we came from user space, check to see if we have any signals to handle */
332 if (unlikely(exception_flags & ARM64_EXCEPTION_FLAG_LOWER_EL)) {
333 /* in the case of receiving a kill signal, this function may not return,
334 * but the scheduler would have been invoked so it's fine.
335 */
336 arm64_thread_process_pending_signals(iframe);
337 }
338
339 /* if we're returning to kernel space, make sure we restore the correct x18 */
340 if ((exception_flags & ARM64_EXCEPTION_FLAG_LOWER_EL) == 0) {
341 iframe->r[18] = (uint64_t)arm64_read_percpu_ptr();
342 }
343 }
344
345 /* called from assembly */
arm64_irq(struct arm64_iframe_short * iframe,uint exception_flags)346 extern "C" uint32_t arm64_irq(struct arm64_iframe_short* iframe, uint exception_flags) {
347 if (exception_flags & ARM64_EXCEPTION_FLAG_LOWER_EL) {
348 // if we came from a lower level, restore the per cpu pointer
349 arm64_restore_percpu_pointer();
350 }
351
352 LTRACEF("iframe %p, flags 0x%x\n", iframe, exception_flags);
353
354 int_handler_saved_state_t state;
355 int_handler_start(&state);
356
357 kcounter_add(exceptions_irq, 1);
358 platform_irq(iframe);
359
360 bool do_preempt = int_handler_finish(&state);
361
362 /* if we came from user space, check to see if we have any signals to handle */
363 if (unlikely(exception_flags & ARM64_EXCEPTION_FLAG_LOWER_EL)) {
364 uint32_t exit_flags = 0;
365 if (thread_is_signaled(get_current_thread()))
366 exit_flags |= ARM64_IRQ_EXIT_THREAD_SIGNALED;
367 if (do_preempt)
368 exit_flags |= ARM64_IRQ_EXIT_RESCHEDULE;
369 return exit_flags;
370 }
371
372 /* preempt the thread if the interrupt has signaled it */
373 if (do_preempt)
374 thread_preempt();
375
376 /* if we're returning to kernel space, make sure we restore the correct x18 */
377 if ((exception_flags & ARM64_EXCEPTION_FLAG_LOWER_EL) == 0) {
378 iframe->r[18] = (uint64_t)arm64_read_percpu_ptr();
379 }
380
381 return 0;
382 }
383
384 /* called from assembly */
arm64_finish_user_irq(uint32_t exit_flags,struct arm64_iframe_long * iframe)385 extern "C" void arm64_finish_user_irq(uint32_t exit_flags, struct arm64_iframe_long* iframe) {
386 // we came from a lower level, so restore the per cpu pointer
387 arm64_restore_percpu_pointer();
388
389 /* in the case of receiving a kill signal, this function may not return,
390 * but the scheduler would have been invoked so it's fine.
391 */
392 if (unlikely(exit_flags & ARM64_IRQ_EXIT_THREAD_SIGNALED)) {
393 DEBUG_ASSERT(iframe != nullptr);
394 arm64_thread_process_pending_signals(iframe);
395 }
396
397 /* preempt the thread if the interrupt has signaled it */
398 if (exit_flags & ARM64_IRQ_EXIT_RESCHEDULE)
399 thread_preempt();
400 }
401
402 /* called from assembly */
arm64_invalid_exception(struct arm64_iframe_long * iframe,unsigned int which)403 extern "C" void arm64_invalid_exception(struct arm64_iframe_long* iframe, unsigned int which) {
404 // restore the percpu pointer (x18) unconditionally
405 arm64_restore_percpu_pointer();
406
407 printf("invalid exception, which 0x%x\n", which);
408 dump_iframe(iframe);
409
410 platform_halt(HALT_ACTION_HALT, HALT_REASON_SW_PANIC);
411 }
412
413 /* called from assembly */
arm64_thread_process_pending_signals(struct arm64_iframe_long * iframe)414 extern "C" void arm64_thread_process_pending_signals(struct arm64_iframe_long* iframe) {
415 thread_t* thread = get_current_thread();
416 DEBUG_ASSERT(iframe != nullptr);
417 DEBUG_ASSERT(thread->arch.suspended_general_regs == nullptr);
418
419 thread->arch.suspended_general_regs = iframe;
420 thread_process_pending_signals();
421 thread->arch.suspended_general_regs = nullptr;
422 }
423
arch_dump_exception_context(const arch_exception_context_t * context)424 void arch_dump_exception_context(const arch_exception_context_t* context) {
425 uint32_t ec = BITS_SHIFT(context->esr, 31, 26);
426 uint32_t iss = BITS(context->esr, 24, 0);
427
428 switch (ec) {
429 case 0b100000: /* instruction abort from lower level */
430 case 0b100001: /* instruction abort from same level */
431 printf("instruction abort: PC at %#" PRIx64
432 ", address %#" PRIx64 " IFSC %#x %s\n",
433 context->frame->elr, context->far,
434 BITS(context->esr, 5, 0),
435 BIT(ec, 0) ? "" : "user ");
436
437 break;
438 case 0b100100: /* data abort from lower level */
439 case 0b100101: /* data abort from same level */
440 printf("data abort: PC at %#" PRIx64
441 ", address %#" PRIx64 " %s%s\n",
442 context->frame->elr, context->far,
443 BIT(ec, 0) ? "" : "user ",
444 BIT(iss, 6) ? "write" : "read");
445 }
446
447 dump_iframe(context->frame);
448
449 // try to dump the user stack
450 if (is_user_address(context->frame->usp)) {
451 uint8_t buf[256];
452 if (arch_copy_from_user(buf, (void*)context->frame->usp, sizeof(buf)) == ZX_OK) {
453 printf("bottom of user stack at 0x%lx:\n", (vaddr_t)context->frame->usp);
454 hexdump_ex(buf, sizeof(buf), context->frame->usp);
455 }
456 }
457 }
458
arch_fill_in_exception_context(const arch_exception_context_t * arch_context,zx_exception_report_t * report)459 void arch_fill_in_exception_context(const arch_exception_context_t* arch_context, zx_exception_report_t* report) {
460 zx_exception_context_t* zx_context = &report->context;
461
462 zx_context->arch.u.arm_64.esr = arch_context->esr;
463
464 // If there was a fatal page fault, fill in the address that caused the fault.
465 if (ZX_EXCP_FATAL_PAGE_FAULT == report->header.type) {
466 zx_context->arch.u.arm_64.far = arch_context->far;
467 } else {
468 zx_context->arch.u.arm_64.far = 0;
469 }
470 }
471
arch_dispatch_user_policy_exception(void)472 zx_status_t arch_dispatch_user_policy_exception(void) {
473 struct arm64_iframe_long frame = {};
474 arch_exception_context_t context = {};
475 context.frame = &frame;
476 return dispatch_user_exception(ZX_EXCP_POLICY_ERROR, &context);
477 }
478