[PATCH 1/8] selftests/powerpc: Test the preservation of FPU and VMX regs across syscall
Michael Neuling
mikey at neuling.org
Mon Nov 23 11:23:13 AEDT 2015
On Wed, 2015-11-18 at 14:26 +1100, Cyril Bur wrote:
> Test that the non volatile floating point and Altivec registers get
> correctly preserved across the fork() syscall.
Can we add a test for VSX too? I realise it's the same registers, but
the enable bits in the MSR are different so it's easy to get them wrong
in the kernel.
Additional comments below.
> fork() works nicely for this purpose, the registers should be the same for
> both parent and child
>
> Signed-off-by: Cyril Bur <cyrilbur at gmail.com>
> ---
> tools/testing/selftests/powerpc/Makefile | 3 +-
> tools/testing/selftests/powerpc/math/Makefile | 14 ++
> tools/testing/selftests/powerpc/math/basic_asm.h | 26 +++
> tools/testing/selftests/powerpc/math/fpu_asm.S | 151 +++++++++++++++++
> tools/testing/selftests/powerpc/math/fpu_syscall.c | 79 +++++++++
> tools/testing/selftests/powerpc/math/vmx_asm.S | 183 +++++++++++++++++++++
> tools/testing/selftests/powerpc/math/vmx_syscall.c | 81 +++++++++
> 7 files changed, 536 insertions(+), 1 deletion(-)
> create mode 100644 tools/testing/selftests/powerpc/math/Makefile
> create mode 100644 tools/testing/selftests/powerpc/math/basic_asm.h
> create mode 100644 tools/testing/selftests/powerpc/math/fpu_asm.S
> create mode 100644 tools/testing/selftests/powerpc/math/fpu_syscall.c
> create mode 100644 tools/testing/selftests/powerpc/math/vmx_asm.S
> create mode 100644 tools/testing/selftests/powerpc/math/vmx_syscall.c
>
> diff --git a/tools/testing/selftests/powerpc/Makefile b/tools/testing/selftests/powerpc/Makefile
> index 0c2706b..19e8191 100644
> --- a/tools/testing/selftests/powerpc/Makefile
> +++ b/tools/testing/selftests/powerpc/Makefile
> @@ -22,7 +22,8 @@ SUB_DIRS = benchmarks > > > \
> > > switch_endian> > \
> > > syscalls> > > \
> > > tm> > > > \
> -> > vphn
> +> > vphn \
> +> > math
>
> endif
>
> diff --git a/tools/testing/selftests/powerpc/math/Makefile b/tools/testing/selftests/powerpc/math/Makefile
> new file mode 100644
> index 0000000..896d9e2
> --- /dev/null
> +++ b/tools/testing/selftests/powerpc/math/Makefile
> @@ -0,0 +1,14 @@
> +TEST_PROGS := fpu_syscall vmx_syscall
Add a new .gitignore in this dirfor these new build objects.
> +
> +all: $(TEST_PROGS)
> +
> +$(TEST_PROGS): ../harness.c
> +$(TEST_PROGS): CFLAGS += -O2 -g
> +
> +fpu_syscall: fpu_asm.S
> +vmx_syscall: vmx_asm.S
> +
> +include ../../lib.mk
> +
> +clean:
> +> > rm -f $(TEST_PROGS) *.o
> diff --git a/tools/testing/selftests/powerpc/math/basic_asm.h b/tools/testing/selftests/powerpc/math/basic_asm.h
> new file mode 100644
> index 0000000..27aca79
> --- /dev/null
> +++ b/tools/testing/selftests/powerpc/math/basic_asm.h
Can you put this up a directory since it's generically useful for
powerpc?
> @@ -0,0 +1,26 @@
> +#include
> +#include
> +
> +#define LOAD_REG_IMMEDIATE(reg,expr) \
> +> > lis> > reg,(expr)@highest;> > \
> +> > ori> > reg,reg,(expr)@higher;> > \
> +> > rldicr> > reg,reg,32,31;> > \
> +> > oris> > reg,reg,(expr)@high;> > \
> +> > ori> > reg,reg,(expr)@l;
> +
> +#define PUSH_BASIC_STACK(size) \
> +> > std> > 2,24(sp); \
> +> > mflr> > r0; \
> +> > std> > r0,16(sp); \
> +> > mfcr> > r0; \
> +> > stw> > r0,8(sp); \
> +> > stdu> > sp,-size(sp);
> +
> +#define POP_BASIC_STACK(size) \
> +> > addi> > sp,sp,size; \
> +> > ld> > 2,24(sp); \
> +> > ld> > r0,16(sp); \
> +> > mtlr> > r0; \
> +> > lwz> > r0,8(sp); \
> +> > mtcr> > r0; \
> +
> diff --git a/tools/testing/selftests/powerpc/math/fpu_asm.S b/tools/testing/selftests/powerpc/math/fpu_asm.S
> new file mode 100644
> index 0000000..d5412c1
> --- /dev/null
> +++ b/tools/testing/selftests/powerpc/math/fpu_asm.S
> @@ -0,0 +1,151 @@
> +#include "basic_asm.h"
> +
> +#define PUSH_FPU(pos) \
> +> > stfd> > f14,pos(sp); \
> +> > stfd> > f15,pos+8(sp); \
> +> > stfd> > f16,pos+16(sp); \
> +> > stfd> > f17,pos+24(sp); \
> +> > stfd> > f18,pos+32(sp); \
> +> > stfd> > f19,pos+40(sp); \
> +> > stfd> > f20,pos+48(sp); \
> +> > stfd> > f21,pos+56(sp); \
> +> > stfd> > f22,pos+64(sp); \
> +> > stfd> > f23,pos+72(sp); \
> +> > stfd> > f24,pos+80(sp); \
> +> > stfd> > f25,pos+88(sp); \
> +> > stfd> > f26,pos+96(sp); \
> +> > stfd> > f27,pos+104(sp); \
> +> > stfd> > f28,pos+112(sp); \
> +> > stfd> > f29,pos+120(sp); \
> +> > stfd> > f30,pos+128(sp); \
> +> > stfd> > f31,pos+136(sp);
> +
> +#define POP_FPU(pos) \
> +> > lfd> > f14,pos(sp); \
> +> > lfd> > f15,pos+8(sp); \
> +> > lfd> > f16,pos+16(sp); \
> +> > lfd> > f17,pos+24(sp); \
> +> > lfd> > f18,pos+32(sp); \
> +> > lfd> > f19,pos+40(sp); \
> +> > lfd> > f20,pos+48(sp); \
> +> > lfd> > f21,pos+56(sp); \
> +> > lfd> > f22,pos+64(sp); \
> +> > lfd> > f23,pos+72(sp); \
> +> > lfd> > f24,pos+80(sp); \
> +> > lfd> > f25,pos+88(sp); \
> +> > lfd> > f26,pos+96(sp); \
> +> > lfd> > f27,pos+104(sp); \
> +> > lfd> > f28,pos+112(sp); \
> +> > lfd> > f29,pos+120(sp); \
> +> > lfd> > f30,pos+128(sp); \
> +> > lfd> > f31,pos+136(sp);
> +
> +#Careful calling this, it will 'clobber' fpu (by design)
> +#Don't call this from C
> +FUNC_START(load_fpu)
> +> > lfd> > f14,0(r3)
> +> > lfd> > f15,8(r3)
> +> > lfd> > f16,16(r3)
> +> > lfd> > f17,24(r3)
> +> > lfd> > f18,32(r3)
> +> > lfd> > f19,40(r3)
> +> > lfd> > f20,48(r3)
> +> > lfd> > f21,56(r3)
> +> > lfd> > f22,64(r3)
> +> > lfd> > f23,72(r3)
> +> > lfd> > f24,80(r3)
> +> > lfd> > f25,88(r3)
> +> > lfd> > f26,96(r3)
> +> > lfd> > f27,104(r3)
> +> > lfd> > f28,112(r3)
> +> > lfd> > f29,120(r3)
> +> > lfd> > f30,128(r3)
> +> > lfd> > f31,136(r3)
> +> > blr
> +FUNC_END(load_fpu)
> +
> +FUNC_START(check_fpu)
> +> > mr r4,r3
> +> > li> > r3,1 #assume a bad result
> +> > lfd> > f0,0(r4)
> +> > fcmpu> > cr1,f0,f14
> +> > bne> > cr1,1f
> +> > lfd> > f0,8(r4)
> +> > fcmpu> > cr1,f0,f15
> +> > bne> > cr1,1f
> +> > lfd> > f0,16(r4)
> +> > fcmpu> > cr1,f0,f16
> +> > bne> > cr1,1f
> +> > lfd> > f0,24(r4)
> +> > fcmpu> > cr1,f0,f17
> +> > bne> > cr1,1f
> +> > lfd> > f0,32(r4)
> +> > fcmpu> > cr1,f0,f18
> +> > bne> > cr1,1f
> +> > lfd> > f0,40(r4)
> +> > fcmpu> > cr1,f0,f19
> +> > bne> > cr1,1f
> +> > lfd> > f0,48(r4)
> +> > fcmpu> > cr1,f0,f20
> +> > bne> > cr1,1f
> +> > lfd> > f0,56(r4)
> +> > fcmpu> > cr1,f0,f21
> +> > bne> > cr1,1f
> +> > lfd> > f0,64(r4)
> +> > fcmpu> > cr1,f0,f22
> +> > bne> > cr1,1f
> +> > lfd> > f0,72(r4)
> +> > fcmpu> > cr1,f0,f23
> +> > bne> > cr1,1f
> +> > lfd> > f0,80(r4)
> +> > fcmpu> > cr1,f0,f24
> +> > bne> > cr1,1f
> +> > lfd> > f0,88(r4)
> +> > fcmpu> > cr1,f0,f25
> +> > bne> > cr1,1f
> +> > lfd> > f0,96(r4)
> +> > fcmpu> > cr1,f0,f26
> +> > bne> > cr1,1f
> +> > lfd> > f0,104(r4)
> +> > fcmpu> > cr1,f0,f27
> +> > bne> > cr1,1f
> +> > lfd> > f0,112(r4)
> +> > fcmpu> > cr1,f0,f28
> +> > bne> > cr1,1f
> +> > lfd> > f0,120(r4)
> +> > fcmpu> > cr1,f0,f29
> +> > bne> > cr1,1f
> +> > lfd> > f0,128(r4)
> +> > fcmpu> > cr1,f0,f30
> +> > bne> > cr1,1f
> +> > lfd> > f0,136(r4)
> +> > fcmpu> > cr1,f0,f31
> +> > bne> > cr1,1f
> +> > li> > r3,0 #Sucess!!!
> +1:> > blr
> +
> +FUNC_START(test_fpu)
> +> > #r3 holds pointer to where to put the result of fork
#r4 seems to hold a ptr to the pid
> + #f14-f31 are non volatiles
> +> > PUSH_BASIC_STACK(256)
> +> > std> > r3,40(sp) #Address of darray
> +> > std r4,48(sp) #Address of pid
> +> > PUSH_FPU(56)
> +
> +> > bl load_fpu
> +> > nop
> +> > li> > r0,__NR_fork
> +> > sc
> +
> +> > #pass the result of the fork to the caller
> +> > ld> > r9,48(sp)
> +> > std> > r3,0(r9)
> +
> +> > ld r3,40(sp)
> +> > bl check_fpu
> +> > nop
> +
> +> > POP_FPU(56)
> +> > POP_BASIC_STACK(256)
> +> > blr
> +FUNC_END(test_fpu)
> diff --git a/tools/testing/selftests/powerpc/math/fpu_syscall.c b/tools/testing/selftests/powerpc/math/fpu_syscall.c
> new file mode 100644
> index 0000000..a967fd6
> --- /dev/null
> +++ b/tools/testing/selftests/powerpc/math/fpu_syscall.c
> @@ -0,0 +1,79 @@
> +#include
> +#include
> +#include
> +#include
> +#include
> +#include
> +#include
> +
> +#include "utils.h"
> +
> +extern int test_fpu(double *darray, pid_t *pid);
> +
> +double darray[] = {0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0,
> +> > > 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0,
> +> > > 2.1};
> +
> +int syscall_fpu(void)
> +{
> +> > pid_t fork_pid;
> +> > int i;
> +> > int ret;
> +> > int child_ret;
> +> > for (i = 0; i < 1000; i++) {
> +> > > /* test_fpu will fork() */
> +> > > ret = test_fpu(darray, &fork_pid);
> +> > > if (fork_pid == -1)
> +> > > > return -1;
> +> > > if (fork_pid == 0)
> +> > > > exit(ret);
> +> > > waitpid(fork_pid, &child_ret, 0);
> +> > > if (ret || child_ret)
> +> > > > return 1;
> +> > }
> +
> +> > return 0;
> +}
> +
> +int test_syscall_fpu(void)
> +{
> +> > /*
> +> > * Setup an environment with much context switching
> +> > */
> +> > pid_t pid2;
> +> > pid_t pid = fork();
> +> > int ret;
> +> > int child_ret;
> +> > FAIL_IF(pid == -1);
> +
> +> > pid2 = fork();
> +> > /* Can't FAIL_IF(pid2 == -1); because already forked once */
> +> > if (pid2 == -1) {
> +> > > /*
> +> > > * Couldn't fork, ensure test is a fail
> +> > > */
> +> > > child_ret = ret = 1;
> +> > } else {
> +> > > ret = syscall_fpu();
> +> > > if (pid2)
> +> > > > waitpid(pid2, &child_ret, 0);
> +> > > else
> +> > > > exit(ret);
> +> > }
> +
> +> > ret |= child_ret;
> +
> +> > if (pid)
> +> > > waitpid(pid, &child_ret, 0);
> +> > else
> +> > > exit(ret);
> +
> +> > FAIL_IF(ret || child_ret);
> +> > return 0;
> +}
> +
> +int main(int argc, char *argv[])
> +{
> +> > return test_harness(test_syscall_fpu, "syscall_fpu");
> +
> +}
> diff --git a/tools/testing/selftests/powerpc/math/vmx_asm.S b/tools/testing/selftests/powerpc/math/vmx_asm.S
> new file mode 100644
> index 0000000..e642e67
> --- /dev/null
> +++ b/tools/testing/selftests/powerpc/math/vmx_asm.S
> @@ -0,0 +1,183 @@
> +#include "basic_asm.h"
> +
> +#define PUSH_VMX(pos,reg) \
> +> > li> > reg,pos; \
> +> > stvx> > v20,reg,sp; \
> +> > addi> > reg,reg,16; \
> +> > stvx> > v21,reg,sp; \
> +> > addi> > reg,reg,16; \
> +> > stvx> > v22,reg,sp; \
> +> > addi> > reg,reg,16; \
> +> > stvx> > v23,reg,sp; \
> +> > addi> > reg,reg,16; \
> +> > stvx> > v24,reg,sp; \
> +> > addi> > reg,reg,16; \
> +> > stvx> > v25,reg,sp; \
> +> > addi> > reg,reg,16; \
> +> > stvx> > v26,reg,sp; \
> +> > addi> > reg,reg,16; \
> +> > stvx> > v27,reg,sp; \
> +> > addi> > reg,reg,16; \
> +> > stvx> > v28,reg,sp; \
> +> > addi> > reg,reg,16; \
> +> > stvx> > v29,reg,sp; \
> +> > addi> > reg,reg,16; \
> +> > stvx> > v30,reg,sp; \
> +> > addi> > reg,reg,16; \
> +> > stvx> > v31,reg,sp;
> +
> +#define POP_VMX(pos,reg) \
> +> > li> > reg,pos; \
> +> > lvx> > v20,reg,sp; \
> +> > addi> > reg,reg,16; \
> +> > lvx> > v21,reg,sp; \
> +> > addi> > reg,reg,16; \
> +> > lvx> > v22,reg,sp; \
> +> > addi> > reg,reg,16; \
> +> > lvx> > v23,reg,sp; \
> +> > addi> > reg,reg,16; \
> +> > lvx> > v24,reg,sp; \
> +> > addi> > reg,reg,16; \
> +> > lvx> > v25,reg,sp; \
> +> > addi> > reg,reg,16; \
> +> > lvx> > v26,reg,sp; \
> +> > addi> > reg,reg,16; \
> +> > lvx> > v27,reg,sp; \
> +> > addi> > reg,reg,16; \
> +> > lvx> > v28,reg,sp; \
> +> > addi> > reg,reg,16; \
> +> > lvx> > v29,reg,sp; \
> +> > addi> > reg,reg,16; \
> +> > lvx> > v30,reg,sp; \
> +> > addi> > reg,reg,16; \
> +> > lvx> > v31,reg,sp;
> +
> +#Carefull this will 'clobber' vmx (by design)
> +#Don't call this from C
> +FUNC_START(load_vmx)
> +> > li> > r5,0
> +> > lvx> > v20,r5,r3
> +> > addi> > r5,r5,16
> +> > lvx> > v21,r5,r3
> +> > addi> > r5,r5,16
> +> > lvx> > v22,r5,r3
> +> > addi> > r5,r5,16
> +> > lvx> > v23,r5,r3
> +> > addi> > r5,r5,16
> +> > lvx> > v24,r5,r3
> +> > addi> > r5,r5,16
> +> > lvx> > v25,r5,r3
> +> > addi> > r5,r5,16
> +> > lvx> > v26,r5,r3
> +> > addi> > r5,r5,16
> +> > lvx> > v27,r5,r3
> +> > addi> > r5,r5,16
> +> > lvx> > v28,r5,r3
> +> > addi> > r5,r5,16
> +> > lvx> > v29,r5,r3
> +> > addi> > r5,r5,16
> +> > lvx> > v30,r5,r3
> +> > addi> > r5,r5,16
> +> > lvx> > v31,r5,r3
> +> > blr
> +FUNC_END(load_vmx)
> +
> +#Should be safe from C, only touches r4, r5 and v0,v1,v2
> +FUNC_START(check_vmx)
> +> > PUSH_BASIC_STACK(16)
> +> > mr r4,r3
> +> > li> > r3,1 #assume a bad result
> +> > li> > r5,0
> +> > lvx> > v0,r5,r4
> +> > vcmpequd.> > v1,v0,v20
> +> > vmr> > v2,v1
> +
> +> > addi> > r5,r5,16
> +> > lvx> > v0,r5,r4
> +> > vcmpequd.> > v1,v0,v21
> +> > vand> > v2,v2,v1
> +
> +> > addi> > r5,r5,16
> +> > lvx> > v0,r5,r4
> +> > vcmpequd.> > v1,v0,v22
> +> > vand> > v2,v2,v1
> +
> +> > addi> > r5,r5,16
> +> > lvx> > v0,r5,r4
> +> > vcmpequd.> > v1,v0,v23
> +> > vand> > v2,v2,v1
> +
> +> > addi> > r5,r5,16
> +> > lvx> > v0,r5,r4
> +> > vcmpequd.> > v1,v0,v24
> +> > vand> > v2,v2,v1
> +
> +> > addi> > r5,r5,16
> +> > lvx> > v0,r5,r4
> +> > vcmpequd.> > v1,v0,v25
> +> > vand> > v2,v2,v1
> +
> +> > addi> > r5,r5,16
> +> > lvx> > v0,r5,r4
> +> > vcmpequd.> > v1,v0,v26
> +> > vand> > v2,v2,v1
> +
> +> > addi> > r5,r5,16
> +> > lvx> > v0,r5,r4
> +> > vcmpequd.> > v1,v0,v27
> +> > vand> > v2,v2,v1
> +
> +> > addi> > r5,r5,16
> +> > lvx> > v0,r5,r4
> +> > vcmpequd.> > v1,v0,v28
> +> > vand> > v2,v2,v1
> +
> +> > addi> > r5,r5,16
> +> > lvx> > v0,r5,r4
> +> > vcmpequd.> > v1,v0,v29
> +> > vand> > v2,v2,v1
> +
> +> > addi> > r5,r5,16
> +> > lvx> > v0,r5,r4
> +> > vcmpequd.> > v1,v0,v30
> +> > vand> > v2,v2,v1
> +
> +> > addi> > r5,r5,16
> +> > lvx> > v0,r5,r4
> +> > vcmpequd.> > v1,v0,v31
> +> > vand> > v2,v2,v1
> +
> +> > li r5,0
> +> > stvx> > v2,r5,sp
> +> > ldx> > r0,r5,sp
> +> > cmpdi> > r0,0xffffffff
> +> > bne> > 1f
> +> > li> > r3,0
> +1:> > POP_BASIC_STACK(16)
> +> > blr
> +FUNC_END(check_vmx)
> +
> +#Safe from C
> +FUNC_START(test_vmx)
> +> > #r3 holds pointer to where to put the result of fork
> +> > #v20-v31 are non-volatile
> +> > PUSH_BASIC_STACK(512)
> +> > std> > r3,40(sp) #Address of varray
> +> > std r4,48(sp) #address of pid
> +> > PUSH_VMX(56, r4)
> +
> +> > bl load_vmx
> +
> +> > li> > r0,__NR_fork
> +> > sc
> +> > #Pass the result of fork back to the caller
> +> > ld> > r9,48(sp)
> +> > std> > r3,0(r9)
> +
> +> > ld r3,40(sp)
> +> > bl check_vmx
> +
> +> > POP_VMX(56,r4)
> +> > POP_BASIC_STACK(512)
> +> > blr
> +FUNC_END(test_vmx)
> diff --git a/tools/testing/selftests/powerpc/math/vmx_syscall.c b/tools/testing/selftests/powerpc/math/vmx_syscall.c
> new file mode 100644
> index 0000000..7adff05
> --- /dev/null
> +++ b/tools/testing/selftests/powerpc/math/vmx_syscall.c
> @@ -0,0 +1,81 @@
> +#include
> +#include
> +#include
> +#include
> +#include
> +#include
> +#include
> +
> +#include "utils.h"
> +
> +typedef int v4si __attribute__ ((vector_size (16)));
> +v4si varray[] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10,11,12},
> +> > {13,14,15,16},{17,18,19,20},{21,22,23,24},
> +> > {25,26,27,28},{29,30,31,32},{33,34,35,36},
> +> > {37,38,39,40},{41,42,43,44},{45,46,47,48}};
> +
> +extern int test_vmx(v4si *varray, pid_t *pid);
> +
> +int vmx_syscall(void)
> +{
> +> > pid_t fork_pid;
> +> > int i;
> +> > int ret;
> +> > int child_ret;
> +> > for (i = 0; i < 1000; i++) {
> +> > > /* test_vmx will fork() */
> +> > > ret = test_vmx(varray, &fork_pid);
> +> > > if (fork_pid == -1)
> +> > > > return -1;
> +> > > if (fork_pid == 0)
> +> > > > exit(ret);
> +> > > waitpid(fork_pid, &child_ret, 0);
> +> > > if (ret || child_ret)
> +> > > > return 1;
> +> > }
> +
> +> > return 0;
> +}
> +
> +int test_vmx_syscall(void)
> +{
> +> > /*
> +> > * Setup an environment with much context switching
> +> > */
> +> > pid_t pid2;
> +> > pid_t pid = fork();
> +> > int ret;
> +> > int child_ret;
> +> > FAIL_IF(pid == -1);
> +
> +> > pid2 = fork();
> +> > ret = vmx_syscall();
> +> > /* Can't FAIL_IF(pid2 == -1); because we've already forked */
> +> > if (pid2 == -1) {
> +> > > /*
> +> > > * Couldn't fork, ensure child_ret is set and is a fail
> +> > > */
> +> > > ret = child_ret = 1;
> +> > } else {
> +> > > if (pid2)
> +> > > > waitpid(pid2, &child_ret, 0);
> +> > > else
> +> > > > exit(ret);
> +> > }
> +
> +> > ret |= child_ret;
> +
> +> > if (pid)
> +> > > waitpid(pid, &child_ret, 0);
> +> > else
> +> > > exit(ret);
> +
> +> > FAIL_IF(ret || child_ret);
> +> > return 0;
> +}
> +
> +int main(int argc, char *argv[])
> +{
> +> > return test_harness(test_vmx_syscall, "vmx_syscall");
> +
> +}
More information about the Linuxppc-dev
mailing list