From 41e341f260c5e836067682e14d086ef02217e5e2 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Sat, 31 Jan 2026 16:06:20 +1100 Subject: [PATCH 1/3] icache: Clear fetch failed flag on flush This fixes a bug where a load that results in a DSI, if it is placed near the end of a page and the following page isn't mapped, can result in the core starting to take the DSI but then jumping off to the ISI vector. Signed-off-by: Paul Mackerras --- icache.vhdl | 1 + 1 file changed, 1 insertion(+) diff --git a/icache.vhdl b/icache.vhdl index 7b0ae59..1240d8b 100644 --- a/icache.vhdl +++ b/icache.vhdl @@ -595,6 +595,7 @@ begin r.hit_valid <= '0'; r.stalled_hit <= '0'; r.stalled_way <= to_unsigned(0, WAY_BITS); + r.fetch_failed <= '0'; elsif stall_in = '1' then if r.state = CLR_TAG then r.stalled_hit <= '0'; From a1d83ba91a17e2812e8f3d99e01df9ca05deb414 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Sat, 31 Jan 2026 16:08:22 +1100 Subject: [PATCH 2/3] tests/mmu: Add a test for a faulting load near the end of a page This tests for the bug where a load near the end of a page, if the load faults and the following page isn't mapped, could cause a DSI followed incorrectly by an ISI shortly afterwards. Signed-off-by: Paul Mackerras --- tests/mmu/head.S | 76 +++++++++++++++++++++---------- tests/mmu/mmu.c | 91 ++++++++++++++++++++++--------------- tests/test_mmu.bin | Bin 24608 -> 24608 bytes tests/test_mmu.console_out | 1 + 4 files changed, 109 insertions(+), 59 deletions(-) diff --git a/tests/mmu/head.S b/tests/mmu/head.S index 824ad67..c2b5731 100644 --- a/tests/mmu/head.S +++ b/tests/mmu/head.S @@ -23,6 +23,7 @@ ori r,r, (e)@l; .section ".head","ax" + .machine "power10" /* * Microwatt currently enters in LE mode at 0x0, so we don't need to @@ -45,82 +46,97 @@ _start: 2: LOAD_IMM64(%r1,__stack_top) li %r0,0 stdu %r0,-16(%r1) + pla %r9,3f-. + mtsprg1 %r9 LOAD_IMM64(%r12, main) mtctr %r12 bctrl - attn // terminate on exit +3: attn // terminate on exit b . /* Read a location with translation on */ .globl test_read test_read: + mfsprg1 %r10 + pla %r9,1f-. + mtsprg1 %r9 mfmsr %r9 ori %r8,%r9,0x10 /* set MSR_DR */ mtmsrd %r8,0 mr %r6,%r3 li %r3,0 ld %r5,0(%r6) - li %r3,1 - /* land here if DSI occurred */ mtmsrd %r9,0 +1: /* land here if DSI occurred */ std %r5,0(%r4) + mtsprg1 %r10 blr /* Write a location with translation on */ .globl test_write test_write: + mfsprg1 %r10 + pla %r9,1f-. + mtsprg1 %r9 mfmsr %r9 ori %r8,%r9,0x10 /* set MSR_DR */ mtmsrd %r8,0 mr %r6,%r3 li %r3,0 std %r4,0(%r6) - li %r3,1 - /* land here if DSI occurred */ mtmsrd %r9,0 +1: /* land here if DSI occurred */ + mtsprg1 %r10 blr /* Do a dcbz with translation on */ .globl test_dcbz test_dcbz: + mfsprg1 %r10 + pla %r9,1f-. + mtsprg1 %r9 mfmsr %r9 ori %r8,%r9,0x10 /* set MSR_DR */ mtmsrd %r8,0 mr %r6,%r3 li %r3,0 dcbz 0,%r6 - li %r3,1 - /* land here if DSI occurred */ mtmsrd %r9,0 +1: /* land here if DSI occurred */ + mtsprg1 %r10 blr .globl test_exec test_exec: + mfsprg1 %r10 + pla %r9,1f-. + mtsprg1 %r9 mtsrr0 %r4 mtsrr1 %r5 rfid +1: /* land here if an interrupt occurred */ + mtsprg1 %r10 + blr #define EXCEPTION(nr) \ .= nr ;\ attn - /* DSI vector - skip the failing instruction + the next one */ + /* DSI vector - set r3 to 0x300, jump to addr in SPRG1 */ . = 0x300 - mtsprg0 %r10 - mfsrr0 %r10 - addi %r10,%r10,8 - mtsrr0 %r10 - rfid + li %r3,0x300 + mfsprg1 %r9 + mtctr %r9 + bctr EXCEPTION(0x380) - /* - * ISI vector - jump to LR to return from the test, - * with r3 cleared - */ + /* ISI vector - set r3 to 0x400, jump to addr in SPRG1 */ . = 0x400 - li %r3,0 - blr + li %r3,0x400 + mfsprg1 %r9 + mtctr %r9 + bctr /* More exception stubs */ EXCEPTION(0x480) @@ -138,7 +154,9 @@ test_exec: * may have been set. */ . = 0xc00 - blr + mfsprg1 %r9 + mtctr %r9 + bctr EXCEPTION(0xd00) EXCEPTION(0xe00) @@ -162,12 +180,13 @@ test_exec: test_start: nop nop - cmpdi %r3,1 + mr %r6,%r3 + li %r3,0 + cmpdi %r6,1 beq test_1 - cmpdi %r3,2 + cmpdi %r6,2 beq test_2 test_return: - li %r3,1 sc . = 0x1ff8 @@ -177,3 +196,14 @@ test_1: b test_return /* test flowing from one page to the next */ test_2: nop b test_return + + . = 0x2fe8 + /* test generating an interrupt just before an instruction page fault */ + .globl test_2_faults +test_2_faults: + nop + nop + mr %r6,%r3 + li %r3,0 + lwz %r7,0(%r6) + ld %r7,0(%r7) diff --git a/tests/mmu/mmu.c b/tests/mmu/mmu.c index ff6a582..749c121 100644 --- a/tests/mmu/mmu.c +++ b/tests/mmu/mmu.c @@ -192,7 +192,7 @@ int mmu_test_1(void) long val; /* this should fail */ - if (test_read(ptr, &val, 0xdeadbeefd00d)) + if (!test_read(ptr, &val, 0xdeadbeefd00d)) return 1; /* dest reg of load should be unchanged */ if (val != 0xdeadbeefd00d) @@ -215,7 +215,7 @@ int mmu_test_2(void) /* initialize the memory content */ mem[33] = 0xbadc0ffee; /* this should succeed and be a cache miss */ - if (!test_read(&ptr[33], &val, 0xdeadbeefd00d)) + if (test_read(&ptr[33], &val, 0xdeadbeefd00d)) return 1; /* dest reg of load should have the value written */ if (val != 0xbadc0ffee) @@ -223,13 +223,13 @@ int mmu_test_2(void) /* load a second TLB entry in the same set as the first */ map(ptr2, mem, DFLT_PERM); /* this should succeed and be a cache hit */ - if (!test_read(&ptr2[33], &val, 0xdeadbeefd00d)) + if (test_read(&ptr2[33], &val, 0xdeadbeefd00d)) return 3; /* dest reg of load should have the value written */ if (val != 0xbadc0ffee) return 4; /* check that the first entry still works */ - if (!test_read(&ptr[33], &val, 0xdeadbeefd00d)) + if (test_read(&ptr[33], &val, 0xdeadbeefd00d)) return 5; if (val != 0xbadc0ffee) return 6; @@ -247,7 +247,7 @@ int mmu_test_3(void) /* initialize the memory content */ mem[45] = 0xfee1800d4ea; /* this should succeed and be a cache miss */ - if (!test_read(&ptr[45], &val, 0xdeadbeefd0d0)) + if (test_read(&ptr[45], &val, 0xdeadbeefd0d0)) return 1; /* dest reg of load should have the value written */ if (val != 0xfee1800d4ea) @@ -255,7 +255,7 @@ int mmu_test_3(void) /* remove the PTE */ unmap(ptr); /* this should fail */ - if (test_read(&ptr[45], &val, 0xdeadbeefd0d0)) + if (!test_read(&ptr[45], &val, 0xdeadbeefd0d0)) return 3; /* dest reg of load should be unchanged */ if (val != 0xdeadbeefd0d0) @@ -278,7 +278,7 @@ int mmu_test_4(void) /* initialize the memory content */ mem[27] = 0xf00f00f00f00; /* this should succeed and be a cache miss */ - if (!test_write(&ptr[27], 0xe44badc0ffee)) + if (test_write(&ptr[27], 0xe44badc0ffee)) return 1; /* memory should now have the value written */ if (mem[27] != 0xe44badc0ffee) @@ -286,14 +286,14 @@ int mmu_test_4(void) /* load a second TLB entry in the same set as the first */ map(ptr2, mem, DFLT_PERM); /* this should succeed and be a cache hit */ - if (!test_write(&ptr2[27], 0x6e11ae)) + if (test_write(&ptr2[27], 0x6e11ae)) return 3; /* memory should have the value written */ if (mem[27] != 0x6e11ae) return 4; /* check that the first entry still exists */ /* (assumes TLB is 2-way associative or more) */ - if (!test_read(&ptr[27], &val, 0xdeadbeefd00d)) + if (test_read(&ptr[27], &val, 0xdeadbeefd00d)) return 5; if (val != 0x6e11ae) return 6; @@ -309,7 +309,7 @@ int mmu_test_5(void) /* create PTE */ map(ptr, mem, DFLT_PERM); /* this should fail */ - if (test_read(ptr, &val, 0xdeadbeef0dd0)) + if (!test_read(ptr, &val, 0xdeadbeef0dd0)) return 1; /* dest reg of load should be unchanged */ if (val != 0xdeadbeef0dd0) @@ -330,7 +330,7 @@ int mmu_test_6(void) /* initialize memory */ *mem = 0x123456789abcdef0; /* this should fail */ - if (test_write(ptr, 0xdeadbeef0dd0)) + if (!test_write(ptr, 0xdeadbeef0dd0)) return 1; /* DAR and DSISR should be set correctly */ if (mfspr(DAR) != ((long)ptr & ~0xfff) + 0x1000 || mfspr(DSISR) != 0x42000000) @@ -348,7 +348,7 @@ int mmu_test_7(void) /* create PTE without R or C */ map(ptr, mem, PERM_RD | PERM_WR); /* this should fail */ - if (test_read(ptr, &val, 0xdeadd00dbeef)) + if (!test_read(ptr, &val, 0xdeadd00dbeef)) return 1; /* dest reg of load should be unchanged */ if (val != 0xdeadd00dbeef) @@ -357,7 +357,7 @@ int mmu_test_7(void) if (mfspr(DAR) != (long) ptr || mfspr(DSISR) != 0x00040000) return 3; /* this should fail */ - if (test_write(ptr, 0xdeadbeef0dd0)) + if (!test_write(ptr, 0xdeadbeef0dd0)) return 4; /* DAR and DSISR should be set correctly */ if (mfspr(DAR) != (long)ptr || mfspr(DSISR) != 0x02040000) @@ -378,10 +378,10 @@ int mmu_test_8(void) /* create PTE with R but not C */ map(ptr, mem, REF | PERM_RD | PERM_WR); /* this should succeed */ - if (!test_read(ptr, &val, 0xdeadd00dbeef)) + if (test_read(ptr, &val, 0xdeadd00dbeef)) return 1; /* this should fail */ - if (test_write(ptr, 0xdeadbeef0dd1)) + if (!test_write(ptr, 0xdeadbeef0dd1)) return 2; /* DAR and DSISR should be set correctly */ if (mfspr(DAR) != (long)ptr || mfspr(DSISR) != 0x02040000) @@ -402,7 +402,7 @@ int mmu_test_9(void) /* create PTE without read or write permission */ map(ptr, mem, REF); /* this should fail */ - if (test_read(ptr, &val, 0xdeadd00dbeef)) + if (!test_read(ptr, &val, 0xdeadd00dbeef)) return 1; /* dest reg of load should be unchanged */ if (val != 0xdeadd00dbeef) @@ -411,7 +411,7 @@ int mmu_test_9(void) if (mfspr(DAR) != (long) ptr || mfspr(DSISR) != 0x08000000) return 3; /* this should fail */ - if (test_write(ptr, 0xdeadbeef0dd1)) + if (!test_write(ptr, 0xdeadbeef0dd1)) return 4; /* DAR and DSISR should be set correctly */ if (mfspr(DAR) != (long)ptr || mfspr(DSISR) != 0x0a000000) @@ -432,10 +432,10 @@ int mmu_test_10(void) /* create PTE with read but not write permission */ map(ptr, mem, REF | PERM_RD); /* this should succeed */ - if (!test_read(ptr, &val, 0xdeadd00dbeef)) + if (test_read(ptr, &val, 0xdeadd00dbeef)) return 1; /* this should fail */ - if (test_write(ptr, 0xdeadbeef0dd1)) + if (!test_write(ptr, 0xdeadbeef0dd1)) return 2; /* DAR and DSISR should be set correctly */ if (mfspr(DAR) != (long)ptr || mfspr(DSISR) != 0x0a000000) @@ -451,7 +451,7 @@ int mmu_test_11(void) unsigned long ptr = 0x523000; /* this should fail */ - if (test_exec(0, ptr, MSR_SF | MSR_HV | MSR_IR | MSR_LE)) + if (!test_exec(0, ptr, MSR_SF | MSR_HV | MSR_IR | MSR_LE)) return 1; /* SRR0 and SRR1 should be set correctly */ if (mfspr(SRR0) != (long) ptr || @@ -469,12 +469,12 @@ int mmu_test_12(void) /* create PTE */ map((void *)ptr, (void *)mem, PERM_EX | REF); /* this should succeed and be a cache miss */ - if (!test_exec(0, ptr, MSR_SF | MSR_HV | MSR_IR | MSR_LE)) + if (test_exec(0, ptr, MSR_SF | MSR_HV | MSR_IR | MSR_LE)) return 1; /* create a second PTE */ map((void *)ptr2, (void *)mem, PERM_EX | REF); /* this should succeed and be a cache hit */ - if (!test_exec(0, ptr2, MSR_SF | MSR_HV | MSR_IR | MSR_LE)) + if (test_exec(0, ptr2, MSR_SF | MSR_HV | MSR_IR | MSR_LE)) return 2; return 0; } @@ -488,14 +488,14 @@ int mmu_test_13(void) /* create a PTE */ map((void *)ptr, (void *)mem, PERM_EX | REF); /* this should succeed */ - if (!test_exec(1, ptr, MSR_SF | MSR_HV | MSR_IR | MSR_LE)) + if (test_exec(1, ptr, MSR_SF | MSR_HV | MSR_IR | MSR_LE)) return 1; /* invalidate the PTE */ unmap((void *)ptr); /* install a second PTE */ map((void *)ptr2, (void *)mem, PERM_EX | REF); /* this should fail */ - if (test_exec(1, ptr, MSR_SF | MSR_HV | MSR_IR | MSR_LE)) + if (!test_exec(1, ptr, MSR_SF | MSR_HV | MSR_IR | MSR_LE)) return 2; /* SRR0 and SRR1 should be set correctly */ if (mfspr(SRR0) != (long) ptr || @@ -514,7 +514,7 @@ int mmu_test_14(void) /* create a PTE */ map((void *)ptr, (void *)mem, PERM_EX | REF); /* this should fail due to second page not being mapped */ - if (test_exec(2, ptr, MSR_SF | MSR_HV | MSR_IR | MSR_LE)) + if (!test_exec(2, ptr, MSR_SF | MSR_HV | MSR_IR | MSR_LE)) return 1; /* SRR0 and SRR1 should be set correctly */ if (mfspr(SRR0) != ptr2 || @@ -523,7 +523,7 @@ int mmu_test_14(void) /* create a PTE for the second page */ map((void *)ptr2, (void *)mem2, PERM_EX | REF); /* this should succeed */ - if (!test_exec(2, ptr, MSR_SF | MSR_HV | MSR_IR | MSR_LE)) + if (test_exec(2, ptr, MSR_SF | MSR_HV | MSR_IR | MSR_LE)) return 3; return 0; } @@ -536,7 +536,7 @@ int mmu_test_15(void) /* create a PTE without execute permission */ map((void *)ptr, (void *)mem, DFLT_PERM); /* this should fail */ - if (test_exec(0, ptr, MSR_SF | MSR_HV | MSR_IR | MSR_LE)) + if (!test_exec(0, ptr, MSR_SF | MSR_HV | MSR_IR | MSR_LE)) return 1; /* SRR0 and SRR1 should be set correctly */ if (mfspr(SRR0) != ptr || @@ -557,7 +557,7 @@ int mmu_test_16(void) /* create a PTE for the second page without execute permission */ map((void *)ptr2, (void *)mem2, PERM_RD | REF); /* this should fail due to second page being no-execute */ - if (test_exec(2, ptr, MSR_SF | MSR_HV | MSR_IR | MSR_LE)) + if (!test_exec(2, ptr, MSR_SF | MSR_HV | MSR_IR | MSR_LE)) return 1; /* SRR0 and SRR1 should be set correctly */ if (mfspr(SRR0) != ptr2 || @@ -566,7 +566,7 @@ int mmu_test_16(void) /* create a PTE for the second page with execute permission */ map((void *)ptr2, (void *)mem2, PERM_RD | PERM_EX | REF); /* this should succeed */ - if (!test_exec(2, ptr, MSR_SF | MSR_HV | MSR_IR | MSR_LE)) + if (test_exec(2, ptr, MSR_SF | MSR_HV | MSR_IR | MSR_LE)) return 3; return 0; } @@ -579,7 +579,7 @@ int mmu_test_17(void) /* create a PTE without the ref bit set */ map((void *)ptr, (void *)mem, PERM_EX); /* this should fail */ - if (test_exec(2, ptr, MSR_SF | MSR_HV | MSR_IR | MSR_LE)) + if (!test_exec(2, ptr, MSR_SF | MSR_HV | MSR_IR | MSR_LE)) return 1; /* SRR0 and SRR1 should be set correctly */ if (mfspr(SRR0) != (long) ptr || @@ -589,7 +589,7 @@ int mmu_test_17(void) unmap((void *)ptr); map((void *)ptr, (void *)mem, 0); /* this should fail */ - if (test_exec(2, ptr, MSR_SF | MSR_HV | MSR_IR | MSR_LE)) + if (!test_exec(2, ptr, MSR_SF | MSR_HV | MSR_IR | MSR_LE)) return 1; /* SRR0 and SRR1 should be set correctly */ /* RC update fail bit should not be set */ @@ -608,12 +608,12 @@ int mmu_test_18(void) /* create PTE */ map(ptr, mem, DFLT_PERM); /* this should succeed and be a cache miss */ - if (!test_dcbz(&ptr[129])) + if (test_dcbz(&ptr[129])) return 1; /* create a second PTE */ map(ptr2, mem, DFLT_PERM); /* this should succeed and be a cache hit */ - if (!test_dcbz(&ptr2[130])) + if (test_dcbz(&ptr2[130])) return 2; return 0; } @@ -627,7 +627,7 @@ int mmu_test_19(void) /* create PTE with read but not write permission */ map(ptr, mem, REF | PERM_RD); /* this should fail and create a TLB entry */ - if (test_write(ptr, 0xdeadbeef0dd1)) + if (!test_write(ptr, 0xdeadbeef0dd1)) return 1; /* DAR and DSISR should be set correctly */ if (mfspr(DAR) != (long)ptr || mfspr(DSISR) != 0x0a000000) @@ -635,11 +635,26 @@ int mmu_test_19(void) /* Update the PTE to have write permission */ map(ptr, mem, REF | CHG | PERM_RD | PERM_WR); /* this should succeed */ - if (!test_write(ptr, 0xdeadbeef0dd1)) + if (test_write(ptr, 0xdeadbeef0dd1)) return 3; return 0; } +int mmu_test_20(void) +{ + unsigned long mem = 0x2000; + unsigned long ptr = 0x324000; + unsigned long ret; + + /* create a PTE with execute permission */ + map((void *)ptr, (void *)mem, PERM_EX | PERM_PRIV | REF); + ret = test_exec(0x124000, ptr + 0xfe8, MSR_SF | MSR_HV | MSR_IR | MSR_DR | MSR_LE); + /* Should see a 300 from the load, not a 400 from the page crossing */ + if (ret != 0x300) + return ret + 1; + return 0; +} + int fail = 0; void do_test(int num, int (*test)(void)) @@ -656,7 +671,10 @@ void do_test(int num, int (*test)(void)) } else { fail = 1; print_string("FAIL "); - putchar(ret + '0'); + if (ret < 10) + putchar(ret + '0'); + else + print_hex(ret); if (num <= 10 || num == 19) { print_string(" DAR="); print_hex(mfspr(DAR)); @@ -696,6 +714,7 @@ int main(void) do_test(17, mmu_test_17); do_test(18, mmu_test_18); do_test(19, mmu_test_19); + do_test(20, mmu_test_20); return fail; } diff --git a/tests/test_mmu.bin b/tests/test_mmu.bin index 6e352a773ba39b843f5e1bc1aaa9abb2beaa0d21..1c4f87692c501636e8c8401cf41255f246c12b70 100755 GIT binary patch literal 24608 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 M2uCSfbu!EU15lie&j0`b literal 24608 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 diff --git a/tests/test_mmu.console_out b/tests/test_mmu.console_out index cb4ad85..aea206f 100644 --- a/tests/test_mmu.console_out +++ b/tests/test_mmu.console_out @@ -17,3 +17,4 @@ test 16:PASS test 17:PASS test 18:PASS test 19:PASS +test 20:PASS From 6100e7b50e5a0e25bdb0dd329df334cae90f57a7 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Wed, 4 Feb 2026 08:48:34 +1100 Subject: [PATCH 3/3] dcache: Fix another dcache bug causing occasional load data corruption Commit a7420c2a4d69 ("dcache: Fix bug causing load to return incorrect data", 2025-12-27) fixed the main cause of the bug, but left a 1-cycle window where the same problem could still occur. If a touch that misses in the dcache is followed immediately by a load to a different cache line with the same index, then because the touch is completed and the new tag is written for the line being touched in the same cycle, it is possible for the following load to use the previous (stale) tag value for the line. If that old value matches the load (i.e., the load would have been a hit in the absence of the touch) then the load will incorrectly return data from the line being touched. Fix this by delaying the completion of the touch until after the new tag has been written, which is indicated by r1.write_tag = 0. Fixes: a7420c2a4d69 ("dcache: Fix bug causing load to return incorrect data", 2025-12-27) Signed-off-by: Paul Mackerras --- dcache.vhdl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dcache.vhdl b/dcache.vhdl index 0d8d354..dcde2ee 100644 --- a/dcache.vhdl +++ b/dcache.vhdl @@ -1703,8 +1703,10 @@ begin r1.wb.adr <= next_row_wb_addr(r1.wb.adr); end if; - -- If this is a touch, complete the instruction - if r1.full = '1' and r1.req.touch = '1' and r1.req.hit_reload = '1' then + -- If this is a touch, complete the instruction, but not + -- until the new tag for this cache line has been written. + if r1.full = '1' and r1.req.touch = '1' and r1.req.hit_reload = '1' and + r1.write_tag = '0' then r1.full <= '0'; r1.slow_valid <= '1'; r1.ls_valid <= '1';