Implement the redirection of ICC_PPI_PRIORITYR<n>_EL1 to ICV_PPI_ENABLER<n>_EL1; the data for this is stored in ICH_PPI_ENABLER<n>_EL2.
The algorithm in gic_ppi_prio_hvi_mask() for expanding 8 mask bits out into 8 mask bytes is suggested by an AI, but it is only 4 lines of code, and I wrote the comments. Signed-off-by: Peter Maydell <[email protected]> --- target/arm/tcg/gicv5-cpuif.c | 64 ++++++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 2 deletions(-) diff --git a/target/arm/tcg/gicv5-cpuif.c b/target/arm/tcg/gicv5-cpuif.c index 2c717b99a0b..e1d3ffb7b1e 100644 --- a/target/arm/tcg/gicv5-cpuif.c +++ b/target/arm/tcg/gicv5-cpuif.c @@ -487,11 +487,70 @@ static void gic_ppi_enable_write(CPUARMState *env, const ARMCPRegInfo *ri, } } +static int icc_ppi_prio_reg_index(const ARMCPRegInfo *ri) +{ + /* + * Return the index of the ICC or ICV PPI_PRIORITYR register, + * which is CRm[0]:opc2[2:0]. + */ + return deposit32(ri->opc2, 3, 1, ri->crm); +} + +static uint64_t gic_ppi_prio_hvi_mask(CPUARMState *env, int idx) +{ + /* + * Return the mask of which fields in the given priority register + * the guest cannot see, which is controlled by the ICH_PPI_HVI + * registers. We need to expand the appropriate 8 bits from + * ICH_PPI_HVI out to a mask where each byte is either all-1s or + * all-0s. The return value here is 1s for "do not show the + * guest", the same sense as the ICH_PPI_HVI registers. + */ + int regno = idx >> 3; + int startbit = idx & 7; + /* Extract the relevant 8 HVI bits from the right register */ + uint64_t mask = extract64(env->gicv5_cpuif.ich_ppi_hvi[regno], startbit, 8); + + /* + * Now we're going to distribute those 8 bits so they end up in + * bits 0, 8, 16, ... 56 of the mask (i.e. in the LSB of each byte). + */ + /* Split the 8 bits into two 4-bit chunks at bits 0 and 32 */ + mask = (mask | (mask << 28)) & 0x0000000f0000000fULL; + /* Split again into four 2-bit chunks at bits 0, 16, 32, 48 */ + mask = (mask | (mask << 14)) & 0x0003000300030003ULL; + /* Split again into eight 1-bit chunks, at the LSB of each byte */ + mask = (mask | (mask << 7)) & 0x0101010101010101ULL; + /* Finally, the multiply turns each 0x1 byte into 0xFF */ + mask *= 0xff; + return mask; +} + +static uint64_t gic_ppi_priority_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + if (gicv5_icv_access(env)) { + int idx = icc_ppi_prio_reg_index(ri); + uint64_t r = env->gicv5_cpuif.ich_ppi_priority[idx]; + uint64_t hvi_mask = gic_ppi_prio_hvi_mask(env, idx); + + return r & ~hvi_mask; + } else { + return raw_read(env, ri); + } +} + static void gic_ppi_priority_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { - raw_write(env, ri, value); - gic_recalc_ppi_hppi(env); + if (gicv5_icv_access(env)) { + int idx = icc_ppi_prio_reg_index(ri); + uint64_t hvi_mask = gic_ppi_prio_hvi_mask(env, idx); + + env->gicv5_cpuif.ich_ppi_priority[idx] = value & ~hvi_mask; + } else { + raw_write(env, ri, value); + gic_recalc_ppi_hppi(env); + } } static void gic_ich_ppi_enable_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -1239,6 +1298,7 @@ void define_gicv5_cpuif_regs(ARMCPU *cpu) .fgt = FGT_ICC_PPI_PRIORITYRN_EL1, .fieldoffset = offsetof(CPUARMState, gicv5_cpuif.ppi_priority[i]), .writefn = gic_ppi_priority_write, .raw_writefn = raw_write, + .readfn = gic_ppi_priority_read, }; define_one_arm_cp_reg(cpu, &ppi_prio); } -- 2.43.0
