Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/tests/lib/libc/sys Add tests for x87 FPU registers



details:   https://anonhg.NetBSD.org/src/rev/a86db3f7f39a
branches:  trunk
changeset: 1014975:a86db3f7f39a
user:      mgorny <mgorny%NetBSD.org@localhost>
date:      Fri Oct 09 17:43:30 2020 +0000

description:
Add tests for x87 FPU registers

Reviewed by kamil

diffstat:

 tests/lib/libc/sys/t_ptrace_x86_wait.h |  353 +++++++++++++++++++++++++++++++-
 1 files changed, 338 insertions(+), 15 deletions(-)

diffs (truncated from 552 to 300 lines):

diff -r f7fe8adb492d -r a86db3f7f39a tests/lib/libc/sys/t_ptrace_x86_wait.h
--- a/tests/lib/libc/sys/t_ptrace_x86_wait.h    Fri Oct 09 17:43:07 2020 +0000
+++ b/tests/lib/libc/sys/t_ptrace_x86_wait.h    Fri Oct 09 17:43:30 2020 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: t_ptrace_x86_wait.h,v 1.26 2020/10/09 17:43:07 mgorny Exp $    */
+/*     $NetBSD: t_ptrace_x86_wait.h,v 1.27 2020/10/09 17:43:30 mgorny Exp $    */
 
 /*-
  * Copyright (c) 2016, 2017, 2018, 2019 The NetBSD Foundation, Inc.
@@ -2186,6 +2186,21 @@
        uint32_t u32;
 };
 
+struct x86_test_fpu_registers {
+       struct {
+               uint64_t mantissa;
+               uint16_t sign_exp;
+       } __aligned(16) st[8];
+
+       uint16_t cw;
+       uint16_t sw;
+       uint16_t tw;
+       uint8_t tw_abridged;
+       uint16_t opcode;
+       union fp_addr ip;
+       union fp_addr dp;
+};
+
 enum x86_test_regset {
        TEST_GPREGS,
        TEST_FPREGS,
@@ -2201,6 +2216,7 @@
        GPREGS_64,
        GPREGS_64_R8,
        /* TEST_FPREGS/TEST_XMMREGS */
+       FPREGS_FPU,
        FPREGS_MM,
        FPREGS_XMM,
        /* TEST_XSTATE */
@@ -2419,6 +2435,79 @@
 #endif
 }
 
+static __inline void get_fpu_regs(struct x86_test_fpu_registers *out)
+{
+       struct save87 fsave;
+       struct fxsave fxsave;
+
+       __CTASSERT(sizeof(out->st[0]) == 16);
+
+       __asm__ __volatile__(
+               "finit\n\t"
+               "int3\n\t"
+#if defined(__x86_64__)
+               "fxsave64 %2\n\t"
+#else
+               "fxsave %2\n\t"
+#endif
+               "fnstenv %1\n\t"
+               "fnclex\n\t"
+               "fstpt 0x00(%0)\n\t"
+               "fstpt 0x10(%0)\n\t"
+               "fstpt 0x20(%0)\n\t"
+               "fstpt 0x30(%0)\n\t"
+               "fstpt 0x40(%0)\n\t"
+               "fstpt 0x50(%0)\n\t"
+               "fstpt 0x60(%0)\n\t"
+               "fstpt 0x70(%0)\n\t"
+               :
+               : "a"(out->st), "m"(fsave), "m"(fxsave)
+               : "st", "memory"
+       );
+
+       FORKEE_ASSERT(fsave.s87_cw == fxsave.fx_cw);
+       FORKEE_ASSERT(fsave.s87_sw == fxsave.fx_sw);
+
+       /* fsave contains full tw */
+       out->cw = fsave.s87_cw;
+       out->sw = fsave.s87_sw;
+       out->tw = fsave.s87_tw;
+       out->tw_abridged = fxsave.fx_tw;
+       out->opcode = fxsave.fx_opcode;
+       out->ip = fxsave.fx_ip;
+       out->dp = fxsave.fx_dp;
+}
+
+/* used as single-precision float */
+uint32_t x86_test_zero = 0;
+
+static __inline void set_fpu_regs(const struct x86_test_fpu_registers *data)
+{
+       __CTASSERT(sizeof(data->st[0]) == 16);
+
+       __asm__ __volatile__(
+               "finit\n\t"
+               "fldcw %1\n\t"
+               /* load on stack in reverse order to make it easier to read */
+               "fldt 0x70(%0)\n\t"
+               "fldt 0x60(%0)\n\t"
+               "fldt 0x50(%0)\n\t"
+               "fldt 0x40(%0)\n\t"
+               "fldt 0x30(%0)\n\t"
+               "fldt 0x20(%0)\n\t"
+               "fldt 0x10(%0)\n\t"
+               "fldt 0x00(%0)\n\t"
+               /* free st7 */
+               "ffree %%st(7)\n\t"
+               /* this should trigger a divide-by-zero */
+               "fdivs (%2)\n\t"
+               "int3\n\t"
+               :
+               : "a"(&data->st), "m"(data->cw), "b"(&x86_test_zero)
+               : "st"
+       );
+}
+
 __attribute__((target("mmx")))
 static __inline void get_mm_regs(union x86_test_register out[])
 {
@@ -2712,6 +2801,54 @@
                   0x262524232221201F, 0x2E2D2C2B2A292827, }},
        };
 
+       const struct x86_test_fpu_registers expected_fpu = {
+               .st = {
+                       {0x8000000000000000, 0x4000}, /* +2.0 */
+                       {0x3f00000000000000, 0x0000}, /* 1.654785e-4932 */
+                       {0x0000000000000000, 0x0000}, /* +0 */
+                       {0x0000000000000000, 0x8000}, /* -0 */
+                       {0x8000000000000000, 0x7fff}, /* +inf */
+                       {0x8000000000000000, 0xffff}, /* -inf */
+                       {0xc000000000000000, 0xffff}, /* nan */
+                       /* st(7) will be freed to test tag word better */
+                       {0x0000000000000000, 0x0000}, /* +0 */
+               },
+               /* 0000 0011 0111 1011
+                *             PU OZDI -- unmask divide-by-zero exc.
+                *           RR --------- reserved
+                *        PC ------------ 64-bit precision
+                *      RC -------------- round to nearest
+                *    I ----------------- allow interrupts (unused)
+                */
+               .cw = 0x037b,
+               /* 1000 0000 1000 0100
+                *            SPU OZDI -- divide-by-zero exception
+                *           I ---------- interrupt (exception handling)
+                *  C    CCC ------------ condition codes
+                *   TO P --------------- top register is 0
+                * B -------------------- FPU is busy
+                */
+               .sw = 0x8084,
+               /* 1110 1010 0101 1000
+                * R7R6 R5R4 R3R2 R1R0
+                *                  nz -- non-zero (+2.0)
+                *                sp ---- special (denormal)
+                *           zrzr ------- zeroes
+                *   sp spsp ------------ specials (NaN + infinities)
+                * em ------------------- empty register
+                */
+               .tw = 0xea58,
+               /* 0111 1111 -- registers 0 to 6 are used */
+               .tw_abridged = 0x7f,
+               /* FDIV */
+               .opcode = 0x0033,
+               /* random bits for IP/DP write test
+                * keep it below 48 bits since it can be truncated
+                */
+               .ip = {.fa_64 = 0x00000a9876543210},
+               .dp = {.fa_64 = 0x0000056789abcdef},
+       };
+
        bool need_32 = false, need_64 = false, need_cpuid = false;
 
        switch (regs) {
@@ -2723,6 +2860,8 @@
        case GPREGS_64_R8:
                need_64 = true;
                break;
+       case FPREGS_FPU:
+               break;
        case FPREGS_MM:
        case FPREGS_XMM:
        case FPREGS_YMM:
@@ -2768,6 +2907,7 @@
                case GPREGS_32_EBP_ESP:
                case GPREGS_64:
                case GPREGS_64_R8:
+               case FPREGS_FPU:
                        __unreachable();
                }
        }
@@ -2776,6 +2916,7 @@
        SYSCALL_REQUIRE((child = fork()) != -1);
        if (child == 0) {
                union x86_test_register vals[16] __aligned(32);
+               struct x86_test_fpu_registers vals_fpu;
 
                DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid());
                FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
@@ -2797,6 +2938,9 @@
                        case GPREGS_64_R8:
                                set_gp64_r8_regs(expected);
                                break;
+                       case FPREGS_FPU:
+                               set_fpu_regs(&expected_fpu);
+                               break;
                        case FPREGS_MM:
                                set_mm_regs(expected);
                                break;
@@ -2822,6 +2966,9 @@
                        case GPREGS_64_R8:
                                get_gp64_r8_regs(vals);
                                break;
+                       case FPREGS_FPU:
+                               get_fpu_regs(&vals_fpu);
+                               break;
                        case FPREGS_MM:
                                get_mm_regs(vals);
                                break;
@@ -2871,6 +3018,47 @@
                                FORKEE_ASSERT(!memcmp(&vals[7].u64,
                                    &expected[7].u64, sizeof(vals->u64)));
                                break;
+                       case FPREGS_FPU:
+                               FORKEE_ASSERT(vals_fpu.cw == expected_fpu.cw);
+                               FORKEE_ASSERT(vals_fpu.sw == expected_fpu.sw);
+                               FORKEE_ASSERT(vals_fpu.tw == expected_fpu.tw);
+                               FORKEE_ASSERT(vals_fpu.tw_abridged
+                                   == expected_fpu.tw_abridged);
+                               FORKEE_ASSERT(vals_fpu.ip.fa_64
+                                   == expected_fpu.ip.fa_64);
+                               FORKEE_ASSERT(vals_fpu.dp.fa_64
+                                   == expected_fpu.dp.fa_64);
+
+                               FORKEE_ASSERT(vals_fpu.st[0].sign_exp
+                                   == expected_fpu.st[0].sign_exp);
+                               FORKEE_ASSERT(vals_fpu.st[0].mantissa
+                                   == expected_fpu.st[0].mantissa);
+                               FORKEE_ASSERT(vals_fpu.st[1].sign_exp
+                                   == expected_fpu.st[1].sign_exp);
+                               FORKEE_ASSERT(vals_fpu.st[1].mantissa
+                                   == expected_fpu.st[1].mantissa);
+                               FORKEE_ASSERT(vals_fpu.st[2].sign_exp
+                                   == expected_fpu.st[2].sign_exp);
+                               FORKEE_ASSERT(vals_fpu.st[2].mantissa
+                                   == expected_fpu.st[2].mantissa);
+                               FORKEE_ASSERT(vals_fpu.st[3].sign_exp
+                                   == expected_fpu.st[3].sign_exp);
+                               FORKEE_ASSERT(vals_fpu.st[3].mantissa
+                                   == expected_fpu.st[3].mantissa);
+                               FORKEE_ASSERT(vals_fpu.st[4].sign_exp
+                                   == expected_fpu.st[4].sign_exp);
+                               FORKEE_ASSERT(vals_fpu.st[4].mantissa
+                                   == expected_fpu.st[4].mantissa);
+                               FORKEE_ASSERT(vals_fpu.st[5].sign_exp
+                                   == expected_fpu.st[5].sign_exp);
+                               FORKEE_ASSERT(vals_fpu.st[5].mantissa
+                                   == expected_fpu.st[5].mantissa);
+                               FORKEE_ASSERT(vals_fpu.st[6].sign_exp
+                                   == expected_fpu.st[6].sign_exp);
+                               FORKEE_ASSERT(vals_fpu.st[6].mantissa
+                                   == expected_fpu.st[6].mantissa);
+                               /* st(7) is left empty == undefined */
+                               break;
                        case FPREGS_XMM:
                                FORKEE_ASSERT(!memcmp(&vals[0].xmm,
                                    &expected[0].xmm, sizeof(vals->xmm)));
@@ -2959,6 +3147,7 @@
 
        if (regset == TEST_XSTATE) {
                switch (regs) {
+               case FPREGS_FPU:
                case FPREGS_MM:
                        xst_flags |= XCR0_X87;
                        break;
@@ -2980,16 +3169,19 @@
        switch (regmode) {
        case TEST_GETREGS:
        case TEST_SETREGS:
-               switch (regset) {
-               case TEST_GPREGS:
-                       ATF_REQUIRE(regs < FPREGS_MM);
+               if (regset == TEST_GPREGS || regs == FPREGS_FPU) {
                        DPRINTF("Call GETREGS for the child process\n");
                        SYSCALL_REQUIRE(ptrace(PT_GETREGS, child, &gpr, 0)
                            != -1);
+               }
+
+               switch (regset) {
+               case TEST_GPREGS:
+                       /* already handled above */
                        break;
                case TEST_XMMREGS:
 #if defined(__i386__)
-                       ATF_REQUIRE(regs >= FPREGS_MM && regs < FPREGS_YMM);
+                       ATF_REQUIRE(regs >= FPREGS_FPU && regs < FPREGS_YMM);
                        DPRINTF("Call GETXMMREGS for the child process\n");
                        SYSCALL_REQUIRE(ptrace(PT_GETXMMREGS, child, &xmm, 0)
                            != -1);
@@ -3000,17 +3192,17 @@



Home | Main Index | Thread Index | Old Index