[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [PATCH gnumach v3 2/3] add tests for FLOAT/XFLOAT state
From: |
Samuel Thibault |
Subject: |
Re: [PATCH gnumach v3 2/3] add tests for FLOAT/XFLOAT state |
Date: |
Sun, 8 Sep 2024 01:08:06 +0200 |
Applied, thanks!
Luca Dariz, le mer. 04 sept. 2024 22:18:05 +0200, a ecrit:
> ---
> tests/include/testlib.h | 1 +
> tests/test-thread-state-fp.c | 240 +++++++++++++++++++++++++++++++++++
> tests/testlib.c | 16 +++
> tests/user-qemu.mk | 3 +-
> 4 files changed, 259 insertions(+), 1 deletion(-)
> create mode 100644 tests/test-thread-state-fp.c
>
> diff --git a/tests/include/testlib.h b/tests/include/testlib.h
> index 7c7c2b11..17a96660 100644
> --- a/tests/include/testlib.h
> +++ b/tests/include/testlib.h
> @@ -70,6 +70,7 @@ thread_t test_thread_start(task_t task,
> void(*routine)(void*), void* arg);
> mach_port_t host_priv(void);
> mach_port_t device_priv(void);
> void wait_thread_terminated(thread_t th);
> +void wait_thread_suspended(thread_t th);
>
> extern vm_size_t vm_page_size;
>
> diff --git a/tests/test-thread-state-fp.c b/tests/test-thread-state-fp.c
> new file mode 100644
> index 00000000..2aab8ca9
> --- /dev/null
> +++ b/tests/test-thread-state-fp.c
> @@ -0,0 +1,240 @@
> +/*
> + * Copyright (c) 2024 Free Software Foundation.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, write to the Free Software Foundation, Inc.,
> + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
> + */
> +
> +#include <syscalls.h>
> +#include <testlib.h>
> +#include <mach/exception.h>
> +#include <mach.user.h>
> +#include <mach_port.user.h>
> +
> +#if defined(__i386__) || defined(__x86_64__)
> +#include <mach_i386.user.h>
> +
> +static void printx(struct i386_xfloat_state *state, int size)
> +{
> + printf("xfloat init %d fp %d exc %d\n",
> + state->initialized, state->fpkind, state->exc_status);
> + struct i386_xfp_save *xfp = (struct i386_xfp_save *) &state->hw_state[0];
> + printf("xfp %x %x %x %x %x %x %x %x\n",
> + xfp->fp_control, xfp->fp_status, xfp->fp_tag, xfp->fp_eip,
> + xfp->fp_cs, xfp->fp_opcode, xfp->fp_dp, xfp->fp_ds);
> + for (int i=0; i<8; i++)
> + {
> + printf("fp%d", i);
> + for (int j=0; j<16; j++)
> + printf(" %02x", xfp->fp_reg_word[i][j]);
> + printf("\n");
> + }
> + for (int i=0; i<16; i++)
> + {
> + printf("xmm%02d", i);
> + for (int j=0; j<16; j++)
> + printf(" %02x", xfp->fp_xreg_word[i][j]);
> + printf("\n");
> + }
> + printf("header xfp_features %llx bv %llx\n",
> + xfp->header.xfp_features, xfp->header.xcomp_bv);
> + int rem = size - sizeof(*state) - sizeof(struct i386_xfp_save);
> + if (rem > 0)
> + {
> + int iter = 0;
> + while (rem > 0)
> + {
> + const int len = 16;
> + int n;
> + if (rem > len)
> + n = len;
> + else
> + n = rem;
> + printf("ext");
> + for (int j=0; j<n; j++)
> + printf(" %02x", xfp->extended[j + iter*len]);
> + printf("\n");
> + rem -= n;
> + iter++;
> + }
> + }
> +}
> +
> +static void thread_fp_getset(void *arg)
> +{
> + int err;
> + thread_t th = *(thread_t*)arg;
> +
> + wait_thread_suspended(th);
> +
> + mach_msg_type_number_t state_count = i386_FLOAT_STATE_COUNT;
> + struct i386_float_state state;
> +
> + memset(&state, 0, sizeof(state));
> + err = thread_get_state(th, i386_FLOAT_STATE,
> + (thread_state_t) &state, &state_count);
> + ASSERT_RET(err, "thread_get_state get failed");
> + ASSERT(state_count == i386_FLOAT_STATE_COUNT, "bad state_count");
> +
> + struct i386_fp_regs *fpr =
> + (struct i386_fp_regs *) (&state.hw_state[0] + sizeof(struct
> i386_fp_save));
> +
> + printf("fp regs get:\n");
> + for (int i=0; i<8; i++)
> + {
> + printf("fp%d", i);
> + for (int j=0; j<5; j++)
> + printf(" %04x", fpr->fp_reg_word[i][j]);
> + printf("\n");
> + }
> +
> + char tmp[10];
> + memcpy(tmp, &fpr->fp_reg_word[1][0], sizeof(tmp));
> + memcpy(&fpr->fp_reg_word[1][0], &fpr->fp_reg_word[0][0], sizeof(tmp));
> + memcpy(&fpr->fp_reg_word[0][0], tmp, sizeof(tmp));
> +
> + printf("fp regs set:\n");
> + for (int i=0; i<8; i++)
> + {
> + printf("fp%d", i);
> + for (int j=0; j<5; j++)
> + printf(" %04x", fpr->fp_reg_word[i][j]);
> + printf("\n");
> + }
> +
> + err = thread_set_state(th, i386_FLOAT_STATE,
> + (thread_state_t) &state, state_count);
> + ASSERT_RET(err, "thread_set_state set failed");
> +
> + err = thread_resume(th);
> + ASSERT_RET(err, "error in thread_resume");
> + thread_terminate(mach_thread_self());
> + FAILURE("thread_terminate");
> +}
> +
> +static void test_fp_state_getset()
> +{
> + int err;
> + thread_t th = mach_thread_self();
> +
> + /* load some known value in FP registers */
> + int n1[1] = {1111111111};
> + float d2[1] = {123.456};
> + asm volatile ("fildl %0\n"
> + "fldl %1\n"
> + :: "m" (n1), "m" (d2) :);
> +
> + /* then switch to the get/set test thread, and wait to be resumed */
> + test_thread_start(mach_task_self(), thread_fp_getset, &th);
> + err = thread_suspend(th);
> + ASSERT_RET(err, "error in thread_suspend");
> +
> + /* and check that now we have the values swapped in FP registers */
> + int m1[1] = {0};
> + float f2[1] = {0.0};
> + asm volatile ("fistpl %0\n"
> + "fstpl %1\n"
> + :: "m" (m1), "m" (f2):);
> + int fint, fdec;
> + fint = (int)f2[0];
> + fdec = (int)((f2[0] - fint) * 1000);
> + printf("fp %d %d.%03d\n", m1[0], fint, fdec);
> + ASSERT(n1[0] == m1[0], "error in moving int value in fp regs");
> + ASSERT(f2[0] == d2[0], "error in moving fp value in fp regs");
> +}
> +
> +
> +static void thread_xfp_getset(void *arg)
> +{
> + int err;
> + thread_t th = *(thread_t*)arg;
> +
> + wait_thread_suspended(th);
> +
> + vm_size_t xfp_size;
> + err = i386_get_xstate_size(host_priv(), &xfp_size);
> + ASSERT_RET(err, "i386_get_xstate_size");
> +
> + mach_msg_type_number_t state_count = xfp_size / sizeof(integer_t);
> + struct i386_xfloat_state *state = __builtin_alloca(xfp_size);
> + printf("xfp size %u min %u\n", xfp_size, sizeof(struct i386_xfloat_state));
> +
> + memset(state, 0, xfp_size);
> + err = thread_get_state(th, i386_XFLOAT_STATE,
> + (thread_state_t) state, &state_count);
> + ASSERT_RET(err, "thread_get_state get failed");
> + ASSERT(state_count == (xfp_size / sizeof(integer_t)), "bad state_count");
> + printx(state, xfp_size);
> +
> + struct i386_xfp_save *xfp = (struct i386_xfp_save *) &state->hw_state[0];
> + printf("xmm3 (after get)");
> + for (int j=0; j<16; j++)
> + printf(" %02x", xfp->fp_xreg_word[3][j]);
> + printf("\n");
> + for (int j=0; j<16; j++)
> + ASSERT(xfp->fp_xreg_word[3][j] == 0x33,
> + "register xmm3 wasn't correctly retrieved from the getset
> thread");
> +
> + memset(xfp->fp_xreg_word[7], 0x77, 16);
> +
> + err = thread_set_state(th, i386_XFLOAT_STATE,
> + (thread_state_t) state, state_count);
> + ASSERT_RET(err, "thread_set_state set failed");
> +
> + err = thread_resume(th);
> + ASSERT_RET(err, "error in thread_resume");
> + thread_terminate(mach_thread_self());
> + FAILURE("thread_terminate");
> +}
> +
> +static void test_xfp_state_getset()
> +{
> + int err;
> + thread_t th = mach_thread_self();
> +
> + /* load some known value in XMM registers */
> + char buf3[16];
> + memset(buf3, 0x33, 16);
> + asm volatile ("movups (%0),%%xmm3" :: "r" (buf3) :);
> +
> + /* then switch to the get/set test thread, and wait to be resumed */
> + test_thread_start(mach_task_self(), thread_xfp_getset, &th);
> + err = thread_suspend(th);
> + ASSERT_RET(err, "error in thread_suspend");
> +
> + /* and check that now we have different values in XMM registers */
> + char buf7[16];
> + memset(buf7, 0, 16);
> + asm volatile ("movups %%xmm7,(%0)" :: "r" (buf7) :);
> +
> + printf("xmm7 (after set)");
> + for (int j=0; j<16; j++)
> + printf(" %02x", buf7[j]);
> + printf("\n");
> + for (int j=0; j<16; j++)
> + ASSERT(buf7[j] == 0x77,
> + "register xmm7 wasn't correctly set by the getset thread");
> +}
> +#endif
> +
> +int main(int argc, char *argv[], int envc, char *envp[])
> +{
> +#if defined(__i386__) || defined(__x86_64__)
> + test_fp_state_getset();
> + test_xfp_state_getset();
> +#else
> + FAILURE("FP/XSTATE test missing on this arch!");
> +#endif
> + return 0;
> +}
> diff --git a/tests/testlib.c b/tests/testlib.c
> index d1ce6d86..77b34372 100644
> --- a/tests/testlib.c
> +++ b/tests/testlib.c
> @@ -224,6 +224,22 @@ void wait_thread_terminated(thread_t th)
> } while (1);
> }
>
> +void wait_thread_suspended(thread_t th)
> +{
> + int err;
> + struct thread_basic_info info;
> + mach_msg_type_number_t count;
> + do {
> + count = THREAD_BASIC_INFO_COUNT;
> + err = thread_info(th, THREAD_BASIC_INFO, (thread_info_t)&info, &count);
> + ASSERT_RET(err, "error in thread_info");
> + if (info.suspend_count <= 0)
> + msleep(100); // don't poll continuously
> + else
> + break;
> + } while (1);
> +}
> +
> /*
> * Minimal _start() for test modules, we just take the arguments from the
> * kernel, call main() and reboot. As in glibc, we expect the argument
> pointer
> diff --git a/tests/user-qemu.mk b/tests/user-qemu.mk
> index a3013b59..b48f49ec 100644
> --- a/tests/user-qemu.mk
> +++ b/tests/user-qemu.mk
> @@ -210,7 +210,8 @@ USER_TESTS := \
> tests/test-machmsg \
> tests/test-task \
> tests/test-threads \
> - tests/test-thread-state
> + tests/test-thread-state \
> + tests/test-thread-state-fp
>
> USER_TESTS_CLEAN = $(subst tests/,clean-,$(USER_TESTS))
>
> --
> 2.39.2
>
>
--
Samuel
<D> m'enfin, le 5 juillet, le mec vient visiter le labo...
* D a marque d'une croix rouge le 5 juillet sur son agenda
<y> niarc niarc niarc
<D> cet homme va souffrir
<B> c'est donc le 5 juillet qu'il meurt d'un accident de la route écrasé par un
truck muni d'un pare buffle
-+- #ens-mim - repaire de terroristes -+-