aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mips/mm
diff options
context:
space:
mode:
authorMaciej W. Rozycki <macro@linux-mips.org>2007-10-23 07:43:25 -0400
committerRalf Baechle <ralf@linux-mips.org>2008-01-29 05:14:55 -0500
commit619b6e18fce20e4b2d0082cde989f37e1be7b3e1 (patch)
treeb56120c0de584f6672aa7e4533163435613f5203 /arch/mips/mm
parent20d60d9973c3b441902b0a3f4f6f7e7ade08f77d (diff)
[MIPS] R4000/R4400 daddiu erratum workaround
This complements the generic R4000/R4400 errata workaround code and adds bits for the daddiu problem. In most places it just modifies handwritten assembly code so that the assembler is allowed to use a temporary register as daddiu may now be treated as a macro that expands to a sequence of li and daddu. It is the AT register or, where AT is unavailable or used explicitly for another purpose, an explicitly-named register is selected, using the .set at=<reg> feature added recently to gas. This feature is only used if CONFIG_CPU_DADDI_WORKAROUNDS has been set, so if the workaround remains disabled, the required version of binutils stays unchanged. Similarly, daddiu instructions put in branch delay slots in noreorder fragments are now taken out of them and the assembler is allowed to reorder them itself as possible (which it does making the whole idea of scheduling them into delay slots manually questionable). Also in the very few places where such a simple conversion was not possible, a handcoded longer sequence is implemented. Other than that there are changes to code responsible for building the TLB fault and page clear/copy handlers to avoid daddiu as appropriate. These are only effective if the erratum is verified to be present at the run time. Finally there is a trivial update to __delay(), because it uses daddiu in a branch delay slot. Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org> Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'arch/mips/mm')
-rw-r--r--arch/mips/mm/pg-r4k.c66
-rw-r--r--arch/mips/mm/tlbex.c42
2 files changed, 61 insertions, 47 deletions
diff --git a/arch/mips/mm/pg-r4k.c b/arch/mips/mm/pg-r4k.c
index 4f770ac885ce..9185fbf37c0d 100644
--- a/arch/mips/mm/pg-r4k.c
+++ b/arch/mips/mm/pg-r4k.c
@@ -4,6 +4,7 @@
4 * for more details. 4 * for more details.
5 * 5 *
6 * Copyright (C) 2003, 04, 05 Ralf Baechle (ralf@linux-mips.org) 6 * Copyright (C) 2003, 04, 05 Ralf Baechle (ralf@linux-mips.org)
7 * Copyright (C) 2007 Maciej W. Rozycki
7 */ 8 */
8#include <linux/init.h> 9#include <linux/init.h>
9#include <linux/kernel.h> 10#include <linux/kernel.h>
@@ -12,6 +13,7 @@
12#include <linux/module.h> 13#include <linux/module.h>
13#include <linux/proc_fs.h> 14#include <linux/proc_fs.h>
14 15
16#include <asm/bugs.h>
15#include <asm/cacheops.h> 17#include <asm/cacheops.h>
16#include <asm/inst.h> 18#include <asm/inst.h>
17#include <asm/io.h> 19#include <asm/io.h>
@@ -255,64 +257,58 @@ static inline void build_store_reg(int reg)
255 __build_store_reg(reg); 257 __build_store_reg(reg);
256} 258}
257 259
258static inline void build_addiu_a2_a0(unsigned long offset) 260static inline void build_addiu_rt_rs(unsigned int rt, unsigned int rs,
261 unsigned long offset)
259{ 262{
260 union mips_instruction mi; 263 union mips_instruction mi;
261 264
262 BUG_ON(offset > 0x7fff); 265 BUG_ON(offset > 0x7fff);
263 266
264 mi.i_format.opcode = cpu_has_64bit_gp_regs ? daddiu_op : addiu_op; 267 if (cpu_has_64bit_gp_regs && DADDI_WAR && r4k_daddiu_bug()) {
265 mi.i_format.rs = 4; /* $a0 */ 268 mi.i_format.opcode = addiu_op;
266 mi.i_format.rt = 6; /* $a2 */ 269 mi.i_format.rs = 0; /* $zero */
267 mi.i_format.simmediate = offset; 270 mi.i_format.rt = 25; /* $t9 */
271 mi.i_format.simmediate = offset;
272 emit_instruction(mi);
268 273
274 mi.r_format.opcode = spec_op;
275 mi.r_format.rs = rs;
276 mi.r_format.rt = 25; /* $t9 */
277 mi.r_format.rd = rt;
278 mi.r_format.re = 0;
279 mi.r_format.func = daddu_op;
280 } else {
281 mi.i_format.opcode = cpu_has_64bit_gp_regs ?
282 daddiu_op : addiu_op;
283 mi.i_format.rs = rs;
284 mi.i_format.rt = rt;
285 mi.i_format.simmediate = offset;
286 }
269 emit_instruction(mi); 287 emit_instruction(mi);
270} 288}
271 289
272static inline void build_addiu_a2(unsigned long offset) 290static inline void build_addiu_a2_a0(unsigned long offset)
273{ 291{
274 union mips_instruction mi; 292 build_addiu_rt_rs(6, 4, offset); /* $a2, $a0, offset */
275 293}
276 BUG_ON(offset > 0x7fff);
277
278 mi.i_format.opcode = cpu_has_64bit_gp_regs ? daddiu_op : addiu_op;
279 mi.i_format.rs = 6; /* $a2 */
280 mi.i_format.rt = 6; /* $a2 */
281 mi.i_format.simmediate = offset;
282 294
283 emit_instruction(mi); 295static inline void build_addiu_a2(unsigned long offset)
296{
297 build_addiu_rt_rs(6, 6, offset); /* $a2, $a2, offset */
284} 298}
285 299
286static inline void build_addiu_a1(unsigned long offset) 300static inline void build_addiu_a1(unsigned long offset)
287{ 301{
288 union mips_instruction mi; 302 build_addiu_rt_rs(5, 5, offset); /* $a1, $a1, offset */
289
290 BUG_ON(offset > 0x7fff);
291
292 mi.i_format.opcode = cpu_has_64bit_gp_regs ? daddiu_op : addiu_op;
293 mi.i_format.rs = 5; /* $a1 */
294 mi.i_format.rt = 5; /* $a1 */
295 mi.i_format.simmediate = offset;
296 303
297 load_offset -= offset; 304 load_offset -= offset;
298
299 emit_instruction(mi);
300} 305}
301 306
302static inline void build_addiu_a0(unsigned long offset) 307static inline void build_addiu_a0(unsigned long offset)
303{ 308{
304 union mips_instruction mi; 309 build_addiu_rt_rs(4, 4, offset); /* $a0, $a0, offset */
305
306 BUG_ON(offset > 0x7fff);
307
308 mi.i_format.opcode = cpu_has_64bit_gp_regs ? daddiu_op : addiu_op;
309 mi.i_format.rs = 4; /* $a0 */
310 mi.i_format.rt = 4; /* $a0 */
311 mi.i_format.simmediate = offset;
312 310
313 store_offset -= offset; 311 store_offset -= offset;
314
315 emit_instruction(mi);
316} 312}
317 313
318static inline void build_bne(unsigned int *dest) 314static inline void build_bne(unsigned int *dest)
diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c
index 511107f92d9c..f8925ba0b39e 100644
--- a/arch/mips/mm/tlbex.c
+++ b/arch/mips/mm/tlbex.c
@@ -6,7 +6,7 @@
6 * Synthesize TLB refill handlers at runtime. 6 * Synthesize TLB refill handlers at runtime.
7 * 7 *
8 * Copyright (C) 2004,2005,2006 by Thiemo Seufer 8 * Copyright (C) 2004,2005,2006 by Thiemo Seufer
9 * Copyright (C) 2005 Maciej W. Rozycki 9 * Copyright (C) 2005, 2007 Maciej W. Rozycki
10 * Copyright (C) 2006 Ralf Baechle (ralf@linux-mips.org) 10 * Copyright (C) 2006 Ralf Baechle (ralf@linux-mips.org)
11 * 11 *
12 * ... and the days got worse and worse and now you see 12 * ... and the days got worse and worse and now you see
@@ -27,6 +27,7 @@
27#include <linux/string.h> 27#include <linux/string.h>
28#include <linux/init.h> 28#include <linux/init.h>
29 29
30#include <asm/bugs.h>
30#include <asm/pgtable.h> 31#include <asm/pgtable.h>
31#include <asm/cacheflush.h> 32#include <asm/cacheflush.h>
32#include <asm/mmu_context.h> 33#include <asm/mmu_context.h>
@@ -293,7 +294,7 @@ static void __init build_insn(u32 **buf, enum opcode opc, ...)
293 break; 294 break;
294 } 295 }
295 296
296 if (!ip) 297 if (!ip || (opc == insn_daddiu && r4k_daddiu_bug()))
297 panic("Unsupported TLB synthesizer instruction %d", opc); 298 panic("Unsupported TLB synthesizer instruction %d", opc);
298 299
299 op = ip->match; 300 op = ip->match;
@@ -525,23 +526,33 @@ L_LA(_r3000_write_probe_fail)
525#define i_ssnop(buf) i_sll(buf, 0, 0, 1) 526#define i_ssnop(buf) i_sll(buf, 0, 0, 1)
526#define i_ehb(buf) i_sll(buf, 0, 0, 3) 527#define i_ehb(buf) i_sll(buf, 0, 0, 3)
527 528
528#ifdef CONFIG_64BIT
529static __init int __maybe_unused in_compat_space_p(long addr) 529static __init int __maybe_unused in_compat_space_p(long addr)
530{ 530{
531 /* Is this address in 32bit compat space? */ 531 /* Is this address in 32bit compat space? */
532#ifdef CONFIG_64BIT
532 return (((addr) & 0xffffffff00000000L) == 0xffffffff00000000L); 533 return (((addr) & 0xffffffff00000000L) == 0xffffffff00000000L);
534#else
535 return 1;
536#endif
533} 537}
534 538
535static __init int __maybe_unused rel_highest(long val) 539static __init int __maybe_unused rel_highest(long val)
536{ 540{
541#ifdef CONFIG_64BIT
537 return ((((val + 0x800080008000L) >> 48) & 0xffff) ^ 0x8000) - 0x8000; 542 return ((((val + 0x800080008000L) >> 48) & 0xffff) ^ 0x8000) - 0x8000;
543#else
544 return 0;
545#endif
538} 546}
539 547
540static __init int __maybe_unused rel_higher(long val) 548static __init int __maybe_unused rel_higher(long val)
541{ 549{
550#ifdef CONFIG_64BIT
542 return ((((val + 0x80008000L) >> 32) & 0xffff) ^ 0x8000) - 0x8000; 551 return ((((val + 0x80008000L) >> 32) & 0xffff) ^ 0x8000) - 0x8000;
543} 552#else
553 return 0;
544#endif 554#endif
555}
545 556
546static __init int rel_hi(long val) 557static __init int rel_hi(long val)
547{ 558{
@@ -555,7 +566,6 @@ static __init int rel_lo(long val)
555 566
556static __init void i_LA_mostly(u32 **buf, unsigned int rs, long addr) 567static __init void i_LA_mostly(u32 **buf, unsigned int rs, long addr)
557{ 568{
558#ifdef CONFIG_64BIT
559 if (!in_compat_space_p(addr)) { 569 if (!in_compat_space_p(addr)) {
560 i_lui(buf, rs, rel_highest(addr)); 570 i_lui(buf, rs, rel_highest(addr));
561 if (rel_higher(addr)) 571 if (rel_higher(addr))
@@ -567,16 +577,18 @@ static __init void i_LA_mostly(u32 **buf, unsigned int rs, long addr)
567 } else 577 } else
568 i_dsll32(buf, rs, rs, 0); 578 i_dsll32(buf, rs, rs, 0);
569 } else 579 } else
570#endif
571 i_lui(buf, rs, rel_hi(addr)); 580 i_lui(buf, rs, rel_hi(addr));
572} 581}
573 582
574static __init void __maybe_unused i_LA(u32 **buf, unsigned int rs, 583static __init void __maybe_unused i_LA(u32 **buf, unsigned int rs, long addr)
575 long addr)
576{ 584{
577 i_LA_mostly(buf, rs, addr); 585 i_LA_mostly(buf, rs, addr);
578 if (rel_lo(addr)) 586 if (rel_lo(addr)) {
579 i_ADDIU(buf, rs, rs, rel_lo(addr)); 587 if (!in_compat_space_p(addr))
588 i_daddiu(buf, rs, rs, rel_lo(addr));
589 else
590 i_addiu(buf, rs, rs, rel_lo(addr));
591 }
580} 592}
581 593
582/* 594/*
@@ -1085,7 +1097,10 @@ build_get_pgd_vmalloc64(u32 **p, struct label **l, struct reloc **r,
1085 } else { 1097 } else {
1086 i_LA_mostly(p, ptr, modd); 1098 i_LA_mostly(p, ptr, modd);
1087 il_b(p, r, label_vmalloc_done); 1099 il_b(p, r, label_vmalloc_done);
1088 i_daddiu(p, ptr, ptr, rel_lo(modd)); 1100 if (in_compat_space_p(modd))
1101 i_addiu(p, ptr, ptr, rel_lo(modd));
1102 else
1103 i_daddiu(p, ptr, ptr, rel_lo(modd));
1089 } 1104 }
1090 1105
1091 l_vmalloc(l, *p); 1106 l_vmalloc(l, *p);
@@ -1106,7 +1121,10 @@ build_get_pgd_vmalloc64(u32 **p, struct label **l, struct reloc **r,
1106 } else { 1121 } else {
1107 i_LA_mostly(p, ptr, swpd); 1122 i_LA_mostly(p, ptr, swpd);
1108 il_b(p, r, label_vmalloc_done); 1123 il_b(p, r, label_vmalloc_done);
1109 i_daddiu(p, ptr, ptr, rel_lo(swpd)); 1124 if (in_compat_space_p(swpd))
1125 i_addiu(p, ptr, ptr, rel_lo(swpd));
1126 else
1127 i_daddiu(p, ptr, ptr, rel_lo(swpd));
1110 } 1128 }
1111} 1129}
1112 1130