[PATCH] MIPS: Workaround bugged BCM4704 & BCM5354 that crash with kmap_coherent()

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Rafał Miłecki <rafal@xxxxxxxxxx>

This workarounds what seems to be a hardware bug present in some early
Broadcom MIPS CPUs. For some reason using kmap_coherent() for copying a
memory causes a "Data bus error" or SoC reboot.

Example:
[   23.530761] Data bus error, epc == 8032f6e8, ra == 8001127c
[   23.536422] Oops[#1]:
[   23.538747] CPU: 0 PID: 761 Comm: pidof Not tainted 4.14.96 #0
[   23.544647] task: 818e3700 task.stack: 81bd2000
[   23.549216] $ 0   : 00000000 10008c00 81bd3e27 00000000
[   23.554530] $ 4   : 81bd3e27 fffd2fb2 00000001 818e3700
[   23.559845] $ 8   : 00000002 00000003 00000001 ffffff80
[   23.565151] $12   : 7fe8a708 00000000 00000000 77fa92c0
[   23.570466] $16   : 8100a920 803f9060 7fd22fb2 81bd3e27
[   23.575781] $20   : 00000001 80548fb2 819d8700 00000000
[   23.581097] $24   : 00000fa3 80018ec0
[   23.586403] $28   : 81bd2000 81bd3d58 80000000 8001127c
[   23.591720] Hi    : 00000000
[   23.594634] Lo    : 00000000
[   23.597590] epc   : 8032f6e8 __copy_user+0x1c4/0x2bc
[   23.602633] ra    : 8001127c copy_from_user_page+0xa8/0x130
[   23.608257] Status: 10008c03 KERNEL EXL IE
[   23.612511] Cause : 0080001c (ExcCode 07)
[   23.616560] PrId  : 00029006 (Broadcom BMIPS3300)
[   23.621301] Modules linked in: pppoe ppp_async b43 pppox ppp_generic nf_conntrack_ipv6 mac80211 iptable_nat ipt_REJECT ipt_MASQUERADE cfg80211 xt_time xt_tcpudp xt_state xt_nat xt_multiport xt_mark xt_mac xt_limit xt_conntrack xt_comment xt_TCPMSS xt_REDIRECT xt_LOG d
[   23.688445] Process pidof (pid: 761, threadinfo=81bd2000, task=818e3700, tls=77faada8)
[   23.696417] Stack : 819d8738 0000a806 7fd22fb3 7fd22fb2 81bd3e27 00000001 7fd22fb2 81bd3e27
[   23.704907]         00000001 81bd3e27 819d8738 800a8f84 00000000 00000000 80416598 00000000
[   23.713389]         80548fb2 00000001 81bd3db0 00000000 00000000 80470000 81a25f60 8100a920
[   23.721873]         7fd22fb3 7fd22fb3 819d8700 000003ff 7fe8a7e8 81bd3f08 81b06000 0000000c
[   23.730356]         7fd22fa7 800a9044 00000000 80088d2c 80d69010 00000002 00000001 00008000
[   23.738838]         ...
[   23.741330] Call Trace:
[   23.743833] [<8032f6e8>] __copy_user+0x1c4/0x2bc
[   23.748536] [<8001127c>] copy_from_user_page+0xa8/0x130
[   23.753870] [<800a8f84>] __access_remote_vm+0x1a0/0x1a8
[   23.759176] [<800a9044>] access_remote_vm+0x28/0x34
[   23.764142] [<8010b60c>] proc_pid_cmdline_read+0x190/0x410
[   23.769714] [<800c008c>] __vfs_read+0x28/0x150
[   23.774222] [<800c0254>] vfs_read+0xa0/0x148
[   23.778559] [<800c0774>] SyS_read+0x58/0xc0
[   23.782809] [<8000eb58>] syscall_common+0x34/0x58
[   23.787570] Code: 00000000  10c0000d  00000000 <80a80000> 24c6ffff  10c00009  a0880000  80a80001  24c6ffff
[   23.797495]
[   23.799272] ---[ end trace 3f76d2c13fe73eb8 ]---

Using ptrace (which involves copy_to_user_page()) results in immediate
reboot without printing anything over a serial console.

This problem has been noticed on:
1) ASUS WL-500g Premium
SoC: BCM4704, CPU: 00029006 (Broadcom BMIPS3300)
2) ASUS WL-500gP V2
SoC: BCM5354, CPU: 00029029 (Broadcom BMIPS3300)
3) Linksys WRT300N V1
SoC: BCM4704, CPU: 00029006 (Broadcom BMIPS3300)

It doesn't exist on:
1) Linksys WRT54G v3.1
SoC: BCM4712, CPU: 00029007 (Broadcom BMIPS3300)

Signed-off-by: Rafał Miłecki <rafal@xxxxxxxxxx>
Link: https://dev.archive.openwrt.org/ticket/1485
---
 arch/mips/include/asm/cpu-features.h                      | 3 +++
 .../mips/include/asm/mach-bcm47xx/cpu-feature-overrides.h | 8 ++++++++
 arch/mips/mm/init.c                                       | 6 +++---
 3 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/arch/mips/include/asm/cpu-features.h b/arch/mips/include/asm/cpu-features.h
index 6998a9796499..351f649eacc3 100644
--- a/arch/mips/include/asm/cpu-features.h
+++ b/arch/mips/include/asm/cpu-features.h
@@ -246,6 +246,9 @@
 #ifndef cpu_has_local_ebase
 #define cpu_has_local_ebase	1
 #endif
+#ifndef cpu_has_kmap_coherent
+#define cpu_has_kmap_coherent	1
+#endif
 
 /*
  * I-Cache snoops remote store.	 This only matters on SMP.  Some multiprocessors
diff --git a/arch/mips/include/asm/mach-bcm47xx/cpu-feature-overrides.h b/arch/mips/include/asm/mach-bcm47xx/cpu-feature-overrides.h
index b23ff47ea475..553505dea60b 100644
--- a/arch/mips/include/asm/mach-bcm47xx/cpu-feature-overrides.h
+++ b/arch/mips/include/asm/mach-bcm47xx/cpu-feature-overrides.h
@@ -80,4 +80,12 @@
 #define cpu_scache_line_size()		0
 #define cpu_has_vz			0
 
+/*
+ * Workaround for the bugged BCM4704 & BCM5354:
+ * copy_from_user_page() + kmap_coherent() causes "Data bus error"
+ * copy_to_user_page() + kmap_coherent() causes immediate reboot
+ */
+#define cpu_has_kmap_coherent		(cpu_data[0].processor_id != 0x29006 && \
+					 cpu_data[0].processor_id != 0x29029)
+
 #endif /* __ASM_MACH_BCM47XX_CPU_FEATURE_OVERRIDES_H */
diff --git a/arch/mips/mm/init.c b/arch/mips/mm/init.c
index c3b45e248806..67007bf15543 100644
--- a/arch/mips/mm/init.c
+++ b/arch/mips/mm/init.c
@@ -174,7 +174,7 @@ void copy_user_highpage(struct page *to, struct page *from,
 	void *vfrom, *vto;
 
 	vto = kmap_atomic(to);
-	if (cpu_has_dc_aliases &&
+	if (cpu_has_kmap_coherent && cpu_has_dc_aliases &&
 	    page_mapcount(from) && !Page_dcache_dirty(from)) {
 		vfrom = kmap_coherent(from, vaddr);
 		copy_page(vto, vfrom);
@@ -196,7 +196,7 @@ void copy_to_user_page(struct vm_area_struct *vma,
 	struct page *page, unsigned long vaddr, void *dst, const void *src,
 	unsigned long len)
 {
-	if (cpu_has_dc_aliases &&
+	if (cpu_has_kmap_coherent && cpu_has_dc_aliases &&
 	    page_mapcount(page) && !Page_dcache_dirty(page)) {
 		void *vto = kmap_coherent(page, vaddr) + (vaddr & ~PAGE_MASK);
 		memcpy(vto, src, len);
@@ -214,7 +214,7 @@ void copy_from_user_page(struct vm_area_struct *vma,
 	struct page *page, unsigned long vaddr, void *dst, const void *src,
 	unsigned long len)
 {
-	if (cpu_has_dc_aliases &&
+	if (cpu_has_kmap_coherent && cpu_has_dc_aliases &&
 	    page_mapcount(page) && !Page_dcache_dirty(page)) {
 		void *vfrom = kmap_coherent(page, vaddr) + (vaddr & ~PAGE_MASK);
 		memcpy(dst, vfrom, len);
-- 
2.20.1



[Index of Archives]     [Linux MIPS Home]     [LKML Archive]     [Linux ARM Kernel]     [Linux ARM]     [Linux]     [Git]     [Yosemite News]     [Linux SCSI]     [Linux Hams]

  Powered by Linux