[PATCH 09/13] powerpc/476: Workaround for dcbf/dcbz workaround on DD1
Dave Kleikamp
shaggy at linux.vnet.ibm.com
Sat Mar 6 07:43:47 EST 2010
powerpc/476: Workaround for dcbf/dcbz workaround on DD1
From: Benjamin Herrenschmidt <benh at 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 <benh at kernel.crashing.org>
Signed-off-by: Dave Kleikamp <shaggy at 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 ee61a9d..8153093 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_end at ha
+try_recov_exception:
+ lis r3,exc_exit_restart_end at ha
addi r3,r3,exc_exit_restart_end at l
cmplw r12,r3
bge 3f
@@ -809,6 +831,13 @@ restore_user:
andis. r10,r0,DBCR0_IDM at 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 9957c44..2f89c7f 100644
--- a/arch/powerpc/kernel/traps.c
+++ b/arch/powerpc/kernel/traps.c
@@ -772,6 +772,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;
@@ -827,6 +852,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;
}
@@ -842,6 +879,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
More information about the Linuxppc-dev
mailing list