powerpc/476: Workaround for dcbf/dcbz workaround on DD1

From: Benjamin Herrenschmidt <b...@kernel.crashing.org>

On the DD1.1 core, the dcbf and dcbz instructions need to be preceded and
followed by an lwsync.  We must trap user-space to ensure that this occurs
there too.

Signed-off-by: Benjamin Herrenschmidt <b...@kernel.crashing.org>
Signed-off-by: Dave Kleikamp <sha...@linux.vnet.ibm.com>
---

 arch/powerpc/include/asm/asm-compat.h |   10 +++++++
 arch/powerpc/include/asm/ppc-opcode.h |    4 +++
 arch/powerpc/include/asm/reg_booke.h  |    9 +++++++
 arch/powerpc/kernel/entry_32.S        |   35 +++++++++++++++++++++++---
 arch/powerpc/kernel/head_44x.S        |    9 +++++++
 arch/powerpc/kernel/misc_32.S         |   32 ++++++++++++++++++++++-
 arch/powerpc/kernel/traps.c           |   45 +++++++++++++++++++++++++++++++++
 arch/powerpc/lib/copy_32.S            |    7 ++++-
 8 files changed, 145 insertions(+), 6 deletions(-)


diff --git a/arch/powerpc/include/asm/asm-compat.h 
b/arch/powerpc/include/asm/asm-compat.h
index 8f0fe79..bee05ec 100644
--- a/arch/powerpc/include/asm/asm-compat.h
+++ b/arch/powerpc/include/asm/asm-compat.h
@@ -64,6 +64,16 @@
 #define PPC405_ERR77(ra,rb)
 #define PPC405_ERR77_SYNC
 #endif
+
+#ifdef CONFIG_PPC_47x
+#define PPC476_ERR_DCBx()                      \
+       BEGIN_FTR_SECTION;                      \
+               lwsync;                         \
+       END_FTR_SECTION_IFSET(CPU_FTR_476_DD1_1)
+#else
+#define PPC476_ERR_DCBx()
+#endif /* CONFIG_PPC_47x */
+
 #endif
 
 #endif /* _ASM_POWERPC_ASM_COMPAT_H */
diff --git a/arch/powerpc/include/asm/ppc-opcode.h 
b/arch/powerpc/include/asm/ppc-opcode.h
index ef9aa84..629b1fe 100644
--- a/arch/powerpc/include/asm/ppc-opcode.h
+++ b/arch/powerpc/include/asm/ppc-opcode.h
@@ -19,6 +19,10 @@
 #define PPC_INST_DCBA                  0x7c0005ec
 #define PPC_INST_DCBA_MASK             0xfc0007fe
 #define PPC_INST_DCBAL                 0x7c2005ec
+#define PPC_INST_DCBF                  0x7c0000ac
+#define PPC_INST_DCBF_MASK             0xfc0007fe
+#define PPC_INST_DCBZ                  0x7c0007ec
+#define PPC_INST_DCBZ_MASK             0xfc0007fe
 #define PPC_INST_DCBZL                 0x7c2007ec
 #define PPC_INST_ISEL                  0x7c00001e
 #define PPC_INST_ISEL_MASK             0xfc00003e
diff --git a/arch/powerpc/include/asm/reg_booke.h 
b/arch/powerpc/include/asm/reg_booke.h
index a9245b9..bca3fba 100644
--- a/arch/powerpc/include/asm/reg_booke.h
+++ b/arch/powerpc/include/asm/reg_booke.h
@@ -276,6 +276,8 @@
 #define ESR_IMCN       0x40000000      /* Instr. Machine Check - Non-config */
 #define ESR_IMCB       0x20000000      /* Instr. Machine Check - Bus error */
 #define ESR_IMCT       0x10000000      /* Instr. Machine Check - Timeout */
+#define ESR_POT1       0x20000000      /* 476 - IOCR1 trap */
+#define ESR_POT2       0x10000000      /* 476 - IOCR2 trap */
 #define ESR_PIL                0x08000000      /* Program Exception - Illegal 
*/
 #define ESR_PPR                0x04000000      /* Program Exception - 
Privileged */
 #define ESR_PTR                0x02000000      /* Program Exception - Trap */
@@ -535,6 +537,13 @@
 #define MMUBE1_VBE3            0x00000004
 #define MMUBE1_VBE4            0x00000002
 #define MMUBE1_VBE5            0x00000001
+#define SPRN_IOCCR     860
+#define                IOCCR_IOCR1EN           0x80000000
+#define                IOCCR_IOCR1M            0x40000000
+#define                IOCCR_IOCR2EN           0x20000000
+#define                IOCCR_IOCR2M            0x10000000
+#define SPRN_IOCR1     861
+#define SPRN_IOCR2     862
 
 #endif /* __ASM_POWERPC_REG_BOOKE_H__ */
 #endif /* __KERNEL__ */
diff --git a/arch/powerpc/kernel/entry_32.S b/arch/powerpc/kernel/entry_32.S
index ed4aeb9..57b7893 100644
--- a/arch/powerpc/kernel/entry_32.S
+++ b/arch/powerpc/kernel/entry_32.S
@@ -142,6 +142,12 @@ transfer_to_handler:
        addi    r2,r12,-THREAD
        tovirt(r2,r2)                   /* set r2 to current */
        beq     2f                      /* if from user, fix up THREAD.regs */
+#ifdef CONFIG_PPC_47x
+BEGIN_FTR_SECTION
+       li      r11,0
+       mtspr   SPRN_IOCCR,r11
+END_FTR_SECTION_IFSET(CPU_FTR_476_DD1_1)
+#endif /* CONFIG_PPC_47x */
        addi    r11,r1,STACK_FRAME_OVERHEAD
        stw     r11,PT_REGS(r12)
 #if defined(CONFIG_40x) || defined(CONFIG_BOOKE)
@@ -280,6 +286,12 @@ stack_ovf:
 0:
 
 _GLOBAL(DoSyscall)
+#ifdef CONFIG_PPC_47x
+BEGIN_FTR_SECTION
+       li      r11,0
+       mtspr   SPRN_IOCCR,r11
+END_FTR_SECTION_IFSET(CPU_FTR_476_DD1_1)
+#endif /* CONFIG_PPC_47x */
        stw     r3,ORIG_GPR3(r1)
        li      r12,0
        stw     r12,RESULT(r1)
@@ -381,6 +393,16 @@ BEGIN_MMU_FTR_SECTION
 1:
 END_MMU_FTR_SECTION_IFCLR(MMU_FTR_TYPE_47x)
 #endif /* CONFIG_44x */
+#ifdef CONFIG_PPC_47x
+BEGIN_FTR_SECTION
+       lwz     r7,_MSR(r1)
+       andi.   r5,r7,MSR_PR
+       beq     11f
+       lis     r4,(IOCCR_IOCR1EN|IOCCR_IOCR2EN)@h
+       mtspr   SPRN_IOCCR,r4
+11:
+END_FTR_SECTION_IFSET(CPU_FTR_476_DD1_1)
+#endif /* CONFIG_PPC_47x */
 BEGIN_FTR_SECTION
        lwarx   r7,0,r1
 END_FTR_SECTION_IFSET(CPU_FTR_NEED_PAIRED_STWCX)
@@ -716,9 +738,8 @@ END_FTR_SECTION_IFSET(CPU_FTR_SPE)
 fast_exception_return:
 #if !(defined(CONFIG_4xx) || defined(CONFIG_BOOKE))
        andi.   r10,r9,MSR_RI           /* check for recoverable interrupt */
-       beq     1f                      /* if not, we've got problems */
+       beq     try_recov_exception     /* if not, we've got problems */
 #endif
-
 2:     REST_4GPRS(3, r11)
        lwz     r10,_CCR(r11)
        REST_GPR(1, r11)
@@ -736,7 +757,8 @@ fast_exception_return:
 
 #if !(defined(CONFIG_4xx) || defined(CONFIG_BOOKE))
 /* check if the exception happened in a restartable section */
-1:     lis     r3,exc_exit_restart_...@ha
+try_recov_exception:
+       lis     r3,exc_exit_restart_...@ha
        addi    r3,r3,exc_exit_restart_...@l
        cmplw   r12,r3
        bge     3f
@@ -809,6 +831,13 @@ restore_user:
        andis.  r10,r0,dbcr0_...@h
        bnel-   load_dbcr0
 #endif
+#ifdef CONFIG_PPC_47x
+BEGIN_FTR_SECTION
+       lis     r0,(IOCCR_IOCR1EN|IOCCR_IOCR2EN)@h
+       mtspr   SPRN_IOCCR,r0
+1:
+END_FTR_SECTION_IFSET(CPU_FTR_476_DD1_1)
+#endif /* CONFIG_PPC_47x */
 
 #ifdef CONFIG_PREEMPT
        b       restore
diff --git a/arch/powerpc/kernel/head_44x.S b/arch/powerpc/kernel/head_44x.S
index af3a9e1..a96796d 100644
--- a/arch/powerpc/kernel/head_44x.S
+++ b/arch/powerpc/kernel/head_44x.S
@@ -1113,6 +1113,15 @@ clear_utlb_entry:
        mtspr   SPRN_CCR0,r3
        isync
 
+       /* XXX DD1.1 workaround, trap on dcbz & dcbf. We pre-configure
+        *     IOCR1 and 2 but we don't enable them in IOCCR, this will
+        *     be done on kernel entry/exit
+        */
+       LOAD_REG_IMMEDIATE(r3, (31 << 26) | (  86 << 1))        /* dcbf */
+       LOAD_REG_IMMEDIATE(r4, (31 << 26) | (1014 << 1))        /* dcbz */
+       mtspr   SPRN_IOCR1, r3
+       mtspr   SPRN_IOCR2, r4
+
 #endif /* CONFIG_PPC_47x */
 
 /*
diff --git a/arch/powerpc/kernel/misc_32.S b/arch/powerpc/kernel/misc_32.S
index 8043d1b..db6b115 100644
--- a/arch/powerpc/kernel/misc_32.S
+++ b/arch/powerpc/kernel/misc_32.S
@@ -393,7 +393,9 @@ _GLOBAL(flush_dcache_range)
        beqlr
        mtctr   r4
 
-1:     dcbf    0,r3
+1:     PPC476_ERR_DCBx()
+       dcbf    0,r3
+       PPC476_ERR_DCBx()
        addi    r3,r3,L1_CACHE_BYTES
        bdnz    1b
        sync                            /* wait for dcbst's to get to ram */
@@ -507,7 +509,9 @@ _GLOBAL(clear_pages)
        li      r0,PAGE_SIZE/L1_CACHE_BYTES
        slw     r0,r0,r4
        mtctr   r0
-1:     dcbz    0,r3
+1:     PPC476_ERR_DCBx()
+       dcbz    0,r3
+       PPC476_ERR_DCBx()
        addi    r3,r3,L1_CACHE_BYTES
        bdnz    1b
        blr
@@ -551,7 +555,9 @@ _GLOBAL(copy_page)
        mtctr   r0
 1:
        dcbt    r11,r4
+       PPC476_ERR_DCBx()
        dcbz    r5,r3
+       PPC476_ERR_DCBx()
        COPY_16_BYTES
 #if L1_CACHE_BYTES >= 32
        COPY_16_BYTES
@@ -807,3 +813,25 @@ relocate_new_kernel_end:
 relocate_new_kernel_size:
        .long relocate_new_kernel_end - relocate_new_kernel
 #endif
+
+#ifdef CONFIG_PPC_47x
+_GLOBAL(__dcbf)
+       lwsync
+1:     dcbf    0,r3
+       lwsync
+       li      r3,0
+       blr
+_GLOBAL(__dcbz)
+       lwsync
+2:     dcbz    0,r3
+       lwsync
+       li      r3,0
+       blr
+3:     li      r3,-EFAULT
+       blr
+       .section __ex_table,"a"
+       .align  2
+       .long   1b,3b
+       .long   2b,3b
+       .text
+#endif /* CONFIG_PPC_47x */
diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c
index 47169f6..34c8a38 100644
--- a/arch/powerpc/kernel/traps.c
+++ b/arch/powerpc/kernel/traps.c
@@ -770,6 +770,31 @@ static int emulate_isel(struct pt_regs *regs, u32 instword)
        return 0;
 }
 
+#ifdef CONFIG_PPC_47x
+
+extern int __dcbf(unsigned long ea);
+extern int __dcbz(unsigned long ea);
+
+static int emulate_dcbf(struct pt_regs *regs, u32 instword)
+{
+       u8 rA = (instword >> 16) & 0x1f;
+       u8 rB = (instword >> 11) & 0x1f;
+       unsigned long ea = regs->gpr[rB] + ((rA == 0) ? 0 : regs->gpr[rA]);
+
+       return __dcbf(ea);
+}
+
+static int emulate_dcbz(struct pt_regs *regs, u32 instword)
+{
+       u8 rA = (instword >> 16) & 0x1f;
+       u8 rB = (instword >> 11) & 0x1f;
+       unsigned long ea = regs->gpr[rB] + ((rA == 0) ? 0 : regs->gpr[rA]);
+
+       return __dcbz(ea);
+}
+
+#endif /* CONFIG_PPC_47x */
+
 static int emulate_instruction(struct pt_regs *regs)
 {
        u32 instword;
@@ -825,6 +850,18 @@ static int emulate_instruction(struct pt_regs *regs)
                return emulate_isel(regs, instword);
        }
 
+#ifdef CONFIG_PPC_47x
+       /* Emulate dcbf instruction */
+       if ((instword & PPC_INST_DCBF_MASK) == PPC_INST_DCBF) {
+               return emulate_dcbf(regs, instword);
+       }
+
+       /* Emulate dcbz instruction */
+       if ((instword & PPC_INST_DCBZ_MASK) == PPC_INST_DCBZ) {
+               return emulate_dcbz(regs, instword);
+       }
+#endif /* CONFIG_47x */
+
        return -EINVAL;
 }
 
@@ -840,6 +877,14 @@ void __kprobes program_check_exception(struct pt_regs 
*regs)
 
        /* We can now get here via a FP Unavailable exception if the core
         * has no FPU, in that case the reason flags will be 0 */
+#ifdef CONFIG_PPC_47x
+       /* Make IOC instruction traps look like illegal instructions
+        * so we hit the proper emulation code path
+        */
+       if (mmu_has_feature(MMU_FTR_TYPE_47x) &&
+           (reason & (ESR_POT1 | ESR_POT2)))
+               reason |= ESR_PIL;
+#endif /* CONFIG_PPC_47x */
 
        if (reason & REASON_FP) {
                /* IEEE FP exception */
diff --git a/arch/powerpc/lib/copy_32.S b/arch/powerpc/lib/copy_32.S
index 74a7f41..d649609 100644
--- a/arch/powerpc/lib/copy_32.S
+++ b/arch/powerpc/lib/copy_32.S
@@ -12,6 +12,7 @@
 #include <asm/cache.h>
 #include <asm/errno.h>
 #include <asm/ppc_asm.h>
+#include <asm/cputable.h>
 
 #define COPY_16_BYTES          \
        lwz     r7,4(r4);       \
@@ -98,7 +99,9 @@ _GLOBAL(cacheable_memzero)
        bdnz    4b
 3:     mtctr   r9
        li      r7,4
-10:    dcbz    r7,r6
+10:    PPC476_ERR_DCBx()
+       dcbz    r7,r6
+       PPC476_ERR_DCBx()
        addi    r6,r6,CACHELINE_BYTES
        bdnz    10b
        clrlwi  r5,r8,32-LG_CACHELINE_BYTES
@@ -368,7 +371,9 @@ _GLOBAL(__copy_tofrom_user)
        mtctr   r8
 
 53:    dcbt    r3,r4
+       PPC476_ERR_DCBx()
 54:    dcbz    r11,r6
+       PPC476_ERR_DCBx()
        .section __ex_table,"a"
        .align  2
        .long   54b,105f

-- 
Dave Kleikamp
IBM Linux Technology Center
_______________________________________________
Linuxppc-dev mailing list
Linuxppc-dev@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/linuxppc-dev

Reply via email to