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