linux/arch/mips/kernel/cpu-bugs64.c
<<
>>
Prefs
   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        /* We have to use single integers for m1 and m2 and a double
  96         * one for p to be sure the mulsidi3 gcc's RTL multiplication
  97         * instruction has the workaround applied.  Older versions of
  98         * gcc have correct umulsi3 and mulsi3, but other
  99         * multiplication variants lack the workaround.
 100         */
 101        asm volatile(
 102                ""
 103                : "=r" (m1), "=r" (m2), "=r" (s)
 104                : "0" (m1), "1" (m2), "2" (s));
 105        align_mod(align, mod);
 106        p = m1 * m2;
 107        lv2 = s << 32;
 108        asm volatile(
 109                ""
 110                : "=r" (lv2)
 111                : "0" (lv2), "r" (p));
 112        local_irq_restore(flags);
 113
 114        *v1 = lv1;
 115        *v2 = lv2;
 116        *w = lw;
 117}
 118
 119static inline void check_mult_sh(void)
 120{
 121        long v1[8], v2[8], w[8];
 122        int bug, fix, i;
 123
 124        printk("Checking for the multiply/shift bug... ");
 125
 126        /*
 127         * Testing discovered false negatives for certain code offsets
 128         * into cache lines.  Hence we test all possible offsets for
 129         * the worst assumption of an R4000 I-cache line width of 32
 130         * bytes.
 131         *
 132         * We can't use a loop as alignment directives need to be
 133         * immediates.
 134         */
 135        mult_sh_align_mod(&v1[0], &v2[0], &w[0], 32, 0);
 136        mult_sh_align_mod(&v1[1], &v2[1], &w[1], 32, 1);
 137        mult_sh_align_mod(&v1[2], &v2[2], &w[2], 32, 2);
 138        mult_sh_align_mod(&v1[3], &v2[3], &w[3], 32, 3);
 139        mult_sh_align_mod(&v1[4], &v2[4], &w[4], 32, 4);
 140        mult_sh_align_mod(&v1[5], &v2[5], &w[5], 32, 5);
 141        mult_sh_align_mod(&v1[6], &v2[6], &w[6], 32, 6);
 142        mult_sh_align_mod(&v1[7], &v2[7], &w[7], 32, 7);
 143
 144        bug = 0;
 145        for (i = 0; i < 8; i++)
 146                if (v1[i] != w[i])
 147                        bug = 1;
 148
 149        if (bug == 0) {
 150                printk("no.\n");
 151                return;
 152        }
 153
 154        printk("yes, workaround... ");
 155
 156        fix = 1;
 157        for (i = 0; i < 8; i++)
 158                if (v2[i] != w[i])
 159                        fix = 0;
 160
 161        if (fix == 1) {
 162                printk("yes.\n");
 163                return;
 164        }
 165
 166        printk("no.\n");
 167        panic(bug64hit, !R4000_WAR ? r4kwar : nowar);
 168}
 169
 170static volatile int daddi_ov __cpuinitdata = 0;
 171
 172asmlinkage void __init do_daddi_ov(struct pt_regs *regs)
 173{
 174        daddi_ov = 1;
 175        regs->cp0_epc += 4;
 176}
 177
 178static inline void check_daddi(void)
 179{
 180        extern asmlinkage void handle_daddi_ov(void);
 181        unsigned long flags;
 182        void *handler;
 183        long v, tmp;
 184
 185        printk("Checking for the daddi bug... ");
 186
 187        local_irq_save(flags);
 188        handler = set_except_vector(12, handle_daddi_ov);
 189        /*
 190         * The following code fails to trigger an overflow exception
 191         * when executed on R4000 rev. 2.2 or 3.0 (PRId 00000422 or
 192         * 00000430, respectively).
 193         *
 194         * See "MIPS R4000PC/SC Errata, Processor Revision 2.2 and
 195         * 3.0" by MIPS Technologies, Inc., erratum #23 for details.
 196         * I got no permission to duplicate it here, sigh... --macro
 197         */
 198        asm volatile(
 199                ".set   push\n\t"
 200                ".set   noat\n\t"
 201                ".set   noreorder\n\t"
 202                ".set   nomacro\n\t"
 203                "addiu  %1, $0, %2\n\t"
 204                "dsrl   %1, %1, 1\n\t"
 205#ifdef HAVE_AS_SET_DADDI
 206                ".set   daddi\n\t"
 207#endif
 208                "daddi  %0, %1, %3\n\t"
 209                ".set   pop"
 210                : "=r" (v), "=&r" (tmp)
 211                : "I" (0xffffffffffffdb9aUL), "I" (0x1234));
 212        set_except_vector(12, handler);
 213        local_irq_restore(flags);
 214
 215        if (daddi_ov) {
 216                printk("no.\n");
 217                return;
 218        }
 219
 220        printk("yes, workaround... ");
 221
 222        local_irq_save(flags);
 223        handler = set_except_vector(12, handle_daddi_ov);
 224        asm volatile(
 225                "addiu  %1, $0, %2\n\t"
 226                "dsrl   %1, %1, 1\n\t"
 227                "daddi  %0, %1, %3"
 228                : "=r" (v), "=&r" (tmp)
 229                : "I" (0xffffffffffffdb9aUL), "I" (0x1234));
 230        set_except_vector(12, handler);
 231        local_irq_restore(flags);
 232
 233        if (daddi_ov) {
 234                printk("yes.\n");
 235                return;
 236        }
 237
 238        printk("no.\n");
 239        panic(bug64hit, !DADDI_WAR ? daddiwar : nowar);
 240}
 241
 242int daddiu_bug __cpuinitdata = -1;
 243
 244static inline void check_daddiu(void)
 245{
 246        long v, w, tmp;
 247
 248        printk("Checking for the daddiu bug... ");
 249
 250        /*
 251         * The following code leads to a wrong result of daddiu when
 252         * executed on R4400 rev. 1.0 (PRId 00000440).
 253         *
 254         * See "MIPS R4400PC/SC Errata, Processor Revision 1.0" by
 255         * MIPS Technologies, Inc., erratum #7 for details.
 256         *
 257         * According to "MIPS R4000PC/SC Errata, Processor Revision
 258         * 2.2 and 3.0" by MIPS Technologies, Inc., erratum #41 this
 259         * problem affects R4000 rev. 2.2 and 3.0 (PRId 00000422 and
 260         * 00000430, respectively), too.  Testing failed to trigger it
 261         * so far.
 262         *
 263         * I got no permission to duplicate the errata here, sigh...
 264         * --macro
 265         */
 266        asm volatile(
 267                ".set   push\n\t"
 268                ".set   noat\n\t"
 269                ".set   noreorder\n\t"
 270                ".set   nomacro\n\t"
 271                "addiu  %2, $0, %3\n\t"
 272                "dsrl   %2, %2, 1\n\t"
 273#ifdef HAVE_AS_SET_DADDI
 274                ".set   daddi\n\t"
 275#endif
 276                "daddiu %0, %2, %4\n\t"
 277                "addiu  %1, $0, %4\n\t"
 278                "daddu  %1, %2\n\t"
 279                ".set   pop"
 280                : "=&r" (v), "=&r" (w), "=&r" (tmp)
 281                : "I" (0xffffffffffffdb9aUL), "I" (0x1234));
 282
 283        daddiu_bug = v != w;
 284
 285        if (!daddiu_bug) {
 286                printk("no.\n");
 287                return;
 288        }
 289
 290        printk("yes, workaround... ");
 291
 292        asm volatile(
 293                "addiu  %2, $0, %3\n\t"
 294                "dsrl   %2, %2, 1\n\t"
 295                "daddiu %0, %2, %4\n\t"
 296                "addiu  %1, $0, %4\n\t"
 297                "daddu  %1, %2"
 298                : "=&r" (v), "=&r" (w), "=&r" (tmp)
 299                : "I" (0xffffffffffffdb9aUL), "I" (0x1234));
 300
 301        if (v == w) {
 302                printk("yes.\n");
 303                return;
 304        }
 305
 306        printk("no.\n");
 307        panic(bug64hit, !DADDI_WAR ? daddiwar : nowar);
 308}
 309
 310void __init check_bugs64_early(void)
 311{
 312        check_mult_sh();
 313        check_daddiu();
 314}
 315
 316void __init check_bugs64(void)
 317{
 318        check_daddi();
 319}
 320