1 /*
2  * Copyright (c) 2008-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 <lk/debug.h>
9 #include <lk/bits.h>
10 #include <arch/arm.h>
11 #include <kernel/thread.h>
12 #include <platform.h>
13 
14 struct fault_handler_table_entry {
15     uint32_t pc;
16     uint32_t fault_handler;
17 };
18 
19 extern struct fault_handler_table_entry __fault_handler_table_start[];
20 extern struct fault_handler_table_entry __fault_handler_table_end[];
21 
dump_mode_regs(uint32_t spsr,uint32_t svc_r13,uint32_t svc_r14)22 static void dump_mode_regs(uint32_t spsr, uint32_t svc_r13, uint32_t svc_r14) {
23     struct arm_mode_regs regs;
24     arm_save_mode_regs(&regs);
25 
26     dprintf(CRITICAL, "%c%s r13 0x%08x r14 0x%08x\n", ((spsr & CPSR_MODE_MASK) == CPSR_MODE_USR) ? '*' : ' ', "usr", regs.usr_r13, regs.usr_r14);
27     dprintf(CRITICAL, "%c%s r13 0x%08x r14 0x%08x\n", ((spsr & CPSR_MODE_MASK) == CPSR_MODE_FIQ) ? '*' : ' ', "fiq", regs.fiq_r13, regs.fiq_r14);
28     dprintf(CRITICAL, "%c%s r13 0x%08x r14 0x%08x\n", ((spsr & CPSR_MODE_MASK) == CPSR_MODE_IRQ) ? '*' : ' ', "irq", regs.irq_r13, regs.irq_r14);
29     dprintf(CRITICAL, "%c%s r13 0x%08x r14 0x%08x\n", 'a', "svc", regs.svc_r13, regs.svc_r14);
30     dprintf(CRITICAL, "%c%s r13 0x%08x r14 0x%08x\n", ((spsr & CPSR_MODE_MASK) == CPSR_MODE_SVC) ? '*' : ' ', "svc", svc_r13, svc_r14);
31     dprintf(CRITICAL, "%c%s r13 0x%08x r14 0x%08x\n", ((spsr & CPSR_MODE_MASK) == CPSR_MODE_UND) ? '*' : ' ', "und", regs.und_r13, regs.und_r14);
32     dprintf(CRITICAL, "%c%s r13 0x%08x r14 0x%08x\n", ((spsr & CPSR_MODE_MASK) == CPSR_MODE_SYS) ? '*' : ' ', "sys", regs.sys_r13, regs.sys_r14);
33 
34     // dump the bottom of the current stack
35     addr_t stack;
36     switch (spsr & CPSR_MODE_MASK) {
37         case CPSR_MODE_FIQ:
38             stack = regs.fiq_r13;
39             break;
40         case CPSR_MODE_IRQ:
41             stack = regs.irq_r13;
42             break;
43         case CPSR_MODE_SVC:
44             stack = svc_r13;
45             break;
46         case CPSR_MODE_UND:
47             stack = regs.und_r13;
48             break;
49         case CPSR_MODE_SYS:
50             stack = regs.sys_r13;
51             break;
52         default:
53             stack = 0;
54     }
55 
56     if (stack != 0) {
57         dprintf(CRITICAL, "bottom of stack at 0x%08x:\n", (unsigned int)stack);
58         hexdump((void *)stack, 128);
59     }
60 }
61 
dump_fault_frame(struct arm_fault_frame * frame)62 static void dump_fault_frame(struct arm_fault_frame *frame) {
63     struct thread *current_thread = get_current_thread();
64 
65     dprintf(CRITICAL, "current_thread %p, name %s\n",
66             current_thread, current_thread ? current_thread->name : "");
67 
68     dprintf(CRITICAL, "r0  0x%08x r1  0x%08x r2  0x%08x r3  0x%08x\n", frame->r[0], frame->r[1], frame->r[2], frame->r[3]);
69     dprintf(CRITICAL, "r4  0x%08x r5  0x%08x r6  0x%08x r7  0x%08x\n", frame->r[4], frame->r[5], frame->r[6], frame->r[7]);
70     dprintf(CRITICAL, "r8  0x%08x r9  0x%08x r10 0x%08x r11 0x%08x\n", frame->r[8], frame->r[9], frame->r[10], frame->r[11]);
71     dprintf(CRITICAL, "r12 0x%08x usp 0x%08x ulr 0x%08x pc  0x%08x\n", frame->r[12], frame->usp, frame->ulr, frame->pc);
72     dprintf(CRITICAL, "spsr 0x%08x\n", frame->spsr);
73 
74     dump_mode_regs(frame->spsr, (uintptr_t)(frame + 1), frame->lr);
75 }
76 
dump_iframe(struct arm_iframe * frame)77 static void dump_iframe(struct arm_iframe *frame) {
78     dprintf(CRITICAL, "r0  0x%08x r1  0x%08x r2  0x%08x r3  0x%08x\n", frame->r0, frame->r1, frame->r2, frame->r3);
79     dprintf(CRITICAL, "r12 0x%08x usp 0x%08x ulr 0x%08x pc  0x%08x\n", frame->r12, frame->usp, frame->ulr, frame->pc);
80     dprintf(CRITICAL, "spsr 0x%08x\n", frame->spsr);
81 
82     dump_mode_regs(frame->spsr, (uintptr_t)(frame + 1), frame->lr);
83 }
84 
exception_die(struct arm_fault_frame * frame,const char * msg)85 static void exception_die(struct arm_fault_frame *frame, const char *msg) {
86     dprintf(CRITICAL, msg);
87     dump_fault_frame(frame);
88 
89     platform_halt(HALT_ACTION_HALT, HALT_REASON_SW_PANIC);
90     for (;;);
91 }
92 
exception_die_iframe(struct arm_iframe * frame,const char * msg)93 static void exception_die_iframe(struct arm_iframe *frame, const char *msg) {
94     dprintf(CRITICAL, msg);
95     dump_iframe(frame);
96 
97     platform_halt(HALT_ACTION_HALT, HALT_REASON_SW_PANIC);
98     for (;;);
99 }
100 
101 void arm_syscall_handler(struct arm_fault_frame *frame);
arm_syscall_handler(struct arm_fault_frame * frame)102 __WEAK void arm_syscall_handler(struct arm_fault_frame *frame) {
103     exception_die(frame, "unhandled syscall, halting\n");
104 }
105 
106 void arm_undefined_handler(struct arm_iframe *frame);
arm_undefined_handler(struct arm_iframe * frame)107 void arm_undefined_handler(struct arm_iframe *frame) {
108     /* look at the undefined instruction, figure out if it's something we can handle */
109     bool in_thumb = frame->spsr & (1<<5);
110     if (in_thumb) {
111         frame->pc -= 2;
112     } else {
113         frame->pc -= 4;
114     }
115 
116     __UNUSED uint32_t opcode = *(uint32_t *)frame->pc;
117     //dprintf(CRITICAL, "undefined opcode 0x%x\n", opcode);
118 
119 #if ARM_WITH_VFP
120     if (in_thumb) {
121         /* look for a 32bit thumb instruction */
122         if (opcode & 0x0000e800) {
123             /* swap the 16bit words */
124             opcode = (opcode >> 16) | (opcode << 16);
125         }
126 
127         if (((opcode & 0xec000e00) == 0xec000a00) || // vfp
128                 ((opcode & 0xef000000) == 0xef000000) || // advanced simd data processing
129                 ((opcode & 0xff100000) == 0xf9000000)) { // VLD
130 
131             //dprintf(CRITICAL, "vfp/neon thumb instruction 0x%08x at 0x%x\n", opcode, frame->pc);
132             goto fpu;
133         }
134     } else {
135         /* look for arm vfp/neon coprocessor instructions */
136         if (((opcode & 0x0c000e00) == 0x0c000a00) || // vfp
137                 ((opcode & 0xfe000000) == 0xf2000000) || // advanced simd data processing
138                 ((opcode & 0xff100000) == 0xf4000000)) { // VLD
139             //dprintf(CRITICAL, "vfp/neon arm instruction 0x%08x at 0x%x\n", opcode, frame->pc);
140             goto fpu;
141         }
142     }
143 #endif
144 
145     exception_die_iframe(frame, "undefined abort, halting\n");
146     return;
147 
148 #if ARM_WITH_VFP
149 fpu:
150     arm_fpu_undefined_instruction(frame);
151 #endif
152 }
153 
154 void arm_data_abort_handler(struct arm_fault_frame *frame);
arm_data_abort_handler(struct arm_fault_frame * frame)155 void arm_data_abort_handler(struct arm_fault_frame *frame) {
156     struct fault_handler_table_entry *fault_handler;
157     uint32_t fsr = arm_read_dfsr();
158     uint32_t far = arm_read_dfar();
159 
160     uint32_t fault_status = (BIT(fsr, 10) ? (1<<4) : 0) |  BITS(fsr, 3, 0);
161 
162     for (fault_handler = __fault_handler_table_start; fault_handler < __fault_handler_table_end; fault_handler++) {
163         if (fault_handler->pc == frame->pc) {
164             frame->pc = fault_handler->fault_handler;
165             return;
166         }
167     }
168 
169     dprintf(CRITICAL, "\n\ncpu %u data abort, ", arch_curr_cpu_num());
170     bool write = !!BIT(fsr, 11);
171 
172     /* decode the fault status (from table B3-23) */
173     switch (fault_status) {
174         case 0b00001: // alignment fault
175             dprintf(CRITICAL, "alignment fault on %s\n", write ? "write" : "read");
176             break;
177         case 0b00101:
178         case 0b00111: // translation fault
179             dprintf(CRITICAL, "translation fault on %s\n", write ? "write" : "read");
180             break;
181         case 0b00011:
182         case 0b00110: // access flag fault
183             dprintf(CRITICAL, "access flag fault on %s\n", write ? "write" : "read");
184             break;
185         case 0b01001:
186         case 0b01011: // domain fault
187             dprintf(CRITICAL, "domain fault, domain %lu\n", BITS_SHIFT(fsr, 7, 4));
188             break;
189         case 0b01101:
190         case 0b01111: // permission fault
191             dprintf(CRITICAL, "permission fault on %s\n", write ? "write" : "read");
192             break;
193         case 0b00010: // debug event
194             dprintf(CRITICAL, "debug event\n");
195             break;
196         case 0b01000: // synchronous external abort
197             dprintf(CRITICAL, "synchronous external abort on %s\n", write ? "write" : "read");
198             break;
199         case 0b10110: // asynchronous external abort
200             dprintf(CRITICAL, "asynchronous external abort on %s\n", write ? "write" : "read");
201             break;
202         case 0b10000: // TLB conflict event
203         case 0b11001: // synchronous parity error on memory access
204         case 0b00100: // fault on instruction cache maintenance
205         case 0b01100: // synchronous external abort on translation table walk
206         case 0b01110: //    "
207         case 0b11100: // synchronous parity error on translation table walk
208         case 0b11110: //    "
209         case 0b11000: // asynchronous parity error on memory access
210         default:
211             dprintf(CRITICAL, "unhandled fault\n");
212             ;
213     }
214 
215     dprintf(CRITICAL, "DFAR 0x%x (fault address)\n", far);
216     dprintf(CRITICAL, "DFSR 0x%x (fault status register)\n", fsr);
217 
218     exception_die(frame, "halting\n");
219 }
220 
221 void arm_prefetch_abort_handler(struct arm_fault_frame *frame);
arm_prefetch_abort_handler(struct arm_fault_frame * frame)222 void arm_prefetch_abort_handler(struct arm_fault_frame *frame) {
223     uint32_t fsr = arm_read_ifsr();
224     uint32_t far = arm_read_ifar();
225 
226     uint32_t fault_status = (BIT(fsr, 10) ? (1<<4) : 0) |  BITS(fsr, 3, 0);
227 
228     dprintf(CRITICAL, "\n\ncpu %u prefetch abort, ", arch_curr_cpu_num());
229 
230     /* decode the fault status (from table B3-23) */
231     switch (fault_status) {
232         case 0b00001: // alignment fault
233             dprintf(CRITICAL, "alignment fault\n");
234             break;
235         case 0b00101:
236         case 0b00111: // translation fault
237             dprintf(CRITICAL, "translation fault\n");
238             break;
239         case 0b00011:
240         case 0b00110: // access flag fault
241             dprintf(CRITICAL, "access flag fault\n");
242             break;
243         case 0b01001:
244         case 0b01011: // domain fault
245             dprintf(CRITICAL, "domain fault, domain %lu\n", BITS_SHIFT(fsr, 7, 4));
246             break;
247         case 0b01101:
248         case 0b01111: // permission fault
249             dprintf(CRITICAL, "permission fault\n");
250             break;
251         case 0b00010: // debug event
252             dprintf(CRITICAL, "debug event\n");
253             break;
254         case 0b01000: // synchronous external abort
255             dprintf(CRITICAL, "synchronous external abort\n");
256             break;
257         case 0b10110: // asynchronous external abort
258             dprintf(CRITICAL, "asynchronous external abort\n");
259             break;
260         case 0b10000: // TLB conflict event
261         case 0b11001: // synchronous parity error on memory access
262         case 0b00100: // fault on instruction cache maintenance
263         case 0b01100: // synchronous external abort on translation table walk
264         case 0b01110: //    "
265         case 0b11100: // synchronous parity error on translation table walk
266         case 0b11110: //    "
267         case 0b11000: // asynchronous parity error on memory access
268         default:
269             dprintf(CRITICAL, "unhandled fault\n");
270             ;
271     }
272 
273     dprintf(CRITICAL, "IFAR 0x%x (fault address)\n", far);
274     dprintf(CRITICAL, "IFSR 0x%x (fault status register)\n", fsr);
275 
276     exception_die(frame, "halting\n");
277 }
278