[PATCH for-10.1 2/3] linux-user/aarch64: Support TPIDR2_MAGIC signal frame record

Peter Maydell posted 3 patches 3 months, 3 weeks ago
Maintainers: Laurent Vivier <laurent@vivier.eu>
There is a newer version of this series
[PATCH for-10.1 2/3] linux-user/aarch64: Support TPIDR2_MAGIC signal frame record
Posted by Peter Maydell 3 months, 3 weeks ago
FEAT_SME adds the TPIDR2 userspace-accessible system register, which
is used as part of the procedure calling standard's lazy saving
scheme for the ZA registers:
 https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst#66the-za-lazy-saving-scheme

The Linux kernel has a signal frame record for saving
and restoring this value when calling signal handlers, but
we forgot to implement this. The result is that code which
tries to unwind an exception out of a signal handler will
not work correctly.

Add support for the missing record.

Cc: qemu-stable@nongnu.org
Fixes: 78011586b90d1 ("target/arm: Enable SME for user-only")
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 linux-user/aarch64/signal.c | 46 +++++++++++++++++++++++++++++++++++--
 1 file changed, 44 insertions(+), 2 deletions(-)

diff --git a/linux-user/aarch64/signal.c b/linux-user/aarch64/signal.c
index b4bab7c040d..9a903576add 100644
--- a/linux-user/aarch64/signal.c
+++ b/linux-user/aarch64/signal.c
@@ -121,6 +121,13 @@ struct target_za_context {
 #define TARGET_ZA_SIG_CONTEXT_SIZE(VQ) \
     TARGET_ZA_SIG_ZAV_OFFSET(VQ, VQ * TARGET_SVE_VQ_BYTES)
 
+#define TARGET_TPIDR2_MAGIC 0x54504902
+
+struct target_tpidr2_context {
+    struct target_aarch64_ctx head;
+    uint64_t tpidr2;
+};
+
 struct target_rt_sigframe {
     struct target_siginfo info;
     struct target_ucontext uc;
@@ -253,6 +260,14 @@ static void target_setup_za_record(struct target_za_context *za,
     }
 }
 
+static void target_setup_tpidr2_record(struct target_tpidr2_context *tpidr2,
+                                       CPUARMState *env, int size)
+{
+    __put_user(TARGET_TPIDR2_MAGIC, &tpidr2->head.magic);
+    __put_user(sizeof(struct target_tpidr2_context), &tpidr2->head.size);
+    __put_user(env->cp15.tpidr2_el0, &tpidr2->tpidr2);
+}
+
 static void target_restore_general_frame(CPUARMState *env,
                                          struct target_rt_sigframe *sf)
 {
@@ -403,6 +418,16 @@ static bool target_restore_za_record(CPUARMState *env,
     return true;
 }
 
+static bool target_restore_tpidr2_record(CPUARMState *env,
+                                         struct target_tpidr2_context *tpidr2)
+{
+    if (!cpu_isar_feature(aa64_sme, env_archcpu(env))) {
+        return false;
+    }
+    __get_user(env->cp15.tpidr2_el0, &tpidr2->tpidr2);
+    return true;
+}
+
 static int target_restore_sigframe(CPUARMState *env,
                                    struct target_rt_sigframe *sf)
 {
@@ -410,6 +435,7 @@ static int target_restore_sigframe(CPUARMState *env,
     struct target_fpsimd_context *fpsimd = NULL;
     struct target_sve_context *sve = NULL;
     struct target_za_context *za = NULL;
+    struct target_tpidr2_context *tpidr2 = NULL;
     uint64_t extra_datap = 0;
     bool used_extra = false;
     int sve_size = 0;
@@ -460,6 +486,13 @@ static int target_restore_sigframe(CPUARMState *env,
             za_size = size;
             break;
 
+        case TARGET_TPIDR2_MAGIC:
+            if (tpidr2 || size != sizeof(struct target_tpidr2_context)) {
+                goto err;
+            }
+            tpidr2 = (struct target_tpidr2_context *)ctx;
+            break;
+
         case TARGET_EXTRA_MAGIC:
             if (extra || size != sizeof(struct target_extra_context)) {
                 goto err;
@@ -497,6 +530,9 @@ static int target_restore_sigframe(CPUARMState *env,
     if (za && !target_restore_za_record(env, za, za_size, &svcr)) {
         goto err;
     }
+    if (tpidr2 && !target_restore_tpidr2_record(env, tpidr2)) {
+        goto err;
+    }
     if (env->svcr != svcr) {
         env->svcr = svcr;
         arm_rebuild_hflags(env);
@@ -568,8 +604,8 @@ static void target_setup_frame(int usig, struct target_sigaction *ka,
         .total_size = offsetof(struct target_rt_sigframe,
                                uc.tuc_mcontext.__reserved),
     };
-    int fpsimd_ofs, fr_ofs, sve_ofs = 0, za_ofs = 0;
-    int sve_size = 0, za_size = 0;
+    int fpsimd_ofs, fr_ofs, sve_ofs = 0, za_ofs = 0, tpidr2_ofs = 0;
+    int sve_size = 0, za_size = 0, tpidr2_size = 0;
     struct target_rt_sigframe *frame;
     struct target_rt_frame_record *fr;
     abi_ulong frame_addr, return_addr;
@@ -585,6 +621,8 @@ static void target_setup_frame(int usig, struct target_sigaction *ka,
         sve_ofs = alloc_sigframe_space(sve_size, &layout);
     }
     if (cpu_isar_feature(aa64_sme, env_archcpu(env))) {
+        tpidr2_size = sizeof(struct target_tpidr2_context);
+        tpidr2_ofs = alloc_sigframe_space(tpidr2_size, &layout);
         /* ZA state needs saving only if it is enabled.  */
         if (FIELD_EX64(env->svcr, SVCR, ZA)) {
             za_size = TARGET_ZA_SIG_CONTEXT_SIZE(sme_vq(env));
@@ -644,6 +682,10 @@ static void target_setup_frame(int usig, struct target_sigaction *ka,
     if (za_ofs) {
         target_setup_za_record((void *)frame + za_ofs, env, za_size);
     }
+    if (tpidr2_ofs) {
+        target_setup_tpidr2_record((void *)frame + tpidr2_ofs,
+                                   env, tpidr2_size);
+    }
 
     /* Set up the stack frame for unwinding.  */
     fr = (void *)frame + fr_ofs;
-- 
2.43.0
Re: [PATCH for-10.1 2/3] linux-user/aarch64: Support TPIDR2_MAGIC signal frame record
Posted by Richard Henderson 3 months, 3 weeks ago
On 7/25/25 04:22, Peter Maydell wrote:
> FEAT_SME adds the TPIDR2 userspace-accessible system register, which
> is used as part of the procedure calling standard's lazy saving
> scheme for the ZA registers:
>   https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst#66the-za-lazy-saving-scheme
> 
> The Linux kernel has a signal frame record for saving
> and restoring this value when calling signal handlers, but
> we forgot to implement this. The result is that code which
> tries to unwind an exception out of a signal handler will
> not work correctly.
> 
> Add support for the missing record.
> 
> Cc: qemu-stable@nongnu.org
> Fixes: 78011586b90d1 ("target/arm: Enable SME for user-only")
> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
> ---
>   linux-user/aarch64/signal.c | 46 +++++++++++++++++++++++++++++++++++--
>   1 file changed, 44 insertions(+), 2 deletions(-)

Oh my.  I spent yesterday working on this and other signal handling fixes.  Though from a 
FEAT_GCS starting point, so I still hadn't seen the clear tpidr2 on signal delivery change.


> +static void target_setup_tpidr2_record(struct target_tpidr2_context *tpidr2,
> +                                       CPUARMState *env, int size)
> +{
> +    __put_user(TARGET_TPIDR2_MAGIC, &tpidr2->head.magic);
> +    __put_user(sizeof(struct target_tpidr2_context), &tpidr2->head.size);
> +    __put_user(env->cp15.tpidr2_el0, &tpidr2->tpidr2);
> +}

Drop the unused size argument.

> +static bool target_restore_tpidr2_record(CPUARMState *env,
> +                                         struct target_tpidr2_context *tpidr2)
> +{
> +    if (!cpu_isar_feature(aa64_sme, env_archcpu(env))) {
> +        return false;
> +    }
> +    __get_user(env->cp15.tpidr2_el0, &tpidr2->tpidr2);
> +    return true;
> +}

The sme check is better placed in target_restore_sigframe ...

> +        case TARGET_TPIDR2_MAGIC:
> +            if (tpidr2 || size != sizeof(struct target_tpidr2_context)) {
> +                goto err;
> +            }

... here.  Then target_restore_tpidr2_record has no failure modes and can return void ...

> @@ -497,6 +530,9 @@ static int target_restore_sigframe(CPUARMState *env,
>       if (za && !target_restore_za_record(env, za, za_size, &svcr)) {
>           goto err;
>       }
> +    if (tpidr2 && !target_restore_tpidr2_record(env, tpidr2)) {
> +        goto err;
> +    }

... which simplifies this.


r~
Re: [PATCH for-10.1 2/3] linux-user/aarch64: Support TPIDR2_MAGIC signal frame record
Posted by Peter Maydell 3 months, 3 weeks ago
On Fri, 25 Jul 2025 at 17:39, Richard Henderson
<richard.henderson@linaro.org> wrote:
>
> On 7/25/25 04:22, Peter Maydell wrote:
> > FEAT_SME adds the TPIDR2 userspace-accessible system register, which
> > is used as part of the procedure calling standard's lazy saving
> > scheme for the ZA registers:
> >   https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst#66the-za-lazy-saving-scheme
> >
> > The Linux kernel has a signal frame record for saving
> > and restoring this value when calling signal handlers, but
> > we forgot to implement this. The result is that code which
> > tries to unwind an exception out of a signal handler will
> > not work correctly.
> >
> > Add support for the missing record.
> >
> > Cc: qemu-stable@nongnu.org
> > Fixes: 78011586b90d1 ("target/arm: Enable SME for user-only")
> > Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
> > ---
> >   linux-user/aarch64/signal.c | 46 +++++++++++++++++++++++++++++++++++--
> >   1 file changed, 44 insertions(+), 2 deletions(-)
>
> Oh my.  I spent yesterday working on this and other signal handling fixes.  Though from a
> FEAT_GCS starting point, so I still hadn't seen the clear tpidr2 on signal delivery change.
>
>
> > +static void target_setup_tpidr2_record(struct target_tpidr2_context *tpidr2,
> > +                                       CPUARMState *env, int size)
> > +{
> > +    __put_user(TARGET_TPIDR2_MAGIC, &tpidr2->head.magic);
> > +    __put_user(sizeof(struct target_tpidr2_context), &tpidr2->head.size);
> > +    __put_user(env->cp15.tpidr2_el0, &tpidr2->tpidr2);
> > +}
>
> Drop the unused size argument.
>
> > +static bool target_restore_tpidr2_record(CPUARMState *env,
> > +                                         struct target_tpidr2_context *tpidr2)
> > +{
> > +    if (!cpu_isar_feature(aa64_sme, env_archcpu(env))) {
> > +        return false;
> > +    }
> > +    __get_user(env->cp15.tpidr2_el0, &tpidr2->tpidr2);
> > +    return true;
> > +}
>
> The sme check is better placed in target_restore_sigframe ...
>
> > +        case TARGET_TPIDR2_MAGIC:
> > +            if (tpidr2 || size != sizeof(struct target_tpidr2_context)) {
> > +                goto err;
> > +            }
>
> ... here.  Then target_restore_tpidr2_record has no failure modes and can return void ...
>
> > @@ -497,6 +530,9 @@ static int target_restore_sigframe(CPUARMState *env,
> >       if (za && !target_restore_za_record(env, za, za_size, &svcr)) {
> >           goto err;
> >       }
> > +    if (tpidr2 && !target_restore_tpidr2_record(env, tpidr2)) {
> > +        goto err;
> > +    }
>
> ... which simplifies this.

Mmm. I wasn't sure to what extent we were trying to follow the
kernel signal.c (which defers essentially all error checks including
sizes to its restore_*_context functions, so that the "switch on
type of record" function is pretty much the same code for each
block, whether it's a complicated variable size one or a simple
fixed record.

-- PMM