[RFC 2/2] um: add a mcontext FP register handling test

Benjamin Berg benjamin at sipsolutions.net
Thu Jun 26 12:57:14 PDT 2025


From: Benjamin Berg <benjamin.berg at intel.com>

This tests checks whether floating point registers are properly saved
into the signal context and restored again. The test works by having a
known value at the top of the FP register stack and in the first xmm
register. Then SIGUSR1 is triggered and both of these registers are
modified by changing the values in the mcontext.

Signed-off-by: Benjamin Berg <benjamin.berg at intel.com>
---
 arch/x86/um/Makefile                     |   2 +
 arch/x86/um/tests/Makefile               |  12 +++
 arch/x86/um/tests/registers.c            |  22 +++++
 arch/x86/um/tests/test-fp-save-restore.c | 118 +++++++++++++++++++++++
 4 files changed, 154 insertions(+)
 create mode 100644 arch/x86/um/tests/Makefile
 create mode 100644 arch/x86/um/tests/registers.c
 create mode 100644 arch/x86/um/tests/test-fp-save-restore.c

diff --git a/arch/x86/um/Makefile b/arch/x86/um/Makefile
index b42c31cd2390..410f5526f1f4 100644
--- a/arch/x86/um/Makefile
+++ b/arch/x86/um/Makefile
@@ -15,6 +15,8 @@ obj-y = bugs_$(BITS).o delay.o fault.o \
 	sys_call_table_$(BITS).o sysrq_$(BITS).o tls_$(BITS).o \
 	mem_$(BITS).o subarch.o os-Linux/
 
+obj-y += tests/
+
 ifeq ($(CONFIG_X86_32),y)
 
 obj-y += syscalls_32.o
diff --git a/arch/x86/um/tests/Makefile b/arch/x86/um/tests/Makefile
new file mode 100644
index 000000000000..c3a868b078f3
--- /dev/null
+++ b/arch/x86/um/tests/Makefile
@@ -0,0 +1,12 @@
+include $(srctree)/init/Makefile.nolibc
+
+ccflags-y := -I$(obj)
+
+um-tests-y += registers.o
+
+userprogs += test-fp-save-restore
+test-fp-save-restore-userccflags := -static $(NOLIBC_USERCFLAGS) -msse
+
+obj-$(CONFIG_KUNIT_UAPI) += um-tests.o
+
+$(obj)/registers.o: $(obj)/test-fp-save-restore
diff --git a/arch/x86/um/tests/registers.c b/arch/x86/um/tests/registers.c
new file mode 100644
index 000000000000..2c4e55da043c
--- /dev/null
+++ b/arch/x86/um/tests/registers.c
@@ -0,0 +1,22 @@
+#include <kunit/test.h>
+#include <kunit/test-bug.h>
+#include <kunit/uapi.h>
+
+static void test_mcontext(struct kunit *test)
+{
+	KUNIT_UAPI_EMBED_BLOB(test_fp_save_restore, "test-fp-save-restore");
+
+	kunit_uapi_run_kselftest(test, &test_fp_save_restore);
+}
+
+static struct kunit_case register_test_cases[] = {
+	KUNIT_CASE(test_mcontext),
+	{}
+};
+
+static struct kunit_suite register_test_suite = {
+	.name = "um_registers",
+	.test_cases = register_test_cases,
+};
+
+kunit_test_suites(&register_test_suite);
diff --git a/arch/x86/um/tests/test-fp-save-restore.c b/arch/x86/um/tests/test-fp-save-restore.c
new file mode 100644
index 000000000000..28a32ca374fe
--- /dev/null
+++ b/arch/x86/um/tests/test-fp-save-restore.c
@@ -0,0 +1,118 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Test FP register handling in userspace mcontext.
+ *
+ * Copyright (C) 2025 Intel Corporation
+ */
+
+#include <math.h>
+#include <signal.h>
+#include <types.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys.h>
+#include <asm/sigcontext.h>
+#include <asm/ucontext.h>
+
+#include "../../../tools/testing/selftests/kselftest.h"
+
+#define ST0_EXP_ADD 10
+
+static void sighandler(int sig, siginfo_t *info, void *p)
+{
+	struct ucontext *uc = p;
+	struct _fpstate *fpstate = (void *)uc->uc_mcontext.fpstate;
+
+	ksft_print_msg("sighandler: extended_size: %d, xstate_size: %d\n",
+		       fpstate->sw_reserved.extended_size,
+		       fpstate->sw_reserved.xstate_size);
+
+#ifdef __i386__
+	fpstate->_st[0].exponent += ST0_EXP_ADD;
+	fpstate->_xmm[1].element[0] |= 0x01010101;
+	fpstate->_xmm[1].element[1] |= 0x01010101;
+	fpstate->_xmm[1].element[2] |= 0x01010101;
+	fpstate->_xmm[1].element[3] |= 0x01010101;
+#else
+	/* Hacky way of modifying the exponent without breaking aliasing */
+	fpstate->st_space[2] += ST0_EXP_ADD;
+	fpstate->xmm_space[4] |= 0x01010101;
+	fpstate->xmm_space[5] |= 0x01010101;
+	fpstate->xmm_space[6] |= 0x01010101;
+	fpstate->xmm_space[7] |= 0x01010101;
+#endif
+}
+
+static int test_mcontext(int xmm_should_change)
+{
+	double num = 0.5;
+	uint32_t sse[4] = {0x11223344, 0x55667788, 0x99aabbcc, 0xddeeff00 };
+	long ret;
+	int xmm_manipulated;
+
+	ksft_print_msg("pre-signal:  %d / 100, %08x %08x %08x %08x\n", (int) (100*num), sse[0], sse[1], sse[2], sse[3]);
+	/*
+	 * This does kill(getpid(), SIGUSR1); with "num" being passed in AND
+	 * out of the floating point stack. We can therefore modify num by
+	 * changing st[0] when handling the signal.
+	 */
+#ifdef __i386__
+	asm volatile (
+		"movups %1, %%xmm1;"
+		"int $0x80;"
+		"movups %%xmm1, %1;"
+		: "=t" (num), "=m" (sse), "=a" (ret)
+		: "0" (num), "2" (__NR_kill), "b" (getpid()), "c" (SIGUSR1) :
+		"xmm1", "memory");
+#else
+	asm volatile (
+		"movups %1, %%xmm1;"
+		"syscall;"
+		"movups %%xmm1, %1;"
+		: "=t" (num), "=m"(sse), "=a" (ret)
+		: "0" (num), "2" (__NR_kill), "D" (getpid()), "S" (SIGUSR1)
+		: "r11", "rcx", "xmm1", "memory");
+#endif
+	if (sse[0] == 0x11223344 || sse[1] == 0x55667788 || sse[2] == 0x99aabbcc || sse[3] == 0xddeeff00)
+		xmm_manipulated = 0;
+	else if (sse[0] == 0x11233345 || sse[1] == 0x55677789 || sse[2] == 0x99abbbcd || sse[3] == 0xddefff01)
+		xmm_manipulated = 1;
+	else
+		xmm_manipulated = 2;
+
+	ksft_print_msg("post-signal: %d / 100, %08x %08x %08x %08x (should change: %d, changed: %d)\n",
+		       (int) (100 * num), sse[0], sse[1], sse[2], sse[3], xmm_should_change, xmm_manipulated);
+
+	if (num != (1 << (ST0_EXP_ADD - 1))) {
+		ksft_print_msg("floating point register was not manipulated\n");
+		return 1;
+	}
+
+	if (xmm_manipulated != xmm_should_change) {
+		ksft_print_msg("xmm/sse had unexpected value!\n");
+		return 1;
+	}
+
+	return 0;
+}
+
+int main(void)
+{
+	struct sigaction sa = {
+		.sa_flags = SA_SIGINFO,
+		.sa_handler = (void (*)(int))sighandler,
+	};
+
+	ksft_print_header();
+	ksft_set_plan(1);
+
+	if (sys_sigaction(SIGUSR1, &sa, NULL) < 0)
+		ksft_exit_fail_msg("Failed to register sigaction: %d\n", errno);
+
+	if (!test_mcontext(1))
+		ksft_test_result_pass("mcontext\n");
+	else
+		ksft_test_result_fail("mcontext failed!\n");
+
+	ksft_finished();
+}
-- 
2.50.0




More information about the linux-um mailing list