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