📄 entry.s
字号:
/* $Id: entry.S,v 1.37 2004/06/11 13:02:46 doyu Exp $ * * linux/arch/sh/entry.S * * Copyright (C) 1999, 2000, 2002 Niibe Yutaka * Copyright (C) 2003 Paul Mundt * * 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/sys.h>#include <linux/linkage.h>#include <linux/config.h>#include <asm/thread_info.h>#include <asm/unistd.h>#if !defined(CONFIG_NFSD) && !defined(CONFIG_NFSD_MODULE)#define sys_nfsservctl sys_ni_syscall#endif#if !defined(CONFIG_MMU)#define sys_madvise sys_ni_syscall#define sys_readahead sys_ni_syscall#define sys_mprotect sys_ni_syscall#define sys_msync sys_ni_syscall#define sys_mlock sys_ni_syscall#define sys_munlock sys_ni_syscall#define sys_mlockall sys_ni_syscall#define sys_munlockall sys_ni_syscall#define sys_mremap sys_ni_syscall#define sys_mincore sys_ni_syscall#define sys_remap_file_pages sys_ni_syscall#endif! NOTE:! GNU as (as of 2.9.1) changes bf/s into bt/s and bra, when the address! to be jumped is too far, but it causes illegal slot exception./* * entry.S 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. * * NOTE: This code handles signal-recognition, which happens every time * after a timer-interrupt and after each system call. * * NOTE: This code uses a convention that instructions in the delay slot * of a transfer-control instruction are indented by an extra space, thus: * * jmp @k0 ! control-transfer instruction * ldc k1, ssr ! delay slot * * Stack layout in 'ret_from_syscall': * ptrace needs to have all regs on the stack. * if the order here is changed, it needs to be * updated in ptrace.c and ptrace.h * * r0 * ... * r15 = stack pointer * spc * pr * ssr * gbr * mach * macl * syscall # * */ENOSYS = 38EINVAL = 22#if defined(CONFIG_CPU_SH3)TRA = 0xffffffd0EXPEVT = 0xffffffd4#if defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709) || \ defined(CONFIG_CPU_SUBTYPE_SH7300) || defined(CONFIG_CPU_SUBTYPE_SH7705)INTEVT = 0xa4000000 ! INTEVTE2(0xa4000000)#elseINTEVT = 0xffffffd8#endifMMU_TEA = 0xfffffffc ! TLB Exception Address Register#elif defined(CONFIG_CPU_SH4)TRA = 0xff000020EXPEVT = 0xff000024INTEVT = 0xff000028MMU_TEA = 0xff00000c ! TLB Exception Address Register#endif#if defined(CONFIG_KGDB_NMI)NMI_VEC = 0x1c0 ! Must catch early for debounce#endif/* Offsets to the stack */OFF_R0 = 0 /* Return value. New ABI also arg4 */OFF_R1 = 4 /* New ABI: arg5 */OFF_R2 = 8 /* New ABI: arg6 */OFF_R3 = 12 /* New ABI: syscall_nr */OFF_R4 = 16 /* New ABI: arg0 */OFF_R5 = 20 /* New ABI: arg1 */OFF_R6 = 24 /* New ABI: arg2 */OFF_R7 = 28 /* New ABI: arg3 */OFF_SP = (15*4)OFF_PC = (16*4)OFF_SR = (16*4+8)OFF_TRA = (16*4+6*4)#define k0 r0#define k1 r1#define k2 r2#define k3 r3#define k4 r4#define k_ex_code r2_bank /* r2_bank1 */#define g_imask r6 /* r6_bank1 */#define k_g_imask r6_bank /* r6_bank1 */#define current r7 /* r7_bank1 *//* * Kernel mode register usage: * k0 scratch * k1 scratch * k2 scratch (Exception code) * k3 scratch (Return address) * k4 scratch * k5 reserved * k6 Global Interrupt Mask (0--15 << 4) * k7 CURRENT_THREAD_INFO (pointer to current thread info) */!! TLB Miss / Initial Page write exception handling! _and_! TLB hits, but the access violate the protection.! It can be valid access, such as stack grow and/or C-O-W.!!! Find the pmd/pte entry and loadtlb! If it's not found, cause address error (SEGV)!! Although this could be written in assembly language (and it'd be faster),! this first version depends *much* on C implementation.!#define CLI() \ stc sr, r0; \ or #0xf0, r0; \ ldc r0, sr#define STI() \ mov.l __INV_IMASK, r11; \ stc sr, r10; \ and r11, r10; \ stc k_g_imask, r11; \ or r11, r10; \ ldc r10, sr#if defined(CONFIG_PREEMPT)# define preempt_stop() CLI()#else# define preempt_stop()# define resume_kernel restore_all#endif#if defined(CONFIG_MMU) .align 2ENTRY(tlb_miss_load) bra call_dpf mov #0, r5 .align 2ENTRY(tlb_miss_store) bra call_dpf mov #1, r5 .align 2ENTRY(initial_page_write) bra call_dpf mov #1, r5 .align 2ENTRY(tlb_protection_violation_load) bra call_dpf mov #0, r5 .align 2ENTRY(tlb_protection_violation_store) bra call_dpf mov #1, r5call_dpf: mov.l 1f, r0 mov r5, r8 mov.l @r0, r6 mov r6, r9 mov.l 2f, r0 sts pr, r10 jsr @r0 mov r15, r4 ! tst r0, r0 bf/s 0f lds r10, pr rts nop0: STI() mov.l 3f, r0 mov r9, r6 mov r8, r5 jmp @r0 mov r15, r4 .align 21: .long MMU_TEA2: .long __do_page_fault3: .long do_page_fault .align 2ENTRY(address_error_load) bra call_dae mov #0,r5 ! writeaccess = 0 .align 2ENTRY(address_error_store) bra call_dae mov #1,r5 ! writeaccess = 1 .align 2call_dae: mov.l 1f, r0 mov.l @r0, r6 ! address mov.l 2f, r0 jmp @r0 mov r15, r4 ! regs .align 21: .long MMU_TEA2: .long do_address_error#endif /* CONFIG_MMU */#if defined(CONFIG_SH_STANDARD_BIOS) || defined(CONFIG_SH_KGDB)! Handle kernel debug if either kgdb (SW) or gdb-stub (FW) is present.! If both are configured, handle the debug traps (breakpoints) in SW,! but still allow BIOS traps to FW. .align 2debug_kernel:#if defined(CONFIG_SH_STANDARD_BIOS) && defined(CONFIG_SH_KGDB) /* Force BIOS call to FW (debug_trap put TRA in r8) */ mov r8,r0 shlr2 r0 cmp/eq #0x3f,r0 bt debug_kernel_fw#endif /* CONFIG_SH_STANDARD_BIOS && CONFIG_SH_KGDB */debug_enter: #if defined(CONFIG_SH_KGDB) /* Jump to kgdb, pass stacked regs as arg */debug_kernel_sw: mov.l 3f, r0 jmp @r0 mov r15, r4 .align 23: .long kgdb_handle_exception#endif /* CONFIG_SH_KGDB */#if defined(CONFIG_SH_STANDARD_BIOS) /* Unwind the stack and jmp to the debug entry */debug_kernel_fw: mov.l @r15+, r0 mov.l @r15+, r1 mov.l @r15+, r2 mov.l @r15+, r3 mov.l @r15+, r4 mov.l @r15+, r5 mov.l @r15+, r6 mov.l @r15+, r7 stc sr, r8 mov.l 1f, r9 ! BL =1, RB=1, IMASK=0x0F or r9, r8 ldc r8, sr ! here, change the register bank mov.l @r15+, r8 mov.l @r15+, r9 mov.l @r15+, r10 mov.l @r15+, r11 mov.l @r15+, r12 mov.l @r15+, r13 mov.l @r15+, r14 mov.l @r15+, k0 ldc.l @r15+, spc lds.l @r15+, pr mov.l @r15+, k1 ldc.l @r15+, gbr lds.l @r15+, mach lds.l @r15+, macl mov k0, r15 ! mov.l 2f, k0 mov.l @k0, k0 jmp @k0 ldc k1, ssr .align 21: .long 0x300000f02: .long gdb_vbr_vector#endif /* CONFIG_SH_STANDARD_BIOS */#endif /* CONFIG_SH_STANDARD_BIOS || CONFIG_SH_KGDB */ .align 2debug_trap: #if defined(CONFIG_SH_STANDARD_BIOS) || defined(CONFIG_SH_KGDB) mov #OFF_SR, r0 mov.l @(r0,r15), r0 ! get status register shll r0 shll r0 ! kernel space? bt/s debug_kernel#endif mov.l @r15, r0 ! Restore R0 value mov.l 1f, r8 jmp @r8 nop .align 2ENTRY(exception_error) ! STI() mov.l 2f, r0 jmp @r0 nop! .align 21: .long break_point_trap_software2: .long do_exception_error .align 2ret_from_exception: preempt_stop()ret_from_irq: ! mov #OFF_SR, r0 mov.l @(r0,r15), r0 ! get status register shll r0 shll r0 ! kernel space? bt/s resume_kernel ! Yes, it's from kernel, go back soon GET_THREAD_INFO(r8)#ifdef CONFIG_PREEMPT bra resume_userspace nopENTRY(resume_kernel) mov.l @(TI_PRE_COUNT,r8), r0 ! current_thread_info->preempt_count tst r0, r0 bf noreschedneed_resched: mov.l @(TI_FLAGS,r8), r0 ! current_thread_info->flags tst #_TIF_NEED_RESCHED, r0 ! need_resched set? bt noresched mov #OFF_SR, r0 mov.l @(r0,r15), r0 ! get status register and #0xf0, r0 ! interrupts off (exception path)? cmp/eq #0xf0, r0 bt noresched mov.l 1f, r0 mov.l r0, @(TI_PRE_COUNT,r8) STI() mov.l 2f, r0 jsr @r0 nop mov #0, r0 mov.l r0, @(TI_PRE_COUNT,r8) CLI() bra need_resched nopnoresched: bra restore_all nop .align 21: .long PREEMPT_ACTIVE2: .long schedule#endifENTRY(resume_userspace) ! r8: current_thread_info CLI() mov.l @(TI_FLAGS,r8), r0 ! current_thread_info->flags tst #_TIF_WORK_MASK, r0 bt/s restore_all tst #_TIF_NEED_RESCHED, r0 .align 2work_pending: ! r0: current_thread_info->flags ! r8: current_thread_info ! t: result of "tst #_TIF_NEED_RESCHED, r0" bf/s work_resched tst #_TIF_SIGPENDING, r0work_notifysig: bt/s restore_all mov r15, r4 mov #0, r5 mov.l 2f, r1 mova restore_all, r0 jmp @r1 lds r0, prwork_resched:#ifndef CONFIG_PREEMPT ! gUSA handling mov.l @(OFF_SP,r15), r0 ! get user space stack pointer mov r0, r1 shll r0 bf/s 1f shll r0 bf/s 1f mov #OFF_PC, r0 ! SP >= 0xc0000000 : gUSA mark mov.l @(r0,r15), r2 ! get user space PC (program counter) mov.l @(OFF_R0,r15), r3 ! end point cmp/hs r3, r2 ! r2 >= r3? bt 1f add r3, r1 ! rewind point #2 mov.l r1, @(r0,r15) ! reset PC to rewind point #2 !1:#endif mov.l 1f, r1 jsr @r1 ! schedule nop CLI() ! mov.l @(TI_FLAGS,r8), r0 ! current_thread_info->flags tst #_TIF_WORK_MASK, r0 bt restore_all bra work_pending tst #_TIF_NEED_RESCHED, r0 .align 21: .long schedule2: .long do_signal .align 2syscall_exit_work: ! r0: current_thread_info->flags ! r8: current_thread_info tst #_TIF_SYSCALL_TRACE, r0 bt/s work_pending tst #_TIF_NEED_RESCHED, r0 STI() ! XXX setup arguments... mov.l 4f, r0 ! do_syscall_trace jsr @r0 nop bra resume_userspace nop .align 2syscall_trace_entry: ! Yes it is traced. ! XXX setup arguments... mov.l 4f, r11 ! Call do_syscall_trace which notifies jsr @r11 ! superior (will chomp R[0-7]) nop ! Reload R0-R4 from kernel stack, where the ! parent may have modified them using ! ptrace(POKEUSR). (Note that R0-R2 are ! used by the system call handler directly ! from the kernel stack anyway, so don't need ! to be reloaded here.) This allows the parent ! to rewrite system calls and args on the fly. mov.l @(OFF_R4,r15), r4 ! arg0 mov.l @(OFF_R5,r15), r5 mov.l @(OFF_R6,r15), r6 mov.l @(OFF_R7,r15), r7 ! arg3 mov.l @(OFF_R3,r15), r3 ! syscall_nr ! Arrange for do_syscall_trace to be called ! again as the system call returns. mov.l 2f, r10 ! Number of syscalls cmp/hs r10, r3 bf syscall_call mov #-ENOSYS, r0 bra syscall_exit mov.l r0, @(OFF_R0,r15) ! Return value/* * Syscall interface: * * Syscall #: R3 * Arguments #0 to #3: R4--R7 * Arguments #4 to #6: R0, R1, R2 * TRA: (number of arguments + 0x10) x 4 * * This code also handles delegating other traps to the BIOS/gdb stub * according to: * * Trap number * (TRA>>2) Purpose * -------- ------- * 0x0-0xf old syscall ABI * 0x10-0x1f new syscall ABI * 0x20-0xff delegated through debug_trap to BIOS/gdb stub. * * Note: When we're first called, the TRA value must be shifted * right 2 bits in order to get the value that was used as the "trapa" * argument. */ .align 2 .globl ret_from_forkret_from_fork: mov.l 1f, r8 jsr @r8 mov r0, r4 bra syscall_exit nop .align 21: .long schedule_tail !ENTRY(system_call) mov.l 1f, r9 mov.l @r9, r8 ! Read from TRA (Trap Address) Register ! ! Is the trap argument >= 0x20? (TRA will be >= 0x80) mov #0x7f, r9 cmp/hi r9, r8 bt/s 0f mov #OFF_TRA, r9 add r15, r9 ! mov.l r8, @r9 ! set TRA value to tra STI() ! Call the system call handler through the table. ! First check for bad syscall number mov r3, r9 mov.l 2f, r8 ! Number of syscalls cmp/hs r8, r9 bf/s good_system_call GET_THREAD_INFO(r8)syscall_badsys: ! Bad syscall number mov #-ENOSYS, r0 bra resume_userspace mov.l r0, @(OFF_R0,r15) ! Return value !0: bra debug_trap nop !good_system_call: ! Good syscall number mov.l @(TI_FLAGS,r8), r8 mov #_TIF_SYSCALL_TRACE, r10 tst r10, r8 bf syscall_trace_entry !syscall_call: shll2 r9 ! x4 mov.l 3f, r8 ! Load the address of sys_call_table add r8, r9 mov.l @r9, r8 jsr @r8 ! jump to specific syscall handler nop mov.l r0, @(OFF_R0,r15) ! save the return value !
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -