From: Jun Sun <jsun@mvista.com>
To: linux-mips@linux-mips.org
Cc: jsun@mvista.com
Subject: Re: possible Malta 4Kc cache problem ...
Date: Wed, 4 Dec 2002 14:53:55 -0800 [thread overview]
Message-ID: <20021204145355.W4363@mvista.com> (raw)
In-Reply-To: <20021203224504.B13437@mvista.com>; from jsun@mvista.com on Tue, Dec 03, 2002 at 10:45:04PM -0800
[-- Attachment #1: Type: text/plain, Size: 4566 bytes --]
OK, I am fully convinced that this is some kind of hardware
problem. Basically, it appears CPU is running from a stale
icache line even though it has invalid flags!
In order to see how I draw this conclusion, you will need to
be a little patient.
Here is the relavent user code segment:
400f3c: 45010009 bc1t 400f64 <C_x_co_DIDI_J+0x84>
400f40: 8c510008 lw $s1,8($v0)
....
400f94: 4501000a bc1t 400fc0 <C_x_co_DIDI_J+0xe0>
400f98: 8fa20050 lw $v0,80($sp)
In both cases, branch are taken. The faulting symptom is that
"lw" in the second delay slot does not load correct value to $v0.
I wrote some sizable instrumentation code to capture what has happened
before and after "lw" emulation. A rough patch of my change is included for
the diehard hackers.
Here is the output from my captured data for the faulting "lw" case:
----------
dsemul_insns = 7fff78f0, epc=00400f98, cpc=00400fc0, ir=8fa20050
v0 = 10000028, s1=00000004
ret_epc = 7fff78f4, ret_ir=8fa20050, ret_sp=7fff7900, ret_sp_80val=00000004
ret_v0 = 10000028, ret_s1=00000004, bad_addr=00000000
cache tags before: 0018a8f2, 03b878f0, 000308f2, 03b888f2
cache tags after : 0018a8f2, 03b87800, 000308f2, 03b888f2
mem @ a3b878f0 : 8fa20050, 8c000001, 0000bd36, 00400fc0
----------
Several notes and observations:
. 7fff78f0 is in fact 83b878f0 (or a3b878f0) in kernel space
. ret_v0 is the v0 value after we return from trampoline execution. Wowla,
it has the same value as before, meaning "lw $v0,80($sp)" did not happen.
To further confirm that, I actually printed out the value at 80($sp) which is
"ret_sp_80val=00000004" (That is the right value $v0 should have, BTW).
. apparently the trampoline is executed, because we did come back from it
through unaligned access exception.
. In order to figure out what the first instruction is, I modified $s1 value
to 0x55 right before we start to execute trampoline code. Guess what, after
it comes back, $s1 is changed to "ret_s1=00000004", which suggests an stale instruction
"lw $s1,8($v0)" was executed. This instruction was put into icache during the previous
bc1t emulation.
. "cache tags before/after" dumps cache tags of all four ways in the same set as the
trampoline. It is clear that after flush_cache_sigtramp() the valid bits are correctly
cleared.
. The last line shows memory indeed has the right values. It is the icache
to blame.
. No page fault has happened during flush_cache_sigtramp() because bad_addr would
otherwise contains the faulting address.
It seems safe to conclude CPU executed from a stale icache line. I have no clue why
icache exhibits such a problem.
I modified flush_cache_sigtramp() to flush the whole icache, and things appear
to be working. However, not knowing the root cause I am not 100% sure
if this is a valid workaround.
Here are some info related to the CPU:
CPU revision is: 00018001
Primary instruction cache 16kb, linesize 16 bytes (4 ways)
Primary data cache 16kb, linesize 16 bytes (4 ways)
Jun
On Tue, Dec 03, 2002 at 10:45:04PM -0800, Jun Sun wrote:
>
> I attached the test case. Untar it. Type 'make' and run 'a.out'.
>
> If the test fails you will see a print-out. Otherwise you see nothing.
>
> It does not always fail. But if it fails, it is usually pretty consistent.
> Try a few times. Moving source tree to a different directory may cause
> the symptom appear or disappear.
>
> I spent quite some time to trace this problem, and came to suspect
> there might be a hardware problem.
>
> The problem involves emulating a "lw" instruction in cp1 branch delay
> slot, which needs to set up trampoline in user stack. The net effect
> looks as if the icache line or dcache line is not flushed properly.
>
> Using gdb/kgdb, printf or printk in any useful places would hide the bug.
>
> I did find a smaller part of the problem. flush_cache_sigtramp for
> MIPS32 (4Kc) calls protected_writeback_dcache_line in mips32_cache.h.
> It uses Hit_Writeback_D, and the 4Kc mannual says it is not implemented
> and executed as no-op (*ick*).
>
> Even after fixing this, I still see the problem happening.
>
> If you replace flush_cache_sigtramp() with flush_cache_all(), symptom
> would disppear.
>
> Several of my tests seem to suggest it is the icache that did not
> get flushed (or updated) properly.
>
> Not re-producible on other MIPS boards. At least so far.
>
> Does anybody with more knowledge about 4Kc have any clues here?
>
> Thanks.
>
> Jun
[-- Attachment #2: trace.patch --]
[-- Type: text/plain, Size: 10213 bytes --]
diff -Nru ./arch/mips/math-emu/cp1emu.c.orig ./arch/mips/math-emu/cp1emu.c
--- ./arch/mips/math-emu/cp1emu.c.orig Tue Oct 22 18:36:50 2002
+++ ./arch/mips/math-emu/cp1emu.c Wed Dec 4 13:44:55 2002
@@ -772,6 +772,47 @@
/* Instruction inserted following the AdELOAD to further tag the sequence */
#define BD_COOKIE 0x0000bd36 /* tne $0,$0 with baggage */
+int jsun_flag=0;
+unsigned long jsun_addr;
+struct {
+ unsigned dsemul_insns, epc, cpc, ir;
+ unsigned v0, s1;
+ unsigned ret_epc, ret_ir, ret_sp, ret_sp_80val;
+ unsigned ret_v0, ret_s1;
+ unsigned long bad_addr;
+ unsigned cache_tag[4], ret_cache_tag[4];
+ unsigned long kaddr;
+ unsigned kaddr_data[4];
+} jsun_array[200];
+int jsun_index;
+
+extern unsigned long user_to_kernel(unsigned long addr);
+extern int dcache_way_offset, icache_way_offset;
+
+#include <asm/cacheops.h>
+static inline unsigned load_icache_line_indexed(unsigned long addr)
+{
+ __asm__ __volatile__(
+ ".set noreorder\n\t"
+ ".set mips3\n\t"
+ "cache %1, (%0)\n\t"
+ ".set mips0\n\t"
+ ".set reorder"
+ :
+ : "r" (addr),
+ "i" (Index_Load_Tag_I));
+
+ return read_32bit_cp0_register(CP0_TAGLO);
+}
+
+void read_cache_tag(unsigned long addr, unsigned *tag)
+{
+ tag[0] = load_icache_line_indexed(addr);
+ tag[1] = load_icache_line_indexed(addr+icache_way_offset);
+ tag[2] = load_icache_line_indexed(addr+icache_way_offset*2);
+ tag[3] = load_icache_line_indexed(addr+icache_way_offset*3);
+}
+
int do_dsemulret(struct pt_regs *xcp)
{
unsigned long *pinst;
@@ -786,7 +827,7 @@
* If we can't even access the area, something is very wrong, but we'll
* leave that to the default handling
*/
- if (verify_area(VERIFY_READ, pinst, sizeof(unsigned long) * 3))
+ if (verify_area(VERIFY_READ, pinst-1, sizeof(unsigned long) * 4))
return 0;
/* Is the instruction pointed to by the EPC an AdELOAD? */
@@ -826,16 +867,77 @@
/* Set EPC to return to post-branch instruction */
xcp->cp0_epc = stackitem;
+ jsun_array[jsun_index].ret_epc = (unsigned long)pinst;
+ jsun_array[jsun_index].ret_ir = mips_get_word(xcp, pinst-1, &err);
+ jsun_array[jsun_index].ret_sp = xcp->regs[29];
+ jsun_array[jsun_index].ret_v0 = xcp->regs[2];
+ jsun_array[jsun_index].ret_s1 = xcp->regs[17];
+ jsun_array[jsun_index].ret_sp_80val = mips_get_word(xcp, xcp->regs[29]+80, &err);
+ if (jsun_array[jsun_index].ret_ir == 0x8fa20050) {
+ xcp->regs[17] = jsun_array[jsun_index].s1;
+ }
+
+ jsun_index ++;
+
return 1;
}
#define AdELOAD 0x8c000001 /* lw $0,1($0) */
+void jsun_dump_struct(void)
+{
+ int i;
+ printk("dump jsun_struct:\n");
+ for (i=0; i< jsun_index; i++) {
+ printk("------\n");
+ printk("dsemul_insns = %08x, epc=%08x, cpc=%08x, ir=%08x\n",
+ jsun_array[i].dsemul_insns,
+ jsun_array[i].epc,
+ jsun_array[i].cpc,
+ jsun_array[i].ir);
+ printk("v0 = %08x, s1=%08x\n",
+ jsun_array[i].v0,
+ jsun_array[i].s1);
+ printk("ret_epc = %08x, ret_ir=%08x, ret_sp=%08x, ret_sp_80val=%08x\n",
+ jsun_array[i].ret_epc,
+ jsun_array[i].ret_ir,
+ jsun_array[i].ret_sp,
+ jsun_array[i].ret_sp_80val);
+ printk("ret_v0 = %08x, ret_s1=%08x, bad_addr=%08lx\n",
+ jsun_array[i].ret_v0,
+ jsun_array[i].ret_s1,
+ jsun_array[i].bad_addr
+ );
+ printk("cache tags before: %08x, %08x, %08x, %08x\n",
+ jsun_array[i].cache_tag[0],
+ jsun_array[i].cache_tag[1],
+ jsun_array[i].cache_tag[2],
+ jsun_array[i].cache_tag[3]
+ );
+ printk("cache tags after : %08x, %08x, %08x, %08x\n",
+ jsun_array[i].ret_cache_tag[0],
+ jsun_array[i].ret_cache_tag[1],
+ jsun_array[i].ret_cache_tag[2],
+ jsun_array[i].ret_cache_tag[3]
+ );
+ printk("mem @ %08x : %08x, %08x, %08x, %08x\n",
+ jsun_array[i].kaddr + 0x20000000,
+ jsun_array[i].kaddr_data[0],
+ jsun_array[i].kaddr_data[1],
+ jsun_array[i].kaddr_data[2],
+ jsun_array[i].kaddr_data[3]);
+
+ }
+ jsun_index = 0;
+}
+
static int mips_dsemul(struct pt_regs *regs, mips_instruction ir, vaddr_t cpc)
{
mips_instruction *dsemul_insns;
extern asmlinkage void handle_dsemulret(void);
+ mips_instruction new_ir;
+ unsigned long temp;
if (ir == 0) { /* a nop is easy */
regs->cp0_epc = VA_TO_REG(cpc);
@@ -864,11 +966,17 @@
dsemul_insns = (mips_instruction *) (regs->regs[29] & ~0xf);
dsemul_insns -= 4; /* Retain 16-byte alignment */
+ temp=user_to_kernel((unsigned long)dsemul_insns);
+
/* Verify that the stack pointer is not competely insane */
if (verify_area
(VERIFY_WRITE, dsemul_insns, sizeof(mips_instruction) * 4))
return SIGBUS;
+ if (ir == 0x8fa20050) {
+ // ir = 0x24020004; // li v0, 9
+ // regs->regs[2]=0;
+ }
if (mips_put_word(regs, &dsemul_insns[0], ir)) {
fpuemuprivate.stats.errors++;
return (SIGBUS);
@@ -890,9 +998,50 @@
return (SIGBUS);
}
+
+ jsun_array[jsun_index].dsemul_insns = (unsigned long)dsemul_insns;
+ jsun_array[jsun_index].epc = (unsigned long)regs->cp0_epc;
+ jsun_array[jsun_index].cpc = cpc;
+ jsun_array[jsun_index].v0 = regs->regs[2];
+ jsun_array[jsun_index].s1 = regs->regs[17];
+ jsun_array[jsun_index].ir = *(unsigned long *)&ir;
+ jsun_array[jsun_index].kaddr = temp;
+
+ // jsun_index ++;
+
+ if (ir == 0x8fa20050) {
+ regs->regs[17] = 0x55;
+ }
+
regs->cp0_epc = VA_TO_REG & dsemul_insns[0];
+ read_cache_tag(temp, jsun_array[jsun_index].cache_tag);
+ jsun_addr=0;
flush_cache_sigtramp((unsigned long)dsemul_insns);
+ jsun_array[jsun_index].bad_addr = jsun_addr;
+ read_cache_tag(temp, jsun_array[jsun_index].ret_cache_tag);
+
+ jsun_array[jsun_index].kaddr_data[0] = *(unsigned*)(temp + 0x20000000);
+ jsun_array[jsun_index].kaddr_data[1] = *(unsigned*)(temp + 0x20000004);
+ jsun_array[jsun_index].kaddr_data[2] = *(unsigned*)(temp + 0x20000008);
+ jsun_array[jsun_index].kaddr_data[3] = *(unsigned*)(temp + 0x2000000c);
+
+ // my_flush_cache_sigtramp((unsigned long)dsemul_insns);
+ // flush_cache_all();
+
+#if 0
+ jsun_addr=user_to_kernel((unsigned long)dsemul_insns);
+ if (jsun_addr)
+ printk("mem @ %08x : %08x, %08x, %08x, %08x\n",
+ jsun_addr,
+ *(unsigned long*)jsun_addr,
+ *(unsigned long*)(jsun_addr+4),
+ *(unsigned long*)(jsun_addr+8),
+ *(unsigned long*)(jsun_addr+12));
+ else
+ printk("convertion failed\n");
+#endif
+
return SIGILL; /* force out of emulation loop */
}
diff -Nru ./arch/mips/mm/c-mips32.c.orig ./arch/mips/mm/c-mips32.c
--- ./arch/mips/mm/c-mips32.c.orig Tue Dec 3 18:54:52 2002
+++ ./arch/mips/mm/c-mips32.c Wed Dec 4 13:04:29 2002
@@ -39,6 +39,7 @@
/* Primary cache parameters. */
int icache_size, dcache_size; /* Size in bytes */
int ic_lsize, dc_lsize; /* LineSize in bytes */
+int icache_way_offset, dcache_way_offset; /* offset between ways w. same set */
/* Secondary cache (if present) parameters. */
unsigned int scache_size, sc_lsize; /* Again, in bytes */
@@ -407,6 +408,55 @@
* very much about what happens in that case. Usually a segmentation
* fault will dump the process later on anyway ...
*/
+static void indexed_writeback_dcache_line(unsigned long addr)
+{
+ int i;
+ int set_size = dcache_size / mips_cpu.dcache.ways;
+ for (i=0; i<mips_cpu.dcache.ways; i++) {
+ flush_dcache_line_indexed(addr);
+ addr += set_size;
+ }
+}
+
+void my_flush_cache_sigtramp(unsigned long addr)
+{
+ indexed_writeback_dcache_line(addr & ~(dc_lsize - 1));
+ protected_flush_icache_line(addr & ~(ic_lsize - 1));
+}
+
+unsigned long user_to_kernel(unsigned long addr)
+{
+ unsigned offset;
+ pgd_t *pgd;
+ pmd_t *pmd;
+ pte_t *pte;
+ mem_map_t *pg;
+ struct mm_struct *mm;
+
+ offset = addr & ~PAGE_MASK;
+ addr &= PAGE_MASK;
+ if (!current->mm ||
+ !current->mm->context ||
+ !current->active_mm ||
+ !current->active_mm->context ||
+ current->mm != current->active_mm) {
+ printk("user_to_kernel condition failed\n");
+ return 0;
+ }
+
+ mm= current->mm;
+ pgd = pgd_offset(mm, addr);
+ pmd = pmd_offset(pgd, addr);
+ pte = pte_offset(pmd, addr);
+ if(!(pte_val(*pte) & _PAGE_VALID)) {
+ printk("user_to_kernel : pte not valid\n");
+ return 0;
+ }
+
+ pg= pte_page(*pte);
+ return (unsigned long)pg->virtual + offset;
+}
+
static void mips32_flush_cache_sigtramp(unsigned long addr)
{
protected_writeback_dcache_line(addr & ~(dc_lsize - 1));
@@ -451,6 +501,7 @@
}
printk("Primary instruction cache %dkb, linesize %d bytes (%d ways)\n",
icache_size >> 10, ic_lsize, mips_cpu.icache.ways);
+ icache_way_offset = icache_size / mips_cpu.icache.ways;
}
static void __init probe_dcache(unsigned long config)
@@ -490,6 +541,7 @@
}
printk("Primary data cache %dkb, linesize %d bytes (%d ways)\n",
dcache_size >> 10, dc_lsize, mips_cpu.dcache.ways);
+ dcache_way_offset = dcache_size / mips_cpu.dcache.ways;
}
diff -Nru ./arch/mips/mm/fault.c.orig ./arch/mips/mm/fault.c
--- ./arch/mips/mm/fault.c.orig Tue Oct 22 18:36:51 2002
+++ ./arch/mips/mm/fault.c Wed Dec 4 10:35:26 2002
@@ -74,6 +74,7 @@
* and the problem, and then passes it off to one of the appropriate
* routines.
*/
+extern unsigned long jsun_addr;
asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long write,
unsigned long address)
{
@@ -193,6 +194,7 @@
long new_epc;
tsk->thread.cp0_baduaddr = address;
+ jsun_addr = address;
new_epc = fixup_exception(dpf_reg, fixup, regs->cp0_epc);
if (development_version)
printk(KERN_DEBUG "%s: Exception at [<%lx>] (%lx)\n",
diff -Nru ./include/asm-mips/mips32_cache.h.orig ./include/asm-mips/mips32_cache.h
--- ./include/asm-mips/mips32_cache.h.orig Tue Oct 22 18:37:03 2002
+++ ./include/asm-mips/mips32_cache.h Tue Dec 3 18:24:25 2002
@@ -171,7 +171,7 @@
".previous"
:
: "r" (addr),
- "i" (Hit_Writeback_D));
+ "i" (Hit_Writeback_Inv_D));
}
#define cache_unroll(base,op) \
diff -Nru ./net/ipv4/icmp.c.orig ./net/ipv4/icmp.c
--- ./net/ipv4/icmp.c.orig Mon Feb 25 11:38:14 2002
+++ ./net/ipv4/icmp.c Tue Dec 3 12:08:01 2002
@@ -733,6 +733,7 @@
static void icmp_echo(struct sk_buff *skb)
{
+ jsun_dump_struct();
if (!sysctl_icmp_echo_ignore_all) {
struct icmp_bxm icmp_param;
prev parent reply other threads:[~2002-12-04 22:54 UTC|newest]
Thread overview: 29+ messages / expand[flat|nested] mbox.gz Atom feed top
2002-12-04 6:45 possible Malta 4Kc cache problem Jun Sun
2002-12-04 9:38 ` Kevin D. Kissell
2002-12-04 9:38 ` Kevin D. Kissell
2002-12-04 9:46 ` Kevin D. Kissell
2002-12-04 9:46 ` Kevin D. Kissell
2002-12-04 10:08 ` Carsten Langgaard
2002-12-04 11:21 ` Carsten Langgaard
2002-12-04 13:06 ` Kevin D. Kissell
2002-12-04 13:06 ` Kevin D. Kissell
2002-12-04 13:14 ` Carsten Langgaard
2002-12-04 13:28 ` Carsten Langgaard
2002-12-04 17:08 ` Kevin D. Kissell
2002-12-04 17:08 ` Kevin D. Kissell
2002-12-04 17:32 ` Daniel Jacobowitz
2002-12-04 20:28 ` Carsten Langgaard
2002-12-04 22:58 ` Jun Sun
2002-12-05 9:38 ` Carsten Langgaard
2002-12-06 16:42 ` Maciej W. Rozycki
2002-12-06 22:24 ` Hartvig Ekner
2002-12-06 22:24 ` Hartvig Ekner
2002-12-09 10:51 ` Dominic Sweetman
2002-12-09 10:51 ` Dominic Sweetman
2002-12-04 22:19 ` Jun Sun
2002-12-05 9:27 ` Kevin D. Kissell
2002-12-05 9:27 ` Kevin D. Kissell
2002-12-04 21:59 ` Jun Sun
2002-12-04 23:14 ` Kevin D. Kissell
2002-12-04 23:14 ` Kevin D. Kissell
2002-12-04 22:53 ` Jun Sun [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20021204145355.W4363@mvista.com \
--to=jsun@mvista.com \
--cc=linux-mips@linux-mips.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox