]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - arch/arm64/kernel/entry.S
arm64: kernel: Add arch-specific SDEI entry code and CPU masking
[mirror_ubuntu-bionic-kernel.git] / arch / arm64 / kernel / entry.S
index b99fc928119c09b38800f3d07836a0ba78ce0f6e..bae2daf5d486e2c6cb755e77c9fdd541eacfbb36 100644 (file)
 
        .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
        /*
        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
        .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 */
@@ -186,8 +204,8 @@ alternative_if ARM64_HAS_PAN
 alternative_else_nop_endif
 
        .if     \el != 0
-       mrs     x21, ttbr1_el1
-       tst     x21, #0xffff << 48              // Check for the reserved ASID
+       mrs     x21, ttbr0_el1
+       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
@@ -259,7 +277,7 @@ alternative_else_nop_endif
         * Cavium erratum 27456 (broadcast TLBI instructions may cause I-cache
         * corruption).
         */
-       post_ttbr_update_workaround
+       bl      post_ttbr_update_workaround
        .endif
 1:
        .if     \el != 0
@@ -271,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
@@ -304,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
@@ -344,6 +378,7 @@ alternative_else_nop_endif
  * x7 is reserved for the system call number in 32-bit mode.
  */
 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
@@ -687,12 +722,15 @@ el0_ia:
         * Instruction abort handling
         */
        mrs     x26, far_el1
-       enable_daif
+       enable_da_f
+#ifdef CONFIG_TRACE_IRQFLAGS
+       bl      trace_hardirqs_off
+#endif
        ct_user_exit
        mov     x0, x26
        mov     x1, x25
        mov     x2, sp
-       bl      do_mem_abort
+       bl      do_el0_ia_bp_hardening
        b       ret_to_user
 el0_fpsimd_acc:
        /*
@@ -729,7 +767,10 @@ el0_sp_pc:
         * Stack or PC alignment exception handling
         */
        mrs     x26, far_el1
-       enable_daif
+       enable_da_f
+#ifdef CONFIG_TRACE_IRQFLAGS
+       bl      trace_hardirqs_off
+#endif
        ct_user_exit
        mov     x0, x26
        mov     x1, x25
@@ -787,6 +828,11 @@ el0_irq_naked:
 #endif
 
        ct_user_exit
+#ifdef CONFIG_HARDEN_BRANCH_PREDICTOR
+       tbz     x22, #55, 1f
+       bl      do_el0_irq_bp_hardening
+1:
+#endif
        irq_handler
 
 #ifdef CONFIG_TRACE_IRQFLAGS
@@ -898,6 +944,7 @@ el0_svc_naked:                                      // compat entry point
        b.ne    __sys_trace
        cmp     wscno, wsc_nr                   // check upper syscall limit
        b.hs    ni_sys
+       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
@@ -956,6 +1003,18 @@ __ni_sys_trace:
        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
@@ -964,16 +1023,9 @@ __ni_sys_trace:
        orr     \tmp, \tmp, #USER_ASID_FLAG
        msr     ttbr1_el1, \tmp
        /*
-        * We avoid running the post_ttbr_update_workaround here because the
-        * user and kernel ASIDs don't have conflicting mappings, so any
-        * "blessing" as described in:
-        *
-        *   http://lkml.kernel.org/r/56BB848A.6060603@caviumnetworks.com
-        *
-        * will not hurt correctness. Whilst this may partially defeat the
-        * point of using split ASIDs in the first place, it avoids
-        * the hit of invalidating the entire I-cache on every return to
-        * userspace.
+        * We avoid running the post_ttbr_update_workaround here because
+        * it's only needed by Cavium ThunderX, which requires KPTI to be
+        * disabled.
         */
        .endm
 
@@ -983,13 +1035,27 @@ __ni_sys_trace:
        .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
-       br      x30
+       ret
        .endm
 
        .macro tramp_exit, regsize = 64
@@ -1027,6 +1093,14 @@ 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 */
 
 /*
@@ -1082,3 +1156,104 @@ ENTRY(ret_from_fork)
        b       ret_to_user
 ENDPROC(ret_from_fork)
 NOKPROBE(ret_from_fork)
+
+#ifdef CONFIG_ARM_SDE_INTERFACE
+
+#include <asm/sdei.h>
+#include <uapi/linux/arm_sdei.h>
+
+/*
+ * Software Delegated Exception entry point.
+ *
+ * x0: Event number
+ * x1: struct sdei_registered_event argument from registration time.
+ * x2: interrupted PC
+ * x3: interrupted PSTATE
+ *
+ * Firmware has preserved x0->x17 for us, we must save/restore the rest to
+ * follow SMC-CC. We save (or retrieve) all the registers as the handler may
+ * want them.
+ */
+ENTRY(__sdei_asm_handler)
+       stp     x2, x3, [x1, #SDEI_EVENT_INTREGS + S_PC]
+       stp     x4, x5, [x1, #SDEI_EVENT_INTREGS + 16 * 2]
+       stp     x6, x7, [x1, #SDEI_EVENT_INTREGS + 16 * 3]
+       stp     x8, x9, [x1, #SDEI_EVENT_INTREGS + 16 * 4]
+       stp     x10, x11, [x1, #SDEI_EVENT_INTREGS + 16 * 5]
+       stp     x12, x13, [x1, #SDEI_EVENT_INTREGS + 16 * 6]
+       stp     x14, x15, [x1, #SDEI_EVENT_INTREGS + 16 * 7]
+       stp     x16, x17, [x1, #SDEI_EVENT_INTREGS + 16 * 8]
+       stp     x18, x19, [x1, #SDEI_EVENT_INTREGS + 16 * 9]
+       stp     x20, x21, [x1, #SDEI_EVENT_INTREGS + 16 * 10]
+       stp     x22, x23, [x1, #SDEI_EVENT_INTREGS + 16 * 11]
+       stp     x24, x25, [x1, #SDEI_EVENT_INTREGS + 16 * 12]
+       stp     x26, x27, [x1, #SDEI_EVENT_INTREGS + 16 * 13]
+       stp     x28, x29, [x1, #SDEI_EVENT_INTREGS + 16 * 14]
+       mov     x4, sp
+       stp     lr, x4, [x1, #SDEI_EVENT_INTREGS + S_LR]
+
+       mov     x19, x1
+
+#ifdef CONFIG_VMAP_STACK
+       /*
+        * entry.S may have been using sp as a scratch register, find whether
+        * this is a normal or critical event and switch to the appropriate
+        * stack for this CPU.
+        */
+       ldrb    w4, [x19, #SDEI_EVENT_PRIORITY]
+       cbnz    w4, 1f
+       ldr_this_cpu dst=x5, sym=sdei_stack_normal_ptr, tmp=x6
+       b       2f
+1:     ldr_this_cpu dst=x5, sym=sdei_stack_critical_ptr, tmp=x6
+2:     mov     x6, #SDEI_STACK_SIZE
+       add     x5, x5, x6
+       mov     sp, x5
+#endif
+
+       /*
+        * We may have interrupted userspace, or a guest, or exit-from or
+        * return-to either of these. We can't trust sp_el0, restore it.
+        */
+       mrs     x28, sp_el0
+       ldr_this_cpu    dst=x0, sym=__entry_task, tmp=x1
+       msr     sp_el0, x0
+
+       /* If we interrupted the kernel point to the previous stack/frame. */
+       and     x0, x3, #0xc
+       mrs     x1, CurrentEL
+       cmp     x0, x1
+       csel    x29, x29, xzr, eq       // fp, or zero
+       csel    x4, x2, xzr, eq         // elr, or zero
+
+       stp     x29, x4, [sp, #-16]!
+       mov     x29, sp
+
+       add     x0, x19, #SDEI_EVENT_INTREGS
+       mov     x1, x19
+       bl      __sdei_handler
+
+       msr     sp_el0, x28
+       /* restore regs >x17 that we clobbered */
+       ldp     x28, x29, [x19, #SDEI_EVENT_INTREGS + 16 * 14]
+       ldp     lr, x4, [x19, #SDEI_EVENT_INTREGS + S_LR]
+       mov     sp, x4
+       ldp     x18, x19, [x19, #SDEI_EVENT_INTREGS + 16 * 9]
+
+       mov     x1, x0                  // address to complete_and_resume
+       /* x0 = (x0 <= 1) ? EVENT_COMPLETE:EVENT_COMPLETE_AND_RESUME */
+       cmp     x0, #1
+       mov_q   x2, SDEI_1_0_FN_SDEI_EVENT_COMPLETE
+       mov_q   x3, SDEI_1_0_FN_SDEI_EVENT_COMPLETE_AND_RESUME
+       csel    x0, x2, x3, ls
+
+       /* On success, this call never returns... */
+       ldr_l   x2, sdei_exit_mode
+       cmp     x2, #SDEI_EXIT_SMC
+       b.ne    1f
+       smc     #0
+       b       .
+1:     hvc     #0
+       b       .
+ENDPROC(__sdei_asm_handler)
+NOKPROBE(__sdei_asm_handler)
+#endif /* CONFIG_ARM_SDE_INTERFACE */