2021-06-02 14:14:22

by Brijesh Singh

[permalink] [raw]
Subject: [PATCH Part2 RFC v3 00/37] Add AMD Secure Nested Paging (SEV-SNP) Hypervisor Support

This part of the Secure Encrypted Paging (SEV-SNP) series focuses on the
changes required in a host OS for SEV-SNP support. The series builds upon
SEV-SNP Part-1.

This series provides the basic building blocks to support booting the SEV-SNP
VMs, it does not cover all the security enhancement introduced by the SEV-SNP
such as interrupt protection.

The CCP driver is enhanced to provide new APIs that use the SEV-SNP
specific commands defined in the SEV-SNP firmware specification. The KVM
driver uses those APIs to create and managed the SEV-SNP guests.

The GHCB specification version 2 introduces new set of NAE's that is
used by the SEV-SNP guest to communicate with the hypervisor. The series
provides support to handle the following new NAE events:
- Register GHCB GPA
- Page State Change Request
- Hypevisor feature
- Guest message request

The RMP check is enforced as soon as SEV-SNP is enabled. Not every memory
access requires an RMP check. In particular, the read accesses from the
hypervisor do not require RMP checks because the data confidentiality is
already protected via memory encryption. When hardware encounters an RMP
checks failure, it raises a page-fault exception. If RMP check failure
is due to the page-size mismatch, then split the large page to resolve
the fault.

The series does not provide support for the following SEV-SNP specific
NAE's yet:

* Extended guest request
* Interrupt security

The series is based on the commit:
a4345a7cecfb (origin/queue, origin/next, next) Merge tag 'kvmarm-fixes-5.13-1'
3bf0fcd75434 (tag: kvm-5.13-1, origin/next, next) KVM: selftests: Speed up set_memory_region_test

Changes since v2:
* Add AP creation support.
* Drop the patch to handle the RMP fault for the kernel address.
* Add functions to track the write access from the hypervisor.
* Do not enable the SNP feature when IOMMU is disabled or is in passthrough mode.
* Dump the RMP entry on RMP violation for the debug.
* Shorten the GHCB macro names.
* Start the SNP_INIT command id from 255 to give some gap for the legacy SEV.
* Sync the header with the latest 0.9 SNP spec.

Changes since v1:
* Add AP reset MSR protocol VMGEXIT NAE.
* Add Hypervisor features VMGEXIT NAE.
* Move the RMP table initialization and RMPUPDATE/PSMASH helper in
arch/x86/kernel/sev.c.
* Add support to map/unmap SEV legacy command buffer to firmware state when
SNP is active.
* Enhance PSP driver to provide helper to allocate/free memory used for the
firmware context page.
* Add support to handle RMP fault for the kernel address.
* Add support to handle GUEST_REQUEST NAE event for attestation.
* Rename RMP table lookup helper.
* Drop typedef from rmpentry struct definition.
* Drop SNP static key and use cpu_feature_enabled() to check whether SEV-SNP
is active.
* Multiple cleanup/fixes to address Boris review feedback.

Brijesh Singh (34):
KVM: SVM: Provide the Hypervisor Feature support VMGEXIT
x86/cpufeatures: Add SEV-SNP CPU feature
x86/sev: Add the host SEV-SNP initialization support
x86/sev: Add RMP entry lookup helpers
x86/sev: Add helper functions for RMPUPDATE and PSMASH instruction
x86/sev: Split the physmap when adding the page in RMP table
x86/traps: Define RMP violation #PF error code
x86/fault: Add support to dump RMP entry on fault
x86/fault: Add support to handle the RMP fault for user address
crypto:ccp: Define the SEV-SNP commands
crypto: ccp: Add support to initialize the AMD-SP for SEV-SNP
crypto: ccp: Shutdown SNP firmware on kexec
crypto:ccp: Provide APIs to issue SEV-SNP commands
crypto: ccp: Handle the legacy TMR allocation when SNP is enabled
crypto: ccp: Handle the legacy SEV command when SNP is enabled
KVM: SVM: make AVIC backing, VMSA and VMCB memory allocation SNP safe
KVM: SVM: Add initial SEV-SNP support
KVM: SVM: Add KVM_SNP_INIT command
KVM: SVM: Add KVM_SEV_SNP_LAUNCH_START command
KVM: SVM: Add KVM_SEV_SNP_LAUNCH_UPDATE command
KVM: SVM: Reclaim the guest pages when SEV-SNP VM terminates
KVM: SVM: Add KVM_SEV_SNP_LAUNCH_FINISH command
KVM: X86: Add kvm_x86_ops to get the max page level for the TDP
KVM: X86: Introduce kvm_mmu_map_tdp_page() for use by SEV
KVM: X86: Introduce kvm_mmu_get_tdp_walk() for SEV-SNP use
KVM: X86: Define new RMP check related #NPF error bits
KVM: X86: update page-fault trace to log the 64-bit error code
KVM: SVM: Add support to handle GHCB GPA register VMGEXIT
KVM: SVM: Add support to handle MSR based Page State Change VMGEXIT
KVM: SVM: Add support to handle Page State Change VMGEXIT
KVM: Add arch hooks to track the host write to guest memory
KVM: X86: Export the kvm_zap_gfn_range() for the SNP use
KVM: SVM: Add support to handle the RMP nested page fault
KVM: SVM: Provide support for SNP_GUEST_REQUEST NAE event

Tom Lendacky (3):
KVM: SVM: Add support to handle AP reset MSR protocol
KVM: SVM: Use a VMSA physical address variable for populating VMCB
KVM: SVM: Support SEV-SNP AP Creation NAE event

arch/x86/include/asm/cpufeatures.h | 1 +
arch/x86/include/asm/disabled-features.h | 8 +-
arch/x86/include/asm/kvm_host.h | 24 +
arch/x86/include/asm/msr-index.h | 6 +
arch/x86/include/asm/sev-common.h | 17 +
arch/x86/include/asm/sev.h | 4 +-
arch/x86/include/asm/svm.h | 8 +
arch/x86/include/asm/trap_pf.h | 18 +-
arch/x86/include/uapi/asm/svm.h | 2 +
arch/x86/kernel/cpu/amd.c | 3 +-
arch/x86/kernel/sev.c | 189 ++++
arch/x86/kvm/lapic.c | 5 +-
arch/x86/kvm/mmu.h | 5 +-
arch/x86/kvm/mmu/mmu.c | 76 +-
arch/x86/kvm/svm/sev.c | 1162 +++++++++++++++++++++-
arch/x86/kvm/svm/svm.c | 37 +-
arch/x86/kvm/svm/svm.h | 39 +-
arch/x86/kvm/trace.h | 6 +-
arch/x86/kvm/vmx/vmx.c | 8 +
arch/x86/kvm/x86.c | 89 +-
arch/x86/mm/fault.c | 148 +++
drivers/crypto/ccp/sev-dev.c | 666 ++++++++++++-
drivers/crypto/ccp/sev-dev.h | 14 +
drivers/crypto/ccp/sp-pci.c | 12 +
include/linux/kvm_host.h | 3 +
include/linux/mm.h | 6 +-
include/linux/psp-sev.h | 323 ++++++
include/linux/sev.h | 76 ++
include/uapi/linux/kvm.h | 43 +
include/uapi/linux/psp-sev.h | 44 +
mm/memory.c | 13 +
tools/arch/x86/include/asm/cpufeatures.h | 1 +
virt/kvm/kvm_main.c | 21 +-
33 files changed, 2992 insertions(+), 85 deletions(-)
create mode 100644 include/linux/sev.h

--
2.17.1


2021-06-02 14:14:38

by Brijesh Singh

[permalink] [raw]
Subject: [PATCH Part2 RFC v3 06/37] x86/sev: Add helper functions for RMPUPDATE and PSMASH instruction

The RMPUPDATE instruction writes a new RMP entry in the RMP Table. The
hypervisor will use the instruction to add pages to the RMP table. See
APM3 for details on the instruction operations.

The PSMASH instruction expands a 2MB RMP entry into a corresponding set of
contiguous 4KB-Page RMP entries. The hypervisor will use this instruction
to adjust the RMP entry without invalidating the previous RMP entry.

Signed-off-by: Brijesh Singh <[email protected]>
---
arch/x86/kernel/sev.c | 42 ++++++++++++++++++++++++++++++++++++++++++
include/linux/sev.h | 20 ++++++++++++++++++++
2 files changed, 62 insertions(+)

diff --git a/arch/x86/kernel/sev.c b/arch/x86/kernel/sev.c
index 51676ab1a321..9727df945fb1 100644
--- a/arch/x86/kernel/sev.c
+++ b/arch/x86/kernel/sev.c
@@ -2226,3 +2226,45 @@ struct rmpentry *snp_lookup_page_in_rmptable(struct page *page, int *level)
return entry;
}
EXPORT_SYMBOL_GPL(snp_lookup_page_in_rmptable);
+
+int psmash(struct page *page)
+{
+ unsigned long spa = page_to_pfn(page) << PAGE_SHIFT;
+ int ret;
+
+ if (!cpu_feature_enabled(X86_FEATURE_SEV_SNP))
+ return -ENXIO;
+
+ /* Retry if another processor is modifying the RMP entry. */
+ do {
+ /* Binutils version 2.36 supports the PSMASH mnemonic. */
+ asm volatile(".byte 0xF3, 0x0F, 0x01, 0xFF"
+ : "=a"(ret)
+ : "a"(spa)
+ : "memory", "cc");
+ } while (ret == FAIL_INUSE);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(psmash);
+
+int rmpupdate(struct page *page, struct rmpupdate *val)
+{
+ unsigned long spa = page_to_pfn(page) << PAGE_SHIFT;
+ int ret;
+
+ if (!cpu_feature_enabled(X86_FEATURE_SEV_SNP))
+ return -ENXIO;
+
+ /* Retry if another processor is modifying the RMP entry. */
+ do {
+ /* Binutils version 2.36 supports the RMPUPDATE mnemonic. */
+ asm volatile(".byte 0xF2, 0x0F, 0x01, 0xFE"
+ : "=a"(ret)
+ : "a"(spa), "c"((unsigned long)val)
+ : "memory", "cc");
+ } while (ret == FAIL_INUSE);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(rmpupdate);
diff --git a/include/linux/sev.h b/include/linux/sev.h
index 83c89e999999..bcd4d75d87c8 100644
--- a/include/linux/sev.h
+++ b/include/linux/sev.h
@@ -39,13 +39,33 @@ struct __packed rmpentry {

#define RMP_TO_X86_PG_LEVEL(level) (((level) == RMP_PG_SIZE_4K) ? PG_LEVEL_4K : PG_LEVEL_2M)

+struct rmpupdate {
+ u64 gpa;
+ u8 assigned;
+ u8 pagesize;
+ u8 immutable;
+ u8 rsvd;
+ u32 asid;
+} __packed;
+
+
+/*
+ * The psmash() and rmpupdate() returns FAIL_INUSE when another processor is
+ * modifying the RMP entry.
+ */
+#define FAIL_INUSE 3
+
#ifdef CONFIG_AMD_MEM_ENCRYPT
struct rmpentry *snp_lookup_page_in_rmptable(struct page *page, int *level);
+int psmash(struct page *page);
+int rmpupdate(struct page *page, struct rmpupdate *e);
#else
static inline struct rmpentry *snp_lookup_page_in_rmptable(struct page *page, int *level)
{
return NULL;
}
+static inline int psmash(struct page *page) { return -ENXIO; }
+static inline int rmpupdate(struct page *page, struct rmpupdate *e) { return -ENXIO; }

#endif /* CONFIG_AMD_MEM_ENCRYPT */
#endif /* __LINUX_SEV_H */
--
2.17.1

2021-06-02 14:15:29

by Brijesh Singh

[permalink] [raw]
Subject: [PATCH Part2 RFC v3 21/37] KVM: SVM: Add KVM_SEV_SNP_LAUNCH_UPDATE command

The KVM_SEV_SNP_LAUNCH_UPDATE command can be used to insert data into the
guest's memory. The data is encrypted with the cryptographic context
created with the KVM_SEV_SNP_LAUNCH_START.

In addition to the inserting data, it can insert a two special pages
into the guests memory: the secrets page and the CPUID page.

For more information see the SEV-SNP specification.

Signed-off-by: Brijesh Singh <[email protected]>
---
arch/x86/kvm/svm/sev.c | 139 +++++++++++++++++++++++++++++++++++++++
include/linux/sev.h | 2 +
include/uapi/linux/kvm.h | 18 +++++
3 files changed, 159 insertions(+)

diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index dac71bdedac4..dc9343ecca14 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -17,6 +17,7 @@
#include <linux/misc_cgroup.h>
#include <linux/processor.h>
#include <linux/trace_events.h>
+#include <linux/sev.h>
#include <asm/fpu/internal.h>

#include <asm/trapnr.h>
@@ -1605,6 +1606,141 @@ static int snp_launch_start(struct kvm *kvm, struct kvm_sev_cmd *argp)
return rc;
}

+static struct kvm_memory_slot *hva_to_memslot(struct kvm *kvm, unsigned long hva)
+{
+ struct kvm_memslots *slots = kvm_memslots(kvm);
+ struct kvm_memory_slot *memslot;
+
+ kvm_for_each_memslot(memslot, slots) {
+ if (hva >= memslot->userspace_addr &&
+ hva < memslot->userspace_addr + (memslot->npages << PAGE_SHIFT))
+ return memslot;
+ }
+
+ return NULL;
+}
+
+static bool hva_to_gpa(struct kvm *kvm, unsigned long hva, gpa_t *gpa)
+{
+ struct kvm_memory_slot *memslot;
+ gpa_t gpa_offset;
+
+ memslot = hva_to_memslot(kvm, hva);
+ if (!memslot)
+ return false;
+
+ gpa_offset = hva - memslot->userspace_addr;
+ *gpa = ((memslot->base_gfn << PAGE_SHIFT) + gpa_offset);
+
+ return true;
+}
+
+static int snp_page_reclaim(struct page *page, int rmppage_size)
+{
+ struct sev_data_snp_page_reclaim data = {};
+ struct rmpupdate e = {};
+ int rc, err;
+
+ data.paddr = __sme_page_pa(page) | rmppage_size;
+ rc = snp_guest_page_reclaim(&data, &err);
+ if (rc)
+ return rc;
+
+ return rmpupdate(page, &e);
+}
+
+static int snp_launch_update(struct kvm *kvm, struct kvm_sev_cmd *argp)
+{
+ unsigned long npages, vaddr, vaddr_end, i, next_vaddr;
+ struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
+ struct sev_data_snp_launch_update data = {};
+ struct kvm_sev_snp_launch_update params;
+ int *error = &argp->error;
+ struct kvm_vcpu *vcpu;
+ struct page **inpages;
+ struct rmpupdate e;
+ int ret;
+
+ if (!sev_snp_guest(kvm))
+ return -ENOTTY;
+
+ if (!sev->snp_context)
+ return -EINVAL;
+
+ if (copy_from_user(&params, (void __user *)(uintptr_t)argp->data, sizeof(params)))
+ return -EFAULT;
+
+ data.gctx_paddr = __psp_pa(sev->snp_context);
+
+ /* Lock the user memory. */
+ inpages = sev_pin_memory(kvm, params.uaddr, params.len, &npages, 1);
+ if (!inpages)
+ return -ENOMEM;
+
+ vcpu = kvm_get_vcpu(kvm, 0);
+ vaddr = params.uaddr;
+ vaddr_end = vaddr + params.len;
+
+ for (i = 0; vaddr < vaddr_end; vaddr = next_vaddr, i++) {
+ unsigned long psize, pmask;
+ int level = PG_LEVEL_4K;
+ gpa_t gpa;
+
+ if (!hva_to_gpa(kvm, vaddr, &gpa)) {
+ ret = -EINVAL;
+ goto e_unpin;
+ }
+
+ psize = page_level_size(level);
+ pmask = page_level_mask(level);
+ gpa = gpa & pmask;
+
+ /* Transition the page state to pre-guest */
+ memset(&e, 0, sizeof(e));
+ e.assigned = 1;
+ e.gpa = gpa;
+ e.asid = sev_get_asid(kvm);
+ e.immutable = true;
+ e.pagesize = X86_TO_RMP_PG_LEVEL(level);
+ ret = rmpupdate(inpages[i], &e);
+ if (ret) {
+ ret = -EFAULT;
+ goto e_unpin;
+ }
+
+ data.address = __sme_page_pa(inpages[i]);
+ data.page_size = e.pagesize;
+ data.page_type = params.page_type;
+ ret = __sev_issue_cmd(argp->sev_fd, SEV_CMD_SNP_LAUNCH_UPDATE, &data, error);
+ if (ret) {
+ snp_page_reclaim(inpages[i], e.pagesize);
+ goto e_unpin;
+ }
+
+ next_vaddr = (vaddr & pmask) + psize;
+ }
+
+e_unpin:
+ /* Content of memory is updated, mark pages dirty */
+ memset(&e, 0, sizeof(e));
+ for (i = 0; i < npages; i++) {
+ set_page_dirty_lock(inpages[i]);
+ mark_page_accessed(inpages[i]);
+
+ /*
+ * If its an error, then update RMP entry to change page ownership
+ * to the hypervisor.
+ */
+ if (ret)
+ rmpupdate(inpages[i], &e);
+ }
+
+ /* Unlock the user pages */
+ sev_unpin_memory(kvm, inpages, npages);
+
+ return ret;
+}
+
int svm_mem_enc_op(struct kvm *kvm, void __user *argp)
{
struct kvm_sev_cmd sev_cmd;
@@ -1697,6 +1833,9 @@ int svm_mem_enc_op(struct kvm *kvm, void __user *argp)
case KVM_SEV_SNP_LAUNCH_START:
r = snp_launch_start(kvm, &sev_cmd);
break;
+ case KVM_SEV_SNP_LAUNCH_UPDATE:
+ r = snp_launch_update(kvm, &sev_cmd);
+ break;
default:
r = -EINVAL;
goto out;
diff --git a/include/linux/sev.h b/include/linux/sev.h
index bcd4d75d87c8..82e804a2ee0d 100644
--- a/include/linux/sev.h
+++ b/include/linux/sev.h
@@ -36,8 +36,10 @@ struct __packed rmpentry {

/* RMP page size */
#define RMP_PG_SIZE_4K 0
+#define RMP_PG_SIZE_2M 1

#define RMP_TO_X86_PG_LEVEL(level) (((level) == RMP_PG_SIZE_4K) ? PG_LEVEL_4K : PG_LEVEL_2M)
+#define X86_TO_RMP_PG_LEVEL(level) (((level) == PG_LEVEL_4K) ? RMP_PG_SIZE_4K : RMP_PG_SIZE_2M)

struct rmpupdate {
u64 gpa;
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 56ab5576741e..8890d5a340be 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -1681,6 +1681,7 @@ enum sev_cmd_id {
/* SNP specific commands */
KVM_SEV_SNP_INIT = 255,
KVM_SEV_SNP_LAUNCH_START,
+ KVM_SEV_SNP_LAUNCH_UPDATE,

KVM_SEV_NR_MAX,
};
@@ -1786,6 +1787,23 @@ struct kvm_sev_snp_launch_start {
__u8 gosvw[16];
};

+#define KVM_SEV_SNP_PAGE_TYPE_NORMAL 0x1
+#define KVM_SEV_SNP_PAGE_TYPE_VMSA 0x2
+#define KVM_SEV_SNP_PAGE_TYPE_ZERO 0x3
+#define KVM_SEV_SNP_PAGE_TYPE_UNMEASURED 0x4
+#define KVM_SEV_SNP_PAGE_TYPE_SECRETS 0x5
+#define KVM_SEV_SNP_PAGE_TYPE_CPUID 0x6
+
+struct kvm_sev_snp_launch_update {
+ __u64 uaddr;
+ __u32 len;
+ __u8 imi_page;
+ __u8 page_type;
+ __u8 vmpl3_perms;
+ __u8 vmpl2_perms;
+ __u8 vmpl1_perms;
+};
+
#define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0)
#define KVM_DEV_ASSIGN_PCI_2_3 (1 << 1)
#define KVM_DEV_ASSIGN_MASK_INTX (1 << 2)
--
2.17.1

2021-06-02 14:17:38

by Brijesh Singh

[permalink] [raw]
Subject: [PATCH Part2 RFC v3 32/37] KVM: Add arch hooks to track the host write to guest memory

The kvm_write_guest{_page} and kvm_vcpu_write_guest{_page} are used by
the hypevisor to write to the guest memory. The kvm_vcpu_map() and
kvm_map_gfn() are used by the hypervisor to map the guest memory and
and access it later.

When SEV-SNP is enabled in the guest VM, the guest memory pages can
either be a private or shared. A write from the hypervisor goes through
the RMP checks. If hardware sees that hypervisor is attempting to write
to a guest private page, then it triggers an RMP violation (i.e, #PF with
RMP bit set).

Enhance the KVM guest write helpers to invoke an architecture specific
hooks (kvm_arch_write_gfn_{begin,end}) to track the write access from the
hypervisor.

When SEV-SNP is enabled, the guest uses the PAGE_STATE vmgexit to ask the
hypervisor to change the page state from shared to private or vice versa.
While changing the page state to private, use the
kvm_host_write_track_is_active() to check whether the page is being
tracked for the host write access (i.e either mapped or kvm_write_guest
is in progress). If its tracked, then do not change the page state.

Signed-off-by: Brijesh Singh <[email protected]>
---
arch/x86/include/asm/kvm_host.h | 6 +++
arch/x86/kvm/svm/sev.c | 51 +++++++++++++++++++++
arch/x86/kvm/svm/svm.c | 2 +
arch/x86/kvm/svm/svm.h | 1 +
arch/x86/kvm/x86.c | 78 +++++++++++++++++++++++++++++++++
include/linux/kvm_host.h | 3 ++
virt/kvm/kvm_main.c | 21 +++++++--
7 files changed, 159 insertions(+), 3 deletions(-)

diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 59185b6bc82a..678992e9966a 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -865,10 +865,13 @@ struct kvm_lpage_info {
int disallow_lpage;
};

+bool kvm_host_write_track_is_active(struct kvm *kvm, gfn_t gfn);
+
struct kvm_arch_memory_slot {
struct kvm_rmap_head *rmap[KVM_NR_PAGE_SIZES];
struct kvm_lpage_info *lpage_info[KVM_NR_PAGE_SIZES - 1];
unsigned short *gfn_track[KVM_PAGE_TRACK_MAX];
+ unsigned short *host_write_track[KVM_PAGE_TRACK_MAX];
};

/*
@@ -1393,6 +1396,9 @@ struct kvm_x86_ops {
void (*vcpu_deliver_sipi_vector)(struct kvm_vcpu *vcpu, u8 vector);
void *(*alloc_apic_backing_page)(struct kvm_vcpu *vcpu);
int (*get_tdp_max_page_level)(struct kvm_vcpu *vcpu, gpa_t gpa, int max_level);
+
+ void (*write_page_begin)(struct kvm *kvm, struct kvm_memory_slot *slot, gfn_t gfn);
+ void (*write_page_end)(struct kvm *kvm, struct kvm_memory_slot *slot, gfn_t gfn);
};

struct kvm_x86_nested_ops {
diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index ddcbae37de4f..d30419e91288 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -2862,6 +2862,19 @@ static int snp_make_page_shared(struct kvm_vcpu *vcpu, gpa_t gpa, kvm_pfn_t pfn,
return rmpupdate(pfn_to_page(pfn), &val);
}

+static inline bool kvm_host_write_track_gpa_range_is_active(struct kvm *kvm,
+ gpa_t start, gpa_t end)
+{
+ while (start < end) {
+ if (kvm_host_write_track_is_active(kvm, gpa_to_gfn(start)))
+ return 1;
+
+ start += PAGE_SIZE;
+ }
+
+ return false;
+}
+
static int snp_make_page_private(struct kvm_vcpu *vcpu, gpa_t gpa, kvm_pfn_t pfn, int level)
{
struct kvm_sev_info *sev = &to_kvm_svm(vcpu->kvm)->sev_info;
@@ -2873,6 +2886,14 @@ static int snp_make_page_private(struct kvm_vcpu *vcpu, gpa_t gpa, kvm_pfn_t pfn
if (!e)
return -EINVAL;

+ /*
+ * If the GPA is tracked for the write access then do not change the
+ * page state from shared to private.
+ */
+ if (kvm_host_write_track_gpa_range_is_active(vcpu->kvm,
+ gpa, gpa + page_level_size(level)))
+ return -EBUSY;
+
/* Log if the entry is validated */
if (rmpentry_validated(e))
pr_warn_ratelimited("Asked to make a pre-validated gpa %llx private\n", gpa);
@@ -3446,3 +3467,33 @@ int sev_get_tdp_max_page_level(struct kvm_vcpu *vcpu, gpa_t gpa, int max_level)

return min_t(uint32_t, level, max_level);
}
+
+void sev_snp_write_page_begin(struct kvm *kvm, struct kvm_memory_slot *slot, gfn_t gfn)
+{
+ struct rmpentry *e;
+ int level, rc;
+ kvm_pfn_t pfn;
+
+ if (!sev_snp_guest(kvm))
+ return;
+
+ pfn = gfn_to_pfn(kvm, gfn);
+ if (is_error_noslot_pfn(pfn))
+ return;
+
+ e = snp_lookup_page_in_rmptable(pfn_to_page(pfn), &level);
+ if (unlikely(!e))
+ return;
+
+ /*
+ * A hypervisor should never write to the guest private page. A write to the
+ * guest private will cause an RMP violation. If the guest page is private,
+ * then make it shared.
+ */
+ if (rmpentry_assigned(e)) {
+ pr_err("SEV-SNP: write to guest private gfn %llx\n", gfn);
+ rc = snp_make_page_shared(kvm_get_vcpu(kvm, 0),
+ gfn << PAGE_SHIFT, pfn, PG_LEVEL_4K);
+ BUG_ON(rc != 0);
+ }
+}
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index 2632eae52aa3..4ff6fc86dd18 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -4577,6 +4577,8 @@ static struct kvm_x86_ops svm_x86_ops __initdata = {

.alloc_apic_backing_page = svm_alloc_apic_backing_page,
.get_tdp_max_page_level = sev_get_tdp_max_page_level,
+
+ .write_page_begin = sev_snp_write_page_begin,
};

static struct kvm_x86_init_ops svm_init_ops __initdata = {
diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h
index af4cce39b30f..e0276ad8a1ae 100644
--- a/arch/x86/kvm/svm/svm.h
+++ b/arch/x86/kvm/svm/svm.h
@@ -576,6 +576,7 @@ void sev_es_prepare_guest_switch(struct vcpu_svm *svm, unsigned int cpu);
void sev_es_unmap_ghcb(struct vcpu_svm *svm);
struct page *snp_safe_alloc_page(struct kvm_vcpu *vcpu);
int sev_get_tdp_max_page_level(struct kvm_vcpu *vcpu, gpa_t gpa, int max_level);
+void sev_snp_write_page_begin(struct kvm *kvm, struct kvm_memory_slot *slot, gfn_t gfn);

/* vmenter.S */

diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index bbc4e04e67ad..1398b8021982 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -9076,6 +9076,48 @@ void kvm_arch_mmu_notifier_invalidate_range(struct kvm *kvm,
kvm_make_all_cpus_request(kvm, KVM_REQ_APIC_PAGE_RELOAD);
}

+static void update_gfn_track(struct kvm_memory_slot *slot, gfn_t gfn,
+ enum kvm_page_track_mode mode, short count)
+{
+ int index, val;
+
+ index = gfn_to_index(gfn, slot->base_gfn, PG_LEVEL_4K);
+
+ val = slot->arch.host_write_track[mode][index];
+
+ if (WARN_ON(val + count < 0 || val + count > USHRT_MAX))
+ return;
+
+ slot->arch.host_write_track[mode][index] += count;
+}
+
+bool kvm_host_write_track_is_active(struct kvm *kvm, gfn_t gfn)
+{
+ struct kvm_memory_slot *slot;
+ int index;
+
+ slot = gfn_to_memslot(kvm, gfn);
+ if (!slot)
+ return false;
+
+ index = gfn_to_index(gfn, slot->base_gfn, PG_LEVEL_4K);
+ return !!READ_ONCE(slot->arch.host_write_track[KVM_PAGE_TRACK_WRITE][index]);
+}
+EXPORT_SYMBOL_GPL(kvm_host_write_track_is_active);
+
+void kvm_arch_write_gfn_begin(struct kvm *kvm, struct kvm_memory_slot *slot, gfn_t gfn)
+{
+ update_gfn_track(slot, gfn, KVM_PAGE_TRACK_WRITE, 1);
+
+ if (kvm_x86_ops.write_page_begin)
+ kvm_x86_ops.write_page_begin(kvm, slot, gfn);
+}
+
+void kvm_arch_write_gfn_end(struct kvm *kvm, struct kvm_memory_slot *slot, gfn_t gfn)
+{
+ update_gfn_track(slot, gfn, KVM_PAGE_TRACK_WRITE, -1);
+}
+
void kvm_vcpu_reload_apic_access_page(struct kvm_vcpu *vcpu)
{
if (!lapic_in_kernel(vcpu))
@@ -10896,6 +10938,36 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
kvm_hv_destroy_vm(kvm);
}

+static void kvm_write_page_track_free_memslot(struct kvm_memory_slot *slot)
+{
+ int i;
+
+ for (i = 0; i < KVM_PAGE_TRACK_MAX; i++) {
+ kvfree(slot->arch.host_write_track[i]);
+ slot->arch.host_write_track[i] = NULL;
+ }
+}
+
+static int kvm_write_page_track_create_memslot(struct kvm_memory_slot *slot,
+ unsigned long npages)
+{
+ int i;
+
+ for (i = 0; i < KVM_PAGE_TRACK_MAX; i++) {
+ slot->arch.host_write_track[i] =
+ kvcalloc(npages, sizeof(*slot->arch.host_write_track[i]),
+ GFP_KERNEL_ACCOUNT);
+ if (!slot->arch.host_write_track[i])
+ goto track_free;
+ }
+
+ return 0;
+
+track_free:
+ kvm_write_page_track_free_memslot(slot);
+ return -ENOMEM;
+}
+
void kvm_arch_free_memslot(struct kvm *kvm, struct kvm_memory_slot *slot)
{
int i;
@@ -10969,8 +11041,14 @@ static int kvm_alloc_memslot_metadata(struct kvm_memory_slot *slot,
if (kvm_page_track_create_memslot(slot, npages))
goto out_free;

+ if (kvm_write_page_track_create_memslot(slot, npages))
+ goto e_free_page_track;
+
return 0;

+e_free_page_track:
+ kvm_page_track_free_memslot(slot);
+
out_free:
for (i = 0; i < KVM_NR_PAGE_SIZES; ++i) {
kvfree(slot->arch.rmap[i]);
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index 2f34487e21f2..f22e22cd2179 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -1550,6 +1550,9 @@ static inline long kvm_arch_vcpu_async_ioctl(struct file *filp,
void kvm_arch_mmu_notifier_invalidate_range(struct kvm *kvm,
unsigned long start, unsigned long end);

+void kvm_arch_write_gfn_begin(struct kvm *kvm, struct kvm_memory_slot *slot, gfn_t gfn);
+void kvm_arch_write_gfn_end(struct kvm *kvm, struct kvm_memory_slot *slot, gfn_t gfn);
+
#ifdef CONFIG_HAVE_KVM_VCPU_RUN_PID_CHANGE
int kvm_arch_vcpu_run_pid_change(struct kvm_vcpu *vcpu);
#else
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 6b4feb92dc79..bc805c15d0de 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -160,6 +160,14 @@ __weak void kvm_arch_mmu_notifier_invalidate_range(struct kvm *kvm,
{
}

+__weak void kvm_arch_write_gfn_begin(struct kvm *kvm, struct kvm_memory_slot *slot, gfn_t gfn)
+{
+}
+
+__weak void kvm_arch_write_gfn_end(struct kvm *kvm, struct kvm_memory_slot *slot, gfn_t gfn)
+{
+}
+
bool kvm_is_zone_device_pfn(kvm_pfn_t pfn)
{
/*
@@ -2309,7 +2317,8 @@ static void kvm_cache_gfn_to_pfn(struct kvm_memory_slot *slot, gfn_t gfn,
cache->generation = gen;
}

-static int __kvm_map_gfn(struct kvm_memslots *slots, gfn_t gfn,
+static int __kvm_map_gfn(struct kvm *kvm,
+ struct kvm_memslots *slots, gfn_t gfn,
struct kvm_host_map *map,
struct gfn_to_pfn_cache *cache,
bool atomic)
@@ -2361,20 +2370,22 @@ static int __kvm_map_gfn(struct kvm_memslots *slots, gfn_t gfn,
map->pfn = pfn;
map->gfn = gfn;

+ kvm_arch_write_gfn_begin(kvm, slot, map->gfn);
+
return 0;
}

int kvm_map_gfn(struct kvm_vcpu *vcpu, gfn_t gfn, struct kvm_host_map *map,
struct gfn_to_pfn_cache *cache, bool atomic)
{
- return __kvm_map_gfn(kvm_memslots(vcpu->kvm), gfn, map,
+ return __kvm_map_gfn(vcpu->kvm, kvm_memslots(vcpu->kvm), gfn, map,
cache, atomic);
}
EXPORT_SYMBOL_GPL(kvm_map_gfn);

int kvm_vcpu_map(struct kvm_vcpu *vcpu, gfn_t gfn, struct kvm_host_map *map)
{
- return __kvm_map_gfn(kvm_vcpu_memslots(vcpu), gfn, map,
+ return __kvm_map_gfn(vcpu->kvm, kvm_vcpu_memslots(vcpu), gfn, map,
NULL, false);
}
EXPORT_SYMBOL_GPL(kvm_vcpu_map);
@@ -2412,6 +2423,8 @@ static void __kvm_unmap_gfn(struct kvm *kvm,
else
kvm_release_pfn(map->pfn, dirty, NULL);

+ kvm_arch_write_gfn_end(kvm, memslot, map->gfn);
+
map->hva = NULL;
map->page = NULL;
}
@@ -2612,7 +2625,9 @@ static int __kvm_write_guest_page(struct kvm *kvm,
addr = gfn_to_hva_memslot(memslot, gfn);
if (kvm_is_error_hva(addr))
return -EFAULT;
+ kvm_arch_write_gfn_begin(kvm, memslot, gfn);
r = __copy_to_user((void __user *)addr + offset, data, len);
+ kvm_arch_write_gfn_end(kvm, memslot, gfn);
if (r)
return -EFAULT;
mark_page_dirty_in_slot(kvm, memslot, gfn);
--
2.17.1

2021-06-02 14:17:56

by Brijesh Singh

[permalink] [raw]
Subject: [PATCH Part2 RFC v3 09/37] x86/fault: Add support to dump RMP entry on fault

When SEV-SNP is enabled globally, a write from the host goes through the
RMP check. If the hardware encounters the check failure, then it raises
the #PF (with RMP set). Dump the RMP table to help the debug.

Signed-off-by: Brijesh Singh <[email protected]>
---
arch/x86/mm/fault.c | 78 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 78 insertions(+)

diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
index 2715240c757e..e6deedf27d78 100644
--- a/arch/x86/mm/fault.c
+++ b/arch/x86/mm/fault.c
@@ -19,6 +19,7 @@
#include <linux/uaccess.h> /* faulthandler_disabled() */
#include <linux/efi.h> /* efi_crash_gracefully_on_page_fault()*/
#include <linux/mm_types.h>
+#include <linux/sev.h> /* snp_lookup_page_in_rmptable() */

#include <asm/cpufeature.h> /* boot_cpu_has, ... */
#include <asm/traps.h> /* dotraplinkage, ... */
@@ -502,6 +503,80 @@ static void show_ldttss(const struct desc_ptr *gdt, const char *name, u16 index)
name, index, addr, (desc.limit0 | (desc.limit1 << 16)));
}

+static void dump_rmpentry(unsigned long address)
+{
+ struct rmpentry *e;
+ unsigned long pfn;
+ pgd_t *pgd;
+ pte_t *pte;
+ int level;
+
+ pgd = __va(read_cr3_pa());
+ pgd += pgd_index(address);
+
+ pte = lookup_address_in_pgd(pgd, address, &level);
+ if (unlikely(!pte))
+ return;
+
+ switch (level) {
+ case PG_LEVEL_4K: {
+ pfn = pte_pfn(*pte);
+ break;
+ }
+ case PG_LEVEL_2M: {
+ pfn = pmd_pfn(*(pmd_t *)pte);
+ break;
+ }
+ case PG_LEVEL_1G: {
+ pfn = pud_pfn(*(pud_t *)pte);
+ break;
+ }
+ case PG_LEVEL_512G: {
+ pfn = p4d_pfn(*(p4d_t *)pte);
+ break;
+ }
+ default:
+ return;
+ }
+
+ e = snp_lookup_page_in_rmptable(pfn_to_page(pfn), &level);
+ if (unlikely(!e))
+ return;
+
+ /*
+ * If the RMP entry at the faulting address was not assigned, then dump may
+ * not provide any useful debug information. Iterate through the entire 2MB
+ * region, and dump the RMP entries if one of the bit in the RMP entry is set.
+ */
+ if (rmpentry_assigned(e)) {
+ pr_alert("RMPEntry paddr 0x%lx [assigned=%d immutable=%d pagesize=%d gpa=0x%lx"
+ " asid=%d vmsa=%d validated=%d]\n", pfn << PAGE_SHIFT,
+ rmpentry_assigned(e), rmpentry_immutable(e), rmpentry_pagesize(e),
+ rmpentry_gpa(e), rmpentry_asid(e), rmpentry_vmsa(e),
+ rmpentry_validated(e));
+
+ pr_alert("RMPEntry paddr 0x%lx %016llx %016llx\n", pfn << PAGE_SHIFT,
+ e->high, e->low);
+ } else {
+ unsigned long pfn_end;
+
+ pfn = pfn & ~0x1ff;
+ pfn_end = pfn + PTRS_PER_PMD;
+
+ while (pfn < pfn_end) {
+ e = snp_lookup_page_in_rmptable(pfn_to_page(pfn), &level);
+
+ if (unlikely(!e))
+ return;
+
+ if (e->low || e->high)
+ pr_alert("RMPEntry paddr 0x%lx: %016llx %016llx\n",
+ pfn << PAGE_SHIFT, e->high, e->low);
+ pfn++;
+ }
+ }
+}
+
static void
show_fault_oops(struct pt_regs *regs, unsigned long error_code, unsigned long address)
{
@@ -578,6 +653,9 @@ show_fault_oops(struct pt_regs *regs, unsigned long error_code, unsigned long ad
}

dump_pagetable(address);
+
+ if (error_code & X86_PF_RMP)
+ dump_rmpentry(address);
}

static noinline void
--
2.17.1

2021-06-02 14:17:59

by Brijesh Singh

[permalink] [raw]
Subject: [PATCH Part2 RFC v3 12/37] crypto: ccp: Add support to initialize the AMD-SP for SEV-SNP

Before SNP VMs can be launched, the platform must be appropriately
configured and initialized. Platform initialization is accomplished via
the SNP_INIT command.

Signed-off-by: Brijesh Singh <[email protected]>
---
drivers/crypto/ccp/sev-dev.c | 111 +++++++++++++++++++++++++++++++++--
drivers/crypto/ccp/sev-dev.h | 2 +
include/linux/psp-sev.h | 16 +++++
3 files changed, 124 insertions(+), 5 deletions(-)

diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c
index 0331d4cea7da..2203167dbc2e 100644
--- a/drivers/crypto/ccp/sev-dev.c
+++ b/drivers/crypto/ccp/sev-dev.c
@@ -591,6 +591,92 @@ static int sev_update_firmware(struct device *dev)
return ret;
}

+static void snp_set_hsave_pa(void *arg)
+{
+ wrmsrl(MSR_VM_HSAVE_PA, 0);
+}
+
+static int __sev_snp_init_locked(int *error)
+{
+ struct psp_device *psp = psp_master;
+ struct sev_device *sev;
+ int rc = 0;
+
+ if (!psp || !psp->sev_data)
+ return -ENODEV;
+
+ sev = psp->sev_data;
+
+ if (sev->snp_inited)
+ return 0;
+
+ /* SNP_INIT requires the MSR_VM_HSAVE_PA must be set to 0h across all cores. */
+ on_each_cpu(snp_set_hsave_pa, NULL, 1);
+
+ /* Prepare for first SEV guest launch after INIT */
+ wbinvd_on_all_cpus();
+
+ /* Issue the SNP_INIT firmware command. */
+ rc = __sev_do_cmd_locked(SEV_CMD_SNP_INIT, NULL, error);
+ if (rc)
+ return rc;
+
+ sev->snp_inited = true;
+ dev_dbg(sev->dev, "SEV-SNP firmware initialized\n");
+
+ return rc;
+}
+
+int sev_snp_init(int *error)
+{
+ int rc;
+
+ if (!cpu_feature_enabled(X86_FEATURE_SEV_SNP))
+ return -ENODEV;
+
+ mutex_lock(&sev_cmd_mutex);
+ rc = __sev_snp_init_locked(error);
+ mutex_unlock(&sev_cmd_mutex);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(sev_snp_init);
+
+static int __sev_snp_shutdown_locked(int *error)
+{
+ struct sev_device *sev = psp_master->sev_data;
+ int ret;
+
+ if (!sev->snp_inited)
+ return 0;
+
+ /* SHUTDOWN requires the DF_FLUSH */
+ wbinvd_on_all_cpus();
+ __sev_do_cmd_locked(SEV_CMD_SNP_DF_FLUSH, NULL, NULL);
+
+ ret = __sev_do_cmd_locked(SEV_CMD_SNP_SHUTDOWN, NULL, error);
+ if (ret) {
+ dev_err(sev->dev, "SEV-SNP firmware shutdown failed\n");
+ return ret;
+ }
+
+ sev->snp_inited = false;
+ dev_dbg(sev->dev, "SEV-SNP firmware shutdown\n");
+
+ return ret;
+}
+
+static int sev_snp_shutdown(int *error)
+{
+ int rc;
+
+ mutex_lock(&sev_cmd_mutex);
+ rc = __sev_snp_shutdown_locked(NULL);
+ mutex_unlock(&sev_cmd_mutex);
+
+ return rc;
+}
+
static int sev_ioctl_do_pek_import(struct sev_issue_cmd *argp, bool writable)
{
struct sev_device *sev = psp_master->sev_data;
@@ -1095,6 +1181,21 @@ void sev_pci_init(void)
"SEV: TMR allocation failed, SEV-ES support unavailable\n");
}

+ /*
+ * If boot CPU supports the SNP, then let first attempt to initialize
+ * the SNP firmware.
+ */
+ if (cpu_feature_enabled(X86_FEATURE_SEV_SNP)) {
+ rc = sev_snp_init(&error);
+ if (rc) {
+ /*
+ * If we failed to INIT SNP then don't abort the probe.
+ * Continue to initialize the legacy SEV firmware.
+ */
+ dev_err(sev->dev, "SEV-SNP: failed to INIT error %#x\n", error);
+ }
+ }
+
/* Initialize the platform */
rc = sev_platform_init(&error);
if (rc && (error == SEV_RET_SECURE_DATA_INVALID)) {
@@ -1109,13 +1210,11 @@ void sev_pci_init(void)
rc = sev_platform_init(&error);
}

- if (rc) {
+ if (rc)
dev_err(sev->dev, "SEV: failed to INIT error %#x\n", error);
- return;
- }

- dev_info(sev->dev, "SEV API:%d.%d build:%d\n", sev->api_major,
- sev->api_minor, sev->build);
+ dev_info(sev->dev, "SEV%s API:%d.%d build:%d\n", sev->snp_inited ?
+ "-SNP" : "", sev->api_major, sev->api_minor, sev->build);

return;

@@ -1138,4 +1237,6 @@ void sev_pci_exit(void)
get_order(SEV_ES_TMR_SIZE));
sev_es_tmr = NULL;
}
+
+ sev_snp_shutdown(NULL);
}
diff --git a/drivers/crypto/ccp/sev-dev.h b/drivers/crypto/ccp/sev-dev.h
index 666c21eb81ab..186ad20cbd24 100644
--- a/drivers/crypto/ccp/sev-dev.h
+++ b/drivers/crypto/ccp/sev-dev.h
@@ -52,6 +52,8 @@ struct sev_device {
u8 build;

void *cmd_buf;
+
+ bool snp_inited;
};

int sev_dev_init(struct psp_device *psp);
diff --git a/include/linux/psp-sev.h b/include/linux/psp-sev.h
index c3755099ab55..1b53e8782250 100644
--- a/include/linux/psp-sev.h
+++ b/include/linux/psp-sev.h
@@ -748,6 +748,20 @@ struct sev_data_snp_init_ex {
*/
int sev_platform_init(int *error);

+/**
+ * sev_snp_init - perform SEV SNP_INIT command
+ *
+ * @error: SEV command return code
+ *
+ * Returns:
+ * 0 if the SEV successfully processed the command
+ * -%ENODEV if the SEV device is not available
+ * -%ENOTSUPP if the SEV does not support SEV
+ * -%ETIMEDOUT if the SEV command timed out
+ * -%EIO if the SEV returned a non-zero return code
+ */
+int sev_snp_init(int *error);
+
/**
* sev_platform_status - perform SEV PLATFORM_STATUS command
*
@@ -855,6 +869,8 @@ sev_platform_status(struct sev_user_data_status *status, int *error) { return -E

static inline int sev_platform_init(int *error) { return -ENODEV; }

+static inline int sev_snp_init(int *error) { return -ENODEV; }
+
static inline int
sev_guest_deactivate(struct sev_data_deactivate *data, int *error) { return -ENODEV; }

--
2.17.1

2021-06-02 14:19:48

by Brijesh Singh

[permalink] [raw]
Subject: [PATCH Part2 RFC v3 36/37] KVM: SVM: Use a VMSA physical address variable for populating VMCB

From: Tom Lendacky <[email protected]>

In preparation to support SEV-SNP AP Creation, use a variable that holds
the VMSA physical address rather than converting the virtual address.
This will allow SEV-SNP AP Creation to set the new physical address that
will be used should the vCPU reset path be taken.

Signed-off-by: Tom Lendacky <[email protected]>
---
arch/x86/kvm/svm/sev.c | 5 ++---
arch/x86/kvm/svm/svm.c | 9 ++++++++-
arch/x86/kvm/svm/svm.h | 1 +
3 files changed, 11 insertions(+), 4 deletions(-)

diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index 5718e2e07788..047f4dbde99b 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -3409,10 +3409,9 @@ void sev_es_init_vmcb(struct vcpu_svm *svm)

/*
* An SEV-ES guest requires a VMSA area that is a separate from the
- * VMCB page. Do not include the encryption mask on the VMSA physical
- * address since hardware will access it using the guest key.
+ * VMCB page.
*/
- svm->vmcb->control.vmsa_pa = __pa(svm->vmsa);
+ svm->vmcb->control.vmsa_pa = svm->vmsa_pa;

/* Can't intercept CR register access, HV can't modify CR registers */
svm_clr_intercept(svm, INTERCEPT_CR0_READ);
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index 32e35d396508..74bc635c9608 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -1379,9 +1379,16 @@ static int svm_create_vcpu(struct kvm_vcpu *vcpu)
svm->vmcb01.ptr = page_address(vmcb01_page);
svm->vmcb01.pa = __sme_set(page_to_pfn(vmcb01_page) << PAGE_SHIFT);

- if (vmsa_page)
+ if (vmsa_page) {
svm->vmsa = page_address(vmsa_page);

+ /*
+ * Do not include the encryption mask on the VMSA physical
+ * address since hardware will access it using the guest key.
+ */
+ svm->vmsa_pa = __pa(svm->vmsa);
+ }
+
svm->guest_state_loaded = false;

svm_switch_vmcb(svm, &svm->vmcb01);
diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h
index d4efcda3070d..52fd3cf30ad9 100644
--- a/arch/x86/kvm/svm/svm.h
+++ b/arch/x86/kvm/svm/svm.h
@@ -176,6 +176,7 @@ struct vcpu_svm {

/* SEV-ES support */
struct sev_es_save_area *vmsa;
+ hpa_t vmsa_pa;
struct ghcb *ghcb;
struct kvm_host_map ghcb_map;
bool received_first_sipi;
--
2.17.1

2021-06-02 14:21:10

by Brijesh Singh

[permalink] [raw]
Subject: [PATCH Part2 RFC v3 18/37] KVM: SVM: Add initial SEV-SNP support

The next generation of SEV is called SEV-SNP (Secure Nested Paging).
SEV-SNP builds upon existing SEV and SEV-ES functionality while adding new
hardware based security protection. SEV-SNP adds strong memory encryption
integrity protection to help prevent malicious hypervisor-based attacks
such as data replay, memory re-mapping, and more, to create an isolated
execution environment.

The SNP feature can be enabled in the KVM by passing the sev-snp module
parameter.

Signed-off-by: Brijesh Singh <[email protected]>
---
arch/x86/kvm/svm/sev.c | 18 ++++++++++++++++++
arch/x86/kvm/svm/svm.h | 12 ++++++++++++
2 files changed, 30 insertions(+)

diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index 411ed72f63af..abca2b9dee83 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -52,9 +52,14 @@ module_param_named(sev, sev_enabled, bool, 0444);
/* enable/disable SEV-ES support */
static bool sev_es_enabled = true;
module_param_named(sev_es, sev_es_enabled, bool, 0444);
+
+/* enable/disable SEV-SNP support */
+static bool sev_snp_enabled = true;
+module_param_named(sev_snp, sev_snp_enabled, bool, 0444);
#else
#define sev_enabled false
#define sev_es_enabled false
+#define sev_snp_enabled false
#endif /* CONFIG_KVM_AMD_SEV */

#define AP_RESET_HOLD_NONE 0
@@ -1825,6 +1830,7 @@ void __init sev_hardware_setup(void)
{
#ifdef CONFIG_KVM_AMD_SEV
unsigned int eax, ebx, ecx, edx, sev_asid_count, sev_es_asid_count;
+ bool sev_snp_supported = false;
bool sev_es_supported = false;
bool sev_supported = false;

@@ -1888,9 +1894,21 @@ void __init sev_hardware_setup(void)
pr_info("SEV-ES supported: %u ASIDs\n", sev_es_asid_count);
sev_es_supported = true;

+ /* SEV-SNP support requested? */
+ if (!sev_snp_enabled)
+ goto out;
+
+ /* Is SEV-SNP enabled? */
+ if (!cpu_feature_enabled(X86_FEATURE_SEV_SNP))
+ goto out;
+
+ pr_info("SEV-SNP supported: %u ASIDs\n", min_sev_asid - 1);
+ sev_snp_supported = true;
+
out:
sev_enabled = sev_supported;
sev_es_enabled = sev_es_supported;
+ sev_snp_enabled = sev_snp_supported;
#endif
}

diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h
index 1175edb02d33..b9ea99f8579e 100644
--- a/arch/x86/kvm/svm/svm.h
+++ b/arch/x86/kvm/svm/svm.h
@@ -58,6 +58,7 @@ enum {
struct kvm_sev_info {
bool active; /* SEV enabled guest */
bool es_active; /* SEV-ES enabled guest */
+ bool snp_active; /* SEV-SNP enabled guest */
unsigned int asid; /* ASID used for this guest */
unsigned int handle; /* SEV firmware handle */
int fd; /* SEV device fd */
@@ -232,6 +233,17 @@ static inline bool sev_es_guest(struct kvm *kvm)
#endif
}

+static inline bool sev_snp_guest(struct kvm *kvm)
+{
+#ifdef CONFIG_KVM_AMD_SEV
+ struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
+
+ return sev_es_guest(kvm) && sev->snp_active;
+#else
+ return false;
+#endif
+}
+
static inline void vmcb_mark_all_dirty(struct vmcb *vmcb)
{
vmcb->control.clean = 0;
--
2.17.1