]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blobdiff - arch/mips/kernel/traps.c
[MIPS] Add support for MIPS CMP platform.
[mirror_ubuntu-zesty-kernel.git] / arch / mips / kernel / traps.c
index fcae6675297273839812a4977c329ee6cb9e5899..88185cd40c3b5eb04c1c6f2318df575f4db1400c 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/kallsyms.h>
 #include <linux/bootmem.h>
 #include <linux/interrupt.h>
+#include <linux/ptrace.h>
 
 #include <asm/bootinfo.h>
 #include <asm/branch.h>
@@ -80,19 +81,22 @@ void (*board_bind_eic_interrupt)(int irq, int regset);
 
 static void show_raw_backtrace(unsigned long reg29)
 {
-       unsigned long *sp = (unsigned long *)reg29;
+       unsigned long *sp = (unsigned long *)(reg29 & ~3);
        unsigned long addr;
 
        printk("Call Trace:");
 #ifdef CONFIG_KALLSYMS
        printk("\n");
 #endif
-       while (!kstack_end(sp)) {
-               addr = *sp++;
-               if (__kernel_text_address(addr))
-                       print_ip_sym(addr);
+#define IS_KVA01(a) ((((unsigned int)a) & 0xc0000000) == 0x80000000)
+       if (IS_KVA01(sp)) {
+               while (!kstack_end(sp)) {
+                       addr = *sp++;
+                       if (__kernel_text_address(addr))
+                               print_ip_sym(addr);
+               }
+               printk("\n");
        }
-       printk("\n");
 }
 
 #ifdef CONFIG_KALLSYMS
@@ -192,16 +196,19 @@ EXPORT_SYMBOL(dump_stack);
 static void show_code(unsigned int __user *pc)
 {
        long i;
+       unsigned short __user *pc16 = NULL;
 
        printk("\nCode:");
 
+       if ((unsigned long)pc & 1)
+               pc16 = (unsigned short __user *)((unsigned long)pc & ~1);
        for(i = -3 ; i < 6 ; i++) {
                unsigned int insn;
-               if (__get_user(insn, pc + i)) {
+               if (pc16 ? __get_user(insn, pc16 + i) : __get_user(insn, pc + i)) {
                        printk(" (Bad address in epc)\n");
                        break;
                }
-               printk("%c%08x%c", (i?' ':'<'), insn, (i?' ':'>'));
+               printk("%c%0*x%c", (i?' ':'<'), pc16 ? 4 : 8, insn, (i?' ':'>'));
        }
 }
 
@@ -311,10 +318,21 @@ void show_regs(struct pt_regs *regs)
 
 void show_registers(const struct pt_regs *regs)
 {
+       const int field = 2 * sizeof(unsigned long);
+
        __show_regs(regs);
        print_modules();
-       printk("Process %s (pid: %d, threadinfo=%p, task=%p)\n",
-               current->comm, task_pid_nr(current), current_thread_info(), current);
+       printk("Process %s (pid: %d, threadinfo=%p, task=%p, tls=%0*lx)\n",
+              current->comm, current->pid, current_thread_info(), current,
+             field, current_thread_info()->tp_value);
+       if (cpu_has_userlocal) {
+               unsigned long tls;
+
+               tls = read_c0_userlocal();
+               if (tls != current_thread_info()->tp_value)
+                       printk("*HwTLS: %0*lx\n", field, tls);
+       }
+
        show_stacktrace(current, regs);
        show_code((unsigned int __user *) regs->cp0_epc);
        printk("\n");
@@ -534,8 +552,7 @@ static int simulate_llsc(struct pt_regs *regs, unsigned int opcode)
 
 /*
  * Simulate trapping 'rdhwr' instructions to provide user accessible
- * registers not implemented in hardware.  The only current use of this
- * is the thread area pointer.
+ * registers not implemented in hardware.
  */
 static int simulate_rdhwr(struct pt_regs *regs, unsigned int opcode)
 {
@@ -545,11 +562,31 @@ static int simulate_rdhwr(struct pt_regs *regs, unsigned int opcode)
                int rd = (opcode & RD) >> 11;
                int rt = (opcode & RT) >> 16;
                switch (rd) {
-                       case 29:
-                               regs->regs[rt] = ti->tp_value;
-                               return 0;
+               case 0:         /* CPU number */
+                       regs->regs[rt] = smp_processor_id();
+                       return 0;
+               case 1:         /* SYNCI length */
+                       regs->regs[rt] = min(current_cpu_data.dcache.linesz,
+                                            current_cpu_data.icache.linesz);
+                       return 0;
+               case 2:         /* Read count register */
+                       regs->regs[rt] = read_c0_count();
+                       return 0;
+               case 3:         /* Count register resolution */
+                       switch (current_cpu_data.cputype) {
+                       case CPU_20KC:
+                       case CPU_25KF:
+                               regs->regs[rt] = 1;
+                               break;
                        default:
-                               return -1;
+                               regs->regs[rt] = 2;
+                       }
+                       return 0;
+               case 29:
+                       regs->regs[rt] = ti->tp_value;
+                       return 0;
+               default:
+                       return -1;
                }
        }
 
@@ -966,6 +1003,21 @@ asmlinkage void do_reserved(struct pt_regs *regs)
              (regs->cp0_cause & 0x7f) >> 2);
 }
 
+static int __initdata l1parity = 1;
+static int __init nol1parity(char *s)
+{
+       l1parity = 0;
+       return 1;
+}
+__setup("nol1par", nol1parity);
+static int __initdata l2parity = 1;
+static int __init nol2parity(char *s)
+{
+       l2parity = 0;
+       return 1;
+}
+__setup("nol2par", nol2parity);
+
 /*
  * Some MIPS CPUs can enable/disable for cache parity detection, but do
  * it different ways.
@@ -975,6 +1027,62 @@ static inline void parity_protection_init(void)
        switch (current_cpu_type()) {
        case CPU_24K:
        case CPU_34K:
+       case CPU_74K:
+       case CPU_1004K:
+               {
+#define ERRCTL_PE      0x80000000
+#define ERRCTL_L2P     0x00800000
+                       unsigned long errctl;
+                       unsigned int l1parity_present, l2parity_present;
+
+                       errctl = read_c0_ecc();
+                       errctl &= ~(ERRCTL_PE|ERRCTL_L2P);
+
+                       /* probe L1 parity support */
+                       write_c0_ecc(errctl | ERRCTL_PE);
+                       back_to_back_c0_hazard();
+                       l1parity_present = (read_c0_ecc() & ERRCTL_PE);
+
+                       /* probe L2 parity support */
+                       write_c0_ecc(errctl|ERRCTL_L2P);
+                       back_to_back_c0_hazard();
+                       l2parity_present = (read_c0_ecc() & ERRCTL_L2P);
+
+                       if (l1parity_present && l2parity_present) {
+                               if (l1parity)
+                                       errctl |= ERRCTL_PE;
+                               if (l1parity ^ l2parity)
+                                       errctl |= ERRCTL_L2P;
+                       } else if (l1parity_present) {
+                               if (l1parity)
+                                       errctl |= ERRCTL_PE;
+                       } else if (l2parity_present) {
+                               if (l2parity)
+                                       errctl |= ERRCTL_L2P;
+                       } else {
+                               /* No parity available */
+                       }
+
+                       printk(KERN_INFO "Writing ErrCtl register=%08lx\n", errctl);
+
+                       write_c0_ecc(errctl);
+                       back_to_back_c0_hazard();
+                       errctl = read_c0_ecc();
+                       printk(KERN_INFO "Readback ErrCtl register=%08lx\n", errctl);
+
+                       if (l1parity_present)
+                               printk(KERN_INFO "Cache parity protection %sabled\n",
+                                      (errctl & ERRCTL_PE) ? "en" : "dis");
+
+                       if (l2parity_present) {
+                               if (l1parity_present && l1parity)
+                                       errctl ^= ERRCTL_L2P;
+                               printk(KERN_INFO "L2 cache parity protection %sabled\n",
+                                      (errctl & ERRCTL_L2P) ? "en" : "dis");
+                       }
+               }
+               break;
+
        case CPU_5KC:
                write_c0_ecc(0x80000000);
                back_to_back_c0_hazard();
@@ -1287,7 +1395,18 @@ int cp0_compare_irq;
 int cp0_perfcount_irq;
 EXPORT_SYMBOL_GPL(cp0_perfcount_irq);
 
-void __init per_cpu_trap_init(void)
+static int __cpuinitdata noulri;
+
+static int __init ulri_disable(char *s)
+{
+       pr_info("Disabling ulri\n");
+       noulri = 1;
+
+       return 1;
+}
+__setup("noulri", ulri_disable);
+
+void __cpuinit per_cpu_trap_init(void)
 {
        unsigned int cpu = smp_processor_id();
        unsigned int status_set = ST0_CU0;
@@ -1323,16 +1442,14 @@ void __init per_cpu_trap_init(void)
        change_c0_status(ST0_CU|ST0_MX|ST0_RE|ST0_FR|ST0_BEV|ST0_TS|ST0_KX|ST0_SX|ST0_UX,
                         status_set);
 
-#ifdef CONFIG_CPU_MIPSR2
        if (cpu_has_mips_r2) {
                unsigned int enable = 0x0000000f;
 
-               if (cpu_has_userlocal)
+               if (!noulri && cpu_has_userlocal)
                        enable |= (1 << 29);
 
                write_c0_hwrena(enable);
        }
-#endif
 
 #ifdef CONFIG_MIPS_MT_SMTC
        if (!secondaryTC) {
@@ -1404,11 +1521,12 @@ void __init set_handler(unsigned long offset, void *addr, unsigned long size)
        flush_icache_range(ebase + offset, ebase + offset + size);
 }
 
-static char panic_null_cerr[] __initdata =
+static char panic_null_cerr[] __cpuinitdata =
        "Trying to set NULL cache error exception handler";
 
 /* Install uncached CPU exception handler */
-void __init set_uncached_handler(unsigned long offset, void *addr, unsigned long size)
+void __cpuinit set_uncached_handler(unsigned long offset, void *addr,
+       unsigned long size)
 {
 #ifdef CONFIG_32BIT
        unsigned long uncached_ebase = KSEG1ADDR(ebase);