tests: Add a test for FP loads and stores
authorPaul Mackerras <paulus@ozlabs.org>
Wed, 1 Jul 2020 08:03:19 +0000 (18:03 +1000)
committerPaul Mackerras <paulus@ozlabs.org>
Thu, 3 Sep 2020 05:15:45 +0000 (15:15 +1000)
This tests that floating-point unavailable exceptions occur as expected
on FP loads and stores, and that the simple FP loads and stores appear
to give reasonable results.

Signed-off-by: Paul Mackerras <paulus@ozlabs.org>
tests/fpu/Makefile [new file with mode: 0644]
tests/fpu/fpu.c [new file with mode: 0644]
tests/fpu/head.S [new file with mode: 0644]
tests/fpu/powerpc.lds [new file with mode: 0644]
tests/test_fpu.bin [new file with mode: 0755]
tests/test_fpu.console_out [new file with mode: 0644]
tests/update_console_tests

diff --git a/tests/fpu/Makefile b/tests/fpu/Makefile
new file mode 100644 (file)
index 0000000..fd8344e
--- /dev/null
@@ -0,0 +1,3 @@
+TEST=fpu
+
+include ../Makefile.test
diff --git a/tests/fpu/fpu.c b/tests/fpu/fpu.c
new file mode 100644 (file)
index 0000000..d61b36e
--- /dev/null
@@ -0,0 +1,196 @@
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "console.h"
+
+#define MSR_FP 0x2000
+#define MSR_FE0        0x800
+#define MSR_FE1        0x100
+
+extern int trapit(long arg, int (*func)(long));
+
+#define SRR0   26
+#define SRR1   27
+
+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));
+}
+
+void disable_fp(void)
+{
+       unsigned long msr;
+
+       __asm__("mfmsr %0" : "=r" (msr));
+       msr &= ~(MSR_FP | MSR_FE0 | MSR_FE1);
+       __asm__("mtmsrd %0" : : "r" (msr));
+}
+
+void enable_fp(void)
+{
+       unsigned long msr;
+
+       __asm__("mfmsr %0" : "=r" (msr));
+       msr |= MSR_FP;
+       __asm__("mtmsrd %0" : : "r" (msr));
+}
+
+void print_string(const char *str)
+{
+       for (; *str; ++str)
+               putchar(*str);
+}
+
+void print_hex(unsigned long val, int ndigits)
+{
+       int i, x;
+
+       for (i = (ndigits - 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(':');
+}
+
+unsigned long foo = 0x3ff8000000000000ul;
+unsigned long foow;
+int fooi = -76543;
+int fooiw;
+
+int do_fp_op(long arg)
+{
+       switch (arg) {
+       case 0:
+               __asm__("lfd 31,0(%0)" : : "b" (&foo));
+               break;
+       case 1:
+               __asm__("stfd 31,0(%0)" : : "b" (&foow) : "memory");
+               break;
+       case 2:
+               __asm__("lfd 30,0(%0); stfd 30,0(%1)"
+                       : : "b" (&foo), "b" (&foow) : "memory");
+               break;
+       case 3:
+               __asm__("lfiwax 29,0,%0; stfd 29,0(%1)"
+                       : : "r" (&fooi), "b" (&foow) : "memory");
+               break;
+       case 4:
+               __asm__("lfiwzx 28,0,%0; stfd 28,0(%1)"
+                       : : "r" (&fooi), "b" (&foow) : "memory");
+               break;
+       case 5:
+               __asm__("lfdx 27,0,%0; stfiwx 27,0,%1"
+                       : : "r" (&foow), "r" (&fooiw) : "memory");
+               break;
+       }
+       return 0;
+}
+
+
+int fpu_test_1(void)
+{
+       int ret;
+
+       disable_fp();
+       /* these should give a FP unavailable exception */
+       ret = trapit(0, do_fp_op);
+       if (ret != 0x800)
+               return 1;
+       ret = trapit(1, do_fp_op);
+       if (ret != 0x800)
+               return 2;
+       enable_fp();
+       /* these should succeed */
+       ret = trapit(0, do_fp_op);
+       if (ret)
+               return ret | 3;
+       ret = trapit(1, do_fp_op);
+       if (ret)
+               return ret | 4;
+       if (foow != foo)
+               return 5;
+       return 0;
+}
+
+int fpu_test_2(void)
+{
+       int ret;
+
+       enable_fp();
+       foow = ~0;
+       ret = trapit(2, do_fp_op);
+       if (ret)
+               return ret | 1;
+       if (foow != foo)
+               return 2;
+       foow = ~0;
+       ret = trapit(3, do_fp_op);
+       if (ret)
+               return ret | 3;
+       if (foow != fooi)
+               return 4;
+       foow = ~0;
+       ret = trapit(4, do_fp_op);
+       if (ret)
+               return ret | 5;
+       if (foow != (unsigned int)fooi)
+               return 6;
+       ret = trapit(5, do_fp_op);
+       if (ret)
+               return ret | 7;
+       if (fooiw != fooi)
+               return 8;
+       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, 4);
+               print_string(" SRR0=");
+               print_hex(mfspr(SRR0), 16);
+               print_string(" SRR1=");
+               print_hex(mfspr(SRR1), 16);
+               print_string("\r\n");
+       }
+}
+
+int main(void)
+{
+       console_init();
+
+       do_test(1, fpu_test_1);
+       do_test(2, fpu_test_2);
+
+       return fail;
+}
diff --git a/tests/fpu/head.S b/tests/fpu/head.S
new file mode 100644 (file)
index 0000000..498606b
--- /dev/null
@@ -0,0 +1,120 @@
+/* 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)
+       LOAD_IMM64(%r10, die)
+       mtsprg0 %r10
+       LOAD_IMM64(%r12, main)
+       mtctr   %r12
+       bctrl
+die:   attn // terminate on exit
+       b .
+
+.global trapit
+trapit:
+       mflr    %r0
+       std     %r0,16(%r1)
+       stdu    %r1,-256(%r1)
+       mtsprg1 %r1
+       r = 14
+       .rept   18
+       std     r,r*8(%r1)
+       r = r + 1
+       .endr
+       mfcr    %r0
+       stw     %r0,13*8(%r1)
+       LOAD_IMM64(%r10, ret)
+       mtsprg0 %r10
+       mr      %r12,%r4
+       mtctr   %r4
+       bctrl
+ret:
+       mfsprg1 %r1
+       LOAD_IMM64(%r10, die)
+       mtsprg0 %r10
+       r = 14
+       .rept   18
+       ld      r,r*8(%r1)
+       r = r + 1
+       .endr
+       lwz     %r0,13*8(%r1)
+       mtcr    %r0
+       ld      %r0,256+16(%r1)
+       addi    %r1,%r1,256
+       mtlr    %r0
+       blr
+
+#define EXCEPTION(nr)          \
+       .= nr                   ;\
+       mfsprg0 %r0             ;\
+       mtctr   %r0             ;\
+       li      %r3,nr          ;\
+       bctr
+
+       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(0xc00)
+       EXCEPTION(0xd00)
+       EXCEPTION(0xe00)
+       EXCEPTION(0xe20)
+       EXCEPTION(0xe40)
+       EXCEPTION(0xe60)
+       EXCEPTION(0xe80)
+       EXCEPTION(0xf00)
+       EXCEPTION(0xf20)
+       EXCEPTION(0xf40)
+       EXCEPTION(0xf60)
+       EXCEPTION(0xf80)
diff --git a/tests/fpu/powerpc.lds b/tests/fpu/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_fpu.bin b/tests/test_fpu.bin
new file mode 100755 (executable)
index 0000000..885368a
Binary files /dev/null and b/tests/test_fpu.bin differ
diff --git a/tests/test_fpu.console_out b/tests/test_fpu.console_out
new file mode 100644 (file)
index 0000000..0c39ae3
--- /dev/null
@@ -0,0 +1,2 @@
+test 01:PASS\r
+test 02:PASS\r
index 906b0cc5643027e6642cc02898f54982aa34a20b..a5e6ffca0d77193069685f013bc12ae866d7a9dc 100755 (executable)
@@ -3,7 +3,7 @@
 # Script to update console related tests from source
 #
 
-for i in sc illegal decrementer xics privileged mmu misc modes reservation trace ; do
+for i in sc illegal decrementer xics privileged mmu misc modes reservation trace fpu ; do
     cd $i
     make
     cd -