For some registers, we do not have a single ID register, but actually
an array of values (e.g. CCSIDR_EL1, where the actual value is
determined by whatever CSSELR_EL1 points to.) If we want to avoid
using a different way to handle registers like that for every
instance, we should provide some kind of infrastructure. Therefore,
add accessors {GET,SET}_IDREG_DEMUX that are similar to the accessors
we already use for regular ID registers.
Signed-off-by: Cornelia Huck <cohuck@redhat.com>
---
target/arm/cpu-sysregs.h | 5 +++++
target/arm/cpu.h | 20 ++++++++++++++++++++
2 files changed, 25 insertions(+)
diff --git a/target/arm/cpu-sysregs.h b/target/arm/cpu-sysregs.h
index 7877a3b06a8e..31f82c6a0afc 100644
--- a/target/arm/cpu-sysregs.h
+++ b/target/arm/cpu-sysregs.h
@@ -35,6 +35,11 @@ typedef enum ARMSysRegs {
#undef DEF
+/* ID registers that vary based upon another register */
+typedef enum ARMIDRegisterDemuxIdx {
+ NUM_ID_DEMUX_IDX,
+} ARMIDRegisterDemuxIdx;
+
extern const uint32_t id_register_sysreg[NUM_ID_IDX];
int get_sysreg_idx(ARMSysRegs sysreg);
diff --git a/target/arm/cpu.h b/target/arm/cpu.h
index 08b7d3fb936a..f7bd19f26fbd 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -905,6 +905,25 @@ typedef struct {
i_->idregs[REG ## _EL1_IDX]; \
})
+#define SET_IDREG_DEMUX(ISAR, REG, INDEX, VALUE) \
+ ({ \
+ ARMISARegisters *i_ = (ISAR); \
+ i_->idregs_demux[REG ## _EL1_DEMUX_IDX][INDEX] = VALUE; \
+ })
+
+#define GET_IDREG_DEMUX(ISAR, REG, INDEX) \
+ ({ \
+ ARMISARegisters *i_ = (ISAR); \
+ i_->idregs_demux[REG ## _EL1_DEMUX_IDX][INDEX]; \
+ })
+
+#define COPY_IDREG_DEMUX(ISAR, REG, FROM_INDEX, TO_INDEX) \
+ ({ \
+ ARMISARegisters *i_ = (ISAR); \
+ i_->idregs_demux[REG ## _EL1_DEMUX_IDX][TO_INDEX] = \
+ i_->idregs_demux[REG ## _EL1_DEMUX_IDX][FROM_INDEX]; \
+ })
+
/**
* ARMCPU:
* @env: #CPUARMState
@@ -1083,6 +1102,7 @@ struct ArchCPU {
uint32_t dbgdevid1;
uint64_t reset_pmcr_el0;
uint64_t idregs[NUM_ID_IDX];
+ uint64_t idregs_demux[NUM_ID_DEMUX_IDX][16];
} isar;
uint64_t midr;
uint32_t revidr;
--
2.52.0
On Mon, 19 Jan 2026 18:27:30 +0100
Cornelia Huck <cohuck@redhat.com> wrote:
> For some registers, we do not have a single ID register, but actually
> an array of values (e.g. CCSIDR_EL1, where the actual value is
> determined by whatever CSSELR_EL1 points to.) If we want to avoid
> using a different way to handle registers like that for every
> instance, we should provide some kind of infrastructure. Therefore,
> add accessors {GET,SET}_IDREG_DEMUX that are similar to the accessors
> we already use for regular ID registers.
>
> Signed-off-by: Cornelia Huck <cohuck@redhat.com>
> ---
> target/arm/cpu-sysregs.h | 5 +++++
> target/arm/cpu.h | 20 ++++++++++++++++++++
> 2 files changed, 25 insertions(+)
>
> diff --git a/target/arm/cpu-sysregs.h b/target/arm/cpu-sysregs.h
> index 7877a3b06a8e..31f82c6a0afc 100644
> --- a/target/arm/cpu-sysregs.h
> +++ b/target/arm/cpu-sysregs.h
> @@ -35,6 +35,11 @@ typedef enum ARMSysRegs {
>
> #undef DEF
>
> +/* ID registers that vary based upon another register */
> +typedef enum ARMIDRegisterDemuxIdx {
> + NUM_ID_DEMUX_IDX,
> +} ARMIDRegisterDemuxIdx;
> +
> extern const uint32_t id_register_sysreg[NUM_ID_IDX];
>
> int get_sysreg_idx(ARMSysRegs sysreg);
> diff --git a/target/arm/cpu.h b/target/arm/cpu.h
> index 08b7d3fb936a..f7bd19f26fbd 100644
> --- a/target/arm/cpu.h
> +++ b/target/arm/cpu.h
> @@ -905,6 +905,25 @@ typedef struct {
> i_->idregs[REG ## _EL1_IDX]; \
> })
>
> +#define SET_IDREG_DEMUX(ISAR, REG, INDEX, VALUE) \
> + ({ \
> + ARMISARegisters *i_ = (ISAR); \
> + i_->idregs_demux[REG ## _EL1_DEMUX_IDX][INDEX] = VALUE; \
> + })
> +
> +#define GET_IDREG_DEMUX(ISAR, REG, INDEX) \
> + ({ \
> + ARMISARegisters *i_ = (ISAR); \
> + i_->idregs_demux[REG ## _EL1_DEMUX_IDX][INDEX]; \
> + })
> +
> +#define COPY_IDREG_DEMUX(ISAR, REG, FROM_INDEX, TO_INDEX) \
> + ({ \
> + ARMISARegisters *i_ = (ISAR); \
> + i_->idregs_demux[REG ## _EL1_DEMUX_IDX][TO_INDEX] = \
> + i_->idregs_demux[REG ## _EL1_DEMUX_IDX][FROM_INDEX]; \
> + })
> +
> /**
> * ARMCPU:
> * @env: #CPUARMState
> @@ -1083,6 +1102,7 @@ struct ArchCPU {
> uint32_t dbgdevid1;
> uint64_t reset_pmcr_el0;
> uint64_t idregs[NUM_ID_IDX];
> + uint64_t idregs_demux[NUM_ID_DEMUX_IDX][16];
Hi,
Trivial, but I'd like a comment on why 16. I assume because that's
the biggest you've seen so far (8 levels, 2 types for CCSIDR)
Just good to have a bread crumb here for future readers.
Otherwise seems reasonable to me.
> } isar;
> uint64_t midr;
> uint32_t revidr;
On Wed, Jan 21 2026, Jonathan Cameron <jonathan.cameron@huawei.com> wrote:
> On Mon, 19 Jan 2026 18:27:30 +0100
> Cornelia Huck <cohuck@redhat.com> wrote:
>> @@ -1083,6 +1102,7 @@ struct ArchCPU {
>> uint32_t dbgdevid1;
>> uint64_t reset_pmcr_el0;
>> uint64_t idregs[NUM_ID_IDX];
>> + uint64_t idregs_demux[NUM_ID_DEMUX_IDX][16];
> Hi,
>
> Trivial, but I'd like a comment on why 16. I assume because that's
> the biggest you've seen so far (8 levels, 2 types for CCSIDR)
> Just good to have a bread crumb here for future readers.
Yes, this came in via the current ccsidr[] sizing (which also might not
be quite the right thing to use, given that we could have 2*7 + 7
entries with FEAT_MTE2); I'm not at all sure whether that's a reasonable
value, but I'll certainly add a comment if we agree that it is. Or maybe
make it a #define.
>
> Otherwise seems reasonable to me.
Thanks for looking!
>> } isar;
>> uint64_t midr;
>> uint32_t revidr;
© 2016 - 2026 Red Hat, Inc.