/*1* linux/arch/m32r/kernel/entry.S2*3* Copyright (c) 2001, 2002 Hirokazu Takata, Hitoshi Yamamoto, H. Kondo4* Copyright (c) 2003 Hitoshi Yamamoto5* Copyright (c) 2004 Hirokazu Takata <takata at linux-m32r.org>6*7* Taken from i386 version.8* Copyright (C) 1991, 1992 Linus Torvalds9*/1011/*12* entry.S contains the system-call and fault low-level handling routines.13* This also contains the timer-interrupt handler, as well as all interrupts14* and faults that can result in a task-switch.15*16* NOTE: This code handles signal-recognition, which happens every time17* after a timer-interrupt and after each system call.18*19* Stack layout in 'ret_from_system_call':20* ptrace needs to have all regs on the stack.21* if the order here is changed, it needs to be22* updated in fork.c:copy_thread, signal.c:do_signal,23* ptrace.c and ptrace.h24*25* M32R/M32Rx/M32R226* @(sp) - r427* @(0x04,sp) - r528* @(0x08,sp) - r629* @(0x0c,sp) - *pt_regs30* @(0x10,sp) - r031* @(0x14,sp) - r132* @(0x18,sp) - r233* @(0x1c,sp) - r334* @(0x20,sp) - r735* @(0x24,sp) - r836* @(0x28,sp) - r937* @(0x2c,sp) - r1038* @(0x30,sp) - r1139* @(0x34,sp) - r1240* @(0x38,sp) - syscall_nr41* @(0x3c,sp) - acc0h42* @(0x40,sp) - acc0l43* @(0x44,sp) - acc1h ; ISA_DSP_LEVEL2 only44* @(0x48,sp) - acc1l ; ISA_DSP_LEVEL2 only45* @(0x4c,sp) - psw46* @(0x50,sp) - bpc47* @(0x54,sp) - bbpsw48* @(0x58,sp) - bbpc49* @(0x5c,sp) - spu (cr3)50* @(0x60,sp) - fp (r13)51* @(0x64,sp) - lr (r14)52* @(0x68,sp) - spi (cr2)53* @(0x6c,sp) - orig_r054*/5556#include <linux/linkage.h>57#include <asm/irq.h>58#include <asm/unistd.h>59#include <asm/assembler.h>60#include <asm/thread_info.h>61#include <asm/errno.h>62#include <asm/segment.h>63#include <asm/smp.h>64#include <asm/page.h>65#include <asm/m32r.h>66#include <asm/mmu_context.h>6768#if !defined(CONFIG_MMU)69#define sys_madvise sys_ni_syscall70#define sys_readahead sys_ni_syscall71#define sys_mprotect sys_ni_syscall72#define sys_msync sys_ni_syscall73#define sys_mlock sys_ni_syscall74#define sys_munlock sys_ni_syscall75#define sys_mlockall sys_ni_syscall76#define sys_munlockall sys_ni_syscall77#define sys_mremap sys_ni_syscall78#define sys_mincore sys_ni_syscall79#define sys_remap_file_pages sys_ni_syscall80#endif /* CONFIG_MMU */8182#define R4(reg) @reg83#define R5(reg) @(0x04,reg)84#define R6(reg) @(0x08,reg)85#define PTREGS(reg) @(0x0C,reg)86#define R0(reg) @(0x10,reg)87#define R1(reg) @(0x14,reg)88#define R2(reg) @(0x18,reg)89#define R3(reg) @(0x1C,reg)90#define R7(reg) @(0x20,reg)91#define R8(reg) @(0x24,reg)92#define R9(reg) @(0x28,reg)93#define R10(reg) @(0x2C,reg)94#define R11(reg) @(0x30,reg)95#define R12(reg) @(0x34,reg)96#define SYSCALL_NR(reg) @(0x38,reg)97#define ACC0H(reg) @(0x3C,reg)98#define ACC0L(reg) @(0x40,reg)99#define ACC1H(reg) @(0x44,reg)100#define ACC1L(reg) @(0x48,reg)101#define PSW(reg) @(0x4C,reg)102#define BPC(reg) @(0x50,reg)103#define BBPSW(reg) @(0x54,reg)104#define BBPC(reg) @(0x58,reg)105#define SPU(reg) @(0x5C,reg)106#define FP(reg) @(0x60,reg) /* FP = R13 */107#define LR(reg) @(0x64,reg)108#define SP(reg) @(0x68,reg)109#define ORIG_R0(reg) @(0x6C,reg)110111#define nr_syscalls ((syscall_table_size)/4)112113#ifdef CONFIG_PREEMPT114#define preempt_stop(x) DISABLE_INTERRUPTS(x)115#else116#define preempt_stop(x)117#define resume_kernel restore_all118#endif119120/* how to get the thread information struct from ASM */121#define GET_THREAD_INFO(reg) GET_THREAD_INFO reg122.macro GET_THREAD_INFO reg123ldi \reg, #-THREAD_SIZE124and \reg, sp125.endm126127ENTRY(ret_from_fork)128pop r0129bl schedule_tail130GET_THREAD_INFO(r8)131bra syscall_exit132133/*134* Return to user mode is not as complex as all this looks,135* but we want the default path for a system call return to136* go as quickly as possible which is why some of this is137* less clear than it otherwise should be.138*/139140; userspace resumption stub bypassing syscall exit tracing141ALIGN142ret_from_exception:143preempt_stop(r4)144ret_from_intr:145ld r4, PSW(sp)146#ifdef CONFIG_ISA_M32R2147and3 r4, r4, #0x8800 ; check BSM and BPM bits148#else149and3 r4, r4, #0x8000 ; check BSM bit150#endif151beqz r4, resume_kernel152resume_userspace:153DISABLE_INTERRUPTS(r4) ; make sure we don't miss an interrupt154; setting need_resched or sigpending155; between sampling and the iret156GET_THREAD_INFO(r8)157ld r9, @(TI_FLAGS, r8)158and3 r4, r9, #_TIF_WORK_MASK ; is there any work to be done on159; int/exception return?160bnez r4, work_pending161bra restore_all162163#ifdef CONFIG_PREEMPT164ENTRY(resume_kernel)165GET_THREAD_INFO(r8)166ld r9, @(TI_PRE_COUNT, r8) ; non-zero preempt_count ?167bnez r9, restore_all168need_resched:169ld r9, @(TI_FLAGS, r8) ; need_resched set ?170and3 r4, r9, #_TIF_NEED_RESCHED171beqz r4, restore_all172ld r4, PSW(sp) ; interrupts off (exception path) ?173and3 r4, r4, #0x4000174beqz r4, restore_all175LDIMM (r4, PREEMPT_ACTIVE)176st r4, @(TI_PRE_COUNT, r8)177ENABLE_INTERRUPTS(r4)178bl schedule179ldi r4, #0180st r4, @(TI_PRE_COUNT, r8)181DISABLE_INTERRUPTS(r4)182bra need_resched183#endif184185; system call handler stub186ENTRY(system_call)187SWITCH_TO_KERNEL_STACK188SAVE_ALL189ENABLE_INTERRUPTS(r4) ; Enable interrupt190st sp, PTREGS(sp) ; implicit pt_regs parameter191cmpui r7, #NR_syscalls192bnc syscall_badsys193st r7, SYSCALL_NR(sp) ; syscall_nr194; system call tracing in operation195GET_THREAD_INFO(r8)196ld r9, @(TI_FLAGS, r8)197and3 r4, r9, #_TIF_SYSCALL_TRACE198bnez r4, syscall_trace_entry199syscall_call:200slli r7, #2 ; table jump for the system call201LDIMM (r4, sys_call_table)202add r7, r4203ld r7, @r7204jl r7 ; execute system call205st r0, R0(sp) ; save the return value206syscall_exit:207DISABLE_INTERRUPTS(r4) ; make sure we don't miss an interrupt208; setting need_resched or sigpending209; between sampling and the iret210ld r9, @(TI_FLAGS, r8)211and3 r4, r9, #_TIF_ALLWORK_MASK ; current->work212bnez r4, syscall_exit_work213restore_all:214RESTORE_ALL215216# perform work that needs to be done immediately before resumption217# r9 : flags218ALIGN219work_pending:220and3 r4, r9, #_TIF_NEED_RESCHED221beqz r4, work_notifysig222work_resched:223bl schedule224DISABLE_INTERRUPTS(r4) ; make sure we don't miss an interrupt225; setting need_resched or sigpending226; between sampling and the iret227ld r9, @(TI_FLAGS, r8)228and3 r4, r9, #_TIF_WORK_MASK ; is there any work to be done other229; than syscall tracing?230beqz r4, restore_all231and3 r4, r4, #_TIF_NEED_RESCHED232bnez r4, work_resched233234work_notifysig: ; deal with pending signals and235; notify-resume requests236mv r0, sp ; arg1 : struct pt_regs *regs237mv r1, r9 ; arg2 : __u32 thread_info_flags238bl do_notify_resume239bra resume_userspace240241; perform syscall exit tracing242ALIGN243syscall_trace_entry:244ldi r4, #-ENOSYS245st r4, R0(sp)246bl do_syscall_trace247ld r0, ORIG_R0(sp)248ld r1, R1(sp)249ld r2, R2(sp)250ld r3, R3(sp)251ld r4, R4(sp)252ld r5, R5(sp)253ld r6, R6(sp)254ld r7, SYSCALL_NR(sp)255cmpui r7, #NR_syscalls256bc syscall_call257bra syscall_exit258259; perform syscall exit tracing260ALIGN261syscall_exit_work:262ld r9, @(TI_FLAGS, r8)263and3 r4, r9, #_TIF_SYSCALL_TRACE264beqz r4, work_pending265ENABLE_INTERRUPTS(r4) ; could let do_syscall_trace() call266; schedule() instead267bl do_syscall_trace268bra resume_userspace269270ALIGN271syscall_fault:272SAVE_ALL273GET_THREAD_INFO(r8)274ldi r4, #-EFAULT275st r4, R0(sp)276bra resume_userspace277278ALIGN279syscall_badsys:280ldi r4, #-ENOSYS281st r4, R0(sp)282bra resume_userspace283284.global eit_vector285286.equ ei_vec_table, eit_vector + 0x0200287288/*289* EI handler routine290*/291ENTRY(ei_handler)292#if defined(CONFIG_CHIP_M32700)293; WORKAROUND: force to clear SM bit and use the kernel stack (SPI).294SWITCH_TO_KERNEL_STACK295#endif296SAVE_ALL297mv r1, sp ; arg1(regs)298; get ICU status299seth r0, #shigh(M32R_ICU_ISTS_ADDR)300ld r0, @(low(M32R_ICU_ISTS_ADDR),r0)301push r0302#if defined(CONFIG_SMP)303/*304* If IRQ == 0 --> Nothing to do, Not write IMASK305* If IRQ == IPI --> Do IPI handler, Not write IMASK306* If IRQ != 0, IPI --> Do do_IRQ(), Write IMASK307*/308slli r0, #4309srli r0, #24 ; r0(irq_num<<2)310;; IRQ exist check311#if defined(CONFIG_CHIP_M32700)312/* WORKAROUND: IMASK bug M32700-TS1, TS2 chip. */313bnez r0, 0f314ld24 r14, #0x00070000315seth r0, #shigh(M32R_ICU_IMASK_ADDR)316st r14, @(low(M32R_ICU_IMASK_ADDR),r0)317bra 1f318.fillinsn3190:320#endif /* CONFIG_CHIP_M32700 */321beqz r0, 1f ; if (!irq_num) goto exit322;; IPI check323cmpi r0, #(M32R_IRQ_IPI0<<2) ; ISN < IPI0 check324bc 2f325cmpi r0, #((M32R_IRQ_IPI7+1)<<2) ; ISN > IPI7 check326bnc 2f327LDIMM (r2, ei_vec_table)328add r2, r0329ld r2, @r2330beqz r2, 1f ; if (no IPI handler) goto exit331mv r0, r1 ; arg0(regs)332jl r2333.fillinsn3341:335addi sp, #4336bra restore_all337.fillinsn3382:339srli r0, #2340#else /* not CONFIG_SMP */341srli r0, #22 ; r0(irq)342#endif /* not CONFIG_SMP */343344#if defined(CONFIG_PLAT_HAS_INT1ICU)345add3 r2, r0, #-(M32R_IRQ_INT1) ; INT1# interrupt346bnez r2, 3f347seth r0, #shigh(M32R_INT1ICU_ISTS)348lduh r0, @(low(M32R_INT1ICU_ISTS),r0) ; bit10-6 : ISN349slli r0, #21350srli r0, #27 ; ISN351addi r0, #(M32R_INT1ICU_IRQ_BASE)352bra check_end353.fillinsn3543:355#endif /* CONFIG_PLAT_HAS_INT1ICU */356#if defined(CONFIG_PLAT_HAS_INT0ICU)357add3 r2, r0, #-(M32R_IRQ_INT0) ; INT0# interrupt358bnez r2, 4f359seth r0, #shigh(M32R_INT0ICU_ISTS)360lduh r0, @(low(M32R_INT0ICU_ISTS),r0) ; bit10-6 : ISN361slli r0, #21362srli r0, #27 ; ISN363add3 r0, r0, #(M32R_INT0ICU_IRQ_BASE)364bra check_end365.fillinsn3664:367#endif /* CONFIG_PLAT_HAS_INT0ICU */368#if defined(CONFIG_PLAT_HAS_INT2ICU)369add3 r2, r0, #-(M32R_IRQ_INT2) ; INT2# interrupt370bnez r2, 5f371seth r0, #shigh(M32R_INT2ICU_ISTS)372lduh r0, @(low(M32R_INT2ICU_ISTS),r0) ; bit10-6 : ISN373slli r0, #21374srli r0, #27 ; ISN375add3 r0, r0, #(M32R_INT2ICU_IRQ_BASE)376; bra check_end377.fillinsn3785:379#endif /* CONFIG_PLAT_HAS_INT2ICU */380381check_end:382bl do_IRQ383pop r14384seth r0, #shigh(M32R_ICU_IMASK_ADDR)385st r14, @(low(M32R_ICU_IMASK_ADDR),r0)386bra ret_from_intr387388/*389* Default EIT handler390*/391ALIGN392int_msg:393.asciz "Unknown interrupt\n"394.byte 0395396ENTRY(default_eit_handler)397push r0398mvfc r0, psw399push r1400push r2401push r3402push r0403LDIMM (r0, __KERNEL_DS)404mv r0, r1405mv r0, r2406LDIMM (r0, int_msg)407bl printk408pop r0409pop r3410pop r2411pop r1412mvtc r0, psw413pop r0414infinit:415bra infinit416417#ifdef CONFIG_MMU418/*419* Access Exception handler420*/421ENTRY(ace_handler)422SWITCH_TO_KERNEL_STACK423SAVE_ALL424425seth r2, #shigh(MMU_REG_BASE) /* Check status register */426ld r4, @(low(MESTS_offset),r2)427st r4, @(low(MESTS_offset),r2)428srl3 r1, r4, #4429#ifdef CONFIG_CHIP_M32700430and3 r1, r1, #0x0000ffff431; WORKAROUND: ignore TME bit for the M32700(TS1).432#endif /* CONFIG_CHIP_M32700 */433beqz r1, inst434oprand:435ld r2, @(low(MDEVA_offset),r2) ; set address436srli r1, #1437bra 1f438inst:439and3 r1, r4, #2440srli r1, #1441or3 r1, r1, #8442mvfc r2, bpc ; set address443.fillinsn4441:445mvfc r3, psw446mv r0, sp447and3 r3, r3, 0x800448srli r3, #9449or r1, r3450/*451* do_page_fault():452* r0 : struct pt_regs *regs453* r1 : unsigned long error-code454* r2 : unsigned long address455* error-code:456* +------+------+------+------+457* | bit3 | bit2 | bit1 | bit0 |458* +------+------+------+------+459* bit 3 == 0:means data, 1:means instruction460* bit 2 == 0:means kernel, 1:means user-mode461* bit 1 == 0:means read, 1:means write462* bit 0 == 0:means no page found 1:means protection fault463*464*/465bl do_page_fault466bra ret_from_intr467#endif /* CONFIG_MMU */468469470ENTRY(alignment_check)471/* void alignment_check(int error_code) */472SWITCH_TO_KERNEL_STACK473SAVE_ALL474ldi r1, #0x30 ; error_code475mv r0, sp ; pt_regs476bl do_alignment_check477error_code:478bra ret_from_exception479480ENTRY(rie_handler)481/* void rie_handler(int error_code) */482SWITCH_TO_KERNEL_STACK483SAVE_ALL484ldi r1, #0x20 ; error_code485mv r0, sp ; pt_regs486bl do_rie_handler487bra error_code488489ENTRY(pie_handler)490/* void pie_handler(int error_code) */491SWITCH_TO_KERNEL_STACK492SAVE_ALL493ldi r1, #0 ; error_code ; FIXME494mv r0, sp ; pt_regs495bl do_pie_handler496bra error_code497498ENTRY(debug_trap)499/* void debug_trap(void) */500.global withdraw_debug_trap501SWITCH_TO_KERNEL_STACK502SAVE_ALL503mv r0, sp ; pt_regs504bl withdraw_debug_trap505ldi r1, #0 ; error_code506mv r0, sp ; pt_regs507bl do_debug_trap508bra error_code509510ENTRY(ill_trap)511/* void ill_trap(void) */512SWITCH_TO_KERNEL_STACK513SAVE_ALL514ldi r1, #0 ; error_code ; FIXME515mv r0, sp ; pt_regs516bl do_ill_trap517bra error_code518519ENTRY(cache_flushing_handler)520/* void _flush_cache_all(void); */521.global _flush_cache_all522SWITCH_TO_KERNEL_STACK523push r0524push r1525push r2526push r3527push r4528push r5529push r6530push r7531push lr532bl _flush_cache_all533pop lr534pop r7535pop r6536pop r5537pop r4538pop r3539pop r2540pop r1541pop r0542rte543544.section .rodata,"a"545#include "syscall_table.S"546547syscall_table_size=(.-sys_call_table)548549550