The restoring of TPIDR2 signal context has been broken since it was merged, fix this and add a test case covering it. This is a result of TPIDR2 context management following a different flow to any of the other state that we provide and the fact that we don't expose TPIDR (which follows the same pattern) to signals.
Signed-off-by: Mark Brown broonie@kernel.org --- Mark Brown (2): arm64/signal: Restore TPIDR2 register rather than memory state kselftest/arm64: Add a test case for TPIDR2 restore
arch/arm64/kernel/signal.c | 2 +- tools/testing/selftests/arm64/signal/.gitignore | 2 +- .../arm64/signal/testcases/tpidr2_restore.c | 85 ++++++++++++++++++++++ 3 files changed, 87 insertions(+), 2 deletions(-) --- base-commit: 858fd168a95c5b9669aac8db6c14a9aeab446375 change-id: 20230621-arm64-fix-tpidr2-signal-restore-713d93798f99
Best regards,
Currently when restoring the TPIDR2 signal context we set the new value from the signal frame in the thread data structure but not the register, following the pattern for the rest of the data we are restoring. This does not work in the case of TPIDR2, the register always has the value for the current task. This means that either we return to userspace and ignore the new value or we context switch and save the register value on top of the newly restored value.
Load the value from the signal context into the register instead.
Fixes: 39e54499280f ("arm64/signal: Include TPIDR2 in the signal context") Signed-off-by: Mark Brown broonie@kernel.org Cc: stable@vger.kernel.org --- arch/arm64/kernel/signal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm64/kernel/signal.c b/arch/arm64/kernel/signal.c index 2cfc810d0a5b..10b407672c42 100644 --- a/arch/arm64/kernel/signal.c +++ b/arch/arm64/kernel/signal.c @@ -398,7 +398,7 @@ static int restore_tpidr2_context(struct user_ctxs *user)
__get_user_error(tpidr2_el0, &user->tpidr2->tpidr2, err); if (!err) - current->thread.tpidr2_el0 = tpidr2_el0; + write_sysreg_s(tpidr2_el0, SYS_TPIDR2_EL0);
return err; }
Due to the fact that TPIDR2 is intended to be managed by libc we don't currently test modifying it via the signal context since that might disrupt libc's usage of it and cause instability. We can however test the opposite case with less risk, modifying TPIDR2 in a signal handler and making sure that the original value is restored after returning from the signal handler. Add a test which does this.
Signed-off-by: Mark Brown broonie@kernel.org --- tools/testing/selftests/arm64/signal/.gitignore | 2 +- .../arm64/signal/testcases/tpidr2_restore.c | 85 ++++++++++++++++++++++ 2 files changed, 86 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/arm64/signal/.gitignore b/tools/testing/selftests/arm64/signal/.gitignore index 8ab4c86837fd..839e3a252629 100644 --- a/tools/testing/selftests/arm64/signal/.gitignore +++ b/tools/testing/selftests/arm64/signal/.gitignore @@ -4,7 +4,7 @@ fake_sigreturn_* sme_* ssve_* sve_* -tpidr2_siginfo +tpidr2_* za_* zt_* !*.[ch] diff --git a/tools/testing/selftests/arm64/signal/testcases/tpidr2_restore.c b/tools/testing/selftests/arm64/signal/testcases/tpidr2_restore.c new file mode 100644 index 000000000000..c01db4d56eb9 --- /dev/null +++ b/tools/testing/selftests/arm64/signal/testcases/tpidr2_restore.c @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 ARM Limited + * + * Verify that the TPIDR2 register context in signal frames is restored. + */ + +#include <signal.h> +#include <ucontext.h> +#include <sys/auxv.h> +#include <sys/prctl.h> +#include <unistd.h> +#include <asm/sigcontext.h> + +#include "test_signals_utils.h" +#include "testcases.h" + +#define SYS_TPIDR2 "S3_3_C13_C0_5" + +static uint64_t get_tpidr2(void) +{ + uint64_t val; + + asm volatile ( + "mrs %0, " SYS_TPIDR2 "\n" + : "=r"(val) + : + : "cc"); + + return val; +} + +static void set_tpidr2(uint64_t val) +{ + asm volatile ( + "msr " SYS_TPIDR2 ", %0\n" + : + : "r"(val) + : "cc"); +} + + +static uint64_t initial_tpidr2; + +static bool save_tpidr2(struct tdescr *td) +{ + initial_tpidr2 = get_tpidr2(); + fprintf(stderr, "Initial TPIDR2: %lx\n", initial_tpidr2); + + return true; +} + +static int modify_tpidr2(struct tdescr *td, siginfo_t *si, ucontext_t *uc) +{ + uint64_t my_tpidr2 = get_tpidr2(); + + my_tpidr2++; + fprintf(stderr, "Setting TPIDR2 to %lx\n", my_tpidr2); + set_tpidr2(my_tpidr2); + + return 0; +} + +static void check_tpidr2(struct tdescr *td) +{ + uint64_t tpidr2 = get_tpidr2(); + + td->pass = tpidr2 == initial_tpidr2; + + if (td->pass) + fprintf(stderr, "TPIDR2 restored\n"); + else + fprintf(stderr, "TPIDR2 was %lx but is now %lx\n", + initial_tpidr2, tpidr2); +} + +struct tdescr tde = { + .name = "TPIDR2 restore", + .descr = "Validate that TPIDR2 is restored from the sigframe", + .timeout = 3, + .sig_trig = SIGUSR1, + .init = save_tpidr2, + .run = modify_tpidr2, + .check_result = check_tpidr2, +};
On Wed, Jun 21, 2023 at 10:00:51PM +0100, Mark Brown wrote:
+struct tdescr tde = {
- .name = "TPIDR2 restore",
- .descr = "Validate that TPIDR2 is restored from the sigframe",
- .timeout = 3,
- .sig_trig = SIGUSR1,
- .init = save_tpidr2,
- .run = modify_tpidr2,
- .check_result = check_tpidr2,
+};
This is missing a
.feats_required = FEAT_SME,
linux-kselftest-mirror@lists.linaro.org