123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750 |
- /*
- * Copyright (C) 2004-2006 Atmel Corporation
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
- /*
- * This file contains the low-level entry-points into the kernel, that is,
- * exception handlers, debug trap handlers, interrupt handlers and the
- * system call handler.
- */
- #include <linux/errno.h>
- #include <asm/asm.h>
- #include <asm/hardirq.h>
- #include <asm/irq.h>
- #include <asm/ocd.h>
- #include <asm/page.h>
- #include <asm/pgtable.h>
- #include <asm/ptrace.h>
- #include <asm/sysreg.h>
- #include <asm/thread_info.h>
- #include <asm/unistd.h>
- #ifdef CONFIG_PREEMPT
- # define preempt_stop mask_interrupts
- #else
- # define preempt_stop
- # define fault_resume_kernel fault_restore_all
- #endif
- #define __MASK(x) ((1 << (x)) - 1)
- #define IRQ_MASK ((__MASK(SOFTIRQ_BITS) << SOFTIRQ_SHIFT) | \
- (__MASK(HARDIRQ_BITS) << HARDIRQ_SHIFT))
- .section .ex.text,"ax",@progbits
- .align 2
- exception_vectors:
- bral handle_critical
- .align 2
- bral handle_critical
- .align 2
- bral do_bus_error_write
- .align 2
- bral do_bus_error_read
- .align 2
- bral do_nmi_ll
- .align 2
- bral handle_address_fault
- .align 2
- bral handle_protection_fault
- .align 2
- bral handle_debug
- .align 2
- bral do_illegal_opcode_ll
- .align 2
- bral do_illegal_opcode_ll
- .align 2
- bral do_illegal_opcode_ll
- .align 2
- bral do_fpe_ll
- .align 2
- bral do_illegal_opcode_ll
- .align 2
- bral handle_address_fault
- .align 2
- bral handle_address_fault
- .align 2
- bral handle_protection_fault
- .align 2
- bral handle_protection_fault
- .align 2
- bral do_dtlb_modified
- /*
- * r0 : PGD/PT/PTE
- * r1 : Offending address
- * r2 : Scratch register
- * r3 : Cause (5, 12 or 13)
- */
- #define tlbmiss_save pushm r0-r3
- #define tlbmiss_restore popm r0-r3
- .section .tlbx.ex.text,"ax",@progbits
- .global itlb_miss
- itlb_miss:
- tlbmiss_save
- rjmp tlb_miss_common
- .section .tlbr.ex.text,"ax",@progbits
- dtlb_miss_read:
- tlbmiss_save
- rjmp tlb_miss_common
- .section .tlbw.ex.text,"ax",@progbits
- dtlb_miss_write:
- tlbmiss_save
- .global tlb_miss_common
- tlb_miss_common:
- mfsr r0, SYSREG_TLBEAR
- mfsr r1, SYSREG_PTBR
- /* Is it the vmalloc space? */
- bld r0, 31
- brcs handle_vmalloc_miss
- /* First level lookup */
- pgtbl_lookup:
- lsr r2, r0, PGDIR_SHIFT
- ld.w r3, r1[r2 << 2]
- bfextu r1, r0, PAGE_SHIFT, PGDIR_SHIFT - PAGE_SHIFT
- bld r3, _PAGE_BIT_PRESENT
- brcc page_table_not_present
- /* Translate to virtual address in P1. */
- andl r3, 0xf000
- sbr r3, 31
- /* Second level lookup */
- ld.w r2, r3[r1 << 2]
- mfsr r0, SYSREG_TLBARLO
- bld r2, _PAGE_BIT_PRESENT
- brcc page_not_present
- /* Mark the page as accessed */
- sbr r2, _PAGE_BIT_ACCESSED
- st.w r3[r1 << 2], r2
- /* Drop software flags */
- andl r2, _PAGE_FLAGS_HARDWARE_MASK & 0xffff
- mtsr SYSREG_TLBELO, r2
- /* Figure out which entry we want to replace */
- mfsr r1, SYSREG_MMUCR
- clz r2, r0
- brcc 1f
- mov r3, -1 /* All entries have been accessed, */
- mov r2, 0 /* so start at 0 */
- mtsr SYSREG_TLBARLO, r3 /* and reset TLBAR */
- 1: bfins r1, r2, SYSREG_DRP_OFFSET, SYSREG_DRP_SIZE
- mtsr SYSREG_MMUCR, r1
- tlbw
- tlbmiss_restore
- rete
- handle_vmalloc_miss:
- /* Simply do the lookup in init's page table */
- mov r1, lo(swapper_pg_dir)
- orh r1, hi(swapper_pg_dir)
- rjmp pgtbl_lookup
- /* --- System Call --- */
- .section .scall.text,"ax",@progbits
- system_call:
- #ifdef CONFIG_PREEMPT
- mask_interrupts
- #endif
- pushm r12 /* r12_orig */
- stmts --sp, r0-lr
- mfsr r0, SYSREG_RAR_SUP
- mfsr r1, SYSREG_RSR_SUP
- #ifdef CONFIG_PREEMPT
- unmask_interrupts
- #endif
- zero_fp
- stm --sp, r0-r1
- /* check for syscall tracing */
- get_thread_info r0
- ld.w r1, r0[TI_flags]
- bld r1, TIF_SYSCALL_TRACE
- brcs syscall_trace_enter
- syscall_trace_cont:
- cp.w r8, NR_syscalls
- brhs syscall_badsys
- lddpc lr, syscall_table_addr
- ld.w lr, lr[r8 << 2]
- mov r8, r5 /* 5th argument (6th is pushed by stub) */
- icall lr
- .global syscall_return
- syscall_return:
- get_thread_info r0
- mask_interrupts /* make sure we don't miss an interrupt
- setting need_resched or sigpending
- between sampling and the rets */
- /* Store the return value so that the correct value is loaded below */
- stdsp sp[REG_R12], r12
- ld.w r1, r0[TI_flags]
- andl r1, _TIF_ALLWORK_MASK, COH
- brne syscall_exit_work
- syscall_exit_cont:
- popm r8-r9
- mtsr SYSREG_RAR_SUP, r8
- mtsr SYSREG_RSR_SUP, r9
- ldmts sp++, r0-lr
- sub sp, -4 /* r12_orig */
- rets
- .align 2
- syscall_table_addr:
- .long sys_call_table
- syscall_badsys:
- mov r12, -ENOSYS
- rjmp syscall_return
- .global ret_from_fork
- ret_from_fork:
- rcall schedule_tail
- /* check for syscall tracing */
- get_thread_info r0
- ld.w r1, r0[TI_flags]
- andl r1, _TIF_ALLWORK_MASK, COH
- brne syscall_exit_work
- rjmp syscall_exit_cont
- syscall_trace_enter:
- pushm r8-r12
- rcall syscall_trace
- popm r8-r12
- rjmp syscall_trace_cont
- syscall_exit_work:
- bld r1, TIF_SYSCALL_TRACE
- brcc 1f
- unmask_interrupts
- rcall syscall_trace
- mask_interrupts
- ld.w r1, r0[TI_flags]
- 1: bld r1, TIF_NEED_RESCHED
- brcc 2f
- unmask_interrupts
- rcall schedule
- mask_interrupts
- ld.w r1, r0[TI_flags]
- rjmp 1b
- 2: mov r2, _TIF_SIGPENDING | _TIF_RESTORE_SIGMASK
- tst r1, r2
- breq 3f
- unmask_interrupts
- mov r12, sp
- mov r11, r0
- rcall do_notify_resume
- mask_interrupts
- ld.w r1, r0[TI_flags]
- rjmp 1b
- 3: bld r1, TIF_BREAKPOINT
- brcc syscall_exit_cont
- mfsr r3, SYSREG_TLBEHI
- lddsp r2, sp[REG_PC]
- andl r3, 0xff, COH
- lsl r3, 1
- sbr r3, 30
- sbr r3, 0
- mtdr DBGREG_BWA2A, r2
- mtdr DBGREG_BWC2A, r3
- rjmp syscall_exit_cont
- /* The slow path of the TLB miss handler */
- page_table_not_present:
- page_not_present:
- tlbmiss_restore
- sub sp, 4
- stmts --sp, r0-lr
- rcall save_full_context_ex
- mfsr r12, SYSREG_ECR
- mov r11, sp
- rcall do_page_fault
- rjmp ret_from_exception
- /* This function expects to find offending PC in SYSREG_RAR_EX */
- save_full_context_ex:
- mfsr r8, SYSREG_RSR_EX
- mov r12, r8
- andh r8, (MODE_MASK >> 16), COH
- mfsr r11, SYSREG_RAR_EX
- brne 2f
- 1: pushm r11, r12 /* PC and SR */
- unmask_exceptions
- ret r12
- 2: sub r10, sp, -(FRAME_SIZE_FULL - REG_LR)
- stdsp sp[4], r10 /* replace saved SP */
- rjmp 1b
- /* Low-level exception handlers */
- handle_critical:
- pushm r12
- pushm r0-r12
- rcall save_full_context_ex
- mfsr r12, SYSREG_ECR
- mov r11, sp
- rcall do_critical_exception
- /* We should never get here... */
- bad_return:
- sub r12, pc, (. - 1f)
- bral panic
- .align 2
- 1: .asciz "Return from critical exception!"
- .align 1
- do_bus_error_write:
- sub sp, 4
- stmts --sp, r0-lr
- rcall save_full_context_ex
- mov r11, 1
- rjmp 1f
- do_bus_error_read:
- sub sp, 4
- stmts --sp, r0-lr
- rcall save_full_context_ex
- mov r11, 0
- 1: mfsr r12, SYSREG_BEAR
- mov r10, sp
- rcall do_bus_error
- rjmp ret_from_exception
- .align 1
- do_nmi_ll:
- sub sp, 4
- stmts --sp, r0-lr
- mfsr r9, SYSREG_RSR_NMI
- mfsr r8, SYSREG_RAR_NMI
- bfextu r0, r9, MODE_SHIFT, 3
- brne 2f
- 1: pushm r8, r9 /* PC and SR */
- mfsr r12, SYSREG_ECR
- mov r11, sp
- rcall do_nmi
- popm r8-r9
- mtsr SYSREG_RAR_NMI, r8
- tst r0, r0
- mtsr SYSREG_RSR_NMI, r9
- brne 3f
- ldmts sp++, r0-lr
- sub sp, -4 /* skip r12_orig */
- rete
- 2: sub r10, sp, -(FRAME_SIZE_FULL - REG_LR)
- stdsp sp[4], r10 /* replace saved SP */
- rjmp 1b
- 3: popm lr
- sub sp, -4 /* skip sp */
- popm r0-r12
- sub sp, -4 /* skip r12_orig */
- rete
- handle_address_fault:
- sub sp, 4
- stmts --sp, r0-lr
- rcall save_full_context_ex
- mfsr r12, SYSREG_ECR
- mov r11, sp
- rcall do_address_exception
- rjmp ret_from_exception
- handle_protection_fault:
- sub sp, 4
- stmts --sp, r0-lr
- rcall save_full_context_ex
- mfsr r12, SYSREG_ECR
- mov r11, sp
- rcall do_page_fault
- rjmp ret_from_exception
- .align 1
- do_illegal_opcode_ll:
- sub sp, 4
- stmts --sp, r0-lr
- rcall save_full_context_ex
- mfsr r12, SYSREG_ECR
- mov r11, sp
- rcall do_illegal_opcode
- rjmp ret_from_exception
- do_dtlb_modified:
- pushm r0-r3
- mfsr r1, SYSREG_TLBEAR
- mfsr r0, SYSREG_PTBR
- lsr r2, r1, PGDIR_SHIFT
- ld.w r0, r0[r2 << 2]
- lsl r1, (32 - PGDIR_SHIFT)
- lsr r1, (32 - PGDIR_SHIFT) + PAGE_SHIFT
- /* Translate to virtual address in P1 */
- andl r0, 0xf000
- sbr r0, 31
- add r2, r0, r1 << 2
- ld.w r3, r2[0]
- sbr r3, _PAGE_BIT_DIRTY
- mov r0, r3
- st.w r2[0], r3
- /* The page table is up-to-date. Update the TLB entry as well */
- andl r0, lo(_PAGE_FLAGS_HARDWARE_MASK)
- mtsr SYSREG_TLBELO, r0
- /* MMUCR[DRP] is updated automatically, so let's go... */
- tlbw
- popm r0-r3
- rete
- do_fpe_ll:
- sub sp, 4
- stmts --sp, r0-lr
- rcall save_full_context_ex
- unmask_interrupts
- mov r12, 26
- mov r11, sp
- rcall do_fpe
- rjmp ret_from_exception
- ret_from_exception:
- mask_interrupts
- lddsp r4, sp[REG_SR]
- andh r4, (MODE_MASK >> 16), COH
- brne fault_resume_kernel
- get_thread_info r0
- ld.w r1, r0[TI_flags]
- andl r1, _TIF_WORK_MASK, COH
- brne fault_exit_work
- fault_resume_user:
- popm r8-r9
- mask_exceptions
- mtsr SYSREG_RAR_EX, r8
- mtsr SYSREG_RSR_EX, r9
- ldmts sp++, r0-lr
- sub sp, -4
- rete
- fault_resume_kernel:
- #ifdef CONFIG_PREEMPT
- get_thread_info r0
- ld.w r2, r0[TI_preempt_count]
- cp.w r2, 0
- brne 1f
- ld.w r1, r0[TI_flags]
- bld r1, TIF_NEED_RESCHED
- brcc 1f
- lddsp r4, sp[REG_SR]
- bld r4, SYSREG_GM_OFFSET
- brcs 1f
- rcall preempt_schedule_irq
- 1:
- #endif
- popm r8-r9
- mask_exceptions
- mfsr r1, SYSREG_SR
- mtsr SYSREG_RAR_EX, r8
- mtsr SYSREG_RSR_EX, r9
- popm lr
- sub sp, -4 /* ignore SP */
- popm r0-r12
- sub sp, -4 /* ignore r12_orig */
- rete
- irq_exit_work:
- /* Switch to exception mode so that we can share the same code. */
- mfsr r8, SYSREG_SR
- cbr r8, SYSREG_M0_OFFSET
- orh r8, hi(SYSREG_BIT(M1) | SYSREG_BIT(M2))
- mtsr SYSREG_SR, r8
- sub pc, -2
- get_thread_info r0
- ld.w r1, r0[TI_flags]
- fault_exit_work:
- bld r1, TIF_NEED_RESCHED
- brcc 1f
- unmask_interrupts
- rcall schedule
- mask_interrupts
- ld.w r1, r0[TI_flags]
- rjmp fault_exit_work
- 1: mov r2, _TIF_SIGPENDING | _TIF_RESTORE_SIGMASK
- tst r1, r2
- breq 2f
- unmask_interrupts
- mov r12, sp
- mov r11, r0
- rcall do_notify_resume
- mask_interrupts
- ld.w r1, r0[TI_flags]
- rjmp fault_exit_work
- 2: bld r1, TIF_BREAKPOINT
- brcc fault_resume_user
- mfsr r3, SYSREG_TLBEHI
- lddsp r2, sp[REG_PC]
- andl r3, 0xff, COH
- lsl r3, 1
- sbr r3, 30
- sbr r3, 0
- mtdr DBGREG_BWA2A, r2
- mtdr DBGREG_BWC2A, r3
- rjmp fault_resume_user
- /* If we get a debug trap from privileged context we end up here */
- handle_debug_priv:
- /* Fix up LR and SP in regs. r11 contains the mode we came from */
- mfsr r8, SYSREG_SR
- mov r9, r8
- andh r8, hi(~MODE_MASK)
- or r8, r11
- mtsr SYSREG_SR, r8
- sub pc, -2
- stdsp sp[REG_LR], lr
- mtsr SYSREG_SR, r9
- sub pc, -2
- sub r10, sp, -FRAME_SIZE_FULL
- stdsp sp[REG_SP], r10
- mov r12, sp
- rcall do_debug_priv
- /* Now, put everything back */
- ssrf SR_EM_BIT
- popm r10, r11
- mtsr SYSREG_RAR_DBG, r10
- mtsr SYSREG_RSR_DBG, r11
- mfsr r8, SYSREG_SR
- mov r9, r8
- andh r8, hi(~MODE_MASK)
- andh r11, hi(MODE_MASK)
- or r8, r11
- mtsr SYSREG_SR, r8
- sub pc, -2
- popm lr
- mtsr SYSREG_SR, r9
- sub pc, -2
- sub sp, -4 /* skip SP */
- popm r0-r12
- sub sp, -4
- retd
- /*
- * At this point, everything is masked, that is, interrupts,
- * exceptions and debugging traps. We might get called from
- * interrupt or exception context in some rare cases, but this
- * will be taken care of by do_debug(), so we're not going to
- * do a 100% correct context save here.
- */
- handle_debug:
- sub sp, 4 /* r12_orig */
- stmts --sp, r0-lr
- mfsr r10, SYSREG_RAR_DBG
- mfsr r11, SYSREG_RSR_DBG
- unmask_exceptions
- pushm r10,r11
- andh r11, (MODE_MASK >> 16), COH
- brne handle_debug_priv
- mov r12, sp
- rcall do_debug
- lddsp r10, sp[REG_SR]
- andh r10, (MODE_MASK >> 16), COH
- breq debug_resume_user
- debug_restore_all:
- popm r10,r11
- mask_exceptions
- mtsr SYSREG_RSR_DBG, r11
- mtsr SYSREG_RAR_DBG, r10
- ldmts sp++, r0-lr
- sub sp, -4
- retd
- debug_resume_user:
- get_thread_info r0
- mask_interrupts
- ld.w r1, r0[TI_flags]
- andl r1, _TIF_DBGWORK_MASK, COH
- breq debug_restore_all
- 1: bld r1, TIF_NEED_RESCHED
- brcc 2f
- unmask_interrupts
- rcall schedule
- mask_interrupts
- ld.w r1, r0[TI_flags]
- rjmp 1b
- 2: mov r2, _TIF_SIGPENDING | _TIF_RESTORE_SIGMASK
- tst r1, r2
- breq 3f
- unmask_interrupts
- mov r12, sp
- mov r11, r0
- rcall do_notify_resume
- mask_interrupts
- ld.w r1, r0[TI_flags]
- rjmp 1b
- 3: bld r1, TIF_SINGLE_STEP
- brcc debug_restore_all
- mfdr r2, DBGREG_DC
- sbr r2, DC_SS_BIT
- mtdr DBGREG_DC, r2
- rjmp debug_restore_all
- .set rsr_int0, SYSREG_RSR_INT0
- .set rsr_int1, SYSREG_RSR_INT1
- .set rsr_int2, SYSREG_RSR_INT2
- .set rsr_int3, SYSREG_RSR_INT3
- .set rar_int0, SYSREG_RAR_INT0
- .set rar_int1, SYSREG_RAR_INT1
- .set rar_int2, SYSREG_RAR_INT2
- .set rar_int3, SYSREG_RAR_INT3
- .macro IRQ_LEVEL level
- .type irq_level\level, @function
- irq_level\level:
- sub sp, 4 /* r12_orig */
- stmts --sp,r0-lr
- mfsr r8, rar_int\level
- mfsr r9, rsr_int\level
- #ifdef CONFIG_PREEMPT
- sub r11, pc, (. - system_call)
- cp.w r11, r8
- breq 4f
- #endif
- pushm r8-r9
- mov r11, sp
- mov r12, \level
- rcall do_IRQ
- lddsp r4, sp[REG_SR]
- bfextu r4, r4, SYSREG_M0_OFFSET, 3
- cp.w r4, MODE_SUPERVISOR >> SYSREG_M0_OFFSET
- breq 2f
- cp.w r4, MODE_USER >> SYSREG_M0_OFFSET
- #ifdef CONFIG_PREEMPT
- brne 3f
- #else
- brne 1f
- #endif
- get_thread_info r0
- ld.w r1, r0[TI_flags]
- andl r1, _TIF_WORK_MASK, COH
- brne irq_exit_work
- 1: popm r8-r9
- mtsr rar_int\level, r8
- mtsr rsr_int\level, r9
- ldmts sp++,r0-lr
- sub sp, -4 /* ignore r12_orig */
- rete
- #ifdef CONFIG_PREEMPT
- 4: mask_interrupts
- mfsr r8, rsr_int\level
- sbr r8, 16
- mtsr rsr_int\level, r8
- ldmts sp++, r0-lr
- sub sp, -4 /* ignore r12_orig */
- rete
- #endif
- 2: get_thread_info r0
- ld.w r1, r0[TI_flags]
- bld r1, TIF_CPU_GOING_TO_SLEEP
- #ifdef CONFIG_PREEMPT
- brcc 3f
- #else
- brcc 1b
- #endif
- sub r1, pc, . - cpu_idle_skip_sleep
- stdsp sp[REG_PC], r1
- #ifdef CONFIG_PREEMPT
- 3: get_thread_info r0
- ld.w r2, r0[TI_preempt_count]
- cp.w r2, 0
- brne 1b
- ld.w r1, r0[TI_flags]
- bld r1, TIF_NEED_RESCHED
- brcc 1b
- lddsp r4, sp[REG_SR]
- bld r4, SYSREG_GM_OFFSET
- brcs 1b
- rcall preempt_schedule_irq
- #endif
- rjmp 1b
- .endm
- .section .irq.text,"ax",@progbits
- .global cpu_idle_sleep
- cpu_idle_sleep:
- mask_interrupts
- get_thread_info r8
- ld.w r9, r8[TI_flags]
- bld r9, TIF_NEED_RESCHED
- brcs cpu_idle_enable_int_and_exit
- sbr r9, TIF_CPU_GOING_TO_SLEEP
- st.w r8[TI_flags], r9
- unmask_interrupts
- sleep 0
- cpu_idle_skip_sleep:
- mask_interrupts
- ld.w r9, r8[TI_flags]
- cbr r9, TIF_CPU_GOING_TO_SLEEP
- st.w r8[TI_flags], r9
- cpu_idle_enable_int_and_exit:
- unmask_interrupts
- retal r12
- .global irq_level0
- .global irq_level1
- .global irq_level2
- .global irq_level3
- IRQ_LEVEL 0
- IRQ_LEVEL 1
- IRQ_LEVEL 2
- IRQ_LEVEL 3
|