]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - arch/arm64/kernel/entry.S
arm64: entry: Explicitly pass exception level to kernel_ventry macro
[mirror_ubuntu-artful-kernel.git] / arch / arm64 / kernel / entry.S
index b738880350f9e43b8e2f3234210bda153586caf5..9b53f5a5c4106574e3f2b55eb82edeff3d2318ec 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
        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  kernel_entry, el, regsize = 64
        .if     \regsize == 32
        mov     w0, w0                          // zero upper 32 bits of x0
        .endif
        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
@@ -125,7 +186,7 @@ alternative_if ARM64_HAS_PAN
 alternative_else_nop_endif
 
        .if     \el != 0
-       mrs     x21, ttbr0_el1
+       mrs     x21, ttbr1_el1
        tst     x21, #0xffff << 48              // 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
@@ -142,8 +203,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 +250,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 +259,7 @@ alternative_else_nop_endif
         * Cavium erratum 27456 (broadcast TLBI instructions may cause I-cache
         * corruption).
         */
-       post_ttbr0_update_workaround
+       post_ttbr_update_workaround
        .endif
 1:
        .if     \el != 0
@@ -259,20 +320,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 +343,9 @@ 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
+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 +369,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
  */
@@ -351,7 +433,8 @@ END(vectors)
        mov     x0, sp
        mov     x1, #\reason
        mrs     x2, esr_el1
-       b       bad_mode
+       bl      bad_mode
+       ASM_BUG()
        .endm
 
 el0_sync_invalid:
@@ -448,14 +531,16 @@ el1_sp_pc:
        mrs     x0, far_el1
        enable_dbg
        mov     x2, sp
-       b       do_sp_pc_abort
+       bl      do_sp_pc_abort
+       ASM_BUG()
 el1_undef:
        /*
         * Undefined instruction
         */
        enable_dbg
        mov     x0, sp
-       b       do_undefinstr
+       bl      do_undefinstr
+       ASM_BUG()
 el1_dbg:
        /*
         * Debug exception handling
@@ -473,7 +558,8 @@ el1_inv:
        mov     x0, sp
        mov     x2, x1
        mov     x1, #BAD_SYNC
-       b       bad_mode
+       bl      bad_mode
+       ASM_BUG()
 ENDPROC(el1_sync)
 
        .align  6
@@ -577,8 +663,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
@@ -706,38 +792,6 @@ el0_irq_naked:
        b       ret_to_user
 ENDPROC(el0_irq)
 
-/*
- * Register switch for AArch64. The callee-saved registers need to be saved
- * and restored. On entry:
- *   x0 = previous task_struct (must be preserved across the switch)
- *   x1 = next task_struct
- * Previous and next are guaranteed not to be the same.
- *
- */
-ENTRY(cpu_switch_to)
-       mov     x10, #THREAD_CPU_CONTEXT
-       add     x8, x0, x10
-       mov     x9, sp
-       stp     x19, x20, [x8], #16             // store callee-saved registers
-       stp     x21, x22, [x8], #16
-       stp     x23, x24, [x8], #16
-       stp     x25, x26, [x8], #16
-       stp     x27, x28, [x8], #16
-       stp     x29, x9, [x8], #16
-       str     lr, [x8]
-       add     x8, x1, x10
-       ldp     x19, x20, [x8], #16             // restore callee-saved registers
-       ldp     x21, x22, [x8], #16
-       ldp     x23, x24, [x8], #16
-       ldp     x25, x26, [x8], #16
-       ldp     x27, x28, [x8], #16
-       ldp     x29, x9, [x8], #16
-       ldr     lr, [x8]
-       mov     sp, x9
-       msr     sp_el0, x1
-       ret
-ENDPROC(cpu_switch_to)
-
 /*
  * This is the fast syscall return path.  We do as little as possible here,
  * and this includes saving x0 back into the kernel stack.
@@ -780,37 +834,25 @@ finish_ret_to_user:
        kernel_exit 0
 ENDPROC(ret_to_user)
 
-/*
- * This is how we return from a fork.
- */
-ENTRY(ret_from_fork)
-       bl      schedule_tail
-       cbz     x19, 1f                         // not a kernel thread
-       mov     x0, x20
-       blr     x19
-1:     get_thread_info tsk
-       b       ret_to_user
-ENDPROC(ret_from_fork)
-
 /*
  * SVC handler.
  */
        .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
+       ldr     x16, [stbl, xscno, lsl #3]      // address in the syscall table
        blr     x16                             // call sys_* routine
        b       ret_fast_syscall
 ni_sys:
@@ -824,24 +866,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:
@@ -858,6 +899,90 @@ __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
+       .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 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.
+        */
+       .endm
+
+       .macro tramp_ventry, regsize = 64
+       .align  7
+1:
+       .if     \regsize == 64
+       msr     tpidrro_el0, x30        // Restored in kernel_ventry
+       .endif
+       tramp_map_kernel        x30
+       ldr     x30, =vectors
+       prfm    plil1strm, [x30, #(1b - tramp_vectors)]
+       msr     vbar_el1, x30
+       add     x30, x30, #(1b - tramp_vectors)
+       isb
+       br      x30
+       .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
+#endif /* CONFIG_UNMAP_KERNEL_AT_EL0 */
+
 /*
  * Special system call wrappers.
  */
@@ -865,3 +990,49 @@ ENTRY(sys_rt_sigreturn_wrapper)
        mov     x0, sp
        b       sys_rt_sigreturn
 ENDPROC(sys_rt_sigreturn_wrapper)
+
+/*
+ * Register switch for AArch64. The callee-saved registers need to be saved
+ * and restored. On entry:
+ *   x0 = previous task_struct (must be preserved across the switch)
+ *   x1 = next task_struct
+ * Previous and next are guaranteed not to be the same.
+ *
+ */
+ENTRY(cpu_switch_to)
+       mov     x10, #THREAD_CPU_CONTEXT
+       add     x8, x0, x10
+       mov     x9, sp
+       stp     x19, x20, [x8], #16             // store callee-saved registers
+       stp     x21, x22, [x8], #16
+       stp     x23, x24, [x8], #16
+       stp     x25, x26, [x8], #16
+       stp     x27, x28, [x8], #16
+       stp     x29, x9, [x8], #16
+       str     lr, [x8]
+       add     x8, x1, x10
+       ldp     x19, x20, [x8], #16             // restore callee-saved registers
+       ldp     x21, x22, [x8], #16
+       ldp     x23, x24, [x8], #16
+       ldp     x25, x26, [x8], #16
+       ldp     x27, x28, [x8], #16
+       ldp     x29, x9, [x8], #16
+       ldr     lr, [x8]
+       mov     sp, x9
+       msr     sp_el0, x1
+       ret
+ENDPROC(cpu_switch_to)
+NOKPROBE(cpu_switch_to)
+
+/*
+ * This is how we return from a fork.
+ */
+ENTRY(ret_from_fork)
+       bl      schedule_tail
+       cbz     x19, 1f                         // not a kernel thread
+       mov     x0, x20
+       blr     x19
+1:     get_thread_info tsk
+       b       ret_to_user
+ENDPROC(ret_from_fork)
+NOKPROBE(ret_from_fork)