1 /*
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 <stdio.h>
9 #include <lk/debug.h>
10 #include <lk/bits.h>
11 #include <arch/arch_ops.h>
12 #include <arch/arm64.h>
13 
14 #define SHUTDOWN_ON_FATAL 1
15 
16 struct fault_handler_table_entry {
17     uint64_t pc;
18     uint64_t fault_handler;
19 };
20 
21 struct fault_status_map {
22     uint32_t fsc;
23     const char *fault_msg;
24 };
25 
26 /* Instruction and Data abort share the fault status encoding */
27 static const struct fault_status_map fsc_map[] = {
28     {
29         .fsc = 0b000000,
30         .fault_msg = "Address size fault, level 0 of translation or translation table base register"
31     },
32     {
33         .fsc = 0b000001,
34         .fault_msg = "Address size fault, level 1"
35     },
36     {
37         .fsc = 0b000010,
38         .fault_msg = "Address size fault, level 2"
39     },
40     {
41         .fsc = 0b000011,
42         .fault_msg = "Address size fault, level 3"
43     },
44     {
45         .fsc = 0b000100,
46         .fault_msg = "Translation fault, level 0"
47     },
48     {
49         .fsc = 0b000101,
50         .fault_msg = "Translation fault, level 1"
51     },
52     {
53         .fsc = 0b000110,
54         .fault_msg = "Translation fault, level 2"
55     },
56     {
57         .fsc = 0b000111,
58         .fault_msg = "Translation fault, level 3"
59     },
60     {
61         .fsc = 0b001001,
62         .fault_msg = "Access flag fault, level 1"
63     },
64     {
65         .fsc = 0b001010,
66         .fault_msg = "Access flag fault, level 2"
67     },
68     {
69         .fsc = 0b001011,
70         .fault_msg = "Access flag fault, level 3"
71     },
72     {
73         .fsc = 0b001101,
74         .fault_msg = "Permission fault, level 1"
75     },
76     {
77         .fsc = 0b001110,
78         .fault_msg = "Permission fault, level 2"
79     },
80     {
81         .fsc = 0b001111,
82         .fault_msg = "Permission fault, level 3"
83     },
84     {
85         .fsc = 0b010000,
86         .fault_msg = "Synchronous External abort, not on translation table walk"
87     },
88     {
89         .fsc = 0b010001,
90         .fault_msg = "Synchronous Tag Check fail"
91     },
92     {
93         .fsc = 0b010100,
94         .fault_msg = "Synchronous External abort, on translation table walk, level 0"
95     },
96     {
97         .fsc = 0b010101,
98         .fault_msg = "Synchronous External abort, on translation table walk, level 1"
99     },
100     {
101         .fsc = 0b010110,
102         .fault_msg = "Synchronous External abort, on translation table walk, level 2"
103     },
104     {
105         .fsc = 0b010111,
106         .fault_msg = "Synchronous External abort, on translation table walk, level 3"
107     },
108     {
109         .fsc = 0b100001,
110         .fault_msg = "Alignment fault"
111     },
112     {
113         .fsc = 0b110000,
114         .fault_msg = "TLB conflict abort"
115     },
116     {
117         .fsc = 0b111101,
118         .fault_msg = "Section Domain Fault, used only for faults reported in the PAR_EL1"
119     },
120     {
121         .fsc = 0b111110,
122         .fault_msg = "Page Domain Fault, used only for faults reported in the PAR_EL1"
123     },
124 };
125 
print_fault_msg(uint32_t fsc)126 static void print_fault_msg(uint32_t fsc)
127 {
128     uint32_t i;
129 
130     for (i = 0; i < countof(fsc_map); i++) {
131         if (fsc_map[i].fsc == fsc) {
132             printf("%s\n", fsc_map[i].fault_msg);
133             break;
134         }
135     }
136 }
137 
138 extern struct fault_handler_table_entry __fault_handler_table_start[];
139 extern struct fault_handler_table_entry __fault_handler_table_end[];
140 
dump_iframe(const struct arm64_iframe_long * iframe)141 static void dump_iframe(const struct arm64_iframe_long *iframe) {
142     printf("iframe %p:\n", iframe);
143     printf("x0  0x%16llx x1  0x%16llx x2  0x%16llx x3  0x%16llx\n", iframe->r[0], iframe->r[1], iframe->r[2], iframe->r[3]);
144     printf("x4  0x%16llx x5  0x%16llx x6  0x%16llx x7  0x%16llx\n", iframe->r[4], iframe->r[5], iframe->r[6], iframe->r[7]);
145     printf("x8  0x%16llx x9  0x%16llx x10 0x%16llx x11 0x%16llx\n", iframe->r[8], iframe->r[9], iframe->r[10], iframe->r[11]);
146     printf("x12 0x%16llx x13 0x%16llx x14 0x%16llx x15 0x%16llx\n", iframe->r[12], iframe->r[13], iframe->r[14], iframe->r[15]);
147     printf("x16 0x%16llx x17 0x%16llx x18 0x%16llx x19 0x%16llx\n", iframe->r[16], iframe->r[17], iframe->r[18], iframe->r[19]);
148     printf("x20 0x%16llx x21 0x%16llx x22 0x%16llx x23 0x%16llx\n", iframe->r[20], iframe->r[21], iframe->r[22], iframe->r[23]);
149     printf("x24 0x%16llx x25 0x%16llx x26 0x%16llx x27 0x%16llx\n", iframe->r[24], iframe->r[25], iframe->r[26], iframe->r[27]);
150     printf("x28 0x%16llx x29 0x%16llx lr  0x%16llx usp 0x%16llx\n", iframe->r[28], iframe->r[29], iframe->lr, iframe->usp);
151     printf("elr 0x%16llx\n", iframe->elr);
152     printf("spsr 0x%16llx\n", iframe->spsr);
153     arch_stacktrace(iframe->r[29], iframe->elr);
154 }
155 
arm64_syscall(struct arm64_iframe_long * iframe,bool is_64bit)156 __WEAK void arm64_syscall(struct arm64_iframe_long *iframe, bool is_64bit) {
157     panic("unhandled syscall vector\n");
158 }
159 
160 void arm64_sync_exception(struct arm64_iframe_long *iframe);
arm64_sync_exception(struct arm64_iframe_long * iframe)161 void arm64_sync_exception(struct arm64_iframe_long *iframe) {
162     struct fault_handler_table_entry *fault_handler;
163     uint32_t esr = ARM64_READ_SYSREG(esr_el1);
164     uint32_t ec = BITS_SHIFT(esr, 31, 26);
165     uint32_t il = BIT(esr, 25);
166     uint32_t iss = BITS(esr, 24, 0);
167 
168     switch (ec) {
169         case 0b000111: /* floating point */
170             arm64_fpu_exception(iframe);
171             return;
172         case 0b010001: /* syscall from arm32 */
173         case 0b010101: /* syscall from arm64 */
174 #ifdef WITH_LIB_SYSCALL
175             void arm64_syscall(struct arm64_iframe_long *iframe);
176             arch_enable_fiqs();
177             arm64_syscall(iframe);
178             arch_disable_fiqs();
179             return;
180 #else
181             arm64_syscall(iframe, (ec == 0x15) ? true : false);
182             return;
183 #endif
184         case 0b100000: /* instruction abort from lower level */
185         case 0b100001: /* instruction abort from same level */
186             printf("instruction abort: PC at 0x%llx\n", iframe->elr);
187             print_fault_msg(BITS(iss, 5, 0));
188             break;
189         case 0b100100: /* data abort from lower level */
190         case 0b100101: { /* data abort from same level */
191             for (fault_handler = __fault_handler_table_start;
192                     fault_handler < __fault_handler_table_end;
193                     fault_handler++) {
194                 if (fault_handler->pc == iframe->elr) {
195                     iframe->elr = fault_handler->fault_handler;
196                     return;
197                 }
198             }
199 
200             /* read the FAR register */
201             uint64_t far = ARM64_READ_SYSREG(far_el1);
202 
203             printf("data fault: %s access from PC 0x%llx, FAR 0x%llx, iss 0x%x (DFSC 0x%lx)\n",
204                    BIT(iss, 6) ? "Write" : "Read", iframe->elr, far, iss, BITS(iss, 5, 0));
205             print_fault_msg(BITS(iss, 5, 0));
206             break;
207         }
208         case 0b111100: {
209             printf("BRK #0x%04lx instruction: PC at 0x%llx\n",
210                    BITS_SHIFT(iss, 15, 0), iframe->elr);
211             break;
212         }
213         default:
214             printf("unhandled synchronous exception\n");
215     }
216 
217     /* unhandled exception, die here */
218     printf("ESR 0x%x: ec 0x%x, il 0x%x, iss 0x%x\n", esr, ec, il, iss);
219     dump_iframe(iframe);
220 
221     panic("die\n");
222 }
223 
224 void arm64_invalid_exception(struct arm64_iframe_long *iframe, unsigned int which);
arm64_invalid_exception(struct arm64_iframe_long * iframe,unsigned int which)225 void arm64_invalid_exception(struct arm64_iframe_long *iframe, unsigned int which) {
226     printf("invalid exception, which 0x%x\n", which);
227     dump_iframe(iframe);
228 
229     panic("die\n");
230 }
231