On Fri, 27 Jun 2025 at 03:59, Benjamin Berg <benja...@sipsolutions.net> wrote: > > From: Benjamin Berg <benjamin.b...@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.b...@intel.com> > ---
I really like this as an example of what we can do with UAPI tests. Alongside the comments from Thomas, one small note below that we'd need to put any SSE stuff behind a check for SSE support (at least on 32-bit), and skip the test. Cheers, -- David > 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(®ister_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__ Just to ruin your day, the SSE register stuff probably needs to be behind some check for SSE support. > + 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 > > -- > You received this message because you are subscribed to the Google Groups > "KUnit Development" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to kunit-dev+unsubscr...@googlegroups.com. > To view this discussion visit > https://groups.google.com/d/msgid/kunit-dev/20250626195714.2123694-3-benjamin%40sipsolutions.net.
smime.p7s
Description: S/MIME Cryptographic Signature