aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mips/mm/tlbex.c
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/tlbex.c
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/tlbex.c')
-rw-r--r--arch/mips/mm/tlbex.c42
1 files changed, 30 insertions, 12 deletions
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