/* * linux/arch/m32r/kernel/entry.S * * Copyright (c) 2001, 2002 Hirokazu Takata, Hitoshi Yamamoto, H. Kondo * Copyright (c) 2003 Hitoshi Yamamoto * Copyright (c) 2004 Hirokazu Takata <takata at linux-m32r.org> * * Taken from i386 version. * Copyright (C) 1991, 1992 Linus Torvalds */ /* * 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. * * Stack layout in 'ret_from_system_call': * ptrace needs to have all regs on the stack. * if the order here is changed, it needs to be * updated in fork.c:copy_thread, signal.c:do_signal, * ptrace.c and ptrace.h * * M32R/M32Rx/M32R2 * @(sp) - r4 * @(0x04,sp) - r5 * @(0x08,sp) - r6 * @(0x0c,sp) - *pt_regs * @(0x10,sp) - r0 * @(0x14,sp) - r1 * @(0x18,sp) - r2 * @(0x1c,sp) - r3 * @(0x20,sp) - r7 * @(0x24,sp) - r8 * @(0x28,sp) - r9 * @(0x2c,sp) - r10 * @(0x30,sp) - r11 * @(0x34,sp) - r12 * @(0x38,sp) - syscall_nr * @(0x3c,sp) - acc0h * @(0x40,sp) - acc0l * @(0x44,sp) - acc1h ; ISA_DSP_LEVEL2 only * @(0x48,sp) - acc1l ; ISA_DSP_LEVEL2 only * @(0x4c,sp) - psw * @(0x50,sp) - bpc * @(0x54,sp) - bbpsw * @(0x58,sp) - bbpc * @(0x5c,sp) - spu (cr3) * @(0x60,sp) - fp (r13) * @(0x64,sp) - lr (r14) * @(0x68,sp) - spi (cr2) * @(0x6c,sp) - orig_r0 */ #include <linux/linkage.h> #include <asm/irq.h> #include <asm/unistd.h> #include <asm/assembler.h> #include <asm/thread_info.h> #include <asm/errno.h> #include <asm/segment.h> #include <asm/smp.h> #include <asm/page.h> #include <asm/m32r.h> #include <asm/mmu_context.h> #include <asm/asm-offsets.h> #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 /* CONFIG_MMU */ #define R4(reg) @reg #define R5(reg) @(0x04,reg) #define R6(reg) @(0x08,reg) #define PTREGS(reg) @(0x0C,reg) #define R0(reg) @(0x10,reg) #define R1(reg) @(0x14,reg) #define R2(reg) @(0x18,reg) #define R3(reg) @(0x1C,reg) #define R7(reg) @(0x20,reg) #define R8(reg) @(0x24,reg) #define R9(reg) @(0x28,reg) #define R10(reg) @(0x2C,reg) #define R11(reg) @(0x30,reg) #define R12(reg) @(0x34,reg) #define SYSCALL_NR(reg) @(0x38,reg) #define ACC0H(reg) @(0x3C,reg) #define ACC0L(reg) @(0x40,reg) #define ACC1H(reg) @(0x44,reg) #define ACC1L(reg) @(0x48,reg) #define PSW(reg) @(0x4C,reg) #define BPC(reg) @(0x50,reg) #define BBPSW(reg) @(0x54,reg) #define BBPC(reg) @(0x58,reg) #define SPU(reg) @(0x5C,reg) #define FP(reg) @(0x60,reg) /* FP = R13 */ #define LR(reg) @(0x64,reg) #define SP(reg) @(0x68,reg) #define ORIG_R0(reg) @(0x6C,reg) #define nr_syscalls ((syscall_table_size)/4) #ifdef CONFIG_PREEMPT #define preempt_stop(x) DISABLE_INTERRUPTS(x) #else #define preempt_stop(x) #define resume_kernel restore_all #endif /* how to get the thread information struct from ASM */ #define GET_THREAD_INFO(reg) GET_THREAD_INFO reg .macro GET_THREAD_INFO reg ldi \reg, #-THREAD_SIZE and \reg, sp .endm ENTRY(ret_from_kernel_thread) pop r0 bl schedule_tail GET_THREAD_INFO(r8) ld r0, R0(r8) ld r1, R1(r8) jl r1 bra syscall_exit ENTRY(ret_from_fork) pop r0 bl schedule_tail GET_THREAD_INFO(r8) bra syscall_exit /* * Return to user mode is not as complex as all this looks, * but we want the default path for a system call return to * go as quickly as possible which is why some of this is * less clear than it otherwise should be. */ ; userspace resumption stub bypassing syscall exit tracing ALIGN ret_from_exception: preempt_stop(r4) ret_from_intr: ld r4, PSW(sp) #ifdef CONFIG_ISA_M32R2 and3 r4, r4, #0x8800 ; check BSM and BPM bits #else and3 r4, r4, #0x8000 ; check BSM bit #endif beqz r4, resume_kernel resume_userspace: DISABLE_INTERRUPTS(r4) ; make sure we don't miss an interrupt ; setting need_resched or sigpending ; between sampling and the iret GET_THREAD_INFO(r8) ld r9, @(TI_FLAGS, r8) and3 r4, r9, #_TIF_WORK_MASK ; is there any work to be done on ; int/exception return? bnez r4, work_pending bra restore_all #ifdef CONFIG_PREEMPT ENTRY(resume_kernel) GET_THREAD_INFO(r8) ld r9, @(TI_PRE_COUNT, r8) ; non-zero preempt_count ? bnez r9, restore_all need_resched: ld r9, @(TI_FLAGS, r8) ; need_resched set ? and3 r4, r9, #_TIF_NEED_RESCHED beqz r4, restore_all ld r4, PSW(sp) ; interrupts off (exception path) ? and3 r4, r4, #0x4000 beqz r4, restore_all bl preempt_schedule_irq bra need_resched #endif ; system call handler stub ENTRY(system_call) SWITCH_TO_KERNEL_STACK SAVE_ALL ENABLE_INTERRUPTS(r4) ; Enable interrupt st sp, PTREGS(sp) ; implicit pt_regs parameter cmpui r7, #NR_syscalls bnc syscall_badsys st r7, SYSCALL_NR(sp) ; syscall_nr ; system call tracing in operation GET_THREAD_INFO(r8) ld r9, @(TI_FLAGS, r8) and3 r4, r9, #_TIF_SYSCALL_TRACE bnez r4, syscall_trace_entry syscall_call: slli r7, #2 ; table jump for the system call LDIMM (r4, sys_call_table) add r7, r4 ld r7, @r7 jl r7 ; execute system call st r0, R0(sp) ; save the return value syscall_exit: DISABLE_INTERRUPTS(r4) ; make sure we don't miss an interrupt ; setting need_resched or sigpending ; between sampling and the iret ld r9, @(TI_FLAGS, r8) and3 r4, r9, #_TIF_ALLWORK_MASK ; current->work bnez r4, syscall_exit_work restore_all: RESTORE_ALL # perform work that needs to be done immediately before resumption # r9 : flags ALIGN work_pending: and3 r4, r9, #_TIF_NEED_RESCHED beqz r4, work_notifysig work_resched: bl schedule DISABLE_INTERRUPTS(r4) ; make sure we don't miss an interrupt ; setting need_resched or sigpending ; between sampling and the iret ld r9, @(TI_FLAGS, r8) and3 r4, r9, #_TIF_WORK_MASK ; is there any work to be done other ; than syscall tracing? beqz r4, restore_all and3 r4, r4, #_TIF_NEED_RESCHED bnez r4, work_resched work_notifysig: ; deal with pending signals and ; notify-resume requests mv r0, sp ; arg1 : struct pt_regs *regs mv r1, r9 ; arg2 : __u32 thread_info_flags bl do_notify_resume bra resume_userspace ; perform syscall exit tracing ALIGN syscall_trace_entry: ldi r4, #-ENOSYS st r4, R0(sp) bl do_syscall_trace ld r0, ORIG_R0(sp) ld r1, R1(sp) ld r2, R2(sp) ld r3, R3(sp) ld r4, R4(sp) ld r5, R5(sp) ld r6, R6(sp) ld r7, SYSCALL_NR(sp) cmpui r7, #NR_syscalls bc syscall_call bra syscall_exit ; perform syscall exit tracing ALIGN syscall_exit_work: ld r9, @(TI_FLAGS, r8) and3 r4, r9, #_TIF_SYSCALL_TRACE beqz r4, work_pending ENABLE_INTERRUPTS(r4) ; could let do_syscall_trace() call ; schedule() instead bl do_syscall_trace bra resume_userspace ALIGN syscall_fault: SAVE_ALL GET_THREAD_INFO(r8) ldi r4, #-EFAULT st r4, R0(sp) bra resume_userspace ALIGN syscall_badsys: ldi r4, #-ENOSYS st r4, R0(sp) bra resume_userspace .global eit_vector .equ ei_vec_table, eit_vector + 0x0200 /* * EI handler routine */ ENTRY(ei_handler) #if defined(CONFIG_CHIP_M32700) ; WORKAROUND: force to clear SM bit and use the kernel stack (SPI). SWITCH_TO_KERNEL_STACK #endif SAVE_ALL mv r1, sp ; arg1(regs) ; get ICU status seth r0, #shigh(M32R_ICU_ISTS_ADDR) ld r0, @(low(M32R_ICU_ISTS_ADDR),r0) push r0 #if defined(CONFIG_SMP) /* * If IRQ == 0 --> Nothing to do, Not write IMASK * If IRQ == IPI --> Do IPI handler, Not write IMASK * If IRQ != 0, IPI --> Do do_IRQ(), Write IMASK */ slli r0, #4 srli r0, #24 ; r0(irq_num<<2) ;; IRQ exist check #if defined(CONFIG_CHIP_M32700) /* WORKAROUND: IMASK bug M32700-TS1, TS2 chip. */ bnez r0, 0f ld24 r14, #0x00070000 seth r0, #shigh(M32R_ICU_IMASK_ADDR) st r14, @(low(M32R_ICU_IMASK_ADDR),r0) bra 1f .fillinsn 0: #endif /* CONFIG_CHIP_M32700 */ beqz r0, 1f ; if (!irq_num) goto exit ;; IPI check cmpi r0, #(M32R_IRQ_IPI0<<2) ; ISN < IPI0 check bc 2f cmpi r0, #((M32R_IRQ_IPI7+1)<<2) ; ISN > IPI7 check bnc 2f LDIMM (r2, ei_vec_table) add r2, r0 ld r2, @r2 beqz r2, 1f ; if (no IPI handler) goto exit mv r0, r1 ; arg0(regs) jl r2 .fillinsn 1: addi sp, #4 bra restore_all .fillinsn 2: srli r0, #2 #else /* not CONFIG_SMP */ srli r0, #22 ; r0(irq) #endif /* not CONFIG_SMP */ #if defined(CONFIG_PLAT_HAS_INT1ICU) add3 r2, r0, #-(M32R_IRQ_INT1) ; INT1# interrupt bnez r2, 3f seth r0, #shigh(M32R_INT1ICU_ISTS) lduh r0, @(low(M32R_INT1ICU_ISTS),r0) ; bit10-6 : ISN slli r0, #21 srli r0, #27 ; ISN addi r0, #(M32R_INT1ICU_IRQ_BASE) bra check_end .fillinsn 3: #endif /* CONFIG_PLAT_HAS_INT1ICU */ #if defined(CONFIG_PLAT_HAS_INT0ICU) add3 r2, r0, #-(M32R_IRQ_INT0) ; INT0# interrupt bnez r2, 4f seth r0, #shigh(M32R_INT0ICU_ISTS) lduh r0, @(low(M32R_INT0ICU_ISTS),r0) ; bit10-6 : ISN slli r0, #21 srli r0, #27 ; ISN add3 r0, r0, #(M32R_INT0ICU_IRQ_BASE) bra check_end .fillinsn 4: #endif /* CONFIG_PLAT_HAS_INT0ICU */ #if defined(CONFIG_PLAT_HAS_INT2ICU) add3 r2, r0, #-(M32R_IRQ_INT2) ; INT2# interrupt bnez r2, 5f seth r0, #shigh(M32R_INT2ICU_ISTS) lduh r0, @(low(M32R_INT2ICU_ISTS),r0) ; bit10-6 : ISN slli r0, #21 srli r0, #27 ; ISN add3 r0, r0, #(M32R_INT2ICU_IRQ_BASE) ; bra check_end .fillinsn 5: #endif /* CONFIG_PLAT_HAS_INT2ICU */ check_end: bl do_IRQ pop r14 seth r0, #shigh(M32R_ICU_IMASK_ADDR) st r14, @(low(M32R_ICU_IMASK_ADDR),r0) bra ret_from_intr /* * Default EIT handler */ ALIGN int_msg: .asciz "Unknown interrupt\n" .byte 0 ENTRY(default_eit_handler) push r0 mvfc r0, psw push r1 push r2 push r3 push r0 LDIMM (r0, __KERNEL_DS) mv r0, r1 mv r0, r2 LDIMM (r0, int_msg) bl printk pop r0 pop r3 pop r2 pop r1 mvtc r0, psw pop r0 infinit: bra infinit #ifdef CONFIG_MMU /* * Access Exception handler */ ENTRY(ace_handler) SWITCH_TO_KERNEL_STACK SAVE_ALL seth r2, #shigh(MMU_REG_BASE) /* Check status register */ ld r4, @(low(MESTS_offset),r2) st r4, @(low(MESTS_offset),r2) srl3 r1, r4, #4 #ifdef CONFIG_CHIP_M32700 and3 r1, r1, #0x0000ffff ; WORKAROUND: ignore TME bit for the M32700(TS1). #endif /* CONFIG_CHIP_M32700 */ beqz r1, inst oprand: ld r2, @(low(MDEVA_offset),r2) ; set address srli r1, #1 bra 1f inst: and3 r1, r4, #2 srli r1, #1 or3 r1, r1, #8 mvfc r2, bpc ; set address .fillinsn 1: mvfc r3, psw mv r0, sp and3 r3, r3, 0x800 srli r3, #9 or r1, r3 /* * do_page_fault(): * r0 : struct pt_regs *regs * r1 : unsigned long error-code * r2 : unsigned long address * error-code: * +------+------+------+------+ * | bit3 | bit2 | bit1 | bit0 | * +------+------+------+------+ * bit 3 == 0:means data, 1:means instruction * bit 2 == 0:means kernel, 1:means user-mode * bit 1 == 0:means read, 1:means write * bit 0 == 0:means no page found 1:means protection fault * */ bl do_page_fault bra ret_from_intr #endif /* CONFIG_MMU */ ENTRY(alignment_check) /* void alignment_check(int error_code) */ SWITCH_TO_KERNEL_STACK SAVE_ALL ldi r1, #0x30 ; error_code mv r0, sp ; pt_regs bl do_alignment_check error_code: bra ret_from_exception ENTRY(rie_handler) /* void rie_handler(int error_code) */ SWITCH_TO_KERNEL_STACK SAVE_ALL ldi r1, #0x20 ; error_code mv r0, sp ; pt_regs bl do_rie_handler bra error_code ENTRY(pie_handler) /* void pie_handler(int error_code) */ SWITCH_TO_KERNEL_STACK SAVE_ALL ldi r1, #0 ; error_code ; FIXME mv r0, sp ; pt_regs bl do_pie_handler bra error_code ENTRY(debug_trap) /* void debug_trap(void) */ .global withdraw_debug_trap SWITCH_TO_KERNEL_STACK SAVE_ALL mv r0, sp ; pt_regs bl withdraw_debug_trap ldi r1, #0 ; error_code mv r0, sp ; pt_regs bl do_debug_trap bra error_code ENTRY(ill_trap) /* void ill_trap(void) */ SWITCH_TO_KERNEL_STACK SAVE_ALL ldi r1, #0 ; error_code ; FIXME mv r0, sp ; pt_regs bl do_ill_trap bra error_code ENTRY(cache_flushing_handler) /* void _flush_cache_all(void); */ .global _flush_cache_all SWITCH_TO_KERNEL_STACK push r0 push r1 push r2 push r3 push r4 push r5 push r6 push r7 push lr bl _flush_cache_all pop lr pop r7 pop r6 pop r5 pop r4 pop r3 pop r2 pop r1 pop r0 rte .section .rodata,"a" #include "syscall_table.S" syscall_table_size=(.-sys_call_table)