1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * (C) Copyright 2013
4 * David Feng <fenghua@phytium.com.cn>
5 */
6
7 #include <asm/esr.h>
8 #include <asm/global_data.h>
9 #include <asm/ptrace.h>
10 #include <irq_func.h>
11 #include <linux/compiler.h>
12 #include <efi_loader.h>
13 #include <semihosting.h>
14
15 DECLARE_GLOBAL_DATA_PTR;
16
interrupt_init(void)17 int interrupt_init(void)
18 {
19 enable_interrupts();
20
21 return 0;
22 }
23
enable_interrupts(void)24 void enable_interrupts(void)
25 {
26 return;
27 }
28
disable_interrupts(void)29 int disable_interrupts(void)
30 {
31 return 0;
32 }
33
show_efi_loaded_images(struct pt_regs * regs)34 static void show_efi_loaded_images(struct pt_regs *regs)
35 {
36 efi_print_image_infos((void *)regs->elr);
37 }
38
dump_far(unsigned long esr)39 static void dump_far(unsigned long esr)
40 {
41 unsigned long el, far;
42
43 switch ((esr >> 26) & 0b111111) {
44 case 0x20:
45 case 0x21:
46 case 0x24:
47 case 0x25:
48 case 0x22:
49 case 0x34:
50 case 0x35:
51 break;
52 default:
53 return;
54 }
55
56 asm("mrs %0, CurrentEl": "=r" (el));
57
58 switch (el >> 2) {
59 case 1:
60 asm("mrs %0, FAR_EL1": "=r" (far));
61 break;
62 case 2:
63 asm("mrs %0, FAR_EL2": "=r" (far));
64 break;
65 default:
66 /* don't print anything to make output pretty */
67 return;
68 }
69
70 printf(", far 0x%lx", far);
71 }
72
dump_instr(struct pt_regs * regs)73 static void dump_instr(struct pt_regs *regs)
74 {
75 u32 *addr = (u32 *)(regs->elr & ~3UL);
76 int i;
77
78 printf("Code: ");
79 for (i = -4; i < 1; i++)
80 printf(i == 0 ? "(%08x) " : "%08x ", addr[i]);
81 printf("\n");
82 }
83
show_regs(struct pt_regs * regs)84 void show_regs(struct pt_regs *regs)
85 {
86 int i;
87
88 if (gd->flags & GD_FLG_RELOC)
89 printf("elr: %016lx lr : %016lx (reloc)\n",
90 regs->elr - gd->reloc_off,
91 regs->regs[30] - gd->reloc_off);
92 printf("elr: %016lx lr : %016lx\n", regs->elr, regs->regs[30]);
93
94 for (i = 0; i < 29; i += 2)
95 printf("x%-2d: %016lx x%-2d: %016lx\n",
96 i, regs->regs[i], i+1, regs->regs[i+1]);
97 printf("\n");
98 dump_instr(regs);
99 }
100
101 /*
102 * Try to "emulate" a semihosting call in the event that we don't have a
103 * debugger attached.
104 */
smh_emulate_trap(struct pt_regs * regs)105 static bool smh_emulate_trap(struct pt_regs *regs)
106 {
107 int size;
108
109 if (ESR_ELx_EC(regs->esr) != ESR_ELx_EC_UNKNOWN)
110 return false;
111
112 if (regs->spsr & PSR_MODE32_BIT) {
113 if (regs->spsr & PSR_AA32_T_BIT) {
114 u16 *insn = (u16 *)ALIGN_DOWN(regs->elr, 2);
115
116 if (*insn != SMH_T32_SVC && *insn != SMH_T32_HLT)
117 return false;
118 size = 2;
119 } else {
120 u32 *insn = (u32 *)ALIGN_DOWN(regs->elr, 4);
121
122 if (*insn != SMH_A32_SVC && *insn != SMH_A32_HLT)
123 return false;
124 size = 4;
125 }
126 } else {
127 u32 *insn = (u32 *)ALIGN_DOWN(regs->elr, 4);
128
129 if (*insn != SMH_A64_HLT)
130 return false;
131 size = 4;
132 }
133
134 /* Avoid future semihosting calls */
135 disable_semihosting();
136
137 /* Just pretend the call failed */
138 regs->regs[0] = -1;
139 regs->elr += size;
140 return true;
141 }
142
143 /*
144 * do_bad_sync handles the impossible case in the Synchronous Abort vector.
145 */
do_bad_sync(struct pt_regs * pt_regs)146 void do_bad_sync(struct pt_regs *pt_regs)
147 {
148 efi_restore_gd();
149 printf("Bad mode in \"Synchronous Abort\" handler, esr 0x%08lx\n",
150 pt_regs->esr);
151 show_regs(pt_regs);
152 show_efi_loaded_images(pt_regs);
153 panic("Resetting CPU ...\n");
154 }
155
156 /*
157 * do_bad_irq handles the impossible case in the Irq vector.
158 */
do_bad_irq(struct pt_regs * pt_regs)159 void do_bad_irq(struct pt_regs *pt_regs)
160 {
161 efi_restore_gd();
162 printf("Bad mode in \"Irq\" handler, esr 0x%08lx\n", pt_regs->esr);
163 show_regs(pt_regs);
164 show_efi_loaded_images(pt_regs);
165 panic("Resetting CPU ...\n");
166 }
167
168 /*
169 * do_bad_fiq handles the impossible case in the Fiq vector.
170 */
do_bad_fiq(struct pt_regs * pt_regs)171 void do_bad_fiq(struct pt_regs *pt_regs)
172 {
173 efi_restore_gd();
174 printf("Bad mode in \"Fiq\" handler, esr 0x%08lx\n", pt_regs->esr);
175 show_regs(pt_regs);
176 show_efi_loaded_images(pt_regs);
177 panic("Resetting CPU ...\n");
178 }
179
180 /*
181 * do_bad_error handles the impossible case in the Error vector.
182 */
do_bad_error(struct pt_regs * pt_regs)183 void do_bad_error(struct pt_regs *pt_regs)
184 {
185 efi_restore_gd();
186 printf("Bad mode in \"Error\" handler, esr 0x%08lx\n", pt_regs->esr);
187 show_regs(pt_regs);
188 show_efi_loaded_images(pt_regs);
189 panic("Resetting CPU ...\n");
190 }
191
192 /*
193 * do_sync handles the Synchronous Abort exception.
194 */
do_sync(struct pt_regs * pt_regs)195 void do_sync(struct pt_regs *pt_regs)
196 {
197 if (CONFIG_IS_ENABLED(SEMIHOSTING_FALLBACK) &&
198 smh_emulate_trap(pt_regs))
199 return;
200 efi_restore_gd();
201 printf("\"Synchronous Abort\" handler, esr 0x%08lx", pt_regs->esr);
202 dump_far(pt_regs->esr);
203 printf("\n");
204 show_regs(pt_regs);
205 show_efi_loaded_images(pt_regs);
206 panic("Resetting CPU ...\n");
207 }
208
209 /*
210 * do_irq handles the Irq exception.
211 */
do_irq(struct pt_regs * pt_regs)212 void do_irq(struct pt_regs *pt_regs)
213 {
214 efi_restore_gd();
215 printf("\"Irq\" handler, esr 0x%08lx\n", pt_regs->esr);
216 show_regs(pt_regs);
217 show_efi_loaded_images(pt_regs);
218 panic("Resetting CPU ...\n");
219 }
220
221 /*
222 * do_fiq handles the Fiq exception.
223 */
do_fiq(struct pt_regs * pt_regs)224 void do_fiq(struct pt_regs *pt_regs)
225 {
226 efi_restore_gd();
227 printf("\"Fiq\" handler, esr 0x%08lx\n", pt_regs->esr);
228 show_regs(pt_regs);
229 show_efi_loaded_images(pt_regs);
230 panic("Resetting CPU ...\n");
231 }
232
233 /*
234 * do_error handles the Error exception.
235 * Errors are more likely to be processor specific,
236 * it is defined with weak attribute and can be redefined
237 * in processor specific code.
238 */
do_error(struct pt_regs * pt_regs)239 void __weak do_error(struct pt_regs *pt_regs)
240 {
241 efi_restore_gd();
242 printf("\"Error\" handler, esr 0x%08lx\n", pt_regs->esr);
243 show_regs(pt_regs);
244 show_efi_loaded_images(pt_regs);
245 panic("Resetting CPU ...\n");
246 }
247