|  | /* | 
|  | * 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 | 
|  |  | 
|  | #define	tlbmiss_save	pushm	r0-r3 | 
|  | #define tlbmiss_restore	popm	r0-r3 | 
|  |  | 
|  | .org	0x50 | 
|  | .global	itlb_miss | 
|  | itlb_miss: | 
|  | tlbmiss_save | 
|  | rjmp	tlb_miss_common | 
|  |  | 
|  | .org	0x60 | 
|  | dtlb_miss_read: | 
|  | tlbmiss_save | 
|  | rjmp	tlb_miss_common | 
|  |  | 
|  | .org	0x70 | 
|  | dtlb_miss_write: | 
|  | tlbmiss_save | 
|  |  | 
|  | .global	tlb_miss_common | 
|  | .align	2 | 
|  | tlb_miss_common: | 
|  | mfsr	r0, SYSREG_TLBEAR | 
|  | mfsr	r1, SYSREG_PTBR | 
|  |  | 
|  | /* | 
|  | * First level lookup: The PGD contains virtual pointers to | 
|  | * the second-level page tables, but they may be NULL if not | 
|  | * present. | 
|  | */ | 
|  | pgtbl_lookup: | 
|  | lsr	r2, r0, PGDIR_SHIFT | 
|  | ld.w	r3, r1[r2 << 2] | 
|  | bfextu	r1, r0, PAGE_SHIFT, PGDIR_SHIFT - PAGE_SHIFT | 
|  | cp.w	r3, 0 | 
|  | breq	page_table_not_present | 
|  |  | 
|  | /* 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 | 
|  |  | 
|  | /* The slow path of the TLB miss handler */ | 
|  | .align	2 | 
|  | page_table_not_present: | 
|  | /* Do we need to synchronize with swapper_pg_dir? */ | 
|  | bld	r0, 31 | 
|  | brcs	sync_with_swapper_pg_dir | 
|  |  | 
|  | page_not_present: | 
|  | tlbmiss_restore | 
|  | sub	sp, 4 | 
|  | stmts	--sp, r0-lr | 
|  | call	save_full_context_ex | 
|  | mfsr	r12, SYSREG_ECR | 
|  | mov	r11, sp | 
|  | call	do_page_fault | 
|  | rjmp	ret_from_exception | 
|  |  | 
|  | .align	2 | 
|  | sync_with_swapper_pg_dir: | 
|  | /* | 
|  | * If swapper_pg_dir contains a non-NULL second-level page | 
|  | * table pointer, copy it into the current PGD. If not, we | 
|  | * must handle it as a full-blown page fault. | 
|  | * | 
|  | * Jumping back to pgtbl_lookup causes an unnecessary lookup, | 
|  | * but it is guaranteed to be a cache hit, it won't happen | 
|  | * very often, and we absolutely do not want to sacrifice any | 
|  | * performance in the fast path in order to improve this. | 
|  | */ | 
|  | mov	r1, lo(swapper_pg_dir) | 
|  | orh	r1, hi(swapper_pg_dir) | 
|  | ld.w	r3, r1[r2 << 2] | 
|  | cp.w	r3, 0 | 
|  | breq	page_not_present | 
|  | mfsr	r1, SYSREG_PTBR | 
|  | st.w	r1[r2 << 2], r3 | 
|  | rjmp	pgtbl_lookup | 
|  |  | 
|  | /* | 
|  | * We currently have two bytes left at this point until we | 
|  | * crash into the system call handler... | 
|  | * | 
|  | * Don't worry, the assembler will let us know. | 
|  | */ | 
|  |  | 
|  |  | 
|  | /* ---                    System Call                    --- */ | 
|  |  | 
|  | .org	0x100 | 
|  | 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: | 
|  | call   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 | 
|  | call	syscall_trace | 
|  | popm	r8-r12 | 
|  | rjmp	syscall_trace_cont | 
|  |  | 
|  | syscall_exit_work: | 
|  | bld	r1, TIF_SYSCALL_TRACE | 
|  | brcc	1f | 
|  | unmask_interrupts | 
|  | call	syscall_trace | 
|  | mask_interrupts | 
|  | ld.w	r1, r0[TI_flags] | 
|  |  | 
|  | 1:	bld	r1, TIF_NEED_RESCHED | 
|  | brcc	2f | 
|  | unmask_interrupts | 
|  | call	schedule | 
|  | mask_interrupts | 
|  | ld.w	r1, r0[TI_flags] | 
|  | rjmp	1b | 
|  |  | 
|  | 2:	mov	r2, _TIF_SIGPENDING | _TIF_NOTIFY_RESUME | 
|  | tst	r1, r2 | 
|  | breq	3f | 
|  | unmask_interrupts | 
|  | mov	r12, sp | 
|  | mov	r11, r0 | 
|  | call	do_notify_resume | 
|  | mask_interrupts | 
|  | ld.w	r1, r0[TI_flags] | 
|  | rjmp	1b | 
|  |  | 
|  | 3:	bld	r1, TIF_BREAKPOINT | 
|  | brcc	syscall_exit_cont | 
|  | rjmp	enter_monitor_mode | 
|  |  | 
|  | /* This function expects to find offending PC in SYSREG_RAR_EX */ | 
|  | .type	save_full_context_ex, @function | 
|  | .align	2 | 
|  | save_full_context_ex: | 
|  | mfsr	r11, SYSREG_RAR_EX | 
|  | sub	r9, pc, . - debug_trampoline | 
|  | mfsr	r8, SYSREG_RSR_EX | 
|  | cp.w	r9, r11 | 
|  | breq	3f | 
|  | mov	r12, r8 | 
|  | andh	r8, (MODE_MASK >> 16), COH | 
|  | 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 | 
|  |  | 
|  | /* | 
|  | * The debug handler set up a trampoline to make us | 
|  | * automatically enter monitor mode upon return, but since | 
|  | * we're saving the full context, we must assume that the | 
|  | * exception handler might want to alter the return address | 
|  | * and/or status register. So we need to restore the original | 
|  | * context and enter monitor mode manually after the exception | 
|  | * has been handled. | 
|  | */ | 
|  | 3:	get_thread_info r8 | 
|  | ld.w	r11, r8[TI_rar_saved] | 
|  | ld.w	r12, r8[TI_rsr_saved] | 
|  | rjmp	1b | 
|  | .size	save_full_context_ex, . - save_full_context_ex | 
|  |  | 
|  | /* Low-level exception handlers */ | 
|  | handle_critical: | 
|  | /* | 
|  | * AT32AP700x errata: | 
|  | * | 
|  | * After a Java stack overflow or underflow trap, any CPU | 
|  | * memory access may cause erratic behavior. This will happen | 
|  | * when the four least significant bits of the JOSP system | 
|  | * register contains any value between 9 and 15 (inclusive). | 
|  | * | 
|  | * Possible workarounds: | 
|  | *   - Don't use the Java Extension Module | 
|  | *   - Ensure that the stack overflow and underflow trap | 
|  | *     handlers do not do any memory access or trigger any | 
|  | *     exceptions before the overflow/underflow condition is | 
|  | *     cleared (by incrementing or decrementing the JOSP) | 
|  | *   - Make sure that JOSP does not contain any problematic | 
|  | *     value before doing any exception or interrupt | 
|  | *     processing. | 
|  | *   - Set up a critical exception handler which writes a | 
|  | *     known-to-be-safe value, e.g. 4, to JOSP before doing | 
|  | *     any further processing. | 
|  | * | 
|  | * We'll use the last workaround for now since we cannot | 
|  | * guarantee that user space processes don't use Java mode. | 
|  | * Non-well-behaving userland will be terminated with extreme | 
|  | * prejudice. | 
|  | */ | 
|  | #ifdef CONFIG_CPU_AT32AP700X | 
|  | /* | 
|  | * There's a chance we can't touch memory, so temporarily | 
|  | * borrow PTBR to save the stack pointer while we fix things | 
|  | * up... | 
|  | */ | 
|  | mtsr	SYSREG_PTBR, sp | 
|  | mov	sp, 4 | 
|  | mtsr	SYSREG_JOSP, sp | 
|  | mfsr	sp, SYSREG_PTBR | 
|  | sub	pc, -2 | 
|  |  | 
|  | /* Push most of pt_regs on stack. We'll do the rest later */ | 
|  | sub	sp, 4 | 
|  | pushm	r0-r12 | 
|  |  | 
|  | /* PTBR mirrors current_thread_info()->task->active_mm->pgd */ | 
|  | get_thread_info r0 | 
|  | ld.w	r1, r0[TI_task] | 
|  | ld.w	r2, r1[TSK_active_mm] | 
|  | ld.w	r3, r2[MM_pgd] | 
|  | mtsr	SYSREG_PTBR, r3 | 
|  | #else | 
|  | sub	sp, 4 | 
|  | pushm	r0-r12 | 
|  | #endif | 
|  | sub	r0, sp, -(14 * 4) | 
|  | mov	r1, lr | 
|  | mfsr	r2, SYSREG_RAR_EX | 
|  | mfsr	r3, SYSREG_RSR_EX | 
|  | pushm	r0-r3 | 
|  |  | 
|  | mfsr	r12, SYSREG_ECR | 
|  | mov	r11, sp | 
|  | call	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 | 
|  | call	save_full_context_ex | 
|  | mov	r11, 1 | 
|  | rjmp	1f | 
|  |  | 
|  | do_bus_error_read: | 
|  | sub	sp, 4 | 
|  | stmts	--sp, r0-lr | 
|  | call	save_full_context_ex | 
|  | mov	r11, 0 | 
|  | 1:	mfsr	r12, SYSREG_BEAR | 
|  | mov	r10, sp | 
|  | call	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 | 
|  | call	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 | 
|  | call	save_full_context_ex | 
|  | mfsr	r12, SYSREG_ECR | 
|  | mov	r11, sp | 
|  | call	do_address_exception | 
|  | rjmp	ret_from_exception | 
|  |  | 
|  | handle_protection_fault: | 
|  | sub	sp, 4 | 
|  | stmts	--sp, r0-lr | 
|  | call	save_full_context_ex | 
|  | mfsr	r12, SYSREG_ECR | 
|  | mov	r11, sp | 
|  | call	do_page_fault | 
|  | rjmp	ret_from_exception | 
|  |  | 
|  | .align	1 | 
|  | do_illegal_opcode_ll: | 
|  | sub	sp, 4 | 
|  | stmts	--sp, r0-lr | 
|  | call	save_full_context_ex | 
|  | mfsr	r12, SYSREG_ECR | 
|  | mov	r11, sp | 
|  | call	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 | 
|  | call	save_full_context_ex | 
|  | unmask_interrupts | 
|  | mov	r12, 26 | 
|  | mov	r11, sp | 
|  | call	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 | 
|  | call	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 | 
|  | call	schedule | 
|  | mask_interrupts | 
|  | ld.w	r1, r0[TI_flags] | 
|  | rjmp	fault_exit_work | 
|  |  | 
|  | 1:	mov	r2, _TIF_SIGPENDING | _TIF_NOTIFY_RESUME | 
|  | tst	r1, r2 | 
|  | breq	2f | 
|  | unmask_interrupts | 
|  | mov	r12, sp | 
|  | mov	r11, r0 | 
|  | call	do_notify_resume | 
|  | mask_interrupts | 
|  | ld.w	r1, r0[TI_flags] | 
|  | rjmp	fault_exit_work | 
|  |  | 
|  | 2:	bld	r1, TIF_BREAKPOINT | 
|  | brcc	fault_resume_user | 
|  | rjmp	enter_monitor_mode | 
|  |  | 
|  | .section .kprobes.text, "ax", @progbits | 
|  | .type	handle_debug, @function | 
|  | handle_debug: | 
|  | sub	sp, 4		/* r12_orig */ | 
|  | stmts	--sp, r0-lr | 
|  | mfsr	r8, SYSREG_RAR_DBG | 
|  | mfsr	r9, SYSREG_RSR_DBG | 
|  | unmask_exceptions | 
|  | pushm	r8-r9 | 
|  | bfextu	r9, r9, SYSREG_MODE_OFFSET, SYSREG_MODE_SIZE | 
|  | brne	debug_fixup_regs | 
|  |  | 
|  | .Ldebug_fixup_cont: | 
|  | #ifdef CONFIG_TRACE_IRQFLAGS | 
|  | call	trace_hardirqs_off | 
|  | #endif | 
|  | mov	r12, sp | 
|  | call	do_debug | 
|  | mov	sp, r12 | 
|  |  | 
|  | lddsp	r2, sp[REG_SR] | 
|  | bfextu	r3, r2, SYSREG_MODE_OFFSET, SYSREG_MODE_SIZE | 
|  | brne	debug_resume_kernel | 
|  |  | 
|  | get_thread_info r0 | 
|  | ld.w	r1, r0[TI_flags] | 
|  | mov	r2, _TIF_DBGWORK_MASK | 
|  | tst	r1, r2 | 
|  | brne	debug_exit_work | 
|  |  | 
|  | bld	r1, TIF_SINGLE_STEP | 
|  | brcc	1f | 
|  | mfdr	r4, OCD_DC | 
|  | sbr	r4, OCD_DC_SS_BIT | 
|  | mtdr	OCD_DC, r4 | 
|  |  | 
|  | 1:	popm	r10,r11 | 
|  | mask_exceptions | 
|  | mtsr	SYSREG_RSR_DBG, r11 | 
|  | mtsr	SYSREG_RAR_DBG, r10 | 
|  | #ifdef CONFIG_TRACE_IRQFLAGS | 
|  | call	trace_hardirqs_on | 
|  | 1: | 
|  | #endif | 
|  | ldmts	sp++, r0-lr | 
|  | sub	sp, -4 | 
|  | retd | 
|  | .size	handle_debug, . - handle_debug | 
|  |  | 
|  | /* Mode of the trapped context is in r9 */ | 
|  | .type	debug_fixup_regs, @function | 
|  | debug_fixup_regs: | 
|  | mfsr	r8, SYSREG_SR | 
|  | mov	r10, r8 | 
|  | bfins	r8, r9, SYSREG_MODE_OFFSET, SYSREG_MODE_SIZE | 
|  | mtsr	SYSREG_SR, r8 | 
|  | sub	pc, -2 | 
|  | stdsp	sp[REG_LR], lr | 
|  | mtsr	SYSREG_SR, r10 | 
|  | sub	pc, -2 | 
|  | sub	r8, sp, -FRAME_SIZE_FULL | 
|  | stdsp	sp[REG_SP], r8 | 
|  | rjmp	.Ldebug_fixup_cont | 
|  | .size	debug_fixup_regs, . - debug_fixup_regs | 
|  |  | 
|  | .type	debug_resume_kernel, @function | 
|  | debug_resume_kernel: | 
|  | mask_exceptions | 
|  | popm	r10, r11 | 
|  | mtsr	SYSREG_RAR_DBG, r10 | 
|  | mtsr	SYSREG_RSR_DBG, r11 | 
|  | #ifdef CONFIG_TRACE_IRQFLAGS | 
|  | bld	r11, SYSREG_GM_OFFSET | 
|  | brcc	1f | 
|  | call	trace_hardirqs_on | 
|  | 1: | 
|  | #endif | 
|  | mfsr	r2, SYSREG_SR | 
|  | mov	r1, r2 | 
|  | bfins	r2, r3, SYSREG_MODE_OFFSET, SYSREG_MODE_SIZE | 
|  | mtsr	SYSREG_SR, r2 | 
|  | sub	pc, -2 | 
|  | popm	lr | 
|  | mtsr	SYSREG_SR, r1 | 
|  | sub	pc, -2 | 
|  | sub	sp, -4		/* skip SP */ | 
|  | popm	r0-r12 | 
|  | sub	sp, -4 | 
|  | retd | 
|  | .size	debug_resume_kernel, . - debug_resume_kernel | 
|  |  | 
|  | .type	debug_exit_work, @function | 
|  | debug_exit_work: | 
|  | /* | 
|  | * We must return from Monitor Mode using a retd, and we must | 
|  | * not schedule since that involves the D bit in SR getting | 
|  | * cleared by something other than the debug hardware. This | 
|  | * may cause undefined behaviour according to the Architecture | 
|  | * manual. | 
|  | * | 
|  | * So we fix up the return address and status and return to a | 
|  | * stub below in Exception mode. From there, we can follow the | 
|  | * normal exception return path. | 
|  | * | 
|  | * The real return address and status registers are stored on | 
|  | * the stack in the way the exception return path understands, | 
|  | * so no need to fix anything up there. | 
|  | */ | 
|  | sub	r8, pc, . - fault_exit_work | 
|  | mtsr	SYSREG_RAR_DBG, r8 | 
|  | mov	r9, 0 | 
|  | orh	r9, hi(SR_EM | SR_GM | MODE_EXCEPTION) | 
|  | mtsr	SYSREG_RSR_DBG, r9 | 
|  | sub	pc, -2 | 
|  | retd | 
|  | .size	debug_exit_work, . - debug_exit_work | 
|  |  | 
|  | .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 | 
|  |  | 
|  | call	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: | 
|  | #ifdef CONFIG_TRACE_IRQFLAGS | 
|  | call	trace_hardirqs_on | 
|  | #endif | 
|  | 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 | 
|  | call	preempt_schedule_irq | 
|  | #endif | 
|  | rjmp	1b | 
|  | .endm | 
|  |  | 
|  | .section .irq.text,"ax",@progbits | 
|  |  | 
|  | .global	irq_level0 | 
|  | .global	irq_level1 | 
|  | .global	irq_level2 | 
|  | .global	irq_level3 | 
|  | IRQ_LEVEL 0 | 
|  | IRQ_LEVEL 1 | 
|  | IRQ_LEVEL 2 | 
|  | IRQ_LEVEL 3 | 
|  |  | 
|  | .section .kprobes.text, "ax", @progbits | 
|  | .type	enter_monitor_mode, @function | 
|  | enter_monitor_mode: | 
|  | /* | 
|  | * We need to enter monitor mode to do a single step. The | 
|  | * monitor code will alter the return address so that we | 
|  | * return directly to the user instead of returning here. | 
|  | */ | 
|  | breakpoint | 
|  | rjmp	breakpoint_failed | 
|  |  | 
|  | .size	enter_monitor_mode, . - enter_monitor_mode | 
|  |  | 
|  | .type	debug_trampoline, @function | 
|  | .global	debug_trampoline | 
|  | debug_trampoline: | 
|  | /* | 
|  | * Save the registers on the stack so that the monitor code | 
|  | * can find them easily. | 
|  | */ | 
|  | sub	sp, 4		/* r12_orig */ | 
|  | stmts	--sp, r0-lr | 
|  | get_thread_info	r0 | 
|  | ld.w	r8, r0[TI_rar_saved] | 
|  | ld.w	r9, r0[TI_rsr_saved] | 
|  | pushm	r8-r9 | 
|  |  | 
|  | /* | 
|  | * The monitor code will alter the return address so we don't | 
|  | * return here. | 
|  | */ | 
|  | breakpoint | 
|  | rjmp	breakpoint_failed | 
|  | .size	debug_trampoline, . - debug_trampoline | 
|  |  | 
|  | .type breakpoint_failed, @function | 
|  | breakpoint_failed: | 
|  | /* | 
|  | * Something went wrong. Perhaps the debug hardware isn't | 
|  | * enabled? | 
|  | */ | 
|  | lda.w	r12, msg_breakpoint_failed | 
|  | mov	r11, sp | 
|  | mov	r10, 9		/* SIGKILL */ | 
|  | call	die | 
|  | 1:	rjmp	1b | 
|  |  | 
|  | msg_breakpoint_failed: | 
|  | .asciz	"Failed to enter Debug Mode" |