1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Copyright (c) 2016-17 Microsemi Corporation. 4 * Padmarao Begari, Microsemi Corporation <padmarao.begari@microsemi.com> 5 * 6 * Copyright (C) 2017 Andes Technology Corporation 7 * Rick Chen, Andes Technology Corporation <rick@andestech.com> 8 * 9 * Copyright (C) 2019 Sean Anderson <seanga2@gmail.com> 10 */ 11 12#include <linux/compat.h> 13#include <efi_loader.h> 14#include <hang.h> 15#include <interrupt.h> 16#include <irq_func.h> 17#include <asm/global_data.h> 18#include <asm/ptrace.h> 19#include <asm/system.h> 20#include <asm/encoding.h> 21#include <semihosting.h> 22 23DECLARE_GLOBAL_DATA_PTR; 24 25static struct resume_data *resume; 26 27void set_resume(struct resume_data *data) 28{ 29 resume = data; 30} 31 32static void show_efi_loaded_images(uintptr_t epc) 33{ 34 efi_print_image_infos((void *)epc); 35} 36 37static void show_regs(struct pt_regs *regs) 38{ 39#ifdef CONFIG_SHOW_REGS 40 printf("\nSP: " REG_FMT " GP: " REG_FMT " TP: " REG_FMT "\n", 41 regs->sp, regs->gp, regs->tp); 42 printf("T0: " REG_FMT " T1: " REG_FMT " T2: " REG_FMT "\n", 43 regs->t0, regs->t1, regs->t2); 44 printf("S0: " REG_FMT " S1: " REG_FMT " A0: " REG_FMT "\n", 45 regs->s0, regs->s1, regs->a0); 46 printf("A1: " REG_FMT " A2: " REG_FMT " A3: " REG_FMT "\n", 47 regs->a1, regs->a2, regs->a3); 48 printf("A4: " REG_FMT " A5: " REG_FMT " A6: " REG_FMT "\n", 49 regs->a4, regs->a5, regs->a6); 50 printf("A7: " REG_FMT " S2: " REG_FMT " S3: " REG_FMT "\n", 51 regs->a7, regs->s2, regs->s3); 52 printf("S4: " REG_FMT " S5: " REG_FMT " S6: " REG_FMT "\n", 53 regs->s4, regs->s5, regs->s6); 54 printf("S7: " REG_FMT " S8: " REG_FMT " S9: " REG_FMT "\n", 55 regs->s7, regs->s8, regs->s9); 56 printf("S10: " REG_FMT " S11: " REG_FMT " T3: " REG_FMT "\n", 57 regs->s10, regs->s11, regs->t3); 58 printf("T4: " REG_FMT " T5: " REG_FMT " T6: " REG_FMT "\n", 59 regs->t4, regs->t5, regs->t6); 60#endif 61} 62 63static void __maybe_unused show_backtrace(struct pt_regs *regs) 64{ 65 uintptr_t *fp = (uintptr_t *)regs->s0; 66 unsigned count = 0; 67 ulong ra; 68 69 printf("\nbacktrace:\n"); 70 71 /* there are a few entry points where the s0 register is 72 * set to gd, so to avoid changing those, just abort if 73 * the value is the same */ 74 while (fp != NULL && fp != (uintptr_t *)gd) { 75 ra = fp[-1]; 76 printf("%3d: FP: " REG_FMT " RA: " REG_FMT, 77 count, (ulong)fp, ra); 78 79 if (gd && gd->flags & GD_FLG_RELOC) 80 printf(" - RA: " REG_FMT " reloc adjusted\n", 81 ra - gd->reloc_off); 82 else 83 printf("\n"); 84 85 fp = (uintptr_t *)fp[-2]; 86 count++; 87 } 88} 89 90/** 91 * instr_len() - get instruction length 92 * 93 * @i: low 16 bits of the instruction 94 * Return: number of u16 in instruction 95 */ 96static int instr_len(u16 i) 97{ 98 if ((i & 0x03) != 0x03) 99 return 1; 100 /* Instructions with more than 32 bits are not yet specified */ 101 return 2; 102} 103 104/** 105 * show_code() - display code leading to exception 106 * 107 * @epc: program counter 108 */ 109static void show_code(ulong epc) 110{ 111 u16 *pos = (u16 *)(epc & ~1UL); 112 int i, len = instr_len(*pos); 113 114 printf("\nCode: "); 115 for (i = -8; i; ++i) 116 printf("%04x ", pos[i]); 117 printf("("); 118 for (i = 0; i < len; ++i) 119 printf("%04x%s", pos[i], i + 1 == len ? ")\n" : " "); 120} 121 122static void _exit_trap(ulong code, ulong epc, ulong tval, struct pt_regs *regs) 123{ 124 static const char * const exception_code[] = { 125 "Instruction address misaligned", 126 "Instruction access fault", 127 "Illegal instruction", 128 "Breakpoint", 129 "Load address misaligned", 130 "Load access fault", 131 "Store/AMO address misaligned", 132 "Store/AMO access fault", 133 "Environment call from U-mode", 134 "Environment call from S-mode", 135 "Reserved", 136 "Environment call from M-mode", 137 "Instruction page fault", 138 "Load page fault", 139 "Reserved", 140 "Store/AMO page fault", 141 }; 142 143 if (resume) { 144 resume->code = code; 145 longjmp(resume->jump, 1); 146 } 147 148 if (code < ARRAY_SIZE(exception_code)) 149 printf("Unhandled exception: %s\n", exception_code[code]); 150 else 151 printf("Unhandled exception code: %ld\n", code); 152 153 printf("EPC: " REG_FMT " RA: " REG_FMT " TVAL: " REG_FMT "\n", 154 epc, regs->ra, tval); 155 /* Print relocation adjustments, but only if gd is initialized */ 156 if (gd && gd->flags & GD_FLG_RELOC) 157 printf("EPC: " REG_FMT " RA: " REG_FMT " reloc adjusted\n", 158 epc - gd->reloc_off, regs->ra - gd->reloc_off); 159 160 show_regs(regs); 161 if (CONFIG_IS_ENABLED(FRAMEPOINTER)) 162 show_backtrace(regs); 163 show_code(epc); 164 show_efi_loaded_images(epc); 165 panic("\n"); 166} 167 168int interrupt_init(void) 169{ 170 return 0; 171} 172 173/* 174 * enable interrupts 175 */ 176void enable_interrupts(void) 177{ 178} 179 180/* 181 * disable interrupts 182 */ 183int disable_interrupts(void) 184{ 185 return 0; 186} 187 188ulong handle_trap(ulong cause, ulong epc, ulong tval, struct pt_regs *regs) 189{ 190 ulong is_irq, irq; 191 192 /* An UEFI application may have changed gd. Restore U-Boot's gd. */ 193 efi_restore_gd(); 194 195 if (cause == CAUSE_BREAKPOINT && 196 CONFIG_IS_ENABLED(SEMIHOSTING_FALLBACK)) { 197 ulong pre_addr = epc - 4, post_addr = epc + 4; 198 199 /* Check for prior and post addresses to be in same page. */ 200 if ((pre_addr & ~(PAGE_SIZE - 1)) == 201 (post_addr & ~(PAGE_SIZE - 1))) { 202 u32 pre = *(u32 *)pre_addr; 203 u32 post = *(u32 *)post_addr; 204 205 /* Check for semihosting, i.e.: 206 * slli zero,zero,0x1f 207 * ebreak 208 * srai zero,zero,0x7 209 */ 210 if (pre == 0x01f01013 && post == 0x40705013) { 211 disable_semihosting(); 212 epc += 4; 213 return epc; 214 } 215 } 216 } 217 218 is_irq = (cause & MCAUSE_INT); 219 irq = (cause & ~MCAUSE_INT); 220 221 if (is_irq) { 222 switch (irq) { 223 case IRQ_M_EXT: 224 case IRQ_S_EXT: 225 external_interrupt(0); /* handle external interrupt */ 226 break; 227 case IRQ_M_TIMER: 228 case IRQ_S_TIMER: 229 timer_interrupt(0); /* handle timer interrupt */ 230 break; 231 default: 232 _exit_trap(cause, epc, tval, regs); 233 break; 234 }; 235 } else { 236 _exit_trap(cause, epc, tval, regs); 237 } 238 239 return epc; 240} 241 242/* 243 *Entry Point for PLIC Interrupt Handler 244 */ 245__attribute__((weak)) void external_interrupt(struct pt_regs *regs) 246{ 247} 248 249__attribute__((weak)) void timer_interrupt(struct pt_regs *regs) 250{ 251} 252