Add AMD Secure Nested Paging (SEV-SNP) Hypervisor Support
Subsystems (11)
v13 cover Add AMD Secure Nested Paging (SEV-SNP) Hypervisor Support
This patchset is also available at:
https://github.com/amdese/linux/commits/snp-host-v13
and is based on commit 4d2deb62185f (as suggested by Paolo) from:
https://git.kernel.org/pub/scm/virt/kvm/kvm.git/log/?h=kvm-coco-queue
Patch Layout
------------
01-03: These patches are minor dependencies for this series and are already
included in both tip/master and mainline, so are only included here as a stop-gap until merged from one of those trees. These are needed by patch #8 in this series which makes use of CC_ATTR_HOST_SEV_SNP
04: This is a small general fix-up for guest_memfd that can be applied
independently of this series.
05-08: These patches add some basic infrastructure and introduces a new
KVM_X86_SNP_VM vm_type to handle differences verses the existing KVM_X86_SEV_VM and KVM_X86_SEV_ES_VM types.
09-11: These implement the KVM API to handle the creation of a
cryptographic launch context, encrypt/measure the initial image into guest memory, and finalize it before launching it.
12-17: These implement handling for various guest-generated events such
as page state changes, onlining of additional vCPUs, etc.
18-21: These implement the gmem hooks needed to prepare gmem-allocated
pages before mapping them into guest private memory ranges as well as cleaning them up prior to returning them to the host for use as normal memory. Because this supplants certain activities like issued WBINVDs during KVM MMU invalidations, there's also a patch to avoid duplicating that work to avoid unecessary overhead.
22: With all the core support in place, the patch adds a kvm_amd module
parameter to enable SNP support.
23-26: These patches all deal with the servicing of guest requests to handle
things like attestation, as well as some related host-management interfaces.
Testing
-------
For testing this via QEMU, use the following tree:
https://github.com/amdese/qemu/commits/snp-v4-wip3
A patched OVMF is also needed due to upstream KVM no longer supporting MMIO ranges that are mapped as private. It is recommended you build the AmdSevX64 variant as it provides the kernel-hashing support present in this series:
https://github.com/amdese/ovmf/commits/apic-mmio-fix1d
A basic command-line invocation for SNP would be:
qemu-system-x86_64 -smp 32,maxcpus=255 -cpu EPYC-Milan-v2
-machine q35,confidential-guest-support=sev0,memory-backend=ram1 -object memory-backend-memfd,id=ram1,size=4G,share=true,reserve=false -object sev-snp-guest,id=sev0,cbitpos=51,reduced-phys-bits=1,id-auth= -bios OVMF_CODE-upstream-20240410-apic-mmio-fix1d-AmdSevX64.fd
With kernel-hashing and certificate data supplied:
qemu-system-x86_64 -smp 32,maxcpus=255 -cpu EPYC-Milan-v2
-machine q35,confidential-guest-support=sev0,memory-backend=ram1
-object memory-backend-memfd,id=ram1,size=4G,share=true,reserve=false
-object sev-snp-guest,id=sev0,cbitpos=51,reduced-phys-bits=1,id-auth=,certs-path=/home/mroth/cert.blob,kernel-hashes=on -bios OVMF_CODE-upstream-20240410-apic-mmio-fix1d-AmdSevX64.fd
-kernel /boot/vmlinuz-$ver
-initrd /boot/initrd.img-$ver
-append "root=UUID=d72a6d1c-06cf-4b79-af43-f1bac4f620f9 ro console=ttyS0,115200n8"
With standard X64 OVMF package with separate image for persistent NVRAM:
qemu-system-x86_64 -smp 32,maxcpus=255 -cpu EPYC-Milan-v2
-machine q35,confidential-guest-support=sev0,memory-backend=ram1
-object memory-backend-memfd,id=ram1,size=4G,share=true,reserve=false
-object sev-snp-guest,id=sev0,cbitpos=51,reduced-phys-bits=1,id-auth=
-bios OVMF_CODE-upstream-20240410-apic-mmio-fix1d.fd
-drive if=pflash,format=raw,unit=0,file=OVMF_VARS-upstream-20240410-apic-mmio-fix1d.fd,readonly=off
Known issues / TODOs
--------------------
* SEV-ES guests may trigger the following warning:
WARNING: CPU: 151 PID: 4003 at arch/x86/kvm/mmu/mmu.c:5855 kvm_mmu_page_fault+0x33b/0x860 [kvm]
It is assumed here that these will be resolved once the transition to PFERR_PRIVATE_ACCESS is fully completed, but if that's not the case let me know and will investigate further.
* Base tree in some cases reports "Unpatched return thunk in use. This should not happen!" the first time it runs an SVM/SEV/SNP guests. This a recent regression upstream and unrelated to this series:
* 2MB hugepage support has been dropped pending discussion on how we plan to re-enable it in gmem.
* Host kexec should work, but there is a known issue with host kdump support while SNP guests are running that will be addressed as a follow-up.
* SNP kselftests are currently a WIP and will be included as part of SNP upstreaming efforts in the near-term.
SEV-SNP Overview
----------------
This part of the Secure Encrypted Paging (SEV-SNP) series focuses on the changes required to add KVM support for SEV-SNP. This series builds upon SEV-SNP guest support, which is now in mainline, and and SEV-SNP host initialization support, which is now in linux-next.
While 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, which will added in the future.
With SNP, when pages are marked as guest-owned in the RMP table, they are assigned to a specific guest/ASID, as well as a specific GFN with in the guest. Any attempts to map it in the RMP table to a different guest/ASID, or a different GFN within a guest/ASID, will result in an RMP nested page fault.
Prior to accessing a guest-owned page, the guest must validate it with a special PVALIDATE instruction which will set a special bit in the RMP table for the guest. This is the only way to set the validated bit outside of the initial pre-encrypted guest payload/image; any attempts outside the guest to modify the RMP entry from that point forward will result in the validated bit being cleared, at which point the guest will trigger an exception if it attempts to access that page so it can be made aware of possible tampering.
One exception to this is the initial guest payload, which is pre-validated by the firmware prior to launching. The guest can use Guest Message requests to fetch an attestation report which will include the measurement of the initial image so that the guest can verify it was booted with the expected image/environment.
After boot, guests can use Page State Change requests to switch pages between shared/hypervisor-owned and private/guest-owned to share data for things like DMA, virtio buffers, and other GHCB requests.
In this implementation of SEV-SNP, private guest memory is managed by a new kernel framework called guest_memfd (gmem). With gmem, a new KVM_SET_MEMORY_ATTRIBUTES KVM ioctl has been added to tell the KVM MMU whether a particular GFN should be backed by shared (normal) memory or private (gmem-allocated) memory. To tie into this, Page State Change requests are forward to userspace via KVM_EXIT_VMGEXIT exits, which will then issue the corresponding KVM_SET_MEMORY_ATTRIBUTES call to set the private/shared state in the KVM MMU.
The gmem / KVM MMU hooks implemented in this series will then update the RMP table entries for the backing PFNs to set them to guest-owned/private when mapping private pages into the guest via KVM MMU, or use the normal KVM MMU handling in the case of shared pages where the corresponding RMP table entries are left in the default shared/hypervisor-owned state.
Feedback/review is very much appreciated!
-Mike
Changes since v12:
* rebased to latest kvm-coco-queue branch (commit 4d2deb62185f)
* add more input validation for SNP_LAUNCH_START, especially for handling things like MBO/MBZ policy bits, and API major/minor minimums. (Paolo)
* block SNP KVM instances from being able to run legacy SEV commands (Paolo)
* don't attempt to measure VMSA for vcpu 0/BSP before the others, let userspace deal with the ordering just like with SEV-ES (Paolo)
* fix up docs for SNP_LAUNCH_FINISH (Paolo)
* introduce svm->sev_es.snp_has_guest_vmsa flag to better distinguish handling for guest-mapped vs non-guest-mapped VMSAs, rename 'snp_ap_create' flag to 'snp_ap_waiting_for_reset' (Paolo)
* drop "KVM: SEV: Use a VMSA physical address variable for populating VMCB" as it is no longer needed due to above VMSA rework
* replace pr_debug_ratelimited() messages for RMP #NPFs with a single trace event
* handle transient PSMASH_FAIL_INUSE return codes in kvm_gmem_invalidate(), switch to WARN_ON*()'s to indicate remaining error cases are not expected and should not be seen in practice. (Paolo)
* add a cond_resched() in kvm_gmem_invalidate() to avoid soft lock-ups when cleaning up large guest memory ranges.
* rename VLEK_REQUIRED to VCEK_DISABLE. it's be more applicable if another key type ever gets added.
* don't allow attestation to be paused while an attestation request is being processed by firmware (Tom)
* add missing Documentation entry for SNP_VLEK_LOAD
* collect Reviewed-by's from Paolo and Tom
Changes since v11:
* Rebase series on kvm-coco-queue and re-work to leverage more infrastructure between SNP/TDX series.
* Drop KVM_SNP_INIT in favor of the new KVM_SEV_INIT2 interface introduced here (Paolo):
https://lore.kernel.org/lkml/[email protected]/
* Drop exposure API fields related to things like VMPL levels, migration agents, etc., until they are actually supported/used (Sean)
* Rework KVM_SEV_SNP_LAUNCH_UPDATE handling to use a new kvm_gmem_populate() interface instead of copying data directly into gmem-allocated pages (Sean)
* Add support for SNP_LOAD_VLEK, rework the SNP_SET_CONFIG_{START,END} to have simpler semantics that are applicable to management of SNP_LOAD_VLEK updates as well, rename interfaces to the now more appropriate SNP_{PAUSE,RESUME}_ATTESTATION
* Fix up documentation wording and do print warnings for userspace-triggerable failures (Peter, Sean)
* Fix a race with AP_CREATION wake-up events (Jacob, Sean)
* Fix a memory leak with VMSA pages (Sean)
* Tighten up handling of RMP page faults to better distinguish between real and spurious cases (Tom)
* Various patch/documentation rewording, cleanups, etc.
----------------------------------------------------------------
Ashish Kalra (1):
KVM: SEV: Avoid WBINVD for HVA-based MMU notifications for SNP
Borislav Petkov (AMD) (3):
[TEMP] x86/kvm/Kconfig: Have KVM_AMD_SEV select ARCH_HAS_CC_PLATFORM [TEMP] x86/cc: Add cc_platform_set/_clear() helpers [TEMP] x86/CPU/AMD: Track SNP host status with cc_platform_*()
Brijesh Singh (10):
KVM: SEV: Add GHCB handling for Hypervisor Feature Support requests
KVM: SEV: Add KVM_SEV_SNP_LAUNCH_START command
KVM: SEV: Add KVM_SEV_SNP_LAUNCH_UPDATE command
KVM: SEV: Add KVM_SEV_SNP_LAUNCH_FINISH command
KVM: SEV: Add support to handle GHCB GPA register VMGEXIT
KVM: SEV: Add support to handle MSR based Page State Change VMGEXIT
KVM: SEV: Add support to handle Page State Change VMGEXIT
KVM: SEV: Add support to handle RMP nested page faults
KVM: SVM: Add module parameter to enable SEV-SNP
KVM: SEV: Provide support for SNP_GUEST_REQUEST NAE event
Michael Roth (10):
KVM: guest_memfd: Fix PTR_ERR() handling in __kvm_gmem_get_pfn()
KVM: SEV: Select KVM_GENERIC_PRIVATE_MEM when CONFIG_KVM_AMD_SEV=y
KVM: SEV: Add initial SEV-SNP support
KVM: SEV: Add support for GHCB-based termination requests
KVM: SEV: Implement gmem hook for initializing private pages
KVM: SEV: Implement gmem hook for invalidating private pages
KVM: x86: Implement gmem hook for determining max NPT mapping level
crypto: ccp: Add the SNP_VLEK_LOAD command
crypto: ccp: Add the SNP_{PAUSE,RESUME}_ATTESTATION commands
KVM: SEV: Provide support for SNP_EXTENDED_GUEST_REQUEST NAE event
Tom Lendacky (2):
KVM: SEV: Add support to handle AP reset MSR protocol
KVM: SEV: Support SEV-SNP AP Creation NAE event
Documentation/virt/coco/sev-guest.rst | 69 +- Documentation/virt/kvm/api.rst | 73 + .../virt/kvm/x86/amd-memory-encryption.rst | 88 +- arch/x86/coco/core.c | 52 + arch/x86/include/asm/kvm_host.h | 2 + arch/x86/include/asm/sev-common.h | 22 +- arch/x86/include/asm/sev.h | 19 +- arch/x86/include/asm/svm.h | 9 +- arch/x86/include/uapi/asm/kvm.h | 39 + arch/x86/kernel/cpu/amd.c | 38 +- arch/x86/kernel/cpu/mtrr/generic.c | 2 +- arch/x86/kernel/sev.c | 10 - arch/x86/kvm/Kconfig | 4 + arch/x86/kvm/mmu.h | 2 - arch/x86/kvm/mmu/mmu.c | 1 + arch/x86/kvm/svm/sev.c | 1444 +++++++++++++++++++- arch/x86/kvm/svm/svm.c | 39 +- arch/x86/kvm/svm/svm.h | 50 + arch/x86/kvm/trace.h | 31 + arch/x86/kvm/x86.c | 19 +- arch/x86/virt/svm/sev.c | 106 +- drivers/crypto/ccp/sev-dev.c | 85 +- drivers/iommu/amd/init.c | 4 +- include/linux/cc_platform.h | 12 + include/linux/psp-sev.h | 4 +- include/uapi/linux/kvm.h | 28 + include/uapi/linux/psp-sev.h | 39 + include/uapi/linux/sev-guest.h | 9 + virt/kvm/guest_memfd.c | 8 +- 29 files changed, 2229 insertions(+), 79 deletions(-)
PATCH v13 1/26 x86/kvm/Kconfig: Have KVM_AMD_SEV select ARCH_HAS_CC_PLATFORM
From: "Borislav Petkov (AMD)" <[email protected]>
The functionality to load SEV-SNP guests by the host will soon rely on cc_platform* helpers because the cpu_feature* API with the early patching is insufficient when SNP support needs to be disabled late.
Therefore, pull that functionality in.
--- arch/x86/kvm/Kconfig | 1 + 1 file changed, 1 insertion(+)
arch/x86/kvm/Kconfig +1 -0
@@ -135,6 +135,7 @@ config KVM_AMD_SEV default y depends on KVM_AMD && X86_64 depends on CRYPTO_DEV_SP_PSP && !(KVM_AMD=y && CRYPTO_DEV_CCP_DD=m) + select ARCH_HAS_CC_PLATFORM help Provides support for launching Encrypted VMs (SEV) and Encrypted VMs with Encrypted State (SEV-ES) on AMD processors.
Signature · 1 line
2.25.1
PATCH v13 2/26 x86/cc: Add cc_platform_set/_clear() helpers
In reply to Michael RothFrom: "Borislav Petkov (AMD)" <[email protected]>
Add functionality to set and/or clear different attributes of the machine as a confidential computing platform. Add the first one too:
whether the machine is running as a host for SEV-SNP guests.
--- arch/x86/coco/core.c | 52 +++++++++++++++++++++++++++++++++++++ include/linux/cc_platform.h | 12 +++++++++ 2 files changed, 64 insertions(+)
arch/x86/coco/core.c +52 -0
@@ -16,6 +16,11 @@enum cc_vendor cc_vendor__ro_after_init =CC_VENDOR_NONE ;u64 cc_mask__ro_after_init ; +static struct cc_attr_flags { +__u64 host_sev_snp :1 , + __resv :63 ; +} cc_flags; + static bool noinstr intel_cc_platform_has(enum cc_attr attr) {switch (attr) {
@@ -89,6 +94,9 @@ static bool noinstr amd_cc_platform_has(enum cc_attr attr)case CC_ATTR_GUEST_SEV_SNP :return sev_status &MSR_AMD64_SEV_SNP_ENABLED ; +case CC_ATTR_HOST_SEV_SNP : +return cc_flags.host_sev_snp; +default :return false; }
@@ -148,3 +156,47 @@ u64 cc_mkdec(u64 val) } }EXPORT_SYMBOL_GPL (cc_mkdec); + +static void amd_cc_platform_clear (enum cc_attr attr) +{ +switch (attr) { +case CC_ATTR_HOST_SEV_SNP : + cc_flags.host_sev_snp =0 ; +break ; +default : +break ; + } +} + +void cc_platform_clear (enum cc_attr attr) +{ +switch (cc_vendor) { +case CC_VENDOR_AMD : +amd_cc_platform_clear (attr); +break ; +default : +break ; + } +} + +static void amd_cc_platform_set (enum cc_attr attr) +{ +switch (attr) { +case CC_ATTR_HOST_SEV_SNP : + cc_flags.host_sev_snp =1 ; +break ; +default : +break ; + } +} + +void cc_platform_set (enum cc_attr attr) +{ +switch (cc_vendor) { +case CC_VENDOR_AMD : +amd_cc_platform_set (attr); +break ; +default : +break ; + } +}
include/linux/cc_platform.h +12 -0
@@ -90,6 +90,14 @@ enum cc_attr {* Examples include TDX Guest. */ CC_ATTR_HOTPLUG_DISABLED , + +/** +* @CC_ATTR_HOST_SEV_SNP: AMD SNP enabled on the host. +* +* The host kernel is running with the necessary features +* enabled to run SEV-SNP guests. +*/ + CC_ATTR_HOST_SEV_SNP, };#ifdef CONFIG_ARCH_HAS_CC_PLATFORM
@@ -107,10 +115,14 @@ enum cc_attr {* * FALSE - Specified Confidential Computing attribute is not active */ bool cc_platform_has (enum cc_attr attr); +void cc_platform_set (enum cc_attr attr); +void cc_platform_clear (enum cc_attr attr);#else /* !CONFIG_ARCH_HAS_CC_PLATFORM */ static inline bool cc_platform_has (enum cc_attr attr) {return false; } +static inline void cc_platform_set (enum cc_attr attr) { } +static inline void cc_platform_clear (enum cc_attr attr) { }#endif /* CONFIG_ARCH_HAS_CC_PLATFORM */
Signature · 1 line
2.25.1
PATCH v13 3/26 x86/CPU/AMD: Track SNP host status with cc_platform_*()
In reply to Michael RothFrom: "Borislav Petkov (AMD)" <[email protected]>
The host SNP worthiness can determined later, after alternatives have been patched, in snp_rmptable_init() depending on cmdline options like iommu=pt which is incompatible with SNP, for example.
Which means that one cannot use X86_FEATURE_SEV_SNP and will need to have a special flag for that control.
Use that newly added CC_ATTR_HOST_SEV_SNP in the appropriate places.
Move kdump_sev_callback() to its rightfull place, while at it.
--- arch/x86/include/asm/sev.h | 4 ++-- arch/x86/kernel/cpu/amd.c | 38 ++++++++++++++++++------------ arch/x86/kernel/cpu/mtrr/generic.c | 2 +- arch/x86/kernel/sev.c | 10 -------- arch/x86/kvm/svm/sev.c | 2 +- arch/x86/virt/svm/sev.c | 26 +++++++++++++------- drivers/crypto/ccp/sev-dev.c | 2 +- drivers/iommu/amd/init.c | 4 +++- 8 files changed, 49 insertions(+), 39 deletions(-)
arch/x86/include/asm/sev.h +2 -2
@@ -228,7 +228,6 @@ int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, struct snvoid snp_accept_memory (phys_addr_t start,phys_addr_t end);u64 snp_get_unsupported_features (u64 status);u64 sev_get_status (void ); -void kdump_sev_callback (void );void sev_show_status (void );#else static inline void sev_es_ist_enter (struct pt_regs *regs) { }
@@ -258,7 +257,6 @@ static inline int snp_issue_guest_request(u64 exit_code, struct snp_req_data *instatic inline void snp_accept_memory (phys_addr_t start,phys_addr_t end) { }static inline u64 snp_get_unsupported_features (u64 status) {return 0 ; }static inline u64 sev_get_status (void ) {return 0 ; } -static inline void kdump_sev_callback (void ) { }static inline void sev_show_status (void ) { }#endif
@@ -270,6 +268,7 @@ int psmash(u64 pfn);int rmp_make_private (u64 pfn,u64 gpa,enum pg_level level,u32 asid,bool immutable);int rmp_make_shared (u64 pfn,enum pg_level level);void snp_leak_pages (u64 pfn,unsigned int npages); +void kdump_sev_callback (void );#else static inline bool snp_probe_rmptable_info (void ) {return false; }static inline int snp_lookup_rmpentry (u64 pfn,bool *assigned,int *level) {return -ENODEV ; }
@@ -282,6 +281,7 @@ static inline int rmp_make_private(u64 pfn, u64 gpa, enum pg_level level, u32 as }static inline int rmp_make_shared (u64 pfn,enum pg_level level) {return -ENODEV ; }static inline void snp_leak_pages (u64 pfn,unsigned int npages) {} +static inline void kdump_sev_callback (void ) { }#endif #endif
arch/x86/kernel/cpu/amd.c +23 -15
@@ -345,6 +345,28 @@ static void srat_detect_node(struct cpuinfo_x86 *c)#endif } +static void bsp_determine_snp (struct cpuinfo_x86 *c) +{ +#ifdef CONFIG_ARCH_HAS_CC_PLATFORM + cc_vendor =CC_VENDOR_AMD ; + +if (cpu_has (c,X86_FEATURE_SEV_SNP )) { +/* +* RMP table entry format is not architectural and is defined by the +* per-processor PPR. Restrict SNP support on the known CPU models +* for which the RMP table entry format is currently defined for. +*/ +if (!cpu_has (c,X86_FEATURE_HYPERVISOR ) && + c->x86 >=0x19 &&snp_probe_rmptable_info ()) { +cc_platform_set (CC_ATTR_HOST_SEV_SNP ); + }else { +setup_clear_cpu_cap (X86_FEATURE_SEV_SNP ); +cc_platform_clear (CC_ATTR_HOST_SEV_SNP ); + } + } +#endif +} +static void bsp_init_amd (struct cpuinfo_x86 *c) {if (cpu_has (c,X86_FEATURE_CONSTANT_TSC )) {
@@ -452,21 +474,7 @@ static void bsp_init_amd(struct cpuinfo_x86 *c)break ; } -if (cpu_has (c,X86_FEATURE_SEV_SNP )) { -/* -* RMP table entry format is not architectural and it can vary by processor -* and is defined by the per-processor PPR. Restrict SNP support on the -* known CPU model and family for which the RMP table entry format is -* currently defined for. -*/ -if (!boot_cpu_has (X86_FEATURE_ZEN3 ) && - !boot_cpu_has (X86_FEATURE_ZEN4 ) && - !boot_cpu_has (X86_FEATURE_ZEN5 )) -setup_clear_cpu_cap (X86_FEATURE_SEV_SNP ); -else if (!snp_probe_rmptable_info ()) -setup_clear_cpu_cap (X86_FEATURE_SEV_SNP ); - } - +bsp_determine_snp (c);return ; warn:
arch/x86/kernel/cpu/mtrr/generic.c +1 -1
@@ -108,7 +108,7 @@ static inline void k8_check_syscfg_dram_mod_en(void) (boot_cpu_data.x86 >= 0x0f)))return ; -if (cpu_feature_enabled (X86_FEATURE_SEV_SNP )) +if (cc_platform_has (CC_ATTR_HOST_SEV_SNP ))return ;rdmsr (MSR_AMD64_SYSCFG , lo, hi);
arch/x86/kernel/sev.c +0 -10
@@ -2287,16 +2287,6 @@ static int __init snp_init_platform_device(void) }device_initcall (snp_init_platform_device); -void kdump_sev_callback (void ) -{ -/* -* Do wbinvd() on remote CPUs when SNP is enabled in order to -* safely do SNP_SHUTDOWN on the local CPU. -*/ -if (cpu_feature_enabled (X86_FEATURE_SEV_SNP )) -wbinvd (); -} -void sev_show_status (void ) {int i;
arch/x86/kvm/svm/sev.c +1 -1
@@ -3296,7 +3296,7 @@ struct page *snp_safe_alloc_page(struct kvm_vcpu *vcpu)unsigned long pfn;struct page *p; -if (!cpu_feature_enabled (X86_FEATURE_SEV_SNP )) +if (!cc_platform_has (CC_ATTR_HOST_SEV_SNP ))return alloc_page (GFP_KERNEL_ACCOUNT | __GFP_ZERO);/*
arch/x86/virt/svm/sev.c +18 -8
@@ -77,7 +77,7 @@ static int __mfd_enable(unsigned int cpu) {u64 val; -if (!cpu_feature_enabled (X86_FEATURE_SEV_SNP )) +if (!cc_platform_has (CC_ATTR_HOST_SEV_SNP ))return 0 ;rdmsrl (MSR_AMD64_SYSCFG , val);
@@ -98,7 +98,7 @@ static int __snp_enable(unsigned int cpu) {u64 val; -if (!cpu_feature_enabled (X86_FEATURE_SEV_SNP )) +if (!cc_platform_has (CC_ATTR_HOST_SEV_SNP ))return 0 ;rdmsrl (MSR_AMD64_SYSCFG , val);
@@ -174,11 +174,11 @@ static int __init snp_rmptable_init(void)u64 rmptable_size;u64 val; -if (!cpu_feature_enabled (X86_FEATURE_SEV_SNP )) +if (!cc_platform_has (CC_ATTR_HOST_SEV_SNP ))return 0 ;if (!amd_iommu_snp_en) -return 0 ; + goto nosnp;if (!probed_rmp_size) goto nosnp;
@@ -225,7 +225,7 @@ static int __init snp_rmptable_init(void)return 0 ; nosnp: -setup_clear_cpu_cap (X86_FEATURE_SEV_SNP ); +cc_platform_clear (CC_ATTR_HOST_SEV_SNP );return -ENOSYS ; }
@@ -246,7 +246,7 @@ static struct rmpentry *__snp_lookup_rmpentry(u64 pfn, int *level) {struct rmpentry *large_entry, *entry; -if (!cpu_feature_enabled (X86_FEATURE_SEV_SNP )) +if (!cc_platform_has (CC_ATTR_HOST_SEV_SNP ))return ERR_PTR (-ENODEV ); entry =get_rmpentry (pfn);
@@ -363,7 +363,7 @@ int psmash(u64 pfn)unsigned long paddr = pfn <<PAGE_SHIFT ;int ret; -if (!cpu_feature_enabled (X86_FEATURE_SEV_SNP )) +if (!cc_platform_has (CC_ATTR_HOST_SEV_SNP ))return -ENODEV ;if (!pfn_valid (pfn))
@@ -472,7 +472,7 @@ static int rmpupdate(u64 pfn, struct rmp_state *state)unsigned long paddr = pfn <<PAGE_SHIFT ;int ret, level; -if (!cpu_feature_enabled (X86_FEATURE_SEV_SNP )) +if (!cc_platform_has (CC_ATTR_HOST_SEV_SNP ))return -ENODEV ; level =RMP_TO_PG_LEVEL (state->pagesize);
@@ -558,3 +558,13 @@ void snp_leak_pages(u64 pfn, unsigned int npages)spin_unlock (&snp_leaked_pages_list_lock); }EXPORT_SYMBOL_GPL (snp_leak_pages); + +void kdump_sev_callback (void ) +{ +/* +* Do wbinvd() on remote CPUs when SNP is enabled in order to +* safely do SNP_SHUTDOWN on the local CPU. +*/ +if (cc_platform_has (CC_ATTR_HOST_SEV_SNP )) +wbinvd (); +}
drivers/crypto/ccp/sev-dev.c +1 -1
@@ -1090,7 +1090,7 @@ static int __sev_snp_init_locked(int *error)void *arg = &data;int cmd, rc =0 ; -if (!cpu_feature_enabled (X86_FEATURE_SEV_SNP )) +if (!cc_platform_has (CC_ATTR_HOST_SEV_SNP ))return -ENODEV ; sev = psp->sev_data;
drivers/iommu/amd/init.c +3 -1
@@ -3228,7 +3228,7 @@ static bool __init detect_ivrs(void)static void iommu_snp_enable (void ) {#ifdef CONFIG_KVM_AMD_SEV -if (!cpu_feature_enabled (X86_FEATURE_SEV_SNP )) +if (!cc_platform_has (CC_ATTR_HOST_SEV_SNP ))return ;/* * The SNP support requires that IOMMU must be enabled, and is
@@ -3236,12 +3236,14 @@ static void iommu_snp_enable(void)*/ if (no_iommu ||iommu_default_passthrough ()) {pr_err ("SNP: IOMMU disabled or configured in passthrough mode, SNP cannot be supported.\n" ); +cc_platform_clear (CC_ATTR_HOST_SEV_SNP );return ; } amd_iommu_snp_en =check_feature (FEATURE_SNP );if (!amd_iommu_snp_en) {pr_err ("SNP: IOMMU SNP feature not enabled, SNP cannot be supported.\n" ); +cc_platform_clear (CC_ATTR_HOST_SEV_SNP );return ; }
Signature · 1 line
2.25.1
PATCH v13 4/26 KVM: guest_memfd: Fix PTR_ERR() handling in __kvm_gmem_get_pfn()
In reply to Michael Rothkvm_gmem_get_folio() may return a PTR_ERR() rather than just NULL. In particular, for cases where EEXISTS is returned when FGP_CREAT_ONLY flag is used. Handle this properly in __kvm_gmem_get_pfn().
--- virt/kvm/guest_memfd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
virt/kvm/guest_memfd.c +2 -2
@@ -580,8 +580,8 @@ static int __kvm_gmem_get_pfn(struct file *file, struct kvm_memory_slot *slot, } folio =kvm_gmem_get_folio (file_inode (file), index, prepare); -if (!folio) -return -ENOMEM ; +if (IS_ERR_OR_NULL (folio)) +return folio ?PTR_ERR (folio) : -ENOMEM ;if (folio_test_hwpoison (folio)) { r = -EHWPOISON ;
Signature · 1 line
2.25.1
Re: [PATCH v13 04/26] KVM: guest_memfd: Fix PTR_ERR() handling in __kvm_gmem_get_pfn()
Quoted 14 lines · Michael Roth
kvm_gmem_get_folio()may return aPTR_ERR()rather than just NULL. In particular, for cases where EEXISTS is returned whenFGP_CREAT_ONLYflag is used. Handle this properly in__kvm_gmem_get_pfn().Signed-off-by Michael Roth <[email protected]>--- virt/kvm/guest_memfd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)virt/kvm/guest_memfd.c +0
-0
virt/kvm/guest_memfd.c static int __kvm_gmem_get_pfn(struct file *file, struct kvm_memory_slot *slot, · line 584
Quoted 7 lines
virt/kvm/guest_memfd.c +2
-2@@ -580,8 +580,8 @@ static int __kvm_gmem_get_pfn(struct file *file, struct kvm_memory_slot *slot, } folio =kvm_gmem_get_folio (file_inode (file), index, prepare); -if (!folio) -return -ENOMEM ; +if (IS_ERR_OR_NULL (folio)) +return folio ?PTR_ERR (folio) : -ENOMEM ;
Will it even return NULL? Staring at other filemap_grab_folio() users, they all check for IS_ERR().
virt/kvm/guest_memfd.c static int __kvm_gmem_get_pfn(struct file *file, struct kvm_memory_slot *slot, · line 587
Quoted 3 lines
virt/kvm/guest_memfd.c +0
-0if (folio_test_hwpoison (folio)) { r = -EHWPOISON ;
Do we have a Fixes: tag?
Signature · 3 lines
Cheers, David / dhildenb
Re: [PATCH v13 04/26] KVM: guest_memfd: Fix PTR_ERR() handling in __kvm_gmem_get_pfn()
Quoted 10 lines · David Hildenbrand
Quoted 9 lines · Michael Roth
kvm_gmem_get_folio()may return aPTR_ERR()rather than just NULL. In particular, for cases where EEXISTS is returned whenFGP_CREAT_ONLYflag is used. Handle this properly in__kvm_gmem_get_pfn().Signed-off-by Michael Roth <[email protected]>--- virt/kvm/guest_memfd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
Quoted 14 lines
virt/kvm/guest_memfd.c static int __kvm_gmem_get_pfn(struct file *file, struct kvm_memory_slot *slot, · line 583
Quoted 11 lines
virt/kvm/guest_memfd.c +2
-2@@ -580,8 +580,8 @@ static int __kvm_gmem_get_pfn(struct file *file, struct kvm_memory_slot *slot, } folio =kvm_gmem_get_folio (file_inode (file), index, prepare); -if (!folio) -return -ENOMEM ; +if (IS_ERR_OR_NULL (folio)) +return folio ?PTR_ERR (folio) : -ENOMEM ;Will it even return NULL? Staring at other
filemap_grab_folio()users, they all check forIS_ERR().
Looks like the NULL case is handled with PTR_ERR(-ENOENT), so IS_ERR() would be sufficient. I think in the past kvm_gmem_get_folio() itself would return NULL in some cases, but as of commit 2b01b7e994e95 that's no longer the case.
I'll fix this up to expect only PTR_ERR() when I re-spin v14, and also address the other kvm_gmem_get_folio() / __filemap_get_folio() call sites.
Quoted 5 lines
virt/kvm/guest_memfd.c static int __kvm_gmem_get_pfn(struct file *file, struct kvm_memory_slot *slot, · line 587
Quoted 3 lines
virt/kvm/guest_memfd.c +0
-0if (folio_test_hwpoison (folio)) { r = -EHWPOISON ;Do we have a Fixes: tag?
Will add that in the re-spin as well.
Thanks!
-Mike
Quoted 6 lines
Signature · 4 lines
-- Cheers, David / dhildenb
Paolo Bonzini
maintainerRe: [PATCH v13 04/26] KVM: guest_memfd: Fix PTR_ERR() handling in __kvm_gmem_get_pfn()
Quoted 37 lines · Michael Roth
Quoted 10 lines · David Hildenbrand
Quoted 9 lines · Michael Roth
kvm_gmem_get_folio()may return aPTR_ERR()rather than just NULL. In particular, for cases where EEXISTS is returned whenFGP_CREAT_ONLYflag is used. Handle this properly in__kvm_gmem_get_pfn().Signed-off-by Michael Roth <[email protected]>--- virt/kvm/guest_memfd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)Quoted 14 lines
virt/kvm/guest_memfd.c static int __kvm_gmem_get_pfn(struct file *file, struct kvm_memory_slot *slot, · line 583
Quoted 11 lines
virt/kvm/guest_memfd.c +2
-2@@ -580,8 +580,8 @@ static int __kvm_gmem_get_pfn(struct file *file, struct kvm_memory_slot *slot, } folio =kvm_gmem_get_folio (file_inode (file), index, prepare); -if (!folio) -return -ENOMEM ; +if (IS_ERR_OR_NULL (folio)) +return folio ?PTR_ERR (folio) : -ENOMEM ;Will it even return NULL? Staring at other
filemap_grab_folio()users, they all check forIS_ERR().Looks like the NULL case is handled with
PTR_ERR(-ENOENT), soIS_ERR()would be sufficient. I think in the pastkvm_gmem_get_folio()itself would return NULL in some cases, but as of commit2b01b7e994e95that's no longer the case.I'll fix this up to expect only
PTR_ERR()when I re-spin v14, and also address the otherkvm_gmem_get_folio()/__filemap_get_folio()call sites.Quoted 1 line
Quoted 6 lines
Quoted 4 lines
virt/kvm/guest_memfd.c static int __kvm_gmem_get_pfn(struct file *file, struct kvm_memory_slot *slot, · line 587
Quoted 2 lines
virt/kvm/guest_memfd.c +0
-0if (folio_test_hwpoison (folio)) { r = -EHWPOISON ;Do we have a Fixes: tag?
Fixes 2b01b7e994e95 ("KVM: guest_memfd: pass error up from filemap_grab_folio")
I'll squash it so when you rebase on the new kvm-coco-queue it will go away. Thanks to both!
Paolo
PATCH v13 5/26 KVM: SEV: Select KVM_GENERIC_PRIVATE_MEM when CONFIG_KVM_AMD_SEV=y
In reply to Michael RothSEV-SNP relies on private memory support to run guests, so make sure to enable that support via the CONFIG_KVM_GENERIC_PRIVATE_MEM config option.
--- arch/x86/kvm/Kconfig | 1 + 1 file changed, 1 insertion(+)
arch/x86/kvm/Kconfig +1 -0
@@ -136,6 +136,7 @@ config KVM_AMD_SEV depends on KVM_AMD && X86_64 depends on CRYPTO_DEV_SP_PSP && !(KVM_AMD=y && CRYPTO_DEV_CCP_DD=m) select ARCH_HAS_CC_PLATFORM + select KVM_GENERIC_PRIVATE_MEM help Provides support for launching Encrypted VMs (SEV) and Encrypted VMs with Encrypted State (SEV-ES) on AMD processors.
Signature · 1 line
2.25.1
PATCH v13 6/26 KVM: SEV: Add support to handle AP reset MSR protocol
In reply to Michael RothFrom: Tom Lendacky <[email protected]>
Add support for AP Reset Hold being invoked using the GHCB MSR protocol, available in version 2 of the GHCB specification.
--- arch/x86/include/asm/sev-common.h | 6 ++-- arch/x86/kvm/svm/sev.c | 56 ++++++++++++++++++++++++++----- arch/x86/kvm/svm/svm.h | 1 + 3 files changed, 53 insertions(+), 10 deletions(-)
arch/x86/include/asm/sev-common.h +4 -2
@@ -54,8 +54,10 @@ (((unsigned long)fn) << 32))/* AP Reset Hold */ -#define GHCB_MSR_AP_RESET_HOLD_REQ 0x006 -#define GHCB_MSR_AP_RESET_HOLD_RESP 0x007 +#define GHCB_MSR_AP_RESET_HOLD_REQ 0x006 +#define GHCB_MSR_AP_RESET_HOLD_RESP 0x007 +#define GHCB_MSR_AP_RESET_HOLD_RESULT_POS 12 +#define GHCB_MSR_AP_RESET_HOLD_RESULT_MASK GENMASK_ULL(51, 0)/* GHCB GPA Register */ #define GHCB_MSR_REG_GPA_REQ 0x012
arch/x86/kvm/svm/sev.c +48 -8
87 lines not on this page · raw message
arch/x86/kvm/svm/svm.h +1 -0
@@ -199,6 +199,7 @@ struct vcpu_sev_es_state {u8 valid_bitmap[16 ];struct kvm_host_map ghcb_map;bool received_first_sipi; +unsigned int ap_reset_hold_type;/* SEV-ES scratch area support */ u64 sw_scratch;
Signature · 1 line
2.25.1
PATCH v13 7/26 KVM: SEV: Add GHCB handling for Hypervisor Feature Support requests
In reply to Michael RothFrom: Brijesh Singh <[email protected]>
Version 2 of the GHCB specification introduced advertisement of features that are supported by the Hypervisor.
Now that KVM supports version 2 of the GHCB specification, bump the maximum supported protocol version.
--- arch/x86/include/asm/sev-common.h | 2 ++ arch/x86/kvm/svm/sev.c | 16 +++++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-)
arch/x86/include/asm/sev-common.h +2 -0
@@ -101,6 +101,8 @@ enum psc_op {/* GHCB Hypervisor Feature Request/Response */ #define GHCB_MSR_HV_FT_REQ 0x080#define GHCB_MSR_HV_FT_RESP 0x081 +#define GHCB_MSR_HV_FT_POS 12 +#define GHCB_MSR_HV_FT_MASK GENMASK_ULL(51, 0)#define GHCB_MSR_HV_FT_RESP_VAL (v) \/* GHCBData[63:12] */ \ (((u64)(v) & GENMASK_ULL(63, 12)) >> 12)
arch/x86/kvm/svm/sev.c +15 -1
@@ -33,9 +33,11 @@#include "cpuid.h" #include "trace.h" -#define GHCB_VERSION_MAX 1ULL +#define GHCB_VERSION_MAX 2ULL#define GHCB_VERSION_MIN 1ULL +#define GHCB_HV_FT_SUPPORTED GHCB_HV_FT_SNP +/* enable/disable SEV support */ static bool sev_enabled = true;module_param_named (sev, sev_enabled, bool,0444 );
@@ -2701,6 +2703,7 @@ static int sev_es_validate_vmgexit(struct vcpu_svm *svm)case SVM_VMGEXIT_AP_HLT_LOOP :case SVM_VMGEXIT_AP_JUMP_TABLE :case SVM_VMGEXIT_UNSUPPORTED_EVENT : +case SVM_VMGEXIT_HV_FEATURES :break ;default : reason =GHCB_ERR_INVALID_EVENT ;
@@ -2961,6 +2964,12 @@ static int sev_handle_vmgexit_msr_protocol(struct vcpu_svm *svm)GHCB_MSR_INFO_MASK , GHCB_MSR_INFO_POS);break ; +case GHCB_MSR_HV_FT_REQ : +set_ghcb_msr_bits (svm,GHCB_HV_FT_SUPPORTED , +GHCB_MSR_HV_FT_MASK ,GHCB_MSR_HV_FT_POS ); +set_ghcb_msr_bits (svm,GHCB_MSR_HV_FT_RESP , +GHCB_MSR_INFO_MASK ,GHCB_MSR_INFO_POS ); +break ;case GHCB_MSR_TERM_REQ : {u64 reason_set, reason_code;
@@ -3085,6 +3094,11 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu) ret =1 ;break ; } +case SVM_VMGEXIT_HV_FEATURES : +ghcb_set_sw_exit_info_2 (svm->sev_es.ghcb,GHCB_HV_FT_SUPPORTED ); + + ret =1 ; +break ;case SVM_VMGEXIT_UNSUPPORTED_EVENT : vcpu_unimpl(vcpu, "vmgexit: unsupported event - exit_info_1=%#llx, exit_info_2=%#llx\n",
Signature · 1 line
2.25.1
PATCH v13 8/26 KVM: SEV: Add initial SEV-SNP support
In reply to Michael RothSEV-SNP builds upon existing SEV and SEV-ES functionality while adding new hardware-based security protection. SEV-SNP adds strong memory encryption and integrity protection to help prevent malicious hypervisor-based attacks such as data replay, memory re-mapping, and more, to create an isolated execution environment.
Define a new KVM_X86_SNP_VM type which makes use of these capabilities and extend the KVM_SEV_INIT2 ioctl to support it. Also add a basic helper to check whether SNP is enabled.
--- arch/x86/include/asm/svm.h | 3 ++- arch/x86/include/uapi/asm/kvm.h | 1 + arch/x86/kvm/svm/sev.c | 21 ++++++++++++++++++++- arch/x86/kvm/svm/svm.c | 3 ++- arch/x86/kvm/svm/svm.h | 12 ++++++++++++ arch/x86/kvm/x86.c | 2 +- 6 files changed, 38 insertions(+), 4 deletions(-)
arch/x86/include/asm/svm.h +2 -1
@@ -285,7 +285,8 @@ static_assert((X2AVIC_MAX_PHYSICAL_ID & AVIC_PHYSICAL_MAX_INDEX_MASK) == X2AVIC_#define AVIC_HPA_MASK ~((0xFFFULL << 52) | 0xFFF) -#define SVM_SEV_FEAT_DEBUG_SWAP BIT(5) +#define SVM_SEV_FEAT_SNP_ACTIVE BIT(0) +#define SVM_SEV_FEAT_DEBUG_SWAP BIT(5)struct vmcb_seg {u16 selector;
arch/x86/include/uapi/asm/kvm.h +1 -0
@@ -872,5 +872,6 @@ struct kvm_hyperv_eventfd {#define KVM_X86_SW_PROTECTED_VM 1#define KVM_X86_SEV_VM 2#define KVM_X86_SEV_ES_VM 3 +#define KVM_X86_SNP_VM 4#endif /* _ASM_X86_KVM_H */
arch/x86/kvm/svm/sev.c +20 -1
@@ -46,6 +46,9 @@ module_param_named(sev, sev_enabled, bool, 0444);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; +/* enable/disable SEV-ES DebugSwap support */ static bool sev_es_debug_swap_enabled = true;module_param_named (debug_swap, sev_es_debug_swap_enabled, bool,0444 );
@@ -275,6 +278,9 @@ static int __sev_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp, sev->es_active = es_active; sev->vmsa_features = data->vmsa_features; +if (vm_type ==KVM_X86_SNP_VM ) + sev->vmsa_features |=SVM_SEV_FEAT_SNP_ACTIVE ; + ret =sev_asid_new (sev);if (ret) goto e_no_asid;
@@ -326,7 +332,8 @@ static int sev_guest_init2(struct kvm *kvm, struct kvm_sev_cmd *argp)return -EINVAL ;if (kvm->arch.vm_type !=KVM_X86_SEV_VM && - kvm->arch.vm_type !=KVM_X86_SEV_ES_VM ) + kvm->arch.vm_type !=KVM_X86_SEV_ES_VM && + kvm->arch.vm_type !=KVM_X86_SNP_VM )return -EINVAL ;if (copy_from_user (&data,u64_to_user_ptr (argp->data),sizeof (data)))
@@ -2306,11 +2313,16 @@ void __init sev_set_cpu_caps(void)kvm_cpu_cap_set (X86_FEATURE_SEV_ES ); kvm_caps.supported_vm_types |=BIT (KVM_X86_SEV_ES_VM ); } +if (sev_snp_enabled) { +kvm_cpu_cap_set (X86_FEATURE_SEV_SNP ); + kvm_caps.supported_vm_types |=BIT (KVM_X86_SNP_VM ); + } }void __init sev_hardware_setup (void ) {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;
@@ -2391,6 +2403,7 @@ void __init sev_hardware_setup(void) sev_es_asid_count = min_sev_asid -1 ;WARN_ON_ONCE (misc_cg_set_capacity (MISC_CG_RES_SEV_ES , sev_es_asid_count)); sev_es_supported = true; + sev_snp_supported = sev_snp_enabled &&cc_platform_has (CC_ATTR_HOST_SEV_SNP ); out:if (boot_cpu_has (X86_FEATURE_SEV ))
@@ -2403,9 +2416,15 @@ void __init sev_hardware_setup(void)pr_info ("SEV-ES %s (ASIDs %u - %u)\n" , sev_es_supported ?"enabled" :"disabled" , min_sev_asid >1 ?1 :0 , min_sev_asid -1 ); +if (boot_cpu_has (X86_FEATURE_SEV_SNP )) +pr_info ("SEV-SNP %s (ASIDs %u - %u)\n" , + sev_snp_supported ?"enabled" :"disabled" , + min_sev_asid >1 ?1 :0 , min_sev_asid -1 ); sev_enabled = sev_supported; sev_es_enabled = sev_es_supported; + sev_snp_enabled = sev_snp_supported; +if (!sev_es_enabled || !cpu_feature_enabled (X86_FEATURE_DEBUG_SWAP ) || !cpu_feature_enabled (X86_FEATURE_NO_NESTED_DATA_BP )) sev_es_debug_swap_enabled = false;
arch/x86/kvm/svm/svm.c +2 -1
@@ -4899,7 +4899,8 @@ static int svm_vm_init(struct kvm *kvm)if (type !=KVM_X86_DEFAULT_VM && type !=KVM_X86_SW_PROTECTED_VM ) { - kvm->arch.has_protected_state = (type ==KVM_X86_SEV_ES_VM ); + kvm->arch.has_protected_state = + (type ==KVM_X86_SEV_ES_VM || type ==KVM_X86_SNP_VM );to_kvm_sev_info (kvm)->need_init = true; }
arch/x86/kvm/svm/svm.h +12 -0
@@ -348,6 +348,18 @@ static __always_inline bool sev_es_guest(struct kvm *kvm)#endif } +static __always_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->vmsa_features &SVM_SEV_FEAT_SNP_ACTIVE ) && + !WARN_ON_ONCE (!sev_es_guest (kvm)); +#else +return false; +#endif +} +static inline void vmcb_mark_all_dirty (struct vmcb *vmcb) { vmcb->control.clean =0 ;
arch/x86/kvm/x86.c +1 -1
@@ -12598,7 +12598,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) kvm->arch.vm_type = type; kvm->arch.has_private_mem = - (type ==KVM_X86_SW_PROTECTED_VM ); + (type ==KVM_X86_SW_PROTECTED_VM || type ==KVM_X86_SNP_VM ); ret =kvm_page_track_init (kvm);if (ret)
Signature · 1 line
2.25.1
Paolo Bonzini
maintainerRe: [PATCH v13 08/26] KVM: SEV: Add initial SEV-SNP support
arch/x86/kvm/svm/svm.c static int svm_vm_init(struct kvm *kvm) · line 4903
Quoted 11 lines · Michael Roth
arch/x86/kvm/svm/svm.c +2
-1@@ -4899,7 +4899,8 @@ static int svm_vm_init(struct kvm *kvm)if (type !=KVM_X86_DEFAULT_VM && type !=KVM_X86_SW_PROTECTED_VM ) { - kvm->arch.has_protected_state = (type ==KVM_X86_SEV_ES_VM ); + kvm->arch.has_protected_state = + (type ==KVM_X86_SEV_ES_VM || type ==KVM_X86_SNP_VM );
I'd rather set has_private_mem here too, rather than in x86.c
Also this patch is the place to have
arch/x86/kvm/svm/svm.c +3 -0
@@ -2056,6 +2056,9 @@ static int npf_interception(struct kvm_vcpu *vcpu)if (WARN_ON_ONCE (error_code &PFERR_SYNTHETIC_MASK )) error_code &= ~PFERR_SYNTHETIC_MASK ; +if (sev_snp_guest (vcpu) && (error_code &PFERR_GUEST_ENC_MASK )) + error_code |=PFERR_PRIVATE_ACCESS ; +trace_kvm_page_fault (vcpu, fault_address, error_code); return kvm_mmu_page_fault(vcpu, fault_address, error_code, static_cpu_has(X86_FEATURE_DECODEASSISTS) ?
for the final shape of the MMU changes.
Paolo
Quoted 16 lines
arch/x86/kvm/x86.c +1
-2@@ -12598,7 +12598,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) kvm->arch.vm_type = type; kvm->arch.has_private_mem = - (type ==KVM_X86_SW_PROTECTED_VM ); + (type ==KVM_X86_SW_PROTECTED_VM || type ==KVM_X86_SNP_VM ); ret =kvm_page_track_init (kvm);if (ret) --2.25.1
PATCH v13 9/26 KVM: SEV: Add KVM_SEV_SNP_LAUNCH_START command
In reply to Michael RothFrom: Brijesh Singh <[email protected]>
KVM_SEV_SNP_LAUNCH_START begins the launch process for an SEV-SNP guest. The command initializes a cryptographic digest context used to construct the measurement of the guest. Other commands can then at that point be used to load/encrypt data into the guest's initial launch image.
For more information see the SEV-SNP specification.
--- .../virt/kvm/x86/amd-memory-encryption.rst | 23 +- arch/x86/include/uapi/asm/kvm.h | 8 + arch/x86/kvm/svm/sev.c | 208 +++++++++++++++++- arch/x86/kvm/svm/svm.h | 1 + 4 files changed, 236 insertions(+), 4 deletions(-)
Documentation/virt/kvm/x86/amd-memory-encryption.rst +22 -1
@@ -459,6 +459,25 @@ issued by the hypervisor to make the guest ready for execution. Returns: 0 on success, -negative on error +18. KVM_SEV_SNP_LAUNCH_START +---------------------------- + +The KVM_SNP_LAUNCH_START command is used for creating the memory encryption +context for the SEV-SNP guest. + +Parameters (in): struct kvm_sev_snp_launch_start + +Returns: 0 on success, -negative on error + +:: + + struct kvm_sev_snp_launch_start { + __u64 policy; /* Guest policy to use. */ + __u8 gosvw[16]; /* Guest OS visible workarounds. */ + }; + +See the SEV-SNP spec [snp-fw-abi]_ for further detail on the launch input. + Device attribute API ====================
@@ -490,9 +509,11 @@ References ========== -See [white-paper]_, [api-spec]_, [amd-apm]_ and [kvm-forum]_ for more info. +See [white-paper]_, [api-spec]_, [amd-apm]_, [kvm-forum]_, and [snp-fw-abi]_ +for more info. .. [white-paper] https://developer.amd.com/wordpress/media/2013/12/AMD_Memory_Encryption_Whitepaper_v7-Public.pdf .. [api-spec] https://support.amd.com/TechDocs/55766_SEV-KM_API_Specification.pdf .. [amd-apm] https://support.amd.com/TechDocs/24593.pdf (section 15.34) .. [kvm-forum] https://www.linux-kvm.org/images/7/74/02x08A-Thomas_Lendacky-AMDs_Virtualizatoin_Memory_Encryption_Technology.pdf +.. [snp-fw-abi] https://www.amd.com/system/files/TechDocs/56860.pdf
arch/x86/include/uapi/asm/kvm.h +8 -0
@@ -697,6 +697,9 @@ enum sev_cmd_id {/* Second time is the charm; improved versions of the above ioctls. */ KVM_SEV_INIT2 , +/* SNP-specific commands */ +KVM_SEV_SNP_LAUNCH_START =100 , + KVM_SEV_NR_MAX, };
@@ -822,6 +825,11 @@ struct kvm_sev_receive_update_data {__u32 pad2; }; +struct kvm_sev_snp_launch_start { +__u64 policy; +__u8 gosvw[16 ]; +}; +#define KVM_X2APIC_API_USE_32BIT_IDS (1ULL << 0)#define KVM_X2APIC_API_DISABLE_BROADCAST_QUIRK (1ULL << 1)
arch/x86/kvm/svm/sev.c +205 -3
266 lines not on this page · raw message
arch/x86/kvm/svm/svm.h +1 -0
@@ -92,6 +92,7 @@ struct kvm_sev_info {struct list_head mirror_entry;/* Use as a list entry of mirrors */ struct misc_cg *misc_cg;/* For misc cgroup accounting */ atomic_t migration_in_progress; +void *snp_context;/* SNP guest context page */ };struct kvm_svm {
Signature · 1 line
2.25.1
Paolo Bonzini
maintainerRe: [PATCH v13 09/26] KVM: SEV: Add KVM_SEV_SNP_LAUNCH_START command
Quoted 9 lines · Michael Roth
arch/x86/kvm/svm/sev.c +9
-0+/* As defined by SEV-SNP Firmware ABI, under "Guest Policy". */ +#define SNP_POLICY_MASK_API_MAJOR GENMASK_ULL(15, 8) +#define SNP_POLICY_MASK_API_MINOR GENMASK_ULL(7, 0) + +#define SNP_POLICY_MASK_VALID (SNP_POLICY_MASK_SMT | \ + SNP_POLICY_MASK_RSVD_MBO | \ + SNP_POLICY_MASK_DEBUG | \ + SNP_POLICY_MASK_SINGLE_SOCKET | \ + SNP_POLICY_MASK_API_MAJOR | \
arch/x86/kvm/svm/sev.c static u64 sev_supported_vmsa_features;
Quoted 10 lines
arch/x86/kvm/svm/sev.c +9
-0+ SNP_POLICY_MASK_API_MINOR) + +/* KVM's SNP support is compatible with 1.51 of the SEV-SNP Firmware ABI. */ +#define SNP_POLICY_API_MAJOR 1 +#define SNP_POLICY_API_MINOR 51 +static inline bool sev_version_greater_or_equal (u8 major,u8 minor) +{ +if (major <SNP_POLICY_API_MAJOR ) +return true;
Should it perhaps refuse version 0.x? With something like a
#define SNP_POLICY_API_MAJOR_MIN 1
to make it a bit more future proof (and testable).
Quoted 1 line
+ major = (params.policy &
SNP_POLICY_MASK_API_MAJOR);
This should be >> 8. Do the QEMU patches not set the API version? :)
Paolo
Re: [PATCH v13 09/26] KVM: SEV: Add KVM_SEV_SNP_LAUNCH_START command
Quoted 23 lines · Paolo Bonzini
Quoted 9 lines · Michael Roth
arch/x86/kvm/svm/sev.c +9
-0+/* As defined by SEV-SNP Firmware ABI, under "Guest Policy". */ +#define SNP_POLICY_MASK_API_MAJOR GENMASK_ULL(15, 8) +#define SNP_POLICY_MASK_API_MINOR GENMASK_ULL(7, 0) + +#define SNP_POLICY_MASK_VALID (SNP_POLICY_MASK_SMT | \ + SNP_POLICY_MASK_RSVD_MBO | \ + SNP_POLICY_MASK_DEBUG | \ + SNP_POLICY_MASK_SINGLE_SOCKET | \ + SNP_POLICY_MASK_API_MAJOR | \arch/x86/kvm/svm/sev.c static u64 sev_supported_vmsa_features;
Quoted 10 lines
arch/x86/kvm/svm/sev.c +9
-0+ SNP_POLICY_MASK_API_MINOR) + +/* KVM's SNP support is compatible with 1.51 of the SEV-SNP Firmware ABI. */ +#define SNP_POLICY_API_MAJOR 1 +#define SNP_POLICY_API_MINOR 51 +static inline bool sev_version_greater_or_equal (u8 major,u8 minor) +{ +if (major <SNP_POLICY_API_MAJOR ) +return true;Should it perhaps refuse version 0.x? With something like a
Quoted 7 lines
#define
SNP_POLICY_API_MAJOR_MIN1to make it a bit more future proof (and testable).
Quoted 1 line
+ major = (params.policy &
SNP_POLICY_MASK_API_MAJOR);This should be >> 8. Do the QEMU patches not set the API version? :)
Argh...it does if you set it via the -object sev-snp-guest,policy=0x... option. I tested with reserved ranges and other flags, but not with non-zero major/minor API fields. =/
But I'm having 2nd thoughts about trying to enforce API version via KVM_SEV_SNP_LAUNCH_START. In practice, the only sensible way to really interpret it is as "the minimum firmware version that userspace decides it is comfortable running a particular guest" on. And that enforcement is already handled as part of the SNP_LAUNCH_START firmware command in the SNP Firmware ABI: if the policy specifies a higher minimum version than what firmware is currently running then it will return as error that will be reported by QEMU as:
sev_snp_launch_start: SNP_LAUNCH_START ret=-5 fw_error=7 'Policy is not allowed'
On the firmware driver side (drivers/crypto/ccp/sev-dev.c), we already enforce 1.51 as minimum supported SNP firmware, so that sort of already covers the SNP_POLICY_API_MAJOR_MIN case as well. E.g. the test surface KVM needs to concern itself with is already effectively 1.51+. In that sense, whether the user decides to be less restrictive with what minimum firmware version they allow is then totally up to the user, and has no bearing on what firmware versions KVM needs to concern itself with.
Then the question of whether or not KVM fully *exposes* a particular user-visible feature of a newer version of the firmware/ABI would be a separate thing to be handled via the normal KVM capabilities/attributes mechanisms.
So my current leaning is to send a v14 that backs out the major/minor policy enforcement and let firmware handle that aspect. (and also address your other comments).
But let me know if you think that should be handled differently.
Thanks!
-Mike
Quoted 3 lines
Paolo
Paolo Bonzini
maintainerRe: [PATCH v13 09/26] KVM: SEV: Add KVM_SEV_SNP_LAUNCH_START command
Quoted 3 lines · Michael Roth
So my current leaning is to send a v14 that backs out the major/minor policy enforcement and let firmware handle that aspect. (and also address your other comments).
Sounds good to me!
In the meanwhile I guess you can also update to have PFERR_PRIVATE_ACCESS.
Paolo
Sean Christopherson
maintainerRe: [PATCH v13 09/26] KVM: SEV: Add KVM_SEV_SNP_LAUNCH_START command
In reply to Michael RothQuoted 18 lines · Michael Roth
arch/x86/kvm/svm/sev.c +18
-0+static inline bool sev_version_greater_or_equal (u8 major,u8 minor) +{ +if (major <SNP_POLICY_API_MAJOR ) +return true; + +if (major ==SNP_POLICY_API_MAJOR && minor <=SNP_POLICY_API_MINOR ) +return true; + +return false; +} + +static int snp_launch_start (struct kvm *kvm,struct kvm_sev_cmd *argp) +{ +struct kvm_sev_info *sev = &to_kvm_svm (kvm)->sev_info; +struct sev_data_snp_launch_start start = {0 }; +struct kvm_sev_snp_launch_start params; +u8 major, minor; +int rc;
arch/x86/kvm/svm/sev.c int sev_dev_get_attr(u32 group, u64 attr, u64 *val) · line 2072
Quoted 10 lines
arch/x86/kvm/svm/sev.c +10
-0+ +if (!sev_snp_guest (kvm)) +return -ENOTTY ; + +if (copy_from_user (¶ms,u64_to_user_ptr (argp->data),sizeof (params))) +return -EFAULT ; + +/* Don't allow userspace to allocate memory for more than 1 SNP context. */ +if (sev->snp_context) { +pr_debug ("SEV-SNP context already exists. Refusing to allocate an additional one.\n" );
What's the plan with all these printks? There are far too many in this series.
Some might be useful, but many of them have no business landing upstream.
arch/x86/kvm/svm/sev.c int sev_dev_get_attr(u32 group, u64 attr, u64 *val) · line 2081
Quoted 9 lines
arch/x86/kvm/svm/sev.c +9
-0+return -EINVAL ; + } + + sev->snp_context =snp_context_create (kvm, argp); +if (!sev->snp_context) +return -ENOTTY ; + +if (params.policy & ~SNP_POLICY_MASK_VALID ) { + pr_debug("SEV-SNP hypervisor does not support requested policy %llx (supported %llx).\n",
What does "SEV-SNP hypervisor" even mean?
Quoted 13 lines
arch/x86/kvm/svm/sev.c +13
-0+ params.policy, SNP_POLICY_MASK_VALID); +return -EINVAL ; + } + +if (!(params.policy &SNP_POLICY_MASK_RSVD_MBO )) { +pr_debug ("SEV-SNP hypervisor does not support requested policy %llx (must be set %llx).\n" , + params.policy,SNP_POLICY_MASK_RSVD_MBO ); +return -EINVAL ; + } + +if (params.policy &SNP_POLICY_MASK_SINGLE_SOCKET ) { +pr_debug ("SEV-SNP hypervisor does not support limiting guests to a single socket.\n" ); +return -EINVAL ;
arch/x86/kvm/svm/sev.c int sev_dev_get_attr(u32 group, u64 attr, u64 *val) · line 2104
Quoted 10 lines
arch/x86/kvm/svm/sev.c +10
-0+ } + +if (!(params.policy &SNP_POLICY_MASK_SMT )) { +pr_debug ("SEV-SNP hypervisor does not support limiting guests to a single SMT thread.\n" ); +return -EINVAL ; + } + + major = (params.policy &SNP_POLICY_MASK_API_MAJOR ); + minor = (params.policy &SNP_POLICY_MASK_API_MINOR ); +if (!sev_version_greater_or_equal (major, minor)) {
Why does this need a someone weirdly named helper? Isn't this just?
if (major < SNP_POLICY_API_MAJOR || (major == SNP_POLICY_API_MAJOR && minor < SNP_POLICY_API_MINOR))
Quoted 66 lines
arch/x86/kvm/svm/sev.c +45
-0+pr_debug ("SEV-SNP hypervisor does not support requested version %d.%d (have %d,%d).\n" , + major, minor,SNP_POLICY_API_MAJOR ,SNP_POLICY_API_MINOR ); +return -EINVAL ; + } + + start.gctx_paddr =__psp_pa (sev->snp_context); + start.policy = params.policy; +memcpy (start.gosvw, params.gosvw,sizeof (params.gosvw)); + rc =__sev_issue_cmd (argp->sev_fd,SEV_CMD_SNP_LAUNCH_START , &start, &argp->error); +if (rc) { +pr_debug ("SEV_CMD_SNP_LAUNCH_START firmware command failed, rc %d\n" , rc); + goto e_free_context; + } + + sev->fd = argp->sev_fd; + rc =snp_bind_asid (kvm, &argp->error); +if (rc) { +pr_debug ("Failed to bind ASID to SEV-SNP context, rc %d\n" , rc); + goto e_free_context; + } + +return 0 ; + +e_free_context: +snp_decommission_context (kvm); + +return rc; +} +int sev_mem_enc_ioctl (struct kvm *kvm,void __user *argp) {struct kvm_sev_cmd sev_cmd;@@ -1999,6 +2154,15 @@ int sev_mem_enc_ioctl(struct kvm *kvm, void __user *argp) goto out; } +/* +* Once KVM_SEV_INIT2 initializes a KVM instance as an SNP guest, only +* allow the use of SNP-specific commands. +*/ +if (sev_snp_guest (kvm) && sev_cmd.id <KVM_SEV_SNP_LAUNCH_START ) { + r = -EPERM ; + goto out; + } +switch (sev_cmd.id) {case KVM_SEV_ES_INIT :if (!sev_es_enabled) {@@ -2063,6 +2227,9 @@ int sev_mem_enc_ioctl(struct kvm *kvm, void __user *argp)case KVM_SEV_RECEIVE_FINISH : r =sev_receive_finish (kvm, &sev_cmd);break ; +case KVM_SEV_SNP_LAUNCH_START : + r =snp_launch_start (kvm, &sev_cmd); +break ;default : r = -EINVAL ; goto out;@@ -2258,6 +2425,33 @@ int sev_vm_copy_enc_context_from(struct kvm *kvm, unsigned int source_fd)return ret; } +static int snp_decommission_context (struct kvm *kvm) +{ +struct kvm_sev_info *sev = &to_kvm_svm (kvm)->sev_info; +struct sev_data_snp_addr data = {};
arch/x86/kvm/svm/sev.c int sev_vm_copy_enc_context_from(struct kvm *kvm, unsigned int source_fd) · line 2441
Quoted 10 lines
arch/x86/kvm/svm/sev.c +10
-0+int ret; + +/* If context is not created then do nothing */ +if (!sev->snp_context) +return 0 ; + + data.address =__sme_pa (sev->snp_context); +down_write (&sev_deactivate_lock); + ret =sev_do_cmd (SEV_CMD_SNP_DECOMMISSION , &data,NULL ); +if (WARN_ONCE (ret,"failed to release guest context" )) {
WARN here, or WARN in the caller, not both. And if you warn here, this can be
down_write(&sev_deactivate_lock); ret = sev_do_cmd(SEV_CMD_SNP_DECOMMISSION, &data, NULL); up_write(&sev_deactivate_lock); if (WARN_ONCE(ret, "..."))
arch/x86/kvm/svm/sev.c int sev_vm_copy_enc_context_from(struct kvm *kvm, unsigned int source_fd) · line 2448
Quoted 7 lines
arch/x86/kvm/svm/sev.c +7
-0+up_write (&sev_deactivate_lock); +return ret; + } + +up_write (&sev_deactivate_lock); + +/* free the context page now */
This doesn't seem like a particularly useful comment. What would be useful is a comment explaining the "decommission" unbinds the ASID.
Quoted 10 lines
arch/x86/kvm/svm/sev.c +6
-0+snp_free_firmware_page (sev->snp_context); + sev->snp_context =NULL ; + +return 0 ; +} +void sev_vm_destroy (struct kvm *kvm) {struct kvm_sev_info *sev = &to_kvm_svm (kvm)->sev_info;
arch/x86/kvm/svm/sev.c void sev_vm_destroy(struct kvm *kvm) · line 2498
Quoted 7 lines
arch/x86/kvm/svm/sev.c +3
-1@@ -2299,7 +2493,15 @@ void sev_vm_destroy(struct kvm *kvm) } } -sev_unbind_asid (kvm, sev->handle); +if (sev_snp_guest (kvm)) { +if (snp_decommission_context (kvm)) { +WARN_ONCE (1 ,"Failed to free SNP guest context, leaking asid!\n" );
WARN on the actually failure, not '1'. And a newline isn't needed.
if (WARN_ONCE(snp_decommission_context(kvm) "Failed to free SNP guest context, leaking asid!")) return;
Quoted 24 lines
arch/x86/kvm/svm/sev.c +6
-0+return ; + } + }else { +sev_unbind_asid (kvm, sev->handle); + } +sev_asid_free (sev); }arch/x86/kvm/svm/svm.h +1
-0@@ -92,6 +92,7 @@ struct kvm_sev_info {struct list_head mirror_entry;/* Use as a list entry of mirrors */ struct misc_cg *misc_cg;/* For misc cgroup accounting */ atomic_t migration_in_progress; +void *snp_context;/* SNP guest context page */ };struct kvm_svm {Signature · 1 line
2.25.1
PATCH v13 10/26 KVM: SEV: Add KVM_SEV_SNP_LAUNCH_UPDATE command
In reply to Michael RothFrom: Brijesh Singh <[email protected]>
A key aspect of a launching an SNP guest is initializing it with a known/measured payload which is then encrypted into guest memory as pre-validated private pages and then measured into the cryptographic launch context created with KVM_SEV_SNP_LAUNCH_START so that the guest can attest itself after booting.
Since all private pages are provided by guest_memfd, make use of the kvm_gmem_populate() interface to handle this. The general flow is that guest_memfd will handle allocating the pages associated with the GPA ranges being initialized by each particular call of KVM_SEV_SNP_LAUNCH_UPDATE, copying data from userspace into those pages, and then the post_populate callback will do the work of setting the RMP entries for these pages to private and issuing the SNP firmware calls to encrypt/measure them.
For more information see the SEV-SNP specification.
--- .../virt/kvm/x86/amd-memory-encryption.rst | 39 ++++ arch/x86/include/uapi/asm/kvm.h | 15 ++ arch/x86/kvm/svm/sev.c | 218 ++++++++++++++++++ 3 files changed, 272 insertions(+)
Documentation/virt/kvm/x86/amd-memory-encryption.rst +39 -0
@@ -478,6 +478,45 @@ Returns: 0 on success, -negative on error See the SEV-SNP spec [snp-fw-abi]_ for further detail on the launch input. +19. KVM_SEV_SNP_LAUNCH_UPDATE +----------------------------- + +The KVM_SEV_SNP_LAUNCH_UPDATE command is used for loading userspace-provided +data into a guest GPA range, measuring the contents into the SNP guest context +created by KVM_SEV_SNP_LAUNCH_START, and then encrypting/validating that GPA +range so that it will be immediately readable using the encryption key +associated with the guest context once it is booted, after which point it can +attest the measurement associated with its context before unlocking any +secrets. + +It is required that the GPA ranges initialized by this command have had the +KVM_MEMORY_ATTRIBUTE_PRIVATE attribute set in advance. See the documentation +for KVM_SET_MEMORY_ATTRIBUTES for more details on this aspect. + +Parameters (in): struct kvm_sev_snp_launch_update + +Returns: 0 on success, -negative on error + +:: + + struct kvm_sev_snp_launch_update { + __u64 gfn_start; /* Guest page number to load/encrypt data into. */ + __u64 uaddr; /* Userspace address of data to be loaded/encrypted. */ + __u32 len; /* 4k-aligned length in bytes to copy into guest memory.*/ + __u8 type; /* The type of the guest pages being initialized. */ + }; + +where the allowed values for page_type are #define'd as:: + + KVM_SEV_SNP_PAGE_TYPE_NORMAL + KVM_SEV_SNP_PAGE_TYPE_ZERO + KVM_SEV_SNP_PAGE_TYPE_UNMEASURED + KVM_SEV_SNP_PAGE_TYPE_SECRETS + KVM_SEV_SNP_PAGE_TYPE_CPUID + +See the SEV-SNP spec [snp-fw-abi]_ for further details on how each page type is +used/measured. + Device attribute API ====================
arch/x86/include/uapi/asm/kvm.h +15 -0
@@ -699,6 +699,7 @@ enum sev_cmd_id {/* SNP-specific commands */ KVM_SEV_SNP_LAUNCH_START =100 , +KVM_SEV_SNP_LAUNCH_UPDATE , KVM_SEV_NR_MAX, };
@@ -830,6 +831,20 @@ struct kvm_sev_snp_launch_start {__u8 gosvw[16 ]; }; +/* Kept in sync with firmware values for simplicity. */ +#define KVM_SEV_SNP_PAGE_TYPE_NORMAL 0x1 +#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 gfn_start; +__u64 uaddr; +__u32 len; +__u8 type; +}; +#define KVM_X2APIC_API_USE_32BIT_IDS (1ULL << 0)#define KVM_X2APIC_API_DISABLE_BROADCAST_QUIRK (1ULL << 1)
arch/x86/kvm/svm/sev.c +218 -0
236 lines not on this page · raw message
Signature · 1 line
2.25.1
Paolo Bonzini
maintainerRe: [PATCH v13 10/26] KVM: SEV: Add KVM_SEV_SNP_LAUNCH_UPDATE command
Quoted 320 lines · Michael Roth
From: Brijesh Singh <[email protected]>
A key aspect of a launching an SNP guest is initializing it with a known/measured payload which is then encrypted into guest memory as pre-validated private pages and then measured into the cryptographic launch context created with
KVM_SEV_SNP_LAUNCH_STARTso that the guest can attest itself after booting.Since all private pages are provided by
guest_memfd, make use of thekvm_gmem_populate()interface to handle this. The general flow is thatguest_memfdwill handle allocating the pages associated with the GPA ranges being initialized by each particular call ofKVM_SEV_SNP_LAUNCH_UPDATE, copying data from userspace into those pages, and then thepost_populatecallback will do the work of setting the RMP entries for these pages to private and issuing the SNP firmware calls to encrypt/measure them.For more information see the SEV-SNP specification.
Signed-off-by Brijesh Singh <[email protected]>Co-developed-by Michael Roth <[email protected]>Signed-off-by Michael Roth <[email protected]>Signed-off-by Ashish Kalra <[email protected]>--- .../virt/kvm/x86/amd-memory-encryption.rst | 39 ++++ arch/x86/include/uapi/asm/kvm.h | 15 ++ arch/x86/kvm/svm/sev.c | 218 ++++++++++++++++++ 3 files changed, 272 insertions(+)Documentation/virt/kvm/x86/amd-memory-encryption.rst +39
-0@@ -478,6 +478,45 @@ Returns: 0 on success, -negative on error See the SEV-SNP spec [snp-fw-abi]_ for further detail on the launch input. +19. KVM_SEV_SNP_LAUNCH_UPDATE +----------------------------- + +The KVM_SEV_SNP_LAUNCH_UPDATE command is used for loading userspace-provided +data into a guest GPA range, measuring the contents into the SNP guest context +created by KVM_SEV_SNP_LAUNCH_START, and then encrypting/validating that GPA +range so that it will be immediately readable using the encryption key +associated with the guest context once it is booted, after which point it can +attest the measurement associated with its context before unlocking any +secrets. + +It is required that the GPA ranges initialized by this command have had the +KVM_MEMORY_ATTRIBUTE_PRIVATE attribute set in advance. See the documentation +for KVM_SET_MEMORY_ATTRIBUTES for more details on this aspect. + +Parameters (in): struct kvm_sev_snp_launch_update + +Returns: 0 on success, -negative on error + +:: + + struct kvm_sev_snp_launch_update { + __u64 gfn_start; /* Guest page number to load/encrypt data into. */ + __u64 uaddr; /* Userspace address of data to be loaded/encrypted. */ + __u32 len; /* 4k-aligned length in bytes to copy into guest memory.*/ + __u8 type; /* The type of the guest pages being initialized. */ + }; + +where the allowed values for page_type are #define'd as:: + + KVM_SEV_SNP_PAGE_TYPE_NORMAL + KVM_SEV_SNP_PAGE_TYPE_ZERO + KVM_SEV_SNP_PAGE_TYPE_UNMEASURED + KVM_SEV_SNP_PAGE_TYPE_SECRETS + KVM_SEV_SNP_PAGE_TYPE_CPUID + +See the SEV-SNP spec [snp-fw-abi]_ for further details on how each page type is +used/measured. + Device attribute API ====================arch/x86/include/uapi/asm/kvm.h +15
-0@@ -699,6 +699,7 @@ enum sev_cmd_id {/* SNP-specific commands */ KVM_SEV_SNP_LAUNCH_START =100 , +KVM_SEV_SNP_LAUNCH_UPDATE , KVM_SEV_NR_MAX, };@@ -830,6 +831,20 @@ struct kvm_sev_snp_launch_start {__u8 gosvw[16 ]; }; +/* Kept in sync with firmware values for simplicity. */ +#define KVM_SEV_SNP_PAGE_TYPE_NORMAL 0x1 +#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 gfn_start; +__u64 uaddr; +__u32 len; +__u8 type; +}; +#define KVM_X2APIC_API_USE_32BIT_IDS (1ULL << 0)#define KVM_X2APIC_API_DISABLE_BROADCAST_QUIRK (1ULL << 1)arch/x86/kvm/svm/sev.c +192
-0@@ -262,6 +262,35 @@ static void sev_decommission(unsigned int handle)sev_guest_decommission (&decommission,NULL ); } +static int snp_page_reclaim (u64 pfn) +{ +struct sev_data_snp_page_reclaim data = {0 }; +int err, rc; + + data.paddr =__sme_set (pfn <<PAGE_SHIFT ); + rc =sev_do_cmd (SEV_CMD_SNP_PAGE_RECLAIM , &data, &err); +if (WARN_ON_ONCE (rc)) { +/* +* This shouldn't happen under normal circumstances, but if the +* reclaim failed, then the page is no longer safe to use. +*/ +snp_leak_pages (pfn,1 ); + } + +return rc; +} + +static int host_rmp_make_shared (u64 pfn,enum pg_level level) +{ +int rc; + + rc =rmp_make_shared (pfn, level); +if (rc) +snp_leak_pages (pfn,page_level_size (level) >>PAGE_SHIFT ); + +return rc; +} +static void sev_unbind_asid (struct kvm *kvm,unsigned int handle) {struct sev_data_deactivate deactivate;@@ -2131,6 +2160,192 @@ static int snp_launch_start(struct kvm *kvm, struct kvm_sev_cmd *argp)return rc; } +struct sev_gmem_populate_args { +__u8 type; +int sev_fd; +int fw_error; +}; + +static int sev_gmem_post_populate (struct kvm *kvm,gfn_t gfn_start,kvm_pfn_t pfn, +void __user *src,int order,void *opaque) +{ +struct sev_gmem_populate_args *sev_populate_args = opaque; +struct kvm_sev_info *sev = &to_kvm_svm (kvm)->sev_info; +int n_private =0 , ret, i; +int npages = (1 << order); +gfn_t gfn; + +pr_debug ("%s: gfn_start %llx pfn_start %llx npages %d\n" , + __func__, gfn_start, pfn, npages); + +for (gfn = gfn_start, i =0 ; gfn < gfn_start + npages; gfn++, i++) { +struct sev_data_snp_launch_update fw_args = {0 }; +bool assigned; +void *vaddr; +int level; + +if (!kvm_mem_is_private (kvm, gfn)) { +pr_debug ("%s: Failed to ensure GFN 0x%llx has private memory attribute set\n" , + __func__, gfn); + ret = -EINVAL ; +break ; + } + + ret =snp_lookup_rmpentry ((u64 )pfn + i, &assigned, &level); +if (ret || assigned) { +pr_debug ("%s: Failed to ensure GFN 0x%llx RMP entry is initial shared state, ret: %d assigned: %d\n" , + __func__, gfn, ret, assigned); + ret = -EINVAL ; +break ; + } + + vaddr =kmap_local_pfn (pfn + i); + ret =copy_from_user (vaddr, src + i *PAGE_SIZE ,PAGE_SIZE ); +if (ret) { +pr_debug ("Failed to copy source page into GFN 0x%llx\n" , gfn); + goto out_unmap; + } + + ret =rmp_make_private (pfn + i, gfn <<PAGE_SHIFT ,PG_LEVEL_4K , +sev_get_asid (kvm), true); +if (ret) { +pr_debug ("%s: Failed to convert GFN 0x%llx to private, ret: %d\n" , + __func__, gfn, ret); + goto out_unmap; + } + + n_private++; + + fw_args.gctx_paddr =__psp_pa (sev->snp_context); + fw_args.address =__sme_set (pfn_to_hpa (pfn + i)); + fw_args.page_size =PG_LEVEL_TO_RMP (PG_LEVEL_4K ); + fw_args.page_type = sev_populate_args->type; + ret =__sev_issue_cmd (sev_populate_args->sev_fd,SEV_CMD_SNP_LAUNCH_UPDATE , + &fw_args, &sev_populate_args->fw_error); +if (ret) { +pr_debug ("%s: SEV-SNP launch update failed, ret: 0x%x, fw_error: 0x%x\n" , + __func__, ret, sev_populate_args->fw_error); + +if (snp_page_reclaim (pfn + i)) + goto out_unmap; + +/* +* When invalid CPUID function entries are detected, +* firmware writes the expected values into the page and +* leaves it unencrypted so it can be used for debugging +* and error-reporting. +* +* Copy this page back into the source buffer so +* userspace can use this information to provide +* information on which CPUID leaves/fields failed CPUID +* validation. +*/ +if (sev_populate_args->type ==KVM_SEV_SNP_PAGE_TYPE_CPUID && + sev_populate_args->fw_error ==SEV_RET_INVALID_PARAM ) { +host_rmp_make_shared (pfn + i,PG_LEVEL_4K ); + +if (copy_to_user (src + i *PAGE_SIZE , + vaddr,PAGE_SIZE )) +pr_debug ("Failed to write CPUID page back to userspace\n" ); + } + } + +out_unmap: +kunmap_local (vaddr); +if (ret) +break ; + } + +if (ret) { +pr_debug ("%s: exiting with error ret %d, undoing %d populated gmem pages.\n" , + __func__, ret, n_private); +for (i =0 ; i < n_private; i++) +host_rmp_make_shared (pfn + i,PG_LEVEL_4K ); + } + +return ret; +} + +static int snp_launch_update (struct kvm *kvm,struct kvm_sev_cmd *argp) +{ +struct kvm_sev_info *sev = &to_kvm_svm (kvm)->sev_info; +struct sev_gmem_populate_args sev_populate_args = {0 }; +struct kvm_sev_snp_launch_update params; +struct kvm_memory_slot *memslot; +unsigned int npages; +int ret =0 ; + +if (!sev_snp_guest (kvm) || !sev->snp_context) +return -EINVAL ; + +if (copy_from_user (¶ms,u64_to_user_ptr (argp->data),sizeof (params))) +return -EFAULT ; + +if (!IS_ALIGNED (params.len,PAGE_SIZE ) || + (params.type !=KVM_SEV_SNP_PAGE_TYPE_NORMAL && + params.type !=KVM_SEV_SNP_PAGE_TYPE_ZERO && + params.type !=KVM_SEV_SNP_PAGE_TYPE_UNMEASURED && + params.type !=KVM_SEV_SNP_PAGE_TYPE_SECRETS && + params.type !=KVM_SEV_SNP_PAGE_TYPE_CPUID )) +return -EINVAL ; + + npages = params.len /PAGE_SIZE ; + +pr_debug ("%s: GFN range 0x%llx-0x%llx type %d\n" , __func__, + params.gfn_start, params.gfn_start + npages, params.type); + +/* +* For each GFN that's being prepared as part of the initial guest +* state, the following pre-conditions are verified: +* +* 1) The backing memslot is a valid private memslot. +* 2) The GFN has been set to private via KVM_SET_MEMORY_ATTRIBUTES +* beforehand. +* 3) The PFN of the guest_memfd has not already been set to private +* in the RMP table. +* +* The KVM MMU relies on kvm->mmu_invalidate_seq to retry nested page +* faults if there's a race between a fault and an attribute update via +* KVM_SET_MEMORY_ATTRIBUTES, and a similar approach could be utilized +* here. However, kvm->slots_lock guards against both this as well as +* concurrent memslot updates occurring while these checks are being +* performed, so use that here to make it easier to reason about the +* initial expected state and better guard against unexpected +* situations. +*/ +mutex_lock (&kvm->slots_lock); + + memslot =gfn_to_memslot (kvm, params.gfn_start); +if (!kvm_slot_can_be_private (memslot)) { + ret = -EINVAL ; + goto out; + } + + sev_populate_args.sev_fd = argp->sev_fd; + sev_populate_args.type = params.type;
arch/x86/kvm/svm/sev.c static int snp_launch_start(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 2335
Quoted 10 lines
arch/x86/kvm/svm/sev.c +10
-0+ + ret =kvm_gmem_populate (kvm, params.gfn_start,u64_to_user_ptr (params.uaddr), + npages, sev_gmem_post_populate, &sev_populate_args); +if (ret <0 ) { + argp->error = sev_populate_args.fw_error; +pr_debug ("%s: kvm_gmem_populate failed, ret %d (fw_error %d)\n" , + __func__, ret, argp->error); + }else if (ret < npages) { + params.len = ret *PAGE_SIZE ; + ret = -EINTR ;
This probably should 1) update also gfn_start and uaddr 2) return 0 for consistency with the planned KVM_PRE_FAULT_MEMORY ioctl (aka KVM_MAP_MEMORY).
Paolo
Paolo Bonzini
maintainerRe: [PATCH v13 10/26] KVM: SEV: Add KVM_SEV_SNP_LAUNCH_UPDATE command
Quoted 10 lines · Paolo Bonzini
arch/x86/kvm/svm/sev.c static int snp_launch_start(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 2335
Quoted 9 lines
arch/x86/kvm/svm/sev.c +9
-0+ ret =kvm_gmem_populate (kvm, params.gfn_start,u64_to_user_ptr (params.uaddr), + npages, sev_gmem_post_populate, &sev_populate_args); +if (ret <0 ) { + argp->error = sev_populate_args.fw_error; +pr_debug ("%s: kvm_gmem_populate failed, ret %d (fw_error %d)\n" , + __func__, ret, argp->error); + }else if (ret < npages) { + params.len = ret *PAGE_SIZE ; + ret = -EINTR ;
Quoted 3 lines
This probably should 1) update also
gfn_startand uaddr 2) return 0 for consistency with the plannedKVM_PRE_FAULT_MEMORYioctl (akaKVM_MAP_MEMORY).
To be more precise, params.len should be set to the number of bytes *left*, i.e.
params.len -= ret * PAGE_SIZE;
params.gfn_start += ret * PAGE_SIZE;
if (params.type != KVM_SEV_SNP_PAGE_TYPE_ZERO)
params.uaddr += ret * PAGE_SIZE;Also this patch needs some other changes:
1) snp_launch_update() should have something like this:
src = params.type == KVM_SEV_SNP_PAGE_TYPE_ZERO ? NULL :u64_to_user_ptr(params.uaddr),;
so that then...
arch/x86/kvm/svm/sev.c static int snp_launch_start(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 2207
Quoted 6 lines
arch/x86/kvm/svm/sev.c +6
-0+ vaddr =kmap_local_pfn (pfn + i); + ret =copy_from_user (vaddr, src + i *PAGE_SIZE ,PAGE_SIZE ); +if (ret) { +pr_debug ("Failed to copy source page into GFN 0x%llx\n" , gfn); + goto out_unmap; + }
... the copy can be done only if src is non-NULL
2) the struct should have some more fields
Documentation/virt/kvm/x86/amd-memory-encryption.rst Returns: 0 on success, -negative on error · line 506
Quoted 5 lines
Documentation/virt/kvm/x86/amd-memory-encryption.rst +5
-0+ struct kvm_sev_snp_launch_update { + __u64 gfn_start; /* Guest page number to load/encrypt data into. */ + __u64 uaddr; /* Userspace address of data to be loaded/encrypted. */ + __u32 len; /* 4k-aligned length in bytes to copy into guest memory.*/ + __u8 type; /* The type of the guest pages being initialized. */
__u8 pad0;__u16 flags; // must be zero__u64 pad1[5];
with accompanying flags check in snp_launch_update().
If you think IMI can be implemented already (with a bit in flags) go ahead and do it.
Paolo
Re: [PATCH v13 10/26] KVM: SEV: Add KVM_SEV_SNP_LAUNCH_UPDATE command
Quoted 48 lines · Paolo Bonzini
Quoted 10 lines · Paolo Bonzini
arch/x86/kvm/svm/sev.c static int snp_launch_start(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 2335
Quoted 9 lines
arch/x86/kvm/svm/sev.c +9
-0+ ret =kvm_gmem_populate (kvm, params.gfn_start,u64_to_user_ptr (params.uaddr), + npages, sev_gmem_post_populate, &sev_populate_args); +if (ret <0 ) { + argp->error = sev_populate_args.fw_error; +pr_debug ("%s: kvm_gmem_populate failed, ret %d (fw_error %d)\n" , + __func__, ret, argp->error); + }else if (ret < npages) { + params.len = ret *PAGE_SIZE ; + ret = -EINTR ;Quoted 3 lines
This probably should 1) update also
gfn_startand uaddr 2) return 0 for consistency with the plannedKVM_PRE_FAULT_MEMORYioctl (akaKVM_MAP_MEMORY).To be more precise, params.len should be set to the number of bytes *left*, i.e.
params.len -= ret * PAGE_SIZE; params.gfn_start += ret * PAGE_SIZE; if (params.type != KVM_SEV_SNP_PAGE_TYPE_ZERO) params.uaddr += ret * PAGE_SIZE;Also this patch needs some other changes:
1)
snp_launch_update()should have something like this:src = params.type == KVM_SEV_SNP_PAGE_TYPE_ZERO ? NULL :
u64_to_user_ptr(params.uaddr),;so that then...
arch/x86/kvm/svm/sev.c static int snp_launch_start(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 2207
Quoted 6 lines
arch/x86/kvm/svm/sev.c +6
-0+ vaddr =kmap_local_pfn (pfn + i); + ret =copy_from_user (vaddr, src + i *PAGE_SIZE ,PAGE_SIZE ); +if (ret) { +pr_debug ("Failed to copy source page into GFN 0x%llx\n" , gfn); + goto out_unmap; + }... the copy can be done only if src is non-NULL
2) the struct should have some more fields
Documentation/virt/kvm/x86/amd-memory-encryption.rst Returns: 0 on success, -negative on error · line 506
Quoted 5 lines
Documentation/virt/kvm/x86/amd-memory-encryption.rst +5
-0+ struct kvm_sev_snp_launch_update { + __u64 gfn_start; /* Guest page number to load/encrypt data into. */ + __u64 uaddr; /* Userspace address of data to be loaded/encrypted. */ + __u32 len; /* 4k-aligned length in bytes to copy into guest memory.*/ + __u8 type; /* The type of the guest pages being initialized. */
Quoted 5 lines
__u8pad0;__u16flags; // must be zero__u64pad1[5];with accompanying flags check in
snp_launch_update().
Have these all addressed in v14, but I ended up making 'len' a __u64, so the final struct looks like this:
struct kvm_sev_snp_launch_update {
__u64 gfn_start;
__u64 uaddr;
__u64 len;
__u8 type;
__u8 pad0;
__u16 flags;
__u32 pad1;
__u64 pad2[4];
};Quoted 3 lines
If you think IMI can be implemented already (with a bit in flags) go ahead and do it.
Migration will also need related flags in LAUNCH_START, and depending on how we implement things, possibly in LAUNCH_FINISH. So for now I've left IMI out, but added similar 'flags' and padding to those structs as well so we have some flexibility with how we end up handling that.
-Mike
Quoted 3 lines
Paolo
PATCH v13 11/26 KVM: SEV: Add KVM_SEV_SNP_LAUNCH_FINISH command
In reply to Michael RothFrom: Brijesh Singh <[email protected]>
Add a KVM_SEV_SNP_LAUNCH_FINISH command to finalize the cryptographic launch digest which stores the measurement of the guest at launch time. Also extend the existing SNP firmware data structures to support disabling the use of Versioned Chip Endorsement Keys (VCEK) by guests as part of this command.
While finalizing the launch flow, the code also issues the LAUNCH_UPDATE SNP firmware commands to encrypt/measure the initial VMSA pages for each configured vCPU, which requires setting the RMP entries for those pages to private, so also add handling to clean up the RMP entries for these pages whening freeing vCPUs during shutdown.
--- .../virt/kvm/x86/amd-memory-encryption.rst | 26 ++++ arch/x86/include/uapi/asm/kvm.h | 15 +++ arch/x86/kvm/svm/sev.c | 123 ++++++++++++++++++ include/linux/psp-sev.h | 4 +- 4 files changed, 167 insertions(+), 1 deletion(-)
Documentation/virt/kvm/x86/amd-memory-encryption.rst +26 -0
@@ -517,6 +517,32 @@ where the allowed values for page_type are #define'd as:: See the SEV-SNP spec [snp-fw-abi]_ for further details on how each page type is used/measured. +20. KVM_SEV_SNP_LAUNCH_FINISH +----------------------------- + +After completion of the SNP guest launch flow, the KVM_SEV_SNP_LAUNCH_FINISH +command can be issued to make the guest ready for execution. + +Parameters (in): struct kvm_sev_snp_launch_finish + +Returns: 0 on success, -negative on error + +:: + + struct kvm_sev_snp_launch_finish { + __u64 id_block_uaddr; + __u64 id_auth_uaddr; + __u8 id_block_en; + __u8 auth_key_en; + __u8 vcek_disabled; + __u8 host_data[32]; + __u8 pad0[5]; + }; + + +See SNP_LAUNCH_FINISH in the SEV-SNP specification [snp-fw-abi]_ for further +details on the input parameters in ``struct kvm_sev_snp_launch_finish``. + Device attribute API ====================
arch/x86/include/uapi/asm/kvm.h +15 -0
@@ -700,6 +700,7 @@ enum sev_cmd_id {/* SNP-specific commands */ KVM_SEV_SNP_LAUNCH_START =100 ,KVM_SEV_SNP_LAUNCH_UPDATE , +KVM_SEV_SNP_LAUNCH_FINISH , KVM_SEV_NR_MAX, };
@@ -845,6 +846,20 @@ struct kvm_sev_snp_launch_update {__u8 type; }; +#define KVM_SEV_SNP_ID_BLOCK_SIZE 96 +#define KVM_SEV_SNP_ID_AUTH_SIZE 4096 +#define KVM_SEV_SNP_FINISH_DATA_SIZE 32 + +struct kvm_sev_snp_launch_finish { +__u64 id_block_uaddr; +__u64 id_auth_uaddr; +__u8 id_block_en; +__u8 auth_key_en; +__u8 vcek_disabled; +__u8 host_data[KVM_SEV_SNP_FINISH_DATA_SIZE ]; +__u8 pad0[5 ]; +}; +#define KVM_X2APIC_API_USE_32BIT_IDS (1ULL << 0)#define KVM_X2APIC_API_DISABLE_BROADCAST_QUIRK (1ULL << 1)
arch/x86/kvm/svm/sev.c +123 -0
152 lines not on this page · raw message
include/linux/psp-sev.h +3 -1
@@ -658,6 +658,7 @@ struct sev_data_snp_launch_update {* @id_auth_paddr: system physical address of ID block authentication structure * @id_block_en: indicates whether ID block is present * @auth_key_en: indicates whether author key is present in authentication structure +* @vcek_disabled: indicates whether use of VCEK is allowed for attestation reports * @rsvd: reserved * @host_data: host-supplied data for guest, not interpreted by firmware */
@@ -667,7 +668,8 @@ struct sev_data_snp_launch_finish {u64 id_auth_paddr; u8 id_block_en:1; u8 auth_key_en:1; - u64 rsvd:62; + u8 vcek_disabled:1; + u64 rsvd:61;u8 host_data[32 ]; } __packed;
Signature · 1 line
2.25.1
PATCH v13 12/26 KVM: SEV: Add support to handle GHCB GPA register VMGEXIT
In reply to Michael RothFrom: Brijesh Singh <[email protected]>
SEV-SNP guests are required to perform a GHCB GPA registration. Before using a GHCB GPA for a vCPU the first time, a guest must register the vCPU GHCB GPA. If hypervisor can work with the guest requested GPA then it must respond back with the same GPA otherwise return -1.
On VMEXIT, verify that the GHCB GPA matches with the registered value. If a mismatch is detected, then abort the guest.
--- arch/x86/include/asm/sev-common.h | 8 ++++++++ arch/x86/kvm/svm/sev.c | 27 +++++++++++++++++++++++++++ arch/x86/kvm/svm/svm.h | 7 +++++++ 3 files changed, 42 insertions(+)
arch/x86/include/asm/sev-common.h +8 -0
@@ -59,6 +59,14 @@#define GHCB_MSR_AP_RESET_HOLD_RESULT_POS 12#define GHCB_MSR_AP_RESET_HOLD_RESULT_MASK GENMASK_ULL(51, 0) +/* Preferred GHCB GPA Request */ +#define GHCB_MSR_PREF_GPA_REQ 0x010 +#define GHCB_MSR_GPA_VALUE_POS 12 +#define GHCB_MSR_GPA_VALUE_MASK GENMASK_ULL(51, 0) + +#define GHCB_MSR_PREF_GPA_RESP 0x011 +#define GHCB_MSR_PREF_GPA_NONE 0xfffffffffffff +/* GHCB GPA Register */ #define GHCB_MSR_REG_GPA_REQ 0x012#define GHCB_MSR_REG_GPA_REQ_VAL (v) \
arch/x86/kvm/svm/sev.c +27 -0
@@ -3532,6 +3532,26 @@ static int sev_handle_vmgexit_msr_protocol(struct vcpu_svm *svm)set_ghcb_msr_bits (svm,GHCB_MSR_HV_FT_RESP ,GHCB_MSR_INFO_MASK ,GHCB_MSR_INFO_POS );break ; +case GHCB_MSR_PREF_GPA_REQ : +set_ghcb_msr_bits (svm,GHCB_MSR_PREF_GPA_NONE ,GHCB_MSR_GPA_VALUE_MASK , +GHCB_MSR_GPA_VALUE_POS ); +set_ghcb_msr_bits (svm,GHCB_MSR_PREF_GPA_RESP ,GHCB_MSR_INFO_MASK , +GHCB_MSR_INFO_POS ); +break ; +case GHCB_MSR_REG_GPA_REQ : { +u64 gfn; + + gfn =get_ghcb_msr_bits (svm,GHCB_MSR_GPA_VALUE_MASK , +GHCB_MSR_GPA_VALUE_POS ); + + svm->sev_es.ghcb_registered_gpa =gfn_to_gpa (gfn); + +set_ghcb_msr_bits (svm, gfn,GHCB_MSR_GPA_VALUE_MASK , +GHCB_MSR_GPA_VALUE_POS ); +set_ghcb_msr_bits (svm,GHCB_MSR_REG_GPA_RESP ,GHCB_MSR_INFO_MASK , +GHCB_MSR_INFO_POS ); +break ; + }case GHCB_MSR_TERM_REQ : {u64 reason_set, reason_code;
@@ -3595,6 +3615,13 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu)trace_kvm_vmgexit_enter (vcpu->vcpu_id, svm->sev_es.ghcb);sev_es_sync_from_ghcb (svm); + +/* SEV-SNP guest requires that the GHCB GPA must be registered */ +if (sev_snp_guest (svm->vcpu.kvm) && !ghcb_gpa_is_registered (svm, ghcb_gpa)) { +vcpu_unimpl (&svm->vcpu,"vmgexit: GHCB GPA [%#llx] is not registered.\n" , ghcb_gpa); +return -EINVAL ; + } + ret =sev_es_validate_vmgexit (svm);if (ret)return ret;
arch/x86/kvm/svm/svm.h +7 -0
@@ -208,6 +208,8 @@ struct vcpu_sev_es_state {u32 ghcb_sa_len;bool ghcb_sa_sync;bool ghcb_sa_free; + +u64 ghcb_registered_gpa; };struct vcpu_svm {
@@ -361,6 +363,11 @@ static __always_inline bool sev_snp_guest(struct kvm *kvm)#endif } +static inline bool ghcb_gpa_is_registered (struct vcpu_svm *svm,u64 val) +{ +return svm->sev_es.ghcb_registered_gpa == val; +} +static inline void vmcb_mark_all_dirty (struct vmcb *vmcb) { vmcb->control.clean =0 ;
Signature · 1 line
2.25.1
PATCH v13 13/26 KVM: SEV: Add support to handle MSR based Page State Change VMGEXIT
In reply to Michael RothFrom: Brijesh Singh <[email protected]>
SEV-SNP VMs can ask the hypervisor to change the page state in the RMP table to be private or shared using the Page State Change MSR protocol as defined in the GHCB specification.
When using gmem, private/shared memory is allocated through separate pools, and KVM relies on userspace issuing a KVM_SET_MEMORY_ATTRIBUTES KVM ioctl to tell the KVM MMU whether or not a particular GFN should be backed by private memory or not.
Forward these page state change requests to userspace so that it can issue the expected KVM ioctls. The KVM MMU will handle updating the RMP entries when it is ready to map a private page into a guest.
Define a new KVM_EXIT_VMGEXIT for exits of this type, and structure it so that it can be extended for other cases where VMGEXITs need some level of handling in userspace.
--- Documentation/virt/kvm/api.rst | 33 +++++++++++++++++++++++++++++++ arch/x86/include/asm/sev-common.h | 6 ++++++ arch/x86/kvm/svm/sev.c | 33 +++++++++++++++++++++++++++++++ include/uapi/linux/kvm.h | 17 ++++++++++++++++ 4 files changed, 89 insertions(+)
Documentation/virt/kvm/api.rst +33 -0
@@ -7060,6 +7060,39 @@ Please note that the kernel is allowed to use the kvm_run structure as the primary storage for certain register types. Therefore, the kernel may use the values in kvm_run even if the corresponding bit in kvm_dirty_regs is not set. +:: + + /* KVM_EXIT_VMGEXIT */ + struct kvm_user_vmgexit { + #define KVM_USER_VMGEXIT_PSC_MSR 1 + __u32 type; /* KVM_USER_VMGEXIT_* type */ + union { + struct { + __u64 gpa; + #define KVM_USER_VMGEXIT_PSC_MSR_OP_PRIVATE 1 + #define KVM_USER_VMGEXIT_PSC_MSR_OP_SHARED 2 + __u8 op; + __u32 ret; + } psc_msr; + }; + }; + +If exit reason is KVM_EXIT_VMGEXIT then it indicates that an SEV-SNP guest +has issued a VMGEXIT instruction (as documented by the AMD Architecture +Programmer's Manual (APM)) to the hypervisor that needs to be serviced by +userspace. These are generally handled by the host kernel, but in some +cases some aspects handling a VMGEXIT are handled by userspace. + +A kvm_user_vmgexit structure is defined to encapsulate the data to be +sent to or returned by userspace. The type field defines the specific type +of exit that needs to be serviced, and that type is used as a discriminator +to determine which union type should be used for input/output. + +For the KVM_USER_VMGEXIT_PSC_MSR type, the psc_msr union type is used. The +kernel will supply the 'gpa' and 'op' fields, and userspace is expected to +update the private/shared state of the GPA using the corresponding +KVM_SET_MEMORY_ATTRIBUTES ioctl. The 'ret' field is to be set to 0 by +userpace on success, or some non-zero value on failure. 6. Capabilities that can be enabled on vCPUs ============================================
arch/x86/include/asm/sev-common.h +6 -0
@@ -101,11 +101,17 @@ enum psc_op {/* GHCBData[11:0] */ \ GHCB_MSR_PSC_REQ) +#define GHCB_MSR_PSC_REQ_TO_GFN (msr) (((msr) & GENMASK_ULL(51, 12)) >> 12) +#define GHCB_MSR_PSC_REQ_TO_OP (msr) (((msr) & GENMASK_ULL(55, 52)) >> 52) +#define GHCB_MSR_PSC_RESP 0x015#define GHCB_MSR_PSC_RESP_VAL (val) \/* GHCBData[63:32] */ \ (((u64)(val) & GENMASK_ULL(63, 32)) >> 32) +/* Set highest bit as a generic error response */ +#define GHCB_MSR_PSC_RESP_ERROR (BIT_ULL(63) | GHCB_MSR_PSC_RESP) +/* GHCB Hypervisor Feature Request/Response */ #define GHCB_MSR_HV_FT_REQ 0x080#define GHCB_MSR_HV_FT_RESP 0x081
arch/x86/kvm/svm/sev.c +33 -0
@@ -3454,6 +3454,36 @@ static void set_ghcb_msr(struct vcpu_svm *svm, u64 value) svm->vmcb->control.ghcb_gpa = value; } +static int snp_complete_psc_msr (struct kvm_vcpu *vcpu) +{ +struct vcpu_svm *svm =to_svm (vcpu); +u64 vmm_ret = vcpu->run->vmgexit.psc_msr.ret; + +set_ghcb_msr (svm, (vmm_ret <<32 ) |GHCB_MSR_PSC_RESP ); + +return 1 ;/* resume guest */ +} + +static int snp_begin_psc_msr (struct kvm_vcpu *vcpu,u64 ghcb_msr) +{ +u64 gpa =gfn_to_gpa (GHCB_MSR_PSC_REQ_TO_GFN (ghcb_msr)); +u8 op =GHCB_MSR_PSC_REQ_TO_OP (ghcb_msr); +struct vcpu_svm *svm =to_svm (vcpu); + +if (op !=SNP_PAGE_STATE_PRIVATE && op !=SNP_PAGE_STATE_SHARED ) { +set_ghcb_msr (svm,GHCB_MSR_PSC_RESP_ERROR ); +return 1 ;/* resume guest */ + } + + vcpu->run->exit_reason =KVM_EXIT_VMGEXIT ; + vcpu->run->vmgexit.type =KVM_USER_VMGEXIT_PSC_MSR ; + vcpu->run->vmgexit.psc_msr.gpa = gpa; + vcpu->run->vmgexit.psc_msr.op = op; + vcpu->arch.complete_userspace_io = snp_complete_psc_msr; + +return 0 ;/* forward request to userspace */ +} +static int sev_handle_vmgexit_msr_protocol (struct vcpu_svm *svm) {struct vmcb_control_area *control = &svm->vmcb->control;
@@ -3552,6 +3582,9 @@ static int sev_handle_vmgexit_msr_protocol(struct vcpu_svm *svm) GHCB_MSR_INFO_POS);break ; } +case GHCB_MSR_PSC_REQ : + ret =snp_begin_psc_msr (vcpu, control->ghcb_gpa); +break ;case GHCB_MSR_TERM_REQ : {u64 reason_set, reason_code;
include/uapi/linux/kvm.h +17 -0
@@ -135,6 +135,20 @@ struct kvm_xen_exit { } u; }; +struct kvm_user_vmgexit { +#define KVM_USER_VMGEXIT_PSC_MSR 1 +__u32 type;/* KVM_USER_VMGEXIT_* type */ +union { +struct { +__u64 gpa; +#define KVM_USER_VMGEXIT_PSC_MSR_OP_PRIVATE 1 +#define KVM_USER_VMGEXIT_PSC_MSR_OP_SHARED 2 +__u8 op; +__u32 ret; + } psc_msr; + }; +}; +#define KVM_S390_GET_SKEYS_NONE 1#define KVM_S390_SKEYS_MAX 1048576
@@ -178,6 +192,7 @@ struct kvm_xen_exit {#define KVM_EXIT_NOTIFY 37#define KVM_EXIT_LOONGARCH_IOCSR 38#define KVM_EXIT_MEMORY_FAULT 39 +#define KVM_EXIT_VMGEXIT 40/* For KVM_EXIT_INTERNAL_ERROR */ /* Emulate instruction failed. */
@@ -433,6 +448,8 @@ struct kvm_run {__u64 gpa;__u64 size; } memory_fault; +/* KVM_EXIT_VMGEXIT */ +struct kvm_user_vmgexit vmgexit;/* Fix the size of the union. */ char padding[256 ]; };
Signature · 1 line
2.25.1
PATCH v13 14/26 KVM: SEV: Add support to handle Page State Change VMGEXIT
In reply to Michael RothFrom: Brijesh Singh <[email protected]>
SEV-SNP VMs can ask the hypervisor to change the page state in the RMP table to be private or shared using the Page State Change NAE event as defined in the GHCB specification version 2.
Forward these requests to userspace as KVM_EXIT_VMGEXITs, similar to how it is done for requests that don't use a GHCB page.
--- Documentation/virt/kvm/api.rst | 14 ++++++++++++++ arch/x86/kvm/svm/sev.c | 16 ++++++++++++++++ include/uapi/linux/kvm.h | 5 +++++ 3 files changed, 35 insertions(+)
Documentation/virt/kvm/api.rst +14 -0
@@ -7065,6 +7065,7 @@ values in kvm_run even if the corresponding bit in kvm_dirty_regs is not set. /* KVM_EXIT_VMGEXIT */ struct kvm_user_vmgexit { #define KVM_USER_VMGEXIT_PSC_MSR 1 + #define KVM_USER_VMGEXIT_PSC 2 __u32 type; /* KVM_USER_VMGEXIT_* type */ union { struct {
@@ -7074,9 +7075,14 @@ values in kvm_run even if the corresponding bit in kvm_dirty_regs is not set. __u8 op; __u32 ret; } psc_msr; + struct { + __u64 shared_gpa; + __u64 ret; + } psc; }; }; + If exit reason is KVM_EXIT_VMGEXIT then it indicates that an SEV-SNP guest has issued a VMGEXIT instruction (as documented by the AMD Architecture Programmer's Manual (APM)) to the hypervisor that needs to be serviced by
@@ -7094,6 +7100,14 @@ update the private/shared state of the GPA using the corresponding KVM_SET_MEMORY_ATTRIBUTES ioctl. The 'ret' field is to be set to 0 by userpace on success, or some non-zero value on failure. +For the KVM_USER_VMGEXIT_PSC type, the psc union type is used. The kernel +will supply the GPA of the Page State Structure defined in the GHCB spec. +Userspace will process this structure as defined by the GHCB, and issue +KVM_SET_MEMORY_ATTRIBUTES ioctls to set the GPAs therein to the expected +private/shared state. Userspace will return a value in 'ret' that is in +agreement with the GHCB-defined return values that the guest will expect +in the SW_EXITINFO2 field of the GHCB in response to these requests. + 6. Capabilities that can be enabled on vCPUs ============================================
arch/x86/kvm/svm/sev.c +16 -0
@@ -3266,6 +3266,7 @@ static int sev_es_validate_vmgexit(struct vcpu_svm *svm)case SVM_VMGEXIT_AP_JUMP_TABLE :case SVM_VMGEXIT_UNSUPPORTED_EVENT :case SVM_VMGEXIT_HV_FEATURES : +case SVM_VMGEXIT_PSC :break ;default : reason =GHCB_ERR_INVALID_EVENT ;
@@ -3484,6 +3485,15 @@ static int snp_begin_psc_msr(struct kvm_vcpu *vcpu, u64 ghcb_msr)return 0 ;/* forward request to userspace */ } +static int snp_complete_psc (struct kvm_vcpu *vcpu) +{ +struct vcpu_svm *svm =to_svm (vcpu); + +ghcb_set_sw_exit_info_2 (svm->sev_es.ghcb, vcpu->run->vmgexit.psc.ret); + +return 1 ;/* resume guest */ +} +static int sev_handle_vmgexit_msr_protocol (struct vcpu_svm *svm) {struct vmcb_control_area *control = &svm->vmcb->control;
@@ -3721,6 +3731,12 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu) ret =1 ;break ; +case SVM_VMGEXIT_PSC : + vcpu->run->exit_reason =KVM_EXIT_VMGEXIT ; + vcpu->run->vmgexit.type =KVM_USER_VMGEXIT_PSC ; + vcpu->run->vmgexit.psc.shared_gpa = svm->sev_es.sw_scratch; + vcpu->arch.complete_userspace_io = snp_complete_psc; +break ;case SVM_VMGEXIT_UNSUPPORTED_EVENT : vcpu_unimpl(vcpu, "vmgexit: unsupported event - exit_info_1=%#llx, exit_info_2=%#llx\n",
include/uapi/linux/kvm.h +5 -0
@@ -137,6 +137,7 @@ struct kvm_xen_exit { struct kvm_user_vmgexit { #define KVM_USER_VMGEXIT_PSC_MSR 1 +#define KVM_USER_VMGEXIT_PSC 2 __u32 type; /* KVM_USER_VMGEXIT_* type */ union { struct {
@@ -146,6 +147,10 @@ struct kvm_user_vmgexit {__u8 op;__u32 ret; } psc_msr; +struct { +__u64 shared_gpa; +__u64 ret; + } psc; }; };
Signature · 1 line
2.25.1
PATCH v13 15/26 KVM: SEV: Add support to handle RMP nested page faults
In reply to Michael RothFrom: Brijesh Singh <[email protected]>
When SEV-SNP is enabled in the guest, the hardware places restrictions on all memory accesses based on the contents of the RMP table. When hardware encounters RMP check failure caused by the guest memory access it raises the #NPF. The error code contains additional information on the access type. See the APM volume 2 for additional information.
When using gmem, RMP faults resulting from mismatches between the state in the RMP table vs. what the guest expects via its page table result in KVM_EXIT_MEMORY_FAULTs being forwarded to userspace to handle. This means the only expected case that needs to be handled in the kernel is when the page size of the entry in the RMP table is larger than the mapping in the nested page table, in which case a PSMASH instruction needs to be issued to split the large RMP entry into individual 4K entries so that subsequent accesses can succeed.
--- arch/x86/include/asm/kvm_host.h | 1 + arch/x86/include/asm/sev.h | 3 + arch/x86/kvm/mmu.h | 2 - arch/x86/kvm/mmu/mmu.c | 1 + arch/x86/kvm/svm/sev.c | 109 ++++++++++++++++++++++++++++++++ arch/x86/kvm/svm/svm.c | 21 ++++-- arch/x86/kvm/svm/svm.h | 3 + arch/x86/kvm/trace.h | 31 +++++++++ arch/x86/kvm/x86.c | 1 + 9 files changed, 166 insertions(+), 6 deletions(-)
arch/x86/include/asm/kvm_host.h +1 -0
@@ -1940,6 +1940,7 @@ void kvm_mmu_slot_leaf_clear_dirty(struct kvm *kvm, const struct kvm_memory_slot *memslot);void kvm_mmu_invalidate_mmio_sptes (struct kvm *kvm,u64 gen);void kvm_mmu_change_mmu_pages (struct kvm *kvm,unsigned long kvm_nr_mmu_pages); +void kvm_zap_gfn_range (struct kvm *kvm,gfn_t gfn_start,gfn_t gfn_end);int load_pdptrs (struct kvm_vcpu *vcpu,unsigned long cr3);
arch/x86/include/asm/sev.h +3 -0
@@ -91,6 +91,9 @@ extern bool handle_vc_boot_ghcb(struct pt_regs *regs);/* RMUPDATE detected 4K page and 2MB page overlap. */ #define RMPUPDATE_FAIL_OVERLAP 4 +/* PSMASH failed due to concurrent access by another CPU */ +#define PSMASH_FAIL_INUSE 3 +/* RMP page size */ #define RMP_PG_SIZE_4K 0#define RMP_PG_SIZE_2M 1
arch/x86/kvm/mmu.h +0 -2
@@ -252,8 +252,6 @@ static inline bool kvm_mmu_honors_guest_mtrrs(struct kvm *kvm)return __kvm_mmu_honors_guest_mtrrs (kvm_arch_has_noncoherent_dma (kvm)); } -void kvm_zap_gfn_range (struct kvm *kvm,gfn_t gfn_start,gfn_t gfn_end); -int kvm_arch_write_log_dirty (struct kvm_vcpu *vcpu);int kvm_mmu_post_init_vm (struct kvm *kvm);
arch/x86/kvm/mmu/mmu.c +1 -0
@@ -6744,6 +6744,7 @@ static bool kvm_mmu_zap_collapsible_spte(struct kvm *kvm,return need_tlb_flush; } +EXPORT_SYMBOL_GPL (kvm_zap_gfn_range);static void kvm_rmap_zap_collapsible_sptes (struct kvm *kvm,const struct kvm_memory_slot *slot)
arch/x86/kvm/svm/sev.c +109 -0
118 lines not on this page · raw message
arch/x86/kvm/svm/svm.c +17 -4
@@ -2043,6 +2043,7 @@ static int pf_interception(struct kvm_vcpu *vcpu)static int npf_interception (struct kvm_vcpu *vcpu) {struct vcpu_svm *svm =to_svm (vcpu); +int rc;u64 fault_address = svm->vmcb->control.exit_info_2;u64 error_code = svm->vmcb->control.exit_info_1;
@@ -2057,10 +2058,22 @@ static int npf_interception(struct kvm_vcpu *vcpu) error_code &= ~PFERR_SYNTHETIC_MASK ;trace_kvm_page_fault (vcpu, fault_address, error_code); -return kvm_mmu_page_fault (vcpu, fault_address, error_code, -static_cpu_has (X86_FEATURE_DECODEASSISTS ) ? - svm->vmcb->control.insn_bytes :NULL , - svm->vmcb->control.insn_len); + rc =kvm_mmu_page_fault (vcpu, fault_address, error_code, +static_cpu_has (X86_FEATURE_DECODEASSISTS ) ? + svm->vmcb->control.insn_bytes :NULL , + svm->vmcb->control.insn_len); + +/* +* rc == 0 indicates a userspace exit is needed to handle page +* transitions, so do that first before updating the RMP table. +*/ +if (error_code &PFERR_GUEST_RMP_MASK ) { +if (rc ==0 ) +return rc; +sev_handle_rmp_fault (vcpu, fault_address, error_code); + } + +return rc; }static int db_interception (struct kvm_vcpu *vcpu)
arch/x86/kvm/svm/svm.h +3 -0
@@ -722,6 +722,7 @@ void sev_hardware_unsetup(void);int sev_cpu_init (struct svm_cpu_data *sd);int sev_dev_get_attr (u32 group,u64 attr,u64 *val);extern unsigned int max_sev_asid; +void sev_handle_rmp_fault (struct kvm_vcpu *vcpu,gpa_t gpa,u64 error_code);#else static inline struct page *snp_safe_alloc_page (struct kvm_vcpu *vcpu) {return alloc_page (GFP_KERNEL_ACCOUNT | __GFP_ZERO);
@@ -735,6 +736,8 @@ static inline void sev_hardware_unsetup(void) {}static inline int sev_cpu_init (struct svm_cpu_data *sd) {return 0 ; }static inline int sev_dev_get_attr (u32 group,u64 attr,u64 *val) {return -ENXIO ; }#define max_sev_asid 0 +static inline void sev_handle_rmp_fault (struct kvm_vcpu *vcpu,gpa_t gpa,u64 error_code) {} +#endif /* vmenter.S */
arch/x86/kvm/trace.h +31 -0
@@ -1834,6 +1834,37 @@ TRACE_EVENT(kvm_vmgexit_msr_protocol_exit, __entry->vcpu_id, __entry->ghcb_gpa, __entry->result) ); +/* +* Tracepoint for #NPFs due to RMP faults. +*/ +TRACE_EVENT (kvm_rmp_fault, + TP_PROTO(struct kvm_vcpu *vcpu, u64 gpa, u64 pfn, u64 error_code, + int rmp_level, int psmash_ret), +TP_ARGS (vcpu, gpa, pfn, error_code, rmp_level, psmash_ret), + +TP_STRUCT__entry ( + __field(unsigned int, vcpu_id) + __field(u64, gpa) + __field(u64, pfn) + __field(u64, error_code) + __field(int, rmp_level) +__field (int, psmash_ret) + ), + +TP_fast_assign ( + __entry->vcpu_id = vcpu->vcpu_id; + __entry->gpa = gpa; + __entry->pfn = pfn; + __entry->error_code = error_code; + __entry->rmp_level = rmp_level; + __entry->psmash_ret = psmash_ret; + ), + +TP_printk ("vcpu %u gpa %016llx pfn 0x%llx error_code 0x%llx rmp_level %d psmash_ret %d" , + __entry->vcpu_id, __entry->gpa, __entry->pfn, + __entry->error_code, __entry->rmp_level, __entry->psmash_ret) +); +#endif /* _TRACE_KVM_H */ #undef TRACE_INCLUDE_PATH
arch/x86/kvm/x86.c +1 -0
@@ -13996,6 +13996,7 @@ EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_vmgexit_enter);EXPORT_TRACEPOINT_SYMBOL_GPL (kvm_vmgexit_exit);EXPORT_TRACEPOINT_SYMBOL_GPL (kvm_vmgexit_msr_protocol_enter);EXPORT_TRACEPOINT_SYMBOL_GPL (kvm_vmgexit_msr_protocol_exit); +EXPORT_TRACEPOINT_SYMBOL_GPL (kvm_rmp_fault);static int __init kvm_x86_init (void ) {
Signature · 1 line
2.25.1
PATCH v13 16/26 KVM: SEV: Support SEV-SNP AP Creation NAE event
In reply to Michael RothFrom: Tom Lendacky <[email protected]>
Add support for the SEV-SNP AP Creation NAE event. This allows SEV-SNP guests to alter the register state of the APs on their own. This allows the guest a way of simulating INIT-SIPI.
A new event, KVM_REQ_UPDATE_PROTECTED_GUEST_STATE, is created and used so as to avoid updating the VMSA pointer while the vCPU is running.
For CREATE
The guest supplies the GPA of the VMSA to be used for the vCPU with the specified APIC ID. The GPA is saved in the svm struct of the target vCPU, the KVM_REQ_UPDATE_PROTECTED_GUEST_STATE event is added to the vCPU and then the vCPU is kicked.
For CREATE_ON_INIT:
The guest supplies the GPA of the VMSA to be used for the vCPU with the specified APIC ID the next time an INIT is performed. The GPA is saved in the svm struct of the target vCPU.
For DESTROY:
The guest indicates it wishes to stop the vCPU. The GPA is cleared from the svm struct, the KVM_REQ_UPDATE_PROTECTED_GUEST_STATE event is added to vCPU and then the vCPU is kicked.
The KVM_REQ_UPDATE_PROTECTED_GUEST_STATE event handler will be invoked as a result of the event or as a result of an INIT. If a new VMSA is to be installed, the VMSA guest page is set as the VMSA in the vCPU VMCB and the vCPU state is set to KVM_MP_STATE_RUNNABLE. If a new VMSA is not to be installed, the VMSA is cleared in the vCPU VMCB and the vCPU state is set to KVM_MP_STATE_HALTED to prevent it from being run.
--- arch/x86/include/asm/kvm_host.h | 1 + arch/x86/include/asm/svm.h | 6 + arch/x86/kvm/svm/sev.c | 229 +++++++++++++++++++++++++++++++- arch/x86/kvm/svm/svm.c | 11 +- arch/x86/kvm/svm/svm.h | 9 ++ arch/x86/kvm/x86.c | 11 ++ 6 files changed, 264 insertions(+), 3 deletions(-)
arch/x86/include/asm/kvm_host.h +1 -0
@@ -121,6 +121,7 @@ KVM_ARCH_REQ_FLAGS(31, KVM_REQUEST_WAIT | KVM_REQUEST_NO_WAKEUP)#define KVM_REQ_HV_TLB_FLUSH \ KVM_ARCH_REQ_FLAGS(32, KVM_REQUEST_WAIT | KVM_REQUEST_NO_WAKEUP) +#define KVM_REQ_UPDATE_PROTECTED_GUEST_STATE KVM_ARCH_REQ(34)#define CR0_RESERVED_BITS \ (~(unsigned long)(X86_CR0_PE | X86_CR0_MP | X86_CR0_EM | X86_CR0_TS \
arch/x86/include/asm/svm.h +6 -0
@@ -286,8 +286,14 @@ static_assert((X2AVIC_MAX_PHYSICAL_ID & AVIC_PHYSICAL_MAX_INDEX_MASK) == X2AVIC_#define AVIC_HPA_MASK ~((0xFFFULL << 52) | 0xFFF)#define SVM_SEV_FEAT_SNP_ACTIVE BIT(0) +#define SVM_SEV_FEAT_RESTRICTED_INJECTION BIT(3) +#define SVM_SEV_FEAT_ALTERNATE_INJECTION BIT(4)#define SVM_SEV_FEAT_DEBUG_SWAP BIT(5) +#define SVM_SEV_FEAT_INT_INJ_MODES \ + (SVM_SEV_FEAT_RESTRICTED_INJECTION | \ + SVM_SEV_FEAT_ALTERNATE_INJECTION) +struct vmcb_seg {u16 selector;u16 attrib;
arch/x86/kvm/svm/sev.c +227 -2
271 lines not on this page · raw message
arch/x86/kvm/svm/svm.c +10 -1
@@ -1398,6 +1398,9 @@ static void svm_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event) svm->spec_ctrl =0 ; svm->virt_spec_ctrl =0 ; +if (init_event) +sev_snp_init_protected_guest_state (vcpu); +init_vmcb (vcpu);if (!init_event)
@@ -4939,6 +4942,12 @@ static void *svm_alloc_apic_backing_page(struct kvm_vcpu *vcpu)return page_address (page); } +static void svm_vcpu_unblocking (struct kvm_vcpu *vcpu) +{ +sev_vcpu_unblocking (vcpu); +avic_vcpu_unblocking (vcpu); +} +static struct kvm_x86_ops svm_x86_ops__initdata = { .name =KBUILD_MODNAME ,
@@ -4961,7 +4970,7 @@ static struct kvm_x86_ops svm_x86_ops __initdata = { .vcpu_load = svm_vcpu_load, .vcpu_put = svm_vcpu_put, .vcpu_blocking = avic_vcpu_blocking, - .vcpu_unblocking = avic_vcpu_unblocking, + .vcpu_unblocking = svm_vcpu_unblocking, .update_exception_bitmap = svm_update_exception_bitmap, .get_msr_feature = svm_get_msr_feature,
arch/x86/kvm/svm/svm.h +9 -0
@@ -210,6 +210,11 @@ struct vcpu_sev_es_state {bool ghcb_sa_free;u64 ghcb_registered_gpa; + +struct mutex snp_vmsa_mutex;/* Used to handle concurrent updates of VMSA. */ +gpa_t snp_vmsa_gpa; +bool snp_ap_waiting_for_reset; +bool snp_has_guest_vmsa; };struct vcpu_svm {
@@ -723,6 +728,8 @@ int sev_cpu_init(struct svm_cpu_data *sd);int sev_dev_get_attr (u32 group,u64 attr,u64 *val);extern unsigned int max_sev_asid;void sev_handle_rmp_fault (struct kvm_vcpu *vcpu,gpa_t gpa,u64 error_code); +void sev_vcpu_unblocking (struct kvm_vcpu *vcpu); +void sev_snp_init_protected_guest_state (struct kvm_vcpu *vcpu);#else static inline struct page *snp_safe_alloc_page (struct kvm_vcpu *vcpu) {return alloc_page (GFP_KERNEL_ACCOUNT | __GFP_ZERO);
@@ -737,6 +744,8 @@ static inline int sev_cpu_init(struct svm_cpu_data *sd) { return 0; }static inline int sev_dev_get_attr (u32 group,u64 attr,u64 *val) {return -ENXIO ; }#define max_sev_asid 0static inline void sev_handle_rmp_fault (struct kvm_vcpu *vcpu,gpa_t gpa,u64 error_code) {} +static inline void sev_vcpu_unblocking (struct kvm_vcpu *vcpu) {} +static inline void sev_snp_init_protected_guest_state (struct kvm_vcpu *vcpu) {}#endif
arch/x86/kvm/x86.c +11 -0
@@ -10938,6 +10938,14 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)if (kvm_check_request (KVM_REQ_UPDATE_CPU_DIRTY_LOGGING , vcpu))static_call (kvm_x86_update_cpu_dirty_logging)(vcpu); + +if (kvm_check_request (KVM_REQ_UPDATE_PROTECTED_GUEST_STATE , vcpu)) { +kvm_vcpu_reset (vcpu, true); +if (vcpu->arch.mp_state !=KVM_MP_STATE_RUNNABLE ) { + r =1 ; + goto out; + } + } } if (kvm_check_request(KVM_REQ_EVENT, vcpu) || req_int_win ||
@@ -13145,6 +13153,9 @@ static inline bool kvm_vcpu_has_events(struct kvm_vcpu *vcpu)if (kvm_test_request (KVM_REQ_PMI , vcpu))return true; +if (kvm_test_request (KVM_REQ_UPDATE_PROTECTED_GUEST_STATE , vcpu)) +return true; +if (kvm_arch_interrupt_allowed (vcpu) && (kvm_cpu_has_interrupt (vcpu) ||kvm_guest_apic_has_interrupt (vcpu)))
Signature · 1 line
2.25.1
Paolo Bonzini
maintainerRe: [PATCH v13 16/26] KVM: SEV: Support SEV-SNP AP Creation NAE event
arch/x86/kvm/svm/sev.c static void sev_es_init_vmcb(struct vcpu_svm *svm) · line 4067
Quoted 8 lines · Michael Roth
* the VMSA will be NULL if this vCPU is the destination for intrahost
* migration, and will be copied later.
*/arch/x86/kvm/svm/sev.c +1
-1-if (svm->sev_es.vmsa) +if (!svm->sev_es.snp_has_guest_vmsa) svm->vmcb->control.vmsa_pa =__pa (svm->sev_es.vmsa);/* Can't intercept CR register access, HV can't modify CR registers */
This needs to be svm->sev_es.vmsa && ... (see existing comment above the "if").
Paolo
PATCH v13 17/26 KVM: SEV: Add support for GHCB-based termination requests
In reply to Michael RothGHCB version 2 adds support for a GHCB-based termination request that a guest can issue when it reaches an error state and wishes to inform the hypervisor that it should be terminated. Implement support for that similarly to GHCB MSR-based termination requests that are already available to SEV-ES guests via earlier versions of the GHCB protocol.
See 'Termination Request' in the 'Invoking VMGEXIT' section of the GHCB specification for more details.
--- arch/x86/kvm/svm/sev.c | 9 +++++++++ 1 file changed, 9 insertions(+)
arch/x86/kvm/svm/sev.c +9 -0
@@ -3272,6 +3272,7 @@ static int sev_es_validate_vmgexit(struct vcpu_svm *svm)case SVM_VMGEXIT_UNSUPPORTED_EVENT :case SVM_VMGEXIT_HV_FEATURES :case SVM_VMGEXIT_PSC : +case SVM_VMGEXIT_TERM_REQUEST :break ;default : reason =GHCB_ERR_INVALID_EVENT ;
@@ -3967,6 +3968,14 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu) ret =1 ;break ; +case SVM_VMGEXIT_TERM_REQUEST : +pr_info ("SEV-ES guest requested termination: reason %#llx info %#llx\n" , + control->exit_info_1, control->exit_info_2); + vcpu->run->exit_reason =KVM_EXIT_SYSTEM_EVENT ; + vcpu->run->system_event.type =KVM_SYSTEM_EVENT_SEV_TERM ; + vcpu->run->system_event.ndata =1 ; + vcpu->run->system_event.data[0 ] = control->ghcb_gpa; +break ;case SVM_VMGEXIT_UNSUPPORTED_EVENT : vcpu_unimpl(vcpu, "vmgexit: unsupported event - exit_info_1=%#llx, exit_info_2=%#llx\n",
Signature · 1 line
2.25.1
PATCH v13 18/26 KVM: SEV: Implement gmem hook for initializing private pages
In reply to Michael RothThis will handle the RMP table updates needed to put a page into a private state before mapping it into an SEV-SNP guest.
--- arch/x86/kvm/Kconfig | 1 + arch/x86/kvm/svm/sev.c | 98 ++++++++++++++++++++++++++++++++++++++++++ arch/x86/kvm/svm/svm.c | 2 + arch/x86/kvm/svm/svm.h | 5 +++ arch/x86/kvm/x86.c | 5 +++ virt/kvm/guest_memfd.c | 4 +- 6 files changed, 113 insertions(+), 2 deletions(-)
arch/x86/kvm/Kconfig +1 -0
@@ -137,6 +137,7 @@ config KVM_AMD_SEV depends on CRYPTO_DEV_SP_PSP && !(KVM_AMD=y && CRYPTO_DEV_CCP_DD=m) select ARCH_HAS_CC_PLATFORM select KVM_GENERIC_PRIVATE_MEM + select HAVE_KVM_GMEM_PREPARE help Provides support for launching Encrypted VMs (SEV) and Encrypted VMs with Encrypted State (SEV-ES) on AMD processors.
arch/x86/kvm/svm/sev.c +98 -0
101 lines not on this page · raw message
arch/x86/kvm/svm/svm.c +2 -0
@@ -5080,6 +5080,8 @@ static struct kvm_x86_ops svm_x86_ops __initdata = { .vcpu_deliver_sipi_vector = svm_vcpu_deliver_sipi_vector, .vcpu_get_apicv_inhibit_reasons = avic_vcpu_get_apicv_inhibit_reasons, .alloc_apic_backing_page = svm_alloc_apic_backing_page, + + .gmem_prepare = sev_gmem_prepare, };/*
arch/x86/kvm/svm/svm.h +5 -0
@@ -730,6 +730,7 @@ extern unsigned int max_sev_asid;void sev_handle_rmp_fault (struct kvm_vcpu *vcpu,gpa_t gpa,u64 error_code);void sev_vcpu_unblocking (struct kvm_vcpu *vcpu);void sev_snp_init_protected_guest_state (struct kvm_vcpu *vcpu); +int sev_gmem_prepare (struct kvm *kvm,kvm_pfn_t pfn,gfn_t gfn,int max_order);#else static inline struct page *snp_safe_alloc_page (struct kvm_vcpu *vcpu) {return alloc_page (GFP_KERNEL_ACCOUNT | __GFP_ZERO);
@@ -746,6 +747,10 @@ static inline int sev_dev_get_attr(u32 group, u64 attr, u64 *val) { return -ENXIstatic inline void sev_handle_rmp_fault (struct kvm_vcpu *vcpu,gpa_t gpa,u64 error_code) {}static inline void sev_vcpu_unblocking (struct kvm_vcpu *vcpu) {}static inline void sev_snp_init_protected_guest_state (struct kvm_vcpu *vcpu) {} +static inline int sev_gmem_prepare (struct kvm *kvm,kvm_pfn_t pfn,gfn_t gfn,int max_order) +{ +return 0 ; +}#endif
arch/x86/kvm/x86.c +5 -0
@@ -13610,6 +13610,11 @@ bool kvm_arch_no_poll(struct kvm_vcpu *vcpu)EXPORT_SYMBOL_GPL (kvm_arch_no_poll);#ifdef CONFIG_HAVE_KVM_GMEM_PREPARE +bool kvm_arch_gmem_prepare_needed (struct kvm *kvm) +{ +return kvm->arch.vm_type ==KVM_X86_SNP_VM ; +} +int kvm_arch_gmem_prepare (struct kvm *kvm,gfn_t gfn,kvm_pfn_t pfn,int max_order) {return static_call (kvm_x86_gmem_prepare)(kvm, pfn, gfn, max_order);
virt/kvm/guest_memfd.c +2 -2
@@ -46,8 +46,8 @@ static int kvm_gmem_prepare_folio(struct inode *inode, pgoff_t index, struct fol gfn = slot->base_gfn + index - slot->gmem.pgoff; rc =kvm_arch_gmem_prepare (kvm, gfn, pfn,compound_order (compound_head (page)));if (rc) { -pr_warn_ratelimited ("gmem: Failed to prepare folio for index %lx, error %d.\n" , - index, rc); +pr_warn_ratelimited ("gmem: Failed to prepare folio for index %lx GFN %llx PFN %llx error %d.\n" , + index, gfn, pfn, rc);return rc; } }
Signature · 1 line
2.25.1
PATCH v13 19/26 KVM: SEV: Implement gmem hook for invalidating private pages
In reply to Michael RothImplement a platform hook to do the work of restoring the direct map entries of gmem-managed pages and transitioning the corresponding RMP table entries back to the default shared/hypervisor-owned state.
--- arch/x86/kvm/Kconfig | 1 + arch/x86/kvm/svm/sev.c | 64 ++++++++++++++++++++++++++++++++++++++++++ arch/x86/kvm/svm/svm.c | 1 + arch/x86/kvm/svm/svm.h | 2 ++ 4 files changed, 68 insertions(+)
arch/x86/kvm/Kconfig +1 -0
@@ -138,6 +138,7 @@ config KVM_AMD_SEV select ARCH_HAS_CC_PLATFORM select KVM_GENERIC_PRIVATE_MEM select HAVE_KVM_GMEM_PREPARE + select HAVE_KVM_GMEM_INVALIDATE help Provides support for launching Encrypted VMs (SEV) and Encrypted VMs with Encrypted State (SEV-ES) on AMD processors.
arch/x86/kvm/svm/sev.c +64 -0
67 lines not on this page · raw message
arch/x86/kvm/svm/svm.c +1 -0
@@ -5082,6 +5082,7 @@ static struct kvm_x86_ops svm_x86_ops __initdata = { .alloc_apic_backing_page = svm_alloc_apic_backing_page, .gmem_prepare = sev_gmem_prepare, + .gmem_invalidate = sev_gmem_invalidate, };/*
arch/x86/kvm/svm/svm.h +2 -0
@@ -731,6 +731,7 @@ void sev_handle_rmp_fault(struct kvm_vcpu *vcpu, gpa_t gpa, u64 error_code);void sev_vcpu_unblocking (struct kvm_vcpu *vcpu);void sev_snp_init_protected_guest_state (struct kvm_vcpu *vcpu);int sev_gmem_prepare (struct kvm *kvm,kvm_pfn_t pfn,gfn_t gfn,int max_order); +void sev_gmem_invalidate (kvm_pfn_t start,kvm_pfn_t end);#else static inline struct page *snp_safe_alloc_page (struct kvm_vcpu *vcpu) {return alloc_page (GFP_KERNEL_ACCOUNT | __GFP_ZERO);
@@ -751,6 +752,7 @@ static inline int sev_gmem_prepare(struct kvm *kvm, kvm_pfn_t pfn, gfn_t gfn, in {return 0 ; } +static inline void sev_gmem_invalidate (kvm_pfn_t start,kvm_pfn_t end) {}#endif
Signature · 1 line
2.25.1
PATCH v13 20/26 KVM: x86: Implement gmem hook for determining max NPT mapping level
In reply to Michael RothIn the case of SEV-SNP, whether or not a 2MB page can be mapped via a 2MB mapping in the guest's nested page table depends on whether or not any subpages within the range have already been initialized as private in the RMP table. The existing mixed-attribute tracking in KVM is insufficient here, for instance:
- gmem allocates 2MB page
- guest issues PVALIDATE on 2MB page
- guest later converts a subpage to shared
- SNP host code issues PSMASH to split 2MB RMP mapping to 4K
- KVM MMU splits NPT mapping to 4K
- guest later converts that shared page back to private
At this point there are no mixed attributes, and KVM would normally allow for 2MB NPT mappings again, but this is actually not allowed because the RMP table mappings are 4K and cannot be promoted on the hypervisor side, so the NPT mappings must still be limited to 4K to match this.
Implement a kvm_x86_ops.gmem_validate_fault() hook for SEV that checks for this condition and adjusts the mapping level accordingly.
--- arch/x86/kvm/svm/sev.c | 32 ++++++++++++++++++++++++++++++++ arch/x86/kvm/svm/svm.c | 1 + arch/x86/kvm/svm/svm.h | 7 +++++++ 3 files changed, 40 insertions(+)
arch/x86/kvm/svm/sev.c +32 -0
@@ -4519,3 +4519,35 @@ void sev_gmem_invalidate(kvm_pfn_t start, kvm_pfn_t end)cond_resched (); } } + +/* +* Re-check whether an #NPF for a private/gmem page can still be serviced, and +* adjust maximum mapping level if needed. +*/ +int sev_gmem_validate_fault (struct kvm *kvm,kvm_pfn_t pfn,gfn_t gfn,bool is_private, +u8 *max_level) +{ +int level, rc; +bool assigned; + +if (!sev_snp_guest (kvm)) +return 0 ; + + rc =snp_lookup_rmpentry (pfn, &assigned, &level); +if (rc) { +pr_err_ratelimited ("SEV: RMP entry not found: GFN %llx PFN %llx level %d error %d\n" , + gfn, pfn, level, rc); +return -ENOENT ; + } + +if (!assigned) { +pr_err_ratelimited ("SEV: RMP entry is not assigned: GFN %llx PFN %llx level %d\n" , + gfn, pfn, level); +return -EINVAL ; + } + +if (level < *max_level) + *max_level = level; + +return 0 ; +}
arch/x86/kvm/svm/svm.c +1 -0
@@ -5083,6 +5083,7 @@ static struct kvm_x86_ops svm_x86_ops __initdata = { .gmem_prepare = sev_gmem_prepare, .gmem_invalidate = sev_gmem_invalidate, + .gmem_validate_fault = sev_gmem_validate_fault, };/*
arch/x86/kvm/svm/svm.h +7 -0
@@ -732,6 +732,8 @@ void sev_vcpu_unblocking(struct kvm_vcpu *vcpu);void sev_snp_init_protected_guest_state (struct kvm_vcpu *vcpu);int sev_gmem_prepare (struct kvm *kvm,kvm_pfn_t pfn,gfn_t gfn,int max_order);void sev_gmem_invalidate (kvm_pfn_t start,kvm_pfn_t end); +int sev_gmem_validate_fault (struct kvm *kvm,kvm_pfn_t pfn,gfn_t gfn,bool is_private, +u8 *max_level);#else static inline struct page *snp_safe_alloc_page (struct kvm_vcpu *vcpu) {return alloc_page (GFP_KERNEL_ACCOUNT | __GFP_ZERO);
@@ -753,6 +755,11 @@ static inline int sev_gmem_prepare(struct kvm *kvm, kvm_pfn_t pfn, gfn_t gfn, in return 0; }static inline void sev_gmem_invalidate (kvm_pfn_t start,kvm_pfn_t end) {} +static inline int sev_gmem_validate_fault (struct kvm *kvm,kvm_pfn_t pfn,gfn_t gfn, +bool is_private,u8 *max_level) +{ +return 0 ; +}#endif
Signature · 1 line
2.25.1
PATCH v13 21/26 KVM: SEV: Avoid WBINVD for HVA-based MMU notifications for SNP
In reply to Michael RothFrom: Ashish Kalra <[email protected]>
With SNP/guest_memfd, private/encrypted memory should not be mappable, and MMU notifications for HVA-mapped memory will only be relevant to unencrypted guest memory. Therefore, the rationale behind issuing a wbinvd_on_all_cpus() in sev_guest_memory_reclaimed() should not apply for SNP guests and can be ignored.
--- arch/x86/kvm/svm/sev.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
arch/x86/kvm/svm/sev.c +8 -1
@@ -3033,7 +3033,14 @@ static void sev_flush_encrypted_page(struct kvm_vcpu *vcpu, void *va)void sev_guest_memory_reclaimed (struct kvm *kvm) { -if (!sev_guest (kvm)) +/* +* With SNP+gmem, private/encrypted memory should be +* unreachable via the hva-based mmu notifiers. Additionally, +* for shared->private translations, H/W coherency will ensure +* first guest access to the page would clear out any existing +* dirty copies of that cacheline. +*/ +if (!sev_guest (kvm) ||sev_snp_guest (kvm))return ;wbinvd_on_all_cpus ();
Signature · 1 line
2.25.1
PATCH v13 22/26 KVM: SVM: Add module parameter to enable SEV-SNP
In reply to Michael RothFrom: Brijesh Singh <[email protected]>
Add a module parameter than can be used to enable or disable the SEV-SNP feature. Now that KVM contains the support for the SNP set the GHCB hypervisor feature flag to indicate that SNP is supported.
--- arch/x86/kvm/svm/sev.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
arch/x86/kvm/svm/sev.c +2 -1
@@ -48,7 +48,8 @@ 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; +static bool sev_snp_enabled = true; +module_param_named (sev_snp, sev_snp_enabled, bool,0444 );/* enable/disable SEV-ES DebugSwap support */ static bool sev_es_debug_swap_enabled = true;
Signature · 1 line
2.25.1
PATCH v13 23/26 KVM: SEV: Provide support for SNP_GUEST_REQUEST NAE event
In reply to Michael RothFrom: Brijesh Singh <[email protected]>
Version 2 of GHCB specification added support for the SNP Guest Request Message NAE event. The event allows for an SEV-SNP guest to make requests to the SEV-SNP firmware through hypervisor using the SNP_GUEST_REQUEST API defined in the SEV-SNP firmware specification.
This is used by guests primarily to request attestation reports from firmware. There are other request types are available as well, but the specifics of what guest requests are being made are opaque to the hypervisor, which only serves as a proxy for the guest requests and firmware responses.
Implement handling for these events.
[mdr: ensure FW command failures are indicated to guest, drop extended
request handling to be re-written as separate patch, massage commit]
--- arch/x86/kvm/svm/sev.c | 83 ++++++++++++++++++++++++++++++++++ include/uapi/linux/sev-guest.h | 9 ++++ 2 files changed, 92 insertions(+)
arch/x86/kvm/svm/sev.c +83 -0
@@ -19,6 +19,7 @@#include <linux/misc_cgroup.h> #include <linux/processor.h> #include <linux/trace_events.h> +#include <uapi/linux/sev-guest.h> #include <asm/pkru.h> #include <asm/trapnr.h>
@@ -3281,6 +3282,7 @@ static int sev_es_validate_vmgexit(struct vcpu_svm *svm)case SVM_VMGEXIT_HV_FEATURES :case SVM_VMGEXIT_PSC :case SVM_VMGEXIT_TERM_REQUEST : +case SVM_VMGEXIT_GUEST_REQUEST :break ;default : reason =GHCB_ERR_INVALID_EVENT ;
@@ -3724,6 +3726,83 @@ static int sev_snp_ap_creation(struct vcpu_svm *svm)return ret; } +static bool snp_setup_guest_buf (struct kvm *kvm,struct sev_data_snp_guest_request *data, +gpa_t req_gpa,gpa_t resp_gpa) +{ +struct kvm_sev_info *sev = &to_kvm_svm (kvm)->sev_info; +kvm_pfn_t req_pfn, resp_pfn; + +if (!PAGE_ALIGNED (req_gpa) || !PAGE_ALIGNED (resp_gpa)) +return false; + + req_pfn =gfn_to_pfn (kvm,gpa_to_gfn (req_gpa)); +if (is_error_noslot_pfn (req_pfn)) +return false; + + resp_pfn =gfn_to_pfn (kvm,gpa_to_gfn (resp_gpa)); +if (is_error_noslot_pfn (resp_pfn)) +return false; + +if (rmp_make_private (resp_pfn,0 ,PG_LEVEL_4K ,0 , true)) +return false; + + data->gctx_paddr =__psp_pa (sev->snp_context); + data->req_paddr =__sme_set (req_pfn <<PAGE_SHIFT ); + data->res_paddr =__sme_set (resp_pfn <<PAGE_SHIFT ); + +return true; +} + +static bool snp_cleanup_guest_buf (struct sev_data_snp_guest_request *data) +{ +u64 pfn =__sme_clr (data->res_paddr) >>PAGE_SHIFT ; + +if (snp_page_reclaim (pfn)) +return false; + +if (rmp_make_shared (pfn,PG_LEVEL_4K )) +return false; + +return true; +} + +static bool __snp_handle_guest_req (struct kvm *kvm,gpa_t req_gpa,gpa_t resp_gpa, +sev_ret_code *fw_err) +{ +struct sev_data_snp_guest_request data = {0 }; +struct kvm_sev_info *sev; +bool ret = true; + +if (!sev_snp_guest (kvm)) +return false; + + sev = &to_kvm_svm (kvm)->sev_info; + +if (!snp_setup_guest_buf (kvm, &data, req_gpa, resp_gpa)) +return false; + +if (sev_issue_cmd (kvm,SEV_CMD_SNP_GUEST_REQUEST , &data, fw_err)) + ret = false; + +if (!snp_cleanup_guest_buf (&data)) + ret = false; + +return ret; +} + +static void snp_handle_guest_req (struct vcpu_svm *svm,gpa_t req_gpa,gpa_t resp_gpa) +{ +struct kvm_vcpu *vcpu = &svm->vcpu; +struct kvm *kvm = vcpu->kvm; +sev_ret_code fw_err =0 ; +int vmm_ret =0 ; + +if (!__snp_handle_guest_req (kvm, req_gpa, resp_gpa, &fw_err)) + vmm_ret =SNP_GUEST_VMM_ERR_GENERIC ; + +ghcb_set_sw_exit_info_2 (svm->sev_es.ghcb,SNP_GUEST_ERR (vmm_ret, fw_err)); +} +static int sev_handle_vmgexit_msr_protocol (struct vcpu_svm *svm) {struct vmcb_control_area *control = &svm->vmcb->control;
@@ -3984,6 +4063,10 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu) vcpu->run->system_event.ndata =1 ; vcpu->run->system_event.data[0 ] = control->ghcb_gpa;break ; +case SVM_VMGEXIT_GUEST_REQUEST : +snp_handle_guest_req (svm, control->exit_info_1, control->exit_info_2); + ret =1 ; +break ;case SVM_VMGEXIT_UNSUPPORTED_EVENT : vcpu_unimpl(vcpu, "vmgexit: unsupported event - exit_info_1=%#llx, exit_info_2=%#llx\n",
include/uapi/linux/sev-guest.h +9 -0
@@ -89,8 +89,17 @@ struct snp_ext_report_req {#define SNP_GUEST_FW_ERR_MASK GENMASK_ULL(31, 0)#define SNP_GUEST_VMM_ERR_SHIFT 32#define SNP_GUEST_VMM_ERR (x) (((u64)x) << SNP_GUEST_VMM_ERR_SHIFT) +#define SNP_GUEST_FW_ERR (x) ((x) & SNP_GUEST_FW_ERR_MASK) +#define SNP_GUEST_ERR (vmm_err, fw_err) (SNP_GUEST_VMM_ERR(vmm_err) | \ + SNP_GUEST_FW_ERR(fw_err)) +/* +* The GHCB spec only formally defines INVALID_LEN/BUSY VMM errors, but define +* a GENERIC error code such that it won't ever conflict with GHCB-defined +* errors if any get added in the future. +*/ #define SNP_GUEST_VMM_ERR_INVALID_LEN 1#define SNP_GUEST_VMM_ERR_BUSY 2 +#define SNP_GUEST_VMM_ERR_GENERIC BIT(31)#endif /* __UAPI_LINUX_SEV_GUEST_H_ */
Signature · 1 line
2.25.1
PATCH v13 24/26 crypto: ccp: Add the SNP_VLEK_LOAD command
In reply to Michael RothWhen requesting an attestation report a guest is able to specify whether it wants SNP firmware to sign the report using either a Versioned Chip Endorsement Key (VCEK), which is derived from chip-unique secrets, or a Versioned Loaded Endorsement Key (VLEK) which is obtained from an AMD Key Derivation Service (KDS) and derived from seeds allocated to enrolled cloud service providers (CSPs).
For VLEK keys, an SNP_VLEK_LOAD SNP firmware command is used to load them into the system after obtaining them from the KDS. Add a corresponding userspace interface so to allow the loading of VLEK keys into the system.
See SEV-SNP Firmware ABI 1.54, SNP_VLEK_LOAD for more details.
--- Documentation/virt/coco/sev-guest.rst | 19 ++++++++++++++ drivers/crypto/ccp/sev-dev.c | 36 +++++++++++++++++++++++++++ include/uapi/linux/psp-sev.h | 27 ++++++++++++++++++++ 3 files changed, 82 insertions(+)
Documentation/virt/coco/sev-guest.rst +19 -0
@@ -176,6 +176,25 @@ to SNP_CONFIG command defined in the SEV-SNP spec. The current values of the firmware parameters affected by this command can be queried via SNP_PLATFORM_STATUS. +2.7 SNP_VLEK_LOAD +----------------- +:Technology: sev-snp +:Type: hypervisor ioctl cmd +:Parameters (in): struct sev_user_data_snp_vlek_load +:Returns (out): 0 on success, -negative on error + +When requesting an attestation report a guest is able to specify whether +it wants SNP firmware to sign the report using either a Versioned Chip +Endorsement Key (VCEK), which is derived from chip-unique secrets, or a +Versioned Loaded Endorsement Key (VLEK) which is obtained from an AMD +Key Derivation Service (KDS) and derived from seeds allocated to +enrolled cloud service providers. + +In the case of VLEK keys, the SNP_VLEK_LOAD SNP command is used to load +them into the system after obtaining them from the KDS, and corresponds +closely to the SNP_VLEK_LOAD firmware command specified in the SEV-SNP +spec. + 3. SEV-SNP CPUID Enforcement ============================
drivers/crypto/ccp/sev-dev.c +36 -0
@@ -2027,6 +2027,39 @@ static int sev_ioctl_do_snp_set_config(struct sev_issue_cmd *argp, bool writablereturn __sev_do_cmd_locked (SEV_CMD_SNP_CONFIG , &config, &argp->error); } +static int sev_ioctl_do_snp_vlek_load (struct sev_issue_cmd *argp,bool writable) +{ +struct sev_device *sev = psp_master->sev_data; +struct sev_user_data_snp_vlek_load input; +void *blob; +int ret; + +if (!sev->snp_initialized || !argp->data) +return -EINVAL ; + +if (!writable) +return -EPERM ; + +if (copy_from_user (&input,u64_to_user_ptr (argp->data),sizeof (input))) +return -EFAULT ; + +if (input.len !=sizeof (input) || input.vlek_wrapped_version !=0 ) +return -EINVAL ; + + blob =psp_copy_user_blob (input.vlek_wrapped_address, +sizeof (struct sev_user_data_snp_wrapped_vlek_hashstick )); +if (IS_ERR (blob)) +return PTR_ERR (blob); + + input.vlek_wrapped_address =__psp_pa (blob); + + ret =__sev_do_cmd_locked (SEV_CMD_SNP_VLEK_LOAD , &input, &argp->error); + +kfree (blob); + +return ret; +} +static long sev_ioctl (struct file *file,unsigned int ioctl,unsigned long arg) {void __user *argp = (void __user *)arg;
@@ -2087,6 +2120,9 @@ static long sev_ioctl(struct file *file, unsigned int ioctl, unsigned long arg)case SNP_SET_CONFIG : ret =sev_ioctl_do_snp_set_config (&input, writable);break ; +case SNP_VLEK_LOAD : + ret =sev_ioctl_do_snp_vlek_load (&input, writable); +break ;default : ret = -EINVAL ; goto out;
include/uapi/linux/psp-sev.h +27 -0
@@ -31,6 +31,7 @@ enum {SNP_PLATFORM_STATUS ,SNP_COMMIT ,SNP_SET_CONFIG , +SNP_VLEK_LOAD , SEV_MAX, };
@@ -214,6 +215,32 @@ struct sev_user_data_snp_config {__u8 rsvd1[52 ]; } __packed; +/** +* struct sev_data_snp_vlek_load - SNP_VLEK_LOAD structure +* +* @len: length of the command buffer read by the PSP +* @vlek_wrapped_version: version of wrapped VLEK hashstick (Must be 0h) +* @rsvd: reserved +* @vlek_wrapped_address: address of a wrapped VLEK hashstick +* (struct sev_user_data_snp_wrapped_vlek_hashstick) +*/ +struct sev_user_data_snp_vlek_load { +__u32 len;/* In */ +__u8 vlek_wrapped_version;/* In */ +__u8 rsvd[3 ];/* In */ +__u64 vlek_wrapped_address;/* In */ +} __packed; + +/** +* struct sev_user_data_snp_vlek_wrapped_vlek_hashstick - Wrapped VLEK data +* +* @data: Opaque data provided by AMD KDS (as described in SEV-SNP Firmware ABI +* 1.54, SNP_VLEK_LOAD) +*/ +struct sev_user_data_snp_wrapped_vlek_hashstick { +__u8 data[432 ];/* In */ +} __packed; +/** * struct sev_issue_cmd - SEV ioctl parameters *
Signature · 1 line
2.25.1
PATCH v13 25/26 crypto: ccp: Add the SNP_{PAUSE,RESUME}_ATTESTATION commands
In reply to Michael RothThese commands can be used to pause servicing of guest attestation requests. This useful when updating the reported TCB or signing key with commands such as SNP_SET_CONFIG/SNP_COMMIT/SNP_VLEK_LOAD, since they may in turn require updates to userspace-supplied certificates, and if an attestation request happens to be in-flight at the time those updates are occurring there is potential for a guest to receive a certificate blob that is out of sync with the effective signing key for the attestation report.
These interfaces also provide some versatility with how similar firmware/certificate update activities can be handled in the future.
--- Documentation/virt/coco/sev-guest.rst | 50 +++++++++++++++++++++++++-- arch/x86/include/asm/sev.h | 6 ++++ arch/x86/virt/svm/sev.c | 43 +++++++++++++++++++++++ drivers/crypto/ccp/sev-dev.c | 47 +++++++++++++++++++++++++ include/uapi/linux/psp-sev.h | 12 +++++++ 5 files changed, 156 insertions(+), 2 deletions(-)
Documentation/virt/coco/sev-guest.rst +48 -2
@@ -128,8 +128,6 @@ the SEV-SNP specification for further details. The SNP_GET_EXT_REPORT ioctl is similar to the SNP_GET_REPORT. The difference is related to the additional certificate data that is returned with the report. -The certificate data returned is being provided by the hypervisor through the -SNP_SET_EXT_CONFIG. The ioctl uses the SNP_GUEST_REQUEST (MSG_REPORT_REQ) command provided by the SEV-SNP firmware to get the attestation report.
@@ -195,6 +193,54 @@ them into the system after obtaining them from the KDS, and corresponds closely to the SNP_VLEK_LOAD firmware command specified in the SEV-SNP spec. +2.8 SNP_PAUSE_ATTESTATION / SNP_RESUME_ATTESTATION +-------------------------------------------------- +:Technology: sev-snp +:Type: hypervisor ioctl cmd +:Parameters (out): struct sev_user_data_snp_pause_transaction +:Returns (out): 0 on success, -negative on error + +When requesting attestation reports, SNP guests have the option of issuing +an extended guest request which allows host userspace to supply additional +certificate data that can be used to validate the signature used to sign +the attestation report. This signature is generated using a key that is +derived from the reported TCB that can be set via the SNP_SET_CONFIG and +SNP_COMMIT ioctls, so the accompanying certificate data needs to be kept in +sync with the changes made to the reported TCB via these ioctls. + +Similarly, interfaces like SNP_LOAD_VLEK can modify the key used to sign +the attestation reports, which may in turn require updating the certificate +data provided to guests via extended guest requests. + +To allow for updating the reported TCB, endorsement key, and any certificate +data in a manner that is atomic to guests, the SNP_PAUSE_ATTESTATION and +SNP_RESUME_ATTESTATION commands are provided. + +After SNP_PAUSE_ATTESTATION is issued, any attestation report requests via +extended guest requests that are in-progress, or received after +SNP_PAUSE_ATTESTATION is issued, will result in the guest receiving a +GHCB-defined error message instructing it to retry the request. Once all +the desired reported TCB, endorsement keys, or certificate data updates +are completed on the host, the SNP_RESUME_ATTESTATION command must be +issued to allow guest attestation requests to proceed. + +In general, hosts should serialize updates of this sort and never have more +than 1 outstanding transaction in flight that could result in the +interleaving of multiple SNP_PAUSE_ATTESTATION/SNP_RESUME_ATTESTATION pairs. +To guard against this, SNP_PAUSE_ATTESTATION will fail if another process +has already paused attestation requests. + +However, there may be occassions where a transaction needs to be aborted due +to unexpected activity in userspace such as timeouts, crashes, etc., so +SNP_RESUME_ATTESTATION will always succeed. Nonetheless, this could +potentially lead to SNP_RESUME_ATTESTATION being called out of sequence, so +to allow for callers of SNP_{PAUSE,RESUME}_ATTESTATION to detect such +occurrences, each ioctl will return a transaction ID in the response so the +caller can monitor whether the start/end ID both match. If they don't, the +caller should assume that attestation has been paused/resumed unexpectedly, +and take whatever measures it deems necessary such as logging, reporting, +auditing the sequence of events. + 3. SEV-SNP CPUID Enforcement ============================
arch/x86/include/asm/sev.h +6 -0
@@ -82,6 +82,8 @@ extern void vc_no_ghcb(void);extern void vc_boot_ghcb (void );extern bool handle_vc_boot_ghcb (struct pt_regs *regs); +extern struct mutex snp_pause_attestation_lock; +/* PVALIDATE return codes */ #define PVALIDATE_FAIL_SIZEMISMATCH 6
@@ -272,6 +274,8 @@ int rmp_make_private(u64 pfn, u64 gpa, enum pg_level level, u32 asid, bool immutint rmp_make_shared (u64 pfn,enum pg_level level);void snp_leak_pages (u64 pfn,unsigned int npages);void kdump_sev_callback (void ); +int snp_pause_attestation (u64 *transaction_id); +void snp_resume_attestation (u64 *transaction_id);#else static inline bool snp_probe_rmptable_info (void ) {return false; }static inline int snp_lookup_rmpentry (u64 pfn,bool *assigned,int *level) {return -ENODEV ; }
@@ -285,6 +289,8 @@ static inline int rmp_make_private(u64 pfn, u64 gpa, enum pg_level level, u32 asstatic inline int rmp_make_shared (u64 pfn,enum pg_level level) {return -ENODEV ; }static inline void snp_leak_pages (u64 pfn,unsigned int npages) {}static inline void kdump_sev_callback (void ) { } +static inline int snp_pause_attestation (u64 *transaction_id) {return 0 ; } +static inline void snp_resume_attestation (u64 *transaction_id) {}#endif #endif
arch/x86/virt/svm/sev.c +43 -0
52 lines not on this page · raw message
drivers/crypto/ccp/sev-dev.c +47 -0
59 lines not on this page · raw message
include/uapi/linux/psp-sev.h +12 -0
@@ -32,6 +32,8 @@ enum {SNP_COMMIT ,SNP_SET_CONFIG ,SNP_VLEK_LOAD , +SNP_PAUSE_ATTESTATION , +SNP_RESUME_ATTESTATION , SEV_MAX, };
@@ -241,6 +243,16 @@ struct sev_user_data_snp_wrapped_vlek_hashstick {__u8 data[432 ];/* In */ } __packed; +/** +* struct sev_user_data_snp_pause_attestation - metadata for pausing attestation +* +* @id: the ID of the transaction started/ended by a call to SNP_PAUSE_ATTESTATION +* or SNP_RESUME_ATTESTATION, respectively. +*/ +struct sev_user_data_snp_pause_attestation { +__u64 id;/* Out */ +} __packed; +/** * struct sev_issue_cmd - SEV ioctl parameters *
Signature · 1 line
2.25.1
PATCH v13 26/26 KVM: SEV: Provide support for SNP_EXTENDED_GUEST_REQUEST NAE event
In reply to Michael RothVersion 2 of GHCB specification added support for the SNP Extended Guest Request Message NAE event. This event serves a nearly identical purpose to the previously-added SNP_GUEST_REQUEST event, but allows for additional certificate data to be supplied via an additional guest-supplied buffer to be used mainly for verifying the signature of an attestation report as returned by firmware.
This certificate data is supplied by userspace, so unlike with SNP_GUEST_REQUEST events, SNP_EXTENDED_GUEST_REQUEST events are first forwarded to userspace via a KVM_EXIT_VMGEXIT exit type, and then the firmware request is made only afterward.
Implement handling for these events.
Since there is a potential for race conditions where the userspace-supplied certificate data may be out-of-sync relative to the reported TCB or VLEK that firmware will use when signing attestation reports, make use of the synchronization mechanisms wired up to the SNP_{PAUSE,RESUME}_ATTESTATION SEV device ioctls such that the guest will be told to retry the request while attestation has been paused due to an update being underway on the system.
--- Documentation/virt/kvm/api.rst | 26 +++++++++++ arch/x86/include/asm/sev.h | 6 +++ arch/x86/kvm/svm/sev.c | 82 ++++++++++++++++++++++++++++++++++ arch/x86/kvm/svm/svm.h | 3 ++ arch/x86/virt/svm/sev.c | 37 +++++++++++++++ include/uapi/linux/kvm.h | 6 +++ 6 files changed, 160 insertions(+)
Documentation/virt/kvm/api.rst +26 -0
@@ -7066,6 +7066,7 @@ values in kvm_run even if the corresponding bit in kvm_dirty_regs is not set. struct kvm_user_vmgexit { #define KVM_USER_VMGEXIT_PSC_MSR 1 #define KVM_USER_VMGEXIT_PSC 2 + #define KVM_USER_VMGEXIT_EXT_GUEST_REQ 3 __u32 type; /* KVM_USER_VMGEXIT_* type */ union { struct {
@@ -7079,6 +7080,11 @@ values in kvm_run even if the corresponding bit in kvm_dirty_regs is not set. __u64 shared_gpa; __u64 ret; } psc; + struct { + __u64 data_gpa; + __u64 data_npages; + __u32 ret; + } ext_guest_req; }; };
@@ -7108,6 +7114,26 @@ private/shared state. Userspace will return a value in 'ret' that is in agreement with the GHCB-defined return values that the guest will expect in the SW_EXITINFO2 field of the GHCB in response to these requests. +For the KVM_USER_VMGEXIT_EXT_GUEST_REQ type, the ext_guest_req union type +is used. The kernel will supply in 'data_gpa' the value the guest supplies +via the RAX field of the GHCB when issued extended guest requests. +'data_npages' will similarly contain the value the guest supplies in RBX +denoting the number of shared pages available to write the certificate +data into. + + - If the supplied number of pages is sufficient, userspace should write + the certificate data blob (in the format defined by the GHCB spec) in + the address indicated by 'data_gpa' and set 'ret' to 0. + + - If the number of pages supplied is not sufficient, userspace must write + the required number of pages in 'data_npages' and then set 'ret' to 1. + + - If userspace is temporarily unable to handle the request, 'ret' should + be set to 2 to inform the guest to retry later. + + - If some other error occurred, userspace should set 'ret' to a non-zero + value that is distinct from the specific return values mentioned above. + 6. Capabilities that can be enabled on vCPUs ============================================
arch/x86/include/asm/sev.h +6 -0
@@ -276,6 +276,9 @@ void snp_leak_pages(u64 pfn, unsigned int npages);void kdump_sev_callback (void );int snp_pause_attestation (u64 *transaction_id);void snp_resume_attestation (u64 *transaction_id); +u64 snp_transaction_get_id (void ); +bool __snp_transaction_is_stale (u64 transaction_id); +bool snp_transaction_is_stale (u64 transaction_id);#else static inline bool snp_probe_rmptable_info (void ) {return false; }static inline int snp_lookup_rmpentry (u64 pfn,bool *assigned,int *level) {return -ENODEV ; }
@@ -291,6 +294,9 @@ static inline void snp_leak_pages(u64 pfn, unsigned int npages) {}static inline void kdump_sev_callback (void ) { }static inline int snp_pause_attestation (u64 *transaction_id) {return 0 ; }static inline void snp_resume_attestation (u64 *transaction_id) {} +static inline u64 snp_transaction_get_id (void ) {return 0 ; } +static inline bool __snp_transaction_is_stale (u64 transaction_id) {return false; } +static inline bool snp_transaction_is_stale (u64 transaction_id) {return false; }#endif #endif
arch/x86/kvm/svm/sev.c +82 -0
100 lines not on this page · raw message
arch/x86/kvm/svm/svm.h +3 -0
@@ -303,6 +303,9 @@ struct vcpu_svm {/* Guest GIF value, used when vGIF is not enabled */ bool guest_gif; + +/* Transaction ID associated with SNP config updates */ +u64 snp_transaction_id; };struct svm_cpu_data {
arch/x86/virt/svm/sev.c +37 -0
@@ -72,6 +72,7 @@ static unsigned long snp_nr_leaked_pages;/* For synchronizing TCB/certificate updates with extended guest requests */ DEFINE_MUTEX (snp_pause_attestation_lock); +EXPORT_SYMBOL_GPL (snp_pause_attestation_lock);static u64 snp_transaction_id;static bool snp_attestation_paused;
@@ -611,3 +612,39 @@ void snp_resume_attestation(u64 *transaction_id)mutex_unlock (&snp_pause_attestation_lock); }EXPORT_SYMBOL_GPL (snp_resume_attestation); + +u64 snp_transaction_get_id (void ) +{ +u64 id; + +mutex_lock (&snp_pause_attestation_lock); + id = snp_transaction_id; +mutex_unlock (&snp_pause_attestation_lock); + +return id; +} +EXPORT_SYMBOL_GPL (snp_transaction_get_id); + +/* Must be called with snp_pause_attestion_lock held */ +bool __snp_transaction_is_stale (u64 transaction_id) +{ +lockdep_assert_held (&snp_pause_attestation_lock); + +return (snp_attestation_paused || + transaction_id != snp_transaction_id); +} +EXPORT_SYMBOL_GPL (__snp_transaction_is_stale); + +bool snp_transaction_is_stale (u64 transaction_id) +{ +bool stale; + +mutex_lock (&snp_pause_attestation_lock); + + stale =__snp_transaction_is_stale (transaction_id); + +mutex_unlock (&snp_pause_attestation_lock); + +return stale; +} +EXPORT_SYMBOL_GPL (snp_transaction_is_stale);
include/uapi/linux/kvm.h +6 -0
@@ -138,6 +138,7 @@ struct kvm_xen_exit { struct kvm_user_vmgexit { #define KVM_USER_VMGEXIT_PSC_MSR 1 #define KVM_USER_VMGEXIT_PSC 2 +#define KVM_USER_VMGEXIT_EXT_GUEST_REQ 3 __u32 type; /* KVM_USER_VMGEXIT_* type */ union { struct {
@@ -151,6 +152,11 @@ struct kvm_user_vmgexit {__u64 shared_gpa;__u64 ret; } psc; +struct { +__u64 data_gpa; +__u64 data_npages; +__u32 ret; + } ext_guest_req; }; };
Signature · 1 line
2.25.1
Paolo Bonzini
maintainerQuoted 8 lines · Michael Roth
This patchset is also available at:
https://github.com/amdese/linux/commits/snp-host-v13
and is based on commit
4d2deb62185f(as suggested by Paolo) from:https://git.kernel.org/pub/scm/virt/kvm/kvm.git/log/?h=kvm-coco-queue
This is pretty much ready to go into kvm-coco-queue. Let me know if you want to do a quick v14 with the few changes I suggested, or I can do them too.
Then the next steps are:
1) get the mm acks
2) figure out the state of patches 1-3
3) wait for more reviews of course
4) merge everything into kvm/next.
Seems in good shape for a 6.10 target.
Paolo
Quoted 286 lines
Patch Layout
------------01-03: These patches are minor dependencies for this series and are already
included in both tip/master and mainline, so are only included here as a stop-gap until merged from one of those trees. These are needed by patch #8 in this series which makes use ofCC_ATTR_HOST_SEV_SNP04: This is a small general fix-up for
guest_memfdthat can be applied
independently of this series.05-08: These patches add some basic infrastructure and introduces a new
KVM_X86_SNP_VMvm_typeto handle differences verses the existingKVM_X86_SEV_VMandKVM_X86_SEV_ES_VMtypes.09-11: These implement the KVM API to handle the creation of a
cryptographic launch context, encrypt/measure the initial image into guest memory, and finalize it before launching it.12-17: These implement handling for various guest-generated events such
as page state changes, onlining of additional vCPUs, etc.18-21: These implement the gmem hooks needed to prepare gmem-allocated
pages before mapping them into guest private memory ranges as well as cleaning them up prior to returning them to the host for use as normal memory. Because this supplants certain activities like issued WBINVDs during KVM MMU invalidations, there's also a patch to avoid duplicating that work to avoid unecessary overhead.22: With all the core support in place, the patch adds a
kvm_amdmodule
parameter to enable SNP support.23-26: These patches all deal with the servicing of guest requests to handle
things like attestation, as well as some related host-management interfaces.Testing
-------For testing this via QEMU, use the following tree:
https://github.com/amdese/qemu/commits/snp-v4-wip3
A patched OVMF is also needed due to upstream KVM no longer supporting MMIO ranges that are mapped as private. It is recommended you build the AmdSevX64 variant as it provides the kernel-hashing support present in this series:
https://github.com/amdese/ovmf/commits/apic-mmio-fix1d
A basic command-line invocation for SNP would be:
qemu-system-x86_64 -smp 32,maxcpus=255 -cpu EPYC-Milan-v2
-machine q35,confidential-guest-support=sev0,memory-backend=ram1 -object memory-backend-memfd,id=ram1,size=4G,share=true,reserve=false -object sev-snp-guest,id=sev0,cbitpos=51,reduced-phys-bits=1,id-auth= -bios OVMF_CODE-upstream-20240410-apic-mmio-fix1d-AmdSevX64.fdWith kernel-hashing and certificate data supplied:
qemu-system-x86_64 -smp 32,maxcpus=255 -cpu EPYC-Milan-v2
-machine q35,confidential-guest-support=sev0,memory-backend=ram1
-object memory-backend-memfd,id=ram1,size=4G,share=true,reserve=false
-object sev-snp-guest,id=sev0,cbitpos=51,reduced-phys-bits=1,id-auth=,certs-path=/home/mroth/cert.blob,kernel-hashes=on -bios OVMF_CODE-upstream-20240410-apic-mmio-fix1d-AmdSevX64.fd
-kernel/boot/vmlinuz-$ver
-initrd/boot/initrd.img-$ver
-append "root=UUID=d72a6d1c-06cf-4b79-af43-f1bac4f620f9 ro console=ttyS0,115200n8"With standard X64 OVMF package with separate image for persistent NVRAM:
qemu-system-x86_64 -smp 32,maxcpus=255 -cpu EPYC-Milan-v2
-machine q35,confidential-guest-support=sev0,memory-backend=ram1
-object memory-backend-memfd,id=ram1,size=4G,share=true,reserve=false
-object sev-snp-guest,id=sev0,cbitpos=51,reduced-phys-bits=1,id-auth=
-bios OVMF_CODE-upstream-20240410-apic-mmio-fix1d.fd
-drive if=pflash,format=raw,unit=0,file=OVMF_VARS-upstream-20240410-apic-mmio-fix1d.fd,readonly=offKnown issues / TODOs
--------------------* SEV-ES guests may trigger the following warning:
WARNING: CPU: 151 PID: 4003 at arch/x86/kvm/mmu/mmu.c:5855 kvm_mmu_page_fault+0x33b/0x860 [kvm]It is assumed here that these will be resolved once the transition to
PFERR_PRIVATE_ACCESSis fully completed, but if that's not the case let me know and will investigate further.* Base tree in some cases reports "Unpatched return thunk in use. This should not happen!" the first time it runs an SVM/SEV/SNP guests. This a recent regression upstream and unrelated to this series:
* 2MB hugepage support has been dropped pending discussion on how we plan to re-enable it in gmem.
* Host kexec should work, but there is a known issue with host kdump support while SNP guests are running that will be addressed as a follow-up.
* SNP kselftests are currently a WIP and will be included as part of SNP upstreaming efforts in the near-term.
SEV-SNP Overview
----------------This part of the Secure Encrypted Paging (SEV-SNP) series focuses on the changes required to add KVM support for SEV-SNP. This series builds upon SEV-SNP guest support, which is now in mainline, and and SEV-SNP host initialization support, which is now in linux-next.
While 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, which will added in the future.
With SNP, when pages are marked as guest-owned in the RMP table, they are assigned to a specific guest/ASID, as well as a specific GFN with in the guest. Any attempts to map it in the RMP table to a different guest/ASID, or a different GFN within a guest/ASID, will result in an RMP nested page fault.
Prior to accessing a guest-owned page, the guest must validate it with a special PVALIDATE instruction which will set a special bit in the RMP table for the guest. This is the only way to set the validated bit outside of the initial pre-encrypted guest payload/image; any attempts outside the guest to modify the RMP entry from that point forward will result in the validated bit being cleared, at which point the guest will trigger an exception if it attempts to access that page so it can be made aware of possible tampering.
One exception to this is the initial guest payload, which is pre-validated by the firmware prior to launching. The guest can use Guest Message requests to fetch an attestation report which will include the measurement of the initial image so that the guest can verify it was booted with the expected image/environment.
After boot, guests can use Page State Change requests to switch pages between shared/hypervisor-owned and private/guest-owned to share data for things like DMA, virtio buffers, and other GHCB requests.
In this implementation of SEV-SNP, private guest memory is managed by a new kernel framework called
guest_memfd(gmem). With gmem, a newKVM_SET_MEMORY_ATTRIBUTESKVM ioctl has been added to tell the KVM MMU whether a particular GFN should be backed by shared (normal) memory or private (gmem-allocated) memory. To tie into this, Page State Change requests are forward to userspace viaKVM_EXIT_VMGEXITexits, which will then issue the correspondingKVM_SET_MEMORY_ATTRIBUTEScall to set the private/shared state in the KVM MMU.The gmem / KVM MMU hooks implemented in this series will then update the RMP table entries for the backing PFNs to set them to guest-owned/private when mapping private pages into the guest via KVM MMU, or use the normal KVM MMU handling in the case of shared pages where the corresponding RMP table entries are left in the default shared/hypervisor-owned state.
Feedback/review is very much appreciated!
-Mike
Changes since v12:
* rebased to latest kvm-coco-queue branch (commit
4d2deb62185f)
* add more input validation forSNP_LAUNCH_START, especially for handling things like MBO/MBZ policy bits, and API major/minor minimums. (Paolo)
* block SNP KVM instances from being able to run legacy SEV commands (Paolo)
* don't attempt to measure VMSA for vcpu 0/BSP before the others, let userspace deal with the ordering just like with SEV-ES (Paolo)
* fix up docs forSNP_LAUNCH_FINISH(Paolo)
* introducesvm->sev_es.snp_has_guest_vmsaflag to better distinguish handling for guest-mapped vs non-guest-mapped VMSAs, rename 'snp_ap_create' flag to 'snp_ap_waiting_for_reset' (Paolo)
* drop "KVM: SEV: Use a VMSA physical address variable for populating VMCB" as it is no longer needed due to above VMSA rework
* replacepr_debug_ratelimited()messages for RMP #NPFs with a single trace event
* handle transientPSMASH_FAIL_INUSEreturn codes inkvm_gmem_invalidate(), switch to WARN_ON*()'s to indicate remaining error cases are not expected and should not be seen in practice. (Paolo)
* add acond_resched()inkvm_gmem_invalidate()to avoid soft lock-ups when cleaning up large guest memory ranges.
* renameVLEK_REQUIREDtoVCEK_DISABLE. it's be more applicable if another key type ever gets added.
* don't allow attestation to be paused while an attestation request is being processed by firmware (Tom)
* add missing Documentation entry forSNP_VLEK_LOAD
* collect Reviewed-by's from Paolo and TomChanges since v11:
* Rebase series on kvm-coco-queue and re-work to leverage more infrastructure between SNP/TDX series.
* DropKVM_SNP_INITin favor of the newKVM_SEV_INIT2interface introduced here (Paolo):https://lore.kernel.org/lkml/[email protected]/* Drop exposure API fields related to things like VMPL levels, migration agents, etc., until they are actually supported/used (Sean)
* ReworkKVM_SEV_SNP_LAUNCH_UPDATEhandling to use a newkvm_gmem_populate()interface instead of copying data directly into gmem-allocated pages (Sean)
* Add support forSNP_LOAD_VLEK, rework the SNP_SET_CONFIG_{START,END} to have simpler semantics that are applicable to management ofSNP_LOAD_VLEKupdates as well, rename interfaces to the now more appropriate SNP_{PAUSE,RESUME}_ATTESTATION
* Fix up documentation wording and do print warnings for userspace-triggerable failures (Peter, Sean)
* Fix a race withAP_CREATIONwake-up events (Jacob, Sean)
* Fix a memory leak with VMSA pages (Sean)
* Tighten up handling of RMP page faults to better distinguish between real and spurious cases (Tom)
* Various patch/documentation rewording, cleanups, etc.----------------------------------------------------------------
Ashish Kalra (1):
KVM: SEV: Avoid WBINVD for HVA-based MMU notifications for SNPBorislav Petkov (AMD) (3):
[TEMP] x86/kvm/Kconfig: HaveKVM_AMD_SEVselectARCH_HAS_CC_PLATFORM[TEMP] x86/cc: Add cc_platform_set/_clear() helpers [TEMP] x86/CPU/AMD: Track SNP host status with cc_platform_*()Brijesh Singh (10):
KVM: SEV: Add GHCB handling for Hypervisor Feature Support requests
KVM: SEV: AddKVM_SEV_SNP_LAUNCH_STARTcommand
KVM: SEV: AddKVM_SEV_SNP_LAUNCH_UPDATEcommand
KVM: SEV: AddKVM_SEV_SNP_LAUNCH_FINISHcommand
KVM: SEV: Add support to handle GHCB GPA register VMGEXIT
KVM: SEV: Add support to handle MSR based Page State Change VMGEXIT
KVM: SEV: Add support to handle Page State Change VMGEXIT
KVM: SEV: Add support to handle RMP nested page faults
KVM: SVM: Add module parameter to enable SEV-SNP
KVM: SEV: Provide support forSNP_GUEST_REQUESTNAE eventMichael Roth (10):
KVM:guest_memfd: FixPTR_ERR()handling in__kvm_gmem_get_pfn()
KVM: SEV: SelectKVM_GENERIC_PRIVATE_MEMwhenCONFIG_KVM_AMD_SEV=y
KVM: SEV: Add initial SEV-SNP support
KVM: SEV: Add support for GHCB-based termination requests
KVM: SEV: Implement gmem hook for initializing private pages
KVM: SEV: Implement gmem hook for invalidating private pages
KVM: x86: Implement gmem hook for determining max NPT mapping level
crypto: ccp: Add theSNP_VLEK_LOADcommand
crypto: ccp: Add the SNP_{PAUSE,RESUME}_ATTESTATION commands
KVM: SEV: Provide support forSNP_EXTENDED_GUEST_REQUESTNAE eventTom Lendacky (2):
KVM: SEV: Add support to handle AP reset MSR protocol
KVM: SEV: Support SEV-SNP AP Creation NAE eventDocumentation/virt/coco/sev-guest.rst | 69 +- Documentation/virt/kvm/api.rst | 73 + .../virt/kvm/x86/amd-memory-encryption.rst | 88 +- arch/x86/coco/core.c | 52 + arch/x86/include/asm/kvm_host.h | 2 + arch/x86/include/asm/sev-common.h | 22 +- arch/x86/include/asm/sev.h | 19 +- arch/x86/include/asm/svm.h | 9 +- arch/x86/include/uapi/asm/kvm.h | 39 + arch/x86/kernel/cpu/amd.c | 38 +- arch/x86/kernel/cpu/mtrr/generic.c | 2 +- arch/x86/kernel/sev.c | 10 - arch/x86/kvm/Kconfig | 4 + arch/x86/kvm/mmu.h | 2 - arch/x86/kvm/mmu/mmu.c | 1 + arch/x86/kvm/svm/sev.c | 1444 +++++++++++++++++++- arch/x86/kvm/svm/svm.c | 39 +- arch/x86/kvm/svm/svm.h | 50 + arch/x86/kvm/trace.h | 31 + arch/x86/kvm/x86.c | 19 +- arch/x86/virt/svm/sev.c | 106 +- drivers/crypto/ccp/sev-dev.c | 85 +- drivers/iommu/amd/init.c | 4 +- include/linux/cc_platform.h | 12 + include/linux/psp-sev.h | 4 +- include/uapi/linux/kvm.h | 28 + include/uapi/linux/psp-sev.h | 39 + include/uapi/linux/sev-guest.h | 9 + virt/kvm/guest_memfd.c | 8 +- 29 files changed, 2229 insertions(+), 79 deletions(-)
Quoted 6 lines · Paolo Bonzini
Quoted 5 lines · Michael Roth
This patchset is also available at:
Quoted 7 lines
Quoted 3 lines
and is based on commit
4d2deb62185f(as suggested by Paolo) from:https://git.kernel.org/pub/scm/virt/kvm/kvm.git/log/?h=kvm-coco-queue
This is pretty much ready to go into kvm-coco-queue. Let me know if you want to do a quick v14 with the few changes I suggested, or I can do them too.
Submitted v14 based on 20cc50a0410f from latest kvm-coco-queue (bf1390326099). Hoping that way you can easily replace v13 with v14 and force-push, but let me know if you wanted to go about it a different way.
Quoted 6 lines
Then the next steps are:
1) get the mm acks
2) figure out the state of patches 1-3
With latest kvm-coco-queue these patches are now in the base tree so I've dropped them from the series.
Quoted 6 lines
3) wait for more reviews of course
4) merge everything into kvm/next.
Seems in good shape for a 6.10 target.
Awesome! If anything needs attention just let me know.
Thanks,
Mike
Quoted 290 lines
Paolo
Quoted 286 lines
Patch Layout
------------01-03: These patches are minor dependencies for this series and are already
included in both tip/master and mainline, so are only included here as a stop-gap until merged from one of those trees. These are needed by patch #8 in this series which makes use ofCC_ATTR_HOST_SEV_SNP04: This is a small general fix-up for
guest_memfdthat can be applied
independently of this series.05-08: These patches add some basic infrastructure and introduces a new
KVM_X86_SNP_VMvm_typeto handle differences verses the existingKVM_X86_SEV_VMandKVM_X86_SEV_ES_VMtypes.09-11: These implement the KVM API to handle the creation of a
cryptographic launch context, encrypt/measure the initial image into guest memory, and finalize it before launching it.12-17: These implement handling for various guest-generated events such
as page state changes, onlining of additional vCPUs, etc.18-21: These implement the gmem hooks needed to prepare gmem-allocated
pages before mapping them into guest private memory ranges as well as cleaning them up prior to returning them to the host for use as normal memory. Because this supplants certain activities like issued WBINVDs during KVM MMU invalidations, there's also a patch to avoid duplicating that work to avoid unecessary overhead.22: With all the core support in place, the patch adds a
kvm_amdmodule
parameter to enable SNP support.23-26: These patches all deal with the servicing of guest requests to handle
things like attestation, as well as some related host-management interfaces.Testing
-------For testing this via QEMU, use the following tree:
https://github.com/amdese/qemu/commits/snp-v4-wip3
A patched OVMF is also needed due to upstream KVM no longer supporting MMIO ranges that are mapped as private. It is recommended you build the AmdSevX64 variant as it provides the kernel-hashing support present in this series:
https://github.com/amdese/ovmf/commits/apic-mmio-fix1d
A basic command-line invocation for SNP would be:
qemu-system-x86_64 -smp 32,maxcpus=255 -cpu EPYC-Milan-v2
-machine q35,confidential-guest-support=sev0,memory-backend=ram1 -object memory-backend-memfd,id=ram1,size=4G,share=true,reserve=false -object sev-snp-guest,id=sev0,cbitpos=51,reduced-phys-bits=1,id-auth= -bios OVMF_CODE-upstream-20240410-apic-mmio-fix1d-AmdSevX64.fdWith kernel-hashing and certificate data supplied:
qemu-system-x86_64 -smp 32,maxcpus=255 -cpu EPYC-Milan-v2
-machine q35,confidential-guest-support=sev0,memory-backend=ram1
-object memory-backend-memfd,id=ram1,size=4G,share=true,reserve=false
-object sev-snp-guest,id=sev0,cbitpos=51,reduced-phys-bits=1,id-auth=,certs-path=/home/mroth/cert.blob,kernel-hashes=on -bios OVMF_CODE-upstream-20240410-apic-mmio-fix1d-AmdSevX64.fd
-kernel/boot/vmlinuz-$ver
-initrd/boot/initrd.img-$ver
-append "root=UUID=d72a6d1c-06cf-4b79-af43-f1bac4f620f9 ro console=ttyS0,115200n8"With standard X64 OVMF package with separate image for persistent NVRAM:
qemu-system-x86_64 -smp 32,maxcpus=255 -cpu EPYC-Milan-v2
-machine q35,confidential-guest-support=sev0,memory-backend=ram1
-object memory-backend-memfd,id=ram1,size=4G,share=true,reserve=false
-object sev-snp-guest,id=sev0,cbitpos=51,reduced-phys-bits=1,id-auth=
-bios OVMF_CODE-upstream-20240410-apic-mmio-fix1d.fd
-drive if=pflash,format=raw,unit=0,file=OVMF_VARS-upstream-20240410-apic-mmio-fix1d.fd,readonly=offKnown issues / TODOs
--------------------* SEV-ES guests may trigger the following warning:
WARNING: CPU: 151 PID: 4003 at arch/x86/kvm/mmu/mmu.c:5855 kvm_mmu_page_fault+0x33b/0x860 [kvm]It is assumed here that these will be resolved once the transition to
PFERR_PRIVATE_ACCESSis fully completed, but if that's not the case let me know and will investigate further.* Base tree in some cases reports "Unpatched return thunk in use. This should not happen!" the first time it runs an SVM/SEV/SNP guests. This a recent regression upstream and unrelated to this series:
* 2MB hugepage support has been dropped pending discussion on how we plan to re-enable it in gmem.
* Host kexec should work, but there is a known issue with host kdump support while SNP guests are running that will be addressed as a follow-up.
* SNP kselftests are currently a WIP and will be included as part of SNP upstreaming efforts in the near-term.
SEV-SNP Overview
----------------This part of the Secure Encrypted Paging (SEV-SNP) series focuses on the changes required to add KVM support for SEV-SNP. This series builds upon SEV-SNP guest support, which is now in mainline, and and SEV-SNP host initialization support, which is now in linux-next.
While 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, which will added in the future.
With SNP, when pages are marked as guest-owned in the RMP table, they are assigned to a specific guest/ASID, as well as a specific GFN with in the guest. Any attempts to map it in the RMP table to a different guest/ASID, or a different GFN within a guest/ASID, will result in an RMP nested page fault.
Prior to accessing a guest-owned page, the guest must validate it with a special PVALIDATE instruction which will set a special bit in the RMP table for the guest. This is the only way to set the validated bit outside of the initial pre-encrypted guest payload/image; any attempts outside the guest to modify the RMP entry from that point forward will result in the validated bit being cleared, at which point the guest will trigger an exception if it attempts to access that page so it can be made aware of possible tampering.
One exception to this is the initial guest payload, which is pre-validated by the firmware prior to launching. The guest can use Guest Message requests to fetch an attestation report which will include the measurement of the initial image so that the guest can verify it was booted with the expected image/environment.
After boot, guests can use Page State Change requests to switch pages between shared/hypervisor-owned and private/guest-owned to share data for things like DMA, virtio buffers, and other GHCB requests.
In this implementation of SEV-SNP, private guest memory is managed by a new kernel framework called
guest_memfd(gmem). With gmem, a newKVM_SET_MEMORY_ATTRIBUTESKVM ioctl has been added to tell the KVM MMU whether a particular GFN should be backed by shared (normal) memory or private (gmem-allocated) memory. To tie into this, Page State Change requests are forward to userspace viaKVM_EXIT_VMGEXITexits, which will then issue the correspondingKVM_SET_MEMORY_ATTRIBUTEScall to set the private/shared state in the KVM MMU.The gmem / KVM MMU hooks implemented in this series will then update the RMP table entries for the backing PFNs to set them to guest-owned/private when mapping private pages into the guest via KVM MMU, or use the normal KVM MMU handling in the case of shared pages where the corresponding RMP table entries are left in the default shared/hypervisor-owned state.
Feedback/review is very much appreciated!
-Mike
Changes since v12:
* rebased to latest kvm-coco-queue branch (commit
4d2deb62185f)
* add more input validation forSNP_LAUNCH_START, especially for handling things like MBO/MBZ policy bits, and API major/minor minimums. (Paolo)
* block SNP KVM instances from being able to run legacy SEV commands (Paolo)
* don't attempt to measure VMSA for vcpu 0/BSP before the others, let userspace deal with the ordering just like with SEV-ES (Paolo)
* fix up docs forSNP_LAUNCH_FINISH(Paolo)
* introducesvm->sev_es.snp_has_guest_vmsaflag to better distinguish handling for guest-mapped vs non-guest-mapped VMSAs, rename 'snp_ap_create' flag to 'snp_ap_waiting_for_reset' (Paolo)
* drop "KVM: SEV: Use a VMSA physical address variable for populating VMCB" as it is no longer needed due to above VMSA rework
* replacepr_debug_ratelimited()messages for RMP #NPFs with a single trace event
* handle transientPSMASH_FAIL_INUSEreturn codes inkvm_gmem_invalidate(), switch to WARN_ON*()'s to indicate remaining error cases are not expected and should not be seen in practice. (Paolo)
* add acond_resched()inkvm_gmem_invalidate()to avoid soft lock-ups when cleaning up large guest memory ranges.
* renameVLEK_REQUIREDtoVCEK_DISABLE. it's be more applicable if another key type ever gets added.
* don't allow attestation to be paused while an attestation request is being processed by firmware (Tom)
* add missing Documentation entry forSNP_VLEK_LOAD
* collect Reviewed-by's from Paolo and TomChanges since v11:
* Rebase series on kvm-coco-queue and re-work to leverage more infrastructure between SNP/TDX series.
* DropKVM_SNP_INITin favor of the newKVM_SEV_INIT2interface introduced here (Paolo):https://lore.kernel.org/lkml/[email protected]/* Drop exposure API fields related to things like VMPL levels, migration agents, etc., until they are actually supported/used (Sean)
* ReworkKVM_SEV_SNP_LAUNCH_UPDATEhandling to use a newkvm_gmem_populate()interface instead of copying data directly into gmem-allocated pages (Sean)
* Add support forSNP_LOAD_VLEK, rework the SNP_SET_CONFIG_{START,END} to have simpler semantics that are applicable to management ofSNP_LOAD_VLEKupdates as well, rename interfaces to the now more appropriate SNP_{PAUSE,RESUME}_ATTESTATION
* Fix up documentation wording and do print warnings for userspace-triggerable failures (Peter, Sean)
* Fix a race withAP_CREATIONwake-up events (Jacob, Sean)
* Fix a memory leak with VMSA pages (Sean)
* Tighten up handling of RMP page faults to better distinguish between real and spurious cases (Tom)
* Various patch/documentation rewording, cleanups, etc.----------------------------------------------------------------
Ashish Kalra (1):
KVM: SEV: Avoid WBINVD for HVA-based MMU notifications for SNPBorislav Petkov (AMD) (3):
[TEMP] x86/kvm/Kconfig: HaveKVM_AMD_SEVselectARCH_HAS_CC_PLATFORM[TEMP] x86/cc: Add cc_platform_set/_clear() helpers [TEMP] x86/CPU/AMD: Track SNP host status with cc_platform_*()Brijesh Singh (10):
KVM: SEV: Add GHCB handling for Hypervisor Feature Support requests
KVM: SEV: AddKVM_SEV_SNP_LAUNCH_STARTcommand
KVM: SEV: AddKVM_SEV_SNP_LAUNCH_UPDATEcommand
KVM: SEV: AddKVM_SEV_SNP_LAUNCH_FINISHcommand
KVM: SEV: Add support to handle GHCB GPA register VMGEXIT
KVM: SEV: Add support to handle MSR based Page State Change VMGEXIT
KVM: SEV: Add support to handle Page State Change VMGEXIT
KVM: SEV: Add support to handle RMP nested page faults
KVM: SVM: Add module parameter to enable SEV-SNP
KVM: SEV: Provide support forSNP_GUEST_REQUESTNAE eventMichael Roth (10):
KVM:guest_memfd: FixPTR_ERR()handling in__kvm_gmem_get_pfn()
KVM: SEV: SelectKVM_GENERIC_PRIVATE_MEMwhenCONFIG_KVM_AMD_SEV=y
KVM: SEV: Add initial SEV-SNP support
KVM: SEV: Add support for GHCB-based termination requests
KVM: SEV: Implement gmem hook for initializing private pages
KVM: SEV: Implement gmem hook for invalidating private pages
KVM: x86: Implement gmem hook for determining max NPT mapping level
crypto: ccp: Add theSNP_VLEK_LOADcommand
crypto: ccp: Add the SNP_{PAUSE,RESUME}_ATTESTATION commands
KVM: SEV: Provide support forSNP_EXTENDED_GUEST_REQUESTNAE eventTom Lendacky (2):
KVM: SEV: Add support to handle AP reset MSR protocol
KVM: SEV: Support SEV-SNP AP Creation NAE eventDocumentation/virt/coco/sev-guest.rst | 69 +- Documentation/virt/kvm/api.rst | 73 + .../virt/kvm/x86/amd-memory-encryption.rst | 88 +- arch/x86/coco/core.c | 52 + arch/x86/include/asm/kvm_host.h | 2 + arch/x86/include/asm/sev-common.h | 22 +- arch/x86/include/asm/sev.h | 19 +- arch/x86/include/asm/svm.h | 9 +- arch/x86/include/uapi/asm/kvm.h | 39 + arch/x86/kernel/cpu/amd.c | 38 +- arch/x86/kernel/cpu/mtrr/generic.c | 2 +- arch/x86/kernel/sev.c | 10 - arch/x86/kvm/Kconfig | 4 + arch/x86/kvm/mmu.h | 2 - arch/x86/kvm/mmu/mmu.c | 1 + arch/x86/kvm/svm/sev.c | 1444 +++++++++++++++++++- arch/x86/kvm/svm/svm.c | 39 +- arch/x86/kvm/svm/svm.h | 50 + arch/x86/kvm/trace.h | 31 + arch/x86/kvm/x86.c | 19 +- arch/x86/virt/svm/sev.c | 106 +- drivers/crypto/ccp/sev-dev.c | 85 +- drivers/iommu/amd/init.c | 4 +- include/linux/cc_platform.h | 12 + include/linux/psp-sev.h | 4 +- include/uapi/linux/kvm.h | 28 + include/uapi/linux/psp-sev.h | 39 + include/uapi/linux/sev-guest.h | 9 + virt/kvm/guest_memfd.c | 8 +- 29 files changed, 2229 insertions(+), 79 deletions(-)