From f29cf77609cc401d28e2e7ec5c57d9d178ef347d Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Tue, 13 Jun 2023 16:51:18 -0700 Subject: xtensa: add load/store exception handler Memory attached to instruction bus of the xtensa CPU is only accessible for a limited subset of opcodes. Other opcodes generate an exception with the load/store error cause code. This property complicates use of such systems. Provide a handler that recognizes and transparently fixes such exceptions. The following opcodes are recognized when used outside of FLIX bundles: l32i, l32i.n, l16ui, l16si, l8ui. Signed-off-by: Max Filippov --- arch/xtensa/Kconfig | 12 +++++ arch/xtensa/include/asm/traps.h | 5 ++ arch/xtensa/kernel/align.S | 109 +++++++++++++++++++++++++++++++++++----- arch/xtensa/kernel/setup.c | 3 +- arch/xtensa/kernel/traps.c | 21 +++++++- 5 files changed, 136 insertions(+), 14 deletions(-) (limited to 'arch') diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig index 45f72c8fa911..c1bcfc2a8581 100644 --- a/arch/xtensa/Kconfig +++ b/arch/xtensa/Kconfig @@ -203,6 +203,18 @@ config XTENSA_UNALIGNED_USER Say Y here to enable unaligned memory access in user space. +config XTENSA_LOAD_STORE + bool "Load/store exception handler for memory only readable with l32" + help + The Xtensa architecture only allows reading memory attached to its + instruction bus with l32r and l32i instructions, all other + instructions raise an exception with the LoadStoreErrorCause code. + This makes it hard to use some configurations, e.g. store string + literals in FLASH memory attached to the instruction bus. + + Say Y here to enable exception handler that allows transparent + byte and 2-byte access to memory attached to instruction bus. + config HAVE_SMP bool "System Supports SMP (MX)" depends on XTENSA_VARIANT_CUSTOM diff --git a/arch/xtensa/include/asm/traps.h b/arch/xtensa/include/asm/traps.h index acffb02f8760..212c3b9ff407 100644 --- a/arch/xtensa/include/asm/traps.h +++ b/arch/xtensa/include/asm/traps.h @@ -47,6 +47,7 @@ __init trap_set_handler(int cause, xtensa_exception_handler *handler); asmlinkage void fast_illegal_instruction_user(void); asmlinkage void fast_syscall_user(void); asmlinkage void fast_alloca(void); +asmlinkage void fast_load_store(void); asmlinkage void fast_unaligned(void); asmlinkage void fast_second_level_miss(void); asmlinkage void fast_store_prohibited(void); @@ -64,6 +65,10 @@ void do_unhandled(struct pt_regs *regs); static inline void __init early_trap_init(void) { static struct exc_table init_exc_table __initdata = { +#ifdef CONFIG_XTENSA_LOAD_STORE + .fast_kernel_handler[EXCCAUSE_LOAD_STORE_ERROR] = + fast_load_store, +#endif #ifdef CONFIG_MMU .fast_kernel_handler[EXCCAUSE_DTLB_MISS] = fast_second_level_miss, diff --git a/arch/xtensa/kernel/align.S b/arch/xtensa/kernel/align.S index bcbd7962a684..20d6b4961001 100644 --- a/arch/xtensa/kernel/align.S +++ b/arch/xtensa/kernel/align.S @@ -22,7 +22,17 @@ #include #include -#if XCHAL_UNALIGNED_LOAD_EXCEPTION || XCHAL_UNALIGNED_STORE_EXCEPTION +#if XCHAL_UNALIGNED_LOAD_EXCEPTION || defined CONFIG_XTENSA_LOAD_STORE +#define LOAD_EXCEPTION_HANDLER +#endif + +#if XCHAL_UNALIGNED_STORE_EXCEPTION || defined LOAD_EXCEPTION_HANDLER +#define ANY_EXCEPTION_HANDLER +#endif + +#if XCHAL_HAVE_WINDOWED +#define UNALIGNED_USER_EXCEPTION +#endif /* First-level exception handler for unaligned exceptions. * @@ -58,10 +68,6 @@ * BE shift left / mask 0 0 X X */ -#if XCHAL_HAVE_WINDOWED -#define UNALIGNED_USER_EXCEPTION -#endif - #if XCHAL_HAVE_BE #define HWORD_START 16 @@ -103,7 +109,7 @@ * * 23 0 * ----------------------------- - * res 0000 0010 + * L8UI xxxx xxxx 0000 ssss tttt 0010 * L16UI xxxx xxxx 0001 ssss tttt 0010 * L32I xxxx xxxx 0010 ssss tttt 0010 * XXX 0011 ssss tttt 0010 @@ -128,9 +134,11 @@ #define OP0_L32I_N 0x8 /* load immediate narrow */ #define OP0_S32I_N 0x9 /* store immediate narrow */ +#define OP0_LSAI 0x2 /* load/store */ #define OP1_SI_MASK 0x4 /* OP1 bit set for stores */ #define OP1_SI_BIT 2 /* OP1 bit number for stores */ +#define OP1_L8UI 0x0 #define OP1_L32I 0x2 #define OP1_L16UI 0x1 #define OP1_L16SI 0x9 @@ -155,8 +163,73 @@ */ .literal_position +#ifdef CONFIG_XTENSA_LOAD_STORE +ENTRY(fast_load_store) + + call0 .Lsave_and_load_instruction + + /* Analyze the instruction (load or store?). */ + + extui a0, a4, INSN_OP0, 4 # get insn.op0 nibble + +#if XCHAL_HAVE_DENSITY + _beqi a0, OP0_L32I_N, 1f # L32I.N, jump +#endif + bnei a0, OP0_LSAI, .Linvalid_instruction + /* 'store indicator bit' set, jump */ + bbsi.l a4, OP1_SI_BIT + INSN_OP1, .Linvalid_instruction + +1: + movi a3, ~3 + and a3, a3, a8 # align memory address + + __ssa8 a8 + +#ifdef CONFIG_MMU + /* l32e can't be used here even when it's available. */ + /* TODO access_ok(a3) could be used here */ + j .Linvalid_instruction +#endif + l32i a5, a3, 0 + l32i a6, a3, 4 + __src_b a3, a5, a6 # a3 has the data word + +#if XCHAL_HAVE_DENSITY + addi a7, a7, 2 # increment PC (assume 16-bit insn) + _beqi a0, OP0_L32I_N, .Lload_w# l32i.n: jump + addi a7, a7, 1 +#else + addi a7, a7, 3 +#endif + + extui a5, a4, INSN_OP1, 4 + _beqi a5, OP1_L32I, .Lload_w + bnei a5, OP1_L8UI, .Lload16 + extui a3, a3, 0, 8 + j .Lload_w + +ENDPROC(fast_load_store) +#endif + +/* + * Entry condition: + * + * 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 + */ + +#ifdef ANY_EXCEPTION_HANDLER ENTRY(fast_unaligned) +#if XCHAL_UNALIGNED_LOAD_EXCEPTION || XCHAL_UNALIGNED_STORE_EXCEPTION + call0 .Lsave_and_load_instruction /* Analyze the instruction (load or store?). */ @@ -171,12 +244,17 @@ ENTRY(fast_unaligned) /* 'store indicator bit' not set, jump */ _bbci.l a4, OP1_SI_BIT + INSN_OP1, .Lload +#endif +#if XCHAL_UNALIGNED_STORE_EXCEPTION + /* Store: Jump to table entry to get the value in the source register.*/ .Lstore:movi a5, .Lstore_table # table extui a6, a4, INSN_T, 4 # get source register addx8 a5, a6, a5 jx a5 # jump into table +#endif +#if XCHAL_UNALIGNED_LOAD_EXCEPTION /* Load: Load memory address. */ @@ -207,7 +285,9 @@ ENTRY(fast_unaligned) extui a5, a4, INSN_OP1, 4 _beqi a5, OP1_L32I, .Lload_w # l32i: jump - +#endif +#ifdef LOAD_EXCEPTION_HANDLER +.Lload16: extui a3, a3, 0, 16 # extract lower 16 bits _beqi a5, OP1_L16UI, .Lload_w addi a5, a5, -OP1_L16SI @@ -247,7 +327,8 @@ ENTRY(fast_unaligned) mov a13, a3 ; _j .Lexit; .align 8 mov a14, a3 ; _j .Lexit; .align 8 mov a15, a3 ; _j .Lexit; .align 8 - +#endif +#if XCHAL_UNALIGNED_STORE_EXCEPTION .Lstore_table: l32i a3, a2, PT_AREG0; _j .Lstore_w; .align 8 mov a3, a1; _j .Lstore_w; .align 8 # fishy?? @@ -265,7 +346,9 @@ ENTRY(fast_unaligned) mov a3, a13 ; _j .Lstore_w; .align 8 mov a3, a14 ; _j .Lstore_w; .align 8 mov a3, a15 ; _j .Lstore_w; .align 8 +#endif +#ifdef ANY_EXCEPTION_HANDLER /* We cannot handle this exception. */ .extern _kernel_exception @@ -294,6 +377,8 @@ ENTRY(fast_unaligned) 2: movi a0, _user_exception jx a0 +#endif +#if XCHAL_UNALIGNED_STORE_EXCEPTION # a7: instruction pointer, a4: instruction, a3: value .Lstore_w: @@ -358,7 +443,8 @@ ENTRY(fast_unaligned) #else s32i a6, a4, 4 #endif - +#endif +#ifdef ANY_EXCEPTION_HANDLER .Lexit: #if XCHAL_HAVE_LOOPS rsr a4, lend # check if we reached LEND @@ -453,7 +539,7 @@ ENTRY(fast_unaligned) __src_b a4, a4, a5 # a4 has the instruction ret - +#endif ENDPROC(fast_unaligned) ENTRY(fast_unaligned_fixup) @@ -490,5 +576,4 @@ ENTRY(fast_unaligned_fixup) jx a0 ENDPROC(fast_unaligned_fixup) - -#endif /* XCHAL_UNALIGNED_LOAD_EXCEPTION || XCHAL_UNALIGNED_STORE_EXCEPTION */ +#endif diff --git a/arch/xtensa/kernel/setup.c b/arch/xtensa/kernel/setup.c index 09ab4d83faf8..aba3ff4e60d8 100644 --- a/arch/xtensa/kernel/setup.c +++ b/arch/xtensa/kernel/setup.c @@ -245,7 +245,8 @@ void __init init_arch(bp_tag_t *bp_start) { /* Initialize basic exception handling if configuration may need it */ - if (IS_ENABLED(CONFIG_KASAN)) + if (IS_ENABLED(CONFIG_KASAN) || + IS_ENABLED(CONFIG_XTENSA_LOAD_STORE)) early_trap_init(); /* Initialize MMU. */ diff --git a/arch/xtensa/kernel/traps.c b/arch/xtensa/kernel/traps.c index c6d93a95db1a..71588bf55632 100644 --- a/arch/xtensa/kernel/traps.c +++ b/arch/xtensa/kernel/traps.c @@ -54,6 +54,9 @@ static void do_interrupt(struct pt_regs *regs); #if XTENSA_FAKE_NMI static void do_nmi(struct pt_regs *regs); #endif +#ifdef CONFIG_XTENSA_LOAD_STORE +static void do_load_store(struct pt_regs *regs); +#endif static void do_unaligned_user(struct pt_regs *regs); static void do_multihit(struct pt_regs *regs); #if XTENSA_HAVE_COPROCESSORS @@ -89,7 +92,10 @@ static dispatch_init_table_t __initdata dispatch_init_table[] = { { EXCCAUSE_SYSTEM_CALL, USER, fast_syscall_user }, { EXCCAUSE_SYSTEM_CALL, 0, system_call }, /* EXCCAUSE_INSTRUCTION_FETCH unhandled */ -/* EXCCAUSE_LOAD_STORE_ERROR unhandled*/ +#ifdef CONFIG_XTENSA_LOAD_STORE +{ EXCCAUSE_LOAD_STORE_ERROR, USER|KRNL, fast_load_store }, +{ EXCCAUSE_LOAD_STORE_ERROR, 0, do_load_store }, +#endif { EXCCAUSE_LEVEL1_INTERRUPT, 0, do_interrupt }, #ifdef SUPPORT_WINDOWED { EXCCAUSE_ALLOCA, USER|KRNL, fast_alloca }, @@ -347,6 +353,19 @@ static void do_div0(struct pt_regs *regs) force_sig_fault(SIGFPE, FPE_INTDIV, (void __user *)regs->pc); } +#ifdef CONFIG_XTENSA_LOAD_STORE +static void do_load_store(struct pt_regs *regs) +{ + __die_if_kernel("Unhandled load/store exception in kernel", + regs, SIGKILL); + + pr_info_ratelimited("Load/store error to %08lx in '%s' (pid = %d, pc = %#010lx)\n", + regs->excvaddr, current->comm, + task_pid_nr(current), regs->pc); + force_sig_fault(SIGBUS, BUS_ADRERR, (void *)regs->excvaddr); +} +#endif + /* * Handle unaligned memory accesses from user space. Kill task. * -- cgit v1.2.3