Commit 3f4a812e authored by Vitaly Kuznetsov's avatar Vitaly Kuznetsov Committed by Paolo Bonzini

KVM: nSVM: hyper-v: Enable L2 TLB flush

Implement Hyper-V L2 TLB flush for nSVM. The feature needs to be enabled
both in extended 'nested controls' in VMCB and VP assist page.
According to Hyper-V TLFS, synthetic vmexit to L1 is performed with
- HV_SVM_EXITCODE_ENL exit_code.
- HV_SVM_ENL_EXITCODE_TRAP_AFTER_FLUSH exit_info_1.

Note: VP assist page is cached in 'struct kvm_vcpu_hv' so
recalc_intercepts() doesn't need to read from guest's memory. KVM
needs to update the case upon each VMRUN and after svm_set_nested_state
(svm_get_nested_state_pages()) to handle the case when the guest got
migrated while L2 was running.
Reviewed-by: default avatarSean Christopherson <seanjc@google.com>
Signed-off-by: default avatarVitaly Kuznetsov <vkuznets@redhat.com>
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
Message-Id: <20221101145426.251680-29-vkuznets@redhat.com>
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent b415d8d4
...@@ -631,6 +631,10 @@ struct hv_vmcb_enlightenments { ...@@ -631,6 +631,10 @@ struct hv_vmcb_enlightenments {
*/ */
#define HV_VMCB_NESTED_ENLIGHTENMENTS 31 #define HV_VMCB_NESTED_ENLIGHTENMENTS 31
/* Synthetic VM-Exit */
#define HV_SVM_EXITCODE_ENL 0xf0000000
#define HV_SVM_ENL_EXITCODE_TRAP_AFTER_FLUSH (1)
struct hv_partition_assist_pg { struct hv_partition_assist_pg {
u32 tlb_lock_count; u32 tlb_lock_count;
}; };
......
...@@ -198,6 +198,17 @@ static inline bool kvm_hv_is_tlb_flush_hcall(struct kvm_vcpu *vcpu) ...@@ -198,6 +198,17 @@ static inline bool kvm_hv_is_tlb_flush_hcall(struct kvm_vcpu *vcpu)
code == HVCALL_FLUSH_VIRTUAL_ADDRESS_LIST_EX); code == HVCALL_FLUSH_VIRTUAL_ADDRESS_LIST_EX);
} }
static inline int kvm_hv_verify_vp_assist(struct kvm_vcpu *vcpu)
{
if (!to_hv_vcpu(vcpu))
return 0;
if (!kvm_hv_assist_page_enabled(vcpu))
return 0;
return kvm_hv_get_assist_page(vcpu);
}
int kvm_hv_vcpu_flush_tlb(struct kvm_vcpu *vcpu); int kvm_hv_vcpu_flush_tlb(struct kvm_vcpu *vcpu);
#endif #endif
...@@ -8,4 +8,11 @@ ...@@ -8,4 +8,11 @@
void svm_hv_inject_synthetic_vmexit_post_tlb_flush(struct kvm_vcpu *vcpu) void svm_hv_inject_synthetic_vmexit_post_tlb_flush(struct kvm_vcpu *vcpu)
{ {
struct vcpu_svm *svm = to_svm(vcpu);
svm->vmcb->control.exit_code = HV_SVM_EXITCODE_ENL;
svm->vmcb->control.exit_code_hi = 0;
svm->vmcb->control.exit_info_1 = HV_SVM_ENL_EXITCODE_TRAP_AFTER_FLUSH;
svm->vmcb->control.exit_info_2 = 0;
nested_svm_vmexit(svm);
} }
...@@ -25,6 +25,21 @@ static inline void nested_svm_hv_update_vm_vp_ids(struct kvm_vcpu *vcpu) ...@@ -25,6 +25,21 @@ static inline void nested_svm_hv_update_vm_vp_ids(struct kvm_vcpu *vcpu)
hv_vcpu->nested.vp_id = hve->hv_vp_id; hv_vcpu->nested.vp_id = hve->hv_vp_id;
} }
static inline bool nested_svm_l2_tlb_flush_enabled(struct kvm_vcpu *vcpu)
{
struct vcpu_svm *svm = to_svm(vcpu);
struct hv_vmcb_enlightenments *hve = &svm->nested.ctl.hv_enlightenments;
struct kvm_vcpu_hv *hv_vcpu = to_hv_vcpu(vcpu);
if (!hv_vcpu)
return false;
if (!hve->hv_enlightenments_control.nested_flush_hypercall)
return false;
return hv_vcpu->vp_assist_page.nested_control.features.directhypercall;
}
void svm_hv_inject_synthetic_vmexit_post_tlb_flush(struct kvm_vcpu *vcpu); void svm_hv_inject_synthetic_vmexit_post_tlb_flush(struct kvm_vcpu *vcpu);
#endif /* __ARCH_X86_KVM_SVM_HYPERV_H__ */ #endif /* __ARCH_X86_KVM_SVM_HYPERV_H__ */
...@@ -150,7 +150,11 @@ void recalc_intercepts(struct vcpu_svm *svm) ...@@ -150,7 +150,11 @@ void recalc_intercepts(struct vcpu_svm *svm)
vmcb_clr_intercept(c, INTERCEPT_VINTR); vmcb_clr_intercept(c, INTERCEPT_VINTR);
} }
/* We don't want to see VMMCALLs from a nested guest */ /*
* We want to see VMMCALLs from a nested guest only when Hyper-V L2 TLB
* flush feature is enabled.
*/
if (!nested_svm_l2_tlb_flush_enabled(&svm->vcpu))
vmcb_clr_intercept(c, INTERCEPT_VMMCALL); vmcb_clr_intercept(c, INTERCEPT_VMMCALL);
for (i = 0; i < MAX_INTERCEPT; i++) for (i = 0; i < MAX_INTERCEPT; i++)
...@@ -473,6 +477,15 @@ static void nested_save_pending_event_to_vmcb12(struct vcpu_svm *svm, ...@@ -473,6 +477,15 @@ static void nested_save_pending_event_to_vmcb12(struct vcpu_svm *svm,
static void nested_svm_transition_tlb_flush(struct kvm_vcpu *vcpu) static void nested_svm_transition_tlb_flush(struct kvm_vcpu *vcpu)
{ {
/*
* KVM_REQ_HV_TLB_FLUSH flushes entries from either L1's VP_ID or
* L2's VP_ID upon request from the guest. Make sure we check for
* pending entries in the right FIFO upon L1/L2 transition as these
* requests are put by other vCPUs asynchronously.
*/
if (to_hv_vcpu(vcpu) && npt_enabled)
kvm_make_request(KVM_REQ_HV_TLB_FLUSH, vcpu);
/* /*
* TODO: optimize unconditional TLB flush/MMU sync. A partial list of * TODO: optimize unconditional TLB flush/MMU sync. A partial list of
* things to fix before this can be conditional: * things to fix before this can be conditional:
...@@ -824,6 +837,13 @@ int nested_svm_vmrun(struct kvm_vcpu *vcpu) ...@@ -824,6 +837,13 @@ int nested_svm_vmrun(struct kvm_vcpu *vcpu)
return 1; return 1;
} }
/* This fails when VP assist page is enabled but the supplied GPA is bogus */
ret = kvm_hv_verify_vp_assist(vcpu);
if (ret) {
kvm_inject_gp(vcpu, 0);
return ret;
}
vmcb12_gpa = svm->vmcb->save.rax; vmcb12_gpa = svm->vmcb->save.rax;
ret = kvm_vcpu_map(vcpu, gpa_to_gfn(vmcb12_gpa), &map); ret = kvm_vcpu_map(vcpu, gpa_to_gfn(vmcb12_gpa), &map);
if (ret == -EINVAL) { if (ret == -EINVAL) {
...@@ -1421,6 +1441,7 @@ static int svm_check_nested_events(struct kvm_vcpu *vcpu) ...@@ -1421,6 +1441,7 @@ static int svm_check_nested_events(struct kvm_vcpu *vcpu)
int nested_svm_exit_special(struct vcpu_svm *svm) int nested_svm_exit_special(struct vcpu_svm *svm)
{ {
u32 exit_code = svm->vmcb->control.exit_code; u32 exit_code = svm->vmcb->control.exit_code;
struct kvm_vcpu *vcpu = &svm->vcpu;
switch (exit_code) { switch (exit_code) {
case SVM_EXIT_INTR: case SVM_EXIT_INTR:
...@@ -1439,6 +1460,13 @@ int nested_svm_exit_special(struct vcpu_svm *svm) ...@@ -1439,6 +1460,13 @@ int nested_svm_exit_special(struct vcpu_svm *svm)
return NESTED_EXIT_HOST; return NESTED_EXIT_HOST;
break; break;
} }
case SVM_EXIT_VMMCALL:
/* Hyper-V L2 TLB flush hypercall is handled by L0 */
if (guest_hv_cpuid_has_l2_tlb_flush(vcpu) &&
nested_svm_l2_tlb_flush_enabled(vcpu) &&
kvm_hv_is_tlb_flush_hcall(vcpu))
return NESTED_EXIT_HOST;
break;
default: default:
break; break;
} }
...@@ -1719,6 +1747,9 @@ static bool svm_get_nested_state_pages(struct kvm_vcpu *vcpu) ...@@ -1719,6 +1747,9 @@ static bool svm_get_nested_state_pages(struct kvm_vcpu *vcpu)
return false; return false;
} }
if (kvm_hv_verify_vp_assist(vcpu))
return false;
return true; return true;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment