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