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

Richard Henderson posted 36 patches 1 week, 5 days ago
Maintainers: Peter Maydell <peter.maydell@linaro.org>, Alexander Graf <agraf@csgraf.de>, Mads Ynddal <mads@ynddal.dk>, Paolo Bonzini <pbonzini@redhat.com>
[PATCH v2 33/36] target/arm: Redirect VHE FOO_EL1 -> FOO_EL2 during translation
Posted by Richard Henderson 1 week, 5 days ago
Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
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 0f681c15e0..49bb1e8365 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -4456,47 +4456,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 */
@@ -4676,17 +4635,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 c0fa2137b6..3ef24fb0c3 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 v2 33/36] target/arm: Redirect VHE FOO_EL1 -> FOO_EL2 during translation
Posted by Peter Maydell 3 days, 2 hours ago
On Tue, 16 Sept 2025 at 15:23, Richard Henderson
<richard.henderson@linaro.org> wrote:
>
> Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
> 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(-)

> -/* 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);
> -}

> diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c
> index c0fa2137b6..3ef24fb0c3 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);
> +    }

I was looking through the details for this one, and noticed that
R_PHHPL says the redirects from FOO_EL12 to FOO_EL1 apply
when "the PE is executing at EL2 or EL3", so I think our
check on "EL == 2" isn't actually correct. But as you can
see in the old redirect_for_e2h() code this has always been
wrong, so as this is a refactoring and shouldn't be introducing
behaviour changes:

Reviewed-by: Peter Maydell <peter.maydell@linaro.org>

thanks
-- PMM
Re: [PATCH v2 33/36] target/arm: Redirect VHE FOO_EL1 -> FOO_EL2 during translation
Posted by Peter Maydell 3 days, 2 hours ago
On Thu, 25 Sept 2025 at 14:54, Peter Maydell <peter.maydell@linaro.org> wrote:
>
> On Tue, 16 Sept 2025 at 15:23, Richard Henderson
> <richard.henderson@linaro.org> wrote:
> >
> > Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
> > 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(-)
>
> > -/* 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);
> > -}
>
> > diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c
> > index c0fa2137b6..3ef24fb0c3 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);
> > +    }
>
> I was looking through the details for this one, and noticed that
> R_PHHPL says the redirects from FOO_EL12 to FOO_EL1 apply
> when "the PE is executing at EL2 or EL3", so I think our
> check on "EL == 2" isn't actually correct.

No, I'm wrong here -- R_PHHPL is for the registers handled in
patch 34. This codepath is for E2H redirects as listed in
R_JGGMV, and those do only happen at EL2, not at EL3.

-- PMM
Re: [PATCH v2 33/36] target/arm: Redirect VHE FOO_EL1 -> FOO_EL2 during translation
Posted by Philippe Mathieu-Daudé 3 days, 3 hours ago
On 16/9/25 16:22, Richard Henderson wrote:
> Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
> 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(-)

Tested-by: Philippe Mathieu-Daudé <philmd@linaro.org>