]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - arch/x86/kernel/entry_64.S
Merge branch 'tip/perf/core' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt...
[mirror_ubuntu-artful-kernel.git] / arch / x86 / kernel / entry_64.S
index 320852d02026171d537b58bd95868113fa458d10..459d4a0dca8dfae3c247858b2cf22be91be392a8 100644 (file)
@@ -73,20 +73,34 @@ ENTRY(mcount)
        retq
 END(mcount)
 
+/* skip is set if stack has been adjusted */
+.macro ftrace_caller_setup skip=0
+       MCOUNT_SAVE_FRAME \skip
+
+       /* Load the ftrace_ops into the 3rd parameter */
+       leaq function_trace_op, %rdx
+
+       /* Load ip into the first parameter */
+       movq RIP(%rsp), %rdi
+       subq $MCOUNT_INSN_SIZE, %rdi
+       /* Load the parent_ip into the second parameter */
+       movq 8(%rbp), %rsi
+.endm
+
 ENTRY(ftrace_caller)
+       /* Check if tracing was disabled (quick check) */
        cmpl $0, function_trace_stop
        jne  ftrace_stub
 
-       MCOUNT_SAVE_FRAME
-
-       movq 0x38(%rsp), %rdi
-       movq 8(%rbp), %rsi
-       subq $MCOUNT_INSN_SIZE, %rdi
+       ftrace_caller_setup
+       /* regs go into 4th parameter (but make it NULL) */
+       movq $0, %rcx
 
 GLOBAL(ftrace_call)
        call ftrace_stub
 
        MCOUNT_RESTORE_FRAME
+ftrace_return:
 
 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
 GLOBAL(ftrace_graph_call)
@@ -97,6 +111,71 @@ GLOBAL(ftrace_stub)
        retq
 END(ftrace_caller)
 
+ENTRY(ftrace_regs_caller)
+       /* Save the current flags before compare (in SS location)*/
+       pushfq
+
+       /* Check if tracing was disabled (quick check) */
+       cmpl $0, function_trace_stop
+       jne  ftrace_restore_flags
+
+       /* skip=8 to skip flags saved in SS */
+       ftrace_caller_setup 8
+
+       /* Save the rest of pt_regs */
+       movq %r15, R15(%rsp)
+       movq %r14, R14(%rsp)
+       movq %r13, R13(%rsp)
+       movq %r12, R12(%rsp)
+       movq %r11, R11(%rsp)
+       movq %r10, R10(%rsp)
+       movq %rbp, RBP(%rsp)
+       movq %rbx, RBX(%rsp)
+       /* Copy saved flags */
+       movq SS(%rsp), %rcx
+       movq %rcx, EFLAGS(%rsp)
+       /* Kernel segments */
+       movq $__KERNEL_DS, %rcx
+       movq %rcx, SS(%rsp)
+       movq $__KERNEL_CS, %rcx
+       movq %rcx, CS(%rsp)
+       /* Stack - skipping return address */
+       leaq SS+16(%rsp), %rcx
+       movq %rcx, RSP(%rsp)
+
+       /* regs go into 4th parameter */
+       leaq (%rsp), %rcx
+
+GLOBAL(ftrace_regs_call)
+       call ftrace_stub
+
+       /* Copy flags back to SS, to restore them */
+       movq EFLAGS(%rsp), %rax
+       movq %rax, SS(%rsp)
+
+       /* restore the rest of pt_regs */
+       movq R15(%rsp), %r15
+       movq R14(%rsp), %r14
+       movq R13(%rsp), %r13
+       movq R12(%rsp), %r12
+       movq R10(%rsp), %r10
+       movq RBP(%rsp), %rbp
+       movq RBX(%rsp), %rbx
+
+       /* skip=8 to skip flags saved in SS */
+       MCOUNT_RESTORE_FRAME 8
+
+       /* Restore flags */
+       popfq
+
+       jmp ftrace_return
+ftrace_restore_flags:
+       popfq
+       jmp  ftrace_stub
+
+END(ftrace_regs_caller)
+
+
 #else /* ! CONFIG_DYNAMIC_FTRACE */
 ENTRY(mcount)
        cmpl $0, function_trace_stop
@@ -119,7 +198,7 @@ GLOBAL(ftrace_stub)
 trace:
        MCOUNT_SAVE_FRAME
 
-       movq 0x38(%rsp), %rdi
+       movq RIP(%rsp), %rdi
        movq 8(%rbp), %rsi
        subq $MCOUNT_INSN_SIZE, %rdi
 
@@ -134,13 +213,10 @@ END(mcount)
 
 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
 ENTRY(ftrace_graph_caller)
-       cmpl $0, function_trace_stop
-       jne ftrace_stub
-
        MCOUNT_SAVE_FRAME
 
        leaq 8(%rbp), %rdi
-       movq 0x38(%rsp), %rsi
+       movq RIP(%rsp), %rsi
        movq (%rbp), %rdx
        subq $MCOUNT_INSN_SIZE, %rsi
 
@@ -190,6 +266,44 @@ ENDPROC(native_usergs_sysret64)
 #endif
 .endm
 
+/*
+ * When dynamic function tracer is enabled it will add a breakpoint
+ * to all locations that it is about to modify, sync CPUs, update
+ * all the code, sync CPUs, then remove the breakpoints. In this time
+ * if lockdep is enabled, it might jump back into the debug handler
+ * outside the updating of the IST protection. (TRACE_IRQS_ON/OFF).
+ *
+ * We need to change the IDT table before calling TRACE_IRQS_ON/OFF to
+ * make sure the stack pointer does not get reset back to the top
+ * of the debug stack, and instead just reuses the current stack.
+ */
+#if defined(CONFIG_DYNAMIC_FTRACE) && defined(CONFIG_TRACE_IRQFLAGS)
+
+.macro TRACE_IRQS_OFF_DEBUG
+       call debug_stack_set_zero
+       TRACE_IRQS_OFF
+       call debug_stack_reset
+.endm
+
+.macro TRACE_IRQS_ON_DEBUG
+       call debug_stack_set_zero
+       TRACE_IRQS_ON
+       call debug_stack_reset
+.endm
+
+.macro TRACE_IRQS_IRETQ_DEBUG offset=ARGOFFSET
+       bt   $9,EFLAGS-\offset(%rsp)    /* interrupts off? */
+       jnc  1f
+       TRACE_IRQS_ON_DEBUG
+1:
+.endm
+
+#else
+# define TRACE_IRQS_OFF_DEBUG          TRACE_IRQS_OFF
+# define TRACE_IRQS_ON_DEBUG           TRACE_IRQS_ON
+# define TRACE_IRQS_IRETQ_DEBUG                TRACE_IRQS_IRETQ
+#endif
+
 /*
  * C code is not supposed to know about undefined top of stack. Every time
  * a C function with an pt_regs argument is called from the SYSCALL based
@@ -1098,7 +1212,7 @@ ENTRY(\sym)
        subq $ORIG_RAX-R15, %rsp
        CFI_ADJUST_CFA_OFFSET ORIG_RAX-R15
        call save_paranoid
-       TRACE_IRQS_OFF
+       TRACE_IRQS_OFF_DEBUG
        movq %rsp,%rdi          /* pt_regs pointer */
        xorl %esi,%esi          /* no error code */
        subq $EXCEPTION_STKSZ, INIT_TSS_IST(\ist)
@@ -1393,7 +1507,7 @@ paranoidzeroentry machine_check *machine_check_vector(%rip)
 ENTRY(paranoid_exit)
        DEFAULT_FRAME
        DISABLE_INTERRUPTS(CLBR_NONE)
-       TRACE_IRQS_OFF
+       TRACE_IRQS_OFF_DEBUG
        testl %ebx,%ebx                         /* swapgs needed? */
        jnz paranoid_restore
        testl $3,CS(%rsp)
@@ -1404,7 +1518,7 @@ paranoid_swapgs:
        RESTORE_ALL 8
        jmp irq_return
 paranoid_restore:
-       TRACE_IRQS_IRETQ 0
+       TRACE_IRQS_IRETQ_DEBUG 0
        RESTORE_ALL 8
        jmp irq_return
 paranoid_userspace:
@@ -1720,10 +1834,30 @@ end_repeat_nmi:
         */
        call save_paranoid
        DEFAULT_FRAME 0
+
+       /*
+        * Save off the CR2 register. If we take a page fault in the NMI then
+        * it could corrupt the CR2 value. If the NMI preempts a page fault
+        * handler before it was able to read the CR2 register, and then the
+        * NMI itself takes a page fault, the page fault that was preempted
+        * will read the information from the NMI page fault and not the
+        * origin fault. Save it off and restore it if it changes.
+        * Use the r12 callee-saved register.
+        */
+       movq %cr2, %r12
+
        /* paranoidentry do_nmi, 0; without TRACE_IRQS_OFF */
        movq %rsp,%rdi
        movq $-1,%rsi
        call do_nmi
+
+       /* Did the NMI take a page fault? Restore cr2 if it did */
+       movq %cr2, %rcx
+       cmpq %rcx, %r12
+       je 1f
+       movq %r12, %cr2
+1:
+       
        testl %ebx,%ebx                         /* swapgs needed? */
        jnz nmi_restore
 nmi_swapgs: