[PATCH 33/61] target/arm: Redirect VHE FOO_EL1 -> FOO_EL2 during translation

Richard Henderson posted 61 patches 1 month ago
[PATCH 33/61] target/arm: Redirect VHE FOO_EL1 -> FOO_EL2 during translation
Posted by Richard Henderson 1 month ago
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
 target/arm/cpregs.h            |  6 ++++
 target/arm/gdbstub.c           |  5 ++++
 target/arm/helper.c            | 53 +---------------------------------
 target/arm/tcg/translate-a64.c |  9 ++++++
 4 files changed, 21 insertions(+), 52 deletions(-)

diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h
index 7bdf6cf847..d34ed0d40b 100644
--- a/target/arm/cpregs.h
+++ b/target/arm/cpregs.h
@@ -911,6 +911,12 @@ struct ARMCPRegInfo {
      */
     uint32_t nv2_redirect_offset;
 
+    /*
+     * With VHE, with E2H, at EL2, access to this EL0/EL1 reg redirects
+     * to the EL2 reg with the specified key.
+     */
+    uint32_t vhe_redir_to_el2;
+
     /* This is used only by VHE. */
     void *opaque;
     /*
diff --git a/target/arm/gdbstub.c b/target/arm/gdbstub.c
index e2fc389170..3727dc01af 100644
--- a/target/arm/gdbstub.c
+++ b/target/arm/gdbstub.c
@@ -249,6 +249,11 @@ static int arm_gdb_get_sysreg(CPUState *cs, GByteArray *buf, int reg)
     if (ri) {
         switch (cpreg_field_type(ri)) {
         case MO_64:
+            if (ri->vhe_redir_to_el2 &&
+                (arm_hcr_el2_eff(env) & HCR_E2H) &&
+                arm_current_el(env) == 2) {
+                ri = get_arm_cp_reginfo(cpu->cp_regs, ri->vhe_redir_to_el2);
+            }
             return gdb_get_reg64(buf, (uint64_t)read_raw_cp_reg(env, ri));
         case MO_32:
             return gdb_get_reg32(buf, (uint32_t)read_raw_cp_reg(env, ri));
diff --git a/target/arm/helper.c b/target/arm/helper.c
index d0ccc23811..3f69ce6cb5 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -4441,47 +4441,6 @@ static CPAccessResult access_el1nvvct(CPUARMState *env, const ARMCPRegInfo *ri,
     return e2h_access(env, ri, isread);
 }
 
-/* Test if system register redirection is to occur in the current state.  */
-static bool redirect_for_e2h(CPUARMState *env)
-{
-    return arm_current_el(env) == 2 && (arm_hcr_el2_eff(env) & HCR_E2H);
-}
-
-static uint64_t el2_e2h_read(CPUARMState *env, const ARMCPRegInfo *ri)
-{
-    CPReadFn *readfn;
-
-    if (redirect_for_e2h(env)) {
-        /* Switch to the saved EL2 version of the register.  */
-        ri = ri->opaque;
-        readfn = ri->readfn;
-    } else {
-        readfn = ri->orig_readfn;
-    }
-    if (readfn == NULL) {
-        readfn = raw_read;
-    }
-    return readfn(env, ri);
-}
-
-static void el2_e2h_write(CPUARMState *env, const ARMCPRegInfo *ri,
-                          uint64_t value)
-{
-    CPWriteFn *writefn;
-
-    if (redirect_for_e2h(env)) {
-        /* Switch to the saved EL2 version of the register.  */
-        ri = ri->opaque;
-        writefn = ri->writefn;
-    } else {
-        writefn = ri->orig_writefn;
-    }
-    if (writefn == NULL) {
-        writefn = raw_write;
-    }
-    writefn(env, ri, value);
-}
-
 static uint64_t el2_e2h_e12_read(CPUARMState *env, const ARMCPRegInfo *ri)
 {
     /* Pass the EL1 register accessor its ri, not the EL12 alias ri */
@@ -4657,17 +4616,7 @@ static void define_arm_vh_e2h_redirects_aliases(ARMCPU *cpu)
                                  (gpointer)(uintptr_t)a->new_key, new_reg);
         g_assert(ok);
 
-        src_reg->opaque = dst_reg;
-        src_reg->orig_readfn = src_reg->readfn ?: raw_read;
-        src_reg->orig_writefn = src_reg->writefn ?: raw_write;
-        if (!src_reg->raw_readfn) {
-            src_reg->raw_readfn = raw_read;
-        }
-        if (!src_reg->raw_writefn) {
-            src_reg->raw_writefn = raw_write;
-        }
-        src_reg->readfn = el2_e2h_read;
-        src_reg->writefn = el2_e2h_write;
+        src_reg->vhe_redir_to_el2 = a->dst_key;
     }
 }
 #endif
diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c
index 8303f0dac2..8fcc74d151 100644
--- a/target/arm/tcg/translate-a64.c
+++ b/target/arm/tcg/translate-a64.c
@@ -2573,6 +2573,15 @@ static void handle_sys(DisasContext *s, bool isread,
         }
     }
 
+    if (ri->vhe_redir_to_el2 && s->current_el == 2 && s->e2h) {
+        /*
+         * This one of the FOO_EL1 registers which redirect to FOO_EL2
+         * from EL2 when HCR_EL2.E2H is set.
+         */
+        key = ri->vhe_redir_to_el2;
+        ri = redirect_cpreg(s, key, isread);
+    }
+
     if (ri->accessfn || (ri->fgt && s->fgt_active)) {
         /* Emit code to perform further access permissions checks at
          * runtime; this may result in an exception.
-- 
2.43.0
Re: [PATCH 33/61] target/arm: Redirect VHE FOO_EL1 -> FOO_EL2 during translation
Posted by Manos Pitsidianakis 1 month ago
On Wed, Aug 27, 2025 at 4:11 AM Richard Henderson
<richard.henderson@linaro.org> wrote:
>
> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
> ---
>  target/arm/cpregs.h            |  6 ++++
>  target/arm/gdbstub.c           |  5 ++++
>  target/arm/helper.c            | 53 +---------------------------------
>  target/arm/tcg/translate-a64.c |  9 ++++++
>  4 files changed, 21 insertions(+), 52 deletions(-)
>
> diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h
> index 7bdf6cf847..d34ed0d40b 100644
> --- a/target/arm/cpregs.h
> +++ b/target/arm/cpregs.h
> @@ -911,6 +911,12 @@ struct ARMCPRegInfo {
>       */
>      uint32_t nv2_redirect_offset;
>
> +    /*
> +     * With VHE, with E2H, at EL2, access to this EL0/EL1 reg redirects
> +     * to the EL2 reg with the specified key.
> +     */
> +    uint32_t vhe_redir_to_el2;
> +
>      /* This is used only by VHE. */
>      void *opaque;
>      /*
> diff --git a/target/arm/gdbstub.c b/target/arm/gdbstub.c
> index e2fc389170..3727dc01af 100644
> --- a/target/arm/gdbstub.c
> +++ b/target/arm/gdbstub.c
> @@ -249,6 +249,11 @@ static int arm_gdb_get_sysreg(CPUState *cs, GByteArray *buf, int reg)
>      if (ri) {
>          switch (cpreg_field_type(ri)) {
>          case MO_64:
> +            if (ri->vhe_redir_to_el2 &&
> +                (arm_hcr_el2_eff(env) & HCR_E2H) &&
> +                arm_current_el(env) == 2) {
> +                ri = get_arm_cp_reginfo(cpu->cp_regs, ri->vhe_redir_to_el2);
> +            }
>              return gdb_get_reg64(buf, (uint64_t)read_raw_cp_reg(env, ri));
>          case MO_32:
>              return gdb_get_reg32(buf, (uint32_t)read_raw_cp_reg(env, ri));
> diff --git a/target/arm/helper.c b/target/arm/helper.c
> index d0ccc23811..3f69ce6cb5 100644
> --- a/target/arm/helper.c
> +++ b/target/arm/helper.c
> @@ -4441,47 +4441,6 @@ static CPAccessResult access_el1nvvct(CPUARMState *env, const ARMCPRegInfo *ri,
>      return e2h_access(env, ri, isread);
>  }
>
> -/* Test if system register redirection is to occur in the current state.  */
> -static bool redirect_for_e2h(CPUARMState *env)
> -{
> -    return arm_current_el(env) == 2 && (arm_hcr_el2_eff(env) & HCR_E2H);
> -}
> -
> -static uint64_t el2_e2h_read(CPUARMState *env, const ARMCPRegInfo *ri)
> -{
> -    CPReadFn *readfn;
> -
> -    if (redirect_for_e2h(env)) {
> -        /* Switch to the saved EL2 version of the register.  */
> -        ri = ri->opaque;
> -        readfn = ri->readfn;
> -    } else {
> -        readfn = ri->orig_readfn;
> -    }
> -    if (readfn == NULL) {
> -        readfn = raw_read;
> -    }
> -    return readfn(env, ri);
> -}
> -
> -static void el2_e2h_write(CPUARMState *env, const ARMCPRegInfo *ri,
> -                          uint64_t value)
> -{
> -    CPWriteFn *writefn;
> -
> -    if (redirect_for_e2h(env)) {
> -        /* Switch to the saved EL2 version of the register.  */
> -        ri = ri->opaque;
> -        writefn = ri->writefn;
> -    } else {
> -        writefn = ri->orig_writefn;
> -    }
> -    if (writefn == NULL) {
> -        writefn = raw_write;
> -    }
> -    writefn(env, ri, value);
> -}
> -
>  static uint64_t el2_e2h_e12_read(CPUARMState *env, const ARMCPRegInfo *ri)
>  {
>      /* Pass the EL1 register accessor its ri, not the EL12 alias ri */
> @@ -4657,17 +4616,7 @@ static void define_arm_vh_e2h_redirects_aliases(ARMCPU *cpu)
>                                   (gpointer)(uintptr_t)a->new_key, new_reg);
>          g_assert(ok);
>
> -        src_reg->opaque = dst_reg;
> -        src_reg->orig_readfn = src_reg->readfn ?: raw_read;
> -        src_reg->orig_writefn = src_reg->writefn ?: raw_write;
> -        if (!src_reg->raw_readfn) {
> -            src_reg->raw_readfn = raw_read;
> -        }
> -        if (!src_reg->raw_writefn) {
> -            src_reg->raw_writefn = raw_write;
> -        }
> -        src_reg->readfn = el2_e2h_read;
> -        src_reg->writefn = el2_e2h_write;
> +        src_reg->vhe_redir_to_el2 = a->dst_key;
>      }
>  }
>  #endif
> diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c
> index 8303f0dac2..8fcc74d151 100644
> --- a/target/arm/tcg/translate-a64.c
> +++ b/target/arm/tcg/translate-a64.c
> @@ -2573,6 +2573,15 @@ static void handle_sys(DisasContext *s, bool isread,
>          }
>      }
>
> +    if (ri->vhe_redir_to_el2 && s->current_el == 2 && s->e2h) {
> +        /*
> +         * This one of the FOO_EL1 registers which redirect to FOO_EL2
> +         * from EL2 when HCR_EL2.E2H is set.
> +         */
> +        key = ri->vhe_redir_to_el2;
> +        ri = redirect_cpreg(s, key, isread);
> +    }
> +
>      if (ri->accessfn || (ri->fgt && s->fgt_active)) {
>          /* Emit code to perform further access permissions checks at
>           * runtime; this may result in an exception.
> --
> 2.43.0
>
>

This looks much better

Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>