diff mbox series

[RFCv2,02/20] arm/cpu: Add sysreg definitions in cpu-sysregs.h

Message ID 20241206112213.88394-3-cohuck@redhat.com (mailing list archive)
State New
Headers show
Series kvm/arm: Introduce a customizable aarch64 KVM host model | expand

Commit Message

Cornelia Huck Dec. 6, 2024, 11:21 a.m. UTC
From: Eric Auger <eric.auger@redhat.com>

This new header contains macros that define aarch64 registers.
In a subsequent patch, this will be replaced by a more exhaustive
version that will be generated from linux arch/arm64/tools/sysreg
file. Those macros are sufficient to migrate the storage of those
ID regs from named fields in isar struct to an array cell.

Signed-off-by: Eric Auger <eric.auger@redhat.com>
Signed-off-by: Cornelia Huck <cohuck@redhat.com>
---
 target/arm/cpu-sysregs.h | 42 +++++++++++++++++++++++++++++++
 target/arm/cpu.h         | 54 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 96 insertions(+)
 create mode 100644 target/arm/cpu-sysregs.h

Comments

Richard Henderson Dec. 12, 2024, 2:37 p.m. UTC | #1
On 12/6/24 05:21, Cornelia Huck wrote:
> +#define SYS_ID_AA64PFR0_EL1                             sys_reg(3, 0, 0, 4, 0)
...
> +typedef struct ARMSysReg {
> +    int op0;
> +    int op1;
> +    int crn;
> +    int crm;
> +    int op2;
> +} ARMSysReg;
...
> +static inline ARMSysReg sys_reg(int op0, int op1, int crn, int crm, int op2)
> +{
> +        ARMSysReg sr = {op0, op1, crn, crm, op2};
> +
> +        return sr;
> +}

Not a fan.  Why take 20 bytes to represent these?

Our existing ENCODE_CP_REG and ENCODE_AA64_CP_REG macros seem much better, even if the 
argument ordering doesn't match the column ordering in Table D22-2.

> @@ -841,6 +849,51 @@ typedef struct IdRegMap {
>       uint64_t regs[NR_ID_REGS];
>   } IdRegMap;
>   
> +#define ARM_FEATURE_ID_RANGE_IDX(op0, op1, crn, crm, op2)               \
> +        ({                                                              \
> +                __u64 __op1 = (op1) & 3;                                \
> +                __op1 -= (__op1 == 3);                                  \
> +                (__op1 << 6 | ((crm) & 7) << 3 | (op2));                \
> +        })

Ah, well, this answers my question re patch 1.

It seems a shame to use 128 slots to represent all 9 id registers in the op1={1,3} space.

Do we really need anything beyond the defined registers, or even the defined registers for 
which qemu knows how to do anything?

I'm certainly happy to replace ARMISARegisters fields with an array, but more like

enum ARMIDRegisterIdx {
     ID_AA64ISAR0_IDX,
     etc
     ordering arbitrary, either machine or macro generated,
     but every register has a symbolic index.
     NUM_ID_IDX,
};

enum ARMSysregs {
     SYS_ID_AA64PFR0_EL1 = ENCODE_AA64_CP_REG(...),
     etc
};

const uint32_t id_register_sysreg[NUM_ID_IDX] = {
     [ID_AA64ISAR0_IDX] = SYS_ID_AA64PFR0_EL1,
     etc
};

struct ARMISARegisters {
     uint64_t id[NUM_ID_IDX];
};

This seems trivial to automate, and wastes no space.


r~
Eric Auger Dec. 12, 2024, 5:46 p.m. UTC | #2
Hi Richard,
On 12/12/24 15:37, Richard Henderson wrote:
> On 12/6/24 05:21, Cornelia Huck wrote:
>> +#define SYS_ID_AA64PFR0_EL1                             sys_reg(3,
>> 0, 0, 4, 0)
> ...
>> +typedef struct ARMSysReg {
>> +    int op0;
>> +    int op1;
>> +    int crn;
>> +    int crm;
>> +    int op2;
>> +} ARMSysReg;
> ...
>> +static inline ARMSysReg sys_reg(int op0, int op1, int crn, int crm,
>> int op2)
>> +{
>> +        ARMSysReg sr = {op0, op1, crn, crm, op2};
>> +
>> +        return sr;
>> +}
>
> Not a fan.  Why take 20 bytes to represent these?
sure we can optimize it
>
> Our existing ENCODE_CP_REG and ENCODE_AA64_CP_REG macros seem much
> better, even if the argument ordering doesn't match the column
> ordering in Table D22-2.
>
>> @@ -841,6 +849,51 @@ typedef struct IdRegMap {
>>       uint64_t regs[NR_ID_REGS];
>>   } IdRegMap;
>>   +#define ARM_FEATURE_ID_RANGE_IDX(op0, op1, crn, crm,
>> op2)               \
>> +       
>> ({                                                              \
>> +                __u64 __op1 = (op1) &
>> 3;                                \
>> +                __op1 -= (__op1 ==
>> 3);                                  \
>> +                (__op1 << 6 | ((crm) & 7) << 3 |
>> (op2));                \
>> +        })
>
> Ah, well, this answers my question re patch 1.
>
> It seems a shame to use 128 slots to represent all 9 id registers in
> the op1={1,3} space.
wouldn't it make sense to use a hashtable then as we don't have
consecutive indexes?
>
> Do we really need anything beyond the defined registers, or even the
> defined registers for which qemu knows how to do anything?
what do you mean by "defined registers". The end goal is to be able to
tune any id reg that the kernel allows to write. So I guess we shall
encompass more regs than qemu currently handles.

Wrt op1={1,3}, tbh I initially sticked to the KVM API. Now looking at
D22-2, effectively we have very few ID regs there. If we were to use a
hashtable we may be more flexible in picking up the indexes that are
relevant for us.
>
> I'm certainly happy to replace ARMISARegisters fields with an array,
> but more like
>
> enum ARMIDRegisterIdx {
>     ID_AA64ISAR0_IDX,
>     etc
>     ordering arbitrary, either machine or macro generated,
>     but every register has a symbolic index.
>     NUM_ID_IDX,
> };
>
> enum ARMSysregs {
>     SYS_ID_AA64PFR0_EL1 = ENCODE_AA64_CP_REG(...),
>     etc
> };
>
> const uint32_t id_register_sysreg[NUM_ID_IDX] = {
>     [ID_AA64ISAR0_IDX] = SYS_ID_AA64PFR0_EL1,
>     etc
> };
>
> struct ARMISARegisters {
>     uint64_t id[NUM_ID_IDX];
> };
>
> This seems trivial to automate, and wastes no space.

Sure we will study such rework. As long as the key (ID_AA64ISAR0_IDX)
can be matched against the index used by the KVM API we should be fine.

Thanks

Eric
>
>
> r~
>
Richard Henderson Dec. 12, 2024, 6:12 p.m. UTC | #3
On 12/12/24 11:46, Eric Auger wrote:
>> Do we really need anything beyond the defined registers, or even the
>> defined registers for which qemu knows how to do anything?
> what do you mean by "defined registers". The end goal is to be able to
> tune any id reg that the kernel allows to write. So I guess we shall
> encompass more regs than qemu currently handles.

Defined registers as in "have an architected definition".

E.g. there's no need to set any fields in (op0=0 op1=0 crn=0, crm=0, op2=1), because that 
isn't a defined system register.  It's in id register space sure, and almost certainly 
RAZ, but there's no call to either set it or represent it within qemu.

Because you're working to a a symbolic command-line interface, with FEAT_FOO, ID_REG.FIELD 
names, qemu will (be extended to) handle every register named.

> Wrt op1={1,3}, tbh I initially sticked to the KVM API. Now looking at
> D22-2, effectively we have very few ID regs there. If we were to use a
> hashtable we may be more flexible in picking up the indexes that are
> relevant for us.

Yes.  And I'm suggesting the "hashtable" be defined by compile-time constants.

>> const uint32_t id_register_sysreg[NUM_ID_IDX] = {
>>      [ID_AA64ISAR0_IDX] = SYS_ID_AA64PFR0_EL1,
>>      etc
>> };
>>
>> struct ARMISARegisters {
>>      uint64_t id[NUM_ID_IDX];
>> };
>>
>> This seems trivial to automate, and wastes no space.
> 
> Sure we will study such rework. As long as the key (ID_AA64ISAR0_IDX)
> can be matched against the index used by the KVM API we should be fine.

I haven't looked to see what KVM_ARM_GET_REG_WRITABLE_MASKS really returns, but I see no 
reason that it should not be trivial to map back and forth between the spaces.


r~
Cornelia Huck Dec. 13, 2024, 4:16 p.m. UTC | #4
On Thu, Dec 12 2024, Richard Henderson <richard.henderson@linaro.org> wrote:

> On 12/12/24 11:46, Eric Auger wrote:
>>> Do we really need anything beyond the defined registers, or even the
>>> defined registers for which qemu knows how to do anything?
>> what do you mean by "defined registers". The end goal is to be able to
>> tune any id reg that the kernel allows to write. So I guess we shall
>> encompass more regs than qemu currently handles.
>
> Defined registers as in "have an architected definition".
>
> E.g. there's no need to set any fields in (op0=0 op1=0 crn=0, crm=0, op2=1), because that 
> isn't a defined system register.  It's in id register space sure, and almost certainly 
> RAZ, but there's no call to either set it or represent it within qemu.
>
> Because you're working to a a symbolic command-line interface, with FEAT_FOO, ID_REG.FIELD 
> names, qemu will (be extended to) handle every register named.

Going from the definitions, we have the potential to generate props for
anything that has been named (do we have named registers/fields that are not
architected?) Exposed on the command line are only those register fields
that are actually writable with the current KVM interface (see patch 18.)

I'm still not quite sure how to continue with FEAT_FOO, but I guess
we're still going to need the ID_REG.FIELD names in any case to handle
differences like DIC in CTR_EL0 mentioned elsewhere in this thread.
diff mbox series

Patch

diff --git a/target/arm/cpu-sysregs.h b/target/arm/cpu-sysregs.h
new file mode 100644
index 000000000000..f4b63a3af77b
--- /dev/null
+++ b/target/arm/cpu-sysregs.h
@@ -0,0 +1,42 @@ 
+#ifndef ARM_CPU_SYSREGS_H
+#define ARM_CPU_SYSREGS_H
+
+/* to be generated */
+
+#define SYS_ID_AA64PFR0_EL1                             sys_reg(3, 0, 0, 4, 0)
+#define SYS_ID_AA64PFR1_EL1                             sys_reg(3, 0, 0, 4, 1)
+#define SYS_ID_AA64SMFR0_EL1                            sys_reg(3, 0, 0, 4, 5)
+#define SYS_ID_AA64DFR0_EL1                             sys_reg(3, 0, 0, 5, 0)
+#define SYS_ID_AA64DFR1_EL1                             sys_reg(3, 0, 0, 5, 1)
+#define SYS_ID_AA64ISAR0_EL1                            sys_reg(3, 0, 0, 6, 0)
+#define SYS_ID_AA64ISAR1_EL1                            sys_reg(3, 0, 0, 6, 1)
+#define SYS_ID_AA64ISAR2_EL1                            sys_reg(3, 0, 0, 6, 2)
+#define SYS_ID_AA64MMFR0_EL1                            sys_reg(3, 0, 0, 7, 0)
+#define SYS_ID_AA64MMFR1_EL1                            sys_reg(3, 0, 0, 7, 1)
+#define SYS_ID_AA64MMFR2_EL1                            sys_reg(3, 0, 0, 7, 2)
+#define SYS_ID_AA64MMFR3_EL1                            sys_reg(3, 0, 0, 7, 3)
+
+#define SYS_ID_PFR0_EL1                                 sys_reg(3, 0, 0, 1, 0)
+#define SYS_ID_PFR1_EL1                                 sys_reg(3, 0, 0, 1, 1)
+#define SYS_ID_DFR0_EL1                                 sys_reg(3, 0, 0, 1, 2)
+#define SYS_ID_MMFR0_EL1                                sys_reg(3, 0, 0, 1, 4)
+#define SYS_ID_MMFR1_EL1                                sys_reg(3, 0, 0, 1, 5)
+#define SYS_ID_MMFR2_EL1                                sys_reg(3, 0, 0, 1, 6)
+#define SYS_ID_MMFR3_EL1                                sys_reg(3, 0, 0, 1, 7)
+#define SYS_ID_ISAR0_EL1                                sys_reg(3, 0, 0, 2, 0)
+#define SYS_ID_ISAR1_EL1                                sys_reg(3, 0, 0, 2, 1)
+#define SYS_ID_ISAR2_EL1                                sys_reg(3, 0, 0, 2, 2)
+#define SYS_ID_ISAR3_EL1                                sys_reg(3, 0, 0, 2, 3)
+#define SYS_ID_ISAR4_EL1                                sys_reg(3, 0, 0, 2, 4)
+#define SYS_ID_ISAR5_EL1                                sys_reg(3, 0, 0, 2, 5)
+#define SYS_ID_MMFR4_EL1                                sys_reg(3, 0, 0, 2, 6)
+#define SYS_ID_ISAR6_EL1                                sys_reg(3, 0, 0, 2, 7)
+#define SYS_MVFR0_EL1                                   sys_reg(3, 0, 0, 3, 0)
+#define SYS_MVFR1_EL1                                   sys_reg(3, 0, 0, 3, 1)
+#define SYS_MVFR2_EL1                                   sys_reg(3, 0, 0, 3, 2)
+#define SYS_ID_PFR2_EL1                                 sys_reg(3, 0, 0, 3, 4)
+#define SYS_ID_DFR1_EL1                                 sys_reg(3, 0, 0, 3, 5)
+#define SYS_ID_MMFR5_EL1                                sys_reg(3, 0, 0, 3, 6)
+#define SYS_ID_AA64ZFR0_EL1                             sys_reg(3, 0, 0, 4, 4)
+
+#endif
diff --git a/target/arm/cpu.h b/target/arm/cpu.h
index e359152a4dbc..9e0403c9810a 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -144,6 +144,14 @@  typedef struct ARMGenericTimer {
     uint64_t ctl; /* Timer Control register */
 } ARMGenericTimer;
 
+typedef struct ARMSysReg {
+    int op0;
+    int op1;
+    int crn;
+    int crm;
+    int op2;
+} ARMSysReg;
+
 /* Define a maximum sized vector register.
  * For 32-bit, this is a 128-bit NEON/AdvSIMD register.
  * For 64-bit, this is a 2048-bit SVE register.
@@ -841,6 +849,51 @@  typedef struct IdRegMap {
     uint64_t regs[NR_ID_REGS];
 } IdRegMap;
 
+#define ARM_FEATURE_ID_RANGE_IDX(op0, op1, crn, crm, op2)               \
+        ({                                                              \
+                __u64 __op1 = (op1) & 3;                                \
+                __op1 -= (__op1 == 3);                                  \
+                (__op1 << 6 | ((crm) & 7) << 3 | (op2));                \
+        })
+
+static inline uint64_t _get_idreg(const IdRegMap *map, ARMSysReg sr)
+{
+    int index = ARM_FEATURE_ID_RANGE_IDX(sr.op0, sr.op1, sr.crn, sr.crm, sr.op2);
+
+    return map->regs[index];
+}
+
+static inline void _set_idreg(IdRegMap *map, ARMSysReg sr, uint64_t value)
+{
+    int index = ARM_FEATURE_ID_RANGE_IDX(sr.op0, sr.op1, sr.crn, sr.crm, sr.op2);
+
+    map->regs[index] = value;
+}
+
+/* REG is ID_XXX */
+#define FIELD_DP64_IDREG(MAP, REG, FIELD, VALUE)              \
+{                                                             \
+uint64_t regval = _get_idreg(MAP, SYS_ ## REG ## _EL1);       \
+regval = FIELD_DP64(regval, REG, FIELD, VALUE);               \
+_set_idreg(MAP, SYS_ ## REG ## _EL1, regval);                 \
+}
+
+#define FIELD_EX64_IDREG(MAP, REG, FIELD)                     \
+FIELD_EX64(_get_idreg(MAP, SYS_ ## REG ## _EL1), REG, FIELD)  \
+
+#define SET_IDREG(MAP, REG, VALUE)                            \
+_set_idreg(MAP, SYS_ ## REG ## _EL1, VALUE)
+
+#define GET_IDREG(MAP, REG)                                   \
+_get_idreg(MAP, SYS_ ## REG ## _EL1)
+
+static inline ARMSysReg sys_reg(int op0, int op1, int crn, int crm, int op2)
+{
+        ARMSysReg sr = {op0, op1, crn, crm, op2};
+
+        return sr;
+}
+
 /**
  * ARMCPU:
  * @env: #CPUARMState
@@ -1052,6 +1105,7 @@  struct ArchCPU {
         uint64_t id_aa64zfr0;
         uint64_t id_aa64smfr0;
         uint64_t reset_pmcr_el0;
+        IdRegMap idregs;
     } isar;
     uint64_t midr;
     uint32_t revidr;