| From 46d6c6f3ef0eaff71c2db6d77d4e2ebb7adac34f Mon Sep 17 00:00:00 2001 |
| From: Sean Christopherson <seanjc@google.com> |
| Date: Wed, 29 Jan 2025 17:08:25 -0800 |
| Subject: KVM: nSVM: Enter guest mode before initializing nested NPT MMU |
| |
| From: Sean Christopherson <seanjc@google.com> |
| |
| commit 46d6c6f3ef0eaff71c2db6d77d4e2ebb7adac34f upstream. |
| |
| When preparing vmcb02 for nested VMRUN (or state restore), "enter" guest |
| mode prior to initializing the MMU for nested NPT so that guest_mode is |
| set in the MMU's role. KVM's model is that all L2 MMUs are tagged with |
| guest_mode, as the behavior of hypervisor MMUs tends to be significantly |
| different than kernel MMUs. |
| |
| Practically speaking, the bug is relatively benign, as KVM only directly |
| queries role.guest_mode in kvm_mmu_free_guest_mode_roots() and |
| kvm_mmu_page_ad_need_write_protect(), which SVM doesn't use, and in paths |
| that are optimizations (mmu_page_zap_pte() and |
| shadow_mmu_try_split_huge_pages()). |
| |
| And while the role is incorprated into shadow page usage, because nested |
| NPT requires KVM to be using NPT for L1, reusing shadow pages across L1 |
| and L2 is impossible as L1 MMUs will always have direct=1, while L2 MMUs |
| will have direct=0. |
| |
| Hoist the TLB processing and setting of HF_GUEST_MASK to the beginning |
| of the flow instead of forcing guest_mode in the MMU, as nothing in |
| nested_vmcb02_prepare_control() between the old and new locations touches |
| TLB flush requests or HF_GUEST_MASK, i.e. there's no reason to present |
| inconsistent vCPU state to the MMU. |
| |
| Fixes: 69cb877487de ("KVM: nSVM: move MMU setup to nested_prepare_vmcb_control") |
| Cc: stable@vger.kernel.org |
| Reported-by: Yosry Ahmed <yosry.ahmed@linux.dev> |
| Reviewed-by: Yosry Ahmed <yosry.ahmed@linux.dev> |
| Link: https://lore.kernel.org/r/20250130010825.220346-1-seanjc@google.com |
| Signed-off-by: Sean Christopherson <seanjc@google.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| arch/x86/kvm/mmu/mmu.c | 2 +- |
| arch/x86/kvm/svm/nested.c | 10 +++++----- |
| 2 files changed, 6 insertions(+), 6 deletions(-) |
| |
| --- a/arch/x86/kvm/mmu/mmu.c |
| +++ b/arch/x86/kvm/mmu/mmu.c |
| @@ -5150,7 +5150,7 @@ void kvm_init_shadow_npt_mmu(struct kvm_ |
| union kvm_mmu_page_role root_role; |
| |
| /* NPT requires CR0.PG=1. */ |
| - WARN_ON_ONCE(cpu_role.base.direct); |
| + WARN_ON_ONCE(cpu_role.base.direct || !cpu_role.base.guest_mode); |
| |
| root_role = cpu_role.base; |
| root_role.level = kvm_mmu_get_tdp_level(vcpu); |
| --- a/arch/x86/kvm/svm/nested.c |
| +++ b/arch/x86/kvm/svm/nested.c |
| @@ -619,6 +619,11 @@ static void nested_vmcb02_prepare_contro |
| u32 pause_count12; |
| u32 pause_thresh12; |
| |
| + nested_svm_transition_tlb_flush(vcpu); |
| + |
| + /* Enter Guest-Mode */ |
| + enter_guest_mode(vcpu); |
| + |
| /* |
| * Filled at exit: exit_code, exit_code_hi, exit_info_1, exit_info_2, |
| * exit_int_info, exit_int_info_err, next_rip, insn_len, insn_bytes. |
| @@ -717,11 +722,6 @@ static void nested_vmcb02_prepare_contro |
| } |
| } |
| |
| - nested_svm_transition_tlb_flush(vcpu); |
| - |
| - /* Enter Guest-Mode */ |
| - enter_guest_mode(vcpu); |
| - |
| /* |
| * Merge guest and host intercepts - must be called with vcpu in |
| * guest-mode to take effect. |