diff --git a/target/arm/helper.h b/target/arm/helper.h
index 5161cdf73d..a13007699d 100644
--- a/target/arm/helper.h
+++ b/target/arm/helper.h
@@ -46,6 +46,7 @@ DEF_HELPER_FLAGS_3(sel_flags, TCG_CALL_NO_RWG_SE,
                    i32, i32, i32, i32)
 DEF_HELPER_2(exception_internal, noreturn, env, i32)
 DEF_HELPER_4(exception_with_syndrome, noreturn, env, i32, i32, i32)
+DEF_HELPER_3(exception_advsimdfp_access, noreturn, env, i32, i32)
 DEF_HELPER_2(exception_bkpt_insn, noreturn, env, i32)
 DEF_HELPER_2(exception_pc_alignment, noreturn, env, tl)
 DEF_HELPER_1(setend, void, env)
diff --git a/target/arm/translate.h b/target/arm/translate.h
index 9f0bb270c5..da5f89d49d 100644
--- a/target/arm/translate.h
+++ b/target/arm/translate.h
@@ -337,6 +337,15 @@ static inline void gen_exception(int excp, uint32_t syndrome,
                                        tcg_constant_i32(target_el));
 }
 
+static inline void gen_exception_advsimdfp_access(DisasContext *s,
+                                                  uint32_t syndrome)
+{
+    gen_helper_exception_advsimdfp_access(cpu_env,
+                                          tcg_constant_i32(syndrome),
+                                          tcg_constant_i32(s->fp_excp_el));
+    s->base.is_jmp = DISAS_NORETURN;
+}
+
 /* Generate an architectural singlestep exception */
 static inline void gen_swstep_exception(DisasContext *s, int isv, int ex)
 {
diff --git a/target/arm/op_helper.c b/target/arm/op_helper.c
index c4bd668870..67b7dbeaa9 100644
--- a/target/arm/op_helper.c
+++ b/target/arm/op_helper.c
@@ -34,16 +34,8 @@ void raise_exception(CPUARMState *env, uint32_t excp,
     CPUState *cs = env_cpu(env);
 
     if (target_el == 1 && (arm_hcr_el2_eff(env) & HCR_TGE)) {
-        /*
-         * Redirect NS EL1 exceptions to NS EL2. These are reported with
-         * their original syndrome register value, with the exception of
-         * SIMD/FP access traps, which are reported as uncategorized
-         * (see DDI0478C.a D1.10.4)
-         */
+        /* Redirect NS EL1 exceptions to NS EL2. */
         target_el = 2;
-        if (syn_get_ec(syndrome) == EC_ADVSIMDFPACCESSTRAP) {
-            syndrome = syn_uncategorized();
-        }
     }
 
     assert(!excp_is_internal(excp));
@@ -384,6 +376,28 @@ void HELPER(exception_with_syndrome)(CPUARMState *env, uint32_t excp,
     raise_exception(env, excp, syndrome, target_el);
 }
 
+/* Raise an exception with EC_ADVSIMDFPACCESS. */
+void HELPER(exception_advsimdfp_access)(CPUARMState *env,
+                                        uint32_t syndrome, uint32_t target_el)
+{
+    if (target_el == 1 && (arm_hcr_el2_eff(env) & HCR_TGE)) {
+        /*
+         * SIMD/FP access traps, when re-routed to EL2, are reported with
+         * uncategorized syndrome.  See DDI0487H.a rule RJNBTN.
+         */
+        target_el = 2;
+        syndrome = syn_uncategorized();
+    } else if (arm_el_is_aa64(env, target_el)) {
+        /*
+         * From AArch32, we will have filled in TA and/or COPROC for use
+         * by aa32 HSR.  But in aa64 ESR_ELx, the low 20 bits are RES0.
+         */
+        syndrome &= 0xfff00000;
+    }
+
+    raise_exception(env, EXCP_UDEF, syndrome, target_el);
+}
+
 /* Raise an EXCP_BKPT with the specified syndrome register value,
  * targeting the correct exception level for debug exceptions.
  */
diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c
index 14b90a37ea..3251c69180 100644
--- a/target/arm/translate-a64.c
+++ b/target/arm/translate-a64.c
@@ -1161,9 +1161,8 @@ static bool fp_access_check(DisasContext *s)
         assert(!s->fp_access_checked);
         s->fp_access_checked = true;
 
-        gen_exception_insn(s, s->pc_curr, EXCP_UDEF,
-                           syn_fp_access_trap(1, 0xe, false, 0),
-                           s->fp_excp_el);
+        gen_a64_set_pc_im(s->pc_curr);
+        gen_exception_advsimdfp_access(s, syn_fp_access_trap(1, 0xe, false, 0));
         return false;
     }
     s->fp_access_checked = true;
diff --git a/target/arm/translate-vfp.c b/target/arm/translate-vfp.c
index 0f797c56fd..2a98387411 100644
--- a/target/arm/translate-vfp.c
+++ b/target/arm/translate-vfp.c
@@ -230,7 +230,9 @@ static bool vfp_access_check_a(DisasContext *s, bool ignore_vfp_enabled)
         int coproc = arm_dc_feature(s, ARM_FEATURE_V8) ? 0 : 0xa;
         uint32_t syn = syn_fp_access_trap(1, 0xe, false, coproc);
 
-        gen_exception_insn(s, s->pc_curr, EXCP_UDEF, syn, s->fp_excp_el);
+        gen_set_condexec(s);
+        gen_set_pc_im(s, s->pc_curr);
+        gen_exception_advsimdfp_access(s, syn);
         return false;
     }
 
