diff mbox series

[v7,17/20] target/arm: Rebuild hflags at MSR writes

Message ID 20191017185110.539-18-richard.henderson@linaro.org
State Superseded
Headers show
Series target/arm: Reduce overhead of cpu_get_tb_cpu_state | expand

Commit Message

Richard Henderson Oct. 17, 2019, 6:51 p.m. UTC
Continue setting, but not relying upon, env->hflags.

Reviewed-by: Alex Bennée <alex.bennee@linaro.org>

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>

---
 target/arm/translate-a64.c | 13 +++++++++++--
 target/arm/translate.c     | 28 +++++++++++++++++++++++-----
 2 files changed, 34 insertions(+), 7 deletions(-)

-- 
2.17.1

Comments

Peter Maydell Oct. 18, 2019, 12:32 p.m. UTC | #1
On Thu, 17 Oct 2019 at 19:51, Richard Henderson
<richard.henderson@linaro.org> wrote:
>

> Continue setting, but not relying upon, env->hflags.

>

> Reviewed-by: Alex Bennée <alex.bennee@linaro.org>

> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>

> ---

>  target/arm/translate-a64.c | 13 +++++++++++--

>  target/arm/translate.c     | 28 +++++++++++++++++++++++-----

>  2 files changed, 34 insertions(+), 7 deletions(-)

> @@ -7068,14 +7070,30 @@ static int disas_coproc_insn(DisasContext *s, uint32_t insn)

>              }

>          }

>

> -        if ((tb_cflags(s->base.tb) & CF_USE_ICOUNT) && (ri->type & ARM_CP_IO)) {

> -            /* I/O operations must end the TB here (whether read or write) */

> -            gen_lookup_tb(s);

> -        } else if (!isread && !(ri->type & ARM_CP_SUPPRESS_TB_END)) {

> -            /* We default to ending the TB on a coprocessor register write,

> +        /* I/O operations must end the TB here (whether read or write) */

> +        need_exit_tb = ((tb_cflags(s->base.tb) & CF_USE_ICOUNT) &&

> +                        (ri->type & ARM_CP_IO));

> +

> +        if (!isread && !(ri->type & ARM_CP_SUPPRESS_TB_END)) {

> +            /*

> +             * A write to any coprocessor regiser that ends a TB


(typo: "register")

> +             * must rebuild the hflags for the next TB.

> +             */

> +            TCGv_i32 tcg_el = tcg_const_i32(s->current_el);

> +            if (arm_dc_feature(s, ARM_FEATURE_M)) {

> +                gen_helper_rebuild_hflags_m32(cpu_env, tcg_el);

> +            } else {

> +                gen_helper_rebuild_hflags_a32(cpu_env, tcg_el);

> +            }

> +            tcg_temp_free_i32(tcg_el);


Why only rebuild hflags if !ARM_CP_SUPPRESS_TB_END ?
For instance on the Xscale CPUs we set SUPPRESS_TB_END for the SCTLR,
but some of the SCTLR bits are cached in hflags, right? Do we somehow
arrange to rebuild the hflags when the TB does eventually end ?

> +            /*

> +             * We default to ending the TB on a coprocessor register write,

>               * but allow this to be suppressed by the register definition

>               * (usually only necessary to work around guest bugs).

>               */

> +            need_exit_tb = true;

> +        }

> +        if (need_exit_tb) {

>              gen_lookup_tb(s);

>          }


thanks
-- PMM
Richard Henderson Oct. 18, 2019, 2:30 p.m. UTC | #2
On 10/18/19 5:32 AM, Peter Maydell wrote:
> On Thu, 17 Oct 2019 at 19:51, Richard Henderson

> <richard.henderson@linaro.org> wrote:

>>

>> Continue setting, but not relying upon, env->hflags.

>>

>> Reviewed-by: Alex Bennée <alex.bennee@linaro.org>

>> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>

>> ---

>>  target/arm/translate-a64.c | 13 +++++++++++--

>>  target/arm/translate.c     | 28 +++++++++++++++++++++++-----

>>  2 files changed, 34 insertions(+), 7 deletions(-)

>> @@ -7068,14 +7070,30 @@ static int disas_coproc_insn(DisasContext *s, uint32_t insn)

>>              }

>>          }

>>

>> -        if ((tb_cflags(s->base.tb) & CF_USE_ICOUNT) && (ri->type & ARM_CP_IO)) {

>> -            /* I/O operations must end the TB here (whether read or write) */

>> -            gen_lookup_tb(s);

>> -        } else if (!isread && !(ri->type & ARM_CP_SUPPRESS_TB_END)) {

>> -            /* We default to ending the TB on a coprocessor register write,

>> +        /* I/O operations must end the TB here (whether read or write) */

>> +        need_exit_tb = ((tb_cflags(s->base.tb) & CF_USE_ICOUNT) &&

>> +                        (ri->type & ARM_CP_IO));

>> +

>> +        if (!isread && !(ri->type & ARM_CP_SUPPRESS_TB_END)) {

>> +            /*

>> +             * A write to any coprocessor regiser that ends a TB

> 

> (typo: "register")

> 

>> +             * must rebuild the hflags for the next TB.

>> +             */

>> +            TCGv_i32 tcg_el = tcg_const_i32(s->current_el);

>> +            if (arm_dc_feature(s, ARM_FEATURE_M)) {

>> +                gen_helper_rebuild_hflags_m32(cpu_env, tcg_el);

>> +            } else {

>> +                gen_helper_rebuild_hflags_a32(cpu_env, tcg_el);

>> +            }

>> +            tcg_temp_free_i32(tcg_el);

> 

> Why only rebuild hflags if !ARM_CP_SUPPRESS_TB_END ?

> For instance on the Xscale CPUs we set SUPPRESS_TB_END for the SCTLR,

> but some of the SCTLR bits are cached in hflags, right? Do we somehow

> arrange to rebuild the hflags when the TB does eventually end ?


No, we don't.  I assumed that all registers which change TB flags would in fact
end the TB.

Why did we suppress tb end for Xscale?


r~
Peter Maydell Oct. 18, 2019, 2:49 p.m. UTC | #3
On Fri, 18 Oct 2019 at 15:30, Richard Henderson
<richard.henderson@linaro.org> wrote:
>

> On 10/18/19 5:32 AM, Peter Maydell wrote:

> > On Thu, 17 Oct 2019 at 19:51, Richard Henderson

> > <richard.henderson@linaro.org> wrote:

> > Why only rebuild hflags if !ARM_CP_SUPPRESS_TB_END ?

> > For instance on the Xscale CPUs we set SUPPRESS_TB_END for the SCTLR,

> > but some of the SCTLR bits are cached in hflags, right? Do we somehow

> > arrange to rebuild the hflags when the TB does eventually end ?

>

> No, we don't.  I assumed that all registers which change TB flags would in fact

> end the TB.

>

> Why did we suppress tb end for Xscale?


The comment in helper.c explains:
            /* Normally we would always end the TB on an SCTLR write, but Linux
             * arch/arm/mach-pxa/sleep.S expects two instructions following
             * an MMU enable to execute from cache.  Imitate this behaviour.
             */

This refers to an older version of the kernel code than the current one:
https://elixir.bootlin.com/linux/v2.6.11/source/arch/arm/mach-pxa/sleep.S#L166
which assumed that the two insns after the SCTLR write that enables
the MMU run from cache and so don't need to actually be accessible
via the addresses that the pre-MMU-enable code runs from.
This bit of kernel magic eventually went away with commit 4f5ad99bb5331c57
(kernel v2.6.39) which converted the PXA to use the generic suspend/resume
support, which presumably handles the enable-the-MMU transition in a more
sensible fashion where the address it's executing from is valid
both before and after the MMU is enabled.

thanks
-- PMM
diff mbox series

Patch

diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c
index 2d6cd09634..d4bebbe629 100644
--- a/target/arm/translate-a64.c
+++ b/target/arm/translate-a64.c
@@ -1789,8 +1789,17 @@  static void handle_sys(DisasContext *s, uint32_t insn, bool isread,
     if ((tb_cflags(s->base.tb) & CF_USE_ICOUNT) && (ri->type & ARM_CP_IO)) {
         /* I/O operations must end the TB here (whether read or write) */
         s->base.is_jmp = DISAS_UPDATE;
-    } else if (!isread && !(ri->type & ARM_CP_SUPPRESS_TB_END)) {
-        /* We default to ending the TB on a coprocessor register write,
+    }
+    if (!isread && !(ri->type & ARM_CP_SUPPRESS_TB_END)) {
+        /*
+         * A write to any coprocessor regiser that ends a TB
+         * must rebuild the hflags for the next TB.
+         */
+        TCGv_i32 tcg_el = tcg_const_i32(s->current_el);
+        gen_helper_rebuild_hflags_a64(cpu_env, tcg_el);
+        tcg_temp_free_i32(tcg_el);
+        /*
+         * We default to ending the TB on a coprocessor register write,
          * but allow this to be suppressed by the register definition
          * (usually only necessary to work around guest bugs).
          */
diff --git a/target/arm/translate.c b/target/arm/translate.c
index 698c594e8c..cb47cd9744 100644
--- a/target/arm/translate.c
+++ b/target/arm/translate.c
@@ -6890,6 +6890,8 @@  static int disas_coproc_insn(DisasContext *s, uint32_t insn)
     ri = get_arm_cp_reginfo(s->cp_regs,
             ENCODE_CP_REG(cpnum, is64, s->ns, crn, crm, opc1, opc2));
     if (ri) {
+        bool need_exit_tb;
+
         /* Check access permissions */
         if (!cp_access_ok(s->current_el, ri, isread)) {
             return 1;
@@ -7068,14 +7070,30 @@  static int disas_coproc_insn(DisasContext *s, uint32_t insn)
             }
         }
 
-        if ((tb_cflags(s->base.tb) & CF_USE_ICOUNT) && (ri->type & ARM_CP_IO)) {
-            /* I/O operations must end the TB here (whether read or write) */
-            gen_lookup_tb(s);
-        } else if (!isread && !(ri->type & ARM_CP_SUPPRESS_TB_END)) {
-            /* We default to ending the TB on a coprocessor register write,
+        /* I/O operations must end the TB here (whether read or write) */
+        need_exit_tb = ((tb_cflags(s->base.tb) & CF_USE_ICOUNT) &&
+                        (ri->type & ARM_CP_IO));
+
+        if (!isread && !(ri->type & ARM_CP_SUPPRESS_TB_END)) {
+            /*
+             * A write to any coprocessor regiser that ends a TB
+             * must rebuild the hflags for the next TB.
+             */
+            TCGv_i32 tcg_el = tcg_const_i32(s->current_el);
+            if (arm_dc_feature(s, ARM_FEATURE_M)) {
+                gen_helper_rebuild_hflags_m32(cpu_env, tcg_el);
+            } else {
+                gen_helper_rebuild_hflags_a32(cpu_env, tcg_el);
+            }
+            tcg_temp_free_i32(tcg_el);
+            /*
+             * We default to ending the TB on a coprocessor register write,
              * but allow this to be suppressed by the register definition
              * (usually only necessary to work around guest bugs).
              */
+            need_exit_tb = true;
+        }
+        if (need_exit_tb) {
             gen_lookup_tb(s);
         }