The ICH_CONTEXTR_EL2 register is how the hypervisor tells the GICv5 to make a VCPU for a VM resident on a CPU, so that the IRS will forward virtual interrupts for that VCPU to this CPU. We implement this similarly to the GICv5 stream protocol, which uses a SetResident command to tell the IRS to make the VCPU resident. This will always fail until we have implemented the IRS's end of virtualization support.
Signed-off-by: Peter Maydell <[email protected]> --- hw/intc/arm_gicv5.c | 8 ++ include/hw/intc/arm_gicv5_stream.h | 30 +++++++ target/arm/cpu.h | 1 + target/arm/tcg/gicv5-cpuif.c | 121 +++++++++++++++++++++++++++++ target/arm/tcg/trace-events | 1 + 5 files changed, 161 insertions(+) diff --git a/hw/intc/arm_gicv5.c b/hw/intc/arm_gicv5.c index f714add1406..5ac50acc786 100644 --- a/hw/intc/arm_gicv5.c +++ b/hw/intc/arm_gicv5.c @@ -1263,6 +1263,14 @@ txfail: "physical address 0x" HWADDR_FMT_plx "\n", intid, l1_addr); } +bool gicv5_set_resident(GICv5Common *cs, GICv5Domain domain, bool valid, + uint32_t vpeid, bool db, uint8_t dbpm, uint32_t vmid, + uint32_t cpuidx) +{ + /* Until we implement virtualization, SetResident always fails */ + return true; +} + /* Data we need to pass through to irs_clean_lpi_cache_entry() */ typedef struct CleanLPICacheUserData { GICv5Common *cs; diff --git a/include/hw/intc/arm_gicv5_stream.h b/include/hw/intc/arm_gicv5_stream.h index 21be859a233..162d55a8e6b 100644 --- a/include/hw/intc/arm_gicv5_stream.h +++ b/include/hw/intc/arm_gicv5_stream.h @@ -229,4 +229,34 @@ GICv5PendingIrq gicv5_get_hppi(GICv5Common *cs, GICv5Domain domain, void gicv5_deactivate(GICv5Common *cs, uint32_t id, GICv5Domain domain, GICv5IntType type, bool virtual); +/** + * gicv5_set_resident + * @cs: GIC IRS to send command to + * @domain: interrupt domain to act on + * @valid: true if setting resident VPE, false to clear it + * @vpeid: identifier of the VPE to make resident + * @db: true to request a doorbell + * @dbpm: doorbell priority mask + * @vmid: Virtual Machine identifier + * @cpuidx: IRS CPU index of this CPU interface + * + * Tell the IRS to make the specified VPE resident for this CPU. This + * corresponds approximately to the stream protocol SetResident command. + * + * Returns the Fault state: false if the SetResident operation + * succeeded, true if the VPE could not be made resident. Clearing the + * VPE always succeeds and returns false. + * + * For QEMU this is a synchronous operation: the function does not return + * until the IRS has either succeeded or failed in making the VPE resident + * (including identifying whether there is a pending virtual interrupt for + * the VPE and forwarding it by calling gicv5_forward_interrupt() if so). + * + * Unlike the stream protocol (where the IRS knows which CPU it is talking + * to), we have to pass the cpuidx to tell the IRS which CPU we are. + */ +bool gicv5_set_resident(GICv5Common *cs, GICv5Domain domain, bool valid, + uint32_t vpeid, bool db, uint8_t dbpm, uint32_t vmid, + uint32_t cpuidx); + #endif diff --git a/target/arm/cpu.h b/target/arm/cpu.h index abaee5fc3a0..0d8c2176a43 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -599,6 +599,7 @@ typedef struct CPUArchState { /* And for the virtual interrupt domain */ GICv5PendingIrq virt_ppi_hppi; + uint64_t ich_contextr_el2; uint64_t ich_apr_el2; uint64_t ich_vctlr_el2; uint64_t ich_vmcr_el2; diff --git a/target/arm/tcg/gicv5-cpuif.c b/target/arm/tcg/gicv5-cpuif.c index a938428b20a..2eb8184e329 100644 --- a/target/arm/tcg/gicv5-cpuif.c +++ b/target/arm/tcg/gicv5-cpuif.c @@ -73,6 +73,14 @@ FIELD(ICH_VMCR_EL2, VMPR, 27, 5) FIELD(ICH_VCTLR_EL2, EN, 0, 1) FIELD(ICH_VCTLR_EL2, V3, 1, 1) +FIELD(ICH_CONTEXTR_EL2, VM, 0, 16) +FIELD(ICH_CONTEXTR_EL2, VPE, 32, 16) +FIELD(ICH_CONTEXTR_EL2, DBPM, 55, 5) +FIELD(ICH_CONTEXTR_EL2, DB, 60, 1) +FIELD(ICH_CONTEXTR_EL2, IRICHPPIDIS, 61, 1) +FIELD(ICH_CONTEXTR_EL2, F, 62, 1) +FIELD(ICH_CONTEXTR_EL2, V, 63, 1) + /* * We implement 24 bits of interrupt ID, the mandated 5 bits of priority, * and no legacy GICv3.3 vcpu interface (yet) @@ -821,6 +829,110 @@ static void gic_ich_vmcr_write(CPUARMState *env, const ARMCPRegInfo *ri, gicv5_update_virq_vfiq(env); } +static void gic_ich_contextr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + GICv5Common *gic = gicv5_get_gic(env); + GICv5Domain domain = gicv5_logical_domain(env); + bool valid = FIELD_EX64(value, ICH_CONTEXTR_EL2, V); + uint32_t vpe = FIELD_EX64(value, ICH_CONTEXTR_EL2, VPE); + bool db = FIELD_EX64(value, ICH_CONTEXTR_EL2, DB); + uint8_t dbpm = FIELD_EX64(value, ICH_CONTEXTR_EL2, DBPM); + uint32_t vm = FIELD_EX64(value, ICH_CONTEXTR_EL2, VM); + uint32_t cpuidx = env->gicv5.cpuidx; + bool fault; + uint64_t oldval = env->gicv5_cpuif.ich_contextr_el2; + bool vm_vpe_changed = (oldval ^ value) & (R_ICH_CONTEXTR_EL2_VM_MASK | + R_ICH_CONTEXTR_EL2_VPE_MASK); + bool valid_changed = (oldval ^ value) & R_ICH_CONTEXTR_EL2_V_MASK; + + /* + * DB and DBPM are RAZ: they are write-only fields that have + * an effect when V is changed from 1 to 0. + */ + value &= ~(R_ICH_CONTEXTR_EL2_DB_MASK | R_ICH_CONTEXTR_EL2_DBPM_MASK); + + raw_write(env, ri, value); + + trace_gicv5_ich_contextr_write(cpuidx, value); + + /* + * Changing V definitely causes us to make a new VPE resident, or + * remove the old one. + * + * Changing VPE or VM while V=1 is CONSTRAINED UNPREDICTABLE + * (R_VFKHN): we choose to make the new VPE resident so that we + * can trust that the information we have in the register matches + * what the IRS thinks the resident VPE is. + * + * For writes that don't trigger a change of resident VPE, we only + * need to update our copy of the register, and recalculate + * interrupts if IIRICHPPIDIS changed. + * + * If we do change the resident VPE, the interrupt recalculation + * will happen when the IRS calls gicv5_forward_interrupt(). + */ + if (!(valid_changed || (valid && vm_vpe_changed))) { + if ((oldval & value) & R_ICH_CONTEXTR_EL2_IRICHPPIDIS_MASK) { + gicv5_update_virq_vfiq(env); + } + return; + } + + if (valid) { + /* Doorbell info is only relevant when clearing the VPE */ + dbpm = 0; + db = false; + } else { + /* The IRS knows what the VPE is so we don't need to tell it */ + vm = 0; + vpe = 0; + } + + fault = gicv5_set_resident(gic, domain, valid, vpe, db, dbpm, vm, cpuidx); + + if (valid) { + /* + * We only update F for a VPE set, not a clear. + * + * On a failure, it is CONSTRAINED UNPREDICTABLE whether V + * reads as 0 or 1 (R_NHCGW); we choose to set it to 0 so we + * can reliably use it to track whether a VPE is resident. We + * use this for instance in GIC VDPEND: we trust the V and VM + * fields to be accurate. + */ + value = env->gicv5_cpuif.ich_contextr_el2; + value = FIELD_DP64(value, ICH_CONTEXTR_EL2, F, fault); + if (fault) { + value = FIELD_DP64(value, ICH_CONTEXTR_EL2, V, 0); + } + env->gicv5_cpuif.ich_contextr_el2 = value; + } +} + +static void gic_ich_contextr_reset(CPUARMState *env, const ARMCPRegInfo *ri) +{ + /* + * This register resets to zero, which includes telling the IRS + * that there is no longer a resident VPE for it. + * + * It doesn't matter if the domain we pass here doesn't match the + * domain of the VM/VPE: as with the GIC stream interface (see + * rule R_PYWKM) our IRS ignores the mismatch for a SetResident + * request with valid = false. + * + * As this is a reset fn we may get here before the GIC pointer + * has been set up yet, so unusually we need to check for NULL> + */ + raw_write(env, ri, 0); + GICv5Common *gic = gicv5_get_gic(env); + + if (gic) { + gicv5_set_resident(gic, gicv5_logical_domain(env), + false, 0, false, 0, 0, env->gicv5.cpuidx); + } +} + /* * ICC_APR_EL1 is banked and reads/writes as the version for the * current logical interrupt domain. @@ -1500,6 +1612,15 @@ static const ARMCPRegInfo gicv5_cpuif_reginfo[] = { .fieldoffset = offsetof(CPUARMState, gicv5_cpuif.ich_vctlr_el2), .writefn = gic_ich_vctlr_write, }, + { .name = "ICH_CONTEXTR_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 11, .opc2 = 6, + .access = PL2_RW, .type = ARM_CP_IO | ARM_CP_NO_RAW, + .nv2_redirect_offset = 0xb08, + .accessfn = access_gicv5_ich_reg, + .fieldoffset = offsetof(CPUARMState, gicv5_cpuif.ich_contextr_el2), + .writefn = gic_ich_contextr_write, + .resetfn = gic_ich_contextr_reset, + }, { .name = "ICH_VMCR_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 11, .opc2 = 7, .access = PL2_RW, .type = ARM_CP_IO | ARM_CP_NO_RAW, diff --git a/target/arm/tcg/trace-events b/target/arm/tcg/trace-events index 71f1369fcda..75f40c44f26 100644 --- a/target/arm/tcg/trace-events +++ b/target/arm/tcg/trace-events @@ -11,3 +11,4 @@ gicv5_cddi(int domain, uint32_t id) "domain %d CDDI deactivating interrupt ID 0x gicv5_update_irq_fiq(bool irq, bool fiq, bool nmi) "now IRQ %d FIQ %d NMI %d" gicv5_update_virq_vfiq(bool virq, bool vinmi) "now VIRQ %d VINMI %d" gicv5_update_ppi_state(int ppi, bool level) "PPI %d source level now %d" +gicv5_ich_contextr_write(uint32_t cpuidx, uint64_t value) "cpuidx %d ICH_CONTEXTR_WRITE 0x%" PRIx64 -- 2.43.0
