[PATCH] target/arm: Trap PMCR when MDCR_EL2.TPMCR is set

Smail AIDER via posted 1 patch 3 months ago
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/qemu tags/patchew/20250722131925.2119169-1-smail.aider@huawei.com
Maintainers: Peter Maydell <peter.maydell@linaro.org>
target/arm/cpregs-pmu.c | 24 ++++++++++++++++++++++--
1 file changed, 22 insertions(+), 2 deletions(-)
[PATCH] target/arm: Trap PMCR when MDCR_EL2.TPMCR is set
Posted by Smail AIDER via 3 months ago
Trap PMCR_EL0 or PMCR accesses to EL2 when MDCR_EL2.TPMCR is set.
Similar to MDCR_EL2.TPM, MDCR_EL2.TPMCR allows trapping EL0 and EL1
accesses to the PMCR register to EL2.

Signed-off-by: Smail AIDER <smail.aider@huawei.com>
---
 target/arm/cpregs-pmu.c | 24 ++++++++++++++++++++++--
 1 file changed, 22 insertions(+), 2 deletions(-)

diff --git a/target/arm/cpregs-pmu.c b/target/arm/cpregs-pmu.c
index 0f295b1376..3aacd4f652 100644
--- a/target/arm/cpregs-pmu.c
+++ b/target/arm/cpregs-pmu.c
@@ -252,6 +252,26 @@ static CPAccessResult pmreg_access(CPUARMState *env, const ARMCPRegInfo *ri,
     return CP_ACCESS_OK;
 }
 
+/*
+ * Check for traps to PMCR register, which are controlled by PMUSERENR.EN
+ * for EL1, MDCR_EL2.TPM/TPMCR for EL2 and MDCR_EL3.TPM for EL3.
+ */
+static CPAccessResult pmreg_access_pmcr(CPUARMState *env, const ARMCPRegInfo *ri,
+                                   bool isread)
+{
+    int el = arm_current_el(env);
+    uint64_t mdcr_el2 = arm_mdcr_el2_eff(env);
+
+    CPAccessResult ret = pmreg_access(env, ri, isread);
+
+    /* Check MDCR_TPMCR if not already trapped to EL1 */
+    if (ret != CP_ACCESS_TRAP_EL1 && el < 2 && (mdcr_el2 & MDCR_TPMCR)) {
+        ret = CP_ACCESS_TRAP_EL2;
+    }
+
+    return ret;
+}
+
 static CPAccessResult pmreg_access_xevcntr(CPUARMState *env,
                                            const ARMCPRegInfo *ri,
                                            bool isread)
@@ -1192,14 +1212,14 @@ void define_pm_cpregs(ARMCPU *cpu)
             .fgt = FGT_PMCR_EL0,
             .type = ARM_CP_IO | ARM_CP_ALIAS,
             .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmcr),
-            .accessfn = pmreg_access,
+            .accessfn = pmreg_access_pmcr,
             .readfn = pmcr_read, .raw_readfn = raw_read,
             .writefn = pmcr_write, .raw_writefn = raw_write,
         };
         const ARMCPRegInfo pmcr64 = {
             .name = "PMCR_EL0", .state = ARM_CP_STATE_AA64,
             .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 0,
-            .access = PL0_RW, .accessfn = pmreg_access,
+            .access = PL0_RW, .accessfn = pmreg_access_pmcr,
             .fgt = FGT_PMCR_EL0,
             .type = ARM_CP_IO,
             .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcr),
-- 
2.34.1
Re: [PATCH] target/arm: Trap PMCR when MDCR_EL2.TPMCR is set
Posted by Michael Tokarev 2 months, 2 weeks ago
On 22.07.2025 16:19, Smail AIDER via wrote:
> Trap PMCR_EL0 or PMCR accesses to EL2 when MDCR_EL2.TPMCR is set.
> Similar to MDCR_EL2.TPM, MDCR_EL2.TPMCR allows trapping EL0 and EL1
> accesses to the PMCR register to EL2.

Ping?

Has this patch been forgotten?

Thanks,

/mjt
Re: [PATCH] target/arm: Trap PMCR when MDCR_EL2.TPMCR is set
Posted by Peter Maydell 2 months, 2 weeks ago
On Sun, 10 Aug 2025 at 08:57, Michael Tokarev <mjt@tls.msk.ru> wrote:
>
> On 22.07.2025 16:19, Smail AIDER via wrote:
> > Trap PMCR_EL0 or PMCR accesses to EL2 when MDCR_EL2.TPMCR is set.
> > Similar to MDCR_EL2.TPM, MDCR_EL2.TPMCR allows trapping EL0 and EL1
> > accesses to the PMCR register to EL2.
>
> Ping?
>
> Has this patch been forgotten?

This is v1, it got code review comments, and there is a v2 on list.

The v2 on my list but it's passed the "worth putting into 10.1"
point already and hasn't yet been reviewed. I was assuming RTH would
review as he looked at v1.

-- PMM
Re: [PATCH] target/arm: Trap PMCR when MDCR_EL2.TPMCR is set
Posted by Richard Henderson 3 months ago
On 7/22/25 06:19, Smail AIDER via wrote:
> Trap PMCR_EL0 or PMCR accesses to EL2 when MDCR_EL2.TPMCR is set.
> Similar to MDCR_EL2.TPM, MDCR_EL2.TPMCR allows trapping EL0 and EL1
> accesses to the PMCR register to EL2.
> 
> Signed-off-by: Smail AIDER <smail.aider@huawei.com>
> ---
>   target/arm/cpregs-pmu.c | 24 ++++++++++++++++++++++--
>   1 file changed, 22 insertions(+), 2 deletions(-)
> 
> diff --git a/target/arm/cpregs-pmu.c b/target/arm/cpregs-pmu.c
> index 0f295b1376..3aacd4f652 100644
> --- a/target/arm/cpregs-pmu.c
> +++ b/target/arm/cpregs-pmu.c
> @@ -252,6 +252,26 @@ static CPAccessResult pmreg_access(CPUARMState *env, const ARMCPRegInfo *ri,
>       return CP_ACCESS_OK;
>   }
>   
> +/*
> + * Check for traps to PMCR register, which are controlled by PMUSERENR.EN
> + * for EL1, MDCR_EL2.TPM/TPMCR for EL2 and MDCR_EL3.TPM for EL3.
> + */
> +static CPAccessResult pmreg_access_pmcr(CPUARMState *env, const ARMCPRegInfo *ri,
> +                                   bool isread)
> +{
> +    int el = arm_current_el(env);
> +    uint64_t mdcr_el2 = arm_mdcr_el2_eff(env);
> +

This could be improved by not computing these until they're needed.

> +    CPAccessResult ret = pmreg_access(env, ri, isread);
> +
> +    /* Check MDCR_TPMCR if not already trapped to EL1 */
> +    if (ret != CP_ACCESS_TRAP_EL1 && el < 2 && (mdcr_el2 & MDCR_TPMCR)) {
> +        ret = CP_ACCESS_TRAP_EL2;
> +    }

Or by creating a common helper for pmreg_access and pmreg_access_pmcr such that those 
values only need computing once within the common helper.  That could make the code more 
obvious as well.

E.g.

static CPAccessResult do_pmreg_access(CPUARMState *env, bool with_tpmcr)
{
     int el = arm_current_el(env);

     if (el == 0 && !(env->cp15.c9_pmuserenr & 1)) {
         return CP_ACCESS_TRAP_EL1;
     }

     if (el < 2) {
         uint64_t mdcr_el2 = arm_mdcr_el2_eff(env);

         if (mdcr_el2 & MDCR_TPM) {
             return CP_ACCESS_TRAP_EL2;
         }
         if (with_tpmcr && (mdcr_el2 & MDCR_TPMCR)) {
             return CP_ACCESS_TRAP_EL2;
         }
     }

     if (el < 3 && (env->cp15.mdcr_el3 & MDCR_TPM)) {
         return CP_ACCESS_TRAP_EL3;
     }
     return CP_ACCESS_OK;
}

static CPAccessResult pmreg_access(CPUARMState *env, const ARMCPRegInfo *ri,
                                    bool isread)
{
     return do_pmreg_access(env, false);
}

static CPAccessResult pmreg_access_pmcr(CPUARMState *env, const ARMCPRegInfo *ri,
                                         bool isread)
{
     return do_pmreg_access(env, true);
}


r~