tests: Add tests for 32-bit and big-endian modes
authorPaul Mackerras <paulus@ozlabs.org>
Thu, 20 Aug 2020 08:03:14 +0000 (18:03 +1000)
committerPaul Mackerras <paulus@ozlabs.org>
Sat, 22 Aug 2020 09:48:31 +0000 (19:48 +1000)
Signed-off-by: Paul Mackerras <paulus@ozlabs.org>
tests/modes/Makefile [new file with mode: 0644]
tests/modes/head.S [new file with mode: 0644]
tests/modes/modes.c [new file with mode: 0644]
tests/modes/powerpc.lds [new file with mode: 0644]
tests/test_modes.bin [new file with mode: 0755]
tests/test_modes.console_out [new file with mode: 0644]
tests/update_console_tests

diff --git a/tests/modes/Makefile b/tests/modes/Makefile
new file mode 100644 (file)
index 0000000..8f40880
--- /dev/null
@@ -0,0 +1,3 @@
+TEST=modes
+
+include ../Makefile.test
diff --git a/tests/modes/head.S b/tests/modes/head.S
new file mode 100644 (file)
index 0000000..d9e69dc
--- /dev/null
@@ -0,0 +1,232 @@
+/* Copyright 2013-2014 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Load an immediate 64-bit value into a register */
+#define LOAD_IMM64(r, e)                       \
+       lis     r,(e)@highest;                  \
+       ori     r,r,(e)@higher;                 \
+       rldicr  r,r, 32, 31;                    \
+       oris    r,r, (e)@h;                     \
+       ori     r,r, (e)@l;
+
+       .section ".head","ax"
+
+       /*
+        * Microwatt currently enters in LE mode at 0x0, so we don't need to
+        * do any endian fix ups
+        */
+       . = 0
+.global _start
+_start:
+       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
+
+2:     LOAD_IMM64(%r1,__stack_top)
+       li      %r0,0
+       stdu    %r0,-16(%r1)
+       mtsprg2 %r0
+       LOAD_IMM64(%r12, main)
+       mtctr   %r12
+       bctrl
+       attn // terminate on exit
+       b .
+
+exception:
+       mfsprg2 %r0
+       cmpdi   %r0,0
+       bne     call_ret
+       attn
+
+#define EXCEPTION(nr)          \
+       .= nr                   ;\
+       li      %r3,nr          ;\
+       b       exception
+
+       EXCEPTION(0x300)
+       EXCEPTION(0x380)
+       EXCEPTION(0x400)
+       EXCEPTION(0x480)
+       EXCEPTION(0x500)
+       EXCEPTION(0x600)
+       EXCEPTION(0x700)
+       EXCEPTION(0x800)
+       EXCEPTION(0x900)
+       EXCEPTION(0x980)
+       EXCEPTION(0xa00)
+       EXCEPTION(0xb00)
+       EXCEPTION(0xd00)
+       EXCEPTION(0xe00)
+       EXCEPTION(0xe20)
+       EXCEPTION(0xe40)
+       EXCEPTION(0xe60)
+       EXCEPTION(0xe80)
+       EXCEPTION(0xf00)
+       EXCEPTION(0xf20)
+       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_code
+test_code:
+       nop
+       nop
+       mflr    %r9
+       cmpdi   %r3,1
+       beq     test_1
+       cmpdi   %r3,2
+       beq     test_2
+       cmpdi   %r3,3
+       beq     test_3
+       li      %r3,0
+       blr
+
+       /* test a doubleword load from memory */
+test_1:        ld      %r3,0(%r4)
+       blr
+
+       /* test a branch from the page at fffff000 */
+test_2:
+       b       test_2a + 0x1000
+test_2a:
+       b       test_2b - 0x1000
+test_2b:
+       beq     test_2c + 0x1000
+test_2c:
+       beq     test_2d - 0x1000
+test_2d:
+       li      %r3,0
+       blr
+
+test_return:
+       mflr    %r3
+       mtlr    %r9
+       blr
+       . = 0x1ffc
+       /* test a branch with link from the 4G-4 address */
+test_3:        bl      test_return
+
+       .globl  test_code_end
+test_code_end:
+
+       . = 0x2000
+       /*
+        * Call a function in a context with a given MSR value.
+        * r3, r4 = args; r5 = function; r6 = MSR
+        */
+       .globl  callit
+callit:
+       mflr    %r0
+       std     %r0,16(%r1)
+       stdu    %r1,-256(%r1)
+       mfcr    %r8
+       stw     %r8,100(%r1)
+       std     %r13,104(%r1)
+       std     %r14,112(%r1)
+       std     %r15,120(%r1)
+       std     %r16,128(%r1)
+       std     %r17,136(%r1)
+       std     %r18,144(%r1)
+       std     %r19,152(%r1)
+       std     %r20,160(%r1)
+       std     %r21,168(%r1)
+       std     %r22,176(%r1)
+       std     %r23,184(%r1)
+       std     %r24,192(%r1)
+       std     %r25,200(%r1)
+       std     %r26,208(%r1)
+       std     %r27,216(%r1)
+       std     %r28,224(%r1)
+       std     %r29,232(%r1)
+       std     %r30,240(%r1)
+       std     %r31,248(%r1)
+       li      %r0,restore@l
+       mtsprg0 %r0
+       mtsprg1 %r1
+       mtsprg2 %r2
+       mfmsr   %r9
+       mtsprg3 %r9
+       li      %r10,call_ret@l
+       mtlr    %r10
+       mtsrr0  %r5
+       mtsrr1  %r6
+       mr      %r12,%r5
+       rfid
+call_ret:
+       tdi     0,%r0,0x48      /* b .+8 if wrong endian */
+       b       2f              /* if endian OK */
+       /* reverse-endian version of instructions from 2: on */
+       .long   0xa642107c
+       .long   0xa642937c
+       .long   0xa602ba7c
+       .long   0xa602db7c
+       .long   0xa643b07c
+       .long   0xa643d37c
+       .long   0xa6031a7c
+       .long   0xa6039b7c
+       .long   0x2400004c
+2:     mfsprg0 %r0
+       mfsprg3 %r4
+       mfsrr0  %r5
+       mfsrr1  %r6
+       mtsprg0 %r5
+       mtsprg3 %r6
+       mtsrr0  %r0
+       mtsrr1  %r4
+       rfid
+restore:
+       mfsprg1 %r1
+       mfsprg2 %r2
+       li      %r7,0
+       mtsprg2 %r7
+       lwz     %r8,100(%r1)
+       mtcr    %r8
+       ld      %r13,104(%r1)
+       ld      %r14,112(%r1)
+       ld      %r15,120(%r1)
+       ld      %r16,128(%r1)
+       ld      %r17,136(%r1)
+       ld      %r18,144(%r1)
+       ld      %r19,152(%r1)
+       ld      %r20,160(%r1)
+       ld      %r21,168(%r1)
+       ld      %r22,176(%r1)
+       ld      %r23,184(%r1)
+       ld      %r24,192(%r1)
+       ld      %r25,200(%r1)
+       ld      %r26,208(%r1)
+       ld      %r27,216(%r1)
+       ld      %r28,224(%r1)
+       ld      %r29,232(%r1)
+       ld      %r30,240(%r1)
+       ld      %r31,248(%r1)
+       addi    %r1,%r1,256
+       ld      %r0,16(%r1)
+       mtlr    %r0
+       blr
diff --git a/tests/modes/modes.c b/tests/modes/modes.c
new file mode 100644 (file)
index 0000000..5d0c870
--- /dev/null
@@ -0,0 +1,339 @@
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "console.h"
+
+#define MSR_LE 0x1
+#define MSR_DR 0x10
+#define MSR_IR 0x20
+#define MSR_SF 0x8000000000000000ul
+
+extern unsigned long callit(unsigned long arg1, unsigned long arg2,
+                           unsigned long fn, 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
+#define PID    48
+#define SPRG0  272
+#define SPRG1  273
+#define PRTBL  720
+
+static inline unsigned long mfspr(int sprnum)
+{
+       long val;
+
+       __asm__ volatile("mfspr %0,%1" : "=r" (val) : "i" (sprnum));
+       return val;
+}
+
+static inline void mtspr(int sprnum, unsigned long val)
+{
+       __asm__ volatile("mtspr %0,%1" : : "i" (sprnum), "r" (val));
+}
+
+static inline void store_pte(unsigned long *p, unsigned long pte)
+{
+       __asm__ volatile("stdbrx %1,0,%0" : : "r" (p), "r" (pte) : "memory");
+}
+
+void print_string(const char *str)
+{
+       for (; *str; ++str)
+               putchar(*str);
+}
+
+void print_hex(unsigned long val, int ndigit)
+{
+       int i, x;
+
+       for (i = (ndigit - 1) * 4; i >= 0; i -= 4) {
+               x = (val >> i) & 0xf;
+               if (x >= 10)
+                       putchar(x + 'a' - 10);
+               else
+                       putchar(x + '0');
+       }
+}
+
+// i < 100
+void print_test_number(int i)
+{
+       print_string("test ");
+       putchar(48 + i/10);
+       putchar(48 + i%10);
+       putchar(':');
+}
+
+#define CACHE_LINE_SIZE        64
+
+void zero_memory(void *ptr, unsigned long nbytes)
+{
+       unsigned long nb, i, nl;
+       void *p;
+
+       for (; nbytes != 0; nbytes -= nb, ptr += nb) {
+               nb = -((unsigned long)ptr) & (CACHE_LINE_SIZE - 1);
+               if (nb == 0 && nbytes >= CACHE_LINE_SIZE) {
+                       nl = nbytes / CACHE_LINE_SIZE;
+                       p = ptr;
+                       for (i = 0; i < nl; ++i) {
+                               __asm__ volatile("dcbz 0,%0" : : "r" (p) : "memory");
+                               p += CACHE_LINE_SIZE;
+                       }
+                       nb = nl * CACHE_LINE_SIZE;
+               } else {
+                       if (nb > nbytes)
+                               nb = nbytes;
+                       for (i = 0; i < nb; ++i)
+                               ((unsigned char *)ptr)[i] = 0;
+               }
+       }
+}
+
+#define PERM_EX                0x001
+#define PERM_WR                0x002
+#define PERM_RD                0x004
+#define PERM_PRIV      0x008
+#define ATTR_NC                0x020
+#define CHG            0x080
+#define REF            0x100
+
+#define DFLT_PERM      (PERM_EX | PERM_WR | PERM_RD | REF | CHG)
+
+/*
+ * Set up an MMU translation tree using memory starting at the 64k point.
+ * We use 3 levels, mapping 512GB, with 4kB PGD/PMD/PTE pages.
+ */
+unsigned long *proc_tbl = (unsigned long *) 0x10000;
+unsigned long *pgdir = (unsigned long *) 0x11000;
+unsigned long free_ptr = 0x12000;
+
+void init_mmu(void)
+{
+       /* set up process table */
+       zero_memory(proc_tbl, 512 * sizeof(unsigned long));
+       mtspr(PRTBL, (unsigned long)proc_tbl);
+       mtspr(PID, 1);
+       zero_memory(pgdir, 512 * sizeof(unsigned long));
+       /* RTS = 8 (512GB address space), RPDS = 9 (512-entry top level) */
+       store_pte(&proc_tbl[2 * 1], (unsigned long) pgdir | 0x2000000000000009);
+       do_tlbie(0xc00, 0);     /* invalidate all TLB entries */
+}
+
+static unsigned long *read_pd(unsigned long *pdp, unsigned long i)
+{
+       unsigned long ret;
+
+       __asm__ volatile("ldbrx %0,%1,%2" : "=r" (ret) : "b" (pdp),
+                        "r" (i * sizeof(unsigned long)));
+       return (unsigned long *) (ret & 0x00ffffffffffff00);
+}
+
+void map(unsigned long ea, unsigned long pa, unsigned long perm_attr)
+{
+       unsigned long epn = ea >> 12;
+       unsigned long h, i, j;
+       unsigned long *ptep;
+       unsigned long *pmdp;
+
+       h = (epn >> 18) & 0x1ff;
+       i = (epn >> 9) & 0x1ff;
+       j = epn & 0x1ff;
+       if (pgdir[h] == 0) {
+               zero_memory((void *)free_ptr, 512 * sizeof(unsigned long));
+               store_pte(&pgdir[h], 0x8000000000000000 | free_ptr | 9);
+               free_ptr += 512 * sizeof(unsigned long);
+       }
+       pmdp = read_pd(pgdir, h);
+       if (pmdp[i] == 0) {
+               zero_memory((void *)free_ptr, 512 * sizeof(unsigned long));
+               store_pte(&pmdp[i], 0x8000000000000000 | free_ptr | 9);
+               free_ptr += 512 * sizeof(unsigned long);
+       }
+       ptep = read_pd(pmdp, i);
+       if (ptep[j]) {
+               ptep[j] = 0;
+               do_tlbie(ea & ~0xfff, 0);
+       }
+       store_pte(&ptep[j], 0xc000000000000000 | (pa & 0x00fffffffffff000) |
+                 perm_attr);
+}
+
+void unmap(void *ea)
+{
+       unsigned long epn = (unsigned long) ea >> 12;
+       unsigned long h, i, j;
+       unsigned long *ptep, *pmdp;
+
+       h = (epn >> 18) & 0x1ff;
+       i = (epn >> 9) & 0x1ff;
+       j = epn & 0x1ff;
+       if (pgdir[h] == 0)
+               return;
+       pmdp = read_pd(pgdir, h);
+       if (pmdp[i] == 0)
+               return;
+       ptep = read_pd(pmdp, i);
+       ptep[j] = 0;
+       do_tlbie(((unsigned long)ea & ~0xfff), 0);
+}
+
+extern unsigned long test_code(unsigned long sel, unsigned long addr);
+
+static unsigned long bits = 0x0102030405060708ul;
+
+int mode_test_1(void)
+{
+       unsigned long ret, msr;
+
+       msr = MSR_SF | MSR_IR | MSR_DR | MSR_LE;
+       ret = callit(1, (unsigned long)&bits, (unsigned long)&test_code, msr);
+       if (ret != bits)
+               return ret? ret: 1;
+       return 0;
+}
+
+unsigned long be_test_code;
+
+int mode_test_2(void)
+{
+       unsigned long i;
+       unsigned int *src, *dst;
+       unsigned long ret, msr;
+
+       /* copy and byte-swap the page containing test_code */
+       be_test_code = free_ptr;
+       free_ptr += 0x1000;
+       src = (unsigned int *) &test_code;
+       dst = (unsigned int *) be_test_code;
+       for (i = 0; i < 0x1000 / sizeof(unsigned int); ++i)
+               dst[i] = __builtin_bswap32(src[i]);
+       __asm__ volatile("isync; icbi 0,%0" : : "r" (be_test_code));
+       map(be_test_code, be_test_code, DFLT_PERM);
+
+       msr = MSR_SF | MSR_IR | MSR_DR;
+       ret = callit(1, (unsigned long)&bits, be_test_code, msr);
+       if (ret != __builtin_bswap64(bits))
+               return ret? ret: 1;
+       return 0;
+}
+
+int mode_test_3(void)
+{
+       unsigned long ret, msr;
+       unsigned long addr = (unsigned long) &bits;
+       unsigned long code = (unsigned long) &test_code;
+
+       msr = MSR_IR | MSR_DR | MSR_LE;
+       ret = callit(1, addr, code, msr);
+       if (ret != bits)
+               return ret? ret: 1;
+       ret = callit(1, addr + 0x5555555500000000ul,
+                    code + 0x9999999900000000ul, msr);
+       if (ret != bits)
+               return ret? ret: 2;
+       return 0;
+}
+
+int mode_test_4(void)
+{
+       unsigned long ret, msr;
+       unsigned long addr = (unsigned long) &bits;
+
+       msr = MSR_IR | MSR_DR;
+       ret = callit(1, addr, be_test_code, msr);
+       if (ret != __builtin_bswap64(bits))
+               return ret? ret: 1;
+       ret = callit(1, addr + 0x5555555500000000ul,
+                    be_test_code + 0x9999999900000000ul, msr);
+       if (ret != __builtin_bswap64(bits))
+               return ret? ret: 2;
+       return 0;
+}
+
+int mode_test_5(void)
+{
+       unsigned long ret, msr;
+
+       /*
+        * Try branching from the page at fffff000
+        * to the page at 0 in 32-bit mode.
+        */
+       map(0xfffff000, (unsigned long) &test_code, DFLT_PERM);
+       map(0, (unsigned long) &test_code, DFLT_PERM);
+       msr = MSR_IR | MSR_DR | MSR_LE;
+       ret = callit(2, 0, 0xfffff000, msr);
+       return ret;
+}
+
+int mode_test_6(void)
+{
+       unsigned long ret, msr;
+
+       /*
+        * Try a bl from address fffffffc in 32-bit mode.
+        * We expect LR to be set to 100000000, though the
+        * arch says the value is undefined.
+        */
+       msr = MSR_IR | MSR_DR | MSR_LE;
+       ret = callit(3, 0, 0xfffff000, msr);
+       if (ret != 0x100000000ul)
+               return 1;
+       return 0;
+}
+
+int fail = 0;
+
+void do_test(int num, int (*test)(void))
+{
+       int ret;
+
+       print_test_number(num);
+       ret = test();
+       if (ret == 0) {
+               print_string("PASS\r\n");
+       } else {
+               fail = 1;
+               print_string("FAIL ");
+               print_hex(ret, 16);
+               if (ret != 0 && (ret & ~0xfe0ul) == 0) {
+                       print_string(" SRR0=");
+                       print_hex(mfspr(SPRG0), 16);
+                       print_string(" SRR1=");
+                       print_hex(mfspr(SPRG1), 16);
+               }
+               print_string("\r\n");
+       }
+}
+
+int main(void)
+{
+       unsigned long addr;
+       extern unsigned char __stack_top[];
+
+       console_init();
+       init_mmu();
+
+       /*
+        * Map test code and stack 1-1
+        */
+       for (addr = 0; addr < (unsigned long)&__stack_top; addr += 0x1000)
+               map(addr, addr, DFLT_PERM);
+
+       do_test(1, mode_test_1);
+       do_test(2, mode_test_2);
+       do_test(3, mode_test_3);
+       do_test(4, mode_test_4);
+       do_test(5, mode_test_5);
+       do_test(6, mode_test_6);
+
+       return fail;
+}
diff --git a/tests/modes/powerpc.lds b/tests/modes/powerpc.lds
new file mode 100644 (file)
index 0000000..99611ab
--- /dev/null
@@ -0,0 +1,27 @@
+SECTIONS
+{
+       . = 0;
+       _start = .;
+       .head : {
+               KEEP(*(.head))
+       }
+       . = 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_modes.bin b/tests/test_modes.bin
new file mode 100755 (executable)
index 0000000..0c52628
Binary files /dev/null and b/tests/test_modes.bin differ
diff --git a/tests/test_modes.console_out b/tests/test_modes.console_out
new file mode 100644 (file)
index 0000000..a49bb9b
--- /dev/null
@@ -0,0 +1,6 @@
+test 01:PASS\r
+test 02:PASS\r
+test 03:PASS\r
+test 04:PASS\r
+test 05:PASS\r
+test 06:PASS\r
index 57ac0b0a6ba65d88f5764a86f7e5ffabfdf82fef..2101c6bd98f7a9d33f29777c4b6ae9f2202ed206 100755 (executable)
@@ -3,7 +3,7 @@
 # Script to update console related tests from source
 #
 
-for i in sc illegal decrementer xics privileged mmu misc ; do
+for i in sc illegal decrementer xics privileged mmu misc modes ; do
     cd $i
     make
     cd -