18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * OpenRISC traps.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Linux architectural port borrowing liberally from similar works of 68c2ecf20Sopenharmony_ci * others. All original copyrights apply as per the original source 78c2ecf20Sopenharmony_ci * declaration. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Modifications for the OpenRISC architecture: 108c2ecf20Sopenharmony_ci * Copyright (C) 2003 Matjaz Breskvar <phoenix@bsemi.com> 118c2ecf20Sopenharmony_ci * Copyright (C) 2010-2011 Jonas Bonn <jonas@southpole.se> 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * Here we handle the break vectors not used by the system call 148c2ecf20Sopenharmony_ci * mechanism, as well as some general stack/register dumping 158c2ecf20Sopenharmony_ci * things. 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/init.h> 198c2ecf20Sopenharmony_ci#include <linux/sched.h> 208c2ecf20Sopenharmony_ci#include <linux/sched/debug.h> 218c2ecf20Sopenharmony_ci#include <linux/sched/task_stack.h> 228c2ecf20Sopenharmony_ci#include <linux/kernel.h> 238c2ecf20Sopenharmony_ci#include <linux/extable.h> 248c2ecf20Sopenharmony_ci#include <linux/kmod.h> 258c2ecf20Sopenharmony_ci#include <linux/string.h> 268c2ecf20Sopenharmony_ci#include <linux/errno.h> 278c2ecf20Sopenharmony_ci#include <linux/ptrace.h> 288c2ecf20Sopenharmony_ci#include <linux/timer.h> 298c2ecf20Sopenharmony_ci#include <linux/mm.h> 308c2ecf20Sopenharmony_ci#include <linux/kallsyms.h> 318c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#include <asm/io.h> 348c2ecf20Sopenharmony_ci#include <asm/unwinder.h> 358c2ecf20Sopenharmony_ci#include <asm/sections.h> 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ciint kstack_depth_to_print = 0x180; 388c2ecf20Sopenharmony_ciint lwa_flag; 398c2ecf20Sopenharmony_ciunsigned long __user *lwa_addr; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_civoid print_trace(void *data, unsigned long addr, int reliable) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci const char *loglvl = data; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci printk("%s[<%p>] %s%pS\n", loglvl, (void *) addr, reliable ? "" : "? ", 468c2ecf20Sopenharmony_ci (void *) addr); 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci/* displays a short stack trace */ 508c2ecf20Sopenharmony_civoid show_stack(struct task_struct *task, unsigned long *esp, const char *loglvl) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci if (esp == NULL) 538c2ecf20Sopenharmony_ci esp = (unsigned long *)&esp; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci printk("%sCall trace:\n", loglvl); 568c2ecf20Sopenharmony_ci unwind_stack((void *)loglvl, esp, print_trace); 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_civoid show_registers(struct pt_regs *regs) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci int i; 628c2ecf20Sopenharmony_ci int in_kernel = 1; 638c2ecf20Sopenharmony_ci unsigned long esp; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci esp = (unsigned long)(regs->sp); 668c2ecf20Sopenharmony_ci if (user_mode(regs)) 678c2ecf20Sopenharmony_ci in_kernel = 0; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci printk("CPU #: %d\n" 708c2ecf20Sopenharmony_ci " PC: %08lx SR: %08lx SP: %08lx\n", 718c2ecf20Sopenharmony_ci smp_processor_id(), regs->pc, regs->sr, regs->sp); 728c2ecf20Sopenharmony_ci printk("GPR00: %08lx GPR01: %08lx GPR02: %08lx GPR03: %08lx\n", 738c2ecf20Sopenharmony_ci 0L, regs->gpr[1], regs->gpr[2], regs->gpr[3]); 748c2ecf20Sopenharmony_ci printk("GPR04: %08lx GPR05: %08lx GPR06: %08lx GPR07: %08lx\n", 758c2ecf20Sopenharmony_ci regs->gpr[4], regs->gpr[5], regs->gpr[6], regs->gpr[7]); 768c2ecf20Sopenharmony_ci printk("GPR08: %08lx GPR09: %08lx GPR10: %08lx GPR11: %08lx\n", 778c2ecf20Sopenharmony_ci regs->gpr[8], regs->gpr[9], regs->gpr[10], regs->gpr[11]); 788c2ecf20Sopenharmony_ci printk("GPR12: %08lx GPR13: %08lx GPR14: %08lx GPR15: %08lx\n", 798c2ecf20Sopenharmony_ci regs->gpr[12], regs->gpr[13], regs->gpr[14], regs->gpr[15]); 808c2ecf20Sopenharmony_ci printk("GPR16: %08lx GPR17: %08lx GPR18: %08lx GPR19: %08lx\n", 818c2ecf20Sopenharmony_ci regs->gpr[16], regs->gpr[17], regs->gpr[18], regs->gpr[19]); 828c2ecf20Sopenharmony_ci printk("GPR20: %08lx GPR21: %08lx GPR22: %08lx GPR23: %08lx\n", 838c2ecf20Sopenharmony_ci regs->gpr[20], regs->gpr[21], regs->gpr[22], regs->gpr[23]); 848c2ecf20Sopenharmony_ci printk("GPR24: %08lx GPR25: %08lx GPR26: %08lx GPR27: %08lx\n", 858c2ecf20Sopenharmony_ci regs->gpr[24], regs->gpr[25], regs->gpr[26], regs->gpr[27]); 868c2ecf20Sopenharmony_ci printk("GPR28: %08lx GPR29: %08lx GPR30: %08lx GPR31: %08lx\n", 878c2ecf20Sopenharmony_ci regs->gpr[28], regs->gpr[29], regs->gpr[30], regs->gpr[31]); 888c2ecf20Sopenharmony_ci printk(" RES: %08lx oGPR11: %08lx\n", 898c2ecf20Sopenharmony_ci regs->gpr[11], regs->orig_gpr11); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci printk("Process %s (pid: %d, stackpage=%08lx)\n", 928c2ecf20Sopenharmony_ci current->comm, current->pid, (unsigned long)current); 938c2ecf20Sopenharmony_ci /* 948c2ecf20Sopenharmony_ci * When in-kernel, we also print out the stack and code at the 958c2ecf20Sopenharmony_ci * time of the fault.. 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_ci if (in_kernel) { 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci printk("\nStack: "); 1008c2ecf20Sopenharmony_ci show_stack(NULL, (unsigned long *)esp, KERN_EMERG); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci printk("\nCode: "); 1038c2ecf20Sopenharmony_ci if (regs->pc < PAGE_OFFSET) 1048c2ecf20Sopenharmony_ci goto bad; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci for (i = -24; i < 24; i++) { 1078c2ecf20Sopenharmony_ci unsigned char c; 1088c2ecf20Sopenharmony_ci if (__get_user(c, &((unsigned char *)regs->pc)[i])) { 1098c2ecf20Sopenharmony_cibad: 1108c2ecf20Sopenharmony_ci printk(" Bad PC value."); 1118c2ecf20Sopenharmony_ci break; 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci if (i == 0) 1158c2ecf20Sopenharmony_ci printk("(%02x) ", c); 1168c2ecf20Sopenharmony_ci else 1178c2ecf20Sopenharmony_ci printk("%02x ", c); 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci printk("\n"); 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_civoid nommu_dump_state(struct pt_regs *regs, 1248c2ecf20Sopenharmony_ci unsigned long ea, unsigned long vector) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci int i; 1278c2ecf20Sopenharmony_ci unsigned long addr, stack = regs->sp; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci printk("\n\r[nommu_dump_state] :: ea %lx, vector %lx\n\r", ea, vector); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci printk("CPU #: %d\n" 1328c2ecf20Sopenharmony_ci " PC: %08lx SR: %08lx SP: %08lx\n", 1338c2ecf20Sopenharmony_ci 0, regs->pc, regs->sr, regs->sp); 1348c2ecf20Sopenharmony_ci printk("GPR00: %08lx GPR01: %08lx GPR02: %08lx GPR03: %08lx\n", 1358c2ecf20Sopenharmony_ci 0L, regs->gpr[1], regs->gpr[2], regs->gpr[3]); 1368c2ecf20Sopenharmony_ci printk("GPR04: %08lx GPR05: %08lx GPR06: %08lx GPR07: %08lx\n", 1378c2ecf20Sopenharmony_ci regs->gpr[4], regs->gpr[5], regs->gpr[6], regs->gpr[7]); 1388c2ecf20Sopenharmony_ci printk("GPR08: %08lx GPR09: %08lx GPR10: %08lx GPR11: %08lx\n", 1398c2ecf20Sopenharmony_ci regs->gpr[8], regs->gpr[9], regs->gpr[10], regs->gpr[11]); 1408c2ecf20Sopenharmony_ci printk("GPR12: %08lx GPR13: %08lx GPR14: %08lx GPR15: %08lx\n", 1418c2ecf20Sopenharmony_ci regs->gpr[12], regs->gpr[13], regs->gpr[14], regs->gpr[15]); 1428c2ecf20Sopenharmony_ci printk("GPR16: %08lx GPR17: %08lx GPR18: %08lx GPR19: %08lx\n", 1438c2ecf20Sopenharmony_ci regs->gpr[16], regs->gpr[17], regs->gpr[18], regs->gpr[19]); 1448c2ecf20Sopenharmony_ci printk("GPR20: %08lx GPR21: %08lx GPR22: %08lx GPR23: %08lx\n", 1458c2ecf20Sopenharmony_ci regs->gpr[20], regs->gpr[21], regs->gpr[22], regs->gpr[23]); 1468c2ecf20Sopenharmony_ci printk("GPR24: %08lx GPR25: %08lx GPR26: %08lx GPR27: %08lx\n", 1478c2ecf20Sopenharmony_ci regs->gpr[24], regs->gpr[25], regs->gpr[26], regs->gpr[27]); 1488c2ecf20Sopenharmony_ci printk("GPR28: %08lx GPR29: %08lx GPR30: %08lx GPR31: %08lx\n", 1498c2ecf20Sopenharmony_ci regs->gpr[28], regs->gpr[29], regs->gpr[30], regs->gpr[31]); 1508c2ecf20Sopenharmony_ci printk(" RES: %08lx oGPR11: %08lx\n", 1518c2ecf20Sopenharmony_ci regs->gpr[11], regs->orig_gpr11); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci printk("Process %s (pid: %d, stackpage=%08lx)\n", 1548c2ecf20Sopenharmony_ci ((struct task_struct *)(__pa(current)))->comm, 1558c2ecf20Sopenharmony_ci ((struct task_struct *)(__pa(current)))->pid, 1568c2ecf20Sopenharmony_ci (unsigned long)current); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci printk("\nStack: "); 1598c2ecf20Sopenharmony_ci printk("Stack dump [0x%08lx]:\n", (unsigned long)stack); 1608c2ecf20Sopenharmony_ci for (i = 0; i < kstack_depth_to_print; i++) { 1618c2ecf20Sopenharmony_ci if (((long)stack & (THREAD_SIZE - 1)) == 0) 1628c2ecf20Sopenharmony_ci break; 1638c2ecf20Sopenharmony_ci stack++; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci printk("%lx :: sp + %02d: 0x%08lx\n", stack, i * 4, 1668c2ecf20Sopenharmony_ci *((unsigned long *)(__pa(stack)))); 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci printk("\n"); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci printk("Call Trace: "); 1718c2ecf20Sopenharmony_ci i = 1; 1728c2ecf20Sopenharmony_ci while (((long)stack & (THREAD_SIZE - 1)) != 0) { 1738c2ecf20Sopenharmony_ci addr = *((unsigned long *)__pa(stack)); 1748c2ecf20Sopenharmony_ci stack++; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci if (kernel_text_address(addr)) { 1778c2ecf20Sopenharmony_ci if (i && ((i % 6) == 0)) 1788c2ecf20Sopenharmony_ci printk("\n "); 1798c2ecf20Sopenharmony_ci printk(" [<%08lx>]", addr); 1808c2ecf20Sopenharmony_ci i++; 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci printk("\n"); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci printk("\nCode: "); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci for (i = -24; i < 24; i++) { 1888c2ecf20Sopenharmony_ci unsigned char c; 1898c2ecf20Sopenharmony_ci c = ((unsigned char *)(__pa(regs->pc)))[i]; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci if (i == 0) 1928c2ecf20Sopenharmony_ci printk("(%02x) ", c); 1938c2ecf20Sopenharmony_ci else 1948c2ecf20Sopenharmony_ci printk("%02x ", c); 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci printk("\n"); 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci/* This is normally the 'Oops' routine */ 2008c2ecf20Sopenharmony_civoid die(const char *str, struct pt_regs *regs, long err) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci console_verbose(); 2048c2ecf20Sopenharmony_ci printk("\n%s#: %04lx\n", str, err & 0xffff); 2058c2ecf20Sopenharmony_ci show_registers(regs); 2068c2ecf20Sopenharmony_ci#ifdef CONFIG_JUMP_UPON_UNHANDLED_EXCEPTION 2078c2ecf20Sopenharmony_ci printk("\n\nUNHANDLED_EXCEPTION: entering infinite loop\n"); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci /* shut down interrupts */ 2108c2ecf20Sopenharmony_ci local_irq_disable(); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci __asm__ __volatile__("l.nop 1"); 2138c2ecf20Sopenharmony_ci do {} while (1); 2148c2ecf20Sopenharmony_ci#endif 2158c2ecf20Sopenharmony_ci make_task_dead(SIGSEGV); 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci/* This is normally the 'Oops' routine */ 2198c2ecf20Sopenharmony_civoid die_if_kernel(const char *str, struct pt_regs *regs, long err) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci if (user_mode(regs)) 2228c2ecf20Sopenharmony_ci return; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci die(str, regs, err); 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_civoid unhandled_exception(struct pt_regs *regs, int ea, int vector) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci printk("Unable to handle exception at EA =0x%x, vector 0x%x", 2308c2ecf20Sopenharmony_ci ea, vector); 2318c2ecf20Sopenharmony_ci die("Oops", regs, 9); 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_civoid __init trap_init(void) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci /* Nothing needs to be done */ 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ciasmlinkage void do_trap(struct pt_regs *regs, unsigned long address) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci force_sig_fault(SIGTRAP, TRAP_TRACE, (void __user *)address); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci regs->pc += 4; 2448c2ecf20Sopenharmony_ci} 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ciasmlinkage void do_unaligned_access(struct pt_regs *regs, unsigned long address) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci if (user_mode(regs)) { 2498c2ecf20Sopenharmony_ci /* Send a SIGBUS */ 2508c2ecf20Sopenharmony_ci force_sig_fault(SIGBUS, BUS_ADRALN, (void __user *)address); 2518c2ecf20Sopenharmony_ci } else { 2528c2ecf20Sopenharmony_ci printk("KERNEL: Unaligned Access 0x%.8lx\n", address); 2538c2ecf20Sopenharmony_ci show_registers(regs); 2548c2ecf20Sopenharmony_ci die("Die:", regs, address); 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ciasmlinkage void do_bus_fault(struct pt_regs *regs, unsigned long address) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci if (user_mode(regs)) { 2628c2ecf20Sopenharmony_ci /* Send a SIGBUS */ 2638c2ecf20Sopenharmony_ci force_sig_fault(SIGBUS, BUS_ADRERR, (void __user *)address); 2648c2ecf20Sopenharmony_ci } else { /* Kernel mode */ 2658c2ecf20Sopenharmony_ci printk("KERNEL: Bus error (SIGBUS) 0x%.8lx\n", address); 2668c2ecf20Sopenharmony_ci show_registers(regs); 2678c2ecf20Sopenharmony_ci die("Die:", regs, address); 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic inline int in_delay_slot(struct pt_regs *regs) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci#ifdef CONFIG_OPENRISC_NO_SPR_SR_DSX 2748c2ecf20Sopenharmony_ci /* No delay slot flag, do the old way */ 2758c2ecf20Sopenharmony_ci unsigned int op, insn; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci insn = *((unsigned int *)regs->pc); 2788c2ecf20Sopenharmony_ci op = insn >> 26; 2798c2ecf20Sopenharmony_ci switch (op) { 2808c2ecf20Sopenharmony_ci case 0x00: /* l.j */ 2818c2ecf20Sopenharmony_ci case 0x01: /* l.jal */ 2828c2ecf20Sopenharmony_ci case 0x03: /* l.bnf */ 2838c2ecf20Sopenharmony_ci case 0x04: /* l.bf */ 2848c2ecf20Sopenharmony_ci case 0x11: /* l.jr */ 2858c2ecf20Sopenharmony_ci case 0x12: /* l.jalr */ 2868c2ecf20Sopenharmony_ci return 1; 2878c2ecf20Sopenharmony_ci default: 2888c2ecf20Sopenharmony_ci return 0; 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci#else 2918c2ecf20Sopenharmony_ci return mfspr(SPR_SR) & SPR_SR_DSX; 2928c2ecf20Sopenharmony_ci#endif 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_cistatic inline void adjust_pc(struct pt_regs *regs, unsigned long address) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci int displacement; 2988c2ecf20Sopenharmony_ci unsigned int rb, op, jmp; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci if (unlikely(in_delay_slot(regs))) { 3018c2ecf20Sopenharmony_ci /* In delay slot, instruction at pc is a branch, simulate it */ 3028c2ecf20Sopenharmony_ci jmp = *((unsigned int *)regs->pc); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci displacement = sign_extend32(((jmp) & 0x3ffffff) << 2, 27); 3058c2ecf20Sopenharmony_ci rb = (jmp & 0x0000ffff) >> 11; 3068c2ecf20Sopenharmony_ci op = jmp >> 26; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci switch (op) { 3098c2ecf20Sopenharmony_ci case 0x00: /* l.j */ 3108c2ecf20Sopenharmony_ci regs->pc += displacement; 3118c2ecf20Sopenharmony_ci return; 3128c2ecf20Sopenharmony_ci case 0x01: /* l.jal */ 3138c2ecf20Sopenharmony_ci regs->pc += displacement; 3148c2ecf20Sopenharmony_ci regs->gpr[9] = regs->pc + 8; 3158c2ecf20Sopenharmony_ci return; 3168c2ecf20Sopenharmony_ci case 0x03: /* l.bnf */ 3178c2ecf20Sopenharmony_ci if (regs->sr & SPR_SR_F) 3188c2ecf20Sopenharmony_ci regs->pc += 8; 3198c2ecf20Sopenharmony_ci else 3208c2ecf20Sopenharmony_ci regs->pc += displacement; 3218c2ecf20Sopenharmony_ci return; 3228c2ecf20Sopenharmony_ci case 0x04: /* l.bf */ 3238c2ecf20Sopenharmony_ci if (regs->sr & SPR_SR_F) 3248c2ecf20Sopenharmony_ci regs->pc += displacement; 3258c2ecf20Sopenharmony_ci else 3268c2ecf20Sopenharmony_ci regs->pc += 8; 3278c2ecf20Sopenharmony_ci return; 3288c2ecf20Sopenharmony_ci case 0x11: /* l.jr */ 3298c2ecf20Sopenharmony_ci regs->pc = regs->gpr[rb]; 3308c2ecf20Sopenharmony_ci return; 3318c2ecf20Sopenharmony_ci case 0x12: /* l.jalr */ 3328c2ecf20Sopenharmony_ci regs->pc = regs->gpr[rb]; 3338c2ecf20Sopenharmony_ci regs->gpr[9] = regs->pc + 8; 3348c2ecf20Sopenharmony_ci return; 3358c2ecf20Sopenharmony_ci default: 3368c2ecf20Sopenharmony_ci break; 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci } else { 3398c2ecf20Sopenharmony_ci regs->pc += 4; 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_cistatic inline void simulate_lwa(struct pt_regs *regs, unsigned long address, 3448c2ecf20Sopenharmony_ci unsigned int insn) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci unsigned int ra, rd; 3478c2ecf20Sopenharmony_ci unsigned long value; 3488c2ecf20Sopenharmony_ci unsigned long orig_pc; 3498c2ecf20Sopenharmony_ci long imm; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci const struct exception_table_entry *entry; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci orig_pc = regs->pc; 3548c2ecf20Sopenharmony_ci adjust_pc(regs, address); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci ra = (insn >> 16) & 0x1f; 3578c2ecf20Sopenharmony_ci rd = (insn >> 21) & 0x1f; 3588c2ecf20Sopenharmony_ci imm = (short)insn; 3598c2ecf20Sopenharmony_ci lwa_addr = (unsigned long __user *)(regs->gpr[ra] + imm); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci if ((unsigned long)lwa_addr & 0x3) { 3628c2ecf20Sopenharmony_ci do_unaligned_access(regs, address); 3638c2ecf20Sopenharmony_ci return; 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci if (get_user(value, lwa_addr)) { 3678c2ecf20Sopenharmony_ci if (user_mode(regs)) { 3688c2ecf20Sopenharmony_ci force_sig(SIGSEGV); 3698c2ecf20Sopenharmony_ci return; 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci if ((entry = search_exception_tables(orig_pc))) { 3738c2ecf20Sopenharmony_ci regs->pc = entry->fixup; 3748c2ecf20Sopenharmony_ci return; 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci /* kernel access in kernel space, load it directly */ 3788c2ecf20Sopenharmony_ci value = *((unsigned long *)lwa_addr); 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci lwa_flag = 1; 3828c2ecf20Sopenharmony_ci regs->gpr[rd] = value; 3838c2ecf20Sopenharmony_ci} 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_cistatic inline void simulate_swa(struct pt_regs *regs, unsigned long address, 3868c2ecf20Sopenharmony_ci unsigned int insn) 3878c2ecf20Sopenharmony_ci{ 3888c2ecf20Sopenharmony_ci unsigned long __user *vaddr; 3898c2ecf20Sopenharmony_ci unsigned long orig_pc; 3908c2ecf20Sopenharmony_ci unsigned int ra, rb; 3918c2ecf20Sopenharmony_ci long imm; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci const struct exception_table_entry *entry; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci orig_pc = regs->pc; 3968c2ecf20Sopenharmony_ci adjust_pc(regs, address); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci ra = (insn >> 16) & 0x1f; 3998c2ecf20Sopenharmony_ci rb = (insn >> 11) & 0x1f; 4008c2ecf20Sopenharmony_ci imm = (short)(((insn & 0x2200000) >> 10) | (insn & 0x7ff)); 4018c2ecf20Sopenharmony_ci vaddr = (unsigned long __user *)(regs->gpr[ra] + imm); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci if (!lwa_flag || vaddr != lwa_addr) { 4048c2ecf20Sopenharmony_ci regs->sr &= ~SPR_SR_F; 4058c2ecf20Sopenharmony_ci return; 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci if ((unsigned long)vaddr & 0x3) { 4098c2ecf20Sopenharmony_ci do_unaligned_access(regs, address); 4108c2ecf20Sopenharmony_ci return; 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci if (put_user(regs->gpr[rb], vaddr)) { 4148c2ecf20Sopenharmony_ci if (user_mode(regs)) { 4158c2ecf20Sopenharmony_ci force_sig(SIGSEGV); 4168c2ecf20Sopenharmony_ci return; 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci if ((entry = search_exception_tables(orig_pc))) { 4208c2ecf20Sopenharmony_ci regs->pc = entry->fixup; 4218c2ecf20Sopenharmony_ci return; 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci /* kernel access in kernel space, store it directly */ 4258c2ecf20Sopenharmony_ci *((unsigned long *)vaddr) = regs->gpr[rb]; 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci lwa_flag = 0; 4298c2ecf20Sopenharmony_ci regs->sr |= SPR_SR_F; 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci#define INSN_LWA 0x1b 4338c2ecf20Sopenharmony_ci#define INSN_SWA 0x33 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ciasmlinkage void do_illegal_instruction(struct pt_regs *regs, 4368c2ecf20Sopenharmony_ci unsigned long address) 4378c2ecf20Sopenharmony_ci{ 4388c2ecf20Sopenharmony_ci unsigned int op; 4398c2ecf20Sopenharmony_ci unsigned int insn = *((unsigned int *)address); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci op = insn >> 26; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci switch (op) { 4448c2ecf20Sopenharmony_ci case INSN_LWA: 4458c2ecf20Sopenharmony_ci simulate_lwa(regs, address, insn); 4468c2ecf20Sopenharmony_ci return; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci case INSN_SWA: 4498c2ecf20Sopenharmony_ci simulate_swa(regs, address, insn); 4508c2ecf20Sopenharmony_ci return; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci default: 4538c2ecf20Sopenharmony_ci break; 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci if (user_mode(regs)) { 4578c2ecf20Sopenharmony_ci /* Send a SIGILL */ 4588c2ecf20Sopenharmony_ci force_sig_fault(SIGILL, ILL_ILLOPC, (void __user *)address); 4598c2ecf20Sopenharmony_ci } else { /* Kernel mode */ 4608c2ecf20Sopenharmony_ci printk("KERNEL: Illegal instruction (SIGILL) 0x%.8lx\n", 4618c2ecf20Sopenharmony_ci address); 4628c2ecf20Sopenharmony_ci show_registers(regs); 4638c2ecf20Sopenharmony_ci die("Die:", regs, address); 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci} 466