[RFC PATCH 39/73] KVM: x86/PVM: Handle hypercall for CR3 switching

Lai Jiangshan
2024-02-26T14:36:45Z

If the guest uses the same page table for supervisor mode and user mode,
then the user mode can access the supervisor mode address space.
Therefore, for safety, the guest needs to provide two different page
tables for one process, which is similar to KPTI. When switching CR3
during the process switching, the guest uses the hypercall to provide
the two page tables for the hypervisor, and then the hypervisor can
switch CR3 during the mode switch automatically. Additionally, an extra
flag is introduced to perform TLB flushing at the same time.

Signed-off-by: Lai Jiangshan <jiangshan.ljs@antgroup.com>
Signed-off-by: Hou Wenlong <houwenlong.hwl@antgroup.com>
---
arch/x86/kvm/pvm/pvm.c | 41 ++++++++++++++++++++++++++++++++++++++++-
1 file changed, 40 insertions(+), 1 deletion(-)

diff --git a/arch/x86/kvm/pvm/pvm.c b/arch/x86/kvm/pvm/pvm.c
index 8d8c783c72b5..ad08643c098a 100644
--- a/arch/x86/kvm/pvm/pvm.c
+++ b/arch/x86/kvm/pvm/pvm.c
@@ -1459,6 +1459,42 @@ static void pvm_flush_tlb_guest_current_kernel_user(struct kvm_vcpu *vcpu)
kvm_make_request(KVM_REQ_TLB_FLUSH_GUEST, vcpu);
}

+/*
+ * Hypercall: PVM_HC_LOAD_PGTBL
+ * Load two PGDs into the current CR3 and MSR_PVM_SWITCH_CR3.
+ *
+ * Arguments:
+ * flags: bit0: flush the TLBs tagged with @pgd and @user_pgd.
+ * bit1: 4 (bit1=0) or 5 (bit1=1 && cpuid_has(LA57)) level paging.
+ * pgd: to be loaded into CR3.
+ * user_pgd: to be loaded into MSR_PVM_SWITCH_CR3.
+ */
+static int handle_hc_load_pagetables(struct kvm_vcpu *vcpu, unsigned long flags,
+ unsigned long pgd, unsigned long user_pgd)
+{
+ struct vcpu_pvm *pvm = to_pvm(vcpu);
+ unsigned long cr4 = vcpu->arch.cr4;
+
+ if (!(flags & 2))
+ cr4 &= ~X86_CR4_LA57;
+ else if (guest_cpuid_has(vcpu, X86_FEATURE_LA57))
+ cr4 |= X86_CR4_LA57;
+
+ if (cr4 != vcpu->arch.cr4) {
+ vcpu->arch.cr4 = cr4;
+ kvm_mmu_reset_context(vcpu);
+ }
+
+ kvm_mmu_new_pgd(vcpu, pgd);
+ vcpu->arch.cr3 = pgd;
+ pvm->msr_switch_cr3 = user_pgd;
+
+ if (flags & 1)
+ pvm_flush_tlb_guest_current_kernel_user(vcpu);
+
+ return 1;
+}
+
/*
* Hypercall: PVM_HC_TLB_FLUSH
* Flush all TLBs.
@@ -1540,7 +1576,7 @@ static int handle_exit_syscall(struct kvm_vcpu *vcpu)
{
struct vcpu_pvm *pvm = to_pvm(vcpu);
unsigned long rip = kvm_rip_read(vcpu);
- unsigned long a0, a1;
+ unsigned long a0, a1, a2;

if (!is_smod(pvm))
return do_pvm_user_event(vcpu, PVM_SYSCALL_VECTOR, false, 0);
@@ -1552,6 +1588,7 @@ static int handle_exit_syscall(struct kvm_vcpu *vcpu)

a0 = kvm_rbx_read(vcpu);
a1 = kvm_r10_read(vcpu);
+ a2 = kvm_rdx_read(vcpu);

// handle hypercall, check it for pvm hypercall and then kvm hypercall
switch (kvm_rax_read(vcpu)) {
@@ -1559,6 +1596,8 @@ static int handle_exit_syscall(struct kvm_vcpu *vcpu)
return handle_hc_interrupt_window(vcpu);
case PVM_HC_IRQ_HALT:
return handle_hc_irq_halt(vcpu);
+ case PVM_HC_LOAD_PGTBL:
+ return handle_hc_load_pagetables(vcpu, a0, a1, a2);
case PVM_HC_TLB_FLUSH:
return handle_hc_flush_tlb_all(vcpu);
case PVM_HC_TLB_FLUSH_CURRENT:
--
2.19.1.6.gb485710b