Skip to content

Commit 75253db

Browse files
codomaniabp3tk0v
authored andcommitted
KVM: SEV: Make AVIC backing, VMSA and VMCB memory allocation SNP safe
Implement a workaround for an SNP erratum where the CPU will incorrectly signal an RMP violation #PF if a hugepage (2MB or 1GB) collides with the RMP entry of a VMCB, VMSA or AVIC backing page. When SEV-SNP is globally enabled, the CPU marks the VMCB, VMSA, and AVIC backing pages as "in-use" via a reserved bit in the corresponding RMP entry after a successful VMRUN. This is done for _all_ VMs, not just SNP-Active VMs. If the hypervisor accesses an in-use page through a writable translation, the CPU will throw an RMP violation #PF. On early SNP hardware, if an in-use page is 2MB-aligned and software accesses any part of the associated 2MB region with a hugepage, the CPU will incorrectly treat the entire 2MB region as in-use and signal a an RMP violation #PF. To avoid this, the recommendation is to not use a 2MB-aligned page for the VMCB, VMSA or AVIC pages. Add a generic allocator that will ensure that the page returned is not 2MB-aligned and is safe to be used when SEV-SNP is enabled. Also implement similar handling for the VMCB/VMSA pages of nested guests. [ mdr: Squash in nested guest handling from Ashish, commit msg fixups. ] Reported-by: Alper Gun <[email protected]> # for nested VMSA case Signed-off-by: Brijesh Singh <[email protected]> Co-developed-by: Marc Orr <[email protected]> Signed-off-by: Marc Orr <[email protected]> Co-developed-by: Ashish Kalra <[email protected]> Signed-off-by: Ashish Kalra <[email protected]> Signed-off-by: Michael Roth <[email protected]> Signed-off-by: Borislav Petkov (AMD) <[email protected]> Acked-by: Vlastimil Babka <[email protected]> Acked-by: Paolo Bonzini <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 8ef9795 commit 75253db

File tree

7 files changed

+54
-5
lines changed

7 files changed

+54
-5
lines changed

arch/x86/include/asm/kvm-x86-ops.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ KVM_X86_OP(complete_emulated_msr)
138138
KVM_X86_OP(vcpu_deliver_sipi_vector)
139139
KVM_X86_OP_OPTIONAL_RET0(vcpu_get_apicv_inhibit_reasons);
140140
KVM_X86_OP_OPTIONAL(get_untagged_addr)
141+
KVM_X86_OP_OPTIONAL(alloc_apic_backing_page)
141142

142143
#undef KVM_X86_OP
143144
#undef KVM_X86_OP_OPTIONAL

arch/x86/include/asm/kvm_host.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1794,6 +1794,7 @@ struct kvm_x86_ops {
17941794
unsigned long (*vcpu_get_apicv_inhibit_reasons)(struct kvm_vcpu *vcpu);
17951795

17961796
gva_t (*get_untagged_addr)(struct kvm_vcpu *vcpu, gva_t gva, unsigned int flags);
1797+
void *(*alloc_apic_backing_page)(struct kvm_vcpu *vcpu);
17971798
};
17981799

17991800
struct kvm_x86_nested_ops {

arch/x86/kvm/lapic.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2815,7 +2815,10 @@ int kvm_create_lapic(struct kvm_vcpu *vcpu, int timer_advance_ns)
28152815

28162816
vcpu->arch.apic = apic;
28172817

2818-
apic->regs = (void *)get_zeroed_page(GFP_KERNEL_ACCOUNT);
2818+
if (kvm_x86_ops.alloc_apic_backing_page)
2819+
apic->regs = static_call(kvm_x86_alloc_apic_backing_page)(vcpu);
2820+
else
2821+
apic->regs = (void *)get_zeroed_page(GFP_KERNEL_ACCOUNT);
28192822
if (!apic->regs) {
28202823
printk(KERN_ERR "malloc apic regs error for vcpu %x\n",
28212824
vcpu->vcpu_id);

arch/x86/kvm/svm/nested.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1181,7 +1181,7 @@ int svm_allocate_nested(struct vcpu_svm *svm)
11811181
if (svm->nested.initialized)
11821182
return 0;
11831183

1184-
vmcb02_page = alloc_page(GFP_KERNEL_ACCOUNT | __GFP_ZERO);
1184+
vmcb02_page = snp_safe_alloc_page(&svm->vcpu);
11851185
if (!vmcb02_page)
11861186
return -ENOMEM;
11871187
svm->nested.vmcb02.ptr = page_address(vmcb02_page);

arch/x86/kvm/svm/sev.c

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3163,3 +3163,35 @@ void sev_vcpu_deliver_sipi_vector(struct kvm_vcpu *vcpu, u8 vector)
31633163

31643164
ghcb_set_sw_exit_info_2(svm->sev_es.ghcb, 1);
31653165
}
3166+
3167+
struct page *snp_safe_alloc_page(struct kvm_vcpu *vcpu)
3168+
{
3169+
unsigned long pfn;
3170+
struct page *p;
3171+
3172+
if (!cpu_feature_enabled(X86_FEATURE_SEV_SNP))
3173+
return alloc_page(GFP_KERNEL_ACCOUNT | __GFP_ZERO);
3174+
3175+
/*
3176+
* Allocate an SNP-safe page to workaround the SNP erratum where
3177+
* the CPU will incorrectly signal an RMP violation #PF if a
3178+
* hugepage (2MB or 1GB) collides with the RMP entry of a
3179+
* 2MB-aligned VMCB, VMSA, or AVIC backing page.
3180+
*
3181+
* Allocate one extra page, choose a page which is not
3182+
* 2MB-aligned, and free the other.
3183+
*/
3184+
p = alloc_pages(GFP_KERNEL_ACCOUNT | __GFP_ZERO, 1);
3185+
if (!p)
3186+
return NULL;
3187+
3188+
split_page(p, 1);
3189+
3190+
pfn = page_to_pfn(p);
3191+
if (IS_ALIGNED(pfn, PTRS_PER_PMD))
3192+
__free_page(p++);
3193+
else
3194+
__free_page(p + 1);
3195+
3196+
return p;
3197+
}

arch/x86/kvm/svm/svm.c

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -703,7 +703,7 @@ static int svm_cpu_init(int cpu)
703703
int ret = -ENOMEM;
704704

705705
memset(sd, 0, sizeof(struct svm_cpu_data));
706-
sd->save_area = alloc_page(GFP_KERNEL | __GFP_ZERO);
706+
sd->save_area = snp_safe_alloc_page(NULL);
707707
if (!sd->save_area)
708708
return ret;
709709

@@ -1421,7 +1421,7 @@ static int svm_vcpu_create(struct kvm_vcpu *vcpu)
14211421
svm = to_svm(vcpu);
14221422

14231423
err = -ENOMEM;
1424-
vmcb01_page = alloc_page(GFP_KERNEL_ACCOUNT | __GFP_ZERO);
1424+
vmcb01_page = snp_safe_alloc_page(vcpu);
14251425
if (!vmcb01_page)
14261426
goto out;
14271427

@@ -1430,7 +1430,7 @@ static int svm_vcpu_create(struct kvm_vcpu *vcpu)
14301430
* SEV-ES guests require a separate VMSA page used to contain
14311431
* the encrypted register state of the guest.
14321432
*/
1433-
vmsa_page = alloc_page(GFP_KERNEL_ACCOUNT | __GFP_ZERO);
1433+
vmsa_page = snp_safe_alloc_page(vcpu);
14341434
if (!vmsa_page)
14351435
goto error_free_vmcb_page;
14361436

@@ -4900,6 +4900,16 @@ static int svm_vm_init(struct kvm *kvm)
49004900
return 0;
49014901
}
49024902

4903+
static void *svm_alloc_apic_backing_page(struct kvm_vcpu *vcpu)
4904+
{
4905+
struct page *page = snp_safe_alloc_page(vcpu);
4906+
4907+
if (!page)
4908+
return NULL;
4909+
4910+
return page_address(page);
4911+
}
4912+
49034913
static struct kvm_x86_ops svm_x86_ops __initdata = {
49044914
.name = KBUILD_MODNAME,
49054915

@@ -5031,6 +5041,7 @@ static struct kvm_x86_ops svm_x86_ops __initdata = {
50315041

50325042
.vcpu_deliver_sipi_vector = svm_vcpu_deliver_sipi_vector,
50335043
.vcpu_get_apicv_inhibit_reasons = avic_vcpu_get_apicv_inhibit_reasons,
5044+
.alloc_apic_backing_page = svm_alloc_apic_backing_page,
50345045
};
50355046

50365047
/*

arch/x86/kvm/svm/svm.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -694,6 +694,7 @@ void sev_es_vcpu_reset(struct vcpu_svm *svm);
694694
void sev_vcpu_deliver_sipi_vector(struct kvm_vcpu *vcpu, u8 vector);
695695
void sev_es_prepare_switch_to_guest(struct sev_es_save_area *hostsa);
696696
void sev_es_unmap_ghcb(struct vcpu_svm *svm);
697+
struct page *snp_safe_alloc_page(struct kvm_vcpu *vcpu);
697698

698699
/* vmenter.S */
699700

0 commit comments

Comments
 (0)