[PATCH v2 07/10] KVM: x86: Always populate L1 GPA for KVM_MAP_MEMORY
From: isaku . yamahata
Date: Wed Apr 10 2024 - 18:09:57 EST
From: Isaku Yamahata <isaku.yamahata@xxxxxxxxx>
Forcibly switch vCPU mode out from guest mode and SMM mode before calling
KVM page fault handler for KVM_MAP_MEMORY.
KVM_MAP_MEMORY populates guest memory with guest physical address (GPA).
If the vCPU is in guest mode, it populates with L2 GPA. If vCPU is in SMM
mode, it populates the SMM address pace. The API would be difficult to use
as such. Change vCPU MMU mode around populating the guest memory to always
populate with L1 GPA.
There are several options to populate L1 GPA irrelevant to vCPU mode.
- Switch vCPU MMU only: This patch.
Pros: Concise implementation.
Cons: Heavily dependent on the KVM MMU implementation.
- Use kvm_x86_nested_ops.get/set_state() to switch to/from guest mode.
Use __get/set_sregs2() to switch to/from SMM mode.
Pros: straightforward.
Cons: This may cause unintended side effects.
- Refactor KVM page fault handler not to pass vCPU. Pass around necessary
parameters and struct kvm.
Pros: The end result will have clearly no side effects.
Cons: This will require big refactoring.
- Return error on guest mode or SMM mode: Without this patch.
Pros: No additional patch.
Cons: Difficult to use.
Signed-off-by: Isaku Yamahata <isaku.yamahata@xxxxxxxxx>
---
v2:
- Newly added.
---
arch/x86/kvm/x86.c | 26 +++++++++++++++++++++++++-
1 file changed, 25 insertions(+), 1 deletion(-)
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 2c765de3531e..8ba9c1720ac9 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -5871,8 +5871,10 @@ static int kvm_vcpu_ioctl_enable_cap(struct kvm_vcpu *vcpu,
int kvm_arch_vcpu_map_memory(struct kvm_vcpu *vcpu,
struct kvm_memory_mapping *mapping)
{
+ struct kvm_mmu *mmu = NULL, *walk_mmu = NULL;
u64 end, error_code = 0;
u8 level = PG_LEVEL_4K;
+ bool is_smm;
int r;
/*
@@ -5882,18 +5884,40 @@ int kvm_arch_vcpu_map_memory(struct kvm_vcpu *vcpu,
if (!tdp_enabled)
return -EOPNOTSUPP;
+ /* Force to use L1 GPA despite of vcpu MMU mode. */
+ is_smm = !!(vcpu->arch.hflags & HF_SMM_MASK);
+ if (is_smm ||
+ vcpu->arch.mmu != &vcpu->arch.root_mmu ||
+ vcpu->arch.walk_mmu != &vcpu->arch.root_mmu) {
+ vcpu->arch.hflags &= ~HF_SMM_MASK;
+ mmu = vcpu->arch.mmu;
+ walk_mmu = vcpu->arch.walk_mmu;
+ vcpu->arch.mmu = &vcpu->arch.root_mmu;
+ vcpu->arch.walk_mmu = &vcpu->arch.root_mmu;
+ kvm_mmu_reset_context(vcpu);
+ }
+
/* reload is optimized for repeated call. */
kvm_mmu_reload(vcpu);
r = kvm_tdp_map_page(vcpu, mapping->base_address, error_code, &level);
if (r)
- return r;
+ goto out;
/* mapping->base_address is not necessarily aligned to level-hugepage. */
end = (mapping->base_address & KVM_HPAGE_MASK(level)) +
KVM_HPAGE_SIZE(level);
mapping->size -= end - mapping->base_address;
mapping->base_address = end;
+
+out:
+ /* Restore MMU state. */
+ if (is_smm || mmu) {
+ vcpu->arch.hflags |= is_smm ? HF_SMM_MASK : 0;
+ vcpu->arch.mmu = mmu;
+ vcpu->arch.walk_mmu = walk_mmu;
+ kvm_mmu_reset_context(vcpu);
+ }
return r;
}
--
2.43.2