/*1* arch/xtensa/kernel/align.S2*3* Handle unalignment and load/store exceptions.4*5* This file is subject to the terms and conditions of the GNU General6* Public License. See the file "COPYING" in the main directory of7* this archive for more details.8*9* Copyright (C) 2001 - 2005 Tensilica, Inc.10* Copyright (C) 2014 Cadence Design Systems Inc.11*12* Rewritten by Chris Zankel <[email protected]>13*14* Based on work from Joe Taylor <[email protected], [email protected]>15* and Marc Gauthier <[email protected], [email protected]>16*/1718#include <linux/linkage.h>19#include <asm/current.h>20#include <asm/asm-offsets.h>21#include <asm/asmmacro.h>22#include <asm/processor.h>2324#if XCHAL_UNALIGNED_LOAD_EXCEPTION || defined CONFIG_XTENSA_LOAD_STORE25#define LOAD_EXCEPTION_HANDLER26#endif2728#if XCHAL_UNALIGNED_STORE_EXCEPTION || defined CONFIG_XTENSA_LOAD_STORE29#define STORE_EXCEPTION_HANDLER30#endif3132#if defined LOAD_EXCEPTION_HANDLER || defined STORE_EXCEPTION_HANDLER33#define ANY_EXCEPTION_HANDLER34#endif3536#if XCHAL_HAVE_WINDOWED && defined CONFIG_MMU37#define UNALIGNED_USER_EXCEPTION38#endif3940/* Big and little endian 16-bit values are located in41* different halves of a register. HWORD_START helps to42* abstract the notion of extracting a 16-bit value from a43* register.44* We also have to define new shifting instructions because45* lsb and msb are on 'opposite' ends in a register for46* different endian machines.47*48* Assume a memory region in ascending address:49* 0 1 2 3|4 5 6 750*51* When loading one word into a register, the content of that register is:52* LE 3 2 1 0, 7 6 5 453* BE 0 1 2 3, 4 5 6 754*55* Masking the bits of the higher/lower address means:56* LE X X 0 0, 0 0 X X57* BE 0 0 X X, X X 0 058*59* Shifting to higher/lower addresses, means:60* LE shift left / shift right61* BE shift right / shift left62*63* Extracting 16 bits from a 32 bit reg. value to higher/lower address means:64* LE mask 0 0 X X / shift left65* BE shift left / mask 0 0 X X66*/6768#if XCHAL_HAVE_BE6970#define HWORD_START 1671#define INSN_OP0 2872#define INSN_T 2473#define INSN_OP1 167475.macro __ssa8r r; ssa8l \r; .endm76.macro __sh r, s; srl \r, \s; .endm77.macro __sl r, s; sll \r, \s; .endm78.macro __exth r, s; extui \r, \s, 0, 16; .endm79.macro __extl r, s; slli \r, \s, 16; .endm8081#else8283#define HWORD_START 084#define INSN_OP0 085#define INSN_T 486#define INSN_OP1 128788.macro __ssa8r r; ssa8b \r; .endm89.macro __sh r, s; sll \r, \s; .endm90.macro __sl r, s; srl \r, \s; .endm91.macro __exth r, s; slli \r, \s, 16; .endm92.macro __extl r, s; extui \r, \s, 0, 16; .endm9394#endif9596/*97* xxxx xxxx = imm8 field98* yyyy = imm4 field99* ssss = s field100* tttt = t field101*102* 16 0103* -------------------104* L32I.N yyyy ssss tttt 1000105* S32I.N yyyy ssss tttt 1001106*107* 23 0108* -----------------------------109* L8UI xxxx xxxx 0000 ssss tttt 0010110* L16UI xxxx xxxx 0001 ssss tttt 0010111* L32I xxxx xxxx 0010 ssss tttt 0010112* XXX 0011 ssss tttt 0010113* XXX 0100 ssss tttt 0010114* S16I xxxx xxxx 0101 ssss tttt 0010115* S32I xxxx xxxx 0110 ssss tttt 0010116* XXX 0111 ssss tttt 0010117* XXX 1000 ssss tttt 0010118* L16SI xxxx xxxx 1001 ssss tttt 0010119* XXX 1010 0010120* **L32AI xxxx xxxx 1011 ssss tttt 0010 unsupported121* XXX 1100 0010122* XXX 1101 0010123* XXX 1110 0010124* **S32RI xxxx xxxx 1111 ssss tttt 0010 unsupported125* -----------------------------126* ^ ^ ^127* sub-opcode (NIBBLE_R) -+ | |128* t field (NIBBLE_T) -----------+ |129* major opcode (NIBBLE_OP0) --------------+130*/131132#define OP0_L32I_N 0x8 /* load immediate narrow */133#define OP0_S32I_N 0x9 /* store immediate narrow */134#define OP0_LSAI 0x2 /* load/store */135#define OP1_SI_MASK 0x4 /* OP1 bit set for stores */136#define OP1_SI_BIT 2 /* OP1 bit number for stores */137138#define OP1_L8UI 0x0139#define OP1_L32I 0x2140#define OP1_L16UI 0x1141#define OP1_L16SI 0x9142#define OP1_L32AI 0xb143144#define OP1_S32I 0x6145#define OP1_S16I 0x5146#define OP1_S32RI 0xf147148/*149* Entry condition:150*151* a0: trashed, original value saved on stack (PT_AREG0)152* a1: a1153* a2: new stack pointer, original in DEPC154* a3: a3155* depc: a2, original value saved on stack (PT_DEPC)156* excsave_1: dispatch table157*158* PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception, DEPC159* < VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception160*/161162.literal_position163#ifdef CONFIG_XTENSA_LOAD_STORE164ENTRY(fast_load_store)165166call0 .Lsave_and_load_instruction167168/* Analyze the instruction (load or store?). */169170extui a0, a4, INSN_OP0, 4 # get insn.op0 nibble171172#if XCHAL_HAVE_DENSITY173_beqi a0, OP0_L32I_N, 1f # L32I.N, jump174#endif175bnei a0, OP0_LSAI, .Linvalid_instruction176/* 'store indicator bit' set, jump */177bbsi.l a4, OP1_SI_BIT + INSN_OP1, .Linvalid_instruction1781791:180movi a3, ~3181and a3, a3, a8 # align memory address182183__ssa8 a8184185#ifdef CONFIG_MMU186/* l32e can't be used here even when it's available. */187/* TODO access_ok(a3) could be used here */188j .Linvalid_instruction189#endif190l32i a5, a3, 0191l32i a6, a3, 4192__src_b a3, a5, a6 # a3 has the data word193194#if XCHAL_HAVE_DENSITY195addi a7, a7, 2 # increment PC (assume 16-bit insn)196_beqi a0, OP0_L32I_N, .Lload_w# l32i.n: jump197addi a7, a7, 1198#else199addi a7, a7, 3200#endif201202extui a5, a4, INSN_OP1, 4203_beqi a5, OP1_L32I, .Lload_w204bnei a5, OP1_L8UI, .Lload16205extui a3, a3, 0, 8206j .Lload_w207208ENDPROC(fast_load_store)209#endif210211/*212* Entry condition:213*214* a0: trashed, original value saved on stack (PT_AREG0)215* a1: a1216* a2: new stack pointer, original in DEPC217* a3: a3218* depc: a2, original value saved on stack (PT_DEPC)219* excsave_1: dispatch table220*221* PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception, DEPC222* < VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception223*/224225#ifdef ANY_EXCEPTION_HANDLER226ENTRY(fast_unaligned)227228call0 .Lsave_and_load_instruction229230/* Analyze the instruction (load or store?). */231232extui a5, a4, INSN_OP0, 4 # get insn.op0 nibble233234#if XCHAL_HAVE_DENSITY235_beqi a5, OP0_L32I_N, .Lload # L32I.N, jump236addi a6, a5, -OP0_S32I_N237_beqz a6, .Lstore # S32I.N, do a store238#endif239/* 'store indicator bit' not set, jump */240_bbci.l a4, OP1_SI_BIT + INSN_OP1, .Lload241242#ifdef STORE_EXCEPTION_HANDLER243244/* Store: Jump to table entry to get the value in the source register.*/245246.Lstore:movi a5, .Lstore_table # table247extui a6, a4, INSN_T, 4 # get source register248addx8 a5, a6, a5249jx a5 # jump into table250#endif251#ifdef LOAD_EXCEPTION_HANDLER252253/* Load: Load memory address. */254255.Lload: movi a3, ~3256and a3, a3, a8 # align memory address257258__ssa8 a8259#ifdef UNALIGNED_USER_EXCEPTION260addi a3, a3, 8261l32e a5, a3, -8262l32e a6, a3, -4263#else264l32i a5, a3, 0265l32i a6, a3, 4266#endif267__src_b a3, a5, a6 # a3 has the data word268269#if XCHAL_HAVE_DENSITY270addi a7, a7, 2 # increment PC (assume 16-bit insn)271272extui a5, a4, INSN_OP0, 4273_beqi a5, OP0_L32I_N, .Lload_w# l32i.n: jump274275addi a7, a7, 1276#else277addi a7, a7, 3278#endif279280extui a5, a4, INSN_OP1, 4281_beqi a5, OP1_L32I, .Lload_w # l32i: jump282#endif283#ifdef LOAD_EXCEPTION_HANDLER284.Lload16:285extui a3, a3, 0, 16 # extract lower 16 bits286_beqi a5, OP1_L16UI, .Lload_w287addi a5, a5, -OP1_L16SI288_bnez a5, .Linvalid_instruction289290/* sign extend value */291#if XCHAL_HAVE_SEXT292sext a3, a3, 15293#else294slli a3, a3, 16295srai a3, a3, 16296#endif297298/* Set target register. */299300.Lload_w:301extui a4, a4, INSN_T, 4 # extract target register302movi a5, .Lload_table303addx8 a4, a4, a5304jx a4 # jump to entry for target register305306.align 8307.Lload_table:308s32i a3, a2, PT_AREG0; _j .Lexit; .align 8309mov a1, a3; _j .Lexit; .align 8 # fishy??310s32i a3, a2, PT_AREG2; _j .Lexit; .align 8311s32i a3, a2, PT_AREG3; _j .Lexit; .align 8312s32i a3, a2, PT_AREG4; _j .Lexit; .align 8313s32i a3, a2, PT_AREG5; _j .Lexit; .align 8314s32i a3, a2, PT_AREG6; _j .Lexit; .align 8315s32i a3, a2, PT_AREG7; _j .Lexit; .align 8316s32i a3, a2, PT_AREG8; _j .Lexit; .align 8317mov a9, a3 ; _j .Lexit; .align 8318mov a10, a3 ; _j .Lexit; .align 8319mov a11, a3 ; _j .Lexit; .align 8320mov a12, a3 ; _j .Lexit; .align 8321mov a13, a3 ; _j .Lexit; .align 8322mov a14, a3 ; _j .Lexit; .align 8323mov a15, a3 ; _j .Lexit; .align 8324#endif325#ifdef STORE_EXCEPTION_HANDLER326.Lstore_table:327l32i a3, a2, PT_AREG0; _j .Lstore_w; .align 8328mov a3, a1; _j .Lstore_w; .align 8 # fishy??329l32i a3, a2, PT_AREG2; _j .Lstore_w; .align 8330l32i a3, a2, PT_AREG3; _j .Lstore_w; .align 8331l32i a3, a2, PT_AREG4; _j .Lstore_w; .align 8332l32i a3, a2, PT_AREG5; _j .Lstore_w; .align 8333l32i a3, a2, PT_AREG6; _j .Lstore_w; .align 8334l32i a3, a2, PT_AREG7; _j .Lstore_w; .align 8335l32i a3, a2, PT_AREG8; _j .Lstore_w; .align 8336mov a3, a9 ; _j .Lstore_w; .align 8337mov a3, a10 ; _j .Lstore_w; .align 8338mov a3, a11 ; _j .Lstore_w; .align 8339mov a3, a12 ; _j .Lstore_w; .align 8340mov a3, a13 ; _j .Lstore_w; .align 8341mov a3, a14 ; _j .Lstore_w; .align 8342mov a3, a15 ; _j .Lstore_w; .align 8343#endif344345/* We cannot handle this exception. */346347.extern _kernel_exception348.Linvalid_instruction:349350movi a4, 0351rsr a3, excsave1352s32i a4, a3, EXC_TABLE_FIXUP353354/* Restore a4...a8 and SAR, set SP, and jump to default exception. */355356l32i a0, a2, PT_SAR357l32i a8, a2, PT_AREG8358l32i a7, a2, PT_AREG7359l32i a6, a2, PT_AREG6360l32i a5, a2, PT_AREG5361l32i a4, a2, PT_AREG4362wsr a0, sar363mov a1, a2364365rsr a0, ps366bbsi.l a0, PS_UM_BIT, 2f # jump if user mode367368movi a0, _kernel_exception369jx a03703712: movi a0, _user_exception372jx a0373374#ifdef STORE_EXCEPTION_HANDLER375376# a7: instruction pointer, a4: instruction, a3: value377.Lstore_w:378movi a6, 0 # mask: ffffffff:00000000379380#if XCHAL_HAVE_DENSITY381addi a7, a7, 2 # incr. PC,assume 16-bit instruction382383extui a5, a4, INSN_OP0, 4 # extract OP0384addi a5, a5, -OP0_S32I_N385_beqz a5, 1f # s32i.n: jump386387addi a7, a7, 1 # increment PC, 32-bit instruction388#else389addi a7, a7, 3 # increment PC, 32-bit instruction390#endif391392extui a5, a4, INSN_OP1, 4 # extract OP1393_beqi a5, OP1_S32I, 1f # jump if 32 bit store394_bnei a5, OP1_S16I, .Linvalid_instruction395396movi a5, -1397__extl a3, a3 # get 16-bit value398__exth a6, a5 # get 16-bit mask ffffffff:ffff0000399400/* Get memory address */4014021:403movi a4, ~3404and a4, a4, a8 # align memory address405406/* Insert value into memory */407408movi a5, -1 # mask: ffffffff:XXXX0000409#ifdef UNALIGNED_USER_EXCEPTION410addi a4, a4, 8411#endif412413__ssa8r a8414__src_b a8, a5, a6 # lo-mask F..F0..0 (BE) 0..0F..F (LE)415__src_b a6, a6, a5 # hi-mask 0..0F..F (BE) F..F0..0 (LE)416#ifdef UNALIGNED_USER_EXCEPTION417l32e a5, a4, -8418#else419l32i a5, a4, 0 # load lower address word420#endif421and a5, a5, a8 # mask422__sh a8, a3 # shift value423or a5, a5, a8 # or with original value424#ifdef UNALIGNED_USER_EXCEPTION425s32e a5, a4, -8426l32e a8, a4, -4427#else428s32i a5, a4, 0 # store429l32i a8, a4, 4 # same for upper address word430#endif431__sl a5, a3432and a6, a8, a6433or a6, a6, a5434#ifdef UNALIGNED_USER_EXCEPTION435s32e a6, a4, -4436#else437s32i a6, a4, 4438#endif439#endif440441.Lexit:442#if XCHAL_HAVE_LOOPS443rsr a4, lend # check if we reached LEND444bne a7, a4, 1f445rsr a4, lcount # and LCOUNT != 0446beqz a4, 1f447addi a4, a4, -1 # decrement LCOUNT and set448rsr a7, lbeg # set PC to LBEGIN449wsr a4, lcount450#endif4514521: wsr a7, epc1 # skip emulated instruction453454/* Update icount if we're single-stepping in userspace. */455rsr a4, icountlevel456beqz a4, 1f457bgeui a4, LOCKLEVEL + 1, 1f458rsr a4, icount459addi a4, a4, 1460wsr a4, icount4611:462movi a4, 0463rsr a3, excsave1464s32i a4, a3, EXC_TABLE_FIXUP465466/* Restore working register */467468l32i a0, a2, PT_SAR469l32i a8, a2, PT_AREG8470l32i a7, a2, PT_AREG7471l32i a6, a2, PT_AREG6472l32i a5, a2, PT_AREG5473l32i a4, a2, PT_AREG4474l32i a3, a2, PT_AREG3475476/* restore SAR and return */477478wsr a0, sar479l32i a0, a2, PT_AREG0480l32i a2, a2, PT_AREG2481rfe482483.align 4484.Lsave_and_load_instruction:485486/* Save some working register */487488s32i a3, a2, PT_AREG3489s32i a4, a2, PT_AREG4490s32i a5, a2, PT_AREG5491s32i a6, a2, PT_AREG6492s32i a7, a2, PT_AREG7493s32i a8, a2, PT_AREG8494495rsr a4, depc496s32i a4, a2, PT_AREG2497498rsr a5, sar499s32i a5, a2, PT_SAR500501rsr a3, excsave1502movi a4, fast_unaligned_fixup503s32i a4, a3, EXC_TABLE_FIXUP504505rsr a8, excvaddr # load unaligned memory address506507/* Now, identify one of the following load/store instructions.508*509* The only possible danger of a double exception on the510* following l32i instructions is kernel code in vmalloc511* memory. The processor was just executing at the EPC_1512* address, and indeed, already fetched the instruction. That513* guarantees a TLB mapping, which hasn't been replaced by514* this unaligned exception handler that uses only static TLB515* mappings. However, high-level interrupt handlers might516* modify TLB entries, so for the generic case, we register a517* TABLE_FIXUP handler here, too.518*/519520/* a3...a6 saved on stack, a2 = SP */521522/* Extract the instruction that caused the unaligned access. */523524rsr a7, epc1 # load exception address525movi a3, ~3526and a3, a3, a7 # mask lower bits527528l32i a4, a3, 0 # load 2 words529l32i a5, a3, 4530531__ssa8 a7532__src_b a4, a4, a5 # a4 has the instruction533534ret535536ENDPROC(fast_unaligned)537538ENTRY(fast_unaligned_fixup)539540l32i a2, a3, EXC_TABLE_DOUBLE_SAVE541wsr a3, excsave1542543l32i a8, a2, PT_AREG8544l32i a7, a2, PT_AREG7545l32i a6, a2, PT_AREG6546l32i a5, a2, PT_AREG5547l32i a4, a2, PT_SAR548l32i a0, a2, PT_AREG2549wsr a4, sar550wsr a0, depc # restore depc and a0551l32i a4, a2, PT_AREG4552553rsr a0, exccause554s32i a0, a2, PT_DEPC # mark as a regular exception555556rsr a0, ps557bbsi.l a0, PS_UM_BIT, 1f # jump if user mode558559rsr a0, exccause560addx4 a0, a0, a3 # find entry in table561l32i a0, a0, EXC_TABLE_FAST_KERNEL # load handler562l32i a3, a2, PT_AREG3563jx a05641:565rsr a0, exccause566addx4 a0, a0, a3 # find entry in table567l32i a0, a0, EXC_TABLE_FAST_USER # load handler568l32i a3, a2, PT_AREG3569jx a0570571ENDPROC(fast_unaligned_fixup)572#endif573574575