KVM: arm64: pKVM init and feature detection fixes
cover KVM: arm64: pKVM init and feature detection fixes
Hi folks,
These six patches are standalone correctness fixes I'd like to land before posting a follow-up to Will's pKVM infrastructure series [1] that moves vCPU state management to EL2. Sending them separately keeps the bigger series focused, but they are all valid fixes to have regardless.
The first patch fixes feature detection for FEAT_Debugv8p9: it was checking the wrong field in ID_AA64DFR0_EL1, causing KVM to treat certain EL2 control bits as RES0 on hardware that implements the feature.
The second patch is a trivial typo fix in comments.
The third patch fixes feature detection for FEAT_SPE_FnE, which was also checking the wrong field.
The last three fix bugs in the pKVM vCPU and hypervisor initialisation
paths: a latent macro parameter bug, a pin-reference leak with a publication ordering issue in __pkvm_init_vcpu(), and a call-ordering hazard in __pkvm_init_finalise() that is benign today but becomes a crash once fix_host_ownership() is extended to operate on a non-empty page-table.
[1] https://lore.kernel.org/all/[email protected]/
Cheers,
/fuad
Fuad Tabba (5):
KVM: arm64: Fix FEAT_Debugv8p9 to check DebugVer, not PMUVer
KVM: arm64: Fix typo in feature check comments
KVM: arm64: Fix FEAT_SPE_FnE to use PMSIDR_EL1.FnE, not PMSVer
KVM: arm64: Fix kvm_vcpu_initialized() macro parameter
KVM: arm64: Fix pin leak and publication ordering in
__pkvm_init_vcpu()
Quentin Perret (1):
KVM: arm64: Fix initialisation order in __pkvm_init_finalise()
arch/arm64/include/asm/kvm_host.h | 2 +- arch/arm64/kvm/config.c | 23 +++++++++++++------ arch/arm64/kvm/hyp/nvhe/pkvm.c | 38 ++++++++++++++++++++----------- arch/arm64/kvm/hyp/nvhe/setup.c | 8 +++---- 4 files changed, 46 insertions(+), 25 deletions(-)
Signature · 1 line
2.54.0.rc2.544.gc7ae2d5bb8-goog
PATCH 1/6 KVM: arm64: Fix FEAT_Debugv8p9 to check DebugVer, not PMUVer
FEAT_Debugv8p9 is incorrectly defined against ID_AA64DFR0_EL1.PMUVer instead of ID_AA64DFR0_EL1.DebugVer. All three consumers of the macro gate features that are architecturally tied to FEAT_Debugv8p9 (DebugVer = 0b1011, DDI0487 M.b A2.2.10):
- HDFGRTR2_EL2.nMDSELR_EL1, HDFGWTR2_EL2.nMDSELR_EL1: MDSELR_EL1 is present only when FEAT_Debugv8p9 is implemented (D24.3.21).
- MDCR_EL2.EBWE: the Extended Breakpoint and Watchpoint Enable bit is RES0 unless FEAT_Debugv8p9 is implemented (D24.3.17).
Neither register has any dependency on PMUVer.
FEAT_Debugv8p9 and FEAT_PMUv3p9 are independent. Per DDI0487 M.b A2.2.10, FEAT_Debugv8p9 is unconditionally mandatory from Armv8.9, whereas FEAT_PMUv3p9 is mandatory only when FEAT_PMUv3 is implemented. An Armv8.9 CPU without a PMU has DebugVer = 0b1011 but PMUVer = 0b0000, so the wrong field check would cause KVM to incorrectly treat EBWE and MDSELR_EL1 as RES0 on such hardware.
--- arch/arm64/kvm/config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/arm64/kvm/config.c +1 -1
@@ -192,7 +192,7 @@ struct reg_feat_map_desc {#define FEAT_SRMASK ID_AA64MMFR4_EL1, SRMASK, IMP#define FEAT_PoPS ID_AA64MMFR4_EL1, PoPS, IMP#define FEAT_PFAR ID_AA64PFR1_EL1, PFAR, IMP -#define FEAT_Debugv8p9 ID_AA64DFR0_EL1, PMUVer, V3P9 +#define FEAT_Debugv8p9 ID_AA64DFR0_EL1, DebugVer, V8P9#define FEAT_PMUv3_SS ID_AA64DFR0_EL1, PMSS, IMP#define FEAT_SEBEP ID_AA64DFR0_EL1, SEBEP, IMP#define FEAT_EBEP ID_AA64DFR1_EL1, EBEP, IMP
Signature · 1 line
2.54.0.rc2.544.gc7ae2d5bb8-goog
PATCH 2/6 KVM: arm64: Fix typo in feature check comments
In reply to Fuad TabbaRevists -> Revisit. The following patch will add another similar line.
No functional change intended.
--- arch/arm64/kvm/config.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
arch/arm64/kvm/config.c +3 -3
@@ -283,7 +283,7 @@ static bool feat_anerr(struct kvm *kvm)static bool feat_sme_smps (struct kvm *kvm) {/* -* Revists this if KVM ever supports SME -- this really should +* Revisit this if KVM ever supports SME -- this really should * look at the guest's view of SMIDR_EL1. Funnily enough, this * is not captured in the JSON file, but only as a note in the * ARM ARM.
@@ -295,7 +295,7 @@ static bool feat_sme_smps(struct kvm *kvm)static bool feat_spe_fds (struct kvm *kvm) {/* -* Revists this if KVM ever supports SPE -- this really should +* Revisit this if KVM ever supports SPE -- this really should * look at the guest's view of PMSIDR_EL1. */ return (kvm_has_feat(kvm, FEAT_SPEv1p4) &&
@@ -305,7 +305,7 @@ static bool feat_spe_fds(struct kvm *kvm)static bool feat_trbe_mpam (struct kvm *kvm) {/* -* Revists this if KVM ever supports both MPAM and TRBE -- +* Revisit this if KVM ever supports both MPAM and TRBE -- * this really should look at the guest's view of TRBIDR_EL1. */ return (kvm_has_feat(kvm, FEAT_TRBE) &&
Signature · 1 line
2.54.0.rc2.544.gc7ae2d5bb8-goog
PATCH 3/6 KVM: arm64: Fix FEAT_SPE_FnE to use PMSIDR_EL1.FnE, not PMSVer
In reply to Fuad TabbaFEAT_SPE_FnE is architecturally detected via PMSIDR_EL1.FnE [6], not ID_AA64DFR0_EL1.PMSVer. The FEAT_X macro form (register, field, value) cannot encode a PMSIDR_EL1-based feature, so FEAT_SPE_FnE was defined identically to FEAT_SPEv1p2 (ID_AA64DFR0_EL1, PMSVer, V1P2), producing a duplicate that used PMSVer >= V1P2 as a proxy.
Replace the macro with feat_spe_fne(), following the same pattern as the sibling feat_spe_fds(): guard on FEAT_SPEv1p2 and read PMSIDR_EL1.FnE [6] directly. Wire the two NEEDS_FEAT consumers to use the new function.
Remove the now-unused FEAT_SPE_FnE macro.
--- arch/arm64/kvm/config.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-)
arch/arm64/kvm/config.c +12 -3
@@ -131,7 +131,6 @@ struct reg_feat_map_desc { }#define FEAT_SPE ID_AA64DFR0_EL1, PMSVer, IMP -#define FEAT_SPE_FnE ID_AA64DFR0_EL1, PMSVer, V1P2#define FEAT_BRBE ID_AA64DFR0_EL1, BRBE, IMP#define FEAT_TRC_SR ID_AA64DFR0_EL1, TraceVer, IMP#define FEAT_PMUv3 ID_AA64DFR0_EL1, PMUVer, IMP
@@ -302,6 +301,16 @@ static bool feat_spe_fds(struct kvm *kvm) (read_sysreg_s(SYS_PMSIDR_EL1) & PMSIDR_EL1_FDS)); } +static bool feat_spe_fne (struct kvm *kvm) +{ +/* +* Revisit this if KVM ever supports SPE -- this really should +* look at the guest's view of PMSIDR_EL1. +*/ +return (kvm_has_feat (kvm, FEAT_SPEv1p2) && + (read_sysreg_s (SYS_PMSIDR_EL1 ) & PMSIDR_EL1_FnE)); +} +static bool feat_trbe_mpam (struct kvm *kvm) {/*
@@ -537,7 +546,7 @@ static const struct reg_bits_to_feat_map hdfgrtr_feat_map[] = { HDFGRTR_EL2_PMBPTR_EL1 | HDFGRTR_EL2_PMBLIMITR_EL1, FEAT_SPE), -NEEDS_FEAT (HDFGRTR_EL2_nPMSNEVFR_EL1, FEAT_SPE_FnE), +NEEDS_FEAT (HDFGRTR_EL2_nPMSNEVFR_EL1, feat_spe_fne), NEEDS_FEAT(HDFGRTR_EL2_nBRBDATA | HDFGRTR_EL2_nBRBCTL | HDFGRTR_EL2_nBRBIDR,
@@ -605,7 +614,7 @@ static const struct reg_bits_to_feat_map hdfgwtr_feat_map[] = { HDFGWTR_EL2_PMBPTR_EL1 | HDFGWTR_EL2_PMBLIMITR_EL1, FEAT_SPE), -NEEDS_FEAT (HDFGWTR_EL2_nPMSNEVFR_EL1, FEAT_SPE_FnE), +NEEDS_FEAT (HDFGWTR_EL2_nPMSNEVFR_EL1, feat_spe_fne),NEEDS_FEAT (HDFGWTR_EL2_nBRBDATA | HDFGWTR_EL2_nBRBCTL, FEAT_BRBE),
Signature · 1 line
2.54.0.rc2.544.gc7ae2d5bb8-goog
PATCH 4/6 KVM: arm64: Fix kvm_vcpu_initialized() macro parameter
In reply to Fuad TabbaThe macro is defined with parameter 'v' but the body references the literal token 'vcpu' instead, causing it to silently operate on whatever 'vcpu' resolves to in the caller's scope rather than the value passed by the caller. All current call sites happen to use a variable named 'vcpu', so the bug is latent.
--- arch/arm64/include/asm/kvm_host.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/arm64/include/asm/kvm_host.h +1 -1
@@ -1548,7 +1548,7 @@ static inline bool __vcpu_has_feature(const struct kvm_arch *ka, int feature)#define kvm_vcpu_has_feature (k, f) __vcpu_has_feature(&(k)->arch, (f))#define vcpu_has_feature (v, f) __vcpu_has_feature(&(v)->kvm->arch, (f)) -#define kvm_vcpu_initialized (v) vcpu_get_flag(vcpu, VCPU_INITIALIZED) +#define kvm_vcpu_initialized (v) vcpu_get_flag(v, VCPU_INITIALIZED)int kvm_trng_call (struct kvm_vcpu *vcpu);#ifdef CONFIG_KVM
Signature · 1 line
2.54.0.rc2.544.gc7ae2d5bb8-goog
PATCH 5/6 KVM: arm64: Fix pin leak and publication ordering in __pkvm_init_vcpu()
In reply to Fuad TabbaTwo bugs exist in the vCPU initialisation path:
1. If a check fails after hyp_pin_shared_mem() succeeds, the cleanup path jumps to 'unlock' without calling unpin_host_vcpu() or unpin_host_sve_state(), permanently leaking pin references on the host vCPU and SVE state pages.
Extract a register_hyp_vcpu() helper that performs the checks and the store. When register_hyp_vcpu() returns an error, call unpin_host_vcpu() and unpin_host_sve_state() inline before falling through to the existing 'unlock' label.
2. register_hyp_vcpu() publishes the new vCPU pointer into 'hyp_vm->vcpus[]' with a bare store, allowing a concurrent caller of pkvm_load_hyp_vcpu() to observe a partially initialised vCPU object.
Ensure the store uses smp_store_release() and the load uses smp_load_acquire(). While 'vm_table_lock' currently serialises the store and the load, these barriers ensure the reader sees the fully initialised 'hyp_vcpu' object even if there were a lockless path or if the lock's own ordering guarantees were insufficient for nested object initialization.
--- arch/arm64/kvm/hyp/nvhe/pkvm.c | 38 ++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 13 deletions(-)
arch/arm64/kvm/hyp/nvhe/pkvm.c +25 -13
@@ -266,7 +266,8 @@ struct pkvm_hyp_vcpu *pkvm_load_hyp_vcpu(pkvm_handle_t handle,if (hyp_vm->kvm.created_vcpus <= vcpu_idx) goto unlock; - hyp_vcpu = hyp_vm->vcpus[vcpu_idx]; +/* Pairs with smp_store_release() in register_hyp_vcpu(). */ + hyp_vcpu =smp_load_acquire (&hyp_vm->vcpus[vcpu_idx]);if (!hyp_vcpu) goto unlock;
@@ -860,12 +861,30 @@ int __pkvm_init_vm(struct kvm *host_kvm, unsigned long vm_hva,* the page-aligned size of 'struct pkvm_hyp_vcpu'. * Return 0 on success, negative error code on failure. */ +static int register_hyp_vcpu (struct pkvm_hyp_vm *hyp_vm, +struct pkvm_hyp_vcpu *hyp_vcpu) +{ +unsigned int idx = hyp_vcpu->vcpu.vcpu_idx; + +if (idx >= hyp_vm->kvm.created_vcpus) +return -EINVAL ; + +if (hyp_vm->vcpus[idx]) +return -EINVAL ; + +/* +* Ensure the hyp_vcpu is initialised before publishing it to +* the vCPU-load path via 'hyp_vm->vcpus[]'. +*/ +smp_store_release (&hyp_vm->vcpus[idx], hyp_vcpu); +return 0 ; +} +int __pkvm_init_vcpu (pkvm_handle_t handle,struct kvm_vcpu *host_vcpu,unsigned long vcpu_hva) {struct pkvm_hyp_vcpu *hyp_vcpu;struct pkvm_hyp_vm *hyp_vm; -unsigned int idx;int ret; hyp_vcpu =map_donated_memory (vcpu_hva,sizeof (*hyp_vcpu));
@@ -884,18 +903,11 @@ int __pkvm_init_vcpu(pkvm_handle_t handle, struct kvm_vcpu *host_vcpu,if (ret) goto unlock; - idx = hyp_vcpu->vcpu.vcpu_idx; -if (idx >= hyp_vm->kvm.created_vcpus) { - ret = -EINVAL ; - goto unlock; + ret =register_hyp_vcpu (hyp_vm, hyp_vcpu); +if (ret) { +unpin_host_vcpu (host_vcpu); +unpin_host_sve_state (hyp_vcpu); } - -if (hyp_vm->vcpus[idx]) { - ret = -EINVAL ; - goto unlock; - } - - hyp_vm->vcpus[idx] = hyp_vcpu; unlock:hyp_spin_unlock (&vm_table_lock);
Signature · 1 line
2.54.0.rc2.544.gc7ae2d5bb8-goog
PATCH 6/6 KVM: arm64: Fix initialisation order in __pkvm_init_finalise()
In reply to Fuad TabbaFrom: Quentin Perret <[email protected]>
fix_host_ownership() walks the hypervisor's stage-1 page-table to adjust the host's stage-2 accordingly. Any such adjustment that requires cache maintenance operations depends on the per-CPU hyp fixmap being present. However, fix_host_ownership() is currently called before fix_hyp_pgtable_refcnt() and hyp_create_fixmap(), so the fixmap does not yet exist when it runs.
This is benign today because the host stage-2 starts empty and no CMOs are needed, but it becomes a latent crash as soon as fix_host_ownership() is extended to operate on a non-empty page-table.
Reorder the calls so that fix_hyp_pgtable_refcnt() and hyp_create_fixmap() complete before fix_host_ownership() is invoked.
--- arch/arm64/kvm/hyp/nvhe/setup.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
arch/arm64/kvm/hyp/nvhe/setup.c +4 -4
@@ -312,10 +312,6 @@ void __noreturn __pkvm_init_finalise(void) }; pkvm_pgtable.mm_ops = &pkvm_pgtable_mm_ops; - ret =fix_host_ownership (); -if (ret) - goto out; - ret =fix_hyp_pgtable_refcnt ();if (ret) goto out;
@@ -324,6 +320,10 @@ void __noreturn __pkvm_init_finalise(void)if (ret) goto out; + ret =fix_host_ownership (); +if (ret) + goto out; + ret =hyp_ffa_init (ffa_proxy_pages);if (ret) goto out;
Signature · 1 line
2.54.0.rc2.544.gc7ae2d5bb8-goog
Marc Zyngier
maintainerQuoted 8 lines · Fuad Tabba
Hi folks,
These six patches are standalone correctness fixes I'd like to land before posting a follow-up to Will's pKVM infrastructure series [1] that moves vCPU state management to EL2. Sending them separately keeps the bigger series focused, but they are all valid fixes to have regardless.
Thanks for posting these, and splitting them from the largest series. I've taken them as a whole, and marked the as Cc stable, except for patch #2.
Cheers,
M.
Signature · 1 line
Without deviation from the norm, progress is not possible.
Quoted 6 lines · Fuad Tabba
These six patches are standalone correctness fixes I'd like to land before posting a follow-up to Will's pKVM infrastructure series [1] that moves vCPU state management to EL2. Sending them separately keeps the bigger series focused, but they are all valid fixes to have regardless.
Quoted 6 lines
The first patch fixes feature detection for
FEAT_Debugv8p9: it was checking the wrong field inID_AA64DFR0_EL1, causing KVM to treat certain EL2 control bits as RES0 on hardware that implements the feature.[...]
Applied to fixes, thanks!
[1/6] KVM: arm64: Fix FEAT_Debugv8p9 to check DebugVer, not PMUVer
commit: 7fe2cd4e1a3ad230d8fcc00cc99c4bcce4412a75
[2/6] KVM: arm64: Fix typo in feature check comments
commit: 2a623408112626d2625a6f00aed665861d59665c
[3/6] KVM: arm64: Fix FEAT_SPE_FnE to use PMSIDR_EL1.FnE, not PMSVer
commit: 08d715338287a1affb4c7ad5733decef4558a5c8
[4/6] KVM: arm64: Fix kvm_vcpu_initialized() macro parameter
commit: d89fdda7dd8a488f922e1175e6782f781ba8a23b
[5/6] KVM: arm64: Fix pin leak and publication ordering in __pkvm_init_vcpu()
commit: 73b9c1e5da84cd69b1a86e374e450817cd051371
[6/6] KVM: arm64: Fix initialisation order in __pkvm_init_finalise()
commit: 5bb0aed57ba944f8c201e4e82ec066e0187e0f85Cheers,
M.
Signature · 1 line
Without deviation from the norm, progress is not possible.