KVM: Allow architectures to disallow pre-fault

All existing architectures which implement KVM pre-fault (x86, s390) have
mechanisms for disallowing pre-faulting.

Currently these are open coded as part of kvm_arch_vcpu_pre_fault_memory().

Formalise this by moving them into a new kvm_arch_pre_fault_allowed() hook,
which every architecture selecting CONFIG_KVM_GENERIC_PRE_FAULT_MEMORY must
implement, returning an error code if the operation is disallowed or 0
otherwise.

The hook is called early in the generic code, allowing architectures to
disallow the operation prior to vCPU load.

This is important, as kvm_vcpu_pre_fault_memory() is the only place where
generic code can call vcpu_load() on a vCPU that has not yet been
initialised.

This lays the foundation for a future change which implements pre-faulting
for arm64 which needs to disallow the mechanism for uninitialised vCPUs.

Suggested-by: Oliver Upton <oupton@kernel.org>
Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
Acked-by: Sean Christopherson <seanjc@google.com>
Reviewed-by: Fuad Tabba <fuad.tabba@linux.dev>
Tested-by: Fuad Tabba <fuad.tabba@linux.dev>
Link: https://patch.msgid.link/20260923-kvm-arm-prefault-v4-1-d4b0b4dfa8c3@kernel.org
Signed-off-by: Marc Zyngier <maz@kernel.org>
This commit is contained in:
Lorenzo Stoakes (ARM) 2026-09-23 16:15:44 +01:00 committed by Marc Zyngier
parent c2778d14cd
commit caa63620f9
5 changed files with 27 additions and 8 deletions

View File

@ -5784,6 +5784,14 @@ void kvm_arch_commit_memory_region(struct kvm *kvm, struct kvm_memory_slot *old,
s390_kvm_mmu_commit_memory_region(kvm, old, new, change);
}
int kvm_arch_pre_fault_allowed(struct kvm_vcpu *vcpu)
{
if (kvm_is_ucontrol(vcpu->kvm))
return -EINVAL;
return 0;
}
/**
* kvm_arch_vcpu_pre_fault_memory() -- pre-fault and link gmap dat tables
* @vcpu: the vcpu that shall appear to have generated the fault-in.
@ -5810,9 +5818,6 @@ long kvm_arch_vcpu_pre_fault_memory(struct kvm_vcpu *vcpu, struct kvm_pre_fault_
gpa_t end;
int rc;
if (kvm_is_ucontrol(vcpu->kvm))
return -EINVAL;
rc = kvm_s390_faultin_gfn(vcpu, NULL, &f);
if (rc == PGM_ADDRESSING)
return -ENOENT;

View File

@ -5086,6 +5086,14 @@ static int kvm_tdp_page_prefault(struct kvm_vcpu *vcpu, gpa_t gpa,
}
}
int kvm_arch_pre_fault_allowed(struct kvm_vcpu *vcpu)
{
if (!vcpu->kvm->arch.pre_fault_allowed)
return -EOPNOTSUPP;
return 0;
}
long kvm_arch_vcpu_pre_fault_memory(struct kvm_vcpu *vcpu,
struct kvm_pre_fault_memory *range)
{
@ -5095,9 +5103,6 @@ long kvm_arch_vcpu_pre_fault_memory(struct kvm_vcpu *vcpu,
u64 end;
int r;
if (!vcpu->kvm->arch.pre_fault_allowed)
return -EOPNOTSUPP;
if (kvm_is_gfn_alias(vcpu->kvm, gpa_to_gfn(range->gpa)))
return -EINVAL;

View File

@ -1708,7 +1708,7 @@ static int tdx_sept_map_leaf_spte(struct kvm *kvm, gfn_t gfn, enum pg_level leve
WARN_ON_ONCE((new_spte & VMX_EPT_RWX_MASK) != VMX_EPT_RWX_MASK);
/*
* Ensure pre_fault_allowed is read by kvm_arch_vcpu_pre_fault_memory()
* Ensure pre_fault_allowed is read by kvm_arch_pre_fault_allowed()
* before kvm_tdx->state. Userspace must not be allowed to pre-fault
* arbitrary memory until the initial memory image is finalized. Pairs
* with the smp_wmb() in tdx_td_finalize().

View File

@ -2643,6 +2643,7 @@ void kvm_arch_gmem_invalidate_range(struct kvm *kvm, struct kvm_gfn_range *range
#endif
#ifdef CONFIG_KVM_GENERIC_PRE_FAULT_MEMORY
int kvm_arch_pre_fault_allowed(struct kvm_vcpu *vcpu);
long kvm_arch_vcpu_pre_fault_memory(struct kvm_vcpu *vcpu,
struct kvm_pre_fault_memory *range);
#endif

View File

@ -4353,7 +4353,7 @@ static int kvm_vcpu_ioctl_get_stats_fd(struct kvm_vcpu *vcpu)
static int kvm_vcpu_pre_fault_memory(struct kvm_vcpu *vcpu,
struct kvm_pre_fault_memory *range)
{
int idx;
int idx, err;
long r;
u64 full_size;
@ -4365,6 +4365,14 @@ static int kvm_vcpu_pre_fault_memory(struct kvm_vcpu *vcpu,
range->gpa + range->size <= range->gpa)
return -EINVAL;
/*
* Certain architectures (e.g. arm64) need to reject the ioctl 'early'
* before vcpu_load().
*/
err = kvm_arch_pre_fault_allowed(vcpu);
if (err)
return err;
vcpu_load(vcpu);
idx = srcu_read_lock(&vcpu->kvm->srcu);