📄 entry.s
字号:
/* * File: arch/blackfin/mach-common/entry.S * Based on: * Author: Linus Torvalds * * Created: ? * Description: contains the system-call and fault low-level handling routines. * This also contains the timer-interrupt handler, as well as all * interrupts and faults that can result in a task-switch. * * Modified: * Copyright 2004-2006 Analog Devices Inc. * * Bugs: Enter bugs at http://blackfin.uclinux.org/ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see the file COPYING, or write * to the Free Software Foundation, Inc., * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *//* NOTE: This code handles signal-recognition, which happens every time * after a timer-interrupt and after each system call. */#include <linux/init.h>#include <linux/linkage.h>#include <linux/unistd.h>#include <asm/blackfin.h>#include <asm/errno.h>#include <asm/thread_info.h> /* TIF_NEED_RESCHED */#include <asm/asm-offsets.h>#include <asm/trace.h>#include <asm/mach-common/context.S>#if defined(CONFIG_BFIN_SCRATCH_REG_RETN)# define EX_SCRATCH_REG RETN#elif defined(CONFIG_BFIN_SCRATCH_REG_RETE)# define EX_SCRATCH_REG RETE#else# define EX_SCRATCH_REG CYCLES#endif#if ANOMALY_05000281ENTRY(_safe_speculative_execution) NOP; NOP; NOP; jump _safe_speculative_execution;ENDPROC(_safe_speculative_execution)#endif#ifdef CONFIG_EXCPT_IRQ_SYSC_L1.section .l1.text#else.text#endif/* Slightly simplified and streamlined entry point for CPLB misses. * This one does not lower the level to IRQ5, and thus can be used to * patch up CPLB misses on the kernel stack. */#if ANOMALY_05000261#define _ex_dviol _ex_workaround_261#define _ex_dmiss _ex_workaround_261#define _ex_dmult _ex_workaround_261ENTRY(_ex_workaround_261) /* * Work around an anomaly: if we see a new DCPLB fault, return * without doing anything. Then, if we get the same fault again, * handle it. */ P4 = R7; /* Store EXCAUSE */ p5.l = _last_cplb_fault_retx; p5.h = _last_cplb_fault_retx; r7 = [p5]; r6 = retx; [p5] = r6; cc = r6 == r7; if !cc jump _bfin_return_from_exception; /* fall through */ R7 = P4; R6 = 0x26; /* Data CPLB Miss */ cc = R6 == R7; if cc jump _ex_dcplb_miss (BP); /* Handle 0x23 Data CPLB Protection Violation * and Data CPLB Multiple Hits - Linux Trap Zero */ jump _ex_trap_c;ENDPROC(_ex_workaround_261)#else#define _ex_dviol _ex_trap_c#define _ex_dmiss _ex_dcplb_miss#define _ex_dmult _ex_trap_c#endifENTRY(_ex_dcplb_miss)ENTRY(_ex_icplb_miss) (R7:6,P5:4) = [sp++]; ASTAT = [sp++]; SAVE_ALL_SYS call __cplb_hdr; DEBUG_START_HWTRACE(p5, r7) RESTORE_ALL_SYS SP = EX_SCRATCH_REG; rtx;ENDPROC(_ex_icplb_miss)ENTRY(_ex_syscall) DEBUG_START_HWTRACE(p5, r7) (R7:6,P5:4) = [sp++]; ASTAT = [sp++]; raise 15; /* invoked by TRAP #0, for sys call */ sp = EX_SCRATCH_REG; rtxENDPROC(_ex_syscall)ENTRY(_ex_soft_bp) r7 = retx; r7 += -2; retx = r7; jump.s _ex_trap_c;ENDPROC(_ex_soft_bp)ENTRY(_ex_single_step) r7 = retx; r6 = reti; cc = r7 == r6; if cc jump _bfin_return_from_exception r7 = syscfg; bitclr (r7, 0); syscfg = R7; p5.l = lo(IPEND); p5.h = hi(IPEND); r6 = [p5]; cc = bittst(r6, 5); if !cc jump _ex_trap_c; p4.l = lo(EVT5); p4.h = hi(EVT5); r6.h = _exception_to_level5; r6.l = _exception_to_level5; r7 = [p4]; cc = r6 == r7; if !cc jump _ex_trap_c;ENDPROC(_ex_single_step)ENTRY(_bfin_return_from_exception) DEBUG_START_HWTRACE(p5, r7)#if ANOMALY_05000257 R7=LC0; LC0=R7; R7=LC1; LC1=R7;#endif (R7:6,P5:4) = [sp++]; ASTAT = [sp++]; sp = EX_SCRATCH_REG; rtx;ENDPROC(_bfin_return_from_exception)ENTRY(_handle_bad_cplb) /* To get here, we just tried and failed to change a CPLB * so, handle things in trap_c (C code), by lowering to * IRQ5, just like we normally do. Since this is not a * "normal" return path, we have a do alot of stuff to * the stack to get ready so, we can fall through - we * need to make a CPLB exception look like a normal exception */ DEBUG_START_HWTRACE(p5, r7) RESTORE_ALL_SYS [--sp] = ASTAT; [--sp] = (R7:6, P5:4);ENTRY(_ex_replaceable) nop;ENTRY(_ex_trap_c) /* Make sure we are not in a double fault */ p4.l = lo(IPEND); p4.h = hi(IPEND); r7 = [p4]; CC = BITTST (r7, 5); if CC jump _double_fault; /* Call C code (trap_c) to handle the exception, which most * likely involves sending a signal to the current process. * To avoid double faults, lower our priority to IRQ5 first. */ P5.h = _exception_to_level5; P5.l = _exception_to_level5; p4.l = lo(EVT5); p4.h = hi(EVT5); [p4] = p5; csync; /* Disable all interrupts, but make sure level 5 is enabled so * we can switch to that level. Save the old mask. */ cli r6; p4.l = _excpt_saved_imask; p4.h = _excpt_saved_imask; [p4] = r6; r6 = 0x3f; sti r6; /* Save the excause into a circular buffer, in case the instruction * which caused this excecptions causes others. */ P5.l = _in_ptr_excause; P5.h = _in_ptr_excause; R7 = [P5]; R7 += 4; R6 = 0xF; R7 = R7 & R6; [P5] = R7; R6.l = _excause_circ_buf; R6.h = _excause_circ_buf; R7 = R7 + R6; p5 = R7; R6 = SEQSTAT; [P5] = R6; DEBUG_START_HWTRACE(p5, r7) (R7:6,P5:4) = [sp++]; ASTAT = [sp++]; SP = EX_SCRATCH_REG; raise 5; rtx;ENDPROC(_ex_trap_c)/* We just realized we got an exception, while we were processing a different * exception. This is a unrecoverable event, so crash */ENTRY(_double_fault) /* Turn caches & protection off, to ensure we don't get any more * double exceptions */ P4.L = LO(IMEM_CONTROL); P4.H = HI(IMEM_CONTROL); R5 = [P4]; /* Control Register*/ BITCLR(R5,ENICPLB_P); SSYNC; /* SSYNC required before writing to IMEM_CONTROL. */ .align 8; [P4] = R5; SSYNC; P4.L = LO(DMEM_CONTROL); P4.H = HI(DMEM_CONTROL); R5 = [P4]; BITCLR(R5,ENDCPLB_P); SSYNC; /* SSYNC required before writing to DMEM_CONTROL. */ .align 8; [P4] = R5; SSYNC; /* Fix up the stack */ (R7:6,P5:4) = [sp++]; ASTAT = [sp++]; SP = EX_SCRATCH_REG; /* We should be out of the exception stack, and back down into * kernel or user space stack */ SAVE_ALL_SYS r0 = sp; /* stack frame pt_regs pointer argument ==> r0 */ SP += -12; call _double_fault_c; SP += 12;.L_double_fault_panic: JUMP .L_double_fault_panicENDPROC(_double_fault)ENTRY(_exception_to_level5) SAVE_ALL_SYS /* Restore interrupt mask. We haven't pushed RETI, so this * doesn't enable interrupts until we return from this handler. */ p4.l = _excpt_saved_imask; p4.h = _excpt_saved_imask; r6 = [p4]; sti r6; /* Restore the hardware error vector. */ P5.h = _evt_ivhw; P5.l = _evt_ivhw; p4.l = lo(EVT5); p4.h = hi(EVT5); [p4] = p5; csync; p2.l = lo(IPEND); p2.h = hi(IPEND); csync; r0 = [p2]; /* Read current IPEND */ [sp + PT_IPEND] = r0; /* Store IPEND */ /* Pop the excause from the circular buffer and push it on the stack * (in the right place - if you change the location of SEQSTAT, you * must change this offset. */.L_excep_to_5_again: P5.l = _out_ptr_excause; P5.h = _out_ptr_excause; R7 = [P5]; R7 += 4; R6 = 0xF; R7 = R7 & R6; [P5] = R7; R6.l = _excause_circ_buf; R6.h = _excause_circ_buf; R7 = R7 + R6; P5 = R7; R1 = [P5]; [SP + 8] = r1; r0 = sp; /* stack frame pt_regs pointer argument ==> r0 */ SP += -12; call _trap_c; SP += 12; /* See if anything else is in the exception buffer * if there is, process it */ P5.l = _out_ptr_excause; P5.h = _out_ptr_excause; P4.l = _in_ptr_excause; P4.h = _in_ptr_excause; R6 = [P5]; R7 = [P4]; CC = R6 == R7; if ! CC JUMP .L_excep_to_5_again call _ret_from_exception; RESTORE_ALL_SYS rti;ENDPROC(_exception_to_level5)ENTRY(_trap) /* Exception: 4th entry into system event table(supervisor mode)*/ /* Since the kernel stack can be anywhere, it's not guaranteed to be * covered by a CPLB. Switch to an exception stack; use RETN as a * scratch register (for want of a better option). */ EX_SCRATCH_REG = sp; sp.l = _exception_stack_top; sp.h = _exception_stack_top; /* Try to deal with syscalls quickly. */ [--sp] = ASTAT; [--sp] = (R7:6, P5:4); DEBUG_STOP_HWTRACE(p5, r7) r7 = SEQSTAT; /* reason code is in bit 5:0 */ r6.l = lo(SEQSTAT_EXCAUSE); r6.h = hi(SEQSTAT_EXCAUSE); r7 = r7 & r6; p5.h = _ex_table; p5.l = _ex_table; p4 = r7; p5 = p5 + (p4 << 2); p4 = [p5]; jump (p4);.Lbadsys: r7 = -ENOSYS; /* signextending enough */ [sp + PT_R0] = r7; /* return value from system call */ jump .Lsyscall_really_exit;ENDPROC(_trap)ENTRY(_kernel_execve) link SIZEOF_PTREGS; p0 = sp; r3 = SIZEOF_PTREGS / 4; r4 = 0(x);0: [p0++] = r4; r3 += -1; cc = r3 == 0; if !cc jump 0b (bp); p0 = sp; sp += -16; [sp + 12] = p0; call _do_execve; SP += 16; cc = r0 == 0; if ! cc jump 1f; /* Success. Copy our temporary pt_regs to the top of the kernel * stack and do a normal exception return. */ r1 = sp; r0 = (-KERNEL_STACK_SIZE) (x); r1 = r1 & r0; p2 = r1; p3 = [p2]; r0 = KERNEL_STACK_SIZE - 4 (z); p1 = r0; p1 = p1 + p2; p0 = fp; r4 = [p0--]; r3 = SIZEOF_PTREGS / 4;0: r4 = [p0--]; [p1--] = r4; r3 += -1; cc = r3 == 0; if ! cc jump 0b (bp); r0 = (KERNEL_STACK_SIZE - SIZEOF_PTREGS) (z); p1 = r0; p1 = p1 + p2; sp = p1; r0 = syscfg; [SP + PT_SYSCFG] = r0; [p3 + (TASK_THREAD + THREAD_KSP)] = sp; RESTORE_CONTEXT; rti;1: unlink; rts;ENDPROC(_kernel_execve)ENTRY(_system_call) /* Store IPEND */ p2.l = lo(IPEND); p2.h = hi(IPEND); csync; r0 = [p2]; [sp + PT_IPEND] = r0; /* Store RETS for now */ r0 = rets; [sp + PT_RESERVED] = r0; /* Set the stack for the current process */ r7 = sp; r6.l = lo(ALIGN_PAGE_MASK); r6.h = hi(ALIGN_PAGE_MASK); r7 = r7 & r6; /* thread_info */ p2 = r7; p2 = [p2]; [p2+(TASK_THREAD+THREAD_KSP)] = sp; /* Check the System Call */ r7 = __NR_syscall; /* System call number is passed in P0 */ r6 = p0; cc = r6 < r7; if ! cc jump .Lbadsys;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -