/*
* Meta exception handling.
*
* Copyright (C) 2005,2006,2007,2008,2009,2012 Imagination Technologies Ltd.
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive
* for more details.
*/
#include <linux/export.h>
#include <linux/sched.h>
#include <linux/signal.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/preempt.h>
#include <linux/ptrace.h>
#include <linux/module.h>
#include <linux/kallsyms.h>
#include <linux/kdebug.h>
#include <linux/kexec.h>
#include <linux/unistd.h>
#include <linux/smp.h>
#include <linux/slab.h>
#include <linux/syscalls.h>
#include <asm/bug.h>
#include <asm/core_reg.h>
#include <asm/irqflags.h>
#include <asm/siginfo.h>
#include <asm/traps.h>
#include <asm/hwthread.h>
#include <asm/setup.h>
#include <asm/switch.h>
#include <asm/user_gateway.h>
#include <asm/syscall.h>
#include <asm/syscalls.h>
/* Passing syscall arguments as long long is quicker. */
typedef unsigned int (*LPSYSCALL) (unsigned long long,
unsigned long long,
unsigned long long);
/*
* Users of LNKSET should compare the bus error bits obtained from DEFR
* against TXDEFR_LNKSET_SUCCESS only as the failure code will vary between
* different cores revisions.
*/
#define TXDEFR_LNKSET_SUCCESS 0x02000000
#define TXDEFR_LNKSET_FAILURE 0x04000000
/*
* Our global TBI handle. Initialised from setup.c/setup_arch.
*/
DECLARE_PER_CPU(PTBI, pTBI);
#ifdef CONFIG_SMP
static DEFINE_PER_CPU(unsigned int, trigger_mask);
#else
unsigned int global_trigger_mask;
EXPORT_SYMBOL(global_trigger_mask);
#endif
unsigned long per_cpu__stack_save[NR_CPUS];
static const char * const trap_names[] = {
[TBIXXF_SIGNUM_IIF] = "Illegal instruction fault",
[TBIXXF_SIGNUM_PGF] = "Privilege violation",
[TBIXXF_SIGNUM_DHF] = "Unaligned data access fault",
[TBIXXF_SIGNUM_IGF] = "Code fetch general read failure",
[TBIXXF_SIGNUM_DGF] = "Data access general read/write fault",
[TBIXXF_SIGNUM_IPF] = "Code fetch page fault",
[TBIXXF_SIGNUM_DPF] = "Data access page fault",
[TBIXXF_SIGNUM_IHF] = "Instruction breakpoint",
[TBIXXF_SIGNUM_DWF] = "Read-only data access fault",
};
const char *trap_name(int trapno)
{
if (trapno >= 0 && trapno < ARRAY_SIZE(trap_names)
&& trap_names[trapno])
return trap_names[trapno];
return "Unknown fault";
}
static DEFINE_SPINLOCK(die_lock);
void __noreturn die(const char *str, struct pt_regs *regs,
long err, unsigned long addr)
{
static int die_counter;
oops_enter();
spin_lock_irq(&die_lock);
console_verbose();
bust_spinlocks(1);
pr_err("%s: err %04lx (%s) addr %08lx [#%d]\n", str, err & 0xffff,
trap_name(err & 0xffff), addr, ++die_counter);
print_modules();
show_regs(regs);
pr_err("Process: %s (pid: %d, stack limit = %p)\n", current->comm,
task_pid_nr(current), task_stack_page(current) + THREAD_SIZE);
bust_spinlocks(0);
add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE);
if (kexec_should_crash(current))
crash_kexec(regs);
if (in_interrupt())
panic("Fatal exception in interrupt");
if (panic_on_oops)
panic("Fatal exception");
spin_unlock_irq(&die_lock);
oops_exit();
do_exit(SIGSEGV);
}
#ifdef CONFIG_METAG_DSP
/*
* The ECH encoding specifies the size of a DSPRAM as,
*
* "slots" / 4
*
* A "slot" is the size of two DSPRAM bank entries; an entry from
* DSPRAM bank A and an entry from DSPRAM bank B. One DSPRAM bank
* entry is 4 bytes.
*/
#define SLOT_SZ 8
static inline unsigned int decode_dspram_size(unsigned int size)
{
unsigned int _sz = size & 0x7f;
return _sz * SLOT_SZ * 4;
}
static void dspram_save(struct meta_ext_context *dsp_ctx,
unsigned int ramA_sz, unsigned int ramB_sz)
{
unsigned int ram_sz[2];
int i;
ram_sz[0] = ramA_sz;
ram_sz[1] = ramB_sz;
for (i = 0; i < 2; i++) {
if (ram_sz[i] != 0) {
unsigned int sz;
if (i == 0)
sz = decode_dspram_size(ram_sz[i] >> 8);
else
sz = decode_dspram_size(ram_sz[i]);
if (dsp_ctx->ram[i] == NULL) {
dsp_ctx->ram[i] = kmalloc(sz, GFP_KERNEL);
if (dsp_ctx->ram[i] == NULL)
panic("couldn't save DSP context");
} else {
if (ram_sz[i] > dsp_ctx->ram_sz[i]) {
kfree(dsp_ctx->ram[i]);
dsp_ctx->ram[i] = kmalloc(sz,
GFP_KERNEL);
if (dsp_ctx->ram[i