]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
xtensa: add load/store exception handler
authorMax Filippov <jcmvbkbc@gmail.com>
Tue, 13 Jun 2023 23:51:18 +0000 (16:51 -0700)
committerMax Filippov <jcmvbkbc@gmail.com>
Wed, 14 Jun 2023 04:56:27 +0000 (21:56 -0700)
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 <jcmvbkbc@gmail.com>
arch/xtensa/Kconfig
arch/xtensa/include/asm/traps.h
arch/xtensa/kernel/align.S
arch/xtensa/kernel/setup.c
arch/xtensa/kernel/traps.c

index 45f72c8fa91169061a88cfbe4253935a12f782a4..c1bcfc2a85815c921a89689765d6d8978df42d92 100644 (file)
@@ -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
index acffb02f876033a56c3ea1a1cd8821d9c2e0f1a1..212c3b9ff407871d9d3047993a880d234e60c3d5 100644 (file)
@@ -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,
index bcbd7962a6840062e32c16b1e82a759d09316a49..20d6b4961001b970aca261d26ad8c07c423addf8 100644 (file)
 #include <asm/asmmacro.h>
 #include <asm/processor.h>
 
-#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.
  *
  *  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
  *
  *            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
 
 #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
  */
 
        .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
index 09ab4d83faf8035a8e9f80b8b19081aaa675a9d6..aba3ff4e60d85740a64a116884391ffda8906bbc 100644 (file)
@@ -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. */
index c6d93a95db1abb52df7472bea84cda652dfcb5bb..71588bf55632d53056536dba225ab7febe971ee1 100644 (file)
@@ -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.
  *