bug-hurd
[Top][All Lists]
Advanced

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

[PATCH gnumach v3 2/3] add tests for FLOAT/XFLOAT state


From: Luca Dariz
Subject: [PATCH gnumach v3 2/3] add tests for FLOAT/XFLOAT state
Date: Wed, 4 Sep 2024 22:18:05 +0200

---
 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




reply via email to

[Prev in Thread] Current Thread [Next in Thread]