powerpc/8xx: Use SPRG2 and DAR registers to stash r11 and cr.
[safe/jmp/linux-2.6] / arch / powerpc / kernel / process.c
index 0a4eb08..e4d71ce 100644 (file)
 #include <linux/mqueue.h>
 #include <linux/hardirq.h>
 #include <linux/utsname.h>
+#include <linux/ftrace.h>
+#include <linux/kernel_stat.h>
+#include <linux/personality.h>
+#include <linux/random.h>
 
 #include <asm/pgtable.h>
 #include <asm/uaccess.h>
@@ -47,6 +51,8 @@
 #ifdef CONFIG_PPC64
 #include <asm/firmware.h>
 #endif
+#include <linux/kprobes.h>
+#include <linux/kdebug.h>
 
 extern unsigned long _get_SP(void);
 
@@ -159,6 +165,13 @@ void enable_kernel_vsx(void)
 EXPORT_SYMBOL(enable_kernel_vsx);
 #endif
 
+void giveup_vsx(struct task_struct *tsk)
+{
+       giveup_fpu(tsk);
+       giveup_altivec(tsk);
+       __giveup_vsx(tsk);
+}
+
 void flush_vsx_to_thread(struct task_struct *tsk)
 {
        if (tsk->thread.regs) {
@@ -232,21 +245,137 @@ void discard_lazy_cpu_state(void)
 }
 #endif /* CONFIG_SMP */
 
+#ifdef CONFIG_PPC_ADV_DEBUG_REGS
+void do_send_trap(struct pt_regs *regs, unsigned long address,
+                 unsigned long error_code, int signal_code, int breakpt)
+{
+       siginfo_t info;
+
+       if (notify_die(DIE_DABR_MATCH, "dabr_match", regs, error_code,
+                       11, SIGSEGV) == NOTIFY_STOP)
+               return;
+
+       /* Deliver the signal to userspace */
+       info.si_signo = SIGTRAP;
+       info.si_errno = breakpt;        /* breakpoint or watchpoint id */
+       info.si_code = signal_code;
+       info.si_addr = (void __user *)address;
+       force_sig_info(SIGTRAP, &info, current);
+}
+#else  /* !CONFIG_PPC_ADV_DEBUG_REGS */
+void do_dabr(struct pt_regs *regs, unsigned long address,
+                   unsigned long error_code)
+{
+       siginfo_t info;
+
+       if (notify_die(DIE_DABR_MATCH, "dabr_match", regs, error_code,
+                       11, SIGSEGV) == NOTIFY_STOP)
+               return;
+
+       if (debugger_dabr_match(regs))
+               return;
+
+       /* Clear the DABR */
+       set_dabr(0);
+
+       /* Deliver the signal to userspace */
+       info.si_signo = SIGTRAP;
+       info.si_errno = 0;
+       info.si_code = TRAP_HWBKPT;
+       info.si_addr = (void __user *)address;
+       force_sig_info(SIGTRAP, &info, current);
+}
+#endif /* CONFIG_PPC_ADV_DEBUG_REGS */
+
 static DEFINE_PER_CPU(unsigned long, current_dabr);
 
+#ifdef CONFIG_PPC_ADV_DEBUG_REGS
+/*
+ * Set the debug registers back to their default "safe" values.
+ */
+static void set_debug_reg_defaults(struct thread_struct *thread)
+{
+       thread->iac1 = thread->iac2 = 0;
+#if CONFIG_PPC_ADV_DEBUG_IACS > 2
+       thread->iac3 = thread->iac4 = 0;
+#endif
+       thread->dac1 = thread->dac2 = 0;
+#if CONFIG_PPC_ADV_DEBUG_DVCS > 0
+       thread->dvc1 = thread->dvc2 = 0;
+#endif
+       thread->dbcr0 = 0;
+#ifdef CONFIG_BOOKE
+       /*
+        * Force User/Supervisor bits to b11 (user-only MSR[PR]=1)
+        */
+       thread->dbcr1 = DBCR1_IAC1US | DBCR1_IAC2US |   \
+                       DBCR1_IAC3US | DBCR1_IAC4US;
+       /*
+        * Force Data Address Compare User/Supervisor bits to be User-only
+        * (0b11 MSR[PR]=1) and set all other bits in DBCR2 register to be 0.
+        */
+       thread->dbcr2 = DBCR2_DAC1US | DBCR2_DAC2US;
+#else
+       thread->dbcr1 = 0;
+#endif
+}
+
+static void prime_debug_regs(struct thread_struct *thread)
+{
+       mtspr(SPRN_IAC1, thread->iac1);
+       mtspr(SPRN_IAC2, thread->iac2);
+#if CONFIG_PPC_ADV_DEBUG_IACS > 2
+       mtspr(SPRN_IAC3, thread->iac3);
+       mtspr(SPRN_IAC4, thread->iac4);
+#endif
+       mtspr(SPRN_DAC1, thread->dac1);
+       mtspr(SPRN_DAC2, thread->dac2);
+#if CONFIG_PPC_ADV_DEBUG_DVCS > 0
+       mtspr(SPRN_DVC1, thread->dvc1);
+       mtspr(SPRN_DVC2, thread->dvc2);
+#endif
+       mtspr(SPRN_DBCR0, thread->dbcr0);
+       mtspr(SPRN_DBCR1, thread->dbcr1);
+#ifdef CONFIG_BOOKE
+       mtspr(SPRN_DBCR2, thread->dbcr2);
+#endif
+}
+/*
+ * Unless neither the old or new thread are making use of the
+ * debug registers, set the debug registers from the values
+ * stored in the new thread.
+ */
+static void switch_booke_debug_regs(struct thread_struct *new_thread)
+{
+       if ((current->thread.dbcr0 & DBCR0_IDM)
+               || (new_thread->dbcr0 & DBCR0_IDM))
+                       prime_debug_regs(new_thread);
+}
+#else  /* !CONFIG_PPC_ADV_DEBUG_REGS */
+static void set_debug_reg_defaults(struct thread_struct *thread)
+{
+       if (thread->dabr) {
+               thread->dabr = 0;
+               set_dabr(0);
+       }
+}
+#endif /* CONFIG_PPC_ADV_DEBUG_REGS */
+
 int set_dabr(unsigned long dabr)
 {
        __get_cpu_var(current_dabr) = dabr;
 
-#ifdef CONFIG_PPC_MERGE                /* XXX for now */
        if (ppc_md.set_dabr)
                return ppc_md.set_dabr(dabr);
-#endif
 
        /* XXX should we have a CPU_FTR_HAS_DABR ? */
-#if defined(CONFIG_PPC64) || defined(CONFIG_6xx)
+#ifdef CONFIG_PPC_ADV_DEBUG_REGS
+       mtspr(SPRN_DAC1, dabr);
+#elif defined(CONFIG_PPC_BOOK3S)
        mtspr(SPRN_DABR, dabr);
 #endif
+
+
        return 0;
 }
 
@@ -290,7 +419,8 @@ struct task_struct *__switch_to(struct task_struct *prev,
 #endif /* CONFIG_ALTIVEC */
 #ifdef CONFIG_VSX
        if (prev->thread.regs && (prev->thread.regs->msr & MSR_VSX))
-               giveup_vsx(prev);
+               /* VMX and FPU registers are already save here */
+               __giveup_vsx(prev);
 #endif /* CONFIG_VSX */
 #ifdef CONFIG_SPE
        /*
@@ -326,8 +456,13 @@ struct task_struct *__switch_to(struct task_struct *prev,
 
 #endif /* CONFIG_SMP */
 
+#ifdef CONFIG_PPC_ADV_DEBUG_REGS
+       switch_booke_debug_regs(&new->thread);
+#else
        if (unlikely(__get_cpu_var(current_dabr) != new->thread.dabr))
                set_dabr(new->thread.dabr);
+#endif
+
 
        new_thread = &new->thread;
        old_thread = &current->thread;
@@ -419,6 +554,8 @@ static struct regbit {
        {MSR_VEC,       "VEC"},
        {MSR_VSX,       "VSX"},
        {MSR_ME,        "ME"},
+       {MSR_CE,        "CE"},
+       {MSR_DE,        "DE"},
        {MSR_IR,        "IR"},
        {MSR_DR,        "DR"},
        {0,             NULL}
@@ -460,7 +597,7 @@ void show_regs(struct pt_regs * regs)
        printk("  CR: %08lx  XER: %08lx\n", regs->ccr, regs->xer);
        trap = TRAP(regs);
        if (trap == 0x300 || trap == 0x600)
-#if defined(CONFIG_4xx) || defined(CONFIG_BOOKE)
+#ifdef CONFIG_PPC_ADV_DEBUG_REGS
                printk("DEAR: "REG", ESR: "REG"\n", regs->dar, regs->dsisr);
 #else
                printk("DAR: "REG", DSISR: "REG"\n", regs->dar, regs->dsisr);
@@ -474,7 +611,7 @@ void show_regs(struct pt_regs * regs)
 
        for (i = 0;  i < 32;  i++) {
                if ((i % REGS_PER_LINE) == 0)
-                       printk("\n" KERN_INFO "GPR%02d: ", i);
+                       printk("\nGPR%02d: ", i);
                printk(REG " ", regs->gpr[i]);
                if (i == LAST_VOLATILE && !FULL_REGS(regs))
                        break;
@@ -500,24 +637,9 @@ void exit_thread(void)
 
 void flush_thread(void)
 {
-#ifdef CONFIG_PPC64
-       struct thread_info *t = current_thread_info();
-
-       if (test_ti_thread_flag(t, TIF_ABI_PENDING)) {
-               clear_ti_thread_flag(t, TIF_ABI_PENDING);
-               if (test_ti_thread_flag(t, TIF_32BIT))
-                       clear_ti_thread_flag(t, TIF_32BIT);
-               else
-                       set_ti_thread_flag(t, TIF_32BIT);
-       }
-#endif
-
        discard_lazy_cpu_state();
 
-       if (current->thread.dabr) {
-               current->thread.dabr = 0;
-               set_dabr(0);
-       }
+       set_debug_reg_defaults(&current->thread);
 }
 
 void
@@ -540,7 +662,7 @@ void prepare_to_copy(struct task_struct *tsk)
 /*
  * Copy a thread..
  */
-int copy_thread(int nr, unsigned long clone_flags, unsigned long usp,
+int copy_thread(unsigned long clone_flags, unsigned long usp,
                unsigned long unused, struct task_struct *p,
                struct pt_regs *regs)
 {
@@ -592,7 +714,7 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp,
        p->thread.ksp_limit = (unsigned long)task_stack_page(p) +
                                _ALIGN_UP(sizeof(struct thread_info), 16);
 
-#ifdef CONFIG_PPC64
+#ifdef CONFIG_PPC_STD_MMU_64
        if (cpu_has_feature(CPU_FTR_SLB)) {
                unsigned long sp_vsid;
                unsigned long llp = mmu_psize_defs[mmu_linear_psize].sllp;
@@ -606,6 +728,7 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp,
                sp_vsid |= SLB_VSID_KERNEL | llp;
                p->thread.ksp_vsid = sp_vsid;
        }
+#endif /* CONFIG_PPC_STD_MMU_64 */
 
        /*
         * The PPC64 ABI makes use of a TOC to contain function 
@@ -613,6 +736,7 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp,
         * to the TOC entry.  The first entry is a pointer to the actual
         * function.
         */
+#ifdef CONFIG_PPC64
        kregs->nip = *((unsigned long *)ret_from_fork);
 #else
        kregs->nip = (unsigned long)ret_from_fork;
@@ -946,13 +1070,25 @@ unsigned long get_wchan(struct task_struct *p)
        return 0;
 }
 
-static int kstack_depth_to_print = 64;
+static int kstack_depth_to_print = CONFIG_PRINT_STACK_DEPTH;
 
 void show_stack(struct task_struct *tsk, unsigned long *stack)
 {
        unsigned long sp, ip, lr, newsp;
        int count = 0;
        int firstframe = 1;
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+       int curr_frame = current->curr_ret_stack;
+       extern void return_to_handler(void);
+       unsigned long rth = (unsigned long)return_to_handler;
+       unsigned long mrth = -1;
+#ifdef CONFIG_PPC64
+       extern void mod_return_to_handler(void);
+       rth = *(unsigned long *)rth;
+       mrth = (unsigned long)mod_return_to_handler;
+       mrth = *(unsigned long *)mrth;
+#endif
+#endif
 
        sp = (unsigned long) stack;
        if (tsk == NULL)
@@ -975,6 +1111,13 @@ void show_stack(struct task_struct *tsk, unsigned long *stack)
                ip = stack[STACK_FRAME_LR_SAVE];
                if (!firstframe || ip != lr) {
                        printk("["REG"] ["REG"] %pS", sp, ip, (void *)ip);
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+                       if ((ip == rth || ip == mrth) && curr_frame >= 0) {
+                               printk(" (%pS)",
+                                      (void *)current->ret_stack[curr_frame].ret);
+                               curr_frame--;
+                       }
+#endif
                        if (firstframe)
                                printk(" (unreliable)");
                        printk("\n");
@@ -1067,3 +1210,58 @@ void thread_info_cache_init(void)
 }
 
 #endif /* THREAD_SHIFT < PAGE_SHIFT */
+
+unsigned long arch_align_stack(unsigned long sp)
+{
+       if (!(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space)
+               sp -= get_random_int() & ~PAGE_MASK;
+       return sp & ~0xf;
+}
+
+static inline unsigned long brk_rnd(void)
+{
+        unsigned long rnd = 0;
+
+       /* 8MB for 32bit, 1GB for 64bit */
+       if (is_32bit_task())
+               rnd = (long)(get_random_int() % (1<<(23-PAGE_SHIFT)));
+       else
+               rnd = (long)(get_random_int() % (1<<(30-PAGE_SHIFT)));
+
+       return rnd << PAGE_SHIFT;
+}
+
+unsigned long arch_randomize_brk(struct mm_struct *mm)
+{
+       unsigned long base = mm->brk;
+       unsigned long ret;
+
+#ifdef CONFIG_PPC_STD_MMU_64
+       /*
+        * If we are using 1TB segments and we are allowed to randomise
+        * the heap, we can put it above 1TB so it is backed by a 1TB
+        * segment. Otherwise the heap will be in the bottom 1TB
+        * which always uses 256MB segments and this may result in a
+        * performance penalty.
+        */
+       if (!is_32bit_task() && (mmu_highuser_ssize == MMU_SEGSIZE_1T))
+               base = max_t(unsigned long, mm->brk, 1UL << SID_SHIFT_1T);
+#endif
+
+       ret = PAGE_ALIGN(base + brk_rnd());
+
+       if (ret < mm->brk)
+               return mm->brk;
+
+       return ret;
+}
+
+unsigned long randomize_et_dyn(unsigned long base)
+{
+       unsigned long ret = PAGE_ALIGN(base + brk_rnd());
+
+       if (ret < base)
+               return base;
+
+       return ret;
+}