Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/arch/mips/kernel/cpu-bugs64.c
10817 views
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
21
static char bug64hit[] __initdata =
22
"reliable operation impossible!\n%s";
23
static char nowar[] __initdata =
24
"Please report to <[email protected]>.";
25
static char r4kwar[] __initdata =
26
"Enable CPU_R4000_WORKAROUNDS to rectify.";
27
static char daddiwar[] __initdata =
28
"Enable CPU_DADDI_WORKAROUNDS to rectify.";
29
30
static 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
44
static 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 fulfill 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
119
static 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
170
static volatile int daddi_ov __cpuinitdata;
171
172
asmlinkage void __init do_daddi_ov(struct pt_regs *regs)
173
{
174
daddi_ov = 1;
175
regs->cp0_epc += 4;
176
}
177
178
static 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
242
int daddiu_bug = -1;
243
244
static 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
310
void __init check_bugs64_early(void)
311
{
312
check_mult_sh();
313
check_daddiu();
314
}
315
316
void __init check_bugs64(void)
317
{
318
check_daddi();
319
}
320
321