680 lines · 14.9 KB · Assembly
RawDownload
/* * Low-level exception handling code * * Copyright (C) 2012 ARM Ltd. * Authors:	Catalin Marinas <catalin.marinas@arm.com> *		Will Deacon <will.deacon@arm.com> * * 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 program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program.  If not, see <http://www.gnu.org/licenses/>. */ #include <linux/init.h>#include <linux/linkage.h> #include <asm/assembler.h>#include <asm/asm-offsets.h>#include <asm/errno.h>#include <asm/esr.h>#include <asm/thread_info.h>#include <asm/unistd.h>#include <asm/unistd32.h> /* * Bad Abort numbers *----------------- */#define BAD_SYNC	0#define BAD_IRQ		1#define BAD_FIQ		2#define BAD_ERROR	3 	.macro	kernel_entry, el, regsize = 64	sub	sp, sp, #S_FRAME_SIZE - S_LR	// room for LR, SP, SPSR, ELR	.if	\regsize == 32	mov	w0, w0				// zero upper 32 bits of x0	.endif	push	x28, x29	push	x26, x27	push	x24, x25	push	x22, x23	push	x20, x21	push	x18, x19	push	x16, x17	push	x14, x15	push	x12, x13	push	x10, x11	push	x8, x9	push	x6, x7	push	x4, x5	push	x2, x3	push	x0, x1	.if	\el == 0	mrs	x21, sp_el0	.else	add	x21, sp, #S_FRAME_SIZE	.endif	mrs	x22, elr_el1	mrs	x23, spsr_el1	stp	lr, x21, [sp, #S_LR]	stp	x22, x23, [sp, #S_PC] 	/*	 * Set syscallno to -1 by default (overridden later if real syscall).	 */	.if	\el == 0	mvn	x21, xzr	str	x21, [sp, #S_SYSCALLNO]	.endif 	/*	 * Registers that may be useful after this macro is invoked:	 *	 * x21 - aborted SP	 * x22 - aborted PC	 * x23 - aborted PSTATE	*/	.endm 	.macro	kernel_exit, el, ret = 0	ldp	x21, x22, [sp, #S_PC]		// load ELR, SPSR	.if	\el == 0	ldr	x23, [sp, #S_SP]		// load return stack pointer	.endif	.if	\ret	ldr	x1, [sp, #S_X1]			// preserve x0 (syscall return)	add	sp, sp, S_X2	.else	pop	x0, x1	.endif	pop	x2, x3				// load the rest of the registers	pop	x4, x5	pop	x6, x7	pop	x8, x9	msr	elr_el1, x21			// set up the return data	msr	spsr_el1, x22	.if	\el == 0	msr	sp_el0, x23	.endif	pop	x10, x11	pop	x12, x13	pop	x14, x15	pop	x16, x17	pop	x18, x19	pop	x20, x21	pop	x22, x23	pop	x24, x25	pop	x26, x27	pop	x28, x29	ldr	lr, [sp], #S_FRAME_SIZE - S_LR	// load LR and restore SP	eret					// return to kernel	.endm 	.macro	get_thread_info, rd	mov	\rd, sp	and	\rd, \rd, #~(THREAD_SIZE - 1)	// top of stack	.endm /* * These are the registers used in the syscall handler, and allow us to * have in theory up to 7 arguments to a function - x0 to x6. * * x7 is reserved for the system call number in 32-bit mode. */sc_nr	.req	x25		// number of system callsscno	.req	x26		// syscall numberstbl	.req	x27		// syscall table pointertsk	.req	x28		// current thread_info /* * Interrupt handling. */	.macro	irq_handler	ldr	x1, handle_arch_irq	mov	x0, sp	blr	x1	.endm 	.text /* * Exception vectors. */ 	.align	11ENTRY(vectors)	ventry	el1_sync_invalid		// Synchronous EL1t	ventry	el1_irq_invalid			// IRQ EL1t	ventry	el1_fiq_invalid			// FIQ EL1t	ventry	el1_error_invalid		// Error EL1t 	ventry	el1_sync			// Synchronous EL1h	ventry	el1_irq				// IRQ EL1h	ventry	el1_fiq_invalid			// FIQ EL1h	ventry	el1_error_invalid		// Error EL1h 	ventry	el0_sync			// Synchronous 64-bit EL0	ventry	el0_irq				// IRQ 64-bit EL0	ventry	el0_fiq_invalid			// FIQ 64-bit EL0	ventry	el0_error_invalid		// Error 64-bit EL0 #ifdef CONFIG_COMPAT	ventry	el0_sync_compat			// Synchronous 32-bit EL0	ventry	el0_irq_compat			// IRQ 32-bit EL0	ventry	el0_fiq_invalid_compat		// FIQ 32-bit EL0	ventry	el0_error_invalid_compat	// Error 32-bit EL0#else	ventry	el0_sync_invalid		// Synchronous 32-bit EL0	ventry	el0_irq_invalid			// IRQ 32-bit EL0	ventry	el0_fiq_invalid			// FIQ 32-bit EL0	ventry	el0_error_invalid		// Error 32-bit EL0#endifEND(vectors) /* * Invalid mode handlers */	.macro	inv_entry, el, reason, regsize = 64	kernel_entry el, \regsize	mov	x0, sp	mov	x1, #\reason	mrs	x2, esr_el1	b	bad_mode	.endm el0_sync_invalid:	inv_entry 0, BAD_SYNCENDPROC(el0_sync_invalid) el0_irq_invalid:	inv_entry 0, BAD_IRQENDPROC(el0_irq_invalid) el0_fiq_invalid:	inv_entry 0, BAD_FIQENDPROC(el0_fiq_invalid) el0_error_invalid:	inv_entry 0, BAD_ERRORENDPROC(el0_error_invalid) #ifdef CONFIG_COMPATel0_fiq_invalid_compat:	inv_entry 0, BAD_FIQ, 32ENDPROC(el0_fiq_invalid_compat) el0_error_invalid_compat:	inv_entry 0, BAD_ERROR, 32ENDPROC(el0_error_invalid_compat)#endif el1_sync_invalid:	inv_entry 1, BAD_SYNCENDPROC(el1_sync_invalid) el1_irq_invalid:	inv_entry 1, BAD_IRQENDPROC(el1_irq_invalid) el1_fiq_invalid:	inv_entry 1, BAD_FIQENDPROC(el1_fiq_invalid) el1_error_invalid:	inv_entry 1, BAD_ERRORENDPROC(el1_error_invalid) /* * EL1 mode handlers. */	.align	6el1_sync:	kernel_entry 1	mrs	x1, esr_el1			// read the syndrome register	lsr	x24, x1, #ESR_EL1_EC_SHIFT	// exception class	cmp	x24, #ESR_EL1_EC_DABT_EL1	// data abort in EL1	b.eq	el1_da	cmp	x24, #ESR_EL1_EC_SYS64		// configurable trap	b.eq	el1_undef	cmp	x24, #ESR_EL1_EC_SP_ALIGN	// stack alignment exception	b.eq	el1_sp_pc	cmp	x24, #ESR_EL1_EC_PC_ALIGN	// pc alignment exception	b.eq	el1_sp_pc	cmp	x24, #ESR_EL1_EC_UNKNOWN	// unknown exception in EL1	b.eq	el1_undef	cmp	x24, #ESR_EL1_EC_BREAKPT_EL1	// debug exception in EL1	b.ge	el1_dbg	b	el1_invel1_da:	/*	 * Data abort handling	 */	mrs	x0, far_el1	enable_dbg_if_not_stepping x2	// re-enable interrupts if they were enabled in the aborted context	tbnz	x23, #7, 1f			// PSR_I_BIT	enable_irq1:	mov	x2, sp				// struct pt_regs	bl	do_mem_abort 	// disable interrupts before pulling preserved data off the stack	disable_irq	kernel_exit 1el1_sp_pc:	/*	 * Stack or PC alignment exception handling	 */	mrs	x0, far_el1	mov	x1, x25	mov	x2, sp	b	do_sp_pc_abortel1_undef:	/*	 * Undefined instruction	 */	mov	x0, sp	b	do_undefinstrel1_dbg:	/*	 * Debug exception handling	 */	tbz	x24, #0, el1_inv		// EL1 only	mrs	x0, far_el1	mov	x2, sp				// struct pt_regs	bl	do_debug_exception 	kernel_exit 1el1_inv:	// TODO: add support for undefined instructions in kernel mode	mov	x0, sp	mov	x1, #BAD_SYNC	mrs	x2, esr_el1	b	bad_modeENDPROC(el1_sync) 	.align	6el1_irq:	kernel_entry 1	enable_dbg_if_not_stepping x0#ifdef CONFIG_TRACE_IRQFLAGS	bl	trace_hardirqs_off#endif 	irq_handler #ifdef CONFIG_PREEMPT	get_thread_info tsk	ldr	w24, [tsk, #TI_PREEMPT]		// restore preempt count	cbnz	w24, 1f				// preempt count != 0	ldr	x0, [tsk, #TI_FLAGS]		// get flags	tbz	x0, #TIF_NEED_RESCHED, 1f	// needs rescheduling?	bl	el1_preempt1:#endif#ifdef CONFIG_TRACE_IRQFLAGS	bl	trace_hardirqs_on#endif	kernel_exit 1ENDPROC(el1_irq) #ifdef CONFIG_PREEMPTel1_preempt:	mov	x24, lr1:	enable_dbg	bl	preempt_schedule_irq		// irq en/disable is done inside	ldr	x0, [tsk, #TI_FLAGS]		// get new tasks TI_FLAGS	tbnz	x0, #TIF_NEED_RESCHED, 1b	// needs rescheduling?	ret	x24#endif /* * EL0 mode handlers. */	.align	6el0_sync:	kernel_entry 0	mrs	x25, esr_el1			// read the syndrome register	lsr	x24, x25, #ESR_EL1_EC_SHIFT	// exception class	cmp	x24, #ESR_EL1_EC_SVC64		// SVC in 64-bit state	b.eq	el0_svc	adr	lr, ret_from_exception	cmp	x24, #ESR_EL1_EC_DABT_EL0	// data abort in EL0	b.eq	el0_da	cmp	x24, #ESR_EL1_EC_IABT_EL0	// instruction abort in EL0	b.eq	el0_ia	cmp	x24, #ESR_EL1_EC_FP_ASIMD	// FP/ASIMD access	b.eq	el0_fpsimd_acc	cmp	x24, #ESR_EL1_EC_FP_EXC64	// FP/ASIMD exception	b.eq	el0_fpsimd_exc	cmp	x24, #ESR_EL1_EC_SYS64		// configurable trap	b.eq	el0_undef	cmp	x24, #ESR_EL1_EC_SP_ALIGN	// stack alignment exception	b.eq	el0_sp_pc	cmp	x24, #ESR_EL1_EC_PC_ALIGN	// pc alignment exception	b.eq	el0_sp_pc	cmp	x24, #ESR_EL1_EC_UNKNOWN	// unknown exception in EL0	b.eq	el0_undef	cmp	x24, #ESR_EL1_EC_BREAKPT_EL0	// debug exception in EL0	b.ge	el0_dbg	b	el0_inv #ifdef CONFIG_COMPAT	.align	6el0_sync_compat:	kernel_entry 0, 32	mrs	x25, esr_el1			// read the syndrome register	lsr	x24, x25, #ESR_EL1_EC_SHIFT	// exception class	cmp	x24, #ESR_EL1_EC_SVC32		// SVC in 32-bit state	b.eq	el0_svc_compat	adr	lr, ret_from_exception	cmp	x24, #ESR_EL1_EC_DABT_EL0	// data abort in EL0	b.eq	el0_da	cmp	x24, #ESR_EL1_EC_IABT_EL0	// instruction abort in EL0	b.eq	el0_ia	cmp	x24, #ESR_EL1_EC_FP_ASIMD	// FP/ASIMD access	b.eq	el0_fpsimd_acc	cmp	x24, #ESR_EL1_EC_FP_EXC32	// FP/ASIMD exception	b.eq	el0_fpsimd_exc	cmp	x24, #ESR_EL1_EC_UNKNOWN	// unknown exception in EL0	b.eq	el0_undef	cmp	x24, #ESR_EL1_EC_CP15_32	// CP15 MRC/MCR trap	b.eq	el0_undef	cmp	x24, #ESR_EL1_EC_CP15_64	// CP15 MRRC/MCRR trap	b.eq	el0_undef	cmp	x24, #ESR_EL1_EC_CP14_MR	// CP14 MRC/MCR trap	b.eq	el0_undef	cmp	x24, #ESR_EL1_EC_CP14_LS	// CP14 LDC/STC trap	b.eq	el0_undef	cmp	x24, #ESR_EL1_EC_CP14_64	// CP14 MRRC/MCRR trap	b.eq	el0_undef	cmp	x24, #ESR_EL1_EC_BREAKPT_EL0	// debug exception in EL0	b.ge	el0_dbg	b	el0_invel0_svc_compat:	/*	 * AArch32 syscall handling	 */	adr	stbl, compat_sys_call_table	// load compat syscall table pointer	uxtw	scno, w7			// syscall number in w7 (r7)	mov     sc_nr, #__NR_compat_syscalls	b	el0_svc_naked 	.align	6el0_irq_compat:	kernel_entry 0, 32	b	el0_irq_naked#endif el0_da:	/*	 * Data abort handling	 */	mrs	x0, far_el1	bic	x0, x0, #(0xff << 56)	disable_step x1	isb	enable_dbg	// enable interrupts before calling the main handler	enable_irq	mov	x1, x25	mov	x2, sp	b	do_mem_abortel0_ia:	/*	 * Instruction abort handling	 */	mrs	x0, far_el1	disable_step x1	isb	enable_dbg	// enable interrupts before calling the main handler	enable_irq	orr	x1, x25, #1 << 24		// use reserved ISS bit for instruction aborts	mov	x2, sp	b	do_mem_abortel0_fpsimd_acc:	/*	 * Floating Point or Advanced SIMD access	 */	mov	x0, x25	mov	x1, sp	b	do_fpsimd_accel0_fpsimd_exc:	/*	 * Floating Point or Advanced SIMD exception	 */	mov	x0, x25	mov	x1, sp	b	do_fpsimd_excel0_sp_pc:	/*	 * Stack or PC alignment exception handling	 */	mrs	x0, far_el1	disable_step x1	isb	enable_dbg	// enable interrupts before calling the main handler	enable_irq	mov	x1, x25	mov	x2, sp	b	do_sp_pc_abortel0_undef:	/*	 * Undefined instruction	 */	mov	x0, sp	// enable interrupts before calling the main handler	enable_irq	b	do_undefinstrel0_dbg:	/*	 * Debug exception handling	 */	tbnz	x24, #0, el0_inv		// EL0 only	mrs	x0, far_el1	disable_step x1	mov	x1, x25	mov	x2, sp	b	do_debug_exceptionel0_inv:	mov	x0, sp	mov	x1, #BAD_SYNC	mrs	x2, esr_el1	b	bad_modeENDPROC(el0_sync) 	.align	6el0_irq:	kernel_entry 0el0_irq_naked:

Lines 1–500 of 680.