From 8c91492b27a2d8971446d08fd05b922ebf4b8d05 Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Tue, 17 Aug 2021 09:11:25 +0100 Subject: [PATCH 01/19] KVM: arm64: Restore mdcr_el2 from vcpu [Upstream commit 1460b4b25fde] 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 --- 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 842ddd559692e12b4d49da68cdb85853774ba7fc Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 5 Apr 2022 19:23:24 +0100 Subject: [PATCH 02/19] irqchip/gic-v3: Exposes bit values for GICR_CTLR.{IR, CES} [Upstream commit 34453c2e9f79] 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 --- 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 eeba777114b5f90534bcde0d76486a700eba5107 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 5 Apr 2022 19:23:25 +0100 Subject: [PATCH 03/19] KVM: arm64: vgic-v3: Expose GICR_CTLR.RWP when disabling LPIs [Upstream commit 94828468a608] 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 --- 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 3497eb7ca3fea5638bc10e891581cfd713b05e54 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 5 Apr 2022 19:23:26 +0100 Subject: [PATCH 04/19] KVM: arm64: vgic-v3: Implement MMIO-based LPI invalidation [Upstream commit 4645d11f4a55] 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 --- 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 63346d95a99206d72df94da5846e952c7f08a54c Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 5 Apr 2022 19:23:27 +0100 Subject: [PATCH 05/19] KVM: arm64: vgic-v3: Advertise GICR_CTLR.{IR, CES} as a new GICD_IIDR revision [Upstream commit 49a1a2c70a7f] 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 --- 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 db83802a792a13cb5db29d50b312c46b6e1c81db Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 9 Apr 2024 09:21:57 +0800 Subject: [PATCH 06/19] KVM: arm64: vgic-v3: Upgrade AP1Rn to 64bit. 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 a79f4eb7f0277ae7bca594e530956424167fef2b Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 9 Apr 2024 09:21:58 +0800 Subject: [PATCH 07/19] KVM: arm64: vgic-v3: Allow the NMI state to make it into the LRs 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 + virt/kvm/arm/vgic/vgic-v3.c | 5 ++++- 2 files changed, 5 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/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 203d54e49c1c17f30a8ff512c5e53346356b52fc Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 9 Apr 2024 09:21:59 +0800 Subject: [PATCH 08/19] KVM: arm64: vgic-v3: Make NMI priority RES0 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 59f989178b3171088ae72a43b1554eaec7fcc82c Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 9 Apr 2024 09:22:00 +0800 Subject: [PATCH 09/19] KVM: arm64: vgic-v4: Propagate the NMI state into the GICv4.1 VSGI configuration 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 0d4aed60d9b93189666b1ca45105de4dde81ef5d Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 9 Apr 2024 09:22:01 +0800 Subject: [PATCH 10/19] KVM: arm64: vgic-v3: Use the NMI attribute as part of the AP-list sorting 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 51cbad8e55eb9006952b500b7f9a753af50092ee Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 9 Apr 2024 09:22:02 +0800 Subject: [PATCH 11/19] KVM: arm64: vgic-v3: Add support for GIC{D,R}_INMIR registers 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 f58571ff7e0f5db7fb6b632d7257215a3d0436a7 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 9 Apr 2024 09:22:03 +0800 Subject: [PATCH 12/19] KVM: arm64: vgic-v3: Add userspace selection for GICv3.3 NMI 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..357bbda35459 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 ee8b2842df13b8265d27a5feae62e63941e02920 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 9 Apr 2024 09:22:04 +0800 Subject: [PATCH 13/19] KVM: arm64: vgic-debug: Add the NMI field to the debug output 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 c5d9bab06aa4070cf21c7844a9084be39718906f Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 9 Apr 2024 09:22:05 +0800 Subject: [PATCH 14/19] KVM: arm64: Allow userspace to control ID_AA64PFR1_EL1.NMI 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 d61bab6ae564f126027b40010250261ac56c4108 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 9 Apr 2024 09:22:06 +0800 Subject: [PATCH 15/19] KVM: arm64: Don't trap ALLINT accesses if the vcpu has FEAT_NMI 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 7b0ef68c2b575de63b9ff0eb24bcef51753947dc Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 9 Apr 2024 09:22:07 +0800 Subject: [PATCH 16/19] KVM: arm64: vgic-v3: Don't inject an NMI if the vcpu doesn't have FEAT_NMI 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 | 3 ++- include/linux/irqchip/arm-gic-common.h | 2 ++ virt/kvm/arm/arm.c | 2 +- virt/kvm/arm/vgic/vgic-v3.c | 12 +++++++++++- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index e83b24e2fad8..8fe491da3572 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -2286,9 +2286,10 @@ 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 +#endif gic_set_kvm_info(&gic_v3_kvm_info); } 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 edabb3ddd421..90c18f1eed11 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; @@ -709,6 +710,15 @@ 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 832d8acb14dc11438bd526d6a601ab7f8dbcfc01 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 9 Apr 2024 09:22:09 +0800 Subject: [PATCH 17/19] KVM: arm64: Handle traps of ALLINT 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 | 3 +++ arch/arm64/kvm/sys_regs.c | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h index 761b26417a5d..13ebfb31ae22 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) @@ -923,5 +925,6 @@ } while (0) #endif +#define ICH_LR_NMI (1ULL << 59) #endif /* __ASM_SYSREG_H */ diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c index eab3f52b8fa8..51e93662cbbd 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) @@ -1398,6 +1406,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 }, @@ -1531,6 +1541,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 53f1ab32da853b5658c4623f52c92ba57717e052 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 9 Apr 2024 09:22:10 +0800 Subject: [PATCH 18/19] arm64: Decouple KVM from CONFIG_ARM64_NMI 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 | 11 +++++++++-- drivers/irqchip/irq-gic-v3.c | 13 ++++++++++++- virt/kvm/arm/arm.c | 3 ++- 4 files changed, 25 insertions(+), 5 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..188ef1839390 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) { /* @@ -1710,7 +1715,9 @@ 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 }, #endif { 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 d57be315966dd8dca70c903e643e2f3d116fd812 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 9 Apr 2024 09:22:11 +0800 Subject: [PATCH 19/19] KVM: arm64: vgic-v3: Handle traps of ICV_NMIAR1_EL1 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 51e93662cbbd..8a8647bb564f 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c @@ -1577,6 +1577,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