diff mbox

[v2,12/35] target-arm: Convert performance monitor reginfo to accesfn

Message ID 1391183143-30724-13-git-send-email-peter.maydell@linaro.org
State New
Headers show

Commit Message

Peter Maydell Jan. 31, 2014, 3:45 p.m. UTC
Convert the performance monitor reginfo definitions to use
an accessfn rather than returning EXCP_UDEF from read and
write functions. This also allows us to fix a couple of XXX
cases where we weren't imposing the access restrictions on
RAZ/WI or constant registers.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 target-arm/helper.c | 70 +++++++++++++++++++++--------------------------------
 1 file changed, 28 insertions(+), 42 deletions(-)

Comments

Peter Crosthwaite Feb. 5, 2014, 6:59 a.m. UTC | #1
cc Alistair, this may conflict with his timer work.

On Sat, Feb 1, 2014 at 1:45 AM, Peter Maydell <peter.maydell@linaro.org> wrote:
> Convert the performance monitor reginfo definitions to use
> an accessfn rather than returning EXCP_UDEF from read and
> write functions. This also allows us to fix a couple of XXX
> cases where we weren't imposing the access restrictions on
> RAZ/WI or constant registers.
>
> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
> ---
>  target-arm/helper.c | 70 +++++++++++++++++++++--------------------------------
>  1 file changed, 28 insertions(+), 42 deletions(-)
>
> diff --git a/target-arm/helper.c b/target-arm/helper.c
> index bd78350..9adaeb9 100644
> --- a/target-arm/helper.c
> +++ b/target-arm/helper.c
> @@ -485,26 +485,20 @@ static const ARMCPRegInfo v6_cp_reginfo[] = {
>      REGINFO_SENTINEL
>  };
>
> -
> -static int pmreg_read(CPUARMState *env, const ARMCPRegInfo *ri,
> -                      uint64_t *value)
> +static CPAccessResult pmreg_access(CPUARMState *env, const ARMCPRegInfo *ri)
>  {
> -    /* Generic performance monitor register read function for where
> -     * user access may be allowed by PMUSERENR.
> +    /* Perfomance monitor registers user accessibility is controlled
> +     * by PMUSERENR.
>       */
>      if (arm_current_pl(env) == 0 && !env->cp15.c9_pmuserenr) {
> -        return EXCP_UDEF;
> +        return CP_ACCESS_TRAP;
>      }
> -    *value = CPREG_FIELD32(env, ri);
> -    return 0;
> +    return CP_ACCESS_OK;
>  }
>
>  static int pmcr_write(CPUARMState *env, const ARMCPRegInfo *ri,
>                        uint64_t value)
>  {
> -    if (arm_current_pl(env) == 0 && !env->cp15.c9_pmuserenr) {
> -        return EXCP_UDEF;
> -    }
>      /* only the DP, X, D and E bits are writable */
>      env->cp15.c9_pmcr &= ~0x39;
>      env->cp15.c9_pmcr |= (value & 0x39);
> @@ -514,9 +508,6 @@ static int pmcr_write(CPUARMState *env, const ARMCPRegInfo *ri,
>  static int pmcntenset_write(CPUARMState *env, const ARMCPRegInfo *ri,
>                              uint64_t value)
>  {
> -    if (arm_current_pl(env) == 0 && !env->cp15.c9_pmuserenr) {
> -        return EXCP_UDEF;
> -    }
>      value &= (1 << 31);
>      env->cp15.c9_pmcnten |= value;
>      return 0;
> @@ -525,9 +516,6 @@ static int pmcntenset_write(CPUARMState *env, const ARMCPRegInfo *ri,
>  static int pmcntenclr_write(CPUARMState *env, const ARMCPRegInfo *ri,
>                              uint64_t value)
>  {
> -    if (arm_current_pl(env) == 0 && !env->cp15.c9_pmuserenr) {
> -        return EXCP_UDEF;
> -    }
>      value &= (1 << 31);
>      env->cp15.c9_pmcnten &= ~value;
>      return 0;
> @@ -536,9 +524,6 @@ static int pmcntenclr_write(CPUARMState *env, const ARMCPRegInfo *ri,
>  static int pmovsr_write(CPUARMState *env, const ARMCPRegInfo *ri,
>                          uint64_t value)
>  {
> -    if (arm_current_pl(env) == 0 && !env->cp15.c9_pmuserenr) {
> -        return EXCP_UDEF;
> -    }
>      env->cp15.c9_pmovsr &= ~value;
>      return 0;
>  }
> @@ -546,9 +531,6 @@ static int pmovsr_write(CPUARMState *env, const ARMCPRegInfo *ri,
>  static int pmxevtyper_write(CPUARMState *env, const ARMCPRegInfo *ri,
>                              uint64_t value)
>  {
> -    if (arm_current_pl(env) == 0 && !env->cp15.c9_pmuserenr) {
> -        return EXCP_UDEF;
> -    }
>      env->cp15.c9_pmxevtyper = value & 0xff;
>      return 0;
>  }
> @@ -624,37 +606,41 @@ static const ARMCPRegInfo v7_cp_reginfo[] = {
>      { .name = "PMCNTENSET", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 1,
>        .access = PL0_RW, .resetvalue = 0,
>        .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten),
> -      .readfn = pmreg_read, .writefn = pmcntenset_write,
> -      .raw_readfn = raw_read, .raw_writefn = raw_write },
> +      .writefn = pmcntenset_write,
> +      .accessfn = pmreg_access,
> +      .raw_writefn = raw_write },

A nit but,

You're field ordering scheme is inconsistent, here you go, write ->
access -> raw_write ....

>      { .name = "PMCNTENCLR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 2,
>        .access = PL0_RW, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten),
> -      .readfn = pmreg_read, .writefn = pmcntenclr_write,
> +      .accessfn = pmreg_access,
> +      .writefn = pmcntenclr_write,
>        .type = ARM_CP_NO_MIGRATE },
>      { .name = "PMOVSR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 3,
>        .access = PL0_RW, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmovsr),
> -      .readfn = pmreg_read, .writefn = pmovsr_write,
> -      .raw_readfn = raw_read, .raw_writefn = raw_write },
> -    /* Unimplemented so WI. Strictly speaking write accesses in PL0 should
> -     * respect PMUSERENR.
> -     */
> +      .accessfn = pmreg_access,
> +      .writefn = pmovsr_write,
> +      .raw_writefn = raw_write },

... and this is access -> write -> raw_write. Is there a prescribed
order to keep new (or gradually refactored) code consistent?

Regards,
Peter

> +    /* Unimplemented so WI. */
>      { .name = "PMSWINC", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 4,
> -      .access = PL0_W, .type = ARM_CP_NOP },
> +      .access = PL0_W, .accessfn = pmreg_access, .type = ARM_CP_NOP },
>      /* Since we don't implement any events, writing to PMSELR is UNPREDICTABLE.
> -     * We choose to RAZ/WI. XXX should respect PMUSERENR.
> +     * We choose to RAZ/WI.
>       */
>      { .name = "PMSELR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 5,
> -      .access = PL0_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
> -    /* Unimplemented, RAZ/WI. XXX PMUSERENR */
> +      .access = PL0_RW, .type = ARM_CP_CONST, .resetvalue = 0,
> +      .accessfn = pmreg_access },
> +    /* Unimplemented, RAZ/WI. */
>      { .name = "PMCCNTR", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 0,
> -      .access = PL0_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
> +      .access = PL0_RW, .type = ARM_CP_CONST, .resetvalue = 0,
> +      .accessfn = pmreg_access },
>      { .name = "PMXEVTYPER", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 1,
>        .access = PL0_RW,
>        .fieldoffset = offsetof(CPUARMState, cp15.c9_pmxevtyper),
> -      .readfn = pmreg_read, .writefn = pmxevtyper_write,
> -      .raw_readfn = raw_read, .raw_writefn = raw_write },
> -    /* Unimplemented, RAZ/WI. XXX PMUSERENR */
> +      .accessfn = pmreg_access, .writefn = pmxevtyper_write,
> +      .raw_writefn = raw_write },
> +    /* Unimplemented, RAZ/WI. */
>      { .name = "PMXEVCNTR", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 2,
> -      .access = PL0_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
> +      .access = PL0_RW, .type = ARM_CP_CONST, .resetvalue = 0,
> +      .accessfn = pmreg_access },
>      { .name = "PMUSERENR", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 0,
>        .access = PL0_R | PL1_RW,
>        .fieldoffset = offsetof(CPUARMState, cp15.c9_pmuserenr),
> @@ -1708,8 +1694,8 @@ void register_cp_regs_for_features(ARMCPU *cpu)
>              .name = "PMCR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 0,
>              .access = PL0_RW, .resetvalue = cpu->midr & 0xff000000,
>              .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcr),
> -            .readfn = pmreg_read, .writefn = pmcr_write,
> -            .raw_readfn = raw_read, .raw_writefn = raw_write,
> +            .accessfn = pmreg_access, .writefn = pmcr_write,
> +            .raw_writefn = raw_write,
>          };
>          ARMCPRegInfo clidr = {
>              .name = "CLIDR", .cp = 15, .crn = 0, .crm = 0, .opc1 = 1, .opc2 = 1,
> --
> 1.8.5
>
>
Peter Maydell Feb. 5, 2014, 11:01 a.m. UTC | #2
On 5 February 2014 06:59, Peter Crosthwaite
<peter.crosthwaite@xilinx.com> wrote:
> cc Alistair, this may conflict with his timer work.
>
> On Sat, Feb 1, 2014 at 1:45 AM, Peter Maydell <peter.maydell@linaro.org> wrote:
>> @@ -624,37 +606,41 @@ static const ARMCPRegInfo v7_cp_reginfo[] = {
>>      { .name = "PMCNTENSET", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 1,
>>        .access = PL0_RW, .resetvalue = 0,
>>        .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten),
>> -      .readfn = pmreg_read, .writefn = pmcntenset_write,
>> -      .raw_readfn = raw_read, .raw_writefn = raw_write },
>> +      .writefn = pmcntenset_write,
>> +      .accessfn = pmreg_access,
>> +      .raw_writefn = raw_write },
>
> A nit but,
>
> You're field ordering scheme is inconsistent, here you go, write ->
> access -> raw_write ....
>
>>      { .name = "PMCNTENCLR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 2,
>>        .access = PL0_RW, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten),
>> -      .readfn = pmreg_read, .writefn = pmcntenclr_write,
>> +      .accessfn = pmreg_access,
>> +      .writefn = pmcntenclr_write,
>>        .type = ARM_CP_NO_MIGRATE },
>>      { .name = "PMOVSR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 3,
>>        .access = PL0_RW, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmovsr),
>> -      .readfn = pmreg_read, .writefn = pmovsr_write,
>> -      .raw_readfn = raw_read, .raw_writefn = raw_write },
>> -    /* Unimplemented so WI. Strictly speaking write accesses in PL0 should
>> -     * respect PMUSERENR.
>> -     */
>> +      .accessfn = pmreg_access,
>> +      .writefn = pmovsr_write,
>> +      .raw_writefn = raw_write },
>
> ... and this is access -> write -> raw_write. Is there a prescribed
> order to keep new (or gradually refactored) code consistent?

No, I've just been going for "whatever looks like it fits reasonably
neatly onto not too many lines and is a fairly minimal change to existing
structs", mostly. The thing I have been trying to be consistent with is
the order for crn/crm/opc1/opc2 fields, which for new registers I've been
making be op0/op1/crn/crm/op2, because that's the order the ARM ARM
seems to have settled on. Unfortunately a lot of our existing definitions
are crn/crm/op1/op2, because at the time there was variance in the
ARM docs and that order seemed sensible to me.

For the other fields, I think "name first; type/state/encoding second;
access related fields; read and write accessors; reset stuff" is probably
not a bad order. But the nice thing about having named fields is it
doesn't actually matter what order things go in.

thanks
-- PMM
Alistair Francis Feb. 6, 2014, 12:05 a.m. UTC | #3
On Wed, Feb 5, 2014 at 9:01 PM, Peter Maydell <peter.maydell@linaro.org> wrote:
> On 5 February 2014 06:59, Peter Crosthwaite
> <peter.crosthwaite@xilinx.com> wrote:
>> cc Alistair, this may conflict with his timer work.

It seems fine to me. None of Peter's code changes conflict with mine,
except for adding the ".accessfn = pmreg_access," to the PMCCNTR
register. My patch never implemented the return EXCP_UDEF; so no
action is needed there

>>
>> On Sat, Feb 1, 2014 at 1:45 AM, Peter Maydell <peter.maydell@linaro.org> wrote:
>>> @@ -624,37 +606,41 @@ static const ARMCPRegInfo v7_cp_reginfo[] = {
>>>      { .name = "PMCNTENSET", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 1,
>>>        .access = PL0_RW, .resetvalue = 0,
>>>        .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten),
>>> -      .readfn = pmreg_read, .writefn = pmcntenset_write,
>>> -      .raw_readfn = raw_read, .raw_writefn = raw_write },
>>> +      .writefn = pmcntenset_write,
>>> +      .accessfn = pmreg_access,
>>> +      .raw_writefn = raw_write },
>>
>> A nit but,
>>
>> You're field ordering scheme is inconsistent, here you go, write ->
>> access -> raw_write ....
>>
>>>      { .name = "PMCNTENCLR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 2,
>>>        .access = PL0_RW, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten),
>>> -      .readfn = pmreg_read, .writefn = pmcntenclr_write,
>>> +      .accessfn = pmreg_access,
>>> +      .writefn = pmcntenclr_write,
>>>        .type = ARM_CP_NO_MIGRATE },
>>>      { .name = "PMOVSR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 3,
>>>        .access = PL0_RW, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmovsr),
>>> -      .readfn = pmreg_read, .writefn = pmovsr_write,
>>> -      .raw_readfn = raw_read, .raw_writefn = raw_write },
>>> -    /* Unimplemented so WI. Strictly speaking write accesses in PL0 should
>>> -     * respect PMUSERENR.
>>> -     */
>>> +      .accessfn = pmreg_access,
>>> +      .writefn = pmovsr_write,
>>> +      .raw_writefn = raw_write },
>>
>> ... and this is access -> write -> raw_write. Is there a prescribed
>> order to keep new (or gradually refactored) code consistent?
>
> No, I've just been going for "whatever looks like it fits reasonably
> neatly onto not too many lines and is a fairly minimal change to existing
> structs", mostly. The thing I have been trying to be consistent with is
> the order for crn/crm/opc1/opc2 fields, which for new registers I've been
> making be op0/op1/crn/crm/op2, because that's the order the ARM ARM
> seems to have settled on. Unfortunately a lot of our existing definitions
> are crn/crm/op1/op2, because at the time there was variance in the
> ARM docs and that order seemed sensible to me.
>
> For the other fields, I think "name first; type/state/encoding second;
> access related fields; read and write accessors; reset stuff" is probably
> not a bad order. But the nice thing about having named fields is it
> doesn't actually matter what order things go in.
>
> thanks
> -- PMM
>
Peter Crosthwaite Feb. 9, 2014, 2:59 a.m. UTC | #4
On Wed, Feb 5, 2014 at 9:01 PM, Peter Maydell <peter.maydell@linaro.org> wrote:
> On 5 February 2014 06:59, Peter Crosthwaite
> <peter.crosthwaite@xilinx.com> wrote:
>> cc Alistair, this may conflict with his timer work.
>>
>> On Sat, Feb 1, 2014 at 1:45 AM, Peter Maydell <peter.maydell@linaro.org> wrote:
>>> @@ -624,37 +606,41 @@ static const ARMCPRegInfo v7_cp_reginfo[] = {
>>>      { .name = "PMCNTENSET", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 1,
>>>        .access = PL0_RW, .resetvalue = 0,
>>>        .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten),
>>> -      .readfn = pmreg_read, .writefn = pmcntenset_write,
>>> -      .raw_readfn = raw_read, .raw_writefn = raw_write },
>>> +      .writefn = pmcntenset_write,
>>> +      .accessfn = pmreg_access,
>>> +      .raw_writefn = raw_write },
>>
>> A nit but,
>>
>> You're field ordering scheme is inconsistent, here you go, write ->
>> access -> raw_write ....
>>
>>>      { .name = "PMCNTENCLR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 2,
>>>        .access = PL0_RW, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten),
>>> -      .readfn = pmreg_read, .writefn = pmcntenclr_write,
>>> +      .accessfn = pmreg_access,
>>> +      .writefn = pmcntenclr_write,
>>>        .type = ARM_CP_NO_MIGRATE },
>>>      { .name = "PMOVSR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 3,
>>>        .access = PL0_RW, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmovsr),
>>> -      .readfn = pmreg_read, .writefn = pmovsr_write,
>>> -      .raw_readfn = raw_read, .raw_writefn = raw_write },
>>> -    /* Unimplemented so WI. Strictly speaking write accesses in PL0 should
>>> -     * respect PMUSERENR.
>>> -     */
>>> +      .accessfn = pmreg_access,
>>> +      .writefn = pmovsr_write,
>>> +      .raw_writefn = raw_write },
>>
>> ... and this is access -> write -> raw_write. Is there a prescribed
>> order to keep new (or gradually refactored) code consistent?
>
> No, I've just been going for "whatever looks like it fits reasonably
> neatly onto not too many lines and is a fairly minimal change to existing
> structs", mostly.

I agree that we should minimise diff. Existing code trumps any new
rules we come up with here. But I picked on this one because its
inconsistent just within the new changes.

The thing I have been trying to be consistent with is
> the order for crn/crm/opc1/opc2 fields, which for new registers I've been
> making be op0/op1/crn/crm/op2, because that's the order the ARM ARM
> seems to have settled on. Unfortunately a lot of our existing definitions
> are crn/crm/op1/op2, because at the time there was variance in the
> ARM docs and that order seemed sensible to me.
>
> For the other fields, I think "name first; type/state/encoding second;
> access related fields; read and write accessors; reset stuff" is probably
> not a bad order.

Agreed. With diff-correction based on this scheme:

Reviewed-by: Peter Crosthwaite <peter.crosthwaite@xilinx.com>

 But the nice thing about having named fields is it
> doesn't actually matter what order things go in.
>

But for similar entries its much more readable if they are self-consistent.

Regards,
Peter

> thanks
> -- PMM
>
Peter Maydell Feb. 9, 2014, 12:04 p.m. UTC | #5
On 9 February 2014 02:59, Peter Crosthwaite
<peter.crosthwaite@xilinx.com> wrote:

> Agreed. With diff-correction based on this scheme:
>
> Reviewed-by: Peter Crosthwaite <peter.crosthwaite@xilinx.com>
>
>  But the nice thing about having named fields is it
>> doesn't actually matter what order things go in.
>>
>
> But for similar entries its much more readable if they are self-consistent.

Yes; this patch in particular was rather inconsistent and I
have fixed it.

thanks
-- PMM
diff mbox

Patch

diff --git a/target-arm/helper.c b/target-arm/helper.c
index bd78350..9adaeb9 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -485,26 +485,20 @@  static const ARMCPRegInfo v6_cp_reginfo[] = {
     REGINFO_SENTINEL
 };
 
-
-static int pmreg_read(CPUARMState *env, const ARMCPRegInfo *ri,
-                      uint64_t *value)
+static CPAccessResult pmreg_access(CPUARMState *env, const ARMCPRegInfo *ri)
 {
-    /* Generic performance monitor register read function for where
-     * user access may be allowed by PMUSERENR.
+    /* Perfomance monitor registers user accessibility is controlled
+     * by PMUSERENR.
      */
     if (arm_current_pl(env) == 0 && !env->cp15.c9_pmuserenr) {
-        return EXCP_UDEF;
+        return CP_ACCESS_TRAP;
     }
-    *value = CPREG_FIELD32(env, ri);
-    return 0;
+    return CP_ACCESS_OK;
 }
 
 static int pmcr_write(CPUARMState *env, const ARMCPRegInfo *ri,
                       uint64_t value)
 {
-    if (arm_current_pl(env) == 0 && !env->cp15.c9_pmuserenr) {
-        return EXCP_UDEF;
-    }
     /* only the DP, X, D and E bits are writable */
     env->cp15.c9_pmcr &= ~0x39;
     env->cp15.c9_pmcr |= (value & 0x39);
@@ -514,9 +508,6 @@  static int pmcr_write(CPUARMState *env, const ARMCPRegInfo *ri,
 static int pmcntenset_write(CPUARMState *env, const ARMCPRegInfo *ri,
                             uint64_t value)
 {
-    if (arm_current_pl(env) == 0 && !env->cp15.c9_pmuserenr) {
-        return EXCP_UDEF;
-    }
     value &= (1 << 31);
     env->cp15.c9_pmcnten |= value;
     return 0;
@@ -525,9 +516,6 @@  static int pmcntenset_write(CPUARMState *env, const ARMCPRegInfo *ri,
 static int pmcntenclr_write(CPUARMState *env, const ARMCPRegInfo *ri,
                             uint64_t value)
 {
-    if (arm_current_pl(env) == 0 && !env->cp15.c9_pmuserenr) {
-        return EXCP_UDEF;
-    }
     value &= (1 << 31);
     env->cp15.c9_pmcnten &= ~value;
     return 0;
@@ -536,9 +524,6 @@  static int pmcntenclr_write(CPUARMState *env, const ARMCPRegInfo *ri,
 static int pmovsr_write(CPUARMState *env, const ARMCPRegInfo *ri,
                         uint64_t value)
 {
-    if (arm_current_pl(env) == 0 && !env->cp15.c9_pmuserenr) {
-        return EXCP_UDEF;
-    }
     env->cp15.c9_pmovsr &= ~value;
     return 0;
 }
@@ -546,9 +531,6 @@  static int pmovsr_write(CPUARMState *env, const ARMCPRegInfo *ri,
 static int pmxevtyper_write(CPUARMState *env, const ARMCPRegInfo *ri,
                             uint64_t value)
 {
-    if (arm_current_pl(env) == 0 && !env->cp15.c9_pmuserenr) {
-        return EXCP_UDEF;
-    }
     env->cp15.c9_pmxevtyper = value & 0xff;
     return 0;
 }
@@ -624,37 +606,41 @@  static const ARMCPRegInfo v7_cp_reginfo[] = {
     { .name = "PMCNTENSET", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 1,
       .access = PL0_RW, .resetvalue = 0,
       .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten),
-      .readfn = pmreg_read, .writefn = pmcntenset_write,
-      .raw_readfn = raw_read, .raw_writefn = raw_write },
+      .writefn = pmcntenset_write,
+      .accessfn = pmreg_access,
+      .raw_writefn = raw_write },
     { .name = "PMCNTENCLR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 2,
       .access = PL0_RW, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten),
-      .readfn = pmreg_read, .writefn = pmcntenclr_write,
+      .accessfn = pmreg_access,
+      .writefn = pmcntenclr_write,
       .type = ARM_CP_NO_MIGRATE },
     { .name = "PMOVSR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 3,
       .access = PL0_RW, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmovsr),
-      .readfn = pmreg_read, .writefn = pmovsr_write,
-      .raw_readfn = raw_read, .raw_writefn = raw_write },
-    /* Unimplemented so WI. Strictly speaking write accesses in PL0 should
-     * respect PMUSERENR.
-     */
+      .accessfn = pmreg_access,
+      .writefn = pmovsr_write,
+      .raw_writefn = raw_write },
+    /* Unimplemented so WI. */
     { .name = "PMSWINC", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 4,
-      .access = PL0_W, .type = ARM_CP_NOP },
+      .access = PL0_W, .accessfn = pmreg_access, .type = ARM_CP_NOP },
     /* Since we don't implement any events, writing to PMSELR is UNPREDICTABLE.
-     * We choose to RAZ/WI. XXX should respect PMUSERENR.
+     * We choose to RAZ/WI.
      */
     { .name = "PMSELR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 5,
-      .access = PL0_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
-    /* Unimplemented, RAZ/WI. XXX PMUSERENR */
+      .access = PL0_RW, .type = ARM_CP_CONST, .resetvalue = 0,
+      .accessfn = pmreg_access },
+    /* Unimplemented, RAZ/WI. */
     { .name = "PMCCNTR", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 0,
-      .access = PL0_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
+      .access = PL0_RW, .type = ARM_CP_CONST, .resetvalue = 0,
+      .accessfn = pmreg_access },
     { .name = "PMXEVTYPER", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 1,
       .access = PL0_RW,
       .fieldoffset = offsetof(CPUARMState, cp15.c9_pmxevtyper),
-      .readfn = pmreg_read, .writefn = pmxevtyper_write,
-      .raw_readfn = raw_read, .raw_writefn = raw_write },
-    /* Unimplemented, RAZ/WI. XXX PMUSERENR */
+      .accessfn = pmreg_access, .writefn = pmxevtyper_write,
+      .raw_writefn = raw_write },
+    /* Unimplemented, RAZ/WI. */
     { .name = "PMXEVCNTR", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 2,
-      .access = PL0_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
+      .access = PL0_RW, .type = ARM_CP_CONST, .resetvalue = 0,
+      .accessfn = pmreg_access },
     { .name = "PMUSERENR", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 0,
       .access = PL0_R | PL1_RW,
       .fieldoffset = offsetof(CPUARMState, cp15.c9_pmuserenr),
@@ -1708,8 +1694,8 @@  void register_cp_regs_for_features(ARMCPU *cpu)
             .name = "PMCR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 0,
             .access = PL0_RW, .resetvalue = cpu->midr & 0xff000000,
             .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcr),
-            .readfn = pmreg_read, .writefn = pmcr_write,
-            .raw_readfn = raw_read, .raw_writefn = raw_write,
+            .accessfn = pmreg_access, .writefn = pmcr_write,
+            .raw_writefn = raw_write,
         };
         ARMCPRegInfo clidr = {
             .name = "CLIDR", .cp = 15, .crn = 0, .crm = 0, .opc1 = 1, .opc2 = 1,