From: Chuanxiao Dong <chuanxiao.dong@xxxxxxxxx> KVM-high manages a guest EPT for its nested VM, which shall be shadowed to shadow EPT in pKVM hypervisor. Introduce a per vcpu pgtable(vEPT) to save the guest EPT related information from KVM-high when vmwrite(EPTP). Each time when a vcpu vmwrites to EPTP, pKVM will setup the vEPT according to the new value or just do nothing if it is the same with the last one. Signed-off-by: Chuanxiao Dong <chuanxiao.dong@xxxxxxxxx> Signed-off-by: Jason Chen CJ <jason.cj.chen@xxxxxxxxx> --- arch/x86/kvm/vmx/pkvm/hyp/ept.c | 33 ++++++++++++++++++++++++++++ arch/x86/kvm/vmx/pkvm/hyp/ept.h | 10 +++++++++ arch/x86/kvm/vmx/pkvm/hyp/memory.c | 6 +++++ arch/x86/kvm/vmx/pkvm/hyp/memory.h | 1 + arch/x86/kvm/vmx/pkvm/hyp/nested.c | 16 ++++++++++++++ arch/x86/kvm/vmx/pkvm/hyp/pkvm_hyp.h | 3 +++ 6 files changed, 69 insertions(+) diff --git a/arch/x86/kvm/vmx/pkvm/hyp/ept.c b/arch/x86/kvm/vmx/pkvm/hyp/ept.c index 0edea266b8bc..641b8252071e 100644 --- a/arch/x86/kvm/vmx/pkvm/hyp/ept.c +++ b/arch/x86/kvm/vmx/pkvm/hyp/ept.c @@ -297,3 +297,36 @@ int pkvm_shadow_ept_init(struct shadow_ept_desc *desc) return 0; } + +/* + * virtual_ept_mm_ops is used as the ops for the ept constructed by + * KVM high in host. + * The physical address in this ept is the host VM GPA, which is + * the same with HPA. + */ +struct pkvm_mm_ops virtual_ept_mm_ops = { + .phys_to_virt = host_gpa2hva, +}; + +void pkvm_guest_ept_deinit(struct shadow_vcpu_state *shadow_vcpu) +{ + struct pkvm_pgtable *vept = &shadow_vcpu->vept; + + memset(vept, 0, sizeof(struct pkvm_pgtable)); +} + +void pkvm_guest_ept_init(struct shadow_vcpu_state *shadow_vcpu, u64 guest_eptp) +{ + /* + * TODO: we just assume guest will use page level the HW supported, + * it actually need align with KVM high + */ + struct pkvm_pgtable_cap cap = { + .level = pkvm_hyp->ept_cap.level, + .allowed_pgsz = pkvm_hyp->ept_cap.allowed_pgsz, + .table_prot = pkvm_hyp->ept_cap.table_prot, + }; + + pkvm_pgtable_init(&shadow_vcpu->vept, &virtual_ept_mm_ops, &ept_ops, &cap, false); + shadow_vcpu->vept.root_pa = host_gpa2hpa(guest_eptp & SPTE_BASE_ADDR_MASK); +} diff --git a/arch/x86/kvm/vmx/pkvm/hyp/ept.h b/arch/x86/kvm/vmx/pkvm/hyp/ept.h index 7badcb3dd621..420c9c5816e9 100644 --- a/arch/x86/kvm/vmx/pkvm/hyp/ept.h +++ b/arch/x86/kvm/vmx/pkvm/hyp/ept.h @@ -22,5 +22,15 @@ int handle_host_ept_violation(unsigned long gpa); int pkvm_shadow_ept_pool_init(void *ept_pool_base, unsigned long ept_pool_pages); int pkvm_shadow_ept_init(struct shadow_ept_desc *desc); void pkvm_shadow_ept_deinit(struct shadow_ept_desc *desc); +void pkvm_guest_ept_init(struct shadow_vcpu_state *shadow_vcpu, u64 guest_eptp); +void pkvm_guest_ept_deinit(struct shadow_vcpu_state *shadow_vcpu); +static inline bool is_valid_eptp(u64 eptp) +{ + if (!eptp || (eptp == INVALID_GPA)) + return false; + + /* TODO: other bits check */ + return true; +} #endif diff --git a/arch/x86/kvm/vmx/pkvm/hyp/memory.c b/arch/x86/kvm/vmx/pkvm/hyp/memory.c index 6a400aef1bd8..43fc39d44c3d 100644 --- a/arch/x86/kvm/vmx/pkvm/hyp/memory.c +++ b/arch/x86/kvm/vmx/pkvm/hyp/memory.c @@ -73,6 +73,12 @@ void *host_gpa2hva(unsigned long gpa) return pkvm_phys_to_virt(gpa); } +unsigned long host_gpa2hpa(unsigned long gpa) +{ + /* Host VM is using identity mapping so GPA == HPA */ + return gpa; +} + extern struct pkvm_pgtable_ops mmu_ops; static struct pkvm_mm_ops mm_ops = { .phys_to_virt = host_gpa2hva, diff --git a/arch/x86/kvm/vmx/pkvm/hyp/memory.h b/arch/x86/kvm/vmx/pkvm/hyp/memory.h index 4a75d8dff1b3..a95ae5f71841 100644 --- a/arch/x86/kvm/vmx/pkvm/hyp/memory.h +++ b/arch/x86/kvm/vmx/pkvm/hyp/memory.h @@ -22,6 +22,7 @@ bool mem_range_included(struct mem_range *child, struct mem_range *parent); #include <linux/kvm_host.h> void *host_gpa2hva(unsigned long gpa); +unsigned long host_gpa2hpa(unsigned long gpa); int gva2gpa(struct kvm_vcpu *vcpu, gva_t gva, gpa_t *gpa, u32 access, struct x86_exception *exception); int read_gva(struct kvm_vcpu *vcpu, gva_t gva, void *addr, diff --git a/arch/x86/kvm/vmx/pkvm/hyp/nested.c b/arch/x86/kvm/vmx/pkvm/hyp/nested.c index 429bfe7bb309..68eddb459cfa 100644 --- a/arch/x86/kvm/vmx/pkvm/hyp/nested.c +++ b/arch/x86/kvm/vmx/pkvm/hyp/nested.c @@ -9,6 +9,7 @@ #include "nested.h" #include "cpu.h" #include "vmx.h" +#include "ept.h" #include "debug.h" /* @@ -704,6 +705,18 @@ static void nested_vmx_run(struct kvm_vcpu *vcpu, bool launch) } } +static void setup_guest_ept(struct shadow_vcpu_state *shadow_vcpu, u64 guest_eptp) +{ + struct vmcs12 *vmcs12 = (struct vmcs12 *)shadow_vcpu->cached_vmcs12; + + if (!is_valid_eptp(guest_eptp)) + pkvm_guest_ept_deinit(shadow_vcpu); + else if (vmcs12->ept_pointer != guest_eptp) { + pkvm_guest_ept_deinit(shadow_vcpu); + pkvm_guest_ept_init(shadow_vcpu, guest_eptp); + } +} + int handle_vmxon(struct kvm_vcpu *vcpu) { struct vcpu_vmx *vmx = to_vmx(vcpu); @@ -908,6 +921,9 @@ int handle_vmwrite(struct kvm_vcpu *vcpu) if (field >= GUEST_ES_AR_BYTES && field <= GUEST_TR_AR_BYTES) value &= 0x1f0ff; + if (field == EPT_POINTER) + setup_guest_ept(cur_shadow_vcpu, value); + vmcs12_write_any(vmcs12, field, offset, value); if (is_emulated_fields(field)) { diff --git a/arch/x86/kvm/vmx/pkvm/hyp/pkvm_hyp.h b/arch/x86/kvm/vmx/pkvm/hyp/pkvm_hyp.h index a1f3644a4a34..f891660d9085 100644 --- a/arch/x86/kvm/vmx/pkvm/hyp/pkvm_hyp.h +++ b/arch/x86/kvm/vmx/pkvm/hyp/pkvm_hyp.h @@ -41,6 +41,9 @@ struct shadow_vcpu_state { struct vcpu_vmx vmx; + /* represents for the virtual EPT configured by kvm-high */ + struct pkvm_pgtable vept; + /* assume vmcs02 is one page */ u8 vmcs02[PAGE_SIZE] __aligned(PAGE_SIZE); u8 cached_vmcs12[VMCS12_SIZE] __aligned(PAGE_SIZE); -- 2.25.1