0001
0002
0003
0004
0005 #include <linux/context_tracking.h>
0006 #include <linux/init.h>
0007 #include <linux/kernel.h>
0008 #include <linux/ptrace.h>
0009 #include <linux/stddef.h>
0010
0011 #include <asm/bugs.h>
0012 #include <asm/compiler.h>
0013 #include <asm/cpu.h>
0014 #include <asm/fpu.h>
0015 #include <asm/mipsregs.h>
0016 #include <asm/setup.h>
0017
0018 static char bug64hit[] __initdata =
0019 "reliable operation impossible!\n%s";
0020 static char nowar[] __initdata =
0021 "Please report to <linux-mips@vger.kernel.org>.";
0022 static char r4kwar[] __initdata =
0023 "Enable CPU_R4000_WORKAROUNDS to rectify.";
0024 static char daddiwar[] __initdata =
0025 "Enable CPU_DADDI_WORKAROUNDS to rectify.";
0026
0027 static __always_inline __init
0028 void align_mod(const int align, const int mod)
0029 {
0030 asm volatile(
0031 ".set push\n\t"
0032 ".set noreorder\n\t"
0033 ".balign %0\n\t"
0034 ".rept %1\n\t"
0035 "nop\n\t"
0036 ".endr\n\t"
0037 ".set pop"
0038 :
0039 : "n"(align), "n"(mod));
0040 }
0041
0042 static __always_inline __init
0043 void mult_sh_align_mod(long *v1, long *v2, long *w,
0044 const int align, const int mod)
0045 {
0046 unsigned long flags;
0047 int m1, m2;
0048 long p, s, lv1, lv2, lw;
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058 local_irq_save(flags);
0059
0060
0061
0062
0063
0064
0065
0066
0067
0068
0069 asm volatile(
0070 ""
0071 : "=r" (m1), "=r" (m2), "=r" (s)
0072 : "0" (5), "1" (8), "2" (5));
0073 align_mod(align, mod);
0074
0075
0076
0077
0078
0079
0080 asm volatile(
0081 ".set push\n\t"
0082 ".set noat\n\t"
0083 ".set noreorder\n\t"
0084 ".set nomacro\n\t"
0085 "mult %2, %3\n\t"
0086 "dsll32 %0, %4, %5\n\t"
0087 "mflo $0\n\t"
0088 "dsll32 %1, %4, %5\n\t"
0089 "nop\n\t"
0090 ".set pop"
0091 : "=&r" (lv1), "=r" (lw)
0092 : "r" (m1), "r" (m2), "r" (s), "I" (0)
0093 : "hi", "lo", "$0");
0094
0095
0096
0097
0098
0099
0100 asm volatile(
0101 ""
0102 : "=r" (m1), "=r" (m2), "=r" (s)
0103 : "0" (m1), "1" (m2), "2" (s));
0104 align_mod(align, mod);
0105 p = m1 * m2;
0106 lv2 = s << 32;
0107 asm volatile(
0108 ""
0109 : "=r" (lv2)
0110 : "0" (lv2), "r" (p));
0111 local_irq_restore(flags);
0112
0113 *v1 = lv1;
0114 *v2 = lv2;
0115 *w = lw;
0116 }
0117
0118 static __always_inline __init void check_mult_sh(void)
0119 {
0120 long v1[8], v2[8], w[8];
0121 int bug, fix, i;
0122
0123 printk("Checking for the multiply/shift bug... ");
0124
0125
0126
0127
0128
0129
0130
0131
0132
0133
0134 mult_sh_align_mod(&v1[0], &v2[0], &w[0], 32, 0);
0135 mult_sh_align_mod(&v1[1], &v2[1], &w[1], 32, 1);
0136 mult_sh_align_mod(&v1[2], &v2[2], &w[2], 32, 2);
0137 mult_sh_align_mod(&v1[3], &v2[3], &w[3], 32, 3);
0138 mult_sh_align_mod(&v1[4], &v2[4], &w[4], 32, 4);
0139 mult_sh_align_mod(&v1[5], &v2[5], &w[5], 32, 5);
0140 mult_sh_align_mod(&v1[6], &v2[6], &w[6], 32, 6);
0141 mult_sh_align_mod(&v1[7], &v2[7], &w[7], 32, 7);
0142
0143 bug = 0;
0144 for (i = 0; i < 8; i++)
0145 if (v1[i] != w[i])
0146 bug = 1;
0147
0148 if (bug == 0) {
0149 pr_cont("no.\n");
0150 return;
0151 }
0152
0153 pr_cont("yes, workaround... ");
0154
0155 fix = 1;
0156 for (i = 0; i < 8; i++)
0157 if (v2[i] != w[i])
0158 fix = 0;
0159
0160 if (fix == 1) {
0161 pr_cont("yes.\n");
0162 return;
0163 }
0164
0165 pr_cont("no.\n");
0166 panic(bug64hit,
0167 IS_ENABLED(CONFIG_CPU_R4000_WORKAROUNDS) ? nowar : r4kwar);
0168 }
0169
0170 static volatile int daddi_ov;
0171
0172 asmlinkage void __init do_daddi_ov(struct pt_regs *regs)
0173 {
0174 enum ctx_state prev_state;
0175
0176 prev_state = exception_enter();
0177 daddi_ov = 1;
0178 regs->cp0_epc += 4;
0179 exception_exit(prev_state);
0180 }
0181
0182 static __init void check_daddi(void)
0183 {
0184 extern asmlinkage void handle_daddi_ov(void);
0185 unsigned long flags;
0186 void *handler;
0187 long v, tmp;
0188
0189 printk("Checking for the daddi bug... ");
0190
0191 local_irq_save(flags);
0192 handler = set_except_vector(EXCCODE_OV, handle_daddi_ov);
0193
0194
0195
0196
0197
0198
0199
0200
0201
0202 asm volatile(
0203 ".set push\n\t"
0204 ".set noat\n\t"
0205 ".set noreorder\n\t"
0206 ".set nomacro\n\t"
0207 "addiu %1, $0, %2\n\t"
0208 "dsrl %1, %1, 1\n\t"
0209 #ifdef HAVE_AS_SET_DADDI
0210 ".set daddi\n\t"
0211 #endif
0212 "daddi %0, %1, %3\n\t"
0213 ".set pop"
0214 : "=r" (v), "=&r" (tmp)
0215 : "I" (0xffffffffffffdb9aUL), "I" (0x1234));
0216 set_except_vector(EXCCODE_OV, handler);
0217 local_irq_restore(flags);
0218
0219 if (daddi_ov) {
0220 pr_cont("no.\n");
0221 return;
0222 }
0223
0224 pr_cont("yes, workaround... ");
0225
0226 local_irq_save(flags);
0227 handler = set_except_vector(EXCCODE_OV, handle_daddi_ov);
0228 asm volatile(
0229 "addiu %1, $0, %2\n\t"
0230 "dsrl %1, %1, 1\n\t"
0231 "daddi %0, %1, %3"
0232 : "=r" (v), "=&r" (tmp)
0233 : "I" (0xffffffffffffdb9aUL), "I" (0x1234));
0234 set_except_vector(EXCCODE_OV, handler);
0235 local_irq_restore(flags);
0236
0237 if (daddi_ov) {
0238 pr_cont("yes.\n");
0239 return;
0240 }
0241
0242 pr_cont("no.\n");
0243 panic(bug64hit,
0244 IS_ENABLED(CONFIG_CPU_DADDI_WORKAROUNDS) ? nowar : daddiwar);
0245 }
0246
0247 int daddiu_bug = -1;
0248
0249 static __init void check_daddiu(void)
0250 {
0251 long v, w, tmp;
0252
0253 printk("Checking for the daddiu bug... ");
0254
0255
0256
0257
0258
0259
0260
0261
0262
0263
0264
0265
0266
0267
0268
0269
0270
0271 asm volatile(
0272 ".set push\n\t"
0273 ".set noat\n\t"
0274 ".set noreorder\n\t"
0275 ".set nomacro\n\t"
0276 "addiu %2, $0, %3\n\t"
0277 "dsrl %2, %2, 1\n\t"
0278 #ifdef HAVE_AS_SET_DADDI
0279 ".set daddi\n\t"
0280 #endif
0281 "daddiu %0, %2, %4\n\t"
0282 "addiu %1, $0, %4\n\t"
0283 "daddu %1, %2\n\t"
0284 ".set pop"
0285 : "=&r" (v), "=&r" (w), "=&r" (tmp)
0286 : "I" (0xffffffffffffdb9aUL), "I" (0x1234));
0287
0288 daddiu_bug = v != w;
0289
0290 if (!daddiu_bug) {
0291 pr_cont("no.\n");
0292 return;
0293 }
0294
0295 pr_cont("yes, workaround... ");
0296
0297 asm volatile(
0298 "addiu %2, $0, %3\n\t"
0299 "dsrl %2, %2, 1\n\t"
0300 "daddiu %0, %2, %4\n\t"
0301 "addiu %1, $0, %4\n\t"
0302 "daddu %1, %2"
0303 : "=&r" (v), "=&r" (w), "=&r" (tmp)
0304 : "I" (0xffffffffffffdb9aUL), "I" (0x1234));
0305
0306 if (v == w) {
0307 pr_cont("yes.\n");
0308 return;
0309 }
0310
0311 pr_cont("no.\n");
0312 panic(bug64hit,
0313 IS_ENABLED(CONFIG_CPU_DADDI_WORKAROUNDS) ? nowar : daddiwar);
0314 }
0315
0316 void __init check_bugs64_early(void)
0317 {
0318 check_mult_sh();
0319 check_daddiu();
0320 }
0321
0322 void __init check_bugs64(void)
0323 {
0324 check_daddi();
0325 }