From b342312e4e2b0301234c128d2d6a0d350ec30609 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Tue, 28 Apr 2020 16:00:00 +1000 Subject: [PATCH] tests: mmu: Add tests for instruction translation This adds tests of instruction translation to the mmu test. This also clears the BSS and improves the linker script. Signed-off-by: Paul Mackerras --- tests/mmu/head.S | 73 ++++++++++-- tests/mmu/mmu.c | 226 ++++++++++++++++++++++++++++++++----- tests/mmu/powerpc.lds | 26 ++++- tests/test_mmu.bin | Bin 12304 -> 20496 bytes tests/test_mmu.console_out | 7 ++ 5 files changed, 288 insertions(+), 44 deletions(-) diff --git a/tests/mmu/head.S b/tests/mmu/head.S index 3627cff..083b1c5 100644 --- a/tests/mmu/head.S +++ b/tests/mmu/head.S @@ -14,8 +14,6 @@ * limitations under the License. */ -#define STACK_TOP 0x4000 - /* Load an immediate 64-bit value into a register */ #define LOAD_IMM64(r, e) \ lis r,(e)@highest; \ @@ -33,12 +31,20 @@ . = 0 .global _start _start: - b boot_entry + LOAD_IMM64(%r10,__bss_start) + LOAD_IMM64(%r11,__bss_end) + subf %r11,%r10,%r11 + addi %r11,%r11,63 + srdi. %r11,%r11,6 + beq 2f + mtctr %r11 +1: dcbz 0,%r10 + addi %r10,%r10,64 + bdnz 1b -.global boot_entry -boot_entry: - /* setup stack */ - LOAD_IMM64(%r1, STACK_TOP - 0x100) +2: LOAD_IMM64(%r1,__stack_top) + li %r0,0 + stdu %r0,-16(%r1) LOAD_IMM64(%r12, main) mtctr %r12 bctrl @@ -74,6 +80,12 @@ test_write: mtmsrd %r9,0 blr + .globl test_exec +test_exec: + mtsrr0 %r4 + mtsrr1 %r5 + rfid + #define EXCEPTION(nr) \ .= nr ;\ attn @@ -86,9 +98,17 @@ test_write: mtsrr0 %r10 rfid - /* More exception stubs */ EXCEPTION(0x380) - EXCEPTION(0x400) + + /* + * ISI vector - jump to LR to return from the test, + * with r3 cleared + */ + . = 0x400 + li %r3,0 + blr + + /* More exception stubs */ EXCEPTION(0x480) EXCEPTION(0x500) EXCEPTION(0x600) @@ -98,7 +118,14 @@ test_write: EXCEPTION(0x980) EXCEPTION(0xa00) EXCEPTION(0xb00) - EXCEPTION(0xc00) + + /* + * System call - used to exit from tests where MSR[PR] + * may have been set. + */ + . = 0xc00 + blr + EXCEPTION(0xd00) EXCEPTION(0xe00) EXCEPTION(0xe20) @@ -110,3 +137,29 @@ test_write: EXCEPTION(0xf40) EXCEPTION(0xf60) EXCEPTION(0xf80) + + . = 0x1000 + /* + * This page gets mapped at various locations and + * the tests try to execute from it. + * r3 contains the test number. + */ + .globl test_start +test_start: + nop + nop + cmpdi %r3,1 + beq test_1 + cmpdi %r3,2 + beq test_2 +test_return: + li %r3,1 + sc + + . = 0x1ff8 + /* test a branch near the end of a page */ +test_1: b test_return + + /* test flowing from one page to the next */ +test_2: nop + b test_return diff --git a/tests/mmu/mmu.c b/tests/mmu/mmu.c index 0a717c7..a44c79d 100644 --- a/tests/mmu/mmu.c +++ b/tests/mmu/mmu.c @@ -4,14 +4,23 @@ #include "console.h" +#define MSR_DR 0x10 +#define MSR_IR 0x20 + extern int test_read(long *addr, long *ret, long init); extern int test_write(long *addr, long val); +extern int test_exec(int testno, unsigned long pc, unsigned long msr); static inline void do_tlbie(unsigned long rb, unsigned long rs) { __asm__ volatile("tlbie %0,%1" : : "r" (rb), "r" (rs) : "memory"); } +#define DSISR 18 +#define DAR 19 +#define SRR0 26 +#define SRR1 27 + static inline unsigned long mfspr(int sprnum) { long val; @@ -135,6 +144,8 @@ void map(void *ea, void *pa, unsigned long perm_attr) free_ptr += 512 * sizeof(unsigned long); } ptep = read_pgd(i); + if (ptep[j]) + do_tlbie(((unsigned long)ea & ~0xfff), 0); store_pte(&ptep[j], 0xc000000000000000 | ((unsigned long)pa & 0x00fffffffffff000) | perm_attr); eas_mapped[neas_mapped++] = ea; } @@ -175,14 +186,14 @@ int mmu_test_1(void) if (val != 0xdeadbeefd00d) return 2; /* DAR and DSISR should be set correctly */ - if (mfspr(19) != (long) ptr || mfspr(18) != 0x40000000) + if (mfspr(DAR) != (long) ptr || mfspr(DSISR) != 0x40000000) return 3; return 0; } int mmu_test_2(void) { - long *mem = (long *) 0x4000; + long *mem = (long *) 0x8000; long *ptr = (long *) 0x124000; long *ptr2 = (long *) 0x1124000; long val; @@ -215,8 +226,8 @@ int mmu_test_2(void) int mmu_test_3(void) { - long *mem = (long *) 0x5000; - long *ptr = (long *) 0x149000; + long *mem = (long *) 0x9000; + long *ptr = (long *) 0x14a000; long val; /* create PTE */ @@ -238,16 +249,16 @@ int mmu_test_3(void) if (val != 0xdeadbeefd0d0) return 4; /* DAR and DSISR should be set correctly */ - if (mfspr(19) != (long) &ptr[45] || mfspr(18) != 0x40000000) + if (mfspr(DAR) != (long) &ptr[45] || mfspr(DSISR) != 0x40000000) return 5; return 0; } int mmu_test_4(void) { - long *mem = (long *) 0x6000; - long *ptr = (long *) 0x10a000; - long *ptr2 = (long *) 0x110a000; + long *mem = (long *) 0xa000; + long *ptr = (long *) 0x10b000; + long *ptr2 = (long *) 0x110b000; long val; /* create PTE */ @@ -279,7 +290,7 @@ int mmu_test_4(void) int mmu_test_5(void) { - long *mem = (long *) 0x7ffd; + long *mem = (long *) 0xbffd; long *ptr = (long *) 0x39fffd; long val; @@ -292,14 +303,14 @@ int mmu_test_5(void) if (val != 0xdeadbeef0dd0) return 2; /* DAR and DSISR should be set correctly */ - if (mfspr(19) != ((long)ptr & ~0xfff) + 0x1000 || mfspr(18) != 0x40000000) + if (mfspr(DAR) != ((long)ptr & ~0xfff) + 0x1000 || mfspr(DSISR) != 0x40000000) return 3; return 0; } int mmu_test_6(void) { - long *mem = (long *) 0x7ffd; + long *mem = (long *) 0xbffd; long *ptr = (long *) 0x39fffd; /* create PTE */ @@ -310,14 +321,14 @@ int mmu_test_6(void) if (test_write(ptr, 0xdeadbeef0dd0)) return 1; /* DAR and DSISR should be set correctly */ - if (mfspr(19) != ((long)ptr & ~0xfff) + 0x1000 || mfspr(18) != 0x42000000) + if (mfspr(DAR) != ((long)ptr & ~0xfff) + 0x1000 || mfspr(DSISR) != 0x42000000) return 2; return 0; } int mmu_test_7(void) { - long *mem = (long *) 0x4000; + long *mem = (long *) 0x8000; long *ptr = (long *) 0x124000; long val; @@ -331,13 +342,13 @@ int mmu_test_7(void) if (val != 0xdeadd00dbeef) return 2; /* DAR and DSISR should be set correctly */ - if (mfspr(19) != (long) ptr || mfspr(18) != 0x00040000) + if (mfspr(DAR) != (long) ptr || mfspr(DSISR) != 0x00040000) return 3; /* this should fail */ if (test_write(ptr, 0xdeadbeef0dd0)) return 4; /* DAR and DSISR should be set correctly */ - if (mfspr(19) != (long)ptr || mfspr(18) != 0x02040000) + if (mfspr(DAR) != (long)ptr || mfspr(DSISR) != 0x02040000) return 5; /* memory should be unchanged */ if (*mem != 0x123456789abcdef0) @@ -347,7 +358,7 @@ int mmu_test_7(void) int mmu_test_8(void) { - long *mem = (long *) 0x4000; + long *mem = (long *) 0x8000; long *ptr = (long *) 0x124000; long val; @@ -361,7 +372,7 @@ int mmu_test_8(void) if (test_write(ptr, 0xdeadbeef0dd1)) return 2; /* DAR and DSISR should be set correctly */ - if (mfspr(19) != (long)ptr || mfspr(18) != 0x02040000) + if (mfspr(DAR) != (long)ptr || mfspr(DSISR) != 0x02040000) return 3; /* memory should be unchanged */ if (*mem != 0x123456789abcdef0) @@ -371,7 +382,7 @@ int mmu_test_8(void) int mmu_test_9(void) { - long *mem = (long *) 0x4000; + long *mem = (long *) 0x8000; long *ptr = (long *) 0x124000; long val; @@ -385,13 +396,13 @@ int mmu_test_9(void) if (val != 0xdeadd00dbeef) return 2; /* DAR and DSISR should be set correctly */ - if (mfspr(19) != (long) ptr || mfspr(18) != 0x08000000) + if (mfspr(DAR) != (long) ptr || mfspr(DSISR) != 0x08000000) return 3; /* this should fail */ if (test_write(ptr, 0xdeadbeef0dd1)) return 4; /* DAR and DSISR should be set correctly */ - if (mfspr(19) != (long)ptr || mfspr(18) != 0x0a000000) + if (mfspr(DAR) != (long)ptr || mfspr(DSISR) != 0x0a000000) return 5; /* memory should be unchanged */ if (*mem != 0x123456789abcdef0) @@ -401,7 +412,7 @@ int mmu_test_9(void) int mmu_test_10(void) { - long *mem = (long *) 0x4000; + long *mem = (long *) 0x8000; long *ptr = (long *) 0x124000; long val; @@ -415,7 +426,7 @@ int mmu_test_10(void) if (test_write(ptr, 0xdeadbeef0dd1)) return 2; /* DAR and DSISR should be set correctly */ - if (mfspr(19) != (long)ptr || mfspr(18) != 0x0a000000) + if (mfspr(DAR) != (long)ptr || mfspr(DSISR) != 0x0a000000) return 3; /* memory should be unchanged */ if (*mem != 0x123456789abcdef0) @@ -423,14 +434,159 @@ int mmu_test_10(void) return 0; } +int mmu_test_11(void) +{ + unsigned long ptr = 0x523000; + + /* this should fail */ + if (test_exec(0, ptr, MSR_IR)) + return 1; + /* SRR0 and SRR1 should be set correctly */ + if (mfspr(SRR0) != (long) ptr || mfspr(SRR1) != 0x40000020) + return 2; + return 0; +} + +int mmu_test_12(void) +{ + unsigned long mem = 0x1000; + unsigned long ptr = 0x324000; + unsigned long ptr2 = 0x1324000; + + /* create PTE */ + map((void *)ptr, (void *)mem, PERM_EX | REF); + /* this should succeed and be a cache miss */ + if (!test_exec(0, ptr, MSR_IR)) + 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_IR)) + return 2; + return 0; +} + +int mmu_test_13(void) +{ + unsigned long mem = 0x1000; + unsigned long ptr = 0x349000; + unsigned long ptr2 = 0x34a000; + + /* create a PTE */ + map((void *)ptr, (void *)mem, PERM_EX | REF); + /* this should succeed */ + if (!test_exec(1, ptr, MSR_IR)) + 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_IR)) + return 2; + /* SRR0 and SRR1 should be set correctly */ + if (mfspr(SRR0) != (long) ptr || mfspr(SRR1) != 0x40000020) + return 3; + return 0; +} + +int mmu_test_14(void) +{ + unsigned long mem = 0x1000; + unsigned long mem2 = 0x2000; + unsigned long ptr = 0x30a000; + unsigned long ptr2 = 0x30b000; + + /* 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_IR)) + return 1; + /* SRR0 and SRR1 should be set correctly */ + if (mfspr(SRR0) != ptr2 || mfspr(SRR1) != 0x40000020) + return 2; + /* create a PTE for the second page */ + map((void *)ptr2, (void *)mem2, PERM_EX | REF); + /* this should succeed */ + if (!test_exec(2, ptr, MSR_IR)) + return 3; + return 0; +} + +int mmu_test_15(void) +{ + unsigned long mem = 0x1000; + unsigned long ptr = 0x324000; + + /* create a PTE without execute permission */ + map((void *)ptr, (void *)mem, DFLT_PERM); + /* this should fail */ + if (test_exec(0, ptr, MSR_IR)) + return 1; + /* SRR0 and SRR1 should be set correctly */ + if (mfspr(SRR0) != ptr || mfspr(SRR1) != 0x10000020) + return 2; + return 0; +} + +int mmu_test_16(void) +{ + unsigned long mem = 0x1000; + unsigned long mem2 = 0x2000; + unsigned long ptr = 0x30a000; + unsigned long ptr2 = 0x30b000; + + /* create a PTE */ + map((void *)ptr, (void *)mem, PERM_EX | REF); + /* 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_IR)) + return 1; + /* SRR0 and SRR1 should be set correctly */ + if (mfspr(SRR0) != ptr2 || mfspr(SRR1) != 0x10000020) + return 2; + /* 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_IR)) + return 3; + return 0; +} + +int mmu_test_17(void) +{ + unsigned long mem = 0x1000; + unsigned long ptr = 0x349000; + + /* create a PTE without the ref bit set */ + map((void *)ptr, (void *)mem, PERM_EX); + /* this should fail */ + if (test_exec(2, ptr, MSR_IR)) + return 1; + /* SRR0 and SRR1 should be set correctly */ + if (mfspr(SRR0) != (long) ptr || mfspr(SRR1) != 0x00040020) + return 2; + /* create a PTE without ref or execute permission */ + map((void *)ptr, (void *)mem, 0); + /* this should fail */ + if (test_exec(2, ptr, MSR_IR)) + return 1; + /* SRR0 and SRR1 should be set correctly */ + /* RC update fail bit should not be set */ + if (mfspr(SRR0) != (long) ptr || mfspr(SRR1) != 0x10000020) + return 2; + return 0; +} + int fail = 0; void do_test(int num, int (*test)(void)) { int ret; - mtspr(18, 0); - mtspr(19, 0); + mtspr(DSISR, 0); + mtspr(DAR, 0); unmap_all(); print_test_number(num); ret = test(); @@ -440,10 +596,17 @@ void do_test(int num, int (*test)(void)) fail = 1; print_string("FAIL "); putchar(ret + '0'); - print_string(" DAR="); - print_hex(mfspr(19)); - print_string(" DSISR="); - print_hex(mfspr(18)); + if (num <= 10) { + print_string(" DAR="); + print_hex(mfspr(DAR)); + print_string(" DSISR="); + print_hex(mfspr(DSISR)); + } else { + print_string(" SRR0="); + print_hex(mfspr(SRR0)); + print_string(" SRR1="); + print_hex(mfspr(SRR1)); + } print_string("\r\n"); } } @@ -463,6 +626,13 @@ int main(void) do_test(8, mmu_test_8); do_test(9, mmu_test_9); do_test(10, mmu_test_10); + do_test(11, mmu_test_11); + do_test(12, mmu_test_12); + do_test(13, mmu_test_13); + do_test(14, mmu_test_14); + do_test(15, mmu_test_15); + do_test(16, mmu_test_16); + do_test(17, mmu_test_17); return fail; } diff --git a/tests/mmu/powerpc.lds b/tests/mmu/powerpc.lds index c4bff13..99611ab 100644 --- a/tests/mmu/powerpc.lds +++ b/tests/mmu/powerpc.lds @@ -1,13 +1,27 @@ SECTIONS { - _start = .; . = 0; + _start = .; .head : { KEEP(*(.head)) } - . = 0x1000; - .text : { *(.text) } - . = 0x3000; - .data : { *(.data) } - .bss : { *(.bss) } + . = ALIGN(0x1000); + .text : { *(.text) *(.text.*) *(.rodata) *(.rodata.*) } + . = ALIGN(0x1000); + .data : { *(.data) *(.data.*) *(.got) *(.toc) } + . = ALIGN(0x80); + __bss_start = .; + .bss : { + *(.dynsbss) + *(.sbss) + *(.scommon) + *(.dynbss) + *(.bss) + *(.common) + *(.bss.*) + } + . = ALIGN(0x80); + __bss_end = .; + . = . + 0x4000; + __stack_top = .; } diff --git a/tests/test_mmu.bin b/tests/test_mmu.bin index 961e2df8a24786b52775933f1f9573d2beea9c7e..afae999223bcb15bede8df58d6abc88bc7ea06dc 100755 GIT binary patch literal 20496 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 ZqtwC6{hW`3fP=u-KtLub?#GFM=zqYWQ&<21 literal 12304 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