powerpc: tm: Enable transactional memory (TM) lazily for userspace
[cascardo/linux.git] / arch / powerpc / kernel / traps.c
index 2cb5892..a1f8f56 100644 (file)
@@ -25,7 +25,8 @@
 #include <linux/user.h>
 #include <linux/interrupt.h>
 #include <linux/init.h>
-#include <linux/module.h>
+#include <linux/extable.h>
+#include <linux/module.h>      /* print_modules */
 #include <linux/prctl.h>
 #include <linux/delay.h>
 #include <linux/kprobes.h>
@@ -116,7 +117,7 @@ static int die_owner = -1;
 static unsigned int die_nest_count;
 static int die_counter;
 
-static unsigned __kprobes long oops_begin(struct pt_regs *regs)
+static unsigned long oops_begin(struct pt_regs *regs)
 {
        int cpu;
        unsigned long flags;
@@ -143,8 +144,9 @@ static unsigned __kprobes long oops_begin(struct pt_regs *regs)
                pmac_backlight_unblank();
        return flags;
 }
+NOKPROBE_SYMBOL(oops_begin);
 
-static void __kprobes oops_end(unsigned long flags, struct pt_regs *regs,
+static void oops_end(unsigned long flags, struct pt_regs *regs,
                               int signr)
 {
        bust_spinlocks(0);
@@ -195,8 +197,9 @@ static void __kprobes oops_end(unsigned long flags, struct pt_regs *regs,
                panic("Fatal exception");
        do_exit(signr);
 }
+NOKPROBE_SYMBOL(oops_end);
 
-static int __kprobes __die(const char *str, struct pt_regs *regs, long err)
+static int __die(const char *str, struct pt_regs *regs, long err)
 {
        printk("Oops: %s, sig: %ld [#%d]\n", str, err, ++die_counter);
 #ifdef CONFIG_PREEMPT
@@ -220,6 +223,7 @@ static int __kprobes __die(const char *str, struct pt_regs *regs, long err)
 
        return 0;
 }
+NOKPROBE_SYMBOL(__die);
 
 void die(const char *str, struct pt_regs *regs, long err)
 {
@@ -801,7 +805,7 @@ void RunModeException(struct pt_regs *regs)
        _exception(SIGTRAP, regs, 0, 0);
 }
 
-void __kprobes single_step_exception(struct pt_regs *regs)
+void single_step_exception(struct pt_regs *regs)
 {
        enum ctx_state prev_state = exception_enter();
 
@@ -818,6 +822,7 @@ void __kprobes single_step_exception(struct pt_regs *regs)
 bail:
        exception_exit(prev_state);
 }
+NOKPROBE_SYMBOL(single_step_exception);
 
 /*
  * After we have successfully emulated an instruction, we have to
@@ -1139,7 +1144,7 @@ static int emulate_math(struct pt_regs *regs)
 static inline int emulate_math(struct pt_regs *regs) { return -1; }
 #endif
 
-void __kprobes program_check_exception(struct pt_regs *regs)
+void program_check_exception(struct pt_regs *regs)
 {
        enum ctx_state prev_state = exception_enter();
        unsigned int reason = get_reason(regs);
@@ -1259,16 +1264,18 @@ sigill:
 bail:
        exception_exit(prev_state);
 }
+NOKPROBE_SYMBOL(program_check_exception);
 
 /*
  * This occurs when running in hypervisor mode on POWER6 or later
  * and an illegal instruction is encountered.
  */
-void __kprobes emulation_assist_interrupt(struct pt_regs *regs)
+void emulation_assist_interrupt(struct pt_regs *regs)
 {
        regs->msr |= REASON_ILLEGAL;
        program_check_exception(regs);
 }
+NOKPROBE_SYMBOL(emulation_assist_interrupt);
 
 void alignment_exception(struct pt_regs *regs)
 {
@@ -1309,6 +1316,18 @@ bail:
        exception_exit(prev_state);
 }
 
+void slb_miss_bad_addr(struct pt_regs *regs)
+{
+       enum ctx_state prev_state = exception_enter();
+
+       if (user_mode(regs))
+               _exception(SIGSEGV, regs, SEGV_BNDERR, regs->dar);
+       else
+               bad_page_fault(regs, regs->dar, SIGSEGV);
+
+       exception_exit(prev_state);
+}
+
 void StackOverflow(struct pt_regs *regs)
 {
        printk(KERN_CRIT "Kernel stack overflow in process %p, r1=%lx\n",
@@ -1371,6 +1390,22 @@ void vsx_unavailable_exception(struct pt_regs *regs)
 }
 
 #ifdef CONFIG_PPC64
+static void tm_unavailable(struct pt_regs *regs)
+{
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+       if (user_mode(regs)) {
+               current->thread.load_tm++;
+               regs->msr |= MSR_TM;
+               tm_enable();
+               tm_restore_sprs(&current->thread);
+               return;
+       }
+#endif
+       pr_emerg("Unrecoverable TM Unavailable Exception "
+                       "%lx at %lx\n", regs->trap, regs->nip);
+       die("Unrecoverable TM Unavailable Exception", regs, SIGABRT);
+}
+
 void facility_unavailable_exception(struct pt_regs *regs)
 {
        static char *facility_strings[] = {
@@ -1450,6 +1485,27 @@ void facility_unavailable_exception(struct pt_regs *regs)
                return;
        }
 
+       if (status == FSCR_TM_LG) {
+               /*
+                * If we're here then the hardware is TM aware because it
+                * generated an exception with FSRM_TM set.
+                *
+                * If cpu_has_feature(CPU_FTR_TM) is false, then either firmware
+                * told us not to do TM, or the kernel is not built with TM
+                * support.
+                *
+                * If both of those things are true, then userspace can spam the
+                * console by triggering the printk() below just by continually
+                * doing tbegin (or any TM instruction). So in that case just
+                * send the process a SIGILL immediately.
+                */
+               if (!cpu_has_feature(CPU_FTR_TM))
+                       goto out;
+
+               tm_unavailable(regs);
+               return;
+       }
+
        if ((status < ARRAY_SIZE(facility_strings)) &&
            facility_strings[status])
                facility = facility_strings[status];
@@ -1462,6 +1518,7 @@ void facility_unavailable_exception(struct pt_regs *regs)
                "%sFacility '%s' unavailable, exception at 0x%lx, MSR=%lx\n",
                hv ? "Hypervisor " : "", facility, regs->nip, regs->msr);
 
+out:
        if (user_mode(regs)) {
                _exception(SIGILL, regs, ILL_ILLOPC, regs->nip);
                return;
@@ -1503,7 +1560,8 @@ void fp_unavailable_tm(struct pt_regs *regs)
 
        /* If VMX is in use, get the transactional values back */
        if (regs->msr & MSR_VEC) {
-               do_load_up_transact_altivec(&current->thread);
+               msr_check_and_set(MSR_VEC);
+               load_vr_state(&current->thread.vr_state);
                /* At this point all the VSX state is loaded, so enable it */
                regs->msr |= MSR_VSX;
        }
@@ -1524,7 +1582,8 @@ void altivec_unavailable_tm(struct pt_regs *regs)
        current->thread.used_vr = 1;
 
        if (regs->msr & MSR_FP) {
-               do_load_up_transact_fpu(&current->thread);
+               msr_check_and_set(MSR_FP);
+               load_fp_state(&current->thread.fp_state);
                regs->msr |= MSR_VSX;
        }
 }
@@ -1563,10 +1622,12 @@ void vsx_unavailable_tm(struct pt_regs *regs)
         */
        tm_recheckpoint(&current->thread, regs->msr & ~orig_msr);
 
+       msr_check_and_set(orig_msr & (MSR_FP | MSR_VEC));
+
        if (orig_msr & MSR_FP)
-               do_load_up_transact_fpu(&current->thread);
+               load_fp_state(&current->thread.fp_state);
        if (orig_msr & MSR_VEC)
-               do_load_up_transact_altivec(&current->thread);
+               load_vr_state(&current->thread.vr_state);
 }
 #endif /* CONFIG_PPC_TRANSACTIONAL_MEM */
 
@@ -1655,7 +1716,7 @@ static void handle_debug(struct pt_regs *regs, unsigned long debug_status)
                mtspr(SPRN_DBCR0, current->thread.debug.dbcr0);
 }
 
-void __kprobes DebugException(struct pt_regs *regs, unsigned long debug_status)
+void DebugException(struct pt_regs *regs, unsigned long debug_status)
 {
        current->thread.debug.dbsr = debug_status;
 
@@ -1716,6 +1777,7 @@ void __kprobes DebugException(struct pt_regs *regs, unsigned long debug_status)
        } else
                handle_debug(regs, debug_status);
 }
+NOKPROBE_SYMBOL(DebugException);
 #endif /* CONFIG_PPC_ADV_DEBUG_REGS */
 
 #if !defined(CONFIG_TAU_INT)