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