]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - arch/arm64/kernel/entry.S
arm64: Move post_ttbr_update_workaround to C code
[mirror_ubuntu-artful-kernel.git] / arch / arm64 / kernel / entry.S
index 9e126d3d8b531ee5fb439a3d3b8ecba175bf572d..fd695e860ecb2370952fd5975faf3a0d10bb277a 100644 (file)
@@ -29,6 +29,8 @@
 #include <asm/esr.h>
 #include <asm/irq.h>
 #include <asm/memory.h>
+#include <asm/mmu.h>
+#include <asm/processor.h>
 #include <asm/ptrace.h>
 #include <asm/thread_info.h>
 #include <asm/asm-uaccess.h>
 #define BAD_FIQ                2
 #define BAD_ERROR      3
 
-       .macro  kernel_entry, el, regsize = 64
+       .macro kernel_ventry, el, label, regsize = 64
+       .align 7
+#ifdef CONFIG_UNMAP_KERNEL_AT_EL0
+alternative_if ARM64_UNMAP_KERNEL_AT_EL0
+       .if     \el == 0
+       .if     \regsize == 64
+       mrs     x30, tpidrro_el0
+       msr     tpidrro_el0, xzr
+       .else
+       mov     x30, xzr
+       .endif
+       .endif
+alternative_else_nop_endif
+#endif
+
        sub     sp, sp, #S_FRAME_SIZE
+#ifdef CONFIG_VMAP_STACK
+       /*
+        * Test whether the SP has overflowed, without corrupting a GPR.
+        * Task and IRQ stacks are aligned to (1 << THREAD_SHIFT).
+        */
+       add     sp, sp, x0                      // sp' = sp + x0
+       sub     x0, sp, x0                      // x0' = sp' - x0 = (sp + x0) - x0 = sp
+       tbnz    x0, #THREAD_SHIFT, 0f
+       sub     x0, sp, x0                      // x0'' = sp' - x0' = (sp + x0) - sp = x0
+       sub     sp, sp, x0                      // sp'' = sp' - x0 = (sp + x0) - x0 = sp
+       b       el\()\el\()_\label
+
+0:
+       /*
+        * Either we've just detected an overflow, or we've taken an exception
+        * while on the overflow stack. Either way, we won't return to
+        * userspace, and can clobber EL0 registers to free up GPRs.
+        */
+
+       /* Stash the original SP (minus S_FRAME_SIZE) in tpidr_el0. */
+       msr     tpidr_el0, x0
+
+       /* Recover the original x0 value and stash it in tpidrro_el0 */
+       sub     x0, sp, x0
+       msr     tpidrro_el0, x0
+
+       /* Switch to the overflow stack */
+       adr_this_cpu sp, overflow_stack + OVERFLOW_STACK_SIZE, x0
+
+       /*
+        * Check whether we were already on the overflow stack. This may happen
+        * after panic() re-enables interrupts.
+        */
+       mrs     x0, tpidr_el0                   // sp of interrupted context
+       sub     x0, sp, x0                      // delta with top of overflow stack
+       tst     x0, #~(OVERFLOW_STACK_SIZE - 1) // within range?
+       b.ne    __bad_stack                     // no? -> bad stack pointer
+
+       /* We were already on the overflow stack. Restore sp/x0 and carry on. */
+       sub     sp, sp, x0
+       mrs     x0, tpidrro_el0
+#endif
+       b       el\()\el\()_\label
+       .endm
+
+       .macro tramp_alias, dst, sym
+       mov_q   \dst, TRAMP_VALIAS
+       add     \dst, \dst, #(\sym - .entry.tramp.text)
+       .endm
+
+       .macro  kernel_entry, el, regsize = 64
        .if     \regsize == 32
        mov     w0, w0                          // zero upper 32 bits of x0
        .endif
        .else
        add     x21, sp, #S_FRAME_SIZE
        get_thread_info tsk
-       /* Save the task's original addr_limit and set USER_DS (TASK_SIZE_64) */
+       /* Save the task's original addr_limit and set USER_DS */
        ldr     x20, [tsk, #TSK_TI_ADDR_LIMIT]
        str     x20, [sp, #S_ORIG_ADDR_LIMIT]
-       mov     x20, #TASK_SIZE_64
+       mov     x20, #USER_DS
        str     x20, [tsk, #TSK_TI_ADDR_LIMIT]
        /* No need to reset PSTATE.UAO, hardware's already set it to 0 for us */
        .endif /* \el == 0 */
        mrs     x23, spsr_el1
        stp     lr, x21, [sp, #S_LR]
 
+       /*
+        * In order to be able to dump the contents of struct pt_regs at the
+        * time the exception was taken (in case we attempt to walk the call
+        * stack later), chain it together with the stack frames.
+        */
+       .if \el == 0
+       stp     xzr, xzr, [sp, #S_STACKFRAME]
+       .else
+       stp     x29, x22, [sp, #S_STACKFRAME]
+       .endif
+       add     x29, sp, #S_STACKFRAME
+
 #ifdef CONFIG_ARM64_SW_TTBR0_PAN
        /*
         * Set the TTBR0 PAN bit in SPSR. When the exception is taken from
@@ -126,7 +205,7 @@ alternative_else_nop_endif
 
        .if     \el != 0
        mrs     x21, ttbr0_el1
-       tst     x21, #0xffff << 48              // Check for the reserved ASID
+       tst     x21, #TTBR_ASID_MASK            // Check for the reserved ASID
        orr     x23, x23, #PSR_PAN_BIT          // Set the emulated PAN in the saved SPSR
        b.eq    1f                              // TTBR0 access already disabled
        and     x23, x23, #~PSR_PAN_BIT         // Clear the emulated PAN in the saved SPSR
@@ -142,8 +221,8 @@ alternative_else_nop_endif
         * Set syscallno to -1 by default (overridden later if real syscall).
         */
        .if     \el == 0
-       mvn     x21, xzr
-       str     x21, [sp, #S_SYSCALLNO]
+       mvn     w21, wzr
+       str     w21, [sp, #S_SYSCALLNO]
        .endif
 
        /*
@@ -189,7 +268,7 @@ alternative_else_nop_endif
        tbnz    x22, #22, 1f                    // Skip re-enabling TTBR0 access if the PSR_PAN_BIT is set
        .endif
 
-       __uaccess_ttbr0_enable x0
+       __uaccess_ttbr0_enable x0, x1
 
        .if     \el == 0
        /*
@@ -198,7 +277,7 @@ alternative_else_nop_endif
         * Cavium erratum 27456 (broadcast TLBI instructions may cause I-cache
         * corruption).
         */
-       post_ttbr0_update_workaround
+       bl      post_ttbr_update_workaround
        .endif
 1:
        .if     \el != 0
@@ -210,18 +289,20 @@ alternative_else_nop_endif
        .if     \el == 0
        ldr     x23, [sp, #S_SP]                // load return stack pointer
        msr     sp_el0, x23
+       tst     x22, #PSR_MODE32_BIT            // native task?
+       b.eq    3f
+
 #ifdef CONFIG_ARM64_ERRATUM_845719
 alternative_if ARM64_WORKAROUND_845719
-       tbz     x22, #4, 1f
 #ifdef CONFIG_PID_IN_CONTEXTIDR
        mrs     x29, contextidr_el1
        msr     contextidr_el1, x29
 #else
        msr contextidr_el1, xzr
 #endif
-1:
 alternative_else_nop_endif
 #endif
+3:
        .endif
 
        msr     elr_el1, x21                    // set up the return data
@@ -243,7 +324,21 @@ alternative_else_nop_endif
        ldp     x28, x29, [sp, #16 * 14]
        ldr     lr, [sp, #S_LR]
        add     sp, sp, #S_FRAME_SIZE           // restore sp
-       eret                                    // return to kernel
+
+       .if     \el == 0
+alternative_insn eret, nop, ARM64_UNMAP_KERNEL_AT_EL0
+#ifdef CONFIG_UNMAP_KERNEL_AT_EL0
+       bne     4f
+       msr     far_el1, x30
+       tramp_alias     x30, tramp_exit_native
+       br      x30
+4:
+       tramp_alias     x30, tramp_exit_compat
+       br      x30
+#endif
+       .else
+       eret
+       .endif
        .endm
 
        .macro  irq_stack_entry
@@ -259,20 +354,12 @@ alternative_else_nop_endif
        and     x25, x25, #~(THREAD_SIZE - 1)
        cbnz    x25, 9998f
 
-       adr_this_cpu x25, irq_stack, x26
-       mov     x26, #IRQ_STACK_START_SP
+       ldr_this_cpu x25, irq_stack_ptr, x26
+       mov     x26, #IRQ_STACK_SIZE
        add     x26, x25, x26
 
        /* switch to the irq stack */
        mov     sp, x26
-
-       /*
-        * Add a dummy stack frame, this non-standard format is fixed up
-        * by unwind_frame()
-        */
-       stp     x29, x19, [sp, #-16]!
-       mov     x29, sp
-
 9998:
        .endm
 
@@ -290,8 +377,10 @@ alternative_else_nop_endif
  *
  * x7 is reserved for the system call number in 32-bit mode.
  */
-sc_nr  .req    x25             // number of system calls
-scno   .req    x26             // syscall number
+wsc_nr .req    w25             // number of system calls
+xsc_nr .req    x25             // number of system calls (zero-extended)
+wscno  .req    w26             // syscall number
+xscno  .req    x26             // syscall number (zero-extended)
 stbl   .req    x27             // syscall table pointer
 tsk    .req    x28             // current thread_info
 
@@ -315,34 +404,62 @@ tsk       .req    x28             // current thread_info
 
        .align  11
 ENTRY(vectors)
-       ventry  el1_sync_invalid                // Synchronous EL1t
-       ventry  el1_irq_invalid                 // IRQ EL1t
-       ventry  el1_fiq_invalid                 // FIQ EL1t
-       ventry  el1_error_invalid               // Error EL1t
+       kernel_ventry   1, sync_invalid                 // Synchronous EL1t
+       kernel_ventry   1, irq_invalid                  // IRQ EL1t
+       kernel_ventry   1, fiq_invalid                  // FIQ EL1t
+       kernel_ventry   1, error_invalid                // Error EL1t
 
-       ventry  el1_sync                        // Synchronous EL1h
-       ventry  el1_irq                         // IRQ EL1h
-       ventry  el1_fiq_invalid                 // FIQ EL1h
-       ventry  el1_error_invalid               // Error EL1h
+       kernel_ventry   1, sync                         // Synchronous EL1h
+       kernel_ventry   1, irq                          // IRQ EL1h
+       kernel_ventry   1, fiq_invalid                  // FIQ EL1h
+       kernel_ventry   1, error_invalid                // Error EL1h
 
-       ventry  el0_sync                        // Synchronous 64-bit EL0
-       ventry  el0_irq                         // IRQ 64-bit EL0
-       ventry  el0_fiq_invalid                 // FIQ 64-bit EL0
-       ventry  el0_error_invalid               // Error 64-bit EL0
+       kernel_ventry   0, sync                         // Synchronous 64-bit EL0
+       kernel_ventry   0, irq                          // IRQ 64-bit EL0
+       kernel_ventry   0, fiq_invalid                  // FIQ 64-bit EL0
+       kernel_ventry   0, error_invalid                // Error 64-bit EL0
 
 #ifdef CONFIG_COMPAT
-       ventry  el0_sync_compat                 // Synchronous 32-bit EL0
-       ventry  el0_irq_compat                  // IRQ 32-bit EL0
-       ventry  el0_fiq_invalid_compat          // FIQ 32-bit EL0
-       ventry  el0_error_invalid_compat        // Error 32-bit EL0
+       kernel_ventry   0, sync_compat, 32              // Synchronous 32-bit EL0
+       kernel_ventry   0, irq_compat, 32               // IRQ 32-bit EL0
+       kernel_ventry   0, fiq_invalid_compat, 32       // FIQ 32-bit EL0
+       kernel_ventry   0, error_invalid_compat, 32     // Error 32-bit EL0
 #else
-       ventry  el0_sync_invalid                // Synchronous 32-bit EL0
-       ventry  el0_irq_invalid                 // IRQ 32-bit EL0
-       ventry  el0_fiq_invalid                 // FIQ 32-bit EL0
-       ventry  el0_error_invalid               // Error 32-bit EL0
+       kernel_ventry   0, sync_invalid, 32             // Synchronous 32-bit EL0
+       kernel_ventry   0, irq_invalid, 32              // IRQ 32-bit EL0
+       kernel_ventry   0, fiq_invalid, 32              // FIQ 32-bit EL0
+       kernel_ventry   0, error_invalid, 32            // Error 32-bit EL0
 #endif
 END(vectors)
 
+#ifdef CONFIG_VMAP_STACK
+       /*
+        * We detected an overflow in kernel_ventry, which switched to the
+        * overflow stack. Stash the exception regs, and head to our overflow
+        * handler.
+        */
+__bad_stack:
+       /* Restore the original x0 value */
+       mrs     x0, tpidrro_el0
+
+       /*
+        * Store the original GPRs to the new stack. The orginal SP (minus
+        * S_FRAME_SIZE) was stashed in tpidr_el0 by kernel_ventry.
+        */
+       sub     sp, sp, #S_FRAME_SIZE
+       kernel_entry 1
+       mrs     x0, tpidr_el0
+       add     x0, x0, #S_FRAME_SIZE
+       str     x0, [sp, #S_SP]
+
+       /* Stash the regs for handle_bad_stack */
+       mov     x0, sp
+
+       /* Time to die */
+       bl      handle_bad_stack
+       ASM_BUG()
+#endif /* CONFIG_VMAP_STACK */
+
 /*
  * Invalid mode handlers
  */
@@ -581,8 +698,8 @@ el0_svc_compat:
         * AArch32 syscall handling
         */
        adrp    stbl, compat_sys_call_table     // load compat syscall table pointer
-       uxtw    scno, w7                        // syscall number in w7 (r7)
-       mov     sc_nr, #__NR_compat_syscalls
+       mov     wscno, w7                       // syscall number in w7 (r7)
+       mov     wsc_nr, #__NR_compat_syscalls
        b       el0_svc_naked
 
        .align  6
@@ -758,19 +875,20 @@ ENDPROC(ret_to_user)
        .align  6
 el0_svc:
        adrp    stbl, sys_call_table            // load syscall table pointer
-       uxtw    scno, w8                        // syscall number in w8
-       mov     sc_nr, #__NR_syscalls
+       mov     wscno, w8                       // syscall number in w8
+       mov     wsc_nr, #__NR_syscalls
 el0_svc_naked:                                 // compat entry point
-       stp     x0, scno, [sp, #S_ORIG_X0]      // save the original x0 and syscall number
+       stp     x0, xscno, [sp, #S_ORIG_X0]     // save the original x0 and syscall number
        enable_dbg_and_irq
        ct_user_exit 1
 
        ldr     x16, [tsk, #TSK_TI_FLAGS]       // check for syscall hooks
        tst     x16, #_TIF_SYSCALL_WORK
        b.ne    __sys_trace
-       cmp     scno, sc_nr                     // check upper syscall limit
+       cmp     wscno, wsc_nr                   // check upper syscall limit
        b.hs    ni_sys
-       ldr     x16, [stbl, scno, lsl #3]       // address in the syscall table
+       mask_nospec64 xscno, xsc_nr, x19        // enforce bounds for syscall number
+       ldr     x16, [stbl, xscno, lsl #3]      // address in the syscall table
        blr     x16                             // call sys_* routine
        b       ret_fast_syscall
 ni_sys:
@@ -784,24 +902,23 @@ ENDPROC(el0_svc)
         * switches, and waiting for our parent to respond.
         */
 __sys_trace:
-       mov     w0, #-1                         // set default errno for
-       cmp     scno, x0                        // user-issued syscall(-1)
+       cmp     wscno, #-1                      // user-issued syscall(-1)?
        b.ne    1f
-       mov     x0, #-ENOSYS
+       mov     x0, #-ENOSYS                    // set default errno if so
        str     x0, [sp, #S_X0]
 1:     mov     x0, sp
        bl      syscall_trace_enter
        cmp     w0, #-1                         // skip the syscall?
        b.eq    __sys_trace_return_skipped
-       uxtw    scno, w0                        // syscall number (possibly new)
+       mov     wscno, w0                       // syscall number (possibly new)
        mov     x1, sp                          // pointer to regs
-       cmp     scno, sc_nr                     // check upper syscall limit
+       cmp     wscno, wsc_nr                   // check upper syscall limit
        b.hs    __ni_sys_trace
        ldp     x0, x1, [sp]                    // restore the syscall args
        ldp     x2, x3, [sp, #S_X2]
        ldp     x4, x5, [sp, #S_X4]
        ldp     x6, x7, [sp, #S_X6]
-       ldr     x16, [stbl, scno, lsl #3]       // address in the syscall table
+       ldr     x16, [stbl, xscno, lsl #3]      // address in the syscall table
        blr     x16                             // call sys_* routine
 
 __sys_trace_return:
@@ -818,6 +935,117 @@ __ni_sys_trace:
 
        .popsection                             // .entry.text
 
+#ifdef CONFIG_UNMAP_KERNEL_AT_EL0
+/*
+ * Exception vectors trampoline.
+ */
+       .pushsection ".entry.tramp.text", "ax"
+
+       .macro tramp_map_kernel, tmp
+       mrs     \tmp, ttbr1_el1
+       sub     \tmp, \tmp, #(SWAPPER_DIR_SIZE + RESERVED_TTBR0_SIZE)
+       bic     \tmp, \tmp, #USER_ASID_FLAG
+       msr     ttbr1_el1, \tmp
+#ifdef CONFIG_QCOM_FALKOR_ERRATUM_1003
+alternative_if ARM64_WORKAROUND_QCOM_FALKOR_E1003
+       /* ASID already in \tmp[63:48] */
+       movk    \tmp, #:abs_g2_nc:(TRAMP_VALIAS >> 12)
+       movk    \tmp, #:abs_g1_nc:(TRAMP_VALIAS >> 12)
+       /* 2MB boundary containing the vectors, so we nobble the walk cache */
+       movk    \tmp, #:abs_g0_nc:((TRAMP_VALIAS & ~(SZ_2M - 1)) >> 12)
+       isb
+       tlbi    vae1, \tmp
+       dsb     nsh
+alternative_else_nop_endif
+#endif /* CONFIG_QCOM_FALKOR_ERRATUM_1003 */
+       .endm
+
+       .macro tramp_unmap_kernel, tmp
+       mrs     \tmp, ttbr1_el1
+       add     \tmp, \tmp, #(SWAPPER_DIR_SIZE + RESERVED_TTBR0_SIZE)
+       orr     \tmp, \tmp, #USER_ASID_FLAG
+       msr     ttbr1_el1, \tmp
+       /*
+        * We avoid running the post_ttbr_update_workaround here because
+        * it's only needed by Cavium ThunderX, which requires KPTI to be
+        * disabled.
+        */
+       .endm
+
+       .macro tramp_ventry, regsize = 64
+       .align  7
+1:
+       .if     \regsize == 64
+       msr     tpidrro_el0, x30        // Restored in kernel_ventry
+       .endif
+       /*
+        * Defend against branch aliasing attacks by pushing a dummy
+        * entry onto the return stack and using a RET instruction to
+        * enter the full-fat kernel vectors.
+        */
+       bl      2f
+       b       .
+2:
+       tramp_map_kernel        x30
+#ifdef CONFIG_RANDOMIZE_BASE
+       adr     x30, tramp_vectors + PAGE_SIZE
+alternative_insn isb, nop, ARM64_WORKAROUND_QCOM_FALKOR_E1003
+       ldr     x30, [x30]
+#else
+       ldr     x30, =vectors
+#endif
+       prfm    plil1strm, [x30, #(1b - tramp_vectors)]
+       msr     vbar_el1, x30
+       add     x30, x30, #(1b - tramp_vectors)
+       isb
+       ret
+       .endm
+
+       .macro tramp_exit, regsize = 64
+       adr     x30, tramp_vectors
+       msr     vbar_el1, x30
+       tramp_unmap_kernel      x30
+       .if     \regsize == 64
+       mrs     x30, far_el1
+       .endif
+       eret
+       .endm
+
+       .align  11
+ENTRY(tramp_vectors)
+       .space  0x400
+
+       tramp_ventry
+       tramp_ventry
+       tramp_ventry
+       tramp_ventry
+
+       tramp_ventry    32
+       tramp_ventry    32
+       tramp_ventry    32
+       tramp_ventry    32
+END(tramp_vectors)
+
+ENTRY(tramp_exit_native)
+       tramp_exit
+END(tramp_exit_native)
+
+ENTRY(tramp_exit_compat)
+       tramp_exit      32
+END(tramp_exit_compat)
+
+       .ltorg
+       .popsection                             // .entry.tramp.text
+#ifdef CONFIG_RANDOMIZE_BASE
+       .pushsection ".rodata", "a"
+       .align PAGE_SHIFT
+       .globl  __entry_tramp_data_start
+__entry_tramp_data_start:
+       .quad   vectors
+       .popsection                             // .rodata
+#endif /* CONFIG_RANDOMIZE_BASE */
+#endif /* CONFIG_UNMAP_KERNEL_AT_EL0 */
+
 /*
  * Special system call wrappers.
  */