1/* 2 * Copyright (C) 2003, 2004 Maciej W. Rozycki 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License 6 * as published by the Free Software Foundation; either version 7 * 2 of the License, or (at your option) any later version. 8 */ 9#include <linux/init.h> 10#include <linux/kernel.h> 11#include <linux/ptrace.h> 12#include <linux/stddef.h> 13 14#include <asm/bugs.h> 15#include <asm/compiler.h> 16#include <asm/cpu.h> 17#include <asm/fpu.h> 18#include <asm/mipsregs.h> 19#include <asm/system.h> 20 21static inline void align_mod(const int align, const int mod) 22{ 23 asm volatile( 24 ".set push\n\t" 25 ".set noreorder\n\t" 26 ".balign %0\n\t" 27 ".rept %1\n\t" 28 "nop\n\t" 29 ".endr\n\t" 30 ".set pop" 31 : 32 : "n" (align), "n" (mod)); 33} 34 35static inline void mult_sh_align_mod(long *v1, long *v2, long *w, 36 const int align, const int mod) 37{ 38 unsigned long flags; 39 int m1, m2; 40 long p, s, lv1, lv2, lw; 41 42 /* 43 * We want the multiply and the shift to be isolated from the 44 * rest of the code to disable gcc optimizations. Hence the 45 * asm statements that execute nothing, but make gcc not know 46 * what the values of m1, m2 and s are and what lv2 and p are 47 * used for. 48 */ 49 50 local_irq_save(flags); 51 /* 52 * The following code leads to a wrong result of the first 53 * dsll32 when executed on R4000 rev. 2.2 or 3.0 (PRId 54 * 00000422 or 00000430, respectively). 55 * 56 * See "MIPS R4000PC/SC Errata, Processor Revision 2.2 and 57 * 3.0" by MIPS Technologies, Inc., errata #16 and #28 for 58 * details. I got no permission to duplicate them here, 59 * sigh... --macro 60 */ 61 asm volatile( 62 "" 63 : "=r" (m1), "=r" (m2), "=r" (s) 64 : "0" (5), "1" (8), "2" (5)); 65 align_mod(align, mod); 66 /* 67 * The trailing nop is needed to fullfill the two-instruction 68 * requirement between reading hi/lo and staring a mult/div. 69 * Leaving it out may cause gas insert a nop itself breaking 70 * the desired alignment of the next chunk. 71 */ 72 asm volatile( 73 ".set push\n\t" 74 ".set noat\n\t" 75 ".set noreorder\n\t" 76 ".set nomacro\n\t" 77 "mult %2, %3\n\t" 78 "dsll32 %0, %4, %5\n\t" 79 "mflo $0\n\t" 80 "dsll32 %1, %4, %5\n\t" 81 "nop\n\t" 82 ".set pop" 83 : "=&r" (lv1), "=r" (lw) 84 : "r" (m1), "r" (m2), "r" (s), "I" (0) 85 : "hi", "lo", GCC_REG_ACCUM); 86 asm volatile( 87 "" 88 : "=r" (m1), "=r" (m2), "=r" (s) 89 : "0" (m1), "1" (m2), "2" (s)); 90 align_mod(align, mod); 91 p = m1 * m2; 92 lv2 = s << 32; 93 asm volatile( 94 "" 95 : "=r" (lv2) 96 : "0" (lv2), "r" (p)); 97 local_irq_restore(flags); 98 99 *v1 = lv1; 100 *v2 = lv2; 101 *w = lw; 102} 103 104static inline void check_mult_sh(void) 105{ 106 long v1[8], v2[8], w[8]; 107 int bug, fix, i; 108 109 printk("Checking for the multiply/shift bug... "); 110 111 /* 112 * Testing discovered false negatives for certain code offsets 113 * into cache lines. Hence we test all possible offsets for 114 * the worst assumption of an R4000 I-cache line width of 32 115 * bytes. 116 * 117 * We can't use a loop as alignment directives need to be 118 * immediates. 119 */ 120 mult_sh_align_mod(&v1[0], &v2[0], &w[0], 32, 0); 121 mult_sh_align_mod(&v1[1], &v2[1], &w[1], 32, 1); 122 mult_sh_align_mod(&v1[2], &v2[2], &w[2], 32, 2); 123 mult_sh_align_mod(&v1[3], &v2[3], &w[3], 32, 3); 124 mult_sh_align_mod(&v1[4], &v2[4], &w[4], 32, 4); 125 mult_sh_align_mod(&v1[5], &v2[5], &w[5], 32, 5); 126 mult_sh_align_mod(&v1[6], &v2[6], &w[6], 32, 6); 127 mult_sh_align_mod(&v1[7], &v2[7], &w[7], 32, 7); 128 129 bug = 0; 130 for (i = 0; i < 8; i++) 131 if (v1[i] != w[i]) 132 bug = 1; 133 134 if (bug == 0) { 135 printk("no.\n"); 136 return; 137 } 138 139 printk("yes, workaround... "); 140 141 fix = 1; 142 for (i = 0; i < 8; i++) 143 if (v2[i] != w[i]) 144 fix = 0; 145 146 if (fix == 1) { 147 printk("yes.\n"); 148 return; 149 } 150 151 printk("no.\n"); 152 panic("Reliable operation impossible!\n" 153#ifndef CONFIG_CPU_R4000 154 "Configure for R4000 to enable the workaround." 155#else 156 "Please report to <linux-mips@linux-mips.org>." 157#endif 158 ); 159} 160 161static volatile int daddi_ov __initdata = 0; 162 163asmlinkage void __init do_daddi_ov(struct pt_regs *regs) 164{ 165 daddi_ov = 1; 166 regs->cp0_epc += 4; 167} 168 169static inline void check_daddi(void) 170{ 171 extern asmlinkage void handle_daddi_ov(void); 172 unsigned long flags; 173 void *handler; 174 long v, tmp; 175 176 printk("Checking for the daddi bug... "); 177 178 local_irq_save(flags); 179 handler = set_except_vector(12, handle_daddi_ov); 180 /* 181 * The following code fails to trigger an overflow exception 182 * when executed on R4000 rev. 2.2 or 3.0 (PRId 00000422 or 183 * 00000430, respectively). 184 * 185 * See "MIPS R4000PC/SC Errata, Processor Revision 2.2 and 186 * 3.0" by MIPS Technologies, Inc., erratum #23 for details. 187 * I got no permission to duplicate it here, sigh... --macro 188 */ 189 asm volatile( 190 ".set push\n\t" 191 ".set noat\n\t" 192 ".set noreorder\n\t" 193 ".set nomacro\n\t" 194 "addiu %1, $0, %2\n\t" 195 "dsrl %1, %1, 1\n\t" 196#ifdef HAVE_AS_SET_DADDI 197 ".set daddi\n\t" 198#endif 199 "daddi %0, %1, %3\n\t" 200 ".set pop" 201 : "=r" (v), "=&r" (tmp) 202 : "I" (0xffffffffffffdb9aUL), "I" (0x1234)); 203 set_except_vector(12, handler); 204 local_irq_restore(flags); 205 206 if (daddi_ov) { 207 printk("no.\n"); 208 return; 209 } 210 211 printk("yes, workaround... "); 212 213 local_irq_save(flags); 214 handler = set_except_vector(12, handle_daddi_ov); 215 asm volatile( 216 "addiu %1, $0, %2\n\t" 217 "dsrl %1, %1, 1\n\t" 218 "daddi %0, %1, %3" 219 : "=r" (v), "=&r" (tmp) 220 : "I" (0xffffffffffffdb9aUL), "I" (0x1234)); 221 set_except_vector(12, handler); 222 local_irq_restore(flags); 223 224 if (daddi_ov) { 225 printk("yes.\n"); 226 return; 227 } 228 229 printk("no.\n"); 230 panic("Reliable operation impossible!\n" 231#if !defined(CONFIG_CPU_R4000) && !defined(CONFIG_CPU_R4400) 232 "Configure for R4000 or R4400 to enable the workaround." 233#else 234 "Please report to <linux-mips@linux-mips.org>." 235#endif 236 ); 237} 238 239static inline void check_daddiu(void) 240{ 241 long v, w, tmp; 242 243 printk("Checking for the daddiu bug... "); 244 245 /* 246 * The following code leads to a wrong result of daddiu when 247 * executed on R4400 rev. 1.0 (PRId 00000440). 248 * 249 * See "MIPS R4400PC/SC Errata, Processor Revision 1.0" by 250 * MIPS Technologies, Inc., erratum #7 for details. 251 * 252 * According to "MIPS R4000PC/SC Errata, Processor Revision 253 * 2.2 and 3.0" by MIPS Technologies, Inc., erratum #41 this 254 * problem affects R4000 rev. 2.2 and 3.0 (PRId 00000422 and 255 * 00000430, respectively), too. Testing failed to trigger it 256 * so far. 257 * 258 * I got no permission to duplicate the errata here, sigh... 259 * --macro 260 */ 261 asm volatile( 262 ".set push\n\t" 263 ".set noat\n\t" 264 ".set noreorder\n\t" 265 ".set nomacro\n\t" 266 "addiu %2, $0, %3\n\t" 267 "dsrl %2, %2, 1\n\t" 268#ifdef HAVE_AS_SET_DADDI 269 ".set daddi\n\t" 270#endif 271 "daddiu %0, %2, %4\n\t" 272 "addiu %1, $0, %4\n\t" 273 "daddu %1, %2\n\t" 274 ".set pop" 275 : "=&r" (v), "=&r" (w), "=&r" (tmp) 276 : "I" (0xffffffffffffdb9aUL), "I" (0x1234)); 277 278 if (v == w) { 279 printk("no.\n"); 280 return; 281 } 282 283 printk("yes, workaround... "); 284 285 asm volatile( 286 "addiu %2, $0, %3\n\t" 287 "dsrl %2, %2, 1\n\t" 288 "daddiu %0, %2, %4\n\t" 289 "addiu %1, $0, %4\n\t" 290 "daddu %1, %2" 291 : "=&r" (v), "=&r" (w), "=&r" (tmp) 292 : "I" (0xffffffffffffdb9aUL), "I" (0x1234)); 293 294 if (v == w) { 295 printk("yes.\n"); 296 return; 297 } 298 299 printk("no.\n"); 300 panic("Reliable operation impossible!\n" 301#if !defined(CONFIG_CPU_R4000) && !defined(CONFIG_CPU_R4400) 302 "Configure for R4000 or R4400 to enable the workaround." 303#else 304 "Please report to <linux-mips@linux-mips.org>." 305#endif 306 ); 307} 308 309void __init check_bugs64(void) 310{ 311 check_mult_sh(); 312 check_daddi(); 313 check_daddiu(); 314} 315