2264 lines · 52.8 KB · Assembly
RawDownload
/* * Low-level exception handling * * This file is subject to the terms and conditions of the GNU General Public * License.  See the file "COPYING" in the main directory of this archive * for more details. * * Copyright (C) 2004 - 2008 by Tensilica Inc. * Copyright (C) 2015 Cadence Design Systems Inc. * * Chris Zankel <chris@zankel.net> * */ #include <linux/linkage.h>#include <linux/pgtable.h>#include <asm/asm-offsets.h>#include <asm/asmmacro.h>#include <asm/processor.h>#include <asm/coprocessor.h>#include <asm/thread_info.h>#include <asm/asm-uaccess.h>#include <asm/unistd.h>#include <asm/ptrace.h>#include <asm/current.h>#include <asm/page.h>#include <asm/signal.h>#include <asm/tlbflush.h>#include <variant/tie-asm.h> /* * Macro to find first bit set in WINDOWBASE from the left + 1 * * 100....0 -> 1 * 010....0 -> 2 * 000....1 -> WSBITS */ 	.macro ffs_ws bit mask #if XCHAL_HAVE_NSA	nsau    \bit, \mask			# 32-WSBITS ... 31 (32 iff 0)	addi    \bit, \bit, WSBITS - 32 + 1   	# uppest bit set -> return 1#else	movi    \bit, WSBITS#if WSBITS > 16	_bltui  \mask, 0x10000, 99f	addi    \bit, \bit, -16	extui   \mask, \mask, 16, 16#endif#if WSBITS > 899:	_bltui  \mask, 0x100, 99f	addi    \bit, \bit, -8	srli    \mask, \mask, 8#endif99:	_bltui  \mask, 0x10, 99f	addi    \bit, \bit, -4	srli    \mask, \mask, 499:	_bltui  \mask, 0x4, 99f	addi    \bit, \bit, -2	srli    \mask, \mask, 299:	_bltui  \mask, 0x2, 99f	addi    \bit, \bit, -199: #endif	.endm  	.macro	irq_save flags tmp#if XTENSA_FAKE_NMI#if defined(CONFIG_DEBUG_KERNEL) && (LOCKLEVEL | TOPLEVEL) >= XCHAL_DEBUGLEVEL	rsr	\flags, ps	extui	\tmp, \flags, PS_INTLEVEL_SHIFT, PS_INTLEVEL_WIDTH	bgei	\tmp, LOCKLEVEL, 99f	rsil	\tmp, LOCKLEVEL99:#else	movi	\tmp, LOCKLEVEL	rsr	\flags, ps	or	\flags, \flags, \tmp	xsr	\flags, ps	rsync#endif#else	rsil	\flags, LOCKLEVEL#endif	.endm /* ----------------- DEFAULT FIRST LEVEL EXCEPTION HANDLERS ----------------- */ /* * First-level exception handler for user exceptions. * Save some special registers, extra states and all registers in the AR * register file that were in use in the user task, and jump to the common * exception code. * We save SAR (used to calculate WMASK), and WB and WS (we don't have to * save them for kernel exceptions). * * Entry condition for user_exception: * *   a0:	trashed, original value saved on stack (PT_AREG0) *   a1:	a1 *   a2:	new stack pointer, original value in depc *   a3:	a3 *   depc:	a2, original value saved on stack (PT_DEPC) *   excsave1:	dispatch table * *   PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception, DEPC *	     <  VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception * * Entry condition for _user_exception: * *   a0-a3 and depc have been saved to PT_AREG0...PT_AREG3 and PT_DEPC *   excsave has been restored, and *   stack pointer (a1) has been set. * * Note: _user_exception might be at an odd address. Don't use call0..call12 */	.literal_position ENTRY(user_exception) 	/* Save a1, a2, a3, and set SP. */ 	rsr	a0, depc	s32i	a1, a2, PT_AREG1	s32i	a0, a2, PT_AREG2	s32i	a3, a2, PT_AREG3	mov	a1, a2 	.globl _user_exception_user_exception: 	/* Save SAR and turn off single stepping */ 	movi	a2, 0	wsr	a2, depc		# terminate user stack trace with 0	rsr	a3, sar	xsr	a2, icountlevel	s32i	a3, a1, PT_SAR	s32i	a2, a1, PT_ICOUNTLEVEL #if XCHAL_HAVE_THREADPTR	rur	a2, threadptr	s32i	a2, a1, PT_THREADPTR#endif 	/* Rotate ws so that the current windowbase is at bit0. */	/* Assume ws = xxwww1yyyy. Rotate ws right, so that a2 = yyyyxxwww1 */ #if defined(USER_SUPPORT_WINDOWED)	rsr	a2, windowbase	rsr	a3, windowstart	ssr	a2	s32i	a2, a1, PT_WINDOWBASE	s32i	a3, a1, PT_WINDOWSTART	slli	a2, a3, 32-WSBITS	src	a2, a3, a2	srli	a2, a2, 32-WSBITS	s32i	a2, a1, PT_WMASK	# needed for restoring registers#else	movi	a2, 0	movi	a3, 1	s32i	a2, a1, PT_WINDOWBASE	s32i	a3, a1, PT_WINDOWSTART	s32i	a3, a1, PT_WMASK#endif 	/* Save only live registers. */ UABI_W	_bbsi.l	a2, 1, .Lsave_window_registers	s32i	a4, a1, PT_AREG4	s32i	a5, a1, PT_AREG5	s32i	a6, a1, PT_AREG6	s32i	a7, a1, PT_AREG7UABI_W	_bbsi.l	a2, 2, .Lsave_window_registers	s32i	a8, a1, PT_AREG8	s32i	a9, a1, PT_AREG9	s32i	a10, a1, PT_AREG10	s32i	a11, a1, PT_AREG11UABI_W	_bbsi.l	a2, 3, .Lsave_window_registers	s32i	a12, a1, PT_AREG12	s32i	a13, a1, PT_AREG13	s32i	a14, a1, PT_AREG14	s32i	a15, a1, PT_AREG15 #if defined(USER_SUPPORT_WINDOWED)	/* If only one valid frame skip saving regs. */ 	beqi	a2, 1, common_exception 	/* Save the remaining registers.	 * We have to save all registers up to the first '1' from	 * the right, except the current frame (bit 0).	 * Assume a2 is:  001001000110001	 * All register frames starting from the top field to the marked '1'	 * must be saved.	 */.Lsave_window_registers:	addi	a3, a2, -1		# eliminate '1' in bit 0: yyyyxxww0	neg	a3, a3			# yyyyxxww0 -> YYYYXXWW1+1	and	a3, a3, a2		# max. only one bit is set 	/* Find number of frames to save */ 	ffs_ws	a0, a3			# number of frames to the '1' from left 	/* Store information into WMASK:	 * bits 0..3: xxx1 masked lower 4 bits of the rotated windowstart,	 * bits 4...: number of valid 4-register frames	 */ 	slli	a3, a0, 4		# number of frames to save in bits 8..4	extui	a2, a2, 0, 4		# mask for the first 16 registers	or	a2, a3, a2	s32i	a2, a1, PT_WMASK	# needed when we restore the reg-file 	/* Save 4 registers at a time */ 1:	rotw	-1	s32i	a0, a5, PT_AREG_END - 16	s32i	a1, a5, PT_AREG_END - 12	s32i	a2, a5, PT_AREG_END - 8	s32i	a3, a5, PT_AREG_END - 4	addi	a0, a4, -1	addi	a1, a5, -16	_bnez	a0, 1b 	/* WINDOWBASE still in SAR! */ 	rsr	a2, sar			# original WINDOWBASE	movi	a3, 1	ssl	a2	sll	a3, a3	wsr	a3, windowstart		# set corresponding WINDOWSTART bit	wsr	a2, windowbase		# and WINDOWSTART	rsync 	/* We are back to the original stack pointer (a1) */#endif	/* Now, jump to the common exception handler. */ 	j	common_exception ENDPROC(user_exception) /* * First-level exit handler for kernel exceptions * Save special registers and the live window frame. * Note: Even though we changes the stack pointer, we don't have to do a *	 MOVSP here, as we do that when we return from the exception. *	 (See comment in the kernel exception exit code) * * Entry condition for kernel_exception: * *   a0:	trashed, original value saved on stack (PT_AREG0) *   a1:	a1 *   a2:	new stack pointer, original in DEPC *   a3:	a3 *   depc:	a2, original value saved on stack (PT_DEPC) *   excsave_1:	dispatch table * *   PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception, DEPC *	     <  VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception * * Entry condition for _kernel_exception: * *   a0-a3 and depc have been saved to PT_AREG0...PT_AREG3 and PT_DEPC *   excsave has been restored, and *   stack pointer (a1) has been set. * * Note: _kernel_exception might be at an odd address. Don't use call0..call12 */ ENTRY(kernel_exception) 	/* Save a1, a2, a3, and set SP. */ 	rsr	a0, depc		# get a2	s32i	a1, a2, PT_AREG1	s32i	a0, a2, PT_AREG2	s32i	a3, a2, PT_AREG3	mov	a1, a2 	.globl _kernel_exception_kernel_exception: 	/* Save SAR and turn off single stepping */ 	movi	a2, 0	rsr	a3, sar	xsr	a2, icountlevel	s32i	a3, a1, PT_SAR	s32i	a2, a1, PT_ICOUNTLEVEL #if defined(__XTENSA_WINDOWED_ABI__)	/* Rotate ws so that the current windowbase is at bit0. */	/* Assume ws = xxwww1yyyy. Rotate ws right, so that a2 = yyyyxxwww1 */ 	rsr	a2, windowbase		# don't need to save these, we only	rsr	a3, windowstart		# need shifted windowstart: windowmask	ssr	a2	slli	a2, a3, 32-WSBITS	src	a2, a3, a2	srli	a2, a2, 32-WSBITS	s32i	a2, a1, PT_WMASK	# needed for kernel_exception_exit#endif 	/* Save only the live window-frame */ KABI_W	_bbsi.l	a2, 1, 1f	s32i	a4, a1, PT_AREG4	s32i	a5, a1, PT_AREG5	s32i	a6, a1, PT_AREG6	s32i	a7, a1, PT_AREG7KABI_W	_bbsi.l	a2, 2, 1f	s32i	a8, a1, PT_AREG8	s32i	a9, a1, PT_AREG9	s32i	a10, a1, PT_AREG10	s32i	a11, a1, PT_AREG11KABI_W	_bbsi.l	a2, 3, 1f	s32i	a12, a1, PT_AREG12	s32i	a13, a1, PT_AREG13	s32i	a14, a1, PT_AREG14	s32i	a15, a1, PT_AREG15 #ifdef __XTENSA_WINDOWED_ABI__	_bnei	a2, 1, 1f	/* Copy spill slots of a0 and a1 to imitate movsp	 * in order to keep exception stack continuous	 */	l32i	a3, a1, PT_KERNEL_SIZE	l32i	a0, a1, PT_KERNEL_SIZE + 4	s32e	a3, a1, -16	s32e	a0, a1, -12#endif1:	l32i	a0, a1, PT_AREG0	# restore saved a0	wsr	a0, depc /* * This is the common exception handler. * We get here from the user exception handler or simply by falling through * from the kernel exception handler. * Save the remaining special registers, switch to kernel mode, and jump * to the second-level exception handler. * */ common_exception: 	/* Save some registers, disable loops and clear the syscall flag. */ 	rsr	a2, debugcause	rsr	a3, epc1	s32i	a2, a1, PT_DEBUGCAUSE	s32i	a3, a1, PT_PC 	movi	a2, NO_SYSCALL	rsr	a3, excvaddr	s32i	a2, a1, PT_SYSCALL	movi	a2, 0	s32i	a3, a1, PT_EXCVADDR#if XCHAL_HAVE_LOOPS	xsr	a2, lcount	s32i	a2, a1, PT_LCOUNT#endif #if XCHAL_HAVE_EXCLUSIVE	/* Clear exclusive access monitor set by interrupted code */	clrex#endif 	/* It is now save to restore the EXC_TABLE_FIXUP variable. */ 	rsr	a2, exccause	movi	a3, 0	rsr	a0, excsave1	s32i	a2, a1, PT_EXCCAUSE	s32i	a3, a0, EXC_TABLE_FIXUP 	/* All unrecoverable states are saved on stack, now, and a1 is valid.	 * Now we can allow exceptions again. In case we've got an interrupt	 * PS.INTLEVEL is set to LOCKLEVEL disabling furhter interrupts,	 * otherwise it's left unchanged.	 *	 * Set PS(EXCM = 0, UM = 0, RING = 0, OWB = 0, WOE = 1, INTLEVEL = X)	 */ 	rsr	a3, ps	s32i	a3, a1, PT_PS		# save ps #if XTENSA_FAKE_NMI	/* Correct PS needs to be saved in the PT_PS:	 * - in case of exception or level-1 interrupt it's in the PS,	 *   and is already saved.	 * - in case of medium level interrupt it's in the excsave2.	 */	movi	a0, EXCCAUSE_MAPPED_NMI	extui	a3, a3, PS_INTLEVEL_SHIFT, PS_INTLEVEL_WIDTH	beq	a2, a0, .Lmedium_level_irq	bnei	a2, EXCCAUSE_LEVEL1_INTERRUPT, .Lexception	beqz	a3, .Llevel1_irq	# level-1 IRQ sets ps.intlevel to 0 .Lmedium_level_irq:	rsr	a0, excsave2	s32i	a0, a1, PT_PS		# save medium-level interrupt ps	bgei	a3, LOCKLEVEL, .Lexception .Llevel1_irq:	movi	a3, LOCKLEVEL .Lexception:KABI_W	movi	a0, PS_WOE_MASKKABI_W	or	a3, a3, a0#else	addi	a2, a2, -EXCCAUSE_LEVEL1_INTERRUPT	movi	a0, LOCKLEVEL	extui	a3, a3, PS_INTLEVEL_SHIFT, PS_INTLEVEL_WIDTH					# a3 = PS.INTLEVEL	moveqz	a3, a0, a2		# a3 = LOCKLEVEL iff interruptKABI_W	movi	a2, PS_WOE_MASKKABI_W	or	a3, a3, a2#endif 	/* restore return address (or 0 if return to userspace) */	rsr	a0, depc	wsr	a3, ps	rsync				# PS.WOE => rsync => overflow 	/* Save lbeg, lend */#if XCHAL_HAVE_LOOPS	rsr	a4, lbeg	rsr	a3, lend	s32i	a4, a1, PT_LBEG	s32i	a3, a1, PT_LEND#endif 	/* Save SCOMPARE1 */ #if XCHAL_HAVE_S32C1I	rsr     a3, scompare1	s32i    a3, a1, PT_SCOMPARE1#endif 	/* Save optional registers. */ 	save_xtregs_opt a1 a3 a4 a5 a6 a7 PT_XTREGS_OPT	#ifdef CONFIG_TRACE_IRQFLAGS	rsr		abi_tmp0, ps	extui		abi_tmp0, abi_tmp0, PS_INTLEVEL_SHIFT, PS_INTLEVEL_WIDTH	beqz		abi_tmp0, 1f	abi_call	trace_hardirqs_off1:#endif#ifdef CONFIG_CONTEXT_TRACKING_USER	l32i		abi_tmp0, a1, PT_PS	bbci.l		abi_tmp0, PS_UM_BIT, 1f	abi_call	user_exit_callable1:#endif 	/* Go to second-level dispatcher. Set up parameters to pass to the	 * exception handler and call the exception handler.	 */ 	l32i		abi_arg1, a1, PT_EXCCAUSE	# pass EXCCAUSE	rsr		abi_tmp0, excsave1	addx4		abi_tmp0, abi_arg1, abi_tmp0	l32i		abi_tmp0, abi_tmp0, EXC_TABLE_DEFAULT	# load handler	mov		abi_arg0, a1			# pass stack frame 	/* Call the second-level handler */ 	abi_callx	abi_tmp0 	/* Jump here for exception exit */	.global common_exception_returncommon_exception_return: #if XTENSA_FAKE_NMI	l32i		abi_tmp0, a1, PT_EXCCAUSE	movi		abi_tmp1, EXCCAUSE_MAPPED_NMI	l32i		abi_saved1, a1, PT_PS	beq		abi_tmp0, abi_tmp1, .Lrestore_state#endif.Ltif_loop:	irq_save	abi_tmp0, abi_tmp1#ifdef CONFIG_TRACE_IRQFLAGS	abi_call	trace_hardirqs_off#endif 	/* Jump if we are returning from kernel exceptions. */ 	l32i		abi_saved1, a1, PT_PS	GET_THREAD_INFO(abi_tmp0, a1)	l32i		abi_saved0, abi_tmp0, TI_FLAGS	_bbci.l		abi_saved1, PS_UM_BIT, .Lexit_tif_loop_kernel

Lines 1–500 of 2264.