From 664107ec4b440247e1eec9d7b950634a94e0933e Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Tue, 17 Aug 2021 09:11:25 +0100 Subject: [PATCH 01/20] KVM: arm64: Restore mdcr_el2 from vcpu commit 1460b4b25fde Upstream On deactivating traps, restore the value of mdcr_el2 from the newly created and preserved host value vcpu context, rather than directly reading the hardware register. Up until and including this patch the two values are the same, i.e., the hardware register and the vcpu one. A future patch will be changing the value of mdcr_el2 on activating traps, and this ensures that its value will be restored. No functional change intended. Signed-off-by: Fuad Tabba Acked-by: Will Deacon Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20210817081134.2918285-7-tabba@google.com Signed-off-by: Xie Xiaodong <624338359@qq.com> --- arch/arm64/include/asm/kvm_host.h | 5 ++++- arch/arm64/include/asm/kvm_hyp.h | 2 +- arch/arm64/kvm/hyp/switch.c | 32 ++++++++++++++----------------- arch/arm64/kvm/hyp/sysreg-sr.c | 2 +- 4 files changed, 20 insertions(+), 21 deletions(-) diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h index 01886b83d120..79139f0d7d85 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -249,10 +249,13 @@ struct kvm_vcpu_arch { void *sve_state; unsigned int sve_max_vl; - /* HYP configuration */ + /* Values of trap registers for the guest. */ u64 hcr_el2; u32 mdcr_el2; + /* Values of trap registers for the host before guest entry. */ + u64 mdcr_el2_host; + /* Exception Information */ struct kvm_vcpu_fault_info fault; diff --git a/arch/arm64/include/asm/kvm_hyp.h b/arch/arm64/include/asm/kvm_hyp.h index 66d4c5e35932..6fdf37cf5209 100644 --- a/arch/arm64/include/asm/kvm_hyp.h +++ b/arch/arm64/include/asm/kvm_hyp.h @@ -79,7 +79,7 @@ void __fpsimd_save_state(struct user_fpsimd_state *fp_regs); void __fpsimd_restore_state(struct user_fpsimd_state *fp_regs); void activate_traps_vhe_load(struct kvm_vcpu *vcpu); -void deactivate_traps_vhe_put(void); +void deactivate_traps_vhe_put(struct kvm_vcpu *vcpu); u64 __guest_enter(struct kvm_vcpu *vcpu, struct kvm_cpu_context *host_ctxt); void __noreturn __hyp_do_panic(unsigned long, ...); diff --git a/arch/arm64/kvm/hyp/switch.c b/arch/arm64/kvm/hyp/switch.c index 624e5c83a497..ef7fe0801906 100644 --- a/arch/arm64/kvm/hyp/switch.c +++ b/arch/arm64/kvm/hyp/switch.c @@ -91,11 +91,14 @@ static void __hyp_text __activate_traps_common(struct kvm_vcpu *vcpu) if (cpus_have_final_cap(ARM64_HAS_NMI)) sysreg_clear_set_s(SYS_HCRX_EL2, 0, HCRX_EL2_TALLINT); + vcpu->arch.mdcr_el2_host = read_sysreg(mdcr_el2); write_sysreg(vcpu->arch.mdcr_el2, mdcr_el2); } -static void __hyp_text __deactivate_traps_common(void) +static void __hyp_text __deactivate_traps_common(struct kvm_vcpu *vcpu) { + write_sysreg(vcpu->arch.mdcr_el2_host, mdcr_el2); + if (cpus_have_final_cap(ARM64_HAS_NMI)) sysreg_clear_set_s(SYS_HCRX_EL2, HCRX_EL2_TALLINT, 0); @@ -178,16 +181,13 @@ static void deactivate_traps_vhe(void) } NOKPROBE_SYMBOL(deactivate_traps_vhe); -static void __hyp_text __deactivate_traps_nvhe(void) +static void __hyp_text __deactivate_traps_nvhe(struct kvm_vcpu *vcpu) { - u64 mdcr_el2 = read_sysreg(mdcr_el2); - - __deactivate_traps_common(); + vcpu->arch.mdcr_el2_host &= MDCR_EL2_HPMN_MASK | + MDCR_EL2_E2PB_MASK << MDCR_EL2_E2PB_SHIFT; - mdcr_el2 &= MDCR_EL2_HPMN_MASK; - mdcr_el2 |= MDCR_EL2_E2PB_MASK << MDCR_EL2_E2PB_SHIFT; + __deactivate_traps_common(vcpu); - write_sysreg(mdcr_el2, mdcr_el2); write_sysreg(HCR_HOST_NVHE_FLAGS, hcr_el2); write_sysreg(CPTR_EL2_DEFAULT, cptr_el2); } @@ -208,7 +208,7 @@ static void __hyp_text __deactivate_traps(struct kvm_vcpu *vcpu) if (has_vhe()) deactivate_traps_vhe(); else - __deactivate_traps_nvhe(); + __deactivate_traps_nvhe(vcpu); } void activate_traps_vhe_load(struct kvm_vcpu *vcpu) @@ -216,17 +216,13 @@ void activate_traps_vhe_load(struct kvm_vcpu *vcpu) __activate_traps_common(vcpu); } -void deactivate_traps_vhe_put(void) +void deactivate_traps_vhe_put(struct kvm_vcpu *vcpu) { - u64 mdcr_el2 = read_sysreg(mdcr_el2); - - mdcr_el2 &= MDCR_EL2_HPMN_MASK | - MDCR_EL2_E2PB_MASK << MDCR_EL2_E2PB_SHIFT | - MDCR_EL2_TPMS; - - write_sysreg(mdcr_el2, mdcr_el2); + vcpu->arch.mdcr_el2_host &= MDCR_EL2_HPMN_MASK | + MDCR_EL2_E2PB_MASK << MDCR_EL2_E2PB_SHIFT | + MDCR_EL2_TPMS; - __deactivate_traps_common(); + __deactivate_traps_common(vcpu); } static void __hyp_text __activate_vm(struct kvm *kvm) diff --git a/arch/arm64/kvm/hyp/sysreg-sr.c b/arch/arm64/kvm/hyp/sysreg-sr.c index 7ddbc849b580..ad83adbf4172 100644 --- a/arch/arm64/kvm/hyp/sysreg-sr.c +++ b/arch/arm64/kvm/hyp/sysreg-sr.c @@ -287,7 +287,7 @@ void kvm_vcpu_put_sysregs(struct kvm_vcpu *vcpu) if (!has_vhe()) return; - deactivate_traps_vhe_put(); + deactivate_traps_vhe_put(vcpu); __sysreg_save_el1_state(guest_ctxt); __sysreg_save_user_state(guest_ctxt); -- Gitee From 9ffc84cc6eb05b83cc5ca75e40759fe99cfb54da Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 5 Apr 2022 19:23:24 +0100 Subject: [PATCH 02/20] irqchip/gic-v3: Exposes bit values for GICR_CTLR.{IR, CES} commit 34453c2e9f79 Upstream As we're about to expose GICR_CTLR.{IR,CES} to guests, populate the include file with the architectural values. Signed-off-by: Marc Zyngier Reviewed-by: Oliver Upton Link: https://lore.kernel.org/r/20220405182327.205520-2-maz@kernel.org Signed-off-by: Xie Xiaodong <624338359@qq.com> --- include/linux/irqchip/arm-gic-v3.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h index 9bf8c0c8b5d5..a24ac1ec1716 100644 --- a/include/linux/irqchip/arm-gic-v3.h +++ b/include/linux/irqchip/arm-gic-v3.h @@ -134,6 +134,8 @@ #define GICR_PIDR2 GICD_PIDR2 #define GICR_CTLR_ENABLE_LPIS (1UL << 0) +#define GICR_CTLR_CES (1UL << 1) +#define GICR_CTLR_IR (1UL << 2) #define GICR_CTLR_RWP (1UL << 3) #define GICR_TYPER_CPU_NUMBER(r) (((r) >> 8) & 0xffff) -- Gitee From dac32c15afe2e4f0fd78483677932ae20b86ce88 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 5 Apr 2022 19:23:25 +0100 Subject: [PATCH 03/20] KVM: arm64: vgic-v3: Expose GICR_CTLR.RWP when disabling LPIs commit 94828468a608 Upstream When disabling LPIs, a guest needs to poll GICR_CTLR.RWP in order to be sure that the write has taken effect. We so far reported it as 0, as we didn't advertise that LPIs could be turned off the first place. Start tracking this state during which LPIs are being disabled, and expose the 'in progress' state via the RWP bit. We also take this opportunity to disallow enabling LPIs and programming GICR_{PEND,PROP}BASER while LPI disabling is in progress, as allowed by the architecture (UNPRED behaviour). We don't advertise the feature to the guest yet (which is allowed by the architecture). Reviewed-by: Oliver Upton Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20220405182327.205520-3-maz@kernel.org Signed-off-by: Xie Xiaodong <624338359@qq.com> --- include/kvm/arm_vgic.h | 4 ++-- virt/kvm/arm/vgic/vgic-its.c | 2 +- virt/kvm/arm/vgic/vgic-mmio-v3.c | 35 ++++++++++++++++++++++++-------- virt/kvm/arm/vgic/vgic.h | 1 + 4 files changed, 31 insertions(+), 11 deletions(-) diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index 317afc404441..d094b29b70b5 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -375,8 +375,8 @@ struct vgic_cpu { /* Contains the attributes and gpa of the LPI pending tables. */ u64 pendbaser; - - bool lpis_enabled; + /* GICR_CTLR.{ENABLE_LPIS,RWP} */ + atomic_t ctlr; /* Cache guest priority bits */ u32 num_pri_bits; diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c index a72f584254d0..ad6cbba74833 100644 --- a/virt/kvm/arm/vgic/vgic-its.c +++ b/virt/kvm/arm/vgic/vgic-its.c @@ -683,7 +683,7 @@ int vgic_its_resolve_lpi(struct kvm *kvm, struct vgic_its *its, if (!vcpu) return E_ITS_INT_UNMAPPED_INTERRUPT; - if (!vcpu->arch.vgic_cpu.lpis_enabled) + if (!vgic_lpis_enabled(vcpu)) return -EBUSY; vgic_its_cache_translation(kvm, its, devid, eventid, ite->irq); diff --git a/virt/kvm/arm/vgic/vgic-mmio-v3.c b/virt/kvm/arm/vgic/vgic-mmio-v3.c index 9f58d383dbc3..beca71a40bb8 100644 --- a/virt/kvm/arm/vgic/vgic-mmio-v3.c +++ b/virt/kvm/arm/vgic/vgic-mmio-v3.c @@ -220,6 +220,13 @@ static void vgic_mmio_write_irouter(struct kvm_vcpu *vcpu, vgic_put_irq(vcpu->kvm, irq); } +bool vgic_lpis_enabled(struct kvm_vcpu *vcpu) +{ + struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; + + return atomic_read(&vgic_cpu->ctlr) == GICR_CTLR_ENABLE_LPIS; +} + static unsigned long vgic_mmio_read_v3r_ctlr(struct kvm_vcpu *vcpu, gpa_t addr, unsigned int len) { @@ -234,20 +241,33 @@ static void vgic_mmio_write_v3r_ctlr(struct kvm_vcpu *vcpu, unsigned long val) { struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; - bool was_enabled = vgic_cpu->lpis_enabled; + u32 ctlr; if (!vgic_has_its(vcpu->kvm)) return; - vgic_cpu->lpis_enabled = val & GICR_CTLR_ENABLE_LPIS; + if (!(val & GICR_CTLR_ENABLE_LPIS)) { + /* + * Don't disable if RWP is set, as there already an + * ongoing disable. Funky guest... + */ + ctlr = atomic_cmpxchg_acquire(&vgic_cpu->ctlr, + GICR_CTLR_ENABLE_LPIS, + GICR_CTLR_RWP); + if (ctlr != GICR_CTLR_ENABLE_LPIS) + return; - if (was_enabled && !vgic_cpu->lpis_enabled) { vgic_flush_pending_lpis(vcpu); vgic_its_invalidate_cache(vcpu->kvm); - } + atomic_set_release(&vgic_cpu->ctlr, 0); + } else { + ctlr = atomic_cmpxchg_acquire(&vgic_cpu->ctlr, 0, + GICR_CTLR_ENABLE_LPIS); + if (ctlr != 0) + return; - if (!was_enabled && vgic_cpu->lpis_enabled) vgic_enable_lpis(vcpu); + } } static unsigned long vgic_mmio_read_v3r_typer(struct kvm_vcpu *vcpu, @@ -485,11 +505,10 @@ static void vgic_mmio_write_propbase(struct kvm_vcpu *vcpu, unsigned long val) { struct vgic_dist *dist = &vcpu->kvm->arch.vgic; - struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; u64 old_propbaser, propbaser; /* Storing a value with LPIs already enabled is undefined */ - if (vgic_cpu->lpis_enabled) + if (vgic_lpis_enabled(vcpu)) return; do { @@ -517,7 +536,7 @@ static void vgic_mmio_write_pendbase(struct kvm_vcpu *vcpu, u64 old_pendbaser, pendbaser; /* Storing a value with LPIs already enabled is undefined */ - if (vgic_cpu->lpis_enabled) + if (vgic_lpis_enabled(vcpu)) return; do { diff --git a/virt/kvm/arm/vgic/vgic.h b/virt/kvm/arm/vgic/vgic.h index 9d98a4345212..a89a6191e9b3 100644 --- a/virt/kvm/arm/vgic/vgic.h +++ b/virt/kvm/arm/vgic/vgic.h @@ -317,6 +317,7 @@ static inline bool vgic_dist_overlap(struct kvm *kvm, gpa_t base, size_t size) (base < d->vgic_dist_base + KVM_VGIC_V3_DIST_SIZE); } +bool vgic_lpis_enabled(struct kvm_vcpu *vcpu); int vgic_copy_lpi_list(struct kvm *kvm, struct kvm_vcpu *vcpu, u32 **intid_ptr); int vgic_its_resolve_lpi(struct kvm *kvm, struct vgic_its *its, u32 devid, u32 eventid, struct vgic_irq **irq); -- Gitee From 32b7d23265fa910748b2d47fa0f5d559957775e1 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 5 Apr 2022 19:23:26 +0100 Subject: [PATCH 04/20] KVM: arm64: vgic-v3: Implement MMIO-based LPI invalidation commit 4645d11f4a55 Upstream Since GICv4.1, it has become legal for an implementation to advertise GICR_{INVLPIR,INVALLR,SYNCR} while having an ITS, allowing for a more efficient invalidation scheme (no guest command queue contention when multiple CPUs are generating invalidations). Provide the invalidation registers as a primitive to their ITS counterpart. Note that we don't advertise them to the guest yet (the architecture allows an implementation to do this). Signed-off-by: Marc Zyngier Reviewed-by: Oliver Upton Link: https://lore.kernel.org/r/20220405182327.205520-4-maz@kernel.org Signed-off-by: Xie Xiaodong <624338359@qq.com> --- include/kvm/arm_vgic.h | 2 +- virt/kvm/arm/vgic/vgic-its.c | 61 +++++++++++++++++++---------- virt/kvm/arm/vgic/vgic-mmio-v3.c | 67 ++++++++++++++++++++++++++++++++ virt/kvm/arm/vgic/vgic.h | 5 +++ 4 files changed, 114 insertions(+), 21 deletions(-) diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index d094b29b70b5..bfb9d59bd4d2 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -372,7 +372,7 @@ struct vgic_cpu { */ struct vgic_io_device rd_iodev; struct vgic_redist_region *rdreg; - + atomic_t syncr_busy; /* Contains the attributes and gpa of the LPI pending tables. */ u64 pendbaser; /* GICR_CTLR.{ENABLE_LPIS,RWP} */ diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c index ad6cbba74833..cd09a41c8760 100644 --- a/virt/kvm/arm/vgic/vgic-its.c +++ b/virt/kvm/arm/vgic/vgic-its.c @@ -1273,6 +1273,11 @@ static int vgic_its_cmd_handle_clear(struct kvm *kvm, struct vgic_its *its, return 0; } +int vgic_its_inv_lpi(struct kvm *kvm, struct vgic_irq *irq) +{ + return update_lpi_config(kvm, irq, NULL, true); +} + /* * The INV command syncs the configuration bits from the memory table. * Must be called with the its_lock mutex held. @@ -1289,9 +1294,43 @@ static int vgic_its_cmd_handle_inv(struct kvm *kvm, struct vgic_its *its, if (!ite) return E_ITS_INV_UNMAPPED_INTERRUPT; - return update_lpi_config(kvm, ite->irq, NULL, true); + return vgic_its_inv_lpi(kvm, ite->irq); } +/** + * vgic_its_invall - invalidate all LPIs targetting a given vcpu + * @vcpu: the vcpu for which the RD is targetted by an invalidation + * + * Contrary to the INVALL command, this targets a RD instead of a + * collection, and we don't need to hold the its_lock, since no ITS is + * involved here. + */ +int vgic_its_invall(struct kvm_vcpu *vcpu) +{ + struct kvm *kvm = vcpu->kvm; + int irq_count, i = 0; + u32 *intids; + + irq_count = vgic_copy_lpi_list(kvm, vcpu, &intids); + if (irq_count < 0) + return irq_count; + + for (i = 0; i < irq_count; i++) { + struct vgic_irq *irq = vgic_get_irq(kvm, NULL, intids[i]); + if (!irq) + continue; + update_lpi_config(kvm, irq, vcpu, false); + vgic_put_irq(kvm, irq); + } + + kfree(intids); + + if (vcpu->arch.vgic_cpu.vgic_v3.its_vpe.its_vm) + its_invall_vpe(&vcpu->arch.vgic_cpu.vgic_v3.its_vpe); + + return 0; + } + /* * The INVALL command requests flushing of all IRQ data in this collection. * Find the VCPU mapped to that collection, then iterate over the VM's list @@ -1306,9 +1345,6 @@ static int vgic_its_cmd_handle_invall(struct kvm *kvm, struct vgic_its *its, u32 coll_id = its_cmd_get_collection(its_cmd); struct its_collection *collection; struct kvm_vcpu *vcpu; - struct vgic_irq *irq; - u32 *intids; - int irq_count, i; collection = find_collection(its, coll_id); if (!its_is_collection_mapped(collection)) @@ -1316,22 +1352,7 @@ static int vgic_its_cmd_handle_invall(struct kvm *kvm, struct vgic_its *its, vcpu = kvm_get_vcpu(kvm, collection->target_addr); - irq_count = vgic_copy_lpi_list(kvm, vcpu, &intids); - if (irq_count < 0) - return irq_count; - - for (i = 0; i < irq_count; i++) { - irq = vgic_get_irq(kvm, NULL, intids[i]); - if (!irq) - continue; - update_lpi_config(kvm, irq, vcpu, false); - vgic_put_irq(kvm, irq); - } - - kfree(intids); - - if (vcpu->arch.vgic_cpu.vgic_v3.its_vpe.its_vm) - its_invall_vpe(&vcpu->arch.vgic_cpu.vgic_v3.its_vpe); + vgic_its_invall(vcpu); return 0; } diff --git a/virt/kvm/arm/vgic/vgic-mmio-v3.c b/virt/kvm/arm/vgic/vgic-mmio-v3.c index beca71a40bb8..707a9cef07d3 100644 --- a/virt/kvm/arm/vgic/vgic-mmio-v3.c +++ b/virt/kvm/arm/vgic/vgic-mmio-v3.c @@ -548,6 +548,64 @@ static void vgic_mmio_write_pendbase(struct kvm_vcpu *vcpu, pendbaser) != old_pendbaser); } +static unsigned long vgic_mmio_read_sync(struct kvm_vcpu *vcpu, + gpa_t addr, unsigned int len) +{ + return !!atomic_read(&vcpu->arch.vgic_cpu.syncr_busy); +} + +static void vgic_set_rdist_busy(struct kvm_vcpu *vcpu, bool busy) +{ + if (busy) { + atomic_inc(&vcpu->arch.vgic_cpu.syncr_busy); + smp_mb__after_atomic(); + } else { + smp_mb__before_atomic(); + atomic_dec(&vcpu->arch.vgic_cpu.syncr_busy); + } +} + +static void vgic_mmio_write_invlpi(struct kvm_vcpu *vcpu, + gpa_t addr, unsigned int len, + unsigned long val) +{ + struct vgic_irq *irq; + + /* + * If the guest wrote only to the upper 32bit part of the + * register, drop the write on the floor, as it is only for + * vPEs (which we don't support for obvious reasons). + * + * Also discard the access if LPIs are not enabled. + */ + if ((addr & 4) || !vgic_lpis_enabled(vcpu)) + return; + + vgic_set_rdist_busy(vcpu, true); + + irq = vgic_get_irq(vcpu->kvm, NULL, lower_32_bits(val)); + if (irq) { + vgic_its_inv_lpi(vcpu->kvm, irq); + vgic_put_irq(vcpu->kvm, irq); + } + + vgic_set_rdist_busy(vcpu, false); +} + +static void vgic_mmio_write_invall(struct kvm_vcpu *vcpu, + gpa_t addr, unsigned int len, + unsigned long val) +{ + /* See vgic_mmio_write_invlpi() for the early return rationale */ + if ((addr & 4) || !vgic_lpis_enabled(vcpu)) + return; + + vgic_set_rdist_busy(vcpu, true); + vgic_its_invall(vcpu); + vgic_set_rdist_busy(vcpu, false); +} + + /* * The GICv3 per-IRQ registers are split to control PPIs and SGIs in the * redistributors, while SPIs are covered by registers in the distributor @@ -651,6 +709,15 @@ static const struct vgic_register_region vgic_v3_rd_registers[] = { REGISTER_DESC_WITH_LENGTH(GICR_PENDBASER, vgic_mmio_read_pendbase, vgic_mmio_write_pendbase, 8, VGIC_ACCESS_64bit | VGIC_ACCESS_32bit), + REGISTER_DESC_WITH_LENGTH(GICR_INVLPIR, + vgic_mmio_read_raz, vgic_mmio_write_invlpi, 8, + VGIC_ACCESS_64bit | VGIC_ACCESS_32bit), + REGISTER_DESC_WITH_LENGTH(GICR_INVALLR, + vgic_mmio_read_raz, vgic_mmio_write_invall, 8, + VGIC_ACCESS_64bit | VGIC_ACCESS_32bit), + REGISTER_DESC_WITH_LENGTH(GICR_SYNCR, + vgic_mmio_read_sync, vgic_mmio_write_wi, 4, + VGIC_ACCESS_32bit), REGISTER_DESC_WITH_LENGTH(GICR_IDREGS, vgic_mmio_read_v3_idregs, vgic_mmio_write_wi, 48, VGIC_ACCESS_32bit), diff --git a/virt/kvm/arm/vgic/vgic.h b/virt/kvm/arm/vgic/vgic.h index a89a6191e9b3..5b3bffbae08a 100644 --- a/virt/kvm/arm/vgic/vgic.h +++ b/virt/kvm/arm/vgic/vgic.h @@ -327,6 +327,11 @@ void vgic_lpi_translation_cache_init(struct kvm *kvm); void vgic_lpi_translation_cache_destroy(struct kvm *kvm); void vgic_its_invalidate_cache(struct kvm *kvm); +/* GICv4.1 MMIO interface */ +int vgic_its_inv_lpi(struct kvm *kvm, struct vgic_irq *irq); +int vgic_its_invall(struct kvm_vcpu *vcpu); + + bool vgic_supports_direct_msis(struct kvm *kvm); int vgic_v4_init(struct kvm *kvm); void vgic_v4_teardown(struct kvm *kvm); -- Gitee From efa80792bc22019ced531e8356141d4ac9b4792e Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 5 Apr 2022 19:23:27 +0100 Subject: [PATCH 05/20] KVM: arm64: vgic-v3: Advertise GICR_CTLR.{IR, CES} as a new GICD_IIDR revision commit 49a1a2c70a7f Upstream Since adversising GICR_CTLR.{IC,CES} is directly observable from a guest, we need to make it selectable from userspace. For that, bump the default GICD_IIDR revision and let userspace downgrade it to the previous default. For GICv2, the two distributor revisions are strictly equivalent. Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20220405182327.205520-5-maz@kernel.org Signed-off-by: Xie Xiaodong <624338359@qq.com> --- include/kvm/arm_vgic.h | 3 +++ virt/kvm/arm/vgic/vgic-init.c | 7 ++++++- virt/kvm/arm/vgic/vgic-mmio-v2.c | 18 +++++++++++++++--- virt/kvm/arm/vgic/vgic-mmio-v3.c | 22 ++++++++++++++++++++-- virt/kvm/arm/vgic/vgic.h | 5 +++++ 5 files changed, 49 insertions(+), 6 deletions(-) diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index bfb9d59bd4d2..0f6cec99a0c6 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -249,6 +249,9 @@ struct vgic_dist { /* Implementation revision as reported in the GICD_IIDR */ u32 implementation_rev; +#define KVM_VGIC_IMP_REV_2 2 /* GICv2 restorable groups */ +#define KVM_VGIC_IMP_REV_3 3 /* GICv3 GICR_CTLR.{IW,CES,RWP} */ +#define KVM_VGIC_IMP_REV_LATEST KVM_VGIC_IMP_REV_3 /* Userspace can write to GICv2 IGROUPR */ bool v2_groups_user_writable; diff --git a/virt/kvm/arm/vgic/vgic-init.c b/virt/kvm/arm/vgic/vgic-init.c index 3d04b3c53984..f5805dc1ecf7 100644 --- a/virt/kvm/arm/vgic/vgic-init.c +++ b/virt/kvm/arm/vgic/vgic-init.c @@ -331,7 +331,12 @@ int vgic_init(struct kvm *kvm) vgic_debug_init(kvm); - dist->implementation_rev = 2; + /* + * If userspace didn't set the GIC implementation revision, + * default to the latest and greatest. You know want it. + */ + if (!dist->implementation_rev) + dist->implementation_rev = KVM_VGIC_IMP_REV_LATEST; dist->initialized = true; out: diff --git a/virt/kvm/arm/vgic/vgic-mmio-v2.c b/virt/kvm/arm/vgic/vgic-mmio-v2.c index d63881f60e1a..0c597f24e337 100644 --- a/virt/kvm/arm/vgic/vgic-mmio-v2.c +++ b/virt/kvm/arm/vgic/vgic-mmio-v2.c @@ -73,9 +73,13 @@ static int vgic_mmio_uaccess_write_v2_misc(struct kvm_vcpu *vcpu, gpa_t addr, unsigned int len, unsigned long val) { + struct vgic_dist *dist = &vcpu->kvm->arch.vgic; + u32 reg; + switch (addr & 0x0c) { case GIC_DIST_IIDR: - if (val != vgic_mmio_read_v2_misc(vcpu, addr, len)) + reg = vgic_mmio_read_v2_misc(vcpu, addr, len); + if ((reg ^ val) & ~GICD_IIDR_REVISION_MASK) return -EINVAL; /* @@ -87,8 +91,16 @@ static int vgic_mmio_uaccess_write_v2_misc(struct kvm_vcpu *vcpu, * migration from old kernels to new kernels with legacy * userspace. */ - vcpu->kvm->arch.vgic.v2_groups_user_writable = true; - return 0; + reg = FIELD_GET(GICD_IIDR_REVISION_MASK, reg); + switch (reg) { + case KVM_VGIC_IMP_REV_2: + case KVM_VGIC_IMP_REV_3: + vcpu->kvm->arch.vgic.v2_groups_user_writable = true; + dist->implementation_rev = reg; + return 0; + default: + return -EINVAL; + } } vgic_mmio_write_v2_misc(vcpu, addr, len, val); diff --git a/virt/kvm/arm/vgic/vgic-mmio-v3.c b/virt/kvm/arm/vgic/vgic-mmio-v3.c index 707a9cef07d3..6c82a78d1140 100644 --- a/virt/kvm/arm/vgic/vgic-mmio-v3.c +++ b/virt/kvm/arm/vgic/vgic-mmio-v3.c @@ -154,13 +154,27 @@ static int vgic_mmio_uaccess_write_v3_misc(struct kvm_vcpu *vcpu, unsigned long val) { struct vgic_dist *dist = &vcpu->kvm->arch.vgic; + u32 reg; switch (addr & 0x0c) { case GICD_TYPER2: - case GICD_IIDR: if (val != vgic_mmio_read_v3_misc(vcpu, addr, len)) return -EINVAL; return 0; + case GICD_IIDR: + reg = vgic_mmio_read_v3_misc(vcpu, addr, len); + if ((reg ^ val) & ~GICD_IIDR_REVISION_MASK) + return -EINVAL; + + reg = FIELD_GET(GICD_IIDR_REVISION_MASK, reg); + switch (reg) { + case KVM_VGIC_IMP_REV_2: + case KVM_VGIC_IMP_REV_3: + dist->implementation_rev = reg; + return 0; + default: + return -EINVAL; + } case GICD_CTLR: /* Not a GICv4.1? No HW SGIs */ if (!kvm_vgic_global_state.has_gicv4_1) @@ -231,8 +245,12 @@ static unsigned long vgic_mmio_read_v3r_ctlr(struct kvm_vcpu *vcpu, gpa_t addr, unsigned int len) { struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; + unsigned long val; - return vgic_cpu->lpis_enabled ? GICR_CTLR_ENABLE_LPIS : 0; + val = atomic_read(&vgic_cpu->ctlr); + if (vgic_get_implementation_rev(vcpu) >= KVM_VGIC_IMP_REV_3) + val |= GICR_CTLR_IR | GICR_CTLR_CES; + return val; } diff --git a/virt/kvm/arm/vgic/vgic.h b/virt/kvm/arm/vgic/vgic.h index 5b3bffbae08a..14a759203571 100644 --- a/virt/kvm/arm/vgic/vgic.h +++ b/virt/kvm/arm/vgic/vgic.h @@ -98,6 +98,11 @@ #define DEBUG_SPINLOCK_BUG_ON(p) #endif +static inline u32 vgic_get_implementation_rev(struct kvm_vcpu *vcpu) +{ + return vcpu->kvm->arch.vgic.implementation_rev; +} + /* Requires the irq_lock to be held by the caller. */ static inline bool irq_is_pending(struct vgic_irq *irq) { -- Gitee From 6e8cb5856680151b1df9764fb369e82ecfaafca1 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 9 Apr 2024 09:21:57 +0800 Subject: [PATCH 06/20] KVM: arm64: vgic-v3: Upgrade AP1Rn to 64bit. commit d46e52452afc8f097ccc91555a429928ddfdc8a7 openEuler With the advent of FEAT_GIC_NMI, ICH_AP1R0_EL2 is now a 64bit register, as the NMI priority is encoded in bit 63. Upgrade the whole of the AP1Rn array to 64bit, leaving the Group0 equivalent to 32bit. Signed-off-by: Marc Zyngier Signed-off-by: Xiang Chen Signed-off-by: caijian Signed-off-by: Xie Xiaodong <624338359@qq.com> --- arch/arm64/kvm/vgic-sys-reg-v3.c | 21 +++++++++++---------- include/kvm/arm_vgic.h | 2 +- virt/kvm/arm/hyp/vgic-v3-sr.c | 11 ++++++++--- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/arch/arm64/kvm/vgic-sys-reg-v3.c b/arch/arm64/kvm/vgic-sys-reg-v3.c index e7d1ea92095d..59fedc2488ce 100644 --- a/arch/arm64/kvm/vgic-sys-reg-v3.c +++ b/arch/arm64/kvm/vgic-sys-reg-v3.c @@ -184,17 +184,18 @@ static void vgic_v3_access_apr_reg(struct kvm_vcpu *vcpu, struct sys_reg_params *p, u8 apr, u8 idx) { struct vgic_v3_cpu_if *vgicv3 = &vcpu->arch.vgic_cpu.vgic_v3; - uint32_t *ap_reg; - if (apr) - ap_reg = &vgicv3->vgic_ap1r[idx]; - else - ap_reg = &vgicv3->vgic_ap0r[idx]; - - if (p->is_write) - *ap_reg = p->regval; - else - p->regval = *ap_reg; + if (apr) { + if (p->is_write) + vgicv3->vgic_ap1r[idx] = p->regval; + else + p->regval = vgicv3->vgic_ap1r[idx]; + } else { + if (p->is_write) + vgicv3->vgic_ap0r[idx] = p->regval; + else + p->regval = vgicv3->vgic_ap0r[idx]; + } } static bool access_gic_aprn(struct kvm_vcpu *vcpu, struct sys_reg_params *p, diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index 0f6cec99a0c6..50a726cdd808 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -331,7 +331,7 @@ struct vgic_v3_cpu_if { u32 vgic_vmcr; u32 vgic_sre; /* Restored only, change ignored */ u32 vgic_ap0r[4]; - u32 vgic_ap1r[4]; + u64 vgic_ap1r[4]; u64 vgic_lr[VGIC_V3_MAX_LRS]; /* diff --git a/virt/kvm/arm/hyp/vgic-v3-sr.c b/virt/kvm/arm/hyp/vgic-v3-sr.c index ccf1fde9836c..b2d0e2c1cbe6 100644 --- a/virt/kvm/arm/hyp/vgic-v3-sr.c +++ b/virt/kvm/arm/hyp/vgic-v3-sr.c @@ -128,7 +128,11 @@ static void __hyp_text __vgic_v3_write_ap0rn(u32 val, int n) } } -static void __hyp_text __vgic_v3_write_ap1rn(u32 val, int n) +/* + * Contrary to ICH_AP0Rn_EL2, ICH_AP1R0_EL2 is 64bit, thanks to the + * NMI bit stuck at [63]. Isn't that fun? + */ +static void __vgic_v3_write_ap1rn(u64 val, int n) { switch (n) { case 0: @@ -170,9 +174,10 @@ static u32 __hyp_text __vgic_v3_read_ap0rn(int n) return val; } -static u32 __hyp_text __vgic_v3_read_ap1rn(int n) +/* Same remark about the 64bit-ness of AP1R0 */ +static u64 __vgic_v3_read_ap1rn(int n) { - u32 val; + u64 val; switch (n) { case 0: -- Gitee From a80286c1b217c2b89080499436cb3ff9c45b7a0c Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 9 Apr 2024 09:21:58 +0800 Subject: [PATCH 07/20] KVM: arm64: vgic-v3: Allow the NMI state to make it into the LRs commit 7b2984c427395f40cfc3154d7484f2f11052a9e2 openEuler Add the new NMI state to the vgic IRQ state, and allow it to make it into the LRs. Nothing can set it yet, so no impact is expected from this change. Signed-off-by: Marc Zyngier Signed-off-by: Xiang Chen Signed-off-by: caijian Signed-off-by: Xie Xiaodong <624338359@qq.com> --- include/kvm/arm_vgic.h | 1 + include/linux/irqchip/arm-gic-v3.h | 1 + virt/kvm/arm/vgic/vgic-v3.c | 5 ++++- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index 50a726cdd808..eb188d86945b 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -148,6 +148,7 @@ struct vgic_irq { bool active; /* not used for LPIs */ bool enabled; bool hw; /* Tied to HW IRQ */ + bool nmi; /* Configured as NMI */ struct kref refcount; /* Used for LPIs */ u32 hwintid; /* HW INTID number */ unsigned int host_irq; /* linux irq corresponding to hwintid */ diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h index a24ac1ec1716..c965119f72ef 100644 --- a/include/linux/irqchip/arm-gic-v3.h +++ b/include/linux/irqchip/arm-gic-v3.h @@ -611,6 +611,7 @@ #define ICH_LR_VIRTUAL_ID_MASK ((1ULL << 32) - 1) #define ICH_LR_EOI (1ULL << 41) +#define ICH_LR_NMI (1ULL << 59) #define ICH_LR_GROUP (1ULL << 60) #define ICH_LR_HW (1ULL << 61) #define ICH_LR_STATE (3ULL << 62) diff --git a/virt/kvm/arm/vgic/vgic-v3.c b/virt/kvm/arm/vgic/vgic-v3.c index 909c5934a664..edabb3ddd421 100644 --- a/virt/kvm/arm/vgic/vgic-v3.c +++ b/virt/kvm/arm/vgic/vgic-v3.c @@ -194,7 +194,10 @@ void vgic_v3_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr) if (irq->group) val |= ICH_LR_GROUP; - val |= (u64)irq->priority << ICH_LR_PRIORITY_SHIFT; + if (irq->nmi) + val |= ICH_LR_NMI; + else + val |= (u64)irq->priority << ICH_LR_PRIORITY_SHIFT; vcpu->arch.vgic_cpu.vgic_v3.vgic_lr[lr] = val; } -- Gitee From 0dc023dd6a4632506d54c51d97d837bdf6a0fa2f Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 9 Apr 2024 09:21:59 +0800 Subject: [PATCH 08/20] KVM: arm64: vgic-v3: Make NMI priority RES0 commit 49c1f33275e1e0afd37d2b2a1c3faae1baa2202f openEuler The priority of an NMI is always RES0. Let's enforce it when the guest accesses the priority MMIO range. Signed-off-by: Marc Zyngier Signed-off-by: Xiang Chen Signed-off-by: caijian Signed-off-by: Xie Xiaodong <624338359@qq.com> --- virt/kvm/arm/vgic/vgic-mmio.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/virt/kvm/arm/vgic/vgic-mmio.c b/virt/kvm/arm/vgic/vgic-mmio.c index 949408af409c..a31c8f62b6b4 100644 --- a/virt/kvm/arm/vgic/vgic-mmio.c +++ b/virt/kvm/arm/vgic/vgic-mmio.c @@ -423,7 +423,7 @@ static unsigned long __vgic_mmio_read_active(struct kvm_vcpu *vcpu, */ #ifdef CONFIG_VIRT_VTIMER_IRQ_BYPASS if (state) -#else +#else if (irq->active) #endif value |= (1U << i); @@ -598,13 +598,17 @@ unsigned long vgic_mmio_read_priority(struct kvm_vcpu *vcpu, gpa_t addr, unsigned int len) { u32 intid = VGIC_ADDR_TO_INTID(addr, 8); + unsigned long flags; int i; u64 val = 0; for (i = 0; i < len; i++) { struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); - val |= (u64)irq->priority << (i * 8); + raw_spin_lock_irqsave(&irq->irq_lock, flags); + if (!irq->nmi) + val |= (u64)irq->priority << (i * 8); + raw_spin_unlock_irqrestore(&irq->irq_lock, flags); vgic_put_irq(vcpu->kvm, irq); } @@ -631,10 +635,16 @@ void vgic_mmio_write_priority(struct kvm_vcpu *vcpu, struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); raw_spin_lock_irqsave(&irq->irq_lock, flags); - /* Narrow the priority range to what we actually support */ - irq->priority = (val >> (i * 8)) & GENMASK(7, 8 - VGIC_PRI_BITS); - if (vgic_direct_sgi_or_ppi(irq)) - vgic_update_vsgi(irq); + if (!irq->nmi) { + /* + * Narrow the priority range to what we + * actually support + */ + irq->priority = (val >> (i * 8)) & + GENMASK(7, 8 - VGIC_PRI_BITS); + if (vgic_direct_sgi_or_ppi(irq)) + vgic_update_vsgi(irq); + } raw_spin_unlock_irqrestore(&irq->irq_lock, flags); vgic_put_irq(vcpu->kvm, irq); -- Gitee From e88500a1aed7d72b1d87febba749cc5e89cde945 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 9 Apr 2024 09:22:00 +0800 Subject: [PATCH 09/20] KVM: arm64: vgic-v4: Propagate the NMI state into the GICv4.1 VSGI configuration commit c35e09506c28e7cf17b0a699f72c490d377343b6 openEuler Just as we now allow the NMI state to make it into the LRs, allow the same state to be propagated into the VSGI configuration. Signed-off-by: Marc Zyngier Signed-off-by: Xiang Chen Signed-off-by: caijian Signed-off-by: Xie Xiaodong <624338359@qq.com> --- drivers/irqchip/irq-gic-v3-its.c | 9 +++++++++ drivers/irqchip/irq-gic-v4.c | 3 ++- include/linux/irqchip/arm-gic-v4.h | 4 +++- virt/kvm/arm/vgic/vgic-mmio.c | 6 ++++-- virt/kvm/arm/vgic/vgic-v4.c | 1 + virt/kvm/arm/vgic/vgic.h | 1 + 6 files changed, 20 insertions(+), 4 deletions(-) diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 9e421c7f1c07..c57925533c8a 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -672,6 +672,7 @@ struct its_cmd_desc { u8 sgi; u8 priority; bool enable; + bool nmi; bool group; bool clear; } its_vsgi_cmd; @@ -842,6 +843,11 @@ static void its_encode_sgi_priority(struct its_cmd_block *cmd, u8 prio) its_mask_encode(&cmd->raw_cmd[0], prio >> 4, 23, 20); } +static void its_encode_sgi_nmi(struct its_cmd_block *cmd, bool nmi) +{ + its_mask_encode(&cmd->raw_cmd[0], nmi, 11, 11); +} + static void its_encode_sgi_group(struct its_cmd_block *cmd, bool grp) { its_mask_encode(&cmd->raw_cmd[0], grp, 10, 10); @@ -1247,6 +1253,7 @@ static struct its_vpe *its_build_vsgi_cmd(struct its_node *its, its_encode_sgi_intid(cmd, desc->its_vsgi_cmd.sgi); #endif its_encode_sgi_priority(cmd, desc->its_vsgi_cmd.priority); + its_encode_sgi_nmi(cmd, desc->its_vsgi_cmd.nmi); its_encode_sgi_group(cmd, desc->its_vsgi_cmd.group); its_encode_sgi_clear(cmd, desc->its_vsgi_cmd.clear); its_encode_sgi_enable(cmd, desc->its_vsgi_cmd.enable); @@ -4521,6 +4528,7 @@ static void its_configure_sgi(struct irq_data *d, bool clear) desc.its_vsgi_cmd.priority = vpe->sgi_config[d->hwirq].priority; desc.its_vsgi_cmd.enable = vpe->sgi_config[d->hwirq].enabled; desc.its_vsgi_cmd.group = vpe->sgi_config[d->hwirq].group; + desc.its_vsgi_cmd.nmi = vpe->sgi_config[d->hwirq].nmi; desc.its_vsgi_cmd.clear = clear; /* @@ -4679,6 +4687,7 @@ static int its_sgi_set_vcpu_affinity(struct irq_data *d, void *vcpu_info) case PROP_UPDATE_VSGI: vpe->sgi_config[d->hwirq].priority = info->priority; vpe->sgi_config[d->hwirq].group = info->group; + vpe->sgi_config[d->hwirq].nmi = info->nmi; its_configure_sgi(d, false); return 0; diff --git a/drivers/irqchip/irq-gic-v4.c b/drivers/irqchip/irq-gic-v4.c index 21b822e201ce..7941da37fc80 100644 --- a/drivers/irqchip/irq-gic-v4.c +++ b/drivers/irqchip/irq-gic-v4.c @@ -398,13 +398,14 @@ int its_prop_update_vlpi(int irq, u8 config, bool inv) return irq_set_vcpu_affinity(irq, &info); } -int its_prop_update_vsgi(int irq, u8 priority, bool group) +int its_prop_update_vsgi(int irq, u8 priority, bool group, bool nmi) { struct its_cmd_info info = { .cmd_type = PROP_UPDATE_VSGI, { .priority = priority, .group = group, + .nmi = nmi, }, }; diff --git a/include/linux/irqchip/arm-gic-v4.h b/include/linux/irqchip/arm-gic-v4.h index 07e8db2aa449..d31c085662b2 100644 --- a/include/linux/irqchip/arm-gic-v4.h +++ b/include/linux/irqchip/arm-gic-v4.h @@ -57,6 +57,7 @@ struct its_vpe { u8 priority; bool enabled; bool group; + bool nmi; #ifdef CONFIG_VIRT_VTIMER_IRQ_BYPASS } sgi_config[32]; int nr_irqs; @@ -131,6 +132,7 @@ struct its_cmd_info { struct { u8 priority; bool group; + bool nmi; }; }; }; @@ -145,7 +147,7 @@ int its_map_vlpi(int irq, struct its_vlpi_map *map); int its_get_vlpi(int irq, struct its_vlpi_map *map); int its_unmap_vlpi(int irq); int its_prop_update_vlpi(int irq, u8 config, bool inv); -int its_prop_update_vsgi(int irq, u8 priority, bool group); +int its_prop_update_vsgi(int irq, u8 priority, bool group, bool nmi); struct irq_domain_ops; int its_init_v4(struct irq_domain *domain, diff --git a/virt/kvm/arm/vgic/vgic-mmio.c b/virt/kvm/arm/vgic/vgic-mmio.c index a31c8f62b6b4..b5698c562056 100644 --- a/virt/kvm/arm/vgic/vgic-mmio.c +++ b/virt/kvm/arm/vgic/vgic-mmio.c @@ -61,9 +61,11 @@ unsigned long vgic_mmio_read_group(struct kvm_vcpu *vcpu, return value; } -static void vgic_update_vsgi(struct vgic_irq *irq) +void vgic_update_vsgi(struct vgic_irq *irq) { - WARN_ON(its_prop_update_vsgi(irq->host_irq, irq->priority, irq->group)); + WARN_ON(its_prop_update_vsgi(irq->host_irq, + irq->nmi ? 0 : irq->priority, + irq->group, irq->nmi)); } void vgic_mmio_write_group(struct kvm_vcpu *vcpu, gpa_t addr, diff --git a/virt/kvm/arm/vgic/vgic-v4.c b/virt/kvm/arm/vgic/vgic-v4.c index 60c758b54fac..b47104e011cd 100644 --- a/virt/kvm/arm/vgic/vgic-v4.c +++ b/virt/kvm/arm/vgic/vgic-v4.c @@ -110,6 +110,7 @@ static void vgic_v4_sync_sgi_config(struct its_vpe *vpe, struct vgic_irq *irq) vpe->sgi_config[irq->intid].enabled = irq->enabled; vpe->sgi_config[irq->intid].group = irq->group; vpe->sgi_config[irq->intid].priority = irq->priority; + vpe->sgi_config[irq->intid].nmi = irq->nmi; } static void vgic_v4_enable_vsgis(struct kvm_vcpu *vcpu) diff --git a/virt/kvm/arm/vgic/vgic.h b/virt/kvm/arm/vgic/vgic.h index 14a759203571..7592f04c5234 100644 --- a/virt/kvm/arm/vgic/vgic.h +++ b/virt/kvm/arm/vgic/vgic.h @@ -341,6 +341,7 @@ bool vgic_supports_direct_msis(struct kvm *kvm); int vgic_v4_init(struct kvm *kvm); void vgic_v4_teardown(struct kvm *kvm); void vgic_v4_configure_vsgis(struct kvm *kvm); +void vgic_update_vsgi(struct vgic_irq *irq); void vgic_v4_get_vlpi_state(struct vgic_irq *irq, bool *val); int vgic_v4_request_vpe_irq(struct kvm_vcpu *vcpu, int irq); -- Gitee From 1e2c2901ed4792cf6f7425d30ca8ebcb3d7c251c Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 9 Apr 2024 09:22:01 +0800 Subject: [PATCH 10/20] KVM: arm64: vgic-v3: Use the NMI attribute as part of the AP-list sorting commit efd9735b630ca7d7708294de4ff93bbe00106e7d openEuler Since we want NMIs to make it quicker into the LRs, add them to the priority sorting. Signed-off-by: Marc Zyngier Signed-off-by: Xiang Chen Signed-off-by: caijian Signed-off-by: Xie Xiaodong <624338359@qq.com> --- virt/kvm/arm/vgic/vgic.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/virt/kvm/arm/vgic/vgic.c b/virt/kvm/arm/vgic/vgic.c index 804e9cc9867f..bac9feb52e36 100644 --- a/virt/kvm/arm/vgic/vgic.c +++ b/virt/kvm/arm/vgic/vgic.c @@ -251,6 +251,7 @@ static struct kvm_vcpu *vgic_target_oracle(struct vgic_irq *irq) * * Otherwise things should be sorted by the priority field and the GIC * hardware support will take care of preemption of priority groups etc. + * NMI acts as a super-priority. * * Return negative if "a" sorts before "b", 0 to preserve order, and positive * to sort "b" before "a". @@ -285,7 +286,11 @@ static int vgic_irq_cmp(void *priv, struct list_head *a, struct list_head *b) goto out; } - /* Both pending and enabled, sort by priority */ + /* Both pending and enabled, sort by NMI and then priority */ + if (irqa->nmi != irqb->nmi) { + ret = (int)irqb->nmi - (int)irqa->nmi; + goto out; + } ret = irqa->priority - irqb->priority; out: raw_spin_unlock(&irqb->irq_lock); -- Gitee From 0f73b0da38195afea8d6be83098e4491a38ac515 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 9 Apr 2024 09:22:02 +0800 Subject: [PATCH 11/20] KVM: arm64: vgic-v3: Add support for GIC{D,R}_INMIR registers commit 620365f70542a72df1a146b75b6447fa6bb79a7a openEuler Plumb the distributor and redistributor NMI configuration registerse into the MMIO spaces. The update to the registers are gated by a distributor flag which is never set, so there is still no observable behaviour change... Signed-off-by: Marc Zyngier Signed-off-by: Xiang Chen Signed-off-by: caijian Signed-off-by: Xie Xiaodong <624338359@qq.com> --- include/kvm/arm_vgic.h | 1 + virt/kvm/arm/vgic/vgic-mmio-v3.c | 55 ++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index eb188d86945b..0168eb599f9b 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -285,6 +285,7 @@ struct vgic_dist { struct vgic_io_device dist_iodev; + bool has_nmi; bool has_its; /* diff --git a/virt/kvm/arm/vgic/vgic-mmio-v3.c b/virt/kvm/arm/vgic/vgic-mmio-v3.c index 6c82a78d1140..e24f4858c060 100644 --- a/virt/kvm/arm/vgic/vgic-mmio-v3.c +++ b/virt/kvm/arm/vgic/vgic-mmio-v3.c @@ -624,6 +624,55 @@ static void vgic_mmio_write_invall(struct kvm_vcpu *vcpu, } +static unsigned long vgic_mmio_read_nmi(struct kvm_vcpu *vcpu, + gpa_t addr, unsigned int len) +{ + u32 intid = VGIC_ADDR_TO_INTID(addr, 1); + u32 value = 0; + int i; + + /* Loop over all IRQs affected by this read */ + for (i = 0; i < len * 8; i++) { + struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); + + if (irq->nmi) + value |= (1U << i); + + vgic_put_irq(vcpu->kvm, irq); + } + + return value; +} + +static void vgic_mmio_write_nmi(struct kvm_vcpu *vcpu, gpa_t addr, + unsigned int len, unsigned long val) +{ + u32 intid = VGIC_ADDR_TO_INTID(addr, 1); + unsigned long flags; + int i; + + if (!vcpu->kvm->arch.vgic.has_nmi) + return; + + for (i = 0; i < len * 8; i++) { + struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); + bool was_nmi; + + raw_spin_lock_irqsave(&irq->irq_lock, flags); + + was_nmi = irq->nmi; + irq->nmi = (val & BIT(i)); + + if (irq->hw && vgic_irq_is_sgi(irq->intid) && + was_nmi != irq->nmi) + vgic_update_vsgi(irq); + + raw_spin_unlock_irqrestore(&irq->irq_lock, flags); + + vgic_put_irq(vcpu->kvm, irq); + } +} + /* * The GICv3 per-IRQ registers are split to control PPIs and SGIs in the * redistributors, while SPIs are covered by registers in the distributor @@ -695,6 +744,9 @@ static const struct vgic_register_region vgic_v3_dist_registers[] = { REGISTER_DESC_WITH_BITS_PER_IRQ_SHARED(GICD_IGRPMODR, vgic_mmio_read_raz, vgic_mmio_write_wi, NULL, NULL, 1, VGIC_ACCESS_32bit), + REGISTER_DESC_WITH_BITS_PER_IRQ_SHARED(GICD_INMIR, + vgic_mmio_read_nmi, vgic_mmio_write_nmi, NULL, NULL, 1, + VGIC_ACCESS_32bit), REGISTER_DESC_WITH_BITS_PER_IRQ_SHARED(GICD_IROUTER, vgic_mmio_read_irouter, vgic_mmio_write_irouter, NULL, NULL, 64, VGIC_ACCESS_64bit | VGIC_ACCESS_32bit), @@ -777,6 +829,9 @@ static const struct vgic_register_region vgic_v3_rd_registers[] = { REGISTER_DESC_WITH_LENGTH(SZ_64K + GICR_NSACR, vgic_mmio_read_raz, vgic_mmio_write_wi, 4, VGIC_ACCESS_32bit), + REGISTER_DESC_WITH_LENGTH(SZ_64K + GICR_INMIR0, + vgic_mmio_read_nmi, vgic_mmio_write_nmi, 4, + VGIC_ACCESS_32bit), }; unsigned int vgic_v3_init_dist_iodev(struct vgic_io_device *dev) -- Gitee From 1fddacaa59c0472e462180587df0a202f7f6ee5c Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 9 Apr 2024 09:22:03 +0800 Subject: [PATCH 12/20] KVM: arm64: vgic-v3: Add userspace selection for GICv3.3 NMI commit ba88649d31ff542c4b05616accc8d431338bf266 openEuler In order to allow the GIC NMI support to be selected from userspace, add userspace access to GICD_TYPER, which allows the NMI support bit to be set. This is gated by a global capability that nobody can set yet, so no observable change is expected. Again. Signed-off-by: Marc Zyngier Signed-off-by: Xiang Chen Signed-off-by: caijian Signed-off-by: Xie Xiaodong <624338359@qq.com> --- include/kvm/arm_vgic.h | 6 +++++- virt/kvm/arm/vgic/vgic-init.c | 9 ++++++++- virt/kvm/arm/vgic/vgic-mmio-v2.c | 1 + virt/kvm/arm/vgic/vgic-mmio-v3.c | 15 ++++++++++++++- 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index 0168eb599f9b..8a0d1a9b06e4 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -99,6 +99,9 @@ struct vgic_global { bool has_gicv4; bool has_gicv4_1; + /* NMI */ + bool has_nmi; + #ifdef CONFIG_VIRT_VTIMER_IRQ_BYPASS /* * Hardware (HiSilicon implementation) has vtimer interrupt @@ -252,7 +255,8 @@ struct vgic_dist { u32 implementation_rev; #define KVM_VGIC_IMP_REV_2 2 /* GICv2 restorable groups */ #define KVM_VGIC_IMP_REV_3 3 /* GICv3 GICR_CTLR.{IW,CES,RWP} */ -#define KVM_VGIC_IMP_REV_LATEST KVM_VGIC_IMP_REV_3 +#define KVM_VGIC_IMP_REV_4 4 /* GICv3 NMI */ +#define KVM_VGIC_IMP_REV_LATEST KVM_VGIC_IMP_REV_4 /* Userspace can write to GICv2 IGROUPR */ bool v2_groups_user_writable; diff --git a/virt/kvm/arm/vgic/vgic-init.c b/virt/kvm/arm/vgic/vgic-init.c index f5805dc1ecf7..3365140f0269 100644 --- a/virt/kvm/arm/vgic/vgic-init.c +++ b/virt/kvm/arm/vgic/vgic-init.c @@ -335,8 +335,15 @@ int vgic_init(struct kvm *kvm) * If userspace didn't set the GIC implementation revision, * default to the latest and greatest. You know want it. */ - if (!dist->implementation_rev) + if (!dist->implementation_rev) { dist->implementation_rev = KVM_VGIC_IMP_REV_LATEST; + /* + * Advertise NMI if available. Userspace that explicitly + * doesn't want NMI will have written to GICD_{IIDR,TYPER} + * to set the implementation and the NMI support status. + */ + dist->has_nmi = kvm_vgic_global_state.has_nmi; + } dist->initialized = true; out: diff --git a/virt/kvm/arm/vgic/vgic-mmio-v2.c b/virt/kvm/arm/vgic/vgic-mmio-v2.c index 0c597f24e337..613d72442244 100644 --- a/virt/kvm/arm/vgic/vgic-mmio-v2.c +++ b/virt/kvm/arm/vgic/vgic-mmio-v2.c @@ -95,6 +95,7 @@ static int vgic_mmio_uaccess_write_v2_misc(struct kvm_vcpu *vcpu, switch (reg) { case KVM_VGIC_IMP_REV_2: case KVM_VGIC_IMP_REV_3: + case KVM_VGIC_IMP_REV_4: vcpu->kvm->arch.vgic.v2_groups_user_writable = true; dist->implementation_rev = reg; return 0; diff --git a/virt/kvm/arm/vgic/vgic-mmio-v3.c b/virt/kvm/arm/vgic/vgic-mmio-v3.c index e24f4858c060..d26043594285 100644 --- a/virt/kvm/arm/vgic/vgic-mmio-v3.c +++ b/virt/kvm/arm/vgic/vgic-mmio-v3.c @@ -77,6 +77,8 @@ static unsigned long vgic_mmio_read_v3_misc(struct kvm_vcpu *vcpu, case GICD_TYPER: value = vgic->nr_spis + VGIC_NR_PRIVATE_IRQS; value = (value >> 5) - 1; + if (vgic->has_nmi) + value |= GICD_TYPER_NMI; if (vgic_has_its(vcpu->kvm)) { value |= (INTERRUPT_ID_BITS_ITS - 1) << 19; value |= GICD_TYPER_LPIS; @@ -157,6 +159,13 @@ static int vgic_mmio_uaccess_write_v3_misc(struct kvm_vcpu *vcpu, u32 reg; switch (addr & 0x0c) { + case GICD_TYPER: + if (dist->implementation_rev >= KVM_VGIC_IMP_REV_4 && + kvm_vgic_global_state.has_nmi) + dist->has_nmi = val & GICD_TYPER_NMI; + else if (val & GICD_TYPER_NMI) + return -EINVAL; + return 0; case GICD_TYPER2: if (val != vgic_mmio_read_v3_misc(vcpu, addr, len)) return -EINVAL; @@ -170,6 +179,10 @@ static int vgic_mmio_uaccess_write_v3_misc(struct kvm_vcpu *vcpu, switch (reg) { case KVM_VGIC_IMP_REV_2: case KVM_VGIC_IMP_REV_3: + /* Disable NMI on selecting an older revision */ + dist->has_nmi = false; + fallthrough; + case KVM_VGIC_IMP_REV_4: dist->implementation_rev = reg; return 0; default: @@ -185,7 +198,7 @@ static int vgic_mmio_uaccess_write_v3_misc(struct kvm_vcpu *vcpu, return 0; } - vgic_mmio_write_v3_misc(vcpu, addr, len, val); + /* Not reachable... */ return 0; } -- Gitee From 0dea69ed6699f0aea5be44d524d4bfdb7db93d8c Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 9 Apr 2024 09:22:04 +0800 Subject: [PATCH 13/20] KVM: arm64: vgic-debug: Add the NMI field to the debug output commit 059de40b831b8bffd7f017d81223aaedfd4d1455 openEuler Add the per-INTID NMI state to the vgic-state file. Signed-off-by: Marc Zyngier Signed-off-by: Xiang Chen Signed-off-by: caijian Signed-off-by: Xie Xiaodong <624338359@qq.com> --- virt/kvm/arm/vgic/vgic-debug.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/virt/kvm/arm/vgic/vgic-debug.c b/virt/kvm/arm/vgic/vgic-debug.c index 19f5e0179b51..afbb1af13f45 100644 --- a/virt/kvm/arm/vgic/vgic-debug.c +++ b/virt/kvm/arm/vgic/vgic-debug.c @@ -155,7 +155,7 @@ static void print_dist_state(struct seq_file *s, struct vgic_dist *dist) seq_printf(s, "P=pending_latch, L=line_level, A=active\n"); seq_printf(s, "E=enabled, H=hw, C=config (level=1, edge=0)\n"); - seq_printf(s, "G=group\n"); + seq_puts(s, "G=group, N=NMI\n"); } static void print_header(struct seq_file *s, struct vgic_irq *irq, @@ -170,8 +170,9 @@ static void print_header(struct seq_file *s, struct vgic_irq *irq, } seq_printf(s, "\n"); - seq_printf(s, "%s%2d TYP ID TGT_ID PLAEHCG HWID TARGET SRC PRI VCPU_ID\n", hdr, id); - seq_printf(s, "----------------------------------------------------------------\n"); + seq_printf(s, "%s%2d TYP ID TGT_ID PLAEHCGN HWID TARGET SRC PRI VCPU_ID\n", + hdr, id); + seq_puts(s, "-----------------------------------------------------------------\n"); } static void print_irq_state(struct seq_file *s, struct vgic_irq *irq, @@ -204,7 +205,7 @@ static void print_irq_state(struct seq_file *s, struct vgic_irq *irq, seq_printf(s, " %s %4d " " %2d " - "%d%d%d%d%d%d%d " + "%d%d%d%d%d%d%d%d " "%8d " "%8x " " %2x " @@ -220,6 +221,7 @@ static void print_irq_state(struct seq_file *s, struct vgic_irq *irq, irq->hw, irq->config == VGIC_CONFIG_LEVEL, irq->group, + irq->nmi, irq->hwintid, irq->mpidr, irq->source, -- Gitee From 19ab16c5a465fc1a6ef9f1aa604a30a5564825d8 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 9 Apr 2024 09:22:05 +0800 Subject: [PATCH 14/20] KVM: arm64: Allow userspace to control ID_AA64PFR1_EL1.NMI commit 395d99d8b0448f995cf3adfd81330525ede3929a openEuler In order for userspace to be able to control the CPU side of the NMI distribution (just like we have it on the GIC side), allow it to set/clear ID_AA64PFR1_EL1.NMI. This relies on a per-VM property that defaults to the host value. Signed-off-by: Marc Zyngier Signed-off-by: Xiang Chen Signed-off-by: caijian Signed-off-by: Xie Xiaodong <624338359@qq.com> --- arch/arm64/include/asm/kvm_host.h | 15 +++++++++++ arch/arm64/kvm/sys_regs.c | 41 ++++++++++++++++++++++++++++++- virt/kvm/arm/arm.c | 3 +++ 3 files changed, 58 insertions(+), 1 deletion(-) diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h index 79139f0d7d85..209ac1b5e1b3 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -73,6 +73,8 @@ struct kvm_arch { /* VTCR_EL2 value for this VM */ u64 vtcr; + u8 pfr1_nmi; + /* The last vcpu id that ran on each physical CPU */ int __percpu *last_vcpu_ran; @@ -84,6 +86,19 @@ struct kvm_arch { /* Mandated version of PSCI */ u32 psci_version; + + /* + * Emulated CPU ID registers per VM + * (Op0, Op1, CRn, CRm, Op2) of the ID registers to be saved in it + * is (3, 0, 0, crm, op2), where 1<=crm<8, 0<=op2<8. + * + * These emulated idregs are VM-wide, but accessed from the context of a vCPU. + * Atomic access to multiple idregs are guarded by kvm_arch.config_lock. + */ + #define IDREG_IDX(id) (((sys_reg_CRm(id) - 1) << 3) | sys_reg_Op2(id)) + #define IDREG(kvm, id) ((kvm)->arch.id_regs[IDREG_IDX(id)]) + #define KVM_ARM_ID_REG_NUM (IDREG_IDX(sys_reg(3, 0, 0, 7, 7)) + 1) + u64 id_regs[KVM_ARM_ID_REG_NUM]; }; #define KVM_NR_MEM_OBJS 40 diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c index 4fad598eb3dd..eab3f52b8fa8 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c @@ -1097,11 +1097,18 @@ static u64 read_id_reg(const struct kvm_vcpu *vcpu, (0xfUL << ID_AA64ISAR1_GPI_SHIFT)); } else if (id == SYS_ID_AA64PFR1_EL1) { val &= ~ID_AA64PFR1_NMI_MASK; + val |= FIELD_PREP(ID_AA64PFR1_NMI_MASK, + vcpu->kvm->arch.pfr1_nmi); } return val; } +void read_id_reg_reset(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r) +{ + read_id_reg(vcpu, r, false); +} + /* cpufeature ID register access trap handlers */ static bool __access_id_reg(struct kvm_vcpu *vcpu, @@ -1229,6 +1236,36 @@ static int __set_id_reg(const struct kvm_vcpu *vcpu, return 0; } +static int set_id_aa64pfr1_el1(struct kvm_vcpu *vcpu, + const struct sys_reg_desc *rd, + const struct kvm_one_reg *reg, void __user *uaddr) +{ + const u64 id = sys_reg_to_index(rd); + int err; + u64 val; + u8 nmi; + u64 r; + + err = reg_from_user(&val, uaddr, id); + if (err) + return err; + + nmi = cpuid_feature_extract_unsigned_field(val, ID_AA64PFR1_NMI_SHIFT); + if (nmi > ID_AA64PFR1_NMI_IMP_DEF || (nmi && !system_uses_nmi())) + return -EINVAL; + + /* We can only differ with NMI, and anything else is an error */ + r = reg_to_encoding(rd); + val ^= IDREG(vcpu->kvm, r); + val &= ~ID_AA64PFR1_NMI_MASK; + if (val) + return -EINVAL; + + vcpu->kvm->arch.pfr1_nmi = nmi; + + return 0; +} + static int get_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd, const struct kvm_one_reg *reg, void __user *uaddr) { @@ -1441,7 +1478,9 @@ static const struct sys_reg_desc sys_reg_descs[] = { /* AArch64 ID registers */ /* CRm=4 */ ID_SANITISED(ID_AA64PFR0_EL1), - ID_SANITISED(ID_AA64PFR1_EL1), + { SYS_DESC(SYS_ID_AA64PFR1_EL1), .access = access_id_reg, + .get_user = get_id_reg, .set_user = set_id_aa64pfr1_el1, + .reset = read_id_reg_reset, }, ID_UNALLOCATED(4,2), ID_UNALLOCATED(4,3), { SYS_DESC(SYS_ID_AA64ZFR0_EL1), access_id_aa64zfr0_el1, .get_user = get_id_aa64zfr0_el1, .set_user = set_id_aa64zfr0_el1, }, diff --git a/virt/kvm/arm/arm.c b/virt/kvm/arm/arm.c index 28cdd2f4d1ef..3f72adc1281d 100644 --- a/virt/kvm/arm/arm.c +++ b/virt/kvm/arm/arm.c @@ -135,6 +135,9 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) kvm->arch.max_vcpus = vgic_present ? kvm_vgic_get_max_vcpus() : KVM_MAX_VCPUS; + if (system_uses_nmi()) + kvm->arch.pfr1_nmi = ID_AA64PFR1_NMI_IMP_DEF; + return ret; out_free_stage2_pgd: kvm_free_stage2_pgd(kvm); -- Gitee From 703ea8a25c0d9a4834c5ed66dd8b413702b57c63 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 9 Apr 2024 09:22:06 +0800 Subject: [PATCH 15/20] KVM: arm64: Don't trap ALLINT accesses if the vcpu has FEAT_NMI commit e2895896e01f2afc3da67eab497f875a127cf4d1 openEuler This is counter-productive, so let it rip. Signed-off-by: Marc Zyngier Signed-off-by: Xiang Chen Signed-off-by: caijian Signed-off-by: Xie Xiaodong <624338359@qq.com> --- arch/arm64/kvm/hyp/switch.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/arm64/kvm/hyp/switch.c b/arch/arm64/kvm/hyp/switch.c index ef7fe0801906..fac08802c432 100644 --- a/arch/arm64/kvm/hyp/switch.c +++ b/arch/arm64/kvm/hyp/switch.c @@ -88,7 +88,8 @@ static void __hyp_text __activate_traps_common(struct kvm_vcpu *vcpu) write_sysreg(0, pmselr_el0); write_sysreg(ARMV8_PMU_USERENR_MASK, pmuserenr_el0); - if (cpus_have_final_cap(ARM64_HAS_NMI)) + if (cpus_have_final_cap(ARM64_HAS_NMI) && + !kern_hyp_va(vcpu->kvm)->arch.pfr1_nmi) sysreg_clear_set_s(SYS_HCRX_EL2, 0, HCRX_EL2_TALLINT); vcpu->arch.mdcr_el2_host = read_sysreg(mdcr_el2); @@ -99,7 +100,8 @@ static void __hyp_text __deactivate_traps_common(struct kvm_vcpu *vcpu) { write_sysreg(vcpu->arch.mdcr_el2_host, mdcr_el2); - if (cpus_have_final_cap(ARM64_HAS_NMI)) + if (cpus_have_final_cap(ARM64_HAS_NMI) && + !kern_hyp_va(vcpu->kvm)->arch.pfr1_nmi) sysreg_clear_set_s(SYS_HCRX_EL2, HCRX_EL2_TALLINT, 0); write_sysreg(0, hstr_el2); -- Gitee From c488ecd6187674721a39c71312ac67f5c9cd4152 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 9 Apr 2024 09:22:07 +0800 Subject: [PATCH 16/20] KVM: arm64: vgic-v3: Don't inject an NMI if the vcpu doesn't have FEAT_NMI commit 8b487c6098bb58981d0c05aea3661e5bde5fc7b8 openEuler Since it is allowed to have any combination of CPU and GIC supporting NMIs or not, let's drop the NMI feature at the point where it is injected in the LR if the vcpu doesn't have FEAT_NMI. Signed-off-by: Marc Zyngier Signed-off-by: Xiang Chen Signed-off-by: caijian Signed-off-by: Xie Xiaodong <624338359@qq.com> --- drivers/irqchip/irq-gic-v3.c | 2 +- virt/kvm/arm/vgic/vgic-v3.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index e83b24e2fad8..82d5aa5b4124 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -2288,7 +2288,7 @@ static void __init gic_acpi_setup_kvm_info(void) gic_v3_kvm_info.has_v4_1 = gic_data.rdists.has_rvpeid; #ifdef CONFIG_VIRT_VTIMER_IRQ_BYPASS gic_v3_kvm_info.has_vtimer = gic_data.rdists.has_vtimer; -#endif +#endif gic_set_kvm_info(&gic_v3_kvm_info); } diff --git a/virt/kvm/arm/vgic/vgic-v3.c b/virt/kvm/arm/vgic/vgic-v3.c index edabb3ddd421..517171ca6e97 100644 --- a/virt/kvm/arm/vgic/vgic-v3.c +++ b/virt/kvm/arm/vgic/vgic-v3.c @@ -194,7 +194,8 @@ void vgic_v3_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr) if (irq->group) val |= ICH_LR_GROUP; - if (irq->nmi) + if (vcpu->kvm->arch.pfr1_nmi == ID_AA64PFR1_NMI_IMP_DEF && + irq->nmi) val |= ICH_LR_NMI; else val |= (u64)irq->priority << ICH_LR_PRIORITY_SHIFT; -- Gitee From a39bcedfac135c3a2105a6a8275e49b57fc86dc5 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 9 Apr 2024 09:22:08 +0800 Subject: [PATCH 17/20] KVM: arm64: Allow GICv3.3 NMI if the host supports it commit e5c5bbf9871230a3497e6ad44a49877eef6a1444 openEuler Let the GICv3 driver populate a has_nmi flag in the kvm_gic_info structure, and communicate it to the rest of KVM. We disallow the use of NMI if trapping of the CPU interface is enabled, because life is definitely too short to write more emulation. Signed-off-by: Marc Zyngier Signed-off-by: Xiang Chen Signed-off-by: caijian Signed-off-by: Xie Xiaodong <624338359@qq.com> --- arch/arm64/kvm/sys_regs.c | 7 ++++--- drivers/irqchip/irq-gic-v3.c | 1 + include/linux/irqchip/arm-gic-common.h | 2 ++ virt/kvm/arm/arm.c | 2 +- virt/kvm/arm/vgic/vgic-v3.c | 6 ++++++ 5 files changed, 14 insertions(+), 4 deletions(-) diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c index eab3f52b8fa8..25b3f67db814 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c @@ -361,14 +361,14 @@ static bool trap_dbgauthstatus_el1(struct kvm_vcpu *vcpu, /* * We want to avoid world-switching all the DBG registers all the * time: - * + * * - If we've touched any debug register, it is likely that we're * going to touch more of them. It then makes sense to disable the * traps and start doing the save/restore dance * - If debug is active (DBG_MDSCR_KDE or DBG_MDSCR_MDE set), it is * then mandatory to save/restore the registers, as the guest * depends on them. - * + * * For this, we use a DIRTY bit, indicating the guest has modified the * debug registers, used as follow: * @@ -1251,7 +1251,8 @@ static int set_id_aa64pfr1_el1(struct kvm_vcpu *vcpu, return err; nmi = cpuid_feature_extract_unsigned_field(val, ID_AA64PFR1_NMI_SHIFT); - if (nmi > ID_AA64PFR1_NMI_IMP_DEF || (nmi && !system_uses_nmi())) + if (nmi > ID_AA64PFR1_NMI_IMP_DEF || + (nmi && (!system_uses_nmi() || static_branch_unlikely(&vgic_v3_cpuif_trap)))) return -EINVAL; /* We can only differ with NMI, and anything else is an error */ diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index 82d5aa5b4124..8fe491da3572 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -2286,6 +2286,7 @@ static void __init gic_acpi_setup_kvm_info(void) gic_v3_kvm_info.has_v4 = gic_data.rdists.has_vlpis; gic_v3_kvm_info.has_v4_1 = gic_data.rdists.has_rvpeid; + gic_v3_kvm_info.has_nmi = has_v3_3_nmi(); #ifdef CONFIG_VIRT_VTIMER_IRQ_BYPASS gic_v3_kvm_info.has_vtimer = gic_data.rdists.has_vtimer; #endif diff --git a/include/linux/irqchip/arm-gic-common.h b/include/linux/irqchip/arm-gic-common.h index 408261a606db..4e29767d3aa7 100644 --- a/include/linux/irqchip/arm-gic-common.h +++ b/include/linux/irqchip/arm-gic-common.h @@ -34,6 +34,8 @@ struct gic_kvm_info { bool has_v4; /* rvpeid support */ bool has_v4_1; + /* NMI support */ + bool has_nmi; #ifdef CONFIG_VIRT_VTIMER_IRQ_BYPASS /* vtimer irqbypass support */ bool has_vtimer; diff --git a/virt/kvm/arm/arm.c b/virt/kvm/arm/arm.c index 3f72adc1281d..e7dc9f7cb5f2 100644 --- a/virt/kvm/arm/arm.c +++ b/virt/kvm/arm/arm.c @@ -135,7 +135,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) kvm->arch.max_vcpus = vgic_present ? kvm_vgic_get_max_vcpus() : KVM_MAX_VCPUS; - if (system_uses_nmi()) + if (system_uses_nmi() && !static_branch_unlikely(&vgic_v3_cpuif_trap)) kvm->arch.pfr1_nmi = ID_AA64PFR1_NMI_IMP_DEF; return ret; diff --git a/virt/kvm/arm/vgic/vgic-v3.c b/virt/kvm/arm/vgic/vgic-v3.c index 517171ca6e97..f250ded6b900 100644 --- a/virt/kvm/arm/vgic/vgic-v3.c +++ b/virt/kvm/arm/vgic/vgic-v3.c @@ -710,6 +710,12 @@ int vgic_v3_probe(const struct gic_kvm_info *info) static_branch_enable(&vgic_v3_cpuif_trap); } + if (info->has_nmi) { + kvm_vgic_global_state.has_nmi = !static_branch_unlikely(&vgic_v3_cpuif_trap); + kvm_info("GICv3 NMI support %s\n", + kvm_vgic_global_state.has_nmi ? "enabled" : "disabled due to trapping"); + } + kvm_vgic_global_state.vctrl_base = NULL; kvm_vgic_global_state.type = VGIC_V3; kvm_vgic_global_state.max_gic_vcpus = VGIC_V3_MAX_CPUS; -- Gitee From 41525b84aa0163c597c69b5bc03928865bf5f2fd Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 9 Apr 2024 09:22:09 +0800 Subject: [PATCH 18/20] KVM: arm64: Handle traps of ALLINT commit dfe028fe605ad0dd165d7283767cd42a8e290a8f openEuler Although we do force a trap of the ALLINT system register when FEAT_NMI isn't exposed to the guest, we don't provide any handler. That's bad. Let's fix that. Signed-off-by: Marc Zyngier Signed-off-by: Xiang Chen Signed-off-by: caijian Signed-off-by: Xie Xiaodong <624338359@qq.com> --- arch/arm64/include/asm/sysreg.h | 2 ++ arch/arm64/kvm/sys_regs.c | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h index 761b26417a5d..0f77e9aa461c 100644 --- a/arch/arm64/include/asm/sysreg.h +++ b/arch/arm64/include/asm/sysreg.h @@ -204,6 +204,8 @@ #define SYS_ALLINT sys_reg(3, 0, 4, 3, 0) +#define SYS_ALLINT sys_reg(3, 0, 4, 3, 0) + #define SYS_ICC_PMR_EL1 sys_reg(3, 0, 4, 6, 0) #define SYS_ICC_NMIAR1_EL1 sys_reg(3, 0, 12, 9, 5) diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c index 25b3f67db814..6f3d31df550c 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c @@ -1013,6 +1013,14 @@ static bool access_pmuserenr(struct kvm_vcpu *vcpu, struct sys_reg_params *p, { SYS_DESC(SYS_PMEVTYPERn_EL0(n)), \ access_pmu_evtyper, reset_unknown, (PMEVTYPER0_EL0 + n), } +static bool undef_access(struct kvm_vcpu *vcpu, struct sys_reg_params *p, + const struct sys_reg_desc *r) +{ + kvm_inject_undefined(vcpu); + + return false; +} + static bool trap_ptrauth(struct kvm_vcpu *vcpu, struct sys_reg_params *p, const struct sys_reg_desc *rd) @@ -1399,6 +1407,8 @@ static bool access_ccsidr(struct kvm_vcpu *vcpu, struct sys_reg_params *p, * more demanding guest... */ static const struct sys_reg_desc sys_reg_descs[] = { + { SYS_DESC(SYS_ALLINT_CLR), undef_access }, + { SYS_DESC(SYS_ALLINT_SET), undef_access }, { SYS_DESC(SYS_DC_ISW), access_dcsw }, { SYS_DESC(SYS_DC_CSW), access_dcsw }, { SYS_DESC(SYS_DC_CISW), access_dcsw }, @@ -1532,6 +1542,8 @@ static const struct sys_reg_desc sys_reg_descs[] = { PTRAUTH_KEY(APDB), PTRAUTH_KEY(APGA), + { SYS_DESC(SYS_ALLINT), undef_access }, + { SYS_DESC(SYS_AFSR0_EL1), access_vm_reg, reset_unknown, AFSR0_EL1 }, { SYS_DESC(SYS_AFSR1_EL1), access_vm_reg, reset_unknown, AFSR1_EL1 }, { SYS_DESC(SYS_ESR_EL1), access_vm_reg, reset_unknown, ESR_EL1 }, -- Gitee From 7289795ff1b4584f087208ffa80e99098b553f01 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 9 Apr 2024 09:22:10 +0800 Subject: [PATCH 19/20] arm64: Decouple KVM from CONFIG_ARM64_NMI commit b8c8255e1d74937638bca666264404952c60395a openEuler Even if the host isn't using NMIs, that isn't a reason for preventing the feature being available to guests, Decouple the two and let the capability be available irrespective of the host NMI level of support. Signed-off-by: Marc Zyngier Signed-off-by: Xiang Chen Signed-off-by: caijian Signed-off-by: Xie Xiaodong <624338359@qq.com> --- arch/arm64/include/asm/cpufeature.h | 3 ++- arch/arm64/kernel/cpufeature.c | 13 +++++++++---- drivers/irqchip/irq-gic-v3.c | 13 ++++++++++++- virt/kvm/arm/arm.c | 3 ++- 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h index f509501323e7..033251f8f716 100644 --- a/arch/arm64/include/asm/cpufeature.h +++ b/arch/arm64/include/asm/cpufeature.h @@ -663,7 +663,8 @@ static __always_inline bool system_uses_irq_prio_masking(void) static __always_inline bool system_uses_nmi(void) { return IS_ENABLED(CONFIG_ARM64_NMI) && - cpus_have_const_cap(ARM64_USES_NMI); + cpus_have_const_cap(ARM64_USES_NMI) && + !system_uses_irq_prio_masking(); } static inline bool system_has_prio_mask_debugging(void) diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index 753d6a6e96f3..716790b0360c 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -1295,20 +1295,24 @@ static bool can_use_gic_priorities(const struct arm64_cpu_capabilities *entry, } #endif -#ifdef CONFIG_ARM64_NMI static bool use_nmi(const struct arm64_cpu_capabilities *entry, int scope) { if (!has_cpuid_feature(entry, scope)) return false; /* + * NMI support was not enabled in the kernel, but can still be + * used by guests. Let the world know. + * * Having both real and pseudo NMIs enabled simultaneously is * likely to cause confusion. Since pseudo NMIs must be * enabled with an explicit command line option, if the user * has set that option on a system with real NMIs for some * reason assume they know what they're doing. */ - if (IS_ENABLED(CONFIG_ARM64_PSEUDO_NMI) && enable_pseudo_nmi) { + if (!IS_ENABLED(CONFIG_ARM64_NMI)) + pr_info("CONFIG_ARM64_NMI disabled, using NMIs for guests only\n"); + else if (IS_ENABLED(CONFIG_ARM64_PSEUDO_NMI) && enable_pseudo_nmi) { pr_info("Pseudo NMI enabled, not using architected NMI\n"); return false; } @@ -1316,6 +1320,7 @@ static bool use_nmi(const struct arm64_cpu_capabilities *entry, int scope) return true; } +#ifdef CONFIG_ARM64_NMI static void nmi_enable(const struct arm64_cpu_capabilities *__unused) { /* @@ -1690,7 +1695,6 @@ static const struct arm64_cpu_capabilities arm64_features[] = { .min_field_value = 1, }, #endif -#ifdef CONFIG_ARM64_NMI { .desc = "Non-maskable Interrupts present", .capability = ARM64_HAS_NMI, @@ -1710,9 +1714,10 @@ static const struct arm64_cpu_capabilities arm64_features[] = { .field_pos = ID_AA64PFR1_NMI_SHIFT, .min_field_value = ID_AA64PFR1_NMI_IMP_DEF, .matches = use_nmi, +#ifdef CONFIG_ARM64_NMI .cpu_enable = nmi_enable, - }, #endif + }, { .desc = "Trap EL0 IMPLEMENTATION DEFINED functionality", .capability = ARM64_HAS_TIDCP1, diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index 8fe491da3572..bc8dcdda7898 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -110,11 +110,21 @@ static inline bool has_v3_3_nmi(void) { return gic_data.has_nmi && system_uses_nmi(); } + +static bool system_is_nmi_capable(void) +{ + return gic_data.has_nmi && cpus_have_const_cap(ARM64_HAS_NMI); +} #else static inline bool has_v3_3_nmi(void) { return false; } + +static bool system_is_nmi_capable(void) +{ + return false; +} #endif #ifdef CONFIG_VIRT_VTIMER_IRQ_BYPASS @@ -1967,6 +1977,7 @@ static void __init gic_of_setup_kvm_info(struct device_node *node) gic_v3_kvm_info.has_v4 = gic_data.rdists.has_vlpis; gic_v3_kvm_info.has_v4_1 = gic_data.rdists.has_rvpeid; + gic_v3_kvm_info.has_nmi = system_is_nmi_capable(); #ifdef CONFIG_VIRT_VTIMER_IRQ_BYPASS gic_v3_kvm_info.has_vtimer = gic_data.rdists.has_vtimer; #endif @@ -2286,7 +2297,7 @@ static void __init gic_acpi_setup_kvm_info(void) gic_v3_kvm_info.has_v4 = gic_data.rdists.has_vlpis; gic_v3_kvm_info.has_v4_1 = gic_data.rdists.has_rvpeid; - gic_v3_kvm_info.has_nmi = has_v3_3_nmi(); + gic_v3_kvm_info.has_nmi = system_is_nmi_capable(); #ifdef CONFIG_VIRT_VTIMER_IRQ_BYPASS gic_v3_kvm_info.has_vtimer = gic_data.rdists.has_vtimer; #endif diff --git a/virt/kvm/arm/arm.c b/virt/kvm/arm/arm.c index e7dc9f7cb5f2..7b2b4f8b6988 100644 --- a/virt/kvm/arm/arm.c +++ b/virt/kvm/arm/arm.c @@ -135,7 +135,8 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) kvm->arch.max_vcpus = vgic_present ? kvm_vgic_get_max_vcpus() : KVM_MAX_VCPUS; - if (system_uses_nmi() && !static_branch_unlikely(&vgic_v3_cpuif_trap)) + if (cpus_have_const_cap(ARM64_HAS_NMI) && + !static_branch_unlikely(&vgic_v3_cpuif_trap)) kvm->arch.pfr1_nmi = ID_AA64PFR1_NMI_IMP_DEF; return ret; -- Gitee From 80feadbcb1caace3dc16ba9b55e6e837b294a9e3 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 9 Apr 2024 09:22:11 +0800 Subject: [PATCH 20/20] KVM: arm64: vgic-v3: Handle traps of ICV_NMIAR1_EL1 commit 3adab6c497a4153129fecf181f539a66df1956ee openEuler Even if we do not plan to deal with the GICv3.3 NMI feature in our in-kernel emulation of the CPU interface for terminally broken systems, the actual system register may still exist and raise its ugly head. Hit it with an UNDEF-sized hammer. Signed-off-by: Marc Zyngier Signed-off-by: Xiang Chen Signed-off-by: caijian Signed-off-by: Xie Xiaodong <624338359@qq.com> --- arch/arm64/kvm/sys_regs.c | 1 + virt/kvm/arm/hyp/vgic-v3-sr.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c index 6f3d31df550c..bb980d1b18c8 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c @@ -1578,6 +1578,7 @@ static const struct sys_reg_desc sys_reg_descs[] = { { SYS_DESC(SYS_ICC_IAR0_EL1), write_to_read_only }, { SYS_DESC(SYS_ICC_EOIR0_EL1), read_from_write_only }, { SYS_DESC(SYS_ICC_HPPIR0_EL1), write_to_read_only }, + { SYS_DESC(SYS_ICC_NMIAR1_EL1), undef_access }, { SYS_DESC(SYS_ICC_DIR_EL1), read_from_write_only }, { SYS_DESC(SYS_ICC_RPR_EL1), write_to_read_only }, { SYS_DESC(SYS_ICC_SGI1R_EL1), access_gic_sgi }, diff --git a/virt/kvm/arm/hyp/vgic-v3-sr.c b/virt/kvm/arm/hyp/vgic-v3-sr.c index b2d0e2c1cbe6..3ffbd5dbb3b0 100644 --- a/virt/kvm/arm/hyp/vgic-v3-sr.c +++ b/virt/kvm/arm/hyp/vgic-v3-sr.c @@ -1033,6 +1033,9 @@ int __hyp_text __vgic_v3_perform_cpuif_access(struct kvm_vcpu *vcpu) return 0; fn = __vgic_v3_read_iar; break; + case SYS_ICC_NMIAR1_EL1: + /* Here's an UNDEF for you */ + return 0; case SYS_ICC_EOIR0_EL1: case SYS_ICC_EOIR1_EL1: if (unlikely(is_read)) -- Gitee