[PATCH v2 21/37] target/arm: Extend PAR_EL1 to 128-bit

Richard Henderson posted 37 patches 1 month ago
Maintainers: Peter Xu <peterx@redhat.com>, Fabiano Rosas <farosas@suse.de>, Peter Maydell <peter.maydell@linaro.org>, Alexander Graf <agraf@csgraf.de>, Mads Ynddal <mads@ynddal.dk>, Paolo Bonzini <pbonzini@redhat.com>
[PATCH v2 21/37] target/arm: Extend PAR_EL1 to 128-bit
Posted by Richard Henderson 1 month ago
Do not yet produce the 128-bit AT format result, but zero the
high bits whenever the low bits are written.  This corresponds
to PAR_EL1.D128 = 0, and bits [127:65] as RES0.

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
 target/arm/cpu.h           |  5 ++++-
 target/arm/internals.h     |  1 +
 target/arm/helper.c        | 18 +++++++++++++++++-
 target/arm/tcg/cpregs-at.c |  4 ++++
 4 files changed, 26 insertions(+), 2 deletions(-)

diff --git a/target/arm/cpu.h b/target/arm/cpu.h
index a030539488..e03d832717 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -429,7 +429,8 @@ typedef struct CPUArchState {
         };
         uint64_t hpfar_el2;
         uint64_t hstr_el2;
-        union { /* Translation result. */
+        /* Translation result. */
+        union {
             struct {
                 uint64_t _unused_par_0;
                 uint64_t par_ns;
@@ -438,6 +439,7 @@ typedef struct CPUArchState {
             };
             uint64_t par_el[4];
         };
+        uint64_t par_el1_hi;  /* high 64 bits of 128-bit PAR_EL1 */
 
         uint32_t c9_insn; /* Cache lockdown registers.  */
         uint32_t c9_data;
@@ -1750,6 +1752,7 @@ static inline void xpsr_write(CPUARMState *env, uint32_t val, uint32_t mask)
 #define SCR_SCTLR2EN          (1ULL << 44)
 #define SCR_PIEN              (1ULL << 45)
 #define SCR_AIEN              (1ULL << 46)
+#define SCR_D128EN            (1ULL << 47)
 #define SCR_GPF               (1ULL << 48)
 #define SCR_MECEN             (1ULL << 49)
 #define SCR_NSE               (1ULL << 62)
diff --git a/target/arm/internals.h b/target/arm/internals.h
index a65386aaed..bda2400b65 100644
--- a/target/arm/internals.h
+++ b/target/arm/internals.h
@@ -251,6 +251,7 @@ FIELD(VSTCR, SA, 30, 1)
 #define HCRX_MSCEN    (1ULL << 11)
 #define HCRX_TCR2EN   (1ULL << 14)
 #define HCRX_SCTLR2EN (1ULL << 15)
+#define HCRX_D128EN   (1ULL << 17)
 #define HCRX_GCSEN    (1ULL << 22)
 
 #define HPFAR_NS      (1ULL << 63)
diff --git a/target/arm/helper.c b/target/arm/helper.c
index 7800d83f48..f9a2a7d62d 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -508,6 +508,20 @@ static CPAccessResult access_tacr(CPUARMState *env, const ARMCPRegInfo *ri,
     return CP_ACCESS_OK;
 }
 
+static CPAccessResult access_d128(CPUARMState *env, const ARMCPRegInfo *ri,
+                                  bool isread)
+{
+    int el = arm_current_el(env);
+
+    if (el <= 1 && !(arm_hcrx_el2_eff(env) & HCRX_D128EN)) {
+        return CP_ACCESS_TRAP_EL2;
+    }
+    if (el <= 2 && !(env->cp15.scr_el3 & SCR_D128EN)) {
+        return CP_ACCESS_TRAP_EL3;
+    }
+    return CP_ACCESS_OK;
+}
+
 static void dacr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
 {
     ARMCPU *cpu = env_archcpu(env);
@@ -3279,7 +3293,9 @@ static void define_par_register(ARMCPU *cpu)
         .name = "PAR_EL1", .state = ARM_CP_STATE_AA64,
         .opc0 = 3, .opc1 = 0, .crn = 7, .crm = 4, .opc2 = 0,
         .access = PL1_RW, .fgt = FGT_PAR_EL1,
-        .fieldoffset = offsetof(CPUARMState, cp15.par_el[1])
+        .type = ARM_CP_128BIT, .access128fn = access_d128,
+        .fieldoffset = offsetof(CPUARMState, cp15.par_el[1]),
+        .fieldoffsethi = offsetof(CPUARMState, cp15.par_el1_hi),
     };
 
     static ARMCPRegInfo par64_reginfo[2] = {
diff --git a/target/arm/tcg/cpregs-at.c b/target/arm/tcg/cpregs-at.c
index 0e8f229aa7..9e6af3d974 100644
--- a/target/arm/tcg/cpregs-at.c
+++ b/target/arm/tcg/cpregs-at.c
@@ -353,6 +353,7 @@ static void ats_write64(CPUARMState *env, const ARMCPRegInfo *ri,
 
     ss = for_el3 ? arm_security_space(env) : arm_security_space_below_el3(env);
     env->cp15.par_el[1] = do_ats_write(env, value, access_perm, mmu_idx, ss);
+    env->cp15.par_el1_hi = 0;
 }
 
 static CPAccessResult ats_access(CPUARMState *env, const ARMCPRegInfo *ri,
@@ -496,6 +497,7 @@ static void ats_s1e1a(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
     ARMSecuritySpace ss = arm_security_space_below_el3(env);
 
     env->cp15.par_el[1] = do_ats_write(env, value, 0, mmu_idx, ss);
+    env->cp15.par_el1_hi = 0;
 }
 
 static void ats_s1e2a(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
@@ -505,12 +507,14 @@ static void ats_s1e2a(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
     ARMSecuritySpace ss = arm_security_space_below_el3(env);
 
     env->cp15.par_el[1] = do_ats_write(env, value, 0, mmu_idx, ss);
+    env->cp15.par_el1_hi = 0;
 }
 
 static void ats_s1e3a(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
 {
     env->cp15.par_el[1] = do_ats_write(env, value, 0, ARMMMUIdx_E3,
                                        arm_security_space(env));
+    env->cp15.par_el1_hi = 0;
 }
 
 static const ARMCPRegInfo ats1a_reginfo[] = {
-- 
2.43.0
Re: [PATCH v2 21/37] target/arm: Extend PAR_EL1 to 128-bit
Posted by Peter Maydell 4 weeks ago
On Tue, 14 Oct 2025 at 21:19, Richard Henderson
<richard.henderson@linaro.org> wrote:
>
> Do not yet produce the 128-bit AT format result, but zero the
> high bits whenever the low bits are written.  This corresponds
> to PAR_EL1.D128 = 0, and bits [127:65] as RES0.
>
> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>


> diff --git a/target/arm/tcg/cpregs-at.c b/target/arm/tcg/cpregs-at.c
> index 0e8f229aa7..9e6af3d974 100644
> --- a/target/arm/tcg/cpregs-at.c
> +++ b/target/arm/tcg/cpregs-at.c
> @@ -353,6 +353,7 @@ static void ats_write64(CPUARMState *env, const ARMCPRegInfo *ri,
>
>      ss = for_el3 ? arm_security_space(env) : arm_security_space_below_el3(env);
>      env->cp15.par_el[1] = do_ats_write(env, value, access_perm, mmu_idx, ss);
> +    env->cp15.par_el1_hi = 0;
>  }
>
>  static CPAccessResult ats_access(CPUARMState *env, const ARMCPRegInfo *ri,
> @@ -496,6 +497,7 @@ static void ats_s1e1a(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
>      ARMSecuritySpace ss = arm_security_space_below_el3(env);
>
>      env->cp15.par_el[1] = do_ats_write(env, value, 0, mmu_idx, ss);
> +    env->cp15.par_el1_hi = 0;

I guess this is where the downside of not storing the
register as an Int128 shows up -- we have to remember
to explicitly clear the high half everywhere that we
have code that's doing an implicit-write to the 64-bit
version of the register.

thanks
-- PMM
Re: [PATCH v2 21/37] target/arm: Extend PAR_EL1 to 128-bit
Posted by Richard Henderson 3 weeks, 6 days ago
On 10/17/25 05:49, Peter Maydell wrote:
> On Tue, 14 Oct 2025 at 21:19, Richard Henderson
> <richard.henderson@linaro.org> wrote:
>>
>> Do not yet produce the 128-bit AT format result, but zero the
>> high bits whenever the low bits are written.  This corresponds
>> to PAR_EL1.D128 = 0, and bits [127:65] as RES0.
>>
>> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
> 
> 
>> diff --git a/target/arm/tcg/cpregs-at.c b/target/arm/tcg/cpregs-at.c
>> index 0e8f229aa7..9e6af3d974 100644
>> --- a/target/arm/tcg/cpregs-at.c
>> +++ b/target/arm/tcg/cpregs-at.c
>> @@ -353,6 +353,7 @@ static void ats_write64(CPUARMState *env, const ARMCPRegInfo *ri,
>>
>>       ss = for_el3 ? arm_security_space(env) : arm_security_space_below_el3(env);
>>       env->cp15.par_el[1] = do_ats_write(env, value, access_perm, mmu_idx, ss);
>> +    env->cp15.par_el1_hi = 0;
>>   }
>>
>>   static CPAccessResult ats_access(CPUARMState *env, const ARMCPRegInfo *ri,
>> @@ -496,6 +497,7 @@ static void ats_s1e1a(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
>>       ARMSecuritySpace ss = arm_security_space_below_el3(env);
>>
>>       env->cp15.par_el[1] = do_ats_write(env, value, 0, mmu_idx, ss);
>> +    env->cp15.par_el1_hi = 0;
> 
> I guess this is where the downside of not storing the
> register as an Int128 shows up -- we have to remember
> to explicitly clear the high half everywhere that we
> have code that's doing an implicit-write to the 64-bit
> version of the register.

Yes.  Thankfully, PAR_EL1 is the only 128-bit register with implicit writes.


r~