xref: /openbmc/linux/arch/xtensa/kernel/align.S (revision 0013a85454c281faaf064ccb576e373a2881aac8)
15a0015d6SChris Zankel/*
25a0015d6SChris Zankel * arch/xtensa/kernel/align.S
35a0015d6SChris Zankel *
45a0015d6SChris Zankel * Handle unalignment exceptions in kernel space.
55a0015d6SChris Zankel *
65a0015d6SChris Zankel * This file is subject to the terms and conditions of the GNU General
75a0015d6SChris Zankel * Public License.  See the file "COPYING" in the main directory of
85a0015d6SChris Zankel * this archive for more details.
95a0015d6SChris Zankel *
105a0015d6SChris Zankel * Copyright (C) 2001 - 2005 Tensilica, Inc.
115a0015d6SChris Zankel *
125a0015d6SChris Zankel * Rewritten by Chris Zankel <chris@zankel.net>
135a0015d6SChris Zankel *
145a0015d6SChris Zankel * Based on work from Joe Taylor <joe@tensilica.com, joetylr@yahoo.com>
155a0015d6SChris Zankel * and Marc Gauthier <marc@tensilica.com, marc@alimni.uwaterloo.ca>
165a0015d6SChris Zankel */
175a0015d6SChris Zankel
185a0015d6SChris Zankel#include <linux/linkage.h>
195a0015d6SChris Zankel#include <asm/ptrace.h>
205a0015d6SChris Zankel#include <asm/ptrace.h>
215a0015d6SChris Zankel#include <asm/current.h>
22*0013a854SSam Ravnborg#include <asm/asm-offsets.h>
235a0015d6SChris Zankel#include <asm/pgtable.h>
245a0015d6SChris Zankel#include <asm/processor.h>
255a0015d6SChris Zankel#include <asm/page.h>
265a0015d6SChris Zankel#include <asm/thread_info.h>
275a0015d6SChris Zankel
285a0015d6SChris Zankel#if XCHAL_UNALIGNED_LOAD_EXCEPTION || XCHAL_UNALIGNED_STORE_EXCEPTION
295a0015d6SChris Zankel
305a0015d6SChris Zankel/*  First-level exception handler for unaligned exceptions.
315a0015d6SChris Zankel *
325a0015d6SChris Zankel *  Note: This handler works only for kernel exceptions.  Unaligned user
335a0015d6SChris Zankel *        access should get a seg fault.
345a0015d6SChris Zankel */
355a0015d6SChris Zankel
365a0015d6SChris Zankel/* Big and little endian 16-bit values are located in
375a0015d6SChris Zankel * different halves of a register.  HWORD_START helps to
385a0015d6SChris Zankel * abstract the notion of extracting a 16-bit value from a
395a0015d6SChris Zankel * register.
405a0015d6SChris Zankel * We also have to define new shifting instructions because
415a0015d6SChris Zankel * lsb and msb are on 'opposite' ends in a register for
425a0015d6SChris Zankel * different endian machines.
435a0015d6SChris Zankel *
445a0015d6SChris Zankel * Assume a memory region in ascending address:
455a0015d6SChris Zankel *   	0 1 2 3|4 5 6 7
465a0015d6SChris Zankel *
475a0015d6SChris Zankel * When loading one word into a register, the content of that register is:
485a0015d6SChris Zankel *  LE	3 2 1 0, 7 6 5 4
495a0015d6SChris Zankel *  BE  0 1 2 3, 4 5 6 7
505a0015d6SChris Zankel *
515a0015d6SChris Zankel * Masking the bits of the higher/lower address means:
525a0015d6SChris Zankel *  LE  X X 0 0, 0 0 X X
535a0015d6SChris Zankel *  BE	0 0 X X, X X 0 0
545a0015d6SChris Zankel *
555a0015d6SChris Zankel * Shifting to higher/lower addresses, means:
565a0015d6SChris Zankel *  LE  shift left / shift right
575a0015d6SChris Zankel *  BE  shift right / shift left
585a0015d6SChris Zankel *
595a0015d6SChris Zankel * Extracting 16 bits from a 32 bit reg. value to higher/lower address means:
605a0015d6SChris Zankel *  LE  mask 0 0 X X / shift left
615a0015d6SChris Zankel *  BE  shift left / mask 0 0 X X
625a0015d6SChris Zankel */
635a0015d6SChris Zankel
645a0015d6SChris Zankel#define UNALIGNED_USER_EXCEPTION
655a0015d6SChris Zankel
665a0015d6SChris Zankel#if XCHAL_HAVE_BE
675a0015d6SChris Zankel
685a0015d6SChris Zankel#define HWORD_START	16
695a0015d6SChris Zankel#define	INSN_OP0	28
705a0015d6SChris Zankel#define	INSN_T		24
715a0015d6SChris Zankel#define	INSN_OP1	16
725a0015d6SChris Zankel
735a0015d6SChris Zankel.macro __src_b	r, w0, w1;	src	\r, \w0, \w1;	.endm
745a0015d6SChris Zankel.macro __ssa8	r;		ssa8b	\r;		.endm
755a0015d6SChris Zankel.macro __ssa8r	r;		ssa8l	\r;		.endm
765a0015d6SChris Zankel.macro __sh	r, s;		srl	\r, \s;		.endm
775a0015d6SChris Zankel.macro __sl	r, s;		sll	\r, \s;		.endm
785a0015d6SChris Zankel.macro __exth	r, s;		extui	\r, \s, 0, 16;	.endm
795a0015d6SChris Zankel.macro __extl	r, s;		slli	\r, \s, 16;	.endm
805a0015d6SChris Zankel
815a0015d6SChris Zankel#else
825a0015d6SChris Zankel
835a0015d6SChris Zankel#define HWORD_START	0
845a0015d6SChris Zankel#define	INSN_OP0	0
855a0015d6SChris Zankel#define	INSN_T		4
865a0015d6SChris Zankel#define	INSN_OP1	12
875a0015d6SChris Zankel
885a0015d6SChris Zankel.macro __src_b	r, w0, w1;	src	\r, \w1, \w0;	.endm
895a0015d6SChris Zankel.macro __ssa8	r;		ssa8l	\r;		.endm
905a0015d6SChris Zankel.macro __ssa8r	r;		ssa8b	\r;		.endm
915a0015d6SChris Zankel.macro __sh	r, s;		sll	\r, \s;		.endm
925a0015d6SChris Zankel.macro __sl	r, s;		srl	\r, \s;		.endm
935a0015d6SChris Zankel.macro __exth	r, s;		slli	\r, \s, 16;	.endm
945a0015d6SChris Zankel.macro __extl	r, s;		extui	\r, \s, 0, 16;	.endm
955a0015d6SChris Zankel
965a0015d6SChris Zankel#endif
975a0015d6SChris Zankel
985a0015d6SChris Zankel/*
995a0015d6SChris Zankel *	xxxx xxxx = imm8 field
1005a0015d6SChris Zankel *	     yyyy = imm4 field
1015a0015d6SChris Zankel *	     ssss = s field
1025a0015d6SChris Zankel *	     tttt = t field
1035a0015d6SChris Zankel *
1045a0015d6SChris Zankel *	       		 16		    0
1055a0015d6SChris Zankel *		          -------------------
1065a0015d6SChris Zankel *	L32I.N		  yyyy ssss tttt 1000
1075a0015d6SChris Zankel *	S32I.N	          yyyy ssss tttt 1001
1085a0015d6SChris Zankel *
1095a0015d6SChris Zankel *	       23			    0
1105a0015d6SChris Zankel *		-----------------------------
1115a0015d6SChris Zankel *	res	          0000           0010
1125a0015d6SChris Zankel *	L16UI	xxxx xxxx 0001 ssss tttt 0010
1135a0015d6SChris Zankel *	L32I	xxxx xxxx 0010 ssss tttt 0010
1145a0015d6SChris Zankel *	XXX	          0011 ssss tttt 0010
1155a0015d6SChris Zankel *	XXX	          0100 ssss tttt 0010
1165a0015d6SChris Zankel *	S16I	xxxx xxxx 0101 ssss tttt 0010
1175a0015d6SChris Zankel *	S32I	xxxx xxxx 0110 ssss tttt 0010
1185a0015d6SChris Zankel *	XXX	          0111 ssss tttt 0010
1195a0015d6SChris Zankel *	XXX	          1000 ssss tttt 0010
1205a0015d6SChris Zankel *	L16SI	xxxx xxxx 1001 ssss tttt 0010
1215a0015d6SChris Zankel *	XXX	          1010           0010
1225a0015d6SChris Zankel *      **L32AI	xxxx xxxx 1011 ssss tttt 0010 unsupported
1235a0015d6SChris Zankel *	XXX	          1100           0010
1245a0015d6SChris Zankel *	XXX	          1101           0010
1255a0015d6SChris Zankel *	XXX	          1110           0010
1265a0015d6SChris Zankel *	**S32RI	xxxx xxxx 1111 ssss tttt 0010 unsupported
1275a0015d6SChris Zankel *		-----------------------------
1285a0015d6SChris Zankel *                           ^         ^    ^
1295a0015d6SChris Zankel *    sub-opcode (NIBBLE_R) -+         |    |
1305a0015d6SChris Zankel *       t field (NIBBLE_T) -----------+    |
1315a0015d6SChris Zankel *  major opcode (NIBBLE_OP0) --------------+
1325a0015d6SChris Zankel */
1335a0015d6SChris Zankel
1345a0015d6SChris Zankel#define OP0_L32I_N	0x8		/* load immediate narrow */
1355a0015d6SChris Zankel#define OP0_S32I_N	0x9		/* store immediate narrow */
1365a0015d6SChris Zankel#define OP1_SI_MASK	0x4		/* OP1 bit set for stores */
1375a0015d6SChris Zankel#define OP1_SI_BIT	2		/* OP1 bit number for stores */
1385a0015d6SChris Zankel
1395a0015d6SChris Zankel#define OP1_L32I	0x2
1405a0015d6SChris Zankel#define OP1_L16UI	0x1
1415a0015d6SChris Zankel#define OP1_L16SI	0x9
1425a0015d6SChris Zankel#define OP1_L32AI	0xb
1435a0015d6SChris Zankel
1445a0015d6SChris Zankel#define OP1_S32I	0x6
1455a0015d6SChris Zankel#define OP1_S16I	0x5
1465a0015d6SChris Zankel#define OP1_S32RI	0xf
1475a0015d6SChris Zankel
1485a0015d6SChris Zankel/*
1495a0015d6SChris Zankel * Entry condition:
1505a0015d6SChris Zankel *
1515a0015d6SChris Zankel *   a0:	trashed, original value saved on stack (PT_AREG0)
1525a0015d6SChris Zankel *   a1:	a1
1535a0015d6SChris Zankel *   a2:	new stack pointer, original in DEPC
1545a0015d6SChris Zankel *   a3:	dispatch table
1555a0015d6SChris Zankel *   depc:	a2, original value saved on stack (PT_DEPC)
1565a0015d6SChris Zankel *   excsave_1:	a3
1575a0015d6SChris Zankel *
1585a0015d6SChris Zankel *   PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception, DEPC
1595a0015d6SChris Zankel *	     <  VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception
1605a0015d6SChris Zankel */
1615a0015d6SChris Zankel
1625a0015d6SChris Zankel
1635a0015d6SChris ZankelENTRY(fast_unaligned)
1645a0015d6SChris Zankel
1655a0015d6SChris Zankel	/* Note: We don't expect the address to be aligned on a word
1665a0015d6SChris Zankel	 *       boundary. After all, the processor generated that exception
1675a0015d6SChris Zankel	 *       and it would be a hardware fault.
1685a0015d6SChris Zankel	 */
1695a0015d6SChris Zankel
1705a0015d6SChris Zankel	/* Save some working register */
1715a0015d6SChris Zankel
1725a0015d6SChris Zankel	s32i	a4, a2, PT_AREG4
1735a0015d6SChris Zankel	s32i	a5, a2, PT_AREG5
1745a0015d6SChris Zankel	s32i	a6, a2, PT_AREG6
1755a0015d6SChris Zankel	s32i	a7, a2, PT_AREG7
1765a0015d6SChris Zankel	s32i	a8, a2, PT_AREG8
1775a0015d6SChris Zankel
1785a0015d6SChris Zankel	rsr	a0, DEPC
1795a0015d6SChris Zankel	xsr	a3, EXCSAVE_1
1805a0015d6SChris Zankel	s32i	a0, a2, PT_AREG2
1815a0015d6SChris Zankel	s32i	a3, a2, PT_AREG3
1825a0015d6SChris Zankel
1835a0015d6SChris Zankel	/* Keep value of SAR in a0 */
1845a0015d6SChris Zankel
1855a0015d6SChris Zankel	rsr	a0, SAR
1865a0015d6SChris Zankel	rsr	a8, EXCVADDR		# load unaligned memory address
1875a0015d6SChris Zankel
1885a0015d6SChris Zankel	/* Now, identify one of the following load/store instructions.
1895a0015d6SChris Zankel	 *
1905a0015d6SChris Zankel	 * The only possible danger of a double exception on the
1915a0015d6SChris Zankel	 * following l32i instructions is kernel code in vmalloc
1925a0015d6SChris Zankel	 * memory. The processor was just executing at the EPC_1
1935a0015d6SChris Zankel	 * address, and indeed, already fetched the instruction.  That
1945a0015d6SChris Zankel	 * guarantees a TLB mapping, which hasn't been replaced by
1955a0015d6SChris Zankel	 * this unaligned exception handler that uses only static TLB
1965a0015d6SChris Zankel	 * mappings. However, high-level interrupt handlers might
1975a0015d6SChris Zankel	 * modify TLB entries, so for the generic case, we register a
1985a0015d6SChris Zankel	 * TABLE_FIXUP handler here, too.
1995a0015d6SChris Zankel	 */
2005a0015d6SChris Zankel
2015a0015d6SChris Zankel	/* a3...a6 saved on stack, a2 = SP */
2025a0015d6SChris Zankel
2035a0015d6SChris Zankel	/* Extract the instruction that caused the unaligned access. */
2045a0015d6SChris Zankel
2055a0015d6SChris Zankel	rsr	a7, EPC_1	# load exception address
2065a0015d6SChris Zankel	movi	a3, ~3
2075a0015d6SChris Zankel	and	a3, a3, a7	# mask lower bits
2085a0015d6SChris Zankel
2095a0015d6SChris Zankel	l32i	a4, a3, 0	# load 2 words
2105a0015d6SChris Zankel	l32i	a5, a3, 4
2115a0015d6SChris Zankel
2125a0015d6SChris Zankel	__ssa8	a7
2135a0015d6SChris Zankel	__src_b	a4, a4, a5	# a4 has the instruction
2145a0015d6SChris Zankel
2155a0015d6SChris Zankel	/* Analyze the instruction (load or store?). */
2165a0015d6SChris Zankel
2175a0015d6SChris Zankel	extui	a5, a4, INSN_OP0, 4	# get insn.op0 nibble
2185a0015d6SChris Zankel
2195a0015d6SChris Zankel#if XCHAL_HAVE_NARROW
2205a0015d6SChris Zankel	_beqi	a5, OP0_L32I_N, .Lload	# L32I.N, jump
2215a0015d6SChris Zankel	addi	a6, a5, -OP0_S32I_N
2225a0015d6SChris Zankel	_beqz	a6, .Lstore		# S32I.N, do a store
2235a0015d6SChris Zankel#endif
2245a0015d6SChris Zankel	/* 'store indicator bit' not set, jump */
2255a0015d6SChris Zankel	_bbci.l	a4, OP1_SI_BIT + INSN_OP1, .Lload
2265a0015d6SChris Zankel
2275a0015d6SChris Zankel	/* Store: Jump to table entry to get the value in the source register.*/
2285a0015d6SChris Zankel
2295a0015d6SChris Zankel.Lstore:movi	a5, .Lstore_table	# table
2305a0015d6SChris Zankel	extui	a6, a4, INSN_T, 4	# get source register
2315a0015d6SChris Zankel	addx8	a5, a6, a5
2325a0015d6SChris Zankel	jx	a5			# jump into table
2335a0015d6SChris Zankel
2345a0015d6SChris Zankel	/* Invalid instruction, CRITICAL! */
2355a0015d6SChris Zankel.Linvalid_instruction_load:
2365a0015d6SChris Zankel	j	.Linvalid_instruction
2375a0015d6SChris Zankel
2385a0015d6SChris Zankel	/* Load: Load memory address. */
2395a0015d6SChris Zankel
2405a0015d6SChris Zankel.Lload: movi	a3, ~3
2415a0015d6SChris Zankel	and	a3, a3, a8		# align memory address
2425a0015d6SChris Zankel
2435a0015d6SChris Zankel	__ssa8	a8
2445a0015d6SChris Zankel#ifdef UNALIGNED_USER_EXCEPTION
2455a0015d6SChris Zankel	addi	a3, a3, 8
2465a0015d6SChris Zankel	l32e	a5, a3, -8
2475a0015d6SChris Zankel	l32e	a6, a3, -4
2485a0015d6SChris Zankel#else
2495a0015d6SChris Zankel	l32i	a5, a3, 0
2505a0015d6SChris Zankel	l32i	a6, a3, 4
2515a0015d6SChris Zankel#endif
2525a0015d6SChris Zankel	__src_b	a3, a5, a6		# a3 has the data word
2535a0015d6SChris Zankel
2545a0015d6SChris Zankel#if XCHAL_HAVE_NARROW
2555a0015d6SChris Zankel	addi	a7, a7, 2		# increment PC (assume 16-bit insn)
2565a0015d6SChris Zankel
2575a0015d6SChris Zankel	extui	a5, a4, INSN_OP0, 4
2585a0015d6SChris Zankel	_beqi	a5, OP0_L32I_N, 1f	# l32i.n: jump
2595a0015d6SChris Zankel
2605a0015d6SChris Zankel	addi	a7, a7, 1
2615a0015d6SChris Zankel#else
2625a0015d6SChris Zankel	addi	a7, a7, 3
2635a0015d6SChris Zankel#endif
2645a0015d6SChris Zankel
2655a0015d6SChris Zankel	extui	a5, a4, INSN_OP1, 4
2665a0015d6SChris Zankel	_beqi	a5, OP1_L32I, 1f	# l32i: jump
2675a0015d6SChris Zankel
2685a0015d6SChris Zankel	extui	a3, a3, 0, 16		# extract lower 16 bits
2695a0015d6SChris Zankel	_beqi	a5, OP1_L16UI, 1f
2705a0015d6SChris Zankel	addi	a5, a5, -OP1_L16SI
2715a0015d6SChris Zankel	_bnez	a5, .Linvalid_instruction_load
2725a0015d6SChris Zankel
2735a0015d6SChris Zankel	/* sign extend value */
2745a0015d6SChris Zankel
2755a0015d6SChris Zankel	slli	a3, a3, 16
2765a0015d6SChris Zankel	srai	a3, a3, 16
2775a0015d6SChris Zankel
2785a0015d6SChris Zankel	/* Set target register. */
2795a0015d6SChris Zankel
2805a0015d6SChris Zankel1:
2815a0015d6SChris Zankel
2825a0015d6SChris Zankel#if XCHAL_HAVE_LOOP
2835a0015d6SChris Zankel	rsr	a3, LEND		# check if we reached LEND
2845a0015d6SChris Zankel	bne	a7, a3, 1f
2855a0015d6SChris Zankel	rsr	a3, LCOUNT		# and LCOUNT != 0
2865a0015d6SChris Zankel	beqz	a3, 1f
2875a0015d6SChris Zankel	addi	a3, a3, -1		# decrement LCOUNT and set
2885a0015d6SChris Zankel	rsr	a7, LBEG		# set PC to LBEGIN
2895a0015d6SChris Zankel	wsr	a3, LCOUNT
2905a0015d6SChris Zankel#endif
2915a0015d6SChris Zankel
2925a0015d6SChris Zankel1:	wsr	a7, EPC_1		# skip load instruction
2935a0015d6SChris Zankel	extui	a4, a4, INSN_T, 4	# extract target register
2945a0015d6SChris Zankel	movi	a5, .Lload_table
2955a0015d6SChris Zankel	addx8	a4, a4, a5
2965a0015d6SChris Zankel	jx	a4			# jump to entry for target register
2975a0015d6SChris Zankel
2985a0015d6SChris Zankel	.align	8
2995a0015d6SChris Zankel.Lload_table:
3005a0015d6SChris Zankel	s32i	a3, a2, PT_AREG0;	_j .Lexit;	.align 8
3015a0015d6SChris Zankel	mov	a1, a3;			_j .Lexit;	.align 8 # fishy??
3025a0015d6SChris Zankel	s32i	a3, a2, PT_AREG2;	_j .Lexit;	.align 8
3035a0015d6SChris Zankel	s32i	a3, a2, PT_AREG3;	_j .Lexit;	.align 8
3045a0015d6SChris Zankel	s32i	a3, a2, PT_AREG4;	_j .Lexit;	.align 8
3055a0015d6SChris Zankel	s32i	a3, a2, PT_AREG5;	_j .Lexit;	.align 8
3065a0015d6SChris Zankel	s32i	a3, a2, PT_AREG6;	_j .Lexit;	.align 8
3075a0015d6SChris Zankel	s32i	a3, a2, PT_AREG7;	_j .Lexit;	.align 8
3085a0015d6SChris Zankel	s32i	a3, a2, PT_AREG8;	_j .Lexit;	.align 8
3095a0015d6SChris Zankel	mov	a9, a3		;	_j .Lexit;	.align 8
3105a0015d6SChris Zankel	mov	a10, a3		;	_j .Lexit;	.align 8
3115a0015d6SChris Zankel	mov	a11, a3		;	_j .Lexit;	.align 8
3125a0015d6SChris Zankel	mov	a12, a3		;	_j .Lexit;	.align 8
3135a0015d6SChris Zankel	mov	a13, a3		;	_j .Lexit;	.align 8
3145a0015d6SChris Zankel	mov	a14, a3		;	_j .Lexit;	.align 8
3155a0015d6SChris Zankel	mov	a15, a3		;	_j .Lexit;	.align 8
3165a0015d6SChris Zankel
3175a0015d6SChris Zankel.Lstore_table:
3185a0015d6SChris Zankel	l32i	a3, a2, PT_AREG0;	_j 1f;	.align 8
3195a0015d6SChris Zankel	mov	a3, a1;			_j 1f;	.align 8	# fishy??
3205a0015d6SChris Zankel	l32i	a3, a2, PT_AREG2;	_j 1f;	.align 8
3215a0015d6SChris Zankel	l32i	a3, a2, PT_AREG3;	_j 1f;	.align 8
3225a0015d6SChris Zankel	l32i	a3, a2, PT_AREG4;	_j 1f;	.align 8
3235a0015d6SChris Zankel	l32i	a3, a2, PT_AREG5;	_j 1f;	.align 8
3245a0015d6SChris Zankel	l32i	a3, a2, PT_AREG6;	_j 1f;	.align 8
3255a0015d6SChris Zankel	l32i	a3, a2, PT_AREG7;	_j 1f;	.align 8
3265a0015d6SChris Zankel	l32i	a3, a2, PT_AREG8;	_j 1f;	.align 8
3275a0015d6SChris Zankel	mov	a3, a9		;	_j 1f;	.align 8
3285a0015d6SChris Zankel	mov	a3, a10		;	_j 1f;	.align 8
3295a0015d6SChris Zankel	mov	a3, a11		;	_j 1f;	.align 8
3305a0015d6SChris Zankel	mov	a3, a12		;	_j 1f;	.align 8
3315a0015d6SChris Zankel	mov	a3, a13		;	_j 1f;	.align 8
3325a0015d6SChris Zankel	mov	a3, a14		;	_j 1f;	.align 8
3335a0015d6SChris Zankel	mov	a3, a15		;	_j 1f;	.align 8
3345a0015d6SChris Zankel
3355a0015d6SChris Zankel1: 	# a7: instruction pointer, a4: instruction, a3: value
3365a0015d6SChris Zankel
3375a0015d6SChris Zankel	movi	a6, 0			# mask: ffffffff:00000000
3385a0015d6SChris Zankel
3395a0015d6SChris Zankel#if XCHAL_HAVE_NARROW
3405a0015d6SChris Zankel	addi	a7, a7, 2		# incr. PC,assume 16-bit instruction
3415a0015d6SChris Zankel
3425a0015d6SChris Zankel	extui	a5, a4, INSN_OP0, 4	# extract OP0
3435a0015d6SChris Zankel	addi	a5, a5, -OP0_S32I_N
3445a0015d6SChris Zankel	_beqz	a5, 1f			# s32i.n: jump
3455a0015d6SChris Zankel
3465a0015d6SChris Zankel	addi	a7, a7, 1		# increment PC, 32-bit instruction
3475a0015d6SChris Zankel#else
3485a0015d6SChris Zankel	addi	a7, a7, 3		# increment PC, 32-bit instruction
3495a0015d6SChris Zankel#endif
3505a0015d6SChris Zankel
3515a0015d6SChris Zankel	extui	a5, a4, INSN_OP1, 4	# extract OP1
3525a0015d6SChris Zankel	_beqi	a5, OP1_S32I, 1f	# jump if 32 bit store
3535a0015d6SChris Zankel	_bnei	a5, OP1_S16I, .Linvalid_instruction_store
3545a0015d6SChris Zankel
3555a0015d6SChris Zankel	movi	a5, -1
3565a0015d6SChris Zankel	__extl	a3, a3			# get 16-bit value
3575a0015d6SChris Zankel	__exth	a6, a5			# get 16-bit mask ffffffff:ffff0000
3585a0015d6SChris Zankel
3595a0015d6SChris Zankel	/* Get memory address */
3605a0015d6SChris Zankel
3615a0015d6SChris Zankel1:
3625a0015d6SChris Zankel#if XCHAL_HAVE_LOOP
3635a0015d6SChris Zankel	rsr	a3, LEND		# check if we reached LEND
3645a0015d6SChris Zankel	bne	a7, a3, 1f
3655a0015d6SChris Zankel	rsr	a3, LCOUNT		# and LCOUNT != 0
3665a0015d6SChris Zankel	beqz	a3, 1f
3675a0015d6SChris Zankel	addi	a3, a3, -1		# decrement LCOUNT and set
3685a0015d6SChris Zankel	rsr	a7, LBEG		# set PC to LBEGIN
3695a0015d6SChris Zankel	wsr	a3, LCOUNT
3705a0015d6SChris Zankel#endif
3715a0015d6SChris Zankel
3725a0015d6SChris Zankel1:	wsr	a7, EPC_1		# skip store instruction
3735a0015d6SChris Zankel	movi	a4, ~3
3745a0015d6SChris Zankel	and	a4, a4, a8		# align memory address
3755a0015d6SChris Zankel
3765a0015d6SChris Zankel	/* Insert value into memory */
3775a0015d6SChris Zankel
3785a0015d6SChris Zankel	movi	a5, -1			# mask: ffffffff:XXXX0000
3795a0015d6SChris Zankel#ifdef UNALIGNED_USER_EXCEPTION
3805a0015d6SChris Zankel	addi	a4, a4, 8
3815a0015d6SChris Zankel#endif
3825a0015d6SChris Zankel
3835a0015d6SChris Zankel	__ssa8r a8
3845a0015d6SChris Zankel	__src_b	a7, a5, a6		# lo-mask  F..F0..0 (BE) 0..0F..F (LE)
3855a0015d6SChris Zankel	__src_b	a6, a6, a5		# hi-mask  0..0F..F (BE) F..F0..0 (LE)
3865a0015d6SChris Zankel#ifdef UNALIGNED_USER_EXCEPTION
3875a0015d6SChris Zankel	l32e	a5, a4, -8
3885a0015d6SChris Zankel#else
3895a0015d6SChris Zankel	l32i	a5, a4, 0		# load lower address word
3905a0015d6SChris Zankel#endif
3915a0015d6SChris Zankel	and	a5, a5, a7		# mask
3925a0015d6SChris Zankel	__sh	a7, a3 			# shift value
3935a0015d6SChris Zankel	or	a5, a5, a7		# or with original value
3945a0015d6SChris Zankel#ifdef UNALIGNED_USER_EXCEPTION
3955a0015d6SChris Zankel	s32e	a5, a4, -8
3965a0015d6SChris Zankel	l32e	a7, a4, -4
3975a0015d6SChris Zankel#else
3985a0015d6SChris Zankel	s32i	a5, a4, 0		# store
3995a0015d6SChris Zankel	l32i	a7, a4, 4		# same for upper address word
4005a0015d6SChris Zankel#endif
4015a0015d6SChris Zankel	__sl	a5, a3
4025a0015d6SChris Zankel	and	a6, a7, a6
4035a0015d6SChris Zankel	or	a6, a6, a5
4045a0015d6SChris Zankel#ifdef UNALIGNED_USER_EXCEPTION
4055a0015d6SChris Zankel	s32e	a6, a4, -4
4065a0015d6SChris Zankel#else
4075a0015d6SChris Zankel	s32i	a6, a4, 4
4085a0015d6SChris Zankel#endif
4095a0015d6SChris Zankel
4105a0015d6SChris Zankel	/* Done. restore stack and return */
4115a0015d6SChris Zankel
4125a0015d6SChris Zankel.Lexit:
4135a0015d6SChris Zankel	movi	a4, 0
4145a0015d6SChris Zankel	rsr	a3, EXCSAVE_1
4155a0015d6SChris Zankel	s32i	a4, a3, EXC_TABLE_FIXUP
4165a0015d6SChris Zankel
4175a0015d6SChris Zankel	/* Restore working register */
4185a0015d6SChris Zankel
4195a0015d6SChris Zankel	l32i	a7, a2, PT_AREG7
4205a0015d6SChris Zankel	l32i	a6, a2, PT_AREG6
4215a0015d6SChris Zankel	l32i	a5, a2, PT_AREG5
4225a0015d6SChris Zankel	l32i	a4, a2, PT_AREG4
4235a0015d6SChris Zankel	l32i	a3, a2, PT_AREG3
4245a0015d6SChris Zankel
4255a0015d6SChris Zankel	/* restore SAR and return */
4265a0015d6SChris Zankel
4275a0015d6SChris Zankel	wsr	a0, SAR
4285a0015d6SChris Zankel	l32i	a0, a2, PT_AREG0
4295a0015d6SChris Zankel	l32i	a2, a2, PT_AREG2
4305a0015d6SChris Zankel	rfe
4315a0015d6SChris Zankel
4325a0015d6SChris Zankel	/* We cannot handle this exception. */
4335a0015d6SChris Zankel
4345a0015d6SChris Zankel	.extern _kernel_exception
4355a0015d6SChris Zankel.Linvalid_instruction_store:
4365a0015d6SChris Zankel.Linvalid_instruction:
4375a0015d6SChris Zankel
4385a0015d6SChris Zankel	/* Restore a4...a8 and SAR, set SP, and jump to default exception. */
4395a0015d6SChris Zankel
4405a0015d6SChris Zankel	l32i	a8, a2, PT_AREG8
4415a0015d6SChris Zankel	l32i	a7, a2, PT_AREG7
4425a0015d6SChris Zankel	l32i	a6, a2, PT_AREG6
4435a0015d6SChris Zankel	l32i	a5, a2, PT_AREG5
4445a0015d6SChris Zankel	l32i	a4, a2, PT_AREG4
4455a0015d6SChris Zankel	wsr	a0, SAR
4465a0015d6SChris Zankel	mov	a1, a2
4475a0015d6SChris Zankel
4485a0015d6SChris Zankel	rsr	a0, PS
4495a0015d6SChris Zankel        bbsi.l  a2, PS_UM_SHIFT, 1f     # jump if user mode
4505a0015d6SChris Zankel
4515a0015d6SChris Zankel	movi	a0, _kernel_exception
4525a0015d6SChris Zankel	jx	a0
4535a0015d6SChris Zankel
4545a0015d6SChris Zankel1:	movi	a0, _user_exception
4555a0015d6SChris Zankel	jx	a0
4565a0015d6SChris Zankel
4575a0015d6SChris Zankel
4585a0015d6SChris Zankel#endif /* XCHAL_UNALIGNED_LOAD_EXCEPTION || XCHAL_UNALIGNED_STORE_EXCEPTION */
4595a0015d6SChris Zankel
460