diff options
Diffstat (limited to 'arch/metag/kernel/traps.c')
-rw-r--r-- | arch/metag/kernel/traps.c | 992 |
1 files changed, 0 insertions, 992 deletions
diff --git a/arch/metag/kernel/traps.c b/arch/metag/kernel/traps.c deleted file mode 100644 index 3b62b1b..0000000 --- a/arch/metag/kernel/traps.c +++ /dev/null @@ -1,992 +0,0 @@ -/* - * 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/sched/debug.h> -#include <linux/sched/task.h> -#include <linux/sched/task_stack.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] == NULL) - panic("couldn't save DSP context"); - } - } - - if (i == 0) - __TBIDspramSaveA(ram_sz[i], dsp_ctx->ram[i]); - else - __TBIDspramSaveB(ram_sz[i], dsp_ctx->ram[i]); - - dsp_ctx->ram_sz[i] = ram_sz[i]; - } - } -} -#endif /* CONFIG_METAG_DSP */ - -/* - * Allow interrupts to be nested and save any "extended" register - * context state, e.g. DSP regs and RAMs. - */ -static void nest_interrupts(TBIRES State, unsigned long mask) -{ -#ifdef CONFIG_METAG_DSP - struct meta_ext_context *dsp_ctx; - unsigned int D0_8; - - /* - * D0.8 may contain an ECH encoding. The upper 16 bits - * tell us what DSP resources the current process is - * using. OR the bits into the SaveMask so that - * __TBINestInts() knows what resources to save as - * part of this context. - * - * Don't save the context if we're nesting interrupts in the - * kernel because the kernel doesn't use DSP hardware. - */ - D0_8 = __core_reg_get(D0.8); - - if (D0_8 && (State.Sig.SaveMask & TBICTX_PRIV_BIT)) { - State.Sig.SaveMask |= (D0_8 >> 16); - - dsp_ctx = current->thread.dsp_context; - if (dsp_ctx == NULL) { - dsp_ctx = kzalloc(sizeof(*dsp_ctx), GFP_KERNEL); - if (dsp_ctx == NULL) - panic("couldn't save DSP context: ENOMEM"); - - current->thread.dsp_context = dsp_ctx; - } - - current->thread.user_flags |= (D0_8 & 0xffff0000); - __TBINestInts(State, &dsp_ctx->regs, mask); - dspram_save(dsp_ctx, D0_8 & 0x7f00, D0_8 & 0x007f); - } else - __TBINestInts(State, NULL, mask); -#else - __TBINestInts(State, NULL, mask); -#endif -} - -void head_end(TBIRES State, unsigned long mask) -{ - unsigned int savemask = (unsigned short)State.Sig.SaveMask; - unsigned int ctx_savemask = (unsigned short)State.Sig.pCtx->SaveMask; - - if (savemask & TBICTX_PRIV_BIT) { - ctx_savemask |= TBICTX_PRIV_BIT; - current->thread.user_flags = savemask; - } - - /* Always undo the sleep bit */ - ctx_savemask &= ~TBICTX_WAIT_BIT; - - /* Always save the catch buffer and RD pipe if they are dirty */ - savemask |= TBICTX_XCBF_BIT; - - /* Only save the catch and RD if we have not already done so. - * Note - the RD bits are in the pCtx only, and not in the - * State.SaveMask. - */ - if ((savemask & TBICTX_CBUF_BIT) || - (ctx_savemask & TBICTX_CBRP_BIT)) { - /* Have we already saved the buffers though? - * - See TestTrack 5071 */ - if (ctx_savemask & TBICTX_XCBF_BIT) { - /* Strip off the bits so the call to __TBINestInts - * won't save the buffers again. */ - savemask &= ~TBICTX_CBUF_BIT; - ctx_savemask &= ~TBICTX_CBRP_BIT; - } - } - -#ifdef CONFIG_METAG_META21 - { - unsigned int depth, txdefr; - - /* - * Save TXDEFR state. - * - * The process may have been interrupted after a LNKSET, but - * before it could read the DEFR state, so we mustn't lose that - * state or it could end up retrying an atomic operation that - * succeeded. - * - * All interrupts are disabled at this point so we - * don't need to perform any locking. We must do this - * dance before we use LNKGET or LNKSET. - */ - BUG_ON(current->thread.int_depth > HARDIRQ_BITS); - - depth = current->thread.int_depth++; - - txdefr = __core_reg_get(TXDEFR); - - txdefr &= TXDEFR_BUS_STATE_BITS; - if (txdefr & TXDEFR_LNKSET_SUCCESS) - current->thread.txdefr_failure &= ~(1 << depth); - else - current->thread.txdefr_failure |= (1 << depth); - } -#endif - - State.Sig.SaveMask = savemask; - State.Sig.pCtx->SaveMask = ctx_savemask; - - nest_interrupts(State, mask); - -#ifdef CONFIG_METAG_POISON_CATCH_BUFFERS - /* Poison the catch registers. This shows up any mistakes we have - * made in their handling MUCH quicker. - */ - __core_reg_set(TXCATCH0, 0x87650021); - __core_reg_set(TXCATCH1, 0x87654322); - __core_reg_set(TXCATCH2, 0x87654323); - __core_reg_set(TXCATCH3, 0x87654324); -#endif /* CONFIG_METAG_POISON_CATCH_BUFFERS */ -} - -TBIRES tail_end_sys(TBIRES State, int syscall, int *restart) -{ - struct pt_regs *regs = (struct pt_regs *)State.Sig.pCtx; - unsigned long flags; - - local_irq_disable(); - - if (user_mode(regs)) { - flags = current_thread_info()->flags; - if (flags & _TIF_WORK_MASK && - do_work_pending(regs, flags, syscall)) { - *restart = 1; - return State; - } - -#ifdef CONFIG_METAG_FPU - if (current->thread.fpu_context && - current->thread.fpu_context->needs_restore) { - __TBICtxFPURestore(State, current->thread.fpu_context); - /* - * Clearing this bit ensures the FP unit is not made - * active again unless it is used. - */ - State.Sig.SaveMask &= ~TBICTX_FPAC_BIT; - current->thread.fpu_context->needs_restore = false; - } - State.Sig.TrigMask |= TBI_TRIG_BIT(TBID_SIGNUM_DFR); -#endif - } - - /* TBI will turn interrupts back on at some point. */ - if (!irqs_disabled_flags((unsigned long)State.Sig.TrigMask)) - trace_hardirqs_on(); - -#ifdef CONFIG_METAG_DSP - /* - * If we previously saved an extended context then restore it - * now. Otherwise, clear D0.8 because this process is not - * using DSP hardware. - */ - if (State.Sig.pCtx->SaveMask & TBICTX_XEXT_BIT) { - unsigned int D0_8; - struct meta_ext_context *dsp_ctx = current->thread.dsp_context; - - /* Make sure we're going to return to userland. */ - BUG_ON(current->thread.int_depth != 1); - - if (dsp_ctx->ram_sz[0] > 0) - __TBIDspramRestoreA(dsp_ctx->ram_sz[0], - dsp_ctx->ram[0]); - if (dsp_ctx->ram_sz[1] > 0) - __TBIDspramRestoreB(dsp_ctx->ram_sz[1], - dsp_ctx->ram[1]); - - State.Sig.SaveMask |= State.Sig.pCtx->SaveMask; - __TBICtxRestore(State, current->thread.dsp_context); - D0_8 = __core_reg_get(D0.8); - D0_8 |= current->thread.user_flags & 0xffff0000; - D0_8 |= (dsp_ctx->ram_sz[1] | dsp_ctx->ram_sz[0]) & 0xffff; - __core_reg_set(D0.8, D0_8); - } else - __core_reg_set(D0.8, 0); -#endif /* CONFIG_METAG_DSP */ - -#ifdef CONFIG_METAG_META21 - { - unsigned int depth, txdefr; - - /* - * If there hasn't been a LNKSET since the last LNKGET then the - * link flag will be set, causing the next LNKSET to succeed if - * the addresses match. The two LNK operations may not be a pair - * (e.g. see atomic_read()), so the LNKSET should fail. - * We use a conditional-never LNKSET to clear the link flag - * without side effects. - */ - asm volatile("LNKSETDNV [D0Re0],D0Re0"); - - depth = --current->thread.int_depth; - - BUG_ON(user_mode(regs) && depth); - - txdefr = __core_reg_get(TXDEFR); - - txdefr &= ~TXDEFR_BUS_STATE_BITS; - - /* Do we need to restore a failure code into TXDEFR? */ - if (current->thread.txdefr_failure & (1 << depth)) - txdefr |= (TXDEFR_LNKSET_FAILURE | TXDEFR_BUS_TRIG_BIT); - else - txdefr |= (TXDEFR_LNKSET_SUCCESS | TXDEFR_BUS_TRIG_BIT); - - __core_reg_set(TXDEFR, txdefr); - } -#endif - return State; -} - -#ifdef CONFIG_SMP -/* - * If we took an interrupt in the middle of __kuser_get_tls then we need - * to rewind the PC to the start of the function in case the process - * gets migrated to another thread (SMP only) and it reads the wrong tls - * data. - */ -static inline void _restart_critical_section(TBIRES State) -{ - unsigned long get_tls_start; - unsigned long get_tls_end; - - get_tls_start = (unsigned long)__kuser_get_tls - - (unsigned long)&__user_gateway_start; - - get_tls_start += USER_GATEWAY_PAGE; - - get_tls_end = (unsigned long)__kuser_get_tls_end - - (unsigned long)&__user_gateway_start; - - get_tls_end += USER_GATEWAY_PAGE; - - if ((State.Sig.pCtx->CurrPC >= get_tls_start) && - (State.Sig.pCtx->CurrPC < get_tls_end)) - State.Sig.pCtx->CurrPC = get_tls_start; -} -#else -/* - * If we took an interrupt in the middle of - * __kuser_cmpxchg then we need to rewind the PC to the - * start of the function. - */ -static inline void _restart_critical_section(TBIRES State) -{ - unsigned long cmpxchg_start; - unsigned long cmpxchg_end; - - cmpxchg_start = (unsigned long)__kuser_cmpxchg - - (unsigned long)&__user_gateway_start; - - cmpxchg_start += USER_GATEWAY_PAGE; - - cmpxchg_end = (unsigned long)__kuser_cmpxchg_end - - (unsigned long)&__user_gateway_start; - - cmpxchg_end += USER_GATEWAY_PAGE; - - if ((State.Sig.pCtx->CurrPC >= cmpxchg_start) && - (State.Sig.pCtx->CurrPC < cmpxchg_end)) - State.Sig.pCtx->CurrPC = cmpxchg_start; -} -#endif - -/* Used by kick_handler() */ -void restart_critical_section(TBIRES State) -{ - _restart_critical_section(State); -} - -TBIRES trigger_handler(TBIRES State, int SigNum, int Triggers, int Inst, - PTBI pTBI) -{ - head_end(State, ~INTS_OFF_MASK); - - /* If we interrupted user code handle any critical sections. */ - if (State.Sig.SaveMask & TBICTX_PRIV_BIT) - _restart_critical_section(State); - - trace_hardirqs_off(); - - do_IRQ(SigNum, (struct pt_regs *)State.Sig.pCtx); - - return tail_end(State); -} - -static unsigned int load_fault(PTBICTXEXTCB0 pbuf) -{ - return pbuf->CBFlags & TXCATCH0_READ_BIT; -} - -static unsigned long fault_address(PTBICTXEXTCB0 pbuf) -{ - return pbuf->CBAddr; -} - -static void unhandled_fault(struct pt_regs *regs, unsigned long addr, - int signo, int code, int trapno) -{ - if (user_mode(regs)) { - siginfo_t info; - - if (show_unhandled_signals && unhandled_signal(current, signo) - && printk_ratelimit()) { - - pr_info("pid %d unhandled fault: pc 0x%08x, addr 0x%08lx, trap %d (%s)\n", - current->pid, regs->ctx.CurrPC, addr, - trapno, trap_name(trapno)); - print_vma_addr(" in ", regs->ctx.CurrPC); - print_vma_addr(" rtp in ", regs->ctx.DX[4].U1); - printk("\n"); - show_regs(regs); - } - - info.si_signo = signo; - info.si_errno = 0; - info.si_code = code; - info.si_addr = (__force void __user *)addr; - info.si_trapno = trapno; - force_sig_info(signo, &info, current); - } else { - die("Oops", regs, trapno, addr); - } -} - -static int handle_data_fault(PTBICTXEXTCB0 pcbuf, struct pt_regs *regs, - unsigned int data_address, int trapno) -{ - int ret; - - ret = do_page_fault(regs, data_address, !load_fault(pcbuf), trapno); - - return ret; -} - -static unsigned long get_inst_fault_address(struct pt_regs *regs) -{ - return regs->ctx.CurrPC; -} - -TBIRES fault_handler(TBIRES State, int SigNum, int Triggers, - int Inst, PTBI pTBI) -{ - struct pt_regs *regs = (struct pt_regs *)State.Sig.pCtx; - PTBICTXEXTCB0 pcbuf = (PTBICTXEXTCB0)®s->extcb0; - unsigned long data_address; - - head_end(State, ~INTS_OFF_MASK); - - /* Hardware breakpoint or data watch */ - if ((SigNum == TBIXXF_SIGNUM_IHF) || - ((SigNum == TBIXXF_SIGNUM_DHF) && - (pcbuf[0].CBFlags & (TXCATCH0_WATCH1_BIT | - TXCATCH0_WATCH0_BIT)))) { - State = __TBIUnExpXXX(State, SigNum, Triggers, Inst, - pTBI); - return tail_end(State); - } - - local_irq_enable(); - - data_address = fault_address(pcbuf); - - switch (SigNum) { - case TBIXXF_SIGNUM_IGF: - /* 1st-level entry invalid (instruction fetch) */ - case TBIXXF_SIGNUM_IPF: { - /* 2nd-level entry invalid (instruction fetch) */ - unsigned long addr = get_inst_fault_address(regs); - do_page_fault(regs, addr, 0, SigNum); - break; - } - - case TBIXXF_SIGNUM_DGF: - /* 1st-level entry invalid (data access) */ - case TBIXXF_SIGNUM_DPF: - /* 2nd-level entry invalid (data access) */ - case TBIXXF_SIGNUM_DWF: - /* Write to read only page */ - handle_data_fault(pcbuf, regs, data_address, SigNum); - break; - - case TBIXXF_SIGNUM_IIF: - /* Illegal instruction */ - unhandled_fault(regs, regs->ctx.CurrPC, SIGILL, ILL_ILLOPC, - SigNum); - break; - - case TBIXXF_SIGNUM_DHF: - /* Unaligned access */ - unhandled_fault(regs, data_address, SIGBUS, BUS_ADRALN, - SigNum); - break; - case TBIXXF_SIGNUM_PGF: - /* Privilege violation */ - unhandled_fault(regs, data_address, SIGSEGV, SEGV_ACCERR, - SigNum); - break; - default: - BUG(); - break; - } - - return tail_end(State); -} - -static bool switch_is_syscall(unsigned int inst) -{ - return inst == __METAG_SW_ENCODING(SYS); -} - -static bool switch_is_legacy_syscall(unsigned int inst) -{ - return inst == __METAG_SW_ENCODING(SYS_LEGACY); -} - -static inline void step_over_switch(struct pt_regs *regs, unsigned int inst) -{ - regs->ctx.CurrPC += 4; -} - -static inline int test_syscall_work(void) -{ - return current_thread_info()->flags & _TIF_WORK_SYSCALL_MASK; -} - -TBIRES switch1_handler(TBIRES State, int SigNum, int Triggers, - int Inst, PTBI pTBI) -{ - struct pt_regs *regs = (struct pt_regs *)State.Sig.pCtx; - unsigned int sysnumber; - unsigned long long a1_a2, a3_a4, a5_a6; - LPSYSCALL syscall_entry; - int restart; - - head_end(State, ~INTS_OFF_MASK); - - /* - * If this is not a syscall SWITCH it could be a breakpoint. - */ - if (!switch_is_syscall(Inst)) { - /* - * Alert the user if they're trying to use legacy system - * calls. This suggests they need to update their C - * library and build against up to date kernel headers. - */ - if (switch_is_legacy_syscall(Inst)) - pr_warn_once("WARNING: A legacy syscall was made. Your userland needs updating.\n"); - /* - * We don't know how to handle the SWITCH and cannot - * safely ignore it, so treat all unknown switches - * (including breakpoints) as traps. - */ - force_sig(SIGTRAP, current); - return tail_end(State); - } - - local_irq_enable(); - -restart_syscall: - restart = 0; - sysnumber = regs->ctx.DX[0].U1; - - if (test_syscall_work()) - sysnumber = syscall_trace_enter(regs); - - /* Skip over the SWITCH instruction - or you just get 'stuck' on it! */ - step_over_switch(regs, Inst); - - if (sysnumber >= __NR_syscalls) { - pr_debug("unknown syscall number: %d\n", sysnumber); - syscall_entry = (LPSYSCALL) sys_ni_syscall; - } else { - syscall_entry = (LPSYSCALL) sys_call_table[sysnumber]; - } - - /* Use 64bit loads for speed. */ - a5_a6 = *(unsigned long long *)®s->ctx.DX[1]; - a3_a4 = *(unsigned long long *)®s->ctx.DX[2]; - a1_a2 = *(unsigned long long *)®s->ctx.DX[3]; - - /* here is the actual call to the syscall handler functions */ - regs->ctx.DX[0].U0 = syscall_entry(a1_a2, a3_a4, a5_a6); - - if (test_syscall_work()) - syscall_trace_leave(regs); - - State = tail_end_sys(State, sysnumber, &restart); - /* Handlerless restarts shouldn't go via userland */ - if (restart) - goto restart_syscall; - return State; -} - -TBIRES switchx_handler(TBIRES State, int SigNum, int Triggers, - int Inst, PTBI pTBI) -{ - struct pt_regs *regs = (struct pt_regs *)State.Sig.pCtx; - - /* - * This can be caused by any user process simply executing an unusual - * SWITCH instruction. If there's no DA, __TBIUnExpXXX will cause the - * thread to stop, so signal a SIGTRAP instead. - */ - head_end(State, ~INTS_OFF_MASK); - if (user_mode(regs)) - force_sig(SIGTRAP, current); - else - State = __TBIUnExpXXX(State, SigNum, Triggers, Inst, pTBI); - return tail_end(State); -} - -#ifdef CONFIG_METAG_META21 -TBIRES fpe_handler(TBIRES State, int SigNum, int Triggers, int Inst, PTBI pTBI) -{ - struct pt_regs *regs = (struct pt_regs *)State.Sig.pCtx; - unsigned int error_state = Triggers; - siginfo_t info; - - head_end(State, ~INTS_OFF_MASK); - - local_irq_enable(); - - info.si_signo = SIGFPE; - - if (error_state & TXSTAT_FPE_INVALID_BIT) - info.si_code = FPE_FLTINV; - else if (error_state & TXSTAT_FPE_DIVBYZERO_BIT) - info.si_code = FPE_FLTDIV; - else if (error_state & TXSTAT_FPE_OVERFLOW_BIT) - info.si_code = FPE_FLTOVF; - else if (error_state & TXSTAT_FPE_UNDERFLOW_BIT) - info.si_code = FPE_FLTUND; - else if (error_state & TXSTAT_FPE_INEXACT_BIT) - info.si_code = FPE_FLTRES; - else - info.si_code = FPE_FIXME; - info.si_errno = 0; - info.si_addr = (__force void __user *)regs->ctx.CurrPC; - force_sig_info(SIGFPE, &info, current); - - return tail_end(State); -} -#endif - -#ifdef CONFIG_METAG_SUSPEND_MEM -struct traps_context { - PTBIAPIFN fnSigs[TBID_SIGNUM_MAX + 1]; -}; - -static struct traps_context *metag_traps_context; - -int traps_save_context(void) -{ - unsigned long cpu = smp_processor_id(); - PTBI _pTBI = per_cpu(pTBI, cpu); - struct traps_context *context; - - context = kzalloc(sizeof(*context), GFP_ATOMIC); - if (!context) - return -ENOMEM; - - memcpy(context->fnSigs, (void *)_pTBI->fnSigs, sizeof(context->fnSigs)); - - metag_traps_context = context; - return 0; -} - -int traps_restore_context(void) -{ - unsigned long cpu = smp_processor_id(); - PTBI _pTBI = per_cpu(pTBI, cpu); - struct traps_context *context = metag_traps_context; - - metag_traps_context = NULL; - - memcpy((void *)_pTBI->fnSigs, context->fnSigs, sizeof(context->fnSigs)); - - kfree(context); - return 0; -} -#endif - -#ifdef CONFIG_SMP -static inline unsigned int _get_trigger_mask(void) -{ - unsigned long cpu = smp_processor_id(); - return per_cpu(trigger_mask, cpu); -} - -unsigned int get_trigger_mask(void) -{ - return _get_trigger_mask(); -} -EXPORT_SYMBOL(get_trigger_mask); - -static void set_trigger_mask(unsigned int mask) -{ - unsigned long cpu = smp_processor_id(); - per_cpu(trigger_mask, cpu) = mask; -} - -void arch_local_irq_enable(void) -{ - preempt_disable(); - arch_local_irq_restore(_get_trigger_mask()); - preempt_enable_no_resched(); -} -EXPORT_SYMBOL(arch_local_irq_enable); -#else -static void set_trigger_mask(unsigned int mask) -{ - global_trigger_mask = mask; -} -#endif - -void per_cpu_trap_init(unsigned long cpu) -{ - TBIRES int_context; - unsigned int thread = cpu_2_hwthread_id[cpu]; - - set_trigger_mask(TBI_INTS_INIT(thread) | /* interrupts */ - TBI_TRIG_BIT(TBID_SIGNUM_LWK) | /* low level kick */ - TBI_TRIG_BIT(TBID_SIGNUM_SW1)); - - /* non-priv - use current stack */ - int_context.Sig.pCtx = NULL; - /* Start with interrupts off */ - int_context.Sig.TrigMask = INTS_OFF_MASK; - int_context.Sig.SaveMask = 0; - - /* And call __TBIASyncTrigger() */ - __TBIASyncTrigger(int_context); -} - -void __init trap_init(void) -{ - unsigned long cpu = smp_processor_id(); - PTBI _pTBI = per_cpu(pTBI, cpu); - - _pTBI->fnSigs[TBID_SIGNUM_XXF] = fault_handler; - _pTBI->fnSigs[TBID_SIGNUM_SW0] = switchx_handler; - _pTBI->fnSigs[TBID_SIGNUM_SW1] = switch1_handler; - _pTBI->fnSigs[TBID_SIGNUM_SW2] = switchx_handler; - _pTBI->fnSigs[TBID_SIGNUM_SW3] = switchx_handler; - _pTBI->fnSigs[TBID_SIGNUM_LWK] = kick_handler; - -#ifdef CONFIG_METAG_META21 - _pTBI->fnSigs[TBID_SIGNUM_DFR] = __TBIHandleDFR; - _pTBI->fnSigs[TBID_SIGNUM_FPE] = fpe_handler; -#endif - - per_cpu_trap_init(cpu); -} - -void tbi_startup_interrupt(int irq) -{ - unsigned long cpu = smp_processor_id(); - PTBI _pTBI = per_cpu(pTBI, cpu); - - BUG_ON(irq > TBID_SIGNUM_MAX); - - /* For TR1 and TR2, the thread id is encoded in the irq number */ - if (irq >= TBID_SIGNUM_T10 && irq < TBID_SIGNUM_TR3) - cpu = hwthread_id_2_cpu[(irq - TBID_SIGNUM_T10) % 4]; - - set_trigger_mask(get_trigger_mask() | TBI_TRIG_BIT(irq)); - - _pTBI->fnSigs[irq] = trigger_handler; -} - -void tbi_shutdown_interrupt(int irq) -{ - unsigned long cpu = smp_processor_id(); - PTBI _pTBI = per_cpu(pTBI, cpu); - - BUG_ON(irq > TBID_SIGNUM_MAX); - - set_trigger_mask(get_trigger_mask() & ~TBI_TRIG_BIT(irq)); - - _pTBI->fnSigs[irq] = __TBIUnExpXXX; -} - -int ret_from_fork(TBIRES arg) -{ - struct task_struct *prev = arg.Switch.pPara; - struct task_struct *tsk = current; - struct pt_regs *regs = task_pt_regs(tsk); - int (*fn)(void *); - TBIRES Next; - - schedule_tail(prev); - - if (tsk->flags & PF_KTHREAD) { - fn = (void *)regs->ctx.DX[4].U1; - BUG_ON(!fn); - - fn((void *)regs->ctx.DX[3].U1); - } - - if (test_syscall_work()) - syscall_trace_leave(regs); - - preempt_disable(); - - Next.Sig.TrigMask = get_trigger_mask(); - Next.Sig.SaveMask = 0; - Next.Sig.pCtx = ®s->ctx; - - set_gateway_tls(current->thread.tls_ptr); - - preempt_enable_no_resched(); - - /* And interrupts should come back on when we resume the real usermode - * code. Call __TBIASyncResume() - */ - __TBIASyncResume(tail_end(Next)); - /* ASyncResume should NEVER return */ - BUG(); - return 0; -} - -void show_trace(struct task_struct *tsk, unsigned long *sp, - struct pt_regs *regs) -{ - unsigned long addr; -#ifdef CONFIG_FRAME_POINTER - unsigned long fp, fpnew; - unsigned long stack; -#endif - - if (regs && user_mode(regs)) - return; - - printk("\nCall trace: "); -#ifdef CONFIG_KALLSYMS - printk("\n"); -#endif - - if (!tsk) - tsk = current; - -#ifdef CONFIG_FRAME_POINTER - if (regs) { - print_ip_sym(regs->ctx.CurrPC); - fp = regs->ctx.AX[1].U0; - } else { - fp = __core_reg_get(A0FrP); - } - - /* detect when the frame pointer has been used for other purposes and - * doesn't point to the stack (it may point completely elsewhere which - * kstack_end may not detect). - */ - stack = (unsigned long)task_stack_page(tsk); - while (fp >= stack && fp + 8 <= stack + THREAD_SIZE) { - addr = __raw_readl((unsigned long *)(fp + 4)) - 4; - if (kernel_text_address(addr)) - print_ip_sym(addr); - else - break; - /* stack grows up, so frame pointers must decrease */ - fpnew = __raw_readl((unsigned long *)(fp + 0)); - if (fpnew >= fp) - break; - fp = fpnew; - } -#else - while (!kstack_end(sp)) { - addr = (*sp--) - 4; - if (kernel_text_address(addr)) - print_ip_sym(addr); - } -#endif - - printk("\n"); - - debug_show_held_locks(tsk); -} - -void show_stack(struct task_struct *tsk, unsigned long *sp) -{ - if (!tsk) - tsk = current; - if (tsk == current) - sp = (unsigned long *)current_stack_pointer; - else - sp = (unsigned long *)tsk->thread.kernel_context->AX[0].U0; - - show_trace(tsk, sp, NULL); -} |