123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990 |
- /*
- * 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] == 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 = 0;
- 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);
- }
|