Skip to contentSkip to conversation

Add AMD SEV guest live migration support

PATCH Superseded Ashish Kalra 7 participants 73 messages Feb 4, 2021 – Apr 2, 2021

Subsystems (8)
Documentation x86 KVM KVM x86 +4 more subsystems

v10 of 13 Superseded Latest is v13 →

lore
Share & tools
lore
#0 lore raw

v10 cover Add AMD SEV guest live migration support

From: Ashish Kalra <[email protected]>

The series add support for AMD SEV guest live migration commands. To protect the confidentiality of an SEV protected guest memory while in transit we need to use the SEV commands defined in SEV API spec [1].

SEV guest VMs have the concept of private and shared memory. Private memory is encrypted with the guest-specific key, while shared memory may be encrypted with hypervisor key. The commands provided by the SEV FW are meant to be used for the private memory only. The patch series introduces a new hypercall. The guest OS can use this hypercall to notify the page encryption status. If the page is encrypted with guest specific-key then we use SEV command during the migration. If page is not encrypted then fallback to default.

The patch adds new ioctls KVM_{SET,GET}_SHARED_PAGES_LIST. The ioctl can be used by the qemu to get the shared pages list. Qemu can consult this list during the migration to know whether the page is encrypted.

This section descibes how the SEV live migration feature is negotiated between the host and guest, the host indicates this feature support via KVM_FEATURE_CPUID. The guest firmware (OVMF) detects this feature and sets a UEFI enviroment variable indicating OVMF support for live migration, the guest kernel also detects the host support for this feature via cpuid and in case of an EFI boot verifies if OVMF also supports this feature by getting the UEFI enviroment variable and if it set then enables live migration feature on host by writing to a custom MSR, if not booted under EFI, then it simply enables the feature by again writing to the custom MSR. The host returns error as part of KVM_GET_SHARED_PAGES_LIST ioctl if guest has not enabled live migration.

A branch containing these patches is available here:
https://github.com/AMDESE/linux/tree/sev-migration-v10

[1] https://developer.amd.com/wp-content/resources/55766.PDF

Changes since v9:
- Transitioning from page encryption bitmap to the shared pages list to keep track of guest's shared/unencrypted memory regions.
- Move back to marking the complete _bss_decrypted section as decrypted in the shared pages list.
- Invoke a new function check_kvm_sev_migration() via kvm_init_platform() for guest to query for host-side support for SEV live migration and to enable the SEV live migration feature, to avoid #ifdefs in code
- Rename MSR_KVM_SEV_LIVE_MIG_EN to MSR_KVM_SEV_LIVE_MIGRATION.
- Invoke a new function handle_unencrypted_region() from sev_dbg_crypt() to bypass unencrypted guest memory regions.

Changes since v8:
- Rebasing to kvm next branch.
- Fixed and added comments as per review feedback on v8 patches.
- Removed implicitly enabling live migration for incoming VMs in in KVM_SET_PAGE_ENC_BITMAP, it is now done via KVM_SET_MSR ioctl.
- Adds support for bypassing unencrypted guest memory regions for DBG_DECRYPT API calls, guest memory region encryption status in sev_dbg_decrypt() is referenced using the page encryption bitmap.

Changes since v7:
- Removed the hypervisor specific hypercall/paravirt callback for SEV live migration and moved back to calling kvm_sev_hypercall3 directly.
- Fix build errors as

build error when CONFIG_HYPERVISOR_GUEST=y and CONFIG_AMD_MEM_ENCRYPT=n.
- Implicitly enabled live migration for incoming VM(s) to handle A->B->C->... VM migrations.
- Fixed Documentation as per comments on v6 patches.
- Fixed error return path in sev_send_update_data() as per comments on v6 patches.

Changes since v6:
- Rebasing to mainline and refactoring to the new split SVM infrastructre.
- Move to static allocation of the unified Page Encryption bitmap instead of the dynamic resizing of the bitmap, the static allocation is done implicitly by extending kvm_arch_commit_memory_region() callack to add svm specific x86_ops which can read the userspace provided memory region/memslots and calculate the amount of guest RAM managed by the KVM and grow the bitmap.
- Fixed KVM_SET_PAGE_ENC_BITMAP ioctl to set the whole bitmap instead of simply clearing specific bits.
- Removed KVM_PAGE_ENC_BITMAP_RESET ioctl, which is now performed using KVM_SET_PAGE_ENC_BITMAP.
- Extended guest support for enabling Live Migration feature by adding a check for UEFI environment variable indicating OVMF support for Live Migration feature and additionally checking for KVM capability for the same feature. If not booted under EFI, then we simply check for KVM capability.
- Add hypervisor specific hypercall for SEV live migration by adding a new paravirt callback as part of x86_hyper_runtime.
(x86 hypervisor specific runtime callbacks)
- Moving MSR handling for MSR_KVM_SEV_LIVE_MIG_EN into svm/sev code and adding check for SEV live migration enabled by guest in the KVM_GET_PAGE_ENC_BITMAP ioctl.
- Instead of the complete __bss_decrypted section, only specific variables such as hv_clock_boot and wall_clock are marked as decrypted in the page encryption bitmap

Changes since v5:
- Fix build errors as

Changes since v4:
- Host support has been added to extend KVM capabilities/feature bits to include a new KVM_FEATURE_SEV_LIVE_MIGRATION, which the guest can query for host-side support for SEV live migration and a new custom MSR MSR_KVM_SEV_LIVE_MIG_EN is added for guest to enable the SEV live migration feature.
- Ensure that _bss_decrypted section is marked as decrypted in the page encryption bitmap.
- Fixing KVM_GET_PAGE_ENC_BITMAP ioctl to return the correct bitmap as per the number of pages being requested by the user. Ensure that we only copy bmap->num_pages bytes in the userspace buffer, if bmap->num_pages is not byte aligned we read the trailing bits from the userspace and copy those bits as is. This fixes guest page(s) corruption issues observed after migration completion.
- Add kexec support for SEV Live Migration to reset the host's page encryption bitmap related to kernel specific page encryption status settings before we load a new kernel by kexec. We cannot reset the complete page encryption bitmap here as we need to retain the UEFI/OVMF firmware specific settings.

Changes since v3:
- Rebasing to mainline and testing.
- Adding a new KVM_PAGE_ENC_BITMAP_RESET ioctl, which resets the page encryption bitmap on a guest reboot event.
- Adding a more reliable sanity check for GPA range being passed to the hypercall to ensure that guest MMIO ranges are also marked in the page encryption bitmap.

Changes since v2:
- reset the page encryption bitmap on vcpu reboot

Changes since v1:
- Add support to share the page encryption between the source and target machine.
- Fix review feedbacks from Tom Lendacky.
- Add check to limit the session blob length.
- Update KVM_GET_PAGE_ENC_BITMAP icotl to use the base_gfn instead of the memory slot when querying the bitmap.

Ashish Kalra (5):
KVM: x86: Introduce new KVM_FEATURE_SEV_LIVE_MIGRATION feature &
Custom MSR.
EFI: Introduce the new AMD Memory Encryption GUID.
KVM: x86: Add guest support for detecting and enabling SEV Live
Migration feature.
KVM: x86: Add kexec support for SEV Live Migration.
KVM: SVM: Bypass DBG_DECRYPT API calls for unencrypted guest memory.

Brijesh Singh (11):
KVM: SVM: Add KVM_SEV SEND_START command
KVM: SVM: Add KVM_SEND_UPDATE_DATA command
KVM: SVM: Add KVM_SEV_SEND_FINISH command
KVM: SVM: Add support for KVM_SEV_RECEIVE_START command
KVM: SVM: Add KVM_SEV_RECEIVE_UPDATE_DATA command
KVM: SVM: Add KVM_SEV_RECEIVE_FINISH command
KVM: x86: Add AMD SEV specific Hypercall3
KVM: X86: Introduce KVM_HC_PAGE_ENC_STATUS hypercall
mm: x86: Invoke hypercall when page encryption status is changed
KVM: x86: Introduce KVM_GET_SHARED_PAGES_LIST ioctl
KVM: x86: Introduce KVM_SET_SHARED_PAGES_LIST ioctl

 .../virt/kvm/amd-memory-encryption.rst        | 120 +++
 Documentation/virt/kvm/api.rst                |  44 +-
 Documentation/virt/kvm/cpuid.rst              |   5 +
 Documentation/virt/kvm/hypercalls.rst         |  15 +
 Documentation/virt/kvm/msr.rst                |  12 +
 arch/x86/include/asm/kvm_host.h               |   6 +
 arch/x86/include/asm/kvm_para.h               |  12 +
 arch/x86/include/asm/mem_encrypt.h            |   8 +
 arch/x86/include/asm/paravirt.h               |  10 +
 arch/x86/include/asm/paravirt_types.h         |   2 +
 arch/x86/include/uapi/asm/kvm_para.h          |   4 +
 arch/x86/kernel/kvm.c                         |  80 ++
 arch/x86/kernel/paravirt.c                    |   1 +
 arch/x86/kvm/svm/sev.c                        | 861 ++++++++++++++++++
 arch/x86/kvm/svm/svm.c                        |  20 +
 arch/x86/kvm/svm/svm.h                        |   9 +
 arch/x86/kvm/vmx/vmx.c                        |   1 +
 arch/x86/kvm/x86.c                            |  30 +
 arch/x86/mm/mem_encrypt.c                     |  98 +-
 arch/x86/mm/pat/set_memory.c                  |   7 +
 include/linux/efi.h                           |   1 +
 include/linux/psp-sev.h                       |   8 +-
 include/uapi/linux/kvm.h                      |  49 +
 include/uapi/linux/kvm_para.h                 |   1 +
 24 files changed, 1398 insertions(+), 6 deletions(-)
Signature · 1 line
2.17.1
#1 lore raw

PATCH v10 1/16 KVM: SVM: Add KVM_SEV SEND_START command

From: Brijesh Singh <[email protected]>

The command is used to create an outgoing SEV guest encryption context.

---
 .../virt/kvm/amd-memory-encryption.rst        |  27 ++++
 arch/x86/kvm/svm/sev.c                        | 125 ++++++++++++++++++
 include/linux/psp-sev.h                       |   8 +-
 include/uapi/linux/kvm.h                      |  12 ++
 4 files changed, 168 insertions(+), 4 deletions(-)
Documentation/virt/kvm/amd-memory-encryption.rst +27 -0
index 09a8f2a34e39..9f9896b72d36 100644
@@ -263,6 +263,33 @@ Returns: 0 on success, -negative on error
                 __u32 trans_len;
         };
 
+10. KVM_SEV_SEND_START
+----------------------
+
+The KVM_SEV_SEND_START command can be used by the hypervisor to create an
+outgoing guest encryption context.
+
+Parameters (in): struct kvm_sev_send_start
+
+Returns: 0 on success, -negative on error
+
+::
+        struct kvm_sev_send_start {
+                __u32 policy;                 /* guest policy */
+
+                __u64 pdh_cert_uaddr;         /* platform Diffie-Hellman certificate */
+                __u32 pdh_cert_len;
+
+                __u64 plat_certs_uaddr;        /* platform certificate chain */
+                __u32 plat_certs_len;
+
+                __u64 amd_certs_uaddr;        /* AMD certificate */
+                __u32 amd_certs_len;
+
+                __u64 session_uaddr;          /* Guest session information */
+                __u32 session_len;
+        };
+
 References
 ==========
 
arch/x86/kvm/svm/sev.c +125 -0

137 lines not on this page · raw message

include/linux/psp-sev.h +4 -4
index 49d155cd2dfe..454f35904d47 100644
@@ -325,11 +325,11 @@ struct sev_data_send_start {
 	u64 pdh_cert_address;			/* In */
 	u32 pdh_cert_len;			/* In */
 	u32 reserved1;
-	u64 plat_cert_address;			/* In */
-	u32 plat_cert_len;			/* In */
+	u64 plat_certs_address;			/* In */
+	u32 plat_certs_len;			/* In */
 	u32 reserved2;
-	u64 amd_cert_address;			/* In */
-	u32 amd_cert_len;			/* In */
+	u64 amd_certs_address;			/* In */
+	u32 amd_certs_len;			/* In */
 	u32 reserved3;
 	u64 session_address;			/* In */
 	u32 session_len;			/* In/Out */
include/uapi/linux/kvm.h +12 -0
index 374c67875cdb..8f538fd873f6 100644
@@ -1645,6 +1645,18 @@ struct kvm_sev_dbg {
 	__u32 len;
 };
 
+struct kvm_sev_send_start {
+	__u32 policy;
+	__u64 pdh_cert_uaddr;
+	__u32 pdh_cert_len;
+	__u64 plat_certs_uaddr;
+	__u32 plat_certs_len;
+	__u64 amd_certs_uaddr;
+	__u32 amd_certs_len;
+	__u64 session_uaddr;
+	__u32 session_len;
+};
+
 #define KVM_DEV_ASSIGN_ENABLE_IOMMU	(1 << 0)
 #define KVM_DEV_ASSIGN_PCI_2_3		(1 << 1)
 #define KVM_DEV_ASSIGN_MASK_INTX	(1 << 2)
Signature · 1 line
2.17.1
#2 lore raw

PATCH v10 2/16 KVM: SVM: Add KVM_SEND_UPDATE_DATA command

In reply to Ashish Kalra

From: Brijesh Singh <[email protected]>

The command is used for encrypting the guest memory region using the encryption context created with KVM_SEV_SEND_START.

Cc Thomas Gleixner <[email protected]>
Cc Ingo Molnar <[email protected]>
Cc "H. Peter Anvin" <[email protected]>
Cc Paolo Bonzini <[email protected]>
Cc "Radim Krčmář" <[email protected]>
Cc Joerg Roedel <[email protected]>
Cc Borislav Petkov <[email protected]>
Cc Tom Lendacky <[email protected]>
---
 .../virt/kvm/amd-memory-encryption.rst        |  24 ++++
 arch/x86/kvm/svm/sev.c                        | 122 ++++++++++++++++++
 include/uapi/linux/kvm.h                      |   9 ++
 3 files changed, 155 insertions(+)
Documentation/virt/kvm/amd-memory-encryption.rst +24 -0
index 9f9896b72d36..8bed1d801558 100644
@@ -290,6 +290,30 @@ Returns: 0 on success, -negative on error
                 __u32 session_len;
         };
 
+11. KVM_SEV_SEND_UPDATE_DATA
+----------------------------
+
+The KVM_SEV_SEND_UPDATE_DATA command can be used by the hypervisor to encrypt the
+outgoing guest memory region with the encryption context creating using
+KVM_SEV_SEND_START.
+
+Parameters (in): struct kvm_sev_send_update_data
+
+Returns: 0 on success, -negative on error
+
+::
+
+        struct kvm_sev_launch_send_update_data {
+                __u64 hdr_uaddr;        /* userspace address containing the packet header */
+                __u32 hdr_len;
+
+                __u64 guest_uaddr;      /* the source memory region to be encrypted */
+                __u32 guest_len;
+
+                __u64 trans_uaddr;      /* the destition memory region  */
+                __u32 trans_len;
+        };
+
 References
 ==========
 
arch/x86/kvm/svm/sev.c +122 -0

146 lines not on this page · raw message

include/uapi/linux/kvm.h +9 -0
index 8f538fd873f6..0ff7bed508fc 100644
@@ -1657,6 +1657,15 @@ struct kvm_sev_send_start {
 	__u32 session_len;
 };
 
+struct kvm_sev_send_update_data {
+	__u64 hdr_uaddr;
+	__u32 hdr_len;
+	__u64 guest_uaddr;
+	__u32 guest_len;
+	__u64 trans_uaddr;
+	__u32 trans_len;
+};
+
 #define KVM_DEV_ASSIGN_ENABLE_IOMMU	(1 << 0)
 #define KVM_DEV_ASSIGN_PCI_2_3		(1 << 1)
 #define KVM_DEV_ASSIGN_MASK_INTX	(1 << 2)
Signature · 1 line
2.17.1
#3 lore raw

PATCH v10 3/16 KVM: SVM: Add KVM_SEV_SEND_FINISH command

In reply to Ashish Kalra

From: Brijesh Singh <[email protected]>

The command is used to finailize the encryption context created with KVM_SEV_SEND_START command.

Cc Thomas Gleixner <[email protected]>
Cc Ingo Molnar <[email protected]>
Cc "H. Peter Anvin" <[email protected]>
Cc Paolo Bonzini <[email protected]>
Cc "Radim Krčmář" <[email protected]>
Cc Joerg Roedel <[email protected]>
Cc Borislav Petkov <[email protected]>
Cc Tom Lendacky <[email protected]>
---
 .../virt/kvm/amd-memory-encryption.rst        |  8 +++++++
 arch/x86/kvm/svm/sev.c                        | 23 +++++++++++++++++++
 2 files changed, 31 insertions(+)
Documentation/virt/kvm/amd-memory-encryption.rst +8 -0
index 8bed1d801558..0da0c199efa8 100644
@@ -314,6 +314,14 @@ Returns: 0 on success, -negative on error
                 __u32 trans_len;
         };
 
+12. KVM_SEV_SEND_FINISH
+------------------------
+
+After completion of the migration flow, the KVM_SEV_SEND_FINISH command can be
+issued by the hypervisor to delete the encryption context.
+
+Returns: 0 on success, -negative on error
+
 References
 ==========
 
arch/x86/kvm/svm/sev.c +23 -0
index 98e46ae1cba3..0d117b1e6491 100644
@@ -1279,6 +1279,26 @@ static int sev_send_update_data(struct kvm *kvm, struct kvm_sev_cmd *argp)
 	return ret;
 }
 
+static int sev_send_finish(struct kvm *kvm, struct kvm_sev_cmd *argp)
+{
+	struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
+	struct sev_data_send_finish *data;
+	int ret;
+
+	if (!sev_guest(kvm))
+		return -ENOTTY;
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	data->handle = sev->handle;
+	ret = sev_issue_cmd(kvm, SEV_CMD_SEND_FINISH, data, &argp->error);
+
+	kfree(data);
+	return ret;
+}
+
 int svm_mem_enc_op(struct kvm *kvm, void __user *argp)
 {
 	struct kvm_sev_cmd sev_cmd;
@@ -1335,6 +1355,9 @@ int svm_mem_enc_op(struct kvm *kvm, void __user *argp)
 	case KVM_SEV_SEND_UPDATE_DATA:
 		r = sev_send_update_data(kvm, &sev_cmd);
 		break;
+	case KVM_SEV_SEND_FINISH:
+		r = sev_send_finish(kvm, &sev_cmd);
+		break;
 	default:
 		r = -EINVAL;
 		goto out;
Signature · 1 line
2.17.1
#4 lore raw

PATCH v10 4/16 KVM: SVM: Add support for KVM_SEV_RECEIVE_START command

In reply to Ashish Kalra

From: Brijesh Singh <[email protected]>

The command is used to create the encryption context for an incoming SEV guest. The encryption context can be later used by the hypervisor to import the incoming data into the SEV guest memory space.

Cc Thomas Gleixner <[email protected]>
Cc Ingo Molnar <[email protected]>
Cc "H. Peter Anvin" <[email protected]>
Cc Paolo Bonzini <[email protected]>
Cc "Radim Krčmář" <[email protected]>
Cc Joerg Roedel <[email protected]>
Cc Borislav Petkov <[email protected]>
Cc Tom Lendacky <[email protected]>
---
 .../virt/kvm/amd-memory-encryption.rst        | 29 +++++++
 arch/x86/kvm/svm/sev.c                        | 81 +++++++++++++++++++
 include/uapi/linux/kvm.h                      |  9 +++
 3 files changed, 119 insertions(+)
Documentation/virt/kvm/amd-memory-encryption.rst +29 -0
index 0da0c199efa8..079ac5ac2459 100644
@@ -322,6 +322,35 @@ issued by the hypervisor to delete the encryption context.
 
 Returns: 0 on success, -negative on error
 
+13. KVM_SEV_RECEIVE_START
+------------------------
+
+The KVM_SEV_RECEIVE_START command is used for creating the memory encryption
+context for an incoming SEV guest. To create the encryption context, the user must
+provide a guest policy, the platform public Diffie-Hellman (PDH) key and session
+information.
+
+Parameters: struct  kvm_sev_receive_start (in/out)
+
+Returns: 0 on success, -negative on error
+
+::
+
+        struct kvm_sev_receive_start {
+                __u32 handle;           /* if zero then firmware creates a new handle */
+                __u32 policy;           /* guest's policy */
+
+                __u64 pdh_uaddr;        /* userspace address pointing to the PDH key */
+                __u32 pdh_len;
+
+                __u64 session_uaddr;    /* userspace address which points to the guest session information */
+                __u32 session_len;
+        };
+
+On success, the 'handle' field contains a new handle and on error, a negative value.
+
+For more details, see SEV spec Section 6.12.
+
 References
 ==========
 
arch/x86/kvm/svm/sev.c +81 -0

93 lines not on this page · raw message

include/uapi/linux/kvm.h +9 -0
index 0ff7bed508fc..d2eea75de8b3 100644
@@ -1666,6 +1666,15 @@ struct kvm_sev_send_update_data {
 	__u32 trans_len;
 };
 
+struct kvm_sev_receive_start {
+	__u32 handle;
+	__u32 policy;
+	__u64 pdh_uaddr;
+	__u32 pdh_len;
+	__u64 session_uaddr;
+	__u32 session_len;
+};
+
 #define KVM_DEV_ASSIGN_ENABLE_IOMMU	(1 << 0)
 #define KVM_DEV_ASSIGN_PCI_2_3		(1 << 1)
 #define KVM_DEV_ASSIGN_MASK_INTX	(1 << 2)
Signature · 1 line
2.17.1
#5 lore raw

PATCH v10 5/16 KVM: SVM: Add KVM_SEV_RECEIVE_UPDATE_DATA command

In reply to Ashish Kalra

From: Brijesh Singh <[email protected]>

The command is used for copying the incoming buffer into the SEV guest memory space.

Cc Thomas Gleixner <[email protected]>
Cc Ingo Molnar <[email protected]>
Cc "H. Peter Anvin" <[email protected]>
Cc Paolo Bonzini <[email protected]>
Cc "Radim Krčmář" <[email protected]>
Cc Joerg Roedel <[email protected]>
Cc Borislav Petkov <[email protected]>
Cc Tom Lendacky <[email protected]>
---
 .../virt/kvm/amd-memory-encryption.rst        | 24 ++++++
 arch/x86/kvm/svm/sev.c                        | 79 +++++++++++++++++++
 include/uapi/linux/kvm.h                      |  9 +++
 3 files changed, 112 insertions(+)
Documentation/virt/kvm/amd-memory-encryption.rst +24 -0
index 079ac5ac2459..da40be3d8bc2 100644
@@ -351,6 +351,30 @@ On success, the 'handle' field contains a new handle and on error, a negative va
 
 For more details, see SEV spec Section 6.12.
 
+14. KVM_SEV_RECEIVE_UPDATE_DATA
+----------------------------
+
+The KVM_SEV_RECEIVE_UPDATE_DATA command can be used by the hypervisor to copy
+the incoming buffers into the guest memory region with encryption context
+created during the KVM_SEV_RECEIVE_START.
+
+Parameters (in): struct kvm_sev_receive_update_data
+
+Returns: 0 on success, -negative on error
+
+::
+
+        struct kvm_sev_launch_receive_update_data {
+                __u64 hdr_uaddr;        /* userspace address containing the packet header */
+                __u32 hdr_len;
+
+                __u64 guest_uaddr;      /* the destination guest memory region */
+                __u32 guest_len;
+
+                __u64 trans_uaddr;      /* the incoming buffer memory region  */
+                __u32 trans_len;
+        };
+
 References
 ==========
 
arch/x86/kvm/svm/sev.c +79 -0

91 lines not on this page · raw message

include/uapi/linux/kvm.h +9 -0
index d2eea75de8b3..c4e195a4220f 100644
@@ -1675,6 +1675,15 @@ struct kvm_sev_receive_start {
 	__u32 session_len;
 };
 
+struct kvm_sev_receive_update_data {
+	__u64 hdr_uaddr;
+	__u32 hdr_len;
+	__u64 guest_uaddr;
+	__u32 guest_len;
+	__u64 trans_uaddr;
+	__u32 trans_len;
+};
+
 #define KVM_DEV_ASSIGN_ENABLE_IOMMU	(1 << 0)
 #define KVM_DEV_ASSIGN_PCI_2_3		(1 << 1)
 #define KVM_DEV_ASSIGN_MASK_INTX	(1 << 2)
Signature · 1 line
2.17.1
#6 lore raw

PATCH v10 6/16 KVM: SVM: Add KVM_SEV_RECEIVE_FINISH command

In reply to Ashish Kalra

From: Brijesh Singh <[email protected]>

The command finalize the guest receiving process and make the SEV guest ready for the execution.

Cc Thomas Gleixner <[email protected]>
Cc Ingo Molnar <[email protected]>
Cc "H. Peter Anvin" <[email protected]>
Cc Paolo Bonzini <[email protected]>
Cc "Radim Krčmář" <[email protected]>
Cc Joerg Roedel <[email protected]>
Cc Borislav Petkov <[email protected]>
Cc Tom Lendacky <[email protected]>
---
 .../virt/kvm/amd-memory-encryption.rst        |  8 +++++++
 arch/x86/kvm/svm/sev.c                        | 23 +++++++++++++++++++
 2 files changed, 31 insertions(+)
Documentation/virt/kvm/amd-memory-encryption.rst +8 -0
index da40be3d8bc2..1f7bbda1f971 100644
@@ -375,6 +375,14 @@ Returns: 0 on success, -negative on error
                 __u32 trans_len;
         };
 
+15. KVM_SEV_RECEIVE_FINISH
+------------------------
+
+After completion of the migration flow, the KVM_SEV_RECEIVE_FINISH command can be
+issued by the hypervisor to make the guest ready for execution.
+
+Returns: 0 on success, -negative on error
+
 References
 ==========
 
arch/x86/kvm/svm/sev.c +23 -0
index 73d5dbb72a65..25eaf35ba51d 100644
@@ -1453,6 +1453,26 @@ static int sev_receive_update_data(struct kvm *kvm, struct kvm_sev_cmd *argp)
 	return ret;
 }
 
+static int sev_receive_finish(struct kvm *kvm, struct kvm_sev_cmd *argp)
+{
+	struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
+	struct sev_data_receive_finish *data;
+	int ret;
+
+	if (!sev_guest(kvm))
+		return -ENOTTY;
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	data->handle = sev->handle;
+	ret = sev_issue_cmd(kvm, SEV_CMD_RECEIVE_FINISH, data, &argp->error);
+
+	kfree(data);
+	return ret;
+}
+
 int svm_mem_enc_op(struct kvm *kvm, void __user *argp)
 {
 	struct kvm_sev_cmd sev_cmd;
@@ -1518,6 +1538,9 @@ int svm_mem_enc_op(struct kvm *kvm, void __user *argp)
 	case KVM_SEV_RECEIVE_UPDATE_DATA:
 		r = sev_receive_update_data(kvm, &sev_cmd);
 		break;
+	case KVM_SEV_RECEIVE_FINISH:
+		r = sev_receive_finish(kvm, &sev_cmd);
+		break;
 	default:
 		r = -EINVAL;
 		goto out;
Signature · 1 line
2.17.1
#7 lore raw

PATCH v10 7/16 KVM: x86: Add AMD SEV specific Hypercall3

In reply to Ashish Kalra

From: Brijesh Singh <[email protected]>

KVM hypercall framework relies on alternative framework to patch the VMCALL -> VMMCALL on AMD platform. If a hypercall is made before apply_alternative() is called then it defaults to VMCALL. The approach works fine on non SEV guest. A VMCALL would causes #UD, and hypervisor will be able to decode the instruction and do the right things. But when SEV is active, guest memory is encrypted with guest key and hypervisor will not be able to decode the instruction bytes.

Add SEV specific hypercall3, it unconditionally uses VMMCALL. The hypercall will be used by the SEV guest to notify encrypted pages to the hypervisor.

---
 arch/x86/include/asm/kvm_para.h | 12 ++++++++++++
 1 file changed, 12 insertions(+)
arch/x86/include/asm/kvm_para.h +12 -0
index 338119852512..bc1b11d057fc 100644
@@ -85,6 +85,18 @@ static inline long kvm_hypercall4(unsigned int nr, unsigned long p1,
 	return ret;
 }
 
+static inline long kvm_sev_hypercall3(unsigned int nr, unsigned long p1,
+				      unsigned long p2, unsigned long p3)
+{
+	long ret;
+
+	asm volatile("vmmcall"
+		     : "=a"(ret)
+		     : "a"(nr), "b"(p1), "c"(p2), "d"(p3)
+		     : "memory");
+	return ret;
+}
+
 #ifdef CONFIG_KVM_GUEST
 bool kvm_para_available(void);
 unsigned int kvm_arch_para_features(void);
Signature · 1 line
2.17.1
#8 lore raw

PATCH v10 8/16 KVM: X86: Introduce KVM_HC_PAGE_ENC_STATUS hypercall

In reply to Ashish Kalra

From: Brijesh Singh <[email protected]>

This hypercall is used by the SEV guest to notify a change in the page encryption status to the hypervisor. The hypercall should be invoked only when the encryption attribute is changed from encrypted -> decrypted and vice versa. By default all guest pages are considered encrypted.

The patch introduces a new shared pages list implemented as a sorted linked list to track the shared/unencrypted regions marked by the guest hypercall.

Cc Thomas Gleixner <[email protected]>
Cc Ingo Molnar <[email protected]>
Cc "H. Peter Anvin" <[email protected]>
Cc Paolo Bonzini <[email protected]>
Cc "Radim Krčmář" <[email protected]>
Cc Joerg Roedel <[email protected]>
Cc Borislav Petkov <[email protected]>
Cc Tom Lendacky <[email protected]>
---
 Documentation/virt/kvm/hypercalls.rst |  15 +++
 arch/x86/include/asm/kvm_host.h       |   2 +
 arch/x86/kvm/svm/sev.c                | 150 ++++++++++++++++++++++++++
 arch/x86/kvm/svm/svm.c                |   2 +
 arch/x86/kvm/svm/svm.h                |   5 +
 arch/x86/kvm/vmx/vmx.c                |   1 +
 arch/x86/kvm/x86.c                    |   6 ++
 include/uapi/linux/kvm_para.h         |   1 +
 8 files changed, 182 insertions(+)
Documentation/virt/kvm/hypercalls.rst +15 -0
index ed4fddd364ea..7aff0cebab7c 100644
@@ -169,3 +169,18 @@ a0: destination APIC ID
 
 :Usage example: When sending a call-function IPI-many to vCPUs, yield if
 	        any of the IPI target vCPUs was preempted.
+
+
+8. KVM_HC_PAGE_ENC_STATUS
+-------------------------
+:Architecture: x86
+:Status: active
+:Purpose: Notify the encryption status changes in guest page table (SEV guest)
+
+a0: the guest physical address of the start page
+a1: the number of pages
+a2: encryption attribute
+
+   Where:
+	* 1: Encryption attribute is set
+	* 0: Encryption attribute is cleared
arch/x86/include/asm/kvm_host.h +2 -0
index 3d6616f6f6ef..2da5f5e2a10e 100644
@@ -1301,6 +1301,8 @@ struct kvm_x86_ops {
 	int (*complete_emulated_msr)(struct kvm_vcpu *vcpu, int err);
 
 	void (*vcpu_deliver_sipi_vector)(struct kvm_vcpu *vcpu, u8 vector);
+	int (*page_enc_status_hc)(struct kvm *kvm, unsigned long gpa,
+				  unsigned long sz, unsigned long mode);
 };
 
 struct kvm_x86_nested_ops {
arch/x86/kvm/svm/sev.c +150 -0

174 lines not on this page · raw message

arch/x86/kvm/svm/svm.c +2 -0
index f923e14e87df..bb249ec625fc 100644
@@ -4536,6 +4536,8 @@ static struct kvm_x86_ops svm_x86_ops __initdata = {
 	.complete_emulated_msr = svm_complete_emulated_msr,
 
 	.vcpu_deliver_sipi_vector = svm_vcpu_deliver_sipi_vector,
+
+	.page_enc_status_hc = svm_page_enc_status_hc,
 };
 
 static struct kvm_x86_init_ops svm_init_ops __initdata = {
arch/x86/kvm/svm/svm.h +5 -0
index 0fe874ae5498..6437c1fa1f24 100644
@@ -79,6 +79,9 @@ struct kvm_sev_info {
 	unsigned long pages_locked; /* Number of pages locked */
 	struct list_head regions_list;  /* List of registered regions */
 	u64 ap_jump_table;	/* SEV-ES AP Jump Table address */
+	/* List and count of shared pages */
+	int shared_pages_list_count;
+	struct list_head shared_pages_list;
 };
 
 struct kvm_svm {
@@ -472,6 +475,8 @@ int nested_svm_check_exception(struct vcpu_svm *svm, unsigned nr,
 			       bool has_error_code, u32 error_code);
 int nested_svm_exit_special(struct vcpu_svm *svm);
 void sync_nested_vmcb_control(struct vcpu_svm *svm);
+int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa,
+			   unsigned long npages, unsigned long enc);
 
 extern struct kvm_x86_nested_ops svm_nested_ops;
 
arch/x86/kvm/vmx/vmx.c +1 -0
index cc60b1fc3ee7..bcbf53851612 100644
@@ -7705,6 +7705,7 @@ static struct kvm_x86_ops vmx_x86_ops __initdata = {
 	.can_emulate_instruction = vmx_can_emulate_instruction,
 	.apic_init_signal_blocked = vmx_apic_init_signal_blocked,
 	.migrate_timers = vmx_migrate_timers,
+	.page_enc_status_hc = NULL,
 
 	.msr_filter_changed = vmx_msr_filter_changed,
 	.complete_emulated_msr = kvm_complete_insn_gp,
arch/x86/kvm/x86.c +6 -0
index 76bce832cade..2f17f0f9ace7 100644
@@ -8162,6 +8162,12 @@ int kvm_emulate_hypercall(struct kvm_vcpu *vcpu)
 		kvm_sched_yield(vcpu->kvm, a0);
 		ret = 0;
 		break;
+	case KVM_HC_PAGE_ENC_STATUS:
+		ret = -KVM_ENOSYS;
+		if (kvm_x86_ops.page_enc_status_hc)
+			ret = kvm_x86_ops.page_enc_status_hc(vcpu->kvm,
+					a0, a1, a2);
+		break;
 	default:
 		ret = -KVM_ENOSYS;
 		break;
include/uapi/linux/kvm_para.h +1 -0
index 8b86609849b9..847b83b75dc8 100644
@@ -29,6 +29,7 @@
 #define KVM_HC_CLOCK_PAIRING		9
 #define KVM_HC_SEND_IPI		10
 #define KVM_HC_SCHED_YIELD		11
+#define KVM_HC_PAGE_ENC_STATUS		12
 
 /*
  * hypercalls use architecture specific
Signature · 1 line
2.17.1
#9 lore raw

Re: [PATCH v10 08/16] KVM: X86: Introduce KVM_HC_PAGE_ENC_STATUS hypercall

Quoted 11 lines · Ashish Kalra

From: Brijesh Singh <[email protected]>

This hypercall is used by the SEV guest to notify a change in the page encryption status to the hypervisor. The hypercall should be invoked only when the encryption attribute is changed from encrypted -> decrypted and vice versa. By default all guest pages are considered encrypted.

The patch introduces a new shared pages list implemented as a sorted linked list to track the shared/unencrypted regions marked by the guest hypercall.

...

arch/x86/kvm/svm/sev.c static int sev_receive_finish(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 1619

Quoted 12 lines
arch/x86/kvm/svm/sev.c +12 -0
+
+	if (enc) {
+		ret = remove_shared_region(gfn_start, gfn_end,
+					   &sev->shared_pages_list);
+		if (ret != -ENOMEM)
+			sev->shared_pages_list_count += ret;
+	} else {
+		ret = add_shared_region(gfn_start, gfn_end,
+					&sev->shared_pages_list);
+		if (ret > 0)
+			sev->shared_pages_list_count++;
+	}

I would move the shared_pages_list_count updates into the add/remove functions and then just return 0 or a -EXXXX error code from those functions. It seems simpler than "adding" ret or checking for a greater than 0 return code.

Thanks,
Tom

Quoted 94 lines
arch/x86/kvm/svm/sev.c +6 -0
+
+	mutex_unlock(&kvm->lock);
+	return ret;
+}
+
  int svm_mem_enc_op(struct kvm *kvm, void __user *argp)
  {
  	struct kvm_sev_cmd sev_cmd;
@@ -1693,6 +1842,7 @@ void sev_vm_destroy(struct kvm *kvm)
 
  	sev_unbind_asid(kvm, sev->handle);
  	sev_asid_free(sev->asid);
+	sev->shared_pages_list_count = 0;
  }
 
  void __init sev_hardware_setup(void)
arch/x86/kvm/svm/svm.c +2 -0
index f923e14e87df..bb249ec625fc 100644
@@ -4536,6 +4536,8 @@ static struct kvm_x86_ops svm_x86_ops __initdata = {
  	.complete_emulated_msr = svm_complete_emulated_msr,
 
  	.vcpu_deliver_sipi_vector = svm_vcpu_deliver_sipi_vector,
+
+	.page_enc_status_hc = svm_page_enc_status_hc,
  };
 
  static struct kvm_x86_init_ops svm_init_ops __initdata = {
arch/x86/kvm/svm/svm.h +5 -0
index 0fe874ae5498..6437c1fa1f24 100644
@@ -79,6 +79,9 @@ struct kvm_sev_info {
  	unsigned long pages_locked; /* Number of pages locked */
  	struct list_head regions_list;  /* List of registered regions */
  	u64 ap_jump_table;	/* SEV-ES AP Jump Table address */
+	/* List and count of shared pages */
+	int shared_pages_list_count;
+	struct list_head shared_pages_list;
  };
 
  struct kvm_svm {
@@ -472,6 +475,8 @@ int nested_svm_check_exception(struct vcpu_svm *svm, unsigned nr,
  			       bool has_error_code, u32 error_code);
  int nested_svm_exit_special(struct vcpu_svm *svm);
  void sync_nested_vmcb_control(struct vcpu_svm *svm);
+int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa,
+			   unsigned long npages, unsigned long enc);
 
  extern struct kvm_x86_nested_ops svm_nested_ops;
 
arch/x86/kvm/vmx/vmx.c +1 -0
index cc60b1fc3ee7..bcbf53851612 100644
@@ -7705,6 +7705,7 @@ static struct kvm_x86_ops vmx_x86_ops __initdata = {
  	.can_emulate_instruction = vmx_can_emulate_instruction,
  	.apic_init_signal_blocked = vmx_apic_init_signal_blocked,
  	.migrate_timers = vmx_migrate_timers,
+	.page_enc_status_hc = NULL,
 
  	.msr_filter_changed = vmx_msr_filter_changed,
  	.complete_emulated_msr = kvm_complete_insn_gp,
arch/x86/kvm/x86.c +6 -0
index 76bce832cade..2f17f0f9ace7 100644
@@ -8162,6 +8162,12 @@ int kvm_emulate_hypercall(struct kvm_vcpu *vcpu)
  		kvm_sched_yield(vcpu->kvm, a0);
  		ret = 0;
  		break;
+	case KVM_HC_PAGE_ENC_STATUS:
+		ret = -KVM_ENOSYS;
+		if (kvm_x86_ops.page_enc_status_hc)
+			ret = kvm_x86_ops.page_enc_status_hc(vcpu->kvm,
+					a0, a1, a2);
+		break;
  	default:
  		ret = -KVM_ENOSYS;
  		break;
include/uapi/linux/kvm_para.h +1 -0
index 8b86609849b9..847b83b75dc8 100644
@@ -29,6 +29,7 @@
  #define KVM_HC_CLOCK_PAIRING		9
  #define KVM_HC_SEND_IPI		10
  #define KVM_HC_SCHED_YIELD		11
+#define KVM_HC_PAGE_ENC_STATUS		12
 
  /*
   * hypercalls use architecture specific
#10 lore raw

Re: [PATCH v10 08/16] KVM: X86: Introduce KVM_HC_PAGE_ENC_STATUS hypercall

In reply to Ashish Kalra
Quoted 126 lines · Ashish Kalra

From: Brijesh Singh <[email protected]>

This hypercall is used by the SEV guest to notify a change in the page encryption status to the hypervisor. The hypercall should be invoked only when the encryption attribute is changed from encrypted -> decrypted and vice versa. By default all guest pages are considered encrypted.

The patch introduces a new shared pages list implemented as a sorted linked list to track the shared/unencrypted regions marked by the guest hypercall.

Cc Thomas Gleixner <[email protected]>
Cc Ingo Molnar <[email protected]>
Cc "H. Peter Anvin" <[email protected]>
Cc Paolo Bonzini <[email protected]>
Cc "Radim Krčmář" <[email protected]>
Cc Joerg Roedel <[email protected]>
Cc Borislav Petkov <[email protected]>
Cc Tom Lendacky <[email protected]>
---
 Documentation/virt/kvm/hypercalls.rst |  15 +++
 arch/x86/include/asm/kvm_host.h       |   2 +
 arch/x86/kvm/svm/sev.c                | 150 ++++++++++++++++++++++++++
 arch/x86/kvm/svm/svm.c                |   2 +
 arch/x86/kvm/svm/svm.h                |   5 +
 arch/x86/kvm/vmx/vmx.c                |   1 +
 arch/x86/kvm/x86.c                    |   6 ++
 include/uapi/linux/kvm_para.h         |   1 +
 8 files changed, 182 insertions(+)
Documentation/virt/kvm/hypercalls.rst +15 -0
index ed4fddd364ea..7aff0cebab7c 100644
@@ -169,3 +169,18 @@ a0: destination APIC ID
 
 :Usage example: When sending a call-function IPI-many to vCPUs, yield if
                any of the IPI target vCPUs was preempted.
+
+
+8. KVM_HC_PAGE_ENC_STATUS
+-------------------------
+:Architecture: x86
+:Status: active
+:Purpose: Notify the encryption status changes in guest page table (SEV guest)
+
+a0: the guest physical address of the start page
+a1: the number of pages
+a2: encryption attribute
+
+   Where:
+       * 1: Encryption attribute is set
+       * 0: Encryption attribute is cleared
arch/x86/include/asm/kvm_host.h +2 -0
index 3d6616f6f6ef..2da5f5e2a10e 100644
@@ -1301,6 +1301,8 @@ struct kvm_x86_ops {
        int (*complete_emulated_msr)(struct kvm_vcpu *vcpu, int err);
 
        void (*vcpu_deliver_sipi_vector)(struct kvm_vcpu *vcpu, u8 vector);
+       int (*page_enc_status_hc)(struct kvm *kvm, unsigned long gpa,
+                                 unsigned long sz, unsigned long mode);
 };
 
 struct kvm_x86_nested_ops {
arch/x86/kvm/svm/sev.c +31 -0
index 25eaf35ba51d..55c628df5155 100644
@@ -45,6 +45,11 @@ struct enc_region {
        unsigned long size;
 };
 
+struct shared_region {
+       struct list_head list;
+       unsigned long gfn_start, gfn_end;
+};
+
 static int sev_flush_asids(void)
 {
        int ret, error = 0;
@@ -196,6 +201,8 @@ static int sev_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp)
        sev->active = true;
        sev->asid = asid;
        INIT_LIST_HEAD(&sev->regions_list);
+       INIT_LIST_HEAD(&sev->shared_pages_list);
+       sev->shared_pages_list_count = 0;
 
        return 0;
 
@@ -1473,6 +1480,148 @@ static int sev_receive_finish(struct kvm *kvm, struct kvm_sev_cmd *argp)
        return ret;
 }
 
+static int remove_shared_region(unsigned long start, unsigned long end,
+                               struct list_head *head)
+{
+       struct shared_region *pos;
+
+       list_for_each_entry(pos, head, list) {
+               if (pos->gfn_start == start &&
+                   pos->gfn_end == end) {
+                       list_del(&pos->list);
+                       kfree(pos);
+                       return -1;
+               } else if (start >= pos->gfn_start && end <= pos->gfn_end) {
+                       if (start == pos->gfn_start)
+                               pos->gfn_start = end + 1;
+                       else if (end == pos->gfn_end)
+                               pos->gfn_end = start - 1;
+                       else {
+                               /* Do a de-merge -- split linked list nodes */
+                               unsigned long tmp;
+                               struct shared_region *shrd_region;
+
+                               tmp = pos->gfn_end;
+                               pos->gfn_end = start-1;
+                               shrd_region = kzalloc(sizeof(*shrd_region), GFP_KERNEL_ACCOUNT);

arch/x86/kvm/svm/sev.c static int sev_receive_finish(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 1516

Quoted 10 lines
arch/x86/kvm/svm/sev.c +10 -0
+                               if (!shrd_region)
+                                       return -ENOMEM;
+                               shrd_region->gfn_start = end + 1;
+                               shrd_region->gfn_end = tmp;
+                               list_add(&shrd_region->list, &pos->list);
+                               return 1;
+                       }
+                       return 0;
+               }
+       }

This doesn't handle the case where the region being marked as encrypted is larger than than the unencrypted region under consideration, which (I believe) can happen with the current kexec handling (since it is oblivious to the prior state).
I would probably break this down into the "five cases": no intersection (skip), entry is completely contained (drop), entry completely contains the removed region (split), intersect start (chop), and intersect end (chop).

Quoted 21 lines
arch/x86/kvm/svm/sev.c +20 -0
+       return 0;
+}
+
+static int add_shared_region(unsigned long start, unsigned long end,
+                            struct list_head *shared_pages_list)
+{
+       struct list_head *head = shared_pages_list;
+       struct shared_region *shrd_region;
+       struct shared_region *pos;
+
+       if (list_empty(head)) {
+               shrd_region = kzalloc(sizeof(*shrd_region), GFP_KERNEL_ACCOUNT);
+               if (!shrd_region)
+                       return -ENOMEM;
+               shrd_region->gfn_start = start;
+               shrd_region->gfn_end = end;
+               list_add_tail(&shrd_region->list, head);
+               return 1;
+       }
+

arch/x86/kvm/svm/sev.c static int sev_receive_finish(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 1546

Quoted 10 lines
arch/x86/kvm/svm/sev.c +10 -0
+       /*
+        * Shared pages list is a sorted list in ascending order of
+        * guest PA's and also merges consecutive range of guest PA's
+        */
+       list_for_each_entry(pos, head, list) {
+               if (pos->gfn_end < start)
+                       continue;
+               /* merge consecutive guest PA(s) */
+               if (pos->gfn_start <= start && pos->gfn_end >= start) {
+                       pos->gfn_end = end;

I'm not sure this correctly avoids having duplicate overlapping elements in the list. It also doesn't merge consecutive contiguous regions. Current guest implementation should never call the hypercall with C=0 for the same region twice, without calling with c=1 in between, but this API should be compatible with that model.

The easiest pattern would probably be to:
1) find (or insert) the node that will contain the added region.
2) remove the contents of the added region from the tail (will typically do nothing).
3) merge the head of the tail into the current node, if the end of the current node matches the start of that head.

Quoted 12 lines
arch/x86/kvm/svm/sev.c +10 -0
+                       return 0;
+               }
+               break;
+       }
 
+       /*
+        * Add a new node, allocate nodes using GFP_KERNEL_ACCOUNT so that
+        * kernel memory can be tracked/throttled in case a
+        * malicious guest makes infinite number of hypercalls to
+        * exhaust host kernel memory and cause a DOS attack.
+        */

arch/x86/kvm/svm/sev.c static int sev_receive_finish(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 1565

Quoted 10 lines
arch/x86/kvm/svm/sev.c +9 -0
+       shrd_region = kzalloc(sizeof(*shrd_region), GFP_KERNEL_ACCOUNT);
+       if (!shrd_region)
+               return -ENOMEM;
+       shrd_region->gfn_start = start;
+       shrd_region->gfn_end = end;
+       list_add_tail(&shrd_region->list, &pos->list);
+       return 1;
 
+}
+

Thanks!
Steve

#11 lore raw

Re: [PATCH v10 08/16] KVM: X86: Introduce KVM_HC_PAGE_ENC_STATUS hypercall

Hello Steve,

Quoted 138 lines · Steve Rutherford
Quoted 126 lines · Ashish Kalra

From: Brijesh Singh <[email protected]>

This hypercall is used by the SEV guest to notify a change in the page encryption status to the hypervisor. The hypercall should be invoked only when the encryption attribute is changed from encrypted -> decrypted and vice versa. By default all guest pages are considered encrypted.

The patch introduces a new shared pages list implemented as a sorted linked list to track the shared/unencrypted regions marked by the guest hypercall.

Cc Thomas Gleixner <[email protected]>
Cc Ingo Molnar <[email protected]>
Cc "H. Peter Anvin" <[email protected]>
Cc Paolo Bonzini <[email protected]>
Cc "Radim Krčmář" <[email protected]>
Cc Joerg Roedel <[email protected]>
Cc Borislav Petkov <[email protected]>
Cc Tom Lendacky <[email protected]>
---
 Documentation/virt/kvm/hypercalls.rst |  15 +++
 arch/x86/include/asm/kvm_host.h       |   2 +
 arch/x86/kvm/svm/sev.c                | 150 ++++++++++++++++++++++++++
 arch/x86/kvm/svm/svm.c                |   2 +
 arch/x86/kvm/svm/svm.h                |   5 +
 arch/x86/kvm/vmx/vmx.c                |   1 +
 arch/x86/kvm/x86.c                    |   6 ++
 include/uapi/linux/kvm_para.h         |   1 +
 8 files changed, 182 insertions(+)
Documentation/virt/kvm/hypercalls.rst +15 -0
index ed4fddd364ea..7aff0cebab7c 100644
@@ -169,3 +169,18 @@ a0: destination APIC ID
 
 :Usage example: When sending a call-function IPI-many to vCPUs, yield if
                any of the IPI target vCPUs was preempted.
+
+
+8. KVM_HC_PAGE_ENC_STATUS
+-------------------------
+:Architecture: x86
+:Status: active
+:Purpose: Notify the encryption status changes in guest page table (SEV guest)
+
+a0: the guest physical address of the start page
+a1: the number of pages
+a2: encryption attribute
+
+   Where:
+       * 1: Encryption attribute is set
+       * 0: Encryption attribute is cleared
arch/x86/include/asm/kvm_host.h +2 -0
index 3d6616f6f6ef..2da5f5e2a10e 100644
@@ -1301,6 +1301,8 @@ struct kvm_x86_ops {
        int (*complete_emulated_msr)(struct kvm_vcpu *vcpu, int err);
 
        void (*vcpu_deliver_sipi_vector)(struct kvm_vcpu *vcpu, u8 vector);
+       int (*page_enc_status_hc)(struct kvm *kvm, unsigned long gpa,
+                                 unsigned long sz, unsigned long mode);
 };
 
 struct kvm_x86_nested_ops {
arch/x86/kvm/svm/sev.c +31 -0
index 25eaf35ba51d..55c628df5155 100644
@@ -45,6 +45,11 @@ struct enc_region {
        unsigned long size;
 };
 
+struct shared_region {
+       struct list_head list;
+       unsigned long gfn_start, gfn_end;
+};
+
 static int sev_flush_asids(void)
 {
        int ret, error = 0;
@@ -196,6 +201,8 @@ static int sev_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp)
        sev->active = true;
        sev->asid = asid;
        INIT_LIST_HEAD(&sev->regions_list);
+       INIT_LIST_HEAD(&sev->shared_pages_list);
+       sev->shared_pages_list_count = 0;
 
        return 0;
 
@@ -1473,6 +1480,148 @@ static int sev_receive_finish(struct kvm *kvm, struct kvm_sev_cmd *argp)
        return ret;
 }
 
+static int remove_shared_region(unsigned long start, unsigned long end,
+                               struct list_head *head)
+{
+       struct shared_region *pos;
+
+       list_for_each_entry(pos, head, list) {
+               if (pos->gfn_start == start &&
+                   pos->gfn_end == end) {
+                       list_del(&pos->list);
+                       kfree(pos);
+                       return -1;
+               } else if (start >= pos->gfn_start && end <= pos->gfn_end) {
+                       if (start == pos->gfn_start)
+                               pos->gfn_start = end + 1;
+                       else if (end == pos->gfn_end)
+                               pos->gfn_end = start - 1;
+                       else {
+                               /* Do a de-merge -- split linked list nodes */
+                               unsigned long tmp;
+                               struct shared_region *shrd_region;
+
+                               tmp = pos->gfn_end;
+                               pos->gfn_end = start-1;
+                               shrd_region = kzalloc(sizeof(*shrd_region), GFP_KERNEL_ACCOUNT);

arch/x86/kvm/svm/sev.c static int sev_receive_finish(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 1516

Quoted 10 lines
arch/x86/kvm/svm/sev.c +10 -0
+                               if (!shrd_region)
+                                       return -ENOMEM;
+                               shrd_region->gfn_start = end + 1;
+                               shrd_region->gfn_end = tmp;
+                               list_add(&shrd_region->list, &pos->list);
+                               return 1;
+                       }
+                       return 0;
+               }
+       }
Quoted 9 lines

This doesn't handle the case where the region being marked as encrypted is larger than than the unencrypted region under consideration, which (I believe) can happen with the current kexec handling (since it is oblivious to the prior state).
I would probably break this down into the "five cases": no intersection (skip), entry is completely contained (drop), entry completely contains the removed region (split), intersect start (chop), and intersect end (chop).

I believe that the above is already handling these cases :

1). no intersection (skip) : handled
2). entry is completely contained (drop) : handled
3). entry completely contains the removed region (split) : handled 4). intersect start in case of #3 (chop) : handled
5). intersect end in case of #3 (chop) : handled.

The one case it does not handle currently is where the region being marked as encrypted is larger than the unencrypted region under consideration.

Quoted 28 lines
Quoted 28 lines
arch/x86/kvm/svm/sev.c +27 -0
+       return 0;
+}
+
+static int add_shared_region(unsigned long start, unsigned long end,
+                            struct list_head *shared_pages_list)
+{
+       struct list_head *head = shared_pages_list;
+       struct shared_region *shrd_region;
+       struct shared_region *pos;
+
+       if (list_empty(head)) { > > +               shrd_region = kzalloc(sizeof(*shrd_region), GFP_KERNEL_ACCOUNT);
+               if (!shrd_region)
+                       return -ENOMEM;
+               shrd_region->gfn_start = start;
+               shrd_region->gfn_end = end;
+               list_add_tail(&shrd_region->list, head);
+               return 1;
+       }
+
+       /*
+        * Shared pages list is a sorted list in ascending order of
+        * guest PA's and also merges consecutive range of guest PA's
+        */
+       list_for_each_entry(pos, head, list) {
+               if (pos->gfn_end < start)
+                       continue;
+               /* merge consecutive guest PA(s) */
Quoted 8 lines

arch/x86/kvm/svm/sev.c static int sev_receive_finish(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 1546

Quoted 2 lines
arch/x86/kvm/svm/sev.c +2 -0
+               if (pos->gfn_start <= start && pos->gfn_end >= start) {
+                       pos->gfn_end = end;

I'm not sure this correctly avoids having duplicate overlapping elements in the list. It also doesn't merge consecutive contiguous regions. Current guest implementation should never call the hypercall with C=0 for the same region twice, without calling with c=1 in between, but this API should be compatible with that model.

Yes it does not handle duplicate overlapping elements being added in the list, i will fix that.

And merging consecutive contigious regions is supported, the code is merging consecutive GPAs as the example below shows :

...

[   50.894254] marking as unencrypted, gfn_start = c0000, gfn_end = fc000
[   50.894255] inserting new node @guest PA = c0000
[   50.894259] marking as unencrypted, gfn_start = fc000, gfn_end = fec00
[   50.894260] merging consecutive GPA start = c0000, end = fc000
[   50.894267] marking as unencrypted, gfn_start = fec00, gfn_end = fec01
[   50.894267] merging consecutive GPA start = c0000, end = fec00
[   50.894274] marking as unencrypted, gfn_start = fec01, gfn_end = fed00
[   50.894274] merging consecutive GPA start = c0000, end = fec01
[   50.894278] marking as unencrypted, gfn_start = fed00, gfn_end = fed01
[   50.894279] merging consecutive GPA start = c0000, end = fed00
[   50.894283] marking as unencrypted, gfn_start = fed00, gfn_end = fed1c
[   50.894283] merging consecutive GPA start = c0000, end = fed01
[   50.894287] marking as unencrypted, gfn_start = fed1c, gfn_end = fed20
[   50.894288] merging consecutive GPA start = c0000, end = fed1c
[   50.894294] marking as unencrypted, gfn_start = fed20, gfn_end = fee00
[   50.894294] merging consecutive GPA start = c0000, end = fed20
[   50.894303] marking as unencrypted, gfn_start = fee00, gfn_end = fef00
[   50.894304] merging consecutive GPA start = c0000, end = fee00
[   50.894669] marking as unencrypted, gfn_start = fef00, gfn_end = 1000000

...

The above is merged into a single entry, as shown below:

qemu-system-x86_64: gfn start = c0000, gfn_end = 1000000

Quoted 3 lines

The easiest pattern would probably be to:
1) find (or insert) the node that will contain the added region.

Except the find part, the above is handled.

Quoted 4 lines

2) remove the contents of the added region from the tail (will typically do nothing).
3) merge the head of the tail into the current node, if the end of the current node matches the start of that head.

This is also being handled.

Thanks,
Ashish

Quoted 25 lines
Quoted 12 lines
arch/x86/kvm/svm/sev.c +10 -0
+                       return 0;
+               }
+               break;
+       }
 
+       /*
+        * Add a new node, allocate nodes using GFP_KERNEL_ACCOUNT so that
+        * kernel memory can be tracked/throttled in case a
+        * malicious guest makes infinite number of hypercalls to
+        * exhaust host kernel memory and cause a DOS attack.
+        */

arch/x86/kvm/svm/sev.c static int sev_receive_finish(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 1565

Quoted 10 lines
arch/x86/kvm/svm/sev.c +9 -0
+       shrd_region = kzalloc(sizeof(*shrd_region), GFP_KERNEL_ACCOUNT);
+       if (!shrd_region)
+               return -ENOMEM;
+       shrd_region->gfn_start = start;
+       shrd_region->gfn_end = end;
+       list_add_tail(&shrd_region->list, &pos->list);
+       return 1;
 
+}
+

Thanks!
Steve

#12 lore raw

PATCH v10 9/16 mm: x86: Invoke hypercall when page encryption status is changed

In reply to Ashish Kalra

From: Brijesh Singh <[email protected]>

Invoke a hypercall when a memory region is changed from encrypted -> decrypted and vice versa. Hypervisor needs to know the page encryption status during the guest migration.

Cc Thomas Gleixner <[email protected]>
Cc Ingo Molnar <[email protected]>
Cc "H. Peter Anvin" <[email protected]>
Cc Paolo Bonzini <[email protected]>
Cc "Radim Krčmář" <[email protected]>
Cc Joerg Roedel <[email protected]>
Cc Borislav Petkov <[email protected]>
Cc Tom Lendacky <[email protected]>
---
 arch/x86/include/asm/paravirt.h       | 10 +++++
 arch/x86/include/asm/paravirt_types.h |  2 +
 arch/x86/kernel/paravirt.c            |  1 +
 arch/x86/mm/mem_encrypt.c             | 57 ++++++++++++++++++++++++++-
 arch/x86/mm/pat/set_memory.c          |  7 ++++
 5 files changed, 76 insertions(+), 1 deletion(-)
arch/x86/include/asm/paravirt.h +10 -0
index f8dce11d2bc1..1265e1f5db5f 100644
@@ -84,6 +84,12 @@ static inline void paravirt_arch_exit_mmap(struct mm_struct *mm)
 	PVOP_VCALL1(mmu.exit_mmap, mm);
 }
 
+static inline void page_encryption_changed(unsigned long vaddr, int npages,
+						bool enc)
+{
+	PVOP_VCALL3(mmu.page_encryption_changed, vaddr, npages, enc);
+}
+
 #ifdef CONFIG_PARAVIRT_XXL
 static inline void load_sp0(unsigned long sp0)
 {
@@ -829,6 +835,10 @@ static inline void paravirt_arch_dup_mmap(struct mm_struct *oldmm,
 static inline void paravirt_arch_exit_mmap(struct mm_struct *mm)
 {
 }
+
+static inline void page_encryption_changed(unsigned long vaddr, int npages, bool enc)
+{
+}
 #endif
 #endif /* __ASSEMBLY__ */
 #endif /* _ASM_X86_PARAVIRT_H */
arch/x86/include/asm/paravirt_types.h +2 -0
index b6b02b7c19cc..6a83821cf758 100644
@@ -208,6 +208,8 @@ struct pv_mmu_ops {
 
 	/* Hook for intercepting the destruction of an mm_struct. */
 	void (*exit_mmap)(struct mm_struct *mm);
+	void (*page_encryption_changed)(unsigned long vaddr, int npages,
+					bool enc);
 
 #ifdef CONFIG_PARAVIRT_XXL
 	struct paravirt_callee_save read_cr2;
arch/x86/kernel/paravirt.c +1 -0
index 6c3407ba6ee9..52913356b6fa 100644
@@ -340,6 +340,7 @@ struct paravirt_patch_template pv_ops = {
 			(void (*)(struct mmu_gather *, void *))tlb_remove_page,
 
 	.mmu.exit_mmap		= paravirt_nop,
+	.mmu.page_encryption_changed	= paravirt_nop,
 
 #ifdef CONFIG_PARAVIRT_XXL
 	.mmu.read_cr2		= __PV_IS_CALLEE_SAVE(native_read_cr2),
arch/x86/mm/mem_encrypt.c +56 -1

98 lines not on this page · raw message

arch/x86/mm/pat/set_memory.c +7 -0
index 16f878c26667..3576b583ac65 100644
@@ -27,6 +27,7 @@
 #include <asm/proto.h>
 #include <asm/memtype.h>
 #include <asm/set_memory.h>
+#include <asm/paravirt.h>
 
 #include "../mm_internal.h"
 
@@ -2012,6 +2013,12 @@ static int __set_memory_enc_dec(unsigned long addr, int numpages, bool enc)
 	 */
 	cpa_flush(&cpa, 0);
 
+	/* Notify hypervisor that a given memory range is mapped encrypted
+	 * or decrypted. The hypervisor will use this information during the
+	 * VM migration.
+	 */
+	page_encryption_changed(addr, numpages, enc);
+
 	return ret;
 }
 
Signature · 1 line
2.17.1
#13 lore raw

PATCH v10 10/16 KVM: x86: Introduce KVM_GET_SHARED_PAGES_LIST ioctl

In reply to Ashish Kalra

From: Brijesh Singh <[email protected]>

The ioctl is used to retrieve a guest's shared pages list.

Cc Thomas Gleixner <[email protected]>
Cc Ingo Molnar <[email protected]>
Cc "H. Peter Anvin" <[email protected]>
Cc Paolo Bonzini <[email protected]>
Cc "Radim Krčmář" <[email protected]>
Cc Joerg Roedel <[email protected]>
Cc Borislav Petkov <[email protected]>
Cc Tom Lendacky <[email protected]>
---
 Documentation/virt/kvm/api.rst  | 24 ++++++++++++++++
 arch/x86/include/asm/kvm_host.h |  2 ++
 arch/x86/kvm/svm/sev.c          | 49 +++++++++++++++++++++++++++++++++
 arch/x86/kvm/svm/svm.c          |  1 +
 arch/x86/kvm/svm/svm.h          |  1 +
 arch/x86/kvm/x86.c              | 12 ++++++++
 include/uapi/linux/kvm.h        |  9 ++++++
 7 files changed, 98 insertions(+)
Documentation/virt/kvm/api.rst +24 -0
index 99ceb978c8b0..59ef537c0cdd 100644
@@ -4677,6 +4677,30 @@ This ioctl resets VCPU registers and control structures according to
 the clear cpu reset definition in the POP. However, the cpu is not put
 into ESA mode. This reset is a superset of the initial reset.
 
+4.125 KVM_GET_SHARED_PAGES_LIST (vm ioctl)
+---------------------------------------
+
+:Capability: basic
+:Architectures: x86
+:Type: vm ioctl
+:Parameters: struct kvm_shared_pages_list (in/out)
+:Returns: 0 on success, -1 on error
+
+/* for KVM_GET_SHARED_PAGES_LIST */
+struct kvm_shared_pages_list {
+	int __user *pnents;
+	void __user *buffer;
+	__u32 size;
+};
+
+The encrypted VMs have the concept of private and shared pages. The private
+pages are encrypted with the guest-specific key, while the shared pages may
+be encrypted with the hypervisor key. The KVM_GET_SHARED_PAGES_LIST can
+be used to get guest's shared/unencrypted memory regions list.
+This list can be used during the guest migration. If the page
+is private then the userspace need to use SEV migration commands to transmit
+the page.
+
 
 4.125 KVM_S390_PV_COMMAND
 -------------------------
arch/x86/include/asm/kvm_host.h +2 -0
index 2da5f5e2a10e..cd354d830e13 100644
@@ -1303,6 +1303,8 @@ struct kvm_x86_ops {
 	void (*vcpu_deliver_sipi_vector)(struct kvm_vcpu *vcpu, u8 vector);
 	int (*page_enc_status_hc)(struct kvm *kvm, unsigned long gpa,
 				  unsigned long sz, unsigned long mode);
+	int (*get_shared_pages_list)(struct kvm *kvm,
+				     struct kvm_shared_pages_list *list);
 };
 
 struct kvm_x86_nested_ops {
arch/x86/kvm/svm/sev.c +49 -0

61 lines not on this page · raw message

arch/x86/kvm/svm/svm.c +1 -0
index bb249ec625fc..533ce47ff158 100644
@@ -4538,6 +4538,7 @@ static struct kvm_x86_ops svm_x86_ops __initdata = {
 	.vcpu_deliver_sipi_vector = svm_vcpu_deliver_sipi_vector,
 
 	.page_enc_status_hc = svm_page_enc_status_hc,
+	.get_shared_pages_list = svm_get_shared_pages_list,
 };
 
 static struct kvm_x86_init_ops svm_init_ops __initdata = {
arch/x86/kvm/svm/svm.h +1 -0
index 6437c1fa1f24..6a777c61373c 100644
@@ -477,6 +477,7 @@ int nested_svm_exit_special(struct vcpu_svm *svm);
 void sync_nested_vmcb_control(struct vcpu_svm *svm);
 int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa,
 			   unsigned long npages, unsigned long enc);
+int svm_get_shared_pages_list(struct kvm *kvm, struct kvm_shared_pages_list *list);
 
 extern struct kvm_x86_nested_ops svm_nested_ops;
 
arch/x86/kvm/x86.c +12 -0
index 2f17f0f9ace7..acfec2ae1402 100644
@@ -5719,6 +5719,18 @@ long kvm_arch_vm_ioctl(struct file *filp,
 	case KVM_X86_SET_MSR_FILTER:
 		r = kvm_vm_ioctl_set_msr_filter(kvm, argp);
 		break;
+	case KVM_GET_SHARED_PAGES_LIST: {
+		struct kvm_shared_pages_list list;
+
+		r = -EFAULT;
+		if (copy_from_user(&list, argp, sizeof(list)))
+			goto out;
+
+		r = -ENOTTY;
+		if (kvm_x86_ops.get_shared_pages_list)
+			r = kvm_x86_ops.get_shared_pages_list(kvm, &list);
+		break;
+	}
 	default:
 		r = -ENOTTY;
 	}
include/uapi/linux/kvm.h +9 -0
index c4e195a4220f..0529ba80498a 100644
@@ -544,6 +544,13 @@ struct kvm_clear_dirty_log {
 	};
 };
 
+/* for KVM_GET_SHARED_PAGES_LIST */
+struct kvm_shared_pages_list {
+	int __user *pnents;
+	void __user *buffer;
+	__u32 size;
+};
+
 /* for KVM_SET_SIGNAL_MASK */
 struct kvm_signal_mask {
 	__u32 len;
@@ -1565,6 +1572,8 @@ struct kvm_pv_cmd {
 /* Available with KVM_CAP_DIRTY_LOG_RING */
 #define KVM_RESET_DIRTY_RINGS		_IO(KVMIO, 0xc7)
 
+#define KVM_GET_SHARED_PAGES_LIST	_IOW(KVMIO, 0xc8, struct kvm_shared_pages_list)
+
 /* Secure Encrypted Virtualization command */
 enum sev_cmd_id {
 	/* Guest initialization commands */
Signature · 1 line
2.17.1
#14 lore raw

Re: [PATCH v10 10/16] KVM: x86: Introduce KVM_GET_SHARED_PAGES_LIST ioctl

Quoted 4 lines · Ashish Kalra

From: Brijesh Singh <[email protected]>

The ioctl is used to retrieve a guest's shared pages list.

...

Quoted 18 lines
arch/x86/kvm/svm/sev.c +17 -0
+int svm_get_shared_pages_list(struct kvm *kvm,
+			      struct kvm_shared_pages_list *list)
+{
+	struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
+	struct shared_region_array_entry *array;
+	struct shared_region *pos;
+	int ret, nents = 0;
+	unsigned long sz;
+
+	if (!sev_guest(kvm))
+		return -ENOTTY;
+
+	if (!list->size)
+		return -EINVAL;
+
+	if (!sev->shared_pages_list_count)
+		return put_user(0, list->pnents);

arch/x86/kvm/svm/sev.c int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa, · line 1656

Quoted 10 lines
arch/x86/kvm/svm/sev.c +10 -0
+
+	sz = sev->shared_pages_list_count * sizeof(struct shared_region_array_entry);
+	if (sz > list->size)
+		return -E2BIG;
+
+	array = kmalloc(sz, GFP_KERNEL);
+	if (!array)
+		return -ENOMEM;
+
+	mutex_lock(&kvm->lock);

I think this lock needs to be taken before the memory size is calculated. If the list is expanded after obtaining the size and before taking the lock, you will run off the end of the array.

Thanks,
Tom

Quoted 17 lines
arch/x86/kvm/svm/sev.c +17 -0
+	list_for_each_entry(pos, &sev->shared_pages_list, list) {
+		array[nents].gfn_start = pos->gfn_start;
+		array[nents++].gfn_end = pos->gfn_end;
+	}
+	mutex_unlock(&kvm->lock);
+
+	ret = -EFAULT;
+	if (copy_to_user(list->buffer, array, sz))
+		goto out;
+	if (put_user(nents, list->pnents))
+		goto out;
+	ret = 0;
+out:
+	kfree(array);
+	return ret;
+}
+
#15 lore raw

Re: [PATCH v10 10/16] KVM: x86: Introduce KVM_GET_SHARED_PAGES_LIST ioctl

Hello Tom,

Quoted 31 lines · Tom Lendacky
Quoted 4 lines · Ashish Kalra

From: Brijesh Singh <[email protected]>

The ioctl is used to retrieve a guest's shared pages list.

...

Quoted 23 lines
arch/x86/kvm/svm/sev.c +23 -0
+int svm_get_shared_pages_list(struct kvm *kvm,
+			      struct kvm_shared_pages_list *list)
+{
+	struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
+	struct shared_region_array_entry *array;
+	struct shared_region *pos;
+	int ret, nents = 0;
+	unsigned long sz;
+
+	if (!sev_guest(kvm))
+		return -ENOTTY;
+
+	if (!list->size)
+		return -EINVAL;
+
+	if (!sev->shared_pages_list_count)
+		return put_user(0, list->pnents);
+
+	sz = sev->shared_pages_list_count * sizeof(struct shared_region_array_entry);
+	if (sz > list->size)
+		return -E2BIG;
+
+	array = kmalloc(sz, GFP_KERNEL);
Quoted 9 lines

arch/x86/kvm/svm/sev.c int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa, · line 1656

Quoted 4 lines
arch/x86/kvm/svm/sev.c +4 -0
+	if (!array)
+		return -ENOMEM;
+
+	mutex_lock(&kvm->lock);

I think this lock needs to be taken before the memory size is calculated. If the list is expanded after obtaining the size and before taking the lock, you will run off the end of the array.

Yes, as the page encryption status hcalls can happen simultaneously to this ioctl, therefore this is an issue, so i will take the lock before the memory size is calculated.

Thanks,
Ashish

Quoted 18 lines
Quoted 18 lines
arch/x86/kvm/svm/sev.c +17 -0
+	list_for_each_entry(pos, &sev->shared_pages_list, list) {
+		array[nents].gfn_start = pos->gfn_start;
+		array[nents++].gfn_end = pos->gfn_end;
+	}
+	mutex_unlock(&kvm->lock);
+
+	ret = -EFAULT;
+	if (copy_to_user(list->buffer, array, sz))
+		goto out;
+	if (put_user(nents, list->pnents))
+		goto out;
+	ret = 0;
+out:
+	kfree(array);
+	return ret;
+}
+
#16 lore raw

Re: [PATCH v10 10/16] KVM: x86: Introduce KVM_GET_SHARED_PAGES_LIST ioctl

In reply to Ashish Kalra
Quoted 3 lines · Ashish Kalra

From: Brijesh Singh <[email protected]>

The ioctl is used to retrieve a guest's shared pages list.

What's the performance hit to boot time if KVM_HC_PAGE_ENC_STATUS is passed through to userspace? That way, userspace could manage the set of pages in whatever data structure they want, and these get/set ioctls go away.

Also, aren't there plans for an in-guest migration helper? If so, do we have any idea what that interface will look like? E.g. if we're going to end up with a full fledged driver in the guest, why not bite the bullet now and bypass KVM entirely?

#17 lore raw

RE: [PATCH v10 10/16] KVM: x86: Introduce KVM_GET_SHARED_PAGES_LIST ioctl

[AMD Public Use]

-----Original Message-----
From: Sean Christopherson <[email protected]>
Sent: Tuesday, February 16, 2021 7:03 PM
To: Kalra, Ashish <[email protected]>
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; Lendacky, Thomas <[email protected]>; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; Singh, Brijesh <[email protected]>
Subject: Re: [PATCH v10 10/16] KVM: x86: Introduce KVM_GET_SHARED_PAGES_LIST ioctl

Quoted 5 lines · Ashish Kalra

From: Brijesh Singh <[email protected]>

The ioctl is used to retrieve a guest's shared pages list.

What's the performance hit to boot time if KVM_HC_PAGE_ENC_STATUS is passed through to userspace? That way, userspace could manage the set of pages >in whatever data structure they want, and these get/set ioctls go away.

What is the advantage of passing KVM_HC_PAGE_ENC_STATUS through to user-space ?

As such it is just a simple interface to get the shared page list via the get/set ioctl's. simply an array is passed to these ioctl to get/set the shared pages list.

Quoted 1 line

Also, aren't there plans for an in-guest migration helper? If so, do we have any idea what that interface will look like? E.g. if we're going to end up with a full >fledged driver in the guest, why not bite the bullet now and bypass KVM entirely?

Even the in-guest migration helper will be using page encryption status hypercalls, so some interface is surely required.

Also the in-guest migration will be mainly an OVMF component, won't really be a full fledged kernel driver in the guest.

Thanks,
Ashish

#18 lore raw

Re: [PATCH v10 10/16] KVM: x86: Introduce KVM_GET_SHARED_PAGES_LIST ioctl

Quoted 10 lines · Ashish

From: Sean Christopherson <[email protected]>

Quoted 7 lines · Ashish Kalra

From: Brijesh Singh <[email protected]>

The ioctl is used to retrieve a guest's shared pages list.

What's the performance hit to boot time if KVM_HC_PAGE_ENC_STATUS is passed through to userspace? That way, userspace could manage the set of pages >in whatever data structure they want, and these get/set ioctls go away.

Quoted 6 lines

What is the advantage of passing KVM_HC_PAGE_ENC_STATUS through to user-space ?

As such it is just a simple interface to get the shared page list via the get/set ioctl's. simply an array is passed to these ioctl to get/set the shared pages list.

It eliminates any probability of the kernel choosing the wrong data structure, and it's two fewer ioctls to maintain and test.

Quoted 7 lines
Quoted 4 lines

Also, aren't there plans for an in-guest migration helper? If so, do we have any idea what that interface will look like? E.g. if we're going to end up with a full >fledged driver in the guest, why not bite the bullet now and bypass KVM entirely?

Even the in-guest migration helper will be using page encryption status hypercalls, so some interface is surely required.

If it's a driver with a more extensive interace, then the hypercalls can be replaced by a driver operation. That's obviously a big if, though.

Quoted 2 lines

Also the in-guest migration will be mainly an OVMF component, won't really be a full fledged kernel driver in the guest.

Is there code and/or a description of what the proposed helper would look like?

#19 lore raw

RE: [PATCH v10 10/16] KVM: x86: Introduce KVM_GET_SHARED_PAGES_LIST ioctl

[AMD Public Use]

-----Original Message-----
From: Sean Christopherson <[email protected]>
Sent: Wednesday, February 17, 2021 10:13 AM
To: Kalra, Ashish <[email protected]>
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; Lendacky, Thomas <[email protected]>; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; Singh, Brijesh <[email protected]>
Subject: Re: [PATCH v10 10/16] KVM: x86: Introduce KVM_GET_SHARED_PAGES_LIST ioctl

Quoted 10 lines · Ashish
Quoted 10 lines

From: Sean Christopherson <[email protected]> On Thu, Feb 04, 2021,

Quoted 7 lines · Ashish Kalra

From: Brijesh Singh <[email protected]>

The ioctl is used to retrieve a guest's shared pages list.

What's the performance hit to boot time if KVM_HC_PAGE_ENC_STATUS is passed through to userspace? That way, userspace could manage the
set of pages >in whatever data structure they want, and these get/set ioctls go away.

Quoted 8 lines
Quoted 6 lines

What is the advantage of passing KVM_HC_PAGE_ENC_STATUS through to user-space ?

As such it is just a simple interface to get the shared page list via the get/set ioctl's. simply an array is passed to these ioctl to get/set the shared pages list.

It eliminates any probability of the kernel choosing the wrong data structure, and it's two fewer ioctls to maintain and test.

The set shared pages list ioctl cannot be avoided as it needs to be issued to setup the shared pages list on the migrated VM, it cannot be achieved by passing KVM_HC_PAGE_ENC_STATUS through to user-space.

So it makes sense to add both get/set shared pages list ioctl, passing through to user-space is just adding more complexity without any significant gains.

Quoted 8 lines
Quoted 4 lines

Also, aren't there plans for an in-guest migration helper? If so, do we have any idea what that interface will look like? E.g. if we're going to end up with a full >fledged driver in the guest, why not bite the bullet now and bypass KVM entirely?

Even the in-guest migration helper will be using page encryption status hypercalls, so some interface is surely required.

Quoted 6 lines

If it's a driver with a more extensive interace, then the hypercalls can be replaced by a driver operation. That's obviously a big if, though.

Also the in-guest migration will be mainly an OVMF component, won't really be a full fledged kernel driver in the guest.

Is there code and/or a description of what the proposed helper would look like?

Not right now, there are prototype(s) under development, I assume they will be posted upstream soon.

Thanks,
Ashish

#20 lore raw

Re: [PATCH v10 10/16] KVM: x86: Introduce KVM_GET_SHARED_PAGES_LIST ioctl

Quoted 20 lines · Ashish

From: Sean Christopherson <[email protected]>

Quoted 16 lines · Ashish
Quoted 16 lines

From: Sean Christopherson <[email protected]> On Thu, Feb 04, 2021,

Quoted 7 lines · Ashish Kalra

From: Brijesh Singh <[email protected]>

The ioctl is used to retrieve a guest's shared pages list.

What's the performance hit to boot time if KVM_HC_PAGE_ENC_STATUS is passed through to userspace? That way, userspace could manage the
set of pages >in whatever data structure they want, and these get/set ioctls go away.

What is the advantage of passing KVM_HC_PAGE_ENC_STATUS through to user-space ?

As such it is just a simple interface to get the shared page list via the get/set ioctl's. simply an array is passed to these ioctl to get/set the shared pages list.

Quoted 6 lines
Quoted 2 lines

It eliminates any probability of the kernel choosing the wrong data structure, and it's two fewer ioctls to maintain and test.

The set shared pages list ioctl cannot be avoided as it needs to be issued to setup the shared pages list on the migrated VM, it cannot be achieved by passing KVM_HC_PAGE_ENC_STATUS through to user-space.

Why's that? AIUI, KVM doesn't do anything with the list other than pass it back to userspace. Assuming that's the case, userspace can just hold onto the list for the next migration.

Quoted 3 lines

So it makes sense to add both get/set shared pages list ioctl, passing through to user-space is just adding more complexity without any significant gains.

No, it reduces complexity for KVM by avoiding locking and memslot dependencies.

#21 lore raw

RE: [PATCH v10 10/16] KVM: x86: Introduce KVM_GET_SHARED_PAGES_LIST ioctl

[AMD Public Use]

-----Original Message-----
From: Sean Christopherson <[email protected]>
Sent: Thursday, February 18, 2021 10:39 AM
To: Kalra, Ashish <[email protected]>
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; Lendacky, Thomas <[email protected]>; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; Singh, Brijesh <[email protected]>
Subject: Re: [PATCH v10 10/16] KVM: x86: Introduce KVM_GET_SHARED_PAGES_LIST ioctl

Quoted 20 lines · Ashish

From: Sean Christopherson <[email protected]>

Quoted 17 lines · Ashish
Quoted 16 lines

From: Sean Christopherson <[email protected]> On Thu, Feb 04, 2021,

Quoted 7 lines · Ashish Kalra

From: Brijesh Singh <[email protected]>

The ioctl is used to retrieve a guest's shared pages list.

What's the performance hit to boot time if KVM_HC_PAGE_ENC_STATUS
is passed through to userspace? That way, userspace could manage
the set of pages >in whatever data structure they want, and these get/set ioctls go away.

What is the advantage of passing KVM_HC_PAGE_ENC_STATUS through to user-space ?

As such it is just a simple interface to get the shared page list via the get/set ioctl's. simply an array is passed to these ioctl to get/set the shared pages list.

Quoted 8 lines
Quoted 6 lines
Quoted 2 lines

It eliminates any probability of the kernel choosing the wrong data structure, and it's two fewer ioctls to maintain and test.

The set shared pages list ioctl cannot be avoided as it needs to be issued to setup the shared pages list on the migrated VM, it cannot be achieved by passing KVM_HC_PAGE_ENC_STATUS through to user-space.

Why's that? AIUI, KVM doesn't do anything with the list other than pass it back to userspace. Assuming that's the case, userspace can just hold onto the list >for the next migration.

KVM does use it as part of the SEV DBG_DECTYPT API, within sev_dbg_decrypt() to check if the guest page(s) are encrypted or not, and accordingly use it to decide whether to decrypt the guest page(s) and return that back to user-space or just return it as it is.

Thanks,
Ashish

#22 lore raw

Re: [PATCH v10 10/16] KVM: x86: Introduce KVM_GET_SHARED_PAGES_LIST ioctl

Quoted 30 lines · Ashish

From: Sean Christopherson <[email protected]>

Quoted 26 lines · Ashish

From: Sean Christopherson <[email protected]>

Quoted 23 lines · Ashish
Quoted 13 lines

From: Sean Christopherson <[email protected]> On Thu, Feb 04, 2021,

Quoted 7 lines · Ashish Kalra

From: Brijesh Singh <[email protected]>

The ioctl is used to retrieve a guest's shared pages list.

What's the performance hit to boot time if KVM_HC_PAGE_ENC_STATUS
is passed through to userspace? That way, userspace could manage
the set of pages >in whatever data structure they want, and these get/set ioctls go away.

What is the advantage of passing KVM_HC_PAGE_ENC_STATUS through to user-space ?

Quoted 6 lines

As such it is just a simple interface to get the shared page list via the get/set ioctl's. simply an array is passed to these ioctl to get/set the shared pages list.

It eliminates any probability of the kernel choosing the wrong data structure, and it's two fewer ioctls to maintain and test.

The set shared pages list ioctl cannot be avoided as it needs to be issued to setup the shared pages list on the migrated VM, it cannot be achieved by passing KVM_HC_PAGE_ENC_STATUS through to user-space.

Quoted 8 lines
Quoted 3 lines

Why's that? AIUI, KVM doesn't do anything with the list other than pass it back to userspace. Assuming that's the case, userspace can just hold onto the list >for the next migration.

KVM does use it as part of the SEV DBG_DECTYPT API, within sev_dbg_decrypt() to check if the guest page(s) are encrypted or not, and accordingly use it to decide whether to decrypt the guest page(s) and return that back to user-space or just return it as it is.

Why is handling shared memory KVM's responsibility? Userspace shouldn't be asking KVM to decrypt memory it knows isn't encrypted. My understanding is that bogus decryption won't harm the kernel, it will only corrupt the guest. In other words, patch 16 can be dropped if managing the set of shared pages is punted to userspace.

#23 lore raw

RE: [PATCH v10 10/16] KVM: x86: Introduce KVM_GET_SHARED_PAGES_LIST ioctl

In reply to Sean Christopherson

[AMD Public Use]

-----Original Message-----
From: Sean Christopherson <[email protected]>
Sent: Tuesday, February 16, 2021 7:03 PM
To: Kalra, Ashish <[email protected]>
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; Lendacky, Thomas <[email protected]>; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; Singh, Brijesh <[email protected]>
Subject: Re: [PATCH v10 10/16] KVM: x86: Introduce KVM_GET_SHARED_PAGES_LIST ioctl

Quoted 5 lines · Ashish Kalra

From: Brijesh Singh <[email protected]>

The ioctl is used to retrieve a guest's shared pages list.

What's the performance hit to boot time if KVM_HC_PAGE_ENC_STATUS is passed through to userspace? That way, userspace could manage the set of pages >in whatever data structure they want, and these get/set ioctls go away.

I will be more concerned about performance hit during guest DMA I/O if the page encryption status hypercalls are passed through to user-space, a lot of guest DMA I/O dynamically sets up pages for encryption and then flips them at DMA completion, so guest I/O will surely take a performance hit with this pass-through stuff.

Thanks,
Ashish

#24 lore raw

Re: [PATCH v10 10/16] KVM: x86: Introduce KVM_GET_SHARED_PAGES_LIST ioctl

Quoted 13 lines · Ashish

[AMD Public Use]

-----Original Message-----
From: Sean Christopherson <[email protected]>
Sent: Tuesday, February 16, 2021 7:03 PM
To: Kalra, Ashish <[email protected]>
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; Lendacky, Thomas <[email protected]>; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; Singh, Brijesh <[email protected]>
Subject: Re: [PATCH v10 10/16] KVM: x86: Introduce KVM_GET_SHARED_PAGES_LIST ioctl

Quoted 2 lines · Ashish Kalra

From: Brijesh Singh <[email protected]>

Quoted 8 lines
Quoted 3 lines

The ioctl is used to retrieve a guest's shared pages list.

What's the performance hit to boot time if KVM_HC_PAGE_ENC_STATUS is passed through to userspace? That way, userspace could manage the set of pages >in whatever data structure they want, and these get/set ioctls go away.

I will be more concerned about performance hit during guest DMA I/O if the page encryption status hypercalls are passed through to user-space, a lot of guest DMA I/O dynamically sets up pages for encryption and then flips them at DMA completion, so guest I/O will surely take a performance hit with this pass-through stuff.

Here are some rough performance numbers comparing # of heavy-weight VMEXITs compared to # of hypercalls, during a SEV guest boot: (launch of a ubuntu 18.04 guest)

# ./perf record -e kvm:kvm_userspace_exit -e kvm:kvm_hypercall -a ./qemu-system-x86_64 -enable-kvm -cpu host -machine q35 -smp 16,maxcpus=64 -m 512M -drive if=pflash,format=raw,unit=0,file=/home/ashish/sev-migration/qemu-5.1.50/OVMF_CODE.fd,readonly -drive if=pflash,format=raw,unit=1,file=OVMF_VARS.fd -drive file=../ubuntu-18.04.qcow2,if=none,id=disk0,format=qcow2 -device virtio-scsi-pci,id=scsi,disable-legacy=on,iommu_platform=true -device scsi-hd,drive=disk0 -object sev-guest,id=sev0,cbitpos=47,reduced-phys-bits=1,policy=0x0 -machine memory-encryption=sev0 -trace events=/tmp/events -nographic -monitor pty -monitor unix:monitor-source,server,nowait -qmp unix:/tmp/qmp-sock,server,nowait -device virtio-rng-pci,disable-legacy=on,iommu_platform=true

...
...

root@diesel2540:/home/ashish/sev-migration/qemu-5.1.50# ./perf report
# To display the perf.data header info, please use --header/--header-only options.
#
#
# Total Lost Samples: 0
#
# Samples: 981K of event 'kvm:kvm_userspace_exit'
# Event count (approx.): 981021
#
# Overhead  Command          Shared Object     Symbol
# ........  ...............  ................  ..................
#
   100.00%  qemu-system-x86  [kernel.vmlinux]  [k] kvm_vcpu_ioctl

# Samples: 19K of event 'kvm:kvm_hypercall'
# Event count (approx.): 19573
#

# Overhead  Command          Shared Object     Symbol
# ........  ...............  ................  .........................

#
100.00% qemu-system-x86 [kernel.vmlinux] [k] kvm_emulate_hypercall

Out of these 19573 hypercalls, # of page encryption status hcalls are 19479, so almost all hypercalls here are page encryption status hypercalls.

The above data indicates that there will be ~2% more Heavyweight VMEXITs during SEV guest boot if we do page encryption status hypercalls pass-through to host userspace.

But, then Brijesh pointed out to me and highlighted that currently
OVMF is doing lot of VMEXITs because they don't use the DMA pool to minimize the C-bit toggles, in other words, OVMF bounce buffer does page state change on every DMA allocate and free.

So here is the performance analysis after kernel and initrd have been loaded into memory using grub and then starting perf just before booting the kernel.

These are the performance #'s after kernel and initrd have been loaded into memory, then perf is attached and kernel is booted :

# Samples: 1M of event 'kvm:kvm_userspace_exit'
# Event count (approx.): 1081235
#
# Overhead Trace output

# ........  ........................
#
    99.77%  reason KVM_EXIT_IO (2)
     0.23%  reason KVM_EXIT_MMIO (6)

# Samples: 1K of event 'kvm:kvm_hypercall'
# Event count (approx.): 1279
#

So as the above data indicates, Linux is only making ~1K hypercalls, compared to ~18K hypercalls made by OVMF in the above use case.

Does the above adds a prerequisite that OVMF needs to be optimized if and before hypercall pass-through can be done ?

Thanks,
Ashish

#25 lore raw

Re: [PATCH v10 10/16] KVM: x86: Introduce KVM_GET_SHARED_PAGES_LIST ioctl

Quoted 10 lines · Ashish Kalra

# Samples: 19K of event 'kvm:kvm_hypercall'
# Event count (approx.): 19573
#

# Overhead  Command          Shared Object     Symbol
# ........  ...............  ................  .........................

#
100.00% qemu-system-x86 [kernel.vmlinux] [k] kvm_emulate_hypercall

Out of these 19573 hypercalls, # of page encryption status hcalls are 19479, so almost all hypercalls here are page encryption status hypercalls.

Oof.

Quoted 27 lines

The above data indicates that there will be ~2% more Heavyweight VMEXITs during SEV guest boot if we do page encryption status hypercalls pass-through to host userspace.

But, then Brijesh pointed out to me and highlighted that currently
OVMF is doing lot of VMEXITs because they don't use the DMA pool to minimize the C-bit toggles, in other words, OVMF bounce buffer does page state change on every DMA allocate and free.

So here is the performance analysis after kernel and initrd have been loaded into memory using grub and then starting perf just before booting the kernel.

These are the performance #'s after kernel and initrd have been loaded into memory, then perf is attached and kernel is booted :

# Samples: 1M of event 'kvm:kvm_userspace_exit'
# Event count (approx.): 1081235
#
# Overhead Trace output

# ........  ........................
#
    99.77%  reason KVM_EXIT_IO (2)
     0.23%  reason KVM_EXIT_MMIO (6)

# Samples: 1K of event 'kvm:kvm_hypercall'
# Event count (approx.): 1279
#

Quoted 5 lines

So as the above data indicates, Linux is only making ~1K hypercalls, compared to ~18K hypercalls made by OVMF in the above use case.

Does the above adds a prerequisite that OVMF needs to be optimized if and before hypercall pass-through can be done ?

Disclaimer: my math could be totally wrong.

I doubt it's a hard requirement. Assuming a conversative roundtrip time of 50k cycles, those 18K hypercalls will add well under a 1/2 a second of boot time. If userspace can push the roundtrip time down to 10k cycles, the overhead is more like 50 milliseconds.

That being said, this does seem like a good OVMF cleanup, irrespective of this new hypercall. I assume it's not cheap to convert a page between encrypted and decrypted.

Thanks much for getting the numbers!

#26 lore raw

Re: [PATCH v10 10/16] KVM: x86: Introduce KVM_GET_SHARED_PAGES_LIST ioctl

Quoted 54 lines · Sean Christopherson
Quoted 10 lines · Ashish Kalra

# Samples: 19K of event 'kvm:kvm_hypercall'
# Event count (approx.): 19573
#

# Overhead  Command          Shared Object     Symbol
# ........  ...............  ................  .........................

#
100.00% qemu-system-x86 [kernel.vmlinux] [k] kvm_emulate_hypercall

Out of these 19573 hypercalls, # of page encryption status hcalls are 19479, so almost all hypercalls here are page encryption status hypercalls.

Oof.

Quoted 27 lines

The above data indicates that there will be ~2% more Heavyweight VMEXITs during SEV guest boot if we do page encryption status hypercalls pass-through to host userspace.

But, then Brijesh pointed out to me and highlighted that currently
OVMF is doing lot of VMEXITs because they don't use the DMA pool to minimize the C-bit toggles, in other words, OVMF bounce buffer does page state change on every DMA allocate and free.

So here is the performance analysis after kernel and initrd have been loaded into memory using grub and then starting perf just before booting the kernel.

These are the performance #'s after kernel and initrd have been loaded into memory, then perf is attached and kernel is booted :

# Samples: 1M of event 'kvm:kvm_userspace_exit'
# Event count (approx.): 1081235
#
# Overhead Trace output

# ........  ........................
#
    99.77%  reason KVM_EXIT_IO (2)
     0.23%  reason KVM_EXIT_MMIO (6)

# Samples: 1K of event 'kvm:kvm_hypercall'
# Event count (approx.): 1279
#

Quoted 5 lines

So as the above data indicates, Linux is only making ~1K hypercalls, compared to ~18K hypercalls made by OVMF in the above use case.

Does the above adds a prerequisite that OVMF needs to be optimized if and before hypercall pass-through can be done ?

Disclaimer: my math could be totally wrong.

I doubt it's a hard requirement. Assuming a conversative roundtrip time of 50k cycles, those 18K hypercalls will add well under a 1/2 a second of boot time. If userspace can push the roundtrip time down to 10k cycles, the overhead is more like 50 milliseconds.

Quoted 5 lines

That being said, this does seem like a good OVMF cleanup, irrespective of this new hypercall. I assume it's not cheap to convert a page between encrypted and decrypted.

Thanks much for getting the numbers!

Considering the above data and guest boot time latencies (and potential issues with OVMF and optimizations required there), do we have any consensus on whether we want to do page encryption status hypercall passthrough or not ?

Thanks,
Ashish

#27 lore raw

Re: [PATCH v10 10/16] KVM: x86: Introduce KVM_GET_SHARED_PAGES_LIST ioctl

Quoted 62 lines · Ashish Kalra
Quoted 54 lines · Sean Christopherson
Quoted 10 lines · Ashish Kalra

# Samples: 19K of event 'kvm:kvm_hypercall'
# Event count (approx.): 19573
#

# Overhead  Command          Shared Object     Symbol
# ........  ...............  ................  .........................

#
100.00% qemu-system-x86 [kernel.vmlinux] [k] kvm_emulate_hypercall

Out of these 19573 hypercalls, # of page encryption status hcalls are 19479, so almost all hypercalls here are page encryption status hypercalls.

Oof.

Quoted 27 lines

The above data indicates that there will be ~2% more Heavyweight VMEXITs during SEV guest boot if we do page encryption status hypercalls pass-through to host userspace.

But, then Brijesh pointed out to me and highlighted that currently
OVMF is doing lot of VMEXITs because they don't use the DMA pool to minimize the C-bit toggles, in other words, OVMF bounce buffer does page state change on every DMA allocate and free.

So here is the performance analysis after kernel and initrd have been loaded into memory using grub and then starting perf just before booting the kernel.

These are the performance #'s after kernel and initrd have been loaded into memory, then perf is attached and kernel is booted :

# Samples: 1M of event 'kvm:kvm_userspace_exit'
# Event count (approx.): 1081235
#
# Overhead Trace output

# ........  ........................
#
    99.77%  reason KVM_EXIT_IO (2)
     0.23%  reason KVM_EXIT_MMIO (6)

# Samples: 1K of event 'kvm:kvm_hypercall'
# Event count (approx.): 1279
#

Quoted 5 lines

So as the above data indicates, Linux is only making ~1K hypercalls, compared to ~18K hypercalls made by OVMF in the above use case.

Does the above adds a prerequisite that OVMF needs to be optimized if and before hypercall pass-through can be done ?

Disclaimer: my math could be totally wrong.

I doubt it's a hard requirement. Assuming a conversative roundtrip time of 50k cycles, those 18K hypercalls will add well under a 1/2 a second of boot time. If userspace can push the roundtrip time down to 10k cycles, the overhead is more like 50 milliseconds.

Quoted 5 lines

That being said, this does seem like a good OVMF cleanup, irrespective of this new hypercall. I assume it's not cheap to convert a page between encrypted and decrypted.

Thanks much for getting the numbers!

Quoted 7 lines

Considering the above data and guest boot time latencies (and potential issues with OVMF and optimizations required there), do we have any consensus on whether we want to do page encryption status hypercall passthrough or not ?

Thanks,
Ashish

Thanks for grabbing the data!

I am fine with both paths. Sean has stated an explicit desire for hypercall exiting, so I think that would be the current consensus.

If we want to do hypercall exiting, this should be in a follow-up series where we implement something more generic, e.g. a hypercall exiting bitmap or hypercall exit list. If we are taking the hypercall exit route, we can drop the kvm side of the hypercall. Userspace could also handle the MSR using MSR filters (would need to confirm that). Then userspace could also be in control of the cpuid bit.

Essentially, I think you could drop most of the host kernel work if there were generic support for hypercall exiting. Then userspace would be responsible for all of that. Thoughts on this?

--Steve

#28 lore raw

Re: [PATCH v10 10/16] KVM: x86: Introduce KVM_GET_SHARED_PAGES_LIST ioctl

Quoted 84 lines · Steve Rutherford
Quoted 62 lines · Ashish Kalra
Quoted 54 lines · Sean Christopherson
Quoted 10 lines · Ashish Kalra

# Samples: 19K of event 'kvm:kvm_hypercall'
# Event count (approx.): 19573
#

# Overhead  Command          Shared Object     Symbol
# ........  ...............  ................  .........................

#
100.00% qemu-system-x86 [kernel.vmlinux] [k] kvm_emulate_hypercall

Out of these 19573 hypercalls, # of page encryption status hcalls are 19479, so almost all hypercalls here are page encryption status hypercalls.

Oof.

Quoted 27 lines

The above data indicates that there will be ~2% more Heavyweight VMEXITs during SEV guest boot if we do page encryption status hypercalls pass-through to host userspace.

But, then Brijesh pointed out to me and highlighted that currently
OVMF is doing lot of VMEXITs because they don't use the DMA pool to minimize the C-bit toggles, in other words, OVMF bounce buffer does page state change on every DMA allocate and free.

So here is the performance analysis after kernel and initrd have been loaded into memory using grub and then starting perf just before booting the kernel.

These are the performance #'s after kernel and initrd have been loaded into memory, then perf is attached and kernel is booted :

# Samples: 1M of event 'kvm:kvm_userspace_exit'
# Event count (approx.): 1081235
#
# Overhead Trace output

# ........  ........................
#
    99.77%  reason KVM_EXIT_IO (2)
     0.23%  reason KVM_EXIT_MMIO (6)

# Samples: 1K of event 'kvm:kvm_hypercall'
# Event count (approx.): 1279
#

Quoted 5 lines

So as the above data indicates, Linux is only making ~1K hypercalls, compared to ~18K hypercalls made by OVMF in the above use case.

Does the above adds a prerequisite that OVMF needs to be optimized if and before hypercall pass-through can be done ?

Disclaimer: my math could be totally wrong.

I doubt it's a hard requirement. Assuming a conversative roundtrip time of 50k cycles, those 18K hypercalls will add well under a 1/2 a second of boot time. If userspace can push the roundtrip time down to 10k cycles, the overhead is more like 50 milliseconds.

Quoted 5 lines

That being said, this does seem like a good OVMF cleanup, irrespective of this new hypercall. I assume it's not cheap to convert a page between encrypted and decrypted.

Thanks much for getting the numbers!

Quoted 7 lines

Considering the above data and guest boot time latencies (and potential issues with OVMF and optimizations required there), do we have any consensus on whether we want to do page encryption status hypercall passthrough or not ?

Thanks,
Ashish

Thanks for grabbing the data!

I am fine with both paths. Sean has stated an explicit desire for hypercall exiting, so I think that would be the current consensus.

If we want to do hypercall exiting, this should be in a follow-up series where we implement something more generic, e.g. a hypercall exiting bitmap or hypercall exit list. If we are taking the hypercall exit route, we can drop the kvm side of the hypercall. Userspace could also handle the MSR using MSR filters (would need to confirm that). Then userspace could also be in control of the cpuid bit.

Quoted 5 lines

Essentially, I think you could drop most of the host kernel work if there were generic support for hypercall exiting. Then userspace would be responsible for all of that. Thoughts on this?

--Steve

This could even go a step further, and use an MSR write from within the guest instead of a hypercall, which could be patched through to userspace without host modification, if I understand the MSR filtering correctly.
--Steve

#29 lore raw

Re: [PATCH v10 10/16] KVM: x86: Introduce KVM_GET_SHARED_PAGES_LIST ioctl

In reply to Steve Rutherford

Hello Steve,

Quoted 83 lines · Steve Rutherford
Quoted 62 lines · Ashish Kalra
Quoted 54 lines · Sean Christopherson
Quoted 10 lines · Ashish Kalra

# Samples: 19K of event 'kvm:kvm_hypercall'
# Event count (approx.): 19573
#

# Overhead  Command          Shared Object     Symbol
# ........  ...............  ................  .........................

#
100.00% qemu-system-x86 [kernel.vmlinux] [k] kvm_emulate_hypercall

Out of these 19573 hypercalls, # of page encryption status hcalls are 19479, so almost all hypercalls here are page encryption status hypercalls.

Oof.

Quoted 27 lines

The above data indicates that there will be ~2% more Heavyweight VMEXITs during SEV guest boot if we do page encryption status hypercalls pass-through to host userspace.

But, then Brijesh pointed out to me and highlighted that currently
OVMF is doing lot of VMEXITs because they don't use the DMA pool to minimize the C-bit toggles, in other words, OVMF bounce buffer does page state change on every DMA allocate and free.

So here is the performance analysis after kernel and initrd have been loaded into memory using grub and then starting perf just before booting the kernel.

These are the performance #'s after kernel and initrd have been loaded into memory, then perf is attached and kernel is booted :

# Samples: 1M of event 'kvm:kvm_userspace_exit'
# Event count (approx.): 1081235
#
# Overhead Trace output

# ........  ........................
#
    99.77%  reason KVM_EXIT_IO (2)
     0.23%  reason KVM_EXIT_MMIO (6)

# Samples: 1K of event 'kvm:kvm_hypercall'
# Event count (approx.): 1279
#

Quoted 5 lines

So as the above data indicates, Linux is only making ~1K hypercalls, compared to ~18K hypercalls made by OVMF in the above use case.

Does the above adds a prerequisite that OVMF needs to be optimized if and before hypercall pass-through can be done ?

Disclaimer: my math could be totally wrong.

I doubt it's a hard requirement. Assuming a conversative roundtrip time of 50k cycles, those 18K hypercalls will add well under a 1/2 a second of boot time. If userspace can push the roundtrip time down to 10k cycles, the overhead is more like 50 milliseconds.

Quoted 5 lines

That being said, this does seem like a good OVMF cleanup, irrespective of this new hypercall. I assume it's not cheap to convert a page between encrypted and decrypted.

Thanks much for getting the numbers!

Quoted 7 lines

Considering the above data and guest boot time latencies (and potential issues with OVMF and optimizations required there), do we have any consensus on whether we want to do page encryption status hypercall passthrough or not ?

Thanks,
Ashish

Thanks for grabbing the data!

I am fine with both paths. Sean has stated an explicit desire for hypercall exiting, so I think that would be the current consensus.

If we want to do hypercall exiting, this should be in a follow-up series where we implement something more generic, e.g. a hypercall exiting bitmap or hypercall exit list. If we are taking the hypercall exit route, we can drop the kvm side of the hypercall. Userspace could also handle the MSR using MSR filters (would need to confirm that). Then userspace could also be in control of the cpuid bit.

Quoted 4 lines

Essentially, I think you could drop most of the host kernel work if there were generic support for hypercall exiting. Then userspace would be responsible for all of that. Thoughts on this?

So if i understand it correctly, i will submitting v11 of this patch-set with in-kernel support for page encryption status hypercalls and shared pages list and the userspace control of SEV live migration feature support and fixes for MSR handling.

In subsequent follow-up patches we will add generic support for hypercall exiting and then drop kvm side of hypercall and also add userspace support for MSR handling.

Thanks,
Ashish

#30 lore raw

Re: [PATCH v10 10/16] KVM: x86: Introduce KVM_GET_SHARED_PAGES_LIST ioctl

+Will and Quentin (arm64)

Moving the non-KVM x86 folks to bcc, I don't they care about KVM details at this point.

Quoted 6 lines · Ashish Kalra
Quoted 5 lines · Steve Rutherford

On Thu, Feb 25, 2021 at 12:20 PM Ashish Kalra <[email protected]> wrote:
Thanks for grabbing the data!

I am fine with both paths. Sean has stated an explicit desire for hypercall exiting, so I think that would be the current consensus.

Yep, though it'd be good to get Paolo's input, too.

Quoted 4 lines
Quoted 4 lines

If we want to do hypercall exiting, this should be in a follow-up series where we implement something more generic, e.g. a hypercall exiting bitmap or hypercall exit list. If we are taking the hypercall exit route, we can drop the kvm side of the hypercall.

I don't think this is a good candidate for arbitrary hypercall interception. Or rather, I think hypercall interception should be an orthogonal implementation.

The guest, including guest firmware, needs to be aware that the hypercall is supported, and the ABI needs to be well-defined. Relying on userspace VMMs to implement a common ABI is an unnecessary risk.

We could make KVM's default behavior be a nop, i.e. have KVM enforce the ABI but require further VMM intervention. But, I just don't see the point, it would save only a few lines of code. It would also limit what KVM could do in the future, e.g. if KVM wanted to do its own bookkeeping _and_ exit to userspace, then mandatory interception would essentially make it impossible for KVM to do bookkeeping while still honoring the interception request.

However, I do think it would make sense to have the userspace exit be a generic exit type. But hey, we already have the necessary ABI defined for that! It's just not used anywhere.

	/* KVM_EXIT_HYPERCALL */
	struct {
		__u64 nr;
		__u64 args[6];
		__u64 ret;
		__u32 longmode;
		__u32 pad;
	} hypercall;
Quoted 2 lines
Quoted 2 lines

Userspace could also handle the MSR using MSR filters (would need to confirm that). Then userspace could also be in control of the cpuid bit.

An MSR is not a great fit; it's x86 specific and limited to 64 bits of data. The data limitation could be fudged by shoving data into non-standard GPRs, but that will result in truly heinous guest code, and extensibility issues.

The data limitation is a moot point, because the x86-only thing is a deal breaker. arm64's pKVM work has a near-identical use case for a guest to share memory with a host. I can't think of a clever way to avoid having to support TDX's and SNP's hypervisor-agnostic variants, but we can at least not have multiple KVM variants.

Quoted 8 lines
Quoted 3 lines

Essentially, I think you could drop most of the host kernel work if there were generic support for hypercall exiting. Then userspace would be responsible for all of that. Thoughts on this?

So if i understand it correctly, i will submitting v11 of this patch-set with in-kernel support for page encryption status hypercalls and shared pages list and the userspace control of SEV live migration feature support and fixes for MSR handling.

At this point, I'd say hold off on putting more effort into an implementation until we have consensus.

Quoted 6 lines

In subsequent follow-up patches we will add generic support for hypercall exiting and then drop kvm side of hypercall and also add userspace support for MSR handling.

Thanks,
Ashish

#31 lore raw

Re: [PATCH v10 10/16] KVM: x86: Introduce KVM_GET_SHARED_PAGES_LIST ioctl

Quoted 55 lines · Sean Christopherson

+Will and Quentin (arm64)

Moving the non-KVM x86 folks to bcc, I don't they care about KVM details at this point.

Quoted 6 lines · Ashish Kalra
Quoted 5 lines · Steve Rutherford

On Thu, Feb 25, 2021 at 12:20 PM Ashish Kalra <[email protected]> wrote:
Thanks for grabbing the data!

I am fine with both paths. Sean has stated an explicit desire for hypercall exiting, so I think that would be the current consensus.

Yep, though it'd be good to get Paolo's input, too.

Quoted 4 lines
Quoted 4 lines

If we want to do hypercall exiting, this should be in a follow-up series where we implement something more generic, e.g. a hypercall exiting bitmap or hypercall exit list. If we are taking the hypercall exit route, we can drop the kvm side of the hypercall.

I don't think this is a good candidate for arbitrary hypercall interception. Or rather, I think hypercall interception should be an orthogonal implementation.

The guest, including guest firmware, needs to be aware that the hypercall is supported, and the ABI needs to be well-defined. Relying on userspace VMMs to implement a common ABI is an unnecessary risk.

We could make KVM's default behavior be a nop, i.e. have KVM enforce the ABI but require further VMM intervention. But, I just don't see the point, it would save only a few lines of code. It would also limit what KVM could do in the future, e.g. if KVM wanted to do its own bookkeeping _and_ exit to userspace, then mandatory interception would essentially make it impossible for KVM to do bookkeeping while still honoring the interception request.

However, I do think it would make sense to have the userspace exit be a generic exit type. But hey, we already have the necessary ABI defined for that! It's just not used anywhere.

	/* KVM_EXIT_HYPERCALL */
	struct {
		__u64 nr;
		__u64 args[6];
		__u64 ret;
		__u32 longmode;
		__u32 pad;
	} hypercall;
Quoted 2 lines
Quoted 2 lines

Userspace could also handle the MSR using MSR filters (would need to confirm that). Then userspace could also be in control of the cpuid bit.

An MSR is not a great fit; it's x86 specific and limited to 64 bits of data. The data limitation could be fudged by shoving data into non-standard GPRs, but that will result in truly heinous guest code, and extensibility issues.

Quoted 5 lines

The data limitation is a moot point, because the x86-only thing is a deal breaker. arm64's pKVM work has a near-identical use case for a guest to share memory with a host. I can't think of a clever way to avoid having to support TDX's and SNP's hypervisor-agnostic variants, but we can at least not have multiple KVM variants.

Looking at arm64's pKVM work, i see that it is a recently introduced RFC patch-set and probably relevant to arm64 nVHE hypervisor mode/implementation, and potentially makes sense as it adds guest memory protection as both host and guest kernels are running on the same privilege level ?

Though i do see that the pKVM stuff adds two hypercalls, specifically :

pkvm_create_mappings() ( I assume this is for setting shared memory regions between host and guest) & pkvm_create_private_mappings().

And the use-cases are quite similar to memory protection architectues use cases, for example, use with virtio devices, guest DMA I/O, etc.

But, isn't this patch set still RFC, and though i agree that it adds an infrastructure for standardised communication between the host and it's guests for mutually controlled shared memory regions and surely adds some kind of portability between hypervisor implementations, but nothing is standardised still, right ?

Thanks,
Ashish

#32 lore raw

Re: [PATCH v10 10/16] KVM: x86: Introduce KVM_GET_SHARED_PAGES_LIST ioctl

Quoted 74 lines · Ashish Kalra
Quoted 55 lines · Sean Christopherson

+Will and Quentin (arm64)

Moving the non-KVM x86 folks to bcc, I don't they care about KVM details at this point.

Quoted 6 lines · Ashish Kalra
Quoted 5 lines · Steve Rutherford

On Thu, Feb 25, 2021 at 12:20 PM Ashish Kalra <[email protected]> wrote:
Thanks for grabbing the data!

I am fine with both paths. Sean has stated an explicit desire for hypercall exiting, so I think that would be the current consensus.

Yep, though it'd be good to get Paolo's input, too.

Quoted 4 lines
Quoted 4 lines

If we want to do hypercall exiting, this should be in a follow-up series where we implement something more generic, e.g. a hypercall exiting bitmap or hypercall exit list. If we are taking the hypercall exit route, we can drop the kvm side of the hypercall.

I don't think this is a good candidate for arbitrary hypercall interception. Or rather, I think hypercall interception should be an orthogonal implementation.

The guest, including guest firmware, needs to be aware that the hypercall is supported, and the ABI needs to be well-defined. Relying on userspace VMMs to implement a common ABI is an unnecessary risk.

We could make KVM's default behavior be a nop, i.e. have KVM enforce the ABI but require further VMM intervention. But, I just don't see the point, it would save only a few lines of code. It would also limit what KVM could do in the future, e.g. if KVM wanted to do its own bookkeeping _and_ exit to userspace, then mandatory interception would essentially make it impossible for KVM to do bookkeeping while still honoring the interception request.

However, I do think it would make sense to have the userspace exit be a generic exit type. But hey, we already have the necessary ABI defined for that! It's just not used anywhere.

	/* KVM_EXIT_HYPERCALL */
	struct {
		__u64 nr;
		__u64 args[6];
		__u64 ret;
		__u32 longmode;
		__u32 pad;
	} hypercall;
Quoted 2 lines
Quoted 2 lines

Userspace could also handle the MSR using MSR filters (would need to confirm that). Then userspace could also be in control of the cpuid bit.

An MSR is not a great fit; it's x86 specific and limited to 64 bits of data. The data limitation could be fudged by shoving data into non-standard GPRs, but that will result in truly heinous guest code, and extensibility issues.

Quoted 5 lines

The data limitation is a moot point, because the x86-only thing is a deal breaker. arm64's pKVM work has a near-identical use case for a guest to share memory with a host. I can't think of a clever way to avoid having to support TDX's and SNP's hypervisor-agnostic variants, but we can at least not have multiple KVM variants.

Looking at arm64's pKVM work, i see that it is a recently introduced RFC patch-set and probably relevant to arm64 nVHE hypervisor mode/implementation, and potentially makes sense as it adds guest memory protection as both host and guest kernels are running on the same privilege level ?

Though i do see that the pKVM stuff adds two hypercalls, specifically :

pkvm_create_mappings() ( I assume this is for setting shared memory regions between host and guest) & pkvm_create_private_mappings().

Quoted 9 lines

And the use-cases are quite similar to memory protection architectues use cases, for example, use with virtio devices, guest DMA I/O, etc.

But, isn't this patch set still RFC, and though i agree that it adds an infrastructure for standardised communication between the host and it's guests for mutually controlled shared memory regions and surely adds some kind of portability between hypervisor implementations, but nothing is standardised still, right ?

And to add here, the hypercall implementation is in-HYP mode, there is no infrastructure as part of this patch-set to do hypercall exiting and handling it in user-space.

Though arguably, we may able to add a hypercall exiting code path on the amd64 implementation for the same hypercall interfaces ?

Alternatively, we implement this in-kernel and then add SET/GET ioctl interfaces to export the shared pages/regions list to user-space.

Thanks,
Ashish

#33 lore raw

Re: [PATCH v10 10/16] KVM: x86: Introduce KVM_GET_SHARED_PAGES_LIST ioctl

In reply to Ashish Kalra

[+Marc]

Quoted 63 lines · Ashish Kalra
Quoted 50 lines · Sean Christopherson
Quoted 6 lines · Ashish Kalra
Quoted 5 lines · Steve Rutherford

On Thu, Feb 25, 2021 at 12:20 PM Ashish Kalra <[email protected]> wrote:
Thanks for grabbing the data!

I am fine with both paths. Sean has stated an explicit desire for hypercall exiting, so I think that would be the current consensus.

Yep, though it'd be good to get Paolo's input, too.

Quoted 4 lines
Quoted 4 lines

If we want to do hypercall exiting, this should be in a follow-up series where we implement something more generic, e.g. a hypercall exiting bitmap or hypercall exit list. If we are taking the hypercall exit route, we can drop the kvm side of the hypercall.

I don't think this is a good candidate for arbitrary hypercall interception. Or rather, I think hypercall interception should be an orthogonal implementation.

The guest, including guest firmware, needs to be aware that the hypercall is supported, and the ABI needs to be well-defined. Relying on userspace VMMs to implement a common ABI is an unnecessary risk.

We could make KVM's default behavior be a nop, i.e. have KVM enforce the ABI but require further VMM intervention. But, I just don't see the point, it would save only a few lines of code. It would also limit what KVM could do in the future, e.g. if KVM wanted to do its own bookkeeping _and_ exit to userspace, then mandatory interception would essentially make it impossible for KVM to do bookkeeping while still honoring the interception request.

However, I do think it would make sense to have the userspace exit be a generic exit type. But hey, we already have the necessary ABI defined for that! It's just not used anywhere.

	/* KVM_EXIT_HYPERCALL */
	struct {
		__u64 nr;
		__u64 args[6];
		__u64 ret;
		__u32 longmode;
		__u32 pad;
	} hypercall;
Quoted 2 lines
Quoted 2 lines

Userspace could also handle the MSR using MSR filters (would need to confirm that). Then userspace could also be in control of the cpuid bit.

An MSR is not a great fit; it's x86 specific and limited to 64 bits of data. The data limitation could be fudged by shoving data into non-standard GPRs, but that will result in truly heinous guest code, and extensibility issues.

Quoted 5 lines

The data limitation is a moot point, because the x86-only thing is a deal breaker. arm64's pKVM work has a near-identical use case for a guest to share memory with a host. I can't think of a clever way to avoid having to support TDX's and SNP's hypervisor-agnostic variants, but we can at least not have multiple KVM variants.

Looking at arm64's pKVM work, i see that it is a recently introduced RFC patch-set and probably relevant to arm64 nVHE hypervisor mode/implementation, and potentially makes sense as it adds guest memory protection as both host and guest kernels are running on the same privilege level ?

Quoted 8 lines

Though i do see that the pKVM stuff adds two hypercalls, specifically :

pkvm_create_mappings() ( I assume this is for setting shared memory regions between host and guest) & pkvm_create_private_mappings().

And the use-cases are quite similar to memory protection architectues use cases, for example, use with virtio devices, guest DMA I/O, etc.

These hypercalls are both private to the host kernel communicating with its hypervisor counterpart, so I don't think they're particularly relevant here. As far as I can see, the more useful thing is to allow the guest to communicate back to the host (and the VMM) that it has opened up a memory window, perhaps for virtio rings or some other shared memory.

We hacked this up as a prototype in the past:

https://android-kvm.googlesource.com/linux/+/d12a9e2c12a52cf7140d40cd9fa092dc8a85fac9%5E%21/

but that's all arm64-specific and if we're solving the same problem as you, then let's avoid arch-specific stuff if possible. The way in which the guest discovers the interface will be arch-specific (we already have a mechanism for that and some hypercalls are already allocated by specs from Arm), but the interface back to the VMM and some (most?) of the host handling could be shared.

Quoted 5 lines

But, isn't this patch set still RFC, and though i agree that it adds an infrastructure for standardised communication between the host and it's guests for mutually controlled shared memory regions and surely adds some kind of portability between hypervisor implementations, but nothing is standardised still, right ?

No, and it seems that you're further ahead than us in terms of implementation in this area. We're happy to review patches though, to make sure we end up with something that works for us both.

Will

#34 lore raw

Re: [PATCH v10 10/16] KVM: x86: Introduce KVM_GET_SHARED_PAGES_LIST ioctl

Quoted 72 lines · Will Deacon

[+Marc]

Quoted 69 lines · Ashish Kalra
Quoted 50 lines · Sean Christopherson
Quoted 6 lines · Ashish Kalra
Quoted 5 lines · Steve Rutherford

On Thu, Feb 25, 2021 at 12:20 PM Ashish Kalra <[email protected]> wrote:
Thanks for grabbing the data!

I am fine with both paths. Sean has stated an explicit desire for hypercall exiting, so I think that would be the current consensus.

Yep, though it'd be good to get Paolo's input, too.

Quoted 4 lines
Quoted 4 lines

If we want to do hypercall exiting, this should be in a follow-up series where we implement something more generic, e.g. a hypercall exiting bitmap or hypercall exit list. If we are taking the hypercall exit route, we can drop the kvm side of the hypercall.

I don't think this is a good candidate for arbitrary hypercall interception. Or rather, I think hypercall interception should be an orthogonal implementation.

The guest, including guest firmware, needs to be aware that the hypercall is supported, and the ABI needs to be well-defined. Relying on userspace VMMs to implement a common ABI is an unnecessary risk.

We could make KVM's default behavior be a nop, i.e. have KVM enforce the ABI but require further VMM intervention. But, I just don't see the point, it would save only a few lines of code. It would also limit what KVM could do in the future, e.g. if KVM wanted to do its own bookkeeping _and_ exit to userspace, then mandatory interception would essentially make it impossible for KVM to do bookkeeping while still honoring the interception request.

However, I do think it would make sense to have the userspace exit be a generic exit type. But hey, we already have the necessary ABI defined for that! It's just not used anywhere.

	/* KVM_EXIT_HYPERCALL */
	struct {
		__u64 nr;
		__u64 args[6];
		__u64 ret;
		__u32 longmode;
		__u32 pad;
	} hypercall;
Quoted 2 lines
Quoted 2 lines

Userspace could also handle the MSR using MSR filters (would need to confirm that). Then userspace could also be in control of the cpuid bit.

An MSR is not a great fit; it's x86 specific and limited to 64 bits of data. The data limitation could be fudged by shoving data into non-standard GPRs, but that will result in truly heinous guest code, and extensibility issues.

Quoted 5 lines

The data limitation is a moot point, because the x86-only thing is a deal breaker. arm64's pKVM work has a near-identical use case for a guest to share memory with a host. I can't think of a clever way to avoid having to support TDX's and SNP's hypervisor-agnostic variants, but we can at least not have multiple KVM variants.

Looking at arm64's pKVM work, i see that it is a recently introduced RFC patch-set and probably relevant to arm64 nVHE hypervisor mode/implementation, and potentially makes sense as it adds guest memory protection as both host and guest kernels are running on the same privilege level ?

Though i do see that the pKVM stuff adds two hypercalls, specifically :

pkvm_create_mappings() ( I assume this is for setting shared memory regions between host and guest) & pkvm_create_private_mappings().

Quoted 6 lines
Quoted 2 lines

And the use-cases are quite similar to memory protection architectues use cases, for example, use with virtio devices, guest DMA I/O, etc.

These hypercalls are both private to the host kernel communicating with its hypervisor counterpart, so I don't think they're particularly relevant here.

Yes, i have the same thoughts here as this looked like a private hypercall interface between host kernel and hypervisor, rather than between guest and host/hypervisor.

Quoted 4 lines

As far as I can see, the more useful thing is to allow
the guest to communicate back to the host (and the VMM) that it has opened up a memory window, perhaps for virtio rings or some other shared memory.

Yes, this is our main use case.

Quoted 11 lines

We hacked this up as a prototype in the past:

https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fandroid-kvm.googlesource.com%2Flinux%2F%2B%2Fd12a9e2c12a52cf7140d40cd9fa092dc8a85fac9%255E%2521%2F&amp;data=04%7C01%7Cashish.kalra%40amd.com%7C7ae6bbd9fa6442f9edcc08d8de75d14b%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637503944913839841%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=Juon5nJ7BB6moTWYssRXOWrDOrYfZLmA%2BLrz3s12Ook%3D&amp;reserved=0

but that's all arm64-specific and if we're solving the same problem as you, then let's avoid arch-specific stuff if possible. The way in which the guest discovers the interface will be arch-specific (we already have a mechanism for that and some hypercalls are already allocated by specs from Arm), but the interface back to the VMM and some (most?) of the host handling could be shared.

Ok, yes and that makes sense.

Quoted 10 lines
Quoted 5 lines

But, isn't this patch set still RFC, and though i agree that it adds an infrastructure for standardised communication between the host and it's guests for mutually controlled shared memory regions and surely adds some kind of portability between hypervisor implementations, but nothing is standardised still, right ?

No, and it seems that you're further ahead than us in terms of implementation in this area. We're happy to review patches though, to make sure we end up with something that works for us both.

Here is the link to the current patch series :
https://lore.kernel.org/lkml/[email protected]/

And the following patches will be relevant here:
KVM: x86: Add AMD SEV specific Hypercall3
KVM: X86: Introduce KVM_HC_PAGE_ENC_STATUS hypercall
mm: x86: Invoke hypercall when page encryption status is changed
KVM: x86: Introduce KVM_GET_SHARED_PAGES_LIST ioctl
KVM: x86: Introduce KVM_SET_SHARED_PAGES_LIST ioctl

Thanks,
Ashish

#35 lore raw

Re: [PATCH v10 10/16] KVM: x86: Introduce KVM_GET_SHARED_PAGES_LIST ioctl

In reply to Will Deacon
Quoted 46 lines · Will Deacon

[+Marc]

Quoted 43 lines · Ashish Kalra
Quoted 42 lines · Sean Christopherson
Quoted 6 lines · Ashish Kalra
Quoted 5 lines · Steve Rutherford

On Thu, Feb 25, 2021 at 12:20 PM Ashish Kalra <[email protected]> wrote:
Thanks for grabbing the data!

I am fine with both paths. Sean has stated an explicit desire for hypercall exiting, so I think that would be the current consensus.

Yep, though it'd be good to get Paolo's input, too.

Quoted 4 lines
Quoted 4 lines

If we want to do hypercall exiting, this should be in a follow-up series where we implement something more generic, e.g. a hypercall exiting bitmap or hypercall exit list. If we are taking the hypercall exit route, we can drop the kvm side of the hypercall.

I don't think this is a good candidate for arbitrary hypercall interception. Or rather, I think hypercall interception should be an orthogonal implementation.

The guest, including guest firmware, needs to be aware that the hypercall is supported, and the ABI needs to be well-defined. Relying on userspace VMMs to implement a common ABI is an unnecessary risk.

We could make KVM's default behavior be a nop, i.e. have KVM enforce the ABI but require further VMM intervention. But, I just don't see the point, it would save only a few lines of code. It would also limit what KVM could do in the future, e.g. if KVM wanted to do its own bookkeeping _and_ exit to userspace, then mandatory interception would essentially make it impossible for KVM to do bookkeeping while still honoring the interception request.

However, I do think it would make sense to have the userspace exit be a generic exit type. But hey, we already have the necessary ABI defined for that! It's just not used anywhere.

	/* KVM_EXIT_HYPERCALL */
	struct {
		__u64 nr;
		__u64 args[6];
		__u64 ret;
		__u32 longmode;
		__u32 pad;
	} hypercall;

Doc mentions to use KVM_EXIT_IO for x86 to implement "hypercall to userspace" functionality.

Any reasons for not using KVM_EXIT_HYPERCALL interface any more, even though it is still there and not removed ?

Quoted 37 lines
Quoted 20 lines
Quoted 8 lines
Quoted 3 lines
Quoted 3 lines

Userspace could also handle the MSR using MSR filters (would need to confirm that). Then userspace could also be in control of the cpuid bit.

An MSR is not a great fit; it's x86 specific and limited to 64 bits of data. The data limitation could be fudged by shoving data into non-standard GPRs, but that will result in truly heinous guest code, and extensibility issues.

Quoted 5 lines

The data limitation is a moot point, because the x86-only thing is a deal breaker. arm64's pKVM work has a near-identical use case for a guest to share memory with a host. I can't think of a clever way to avoid having to support TDX's and SNP's hypervisor-agnostic variants, but we can at least not have multiple KVM variants.

Looking at arm64's pKVM work, i see that it is a recently introduced RFC patch-set and probably relevant to arm64 nVHE hypervisor mode/implementation, and potentially makes sense as it adds guest memory protection as both host and guest kernels are running on the same privilege level ?

Quoted 8 lines

Though i do see that the pKVM stuff adds two hypercalls, specifically :

pkvm_create_mappings() ( I assume this is for setting shared memory regions between host and guest) & pkvm_create_private_mappings().

And the use-cases are quite similar to memory protection architectues use cases, for example, use with virtio devices, guest DMA I/O, etc.

These hypercalls are both private to the host kernel communicating with its hypervisor counterpart, so I don't think they're particularly relevant here. As far as I can see, the more useful thing is to allow the guest to communicate back to the host (and the VMM) that it has opened up a memory window, perhaps for virtio rings or some other shared memory.

We hacked this up as a prototype in the past:

Quoted 9 lines

https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fandroid-kvm.googlesource.com%2Flinux%2F%2B%2Fd12a9e2c12a52cf7140d40cd9fa092dc8a85fac9%255E%2521%2F&amp;data=04%7C01%7Cashish.kalra%40amd.com%7C7ae6bbd9fa6442f9edcc08d8de75d14b%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637503944913839841%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=Juon5nJ7BB6moTWYssRXOWrDOrYfZLmA%2BLrz3s12Ook%3D&amp;reserved=0

but that's all arm64-specific and if we're solving the same problem as you, then let's avoid arch-specific stuff if possible. The way in which the guest discovers the interface will be arch-specific (we already have a mechanism for that and some hypercalls are already allocated by specs from Arm), but the interface back to the VMM and some (most?) of the host handling could be shared.

Again any reason for not using the KVM_EXIT_HYPERCALL interface for these ?

Thanks,
Ashish

Quoted 11 lines
Quoted 5 lines

But, isn't this patch set still RFC, and though i agree that it adds an infrastructure for standardised communication between the host and it's guests for mutually controlled shared memory regions and surely adds some kind of portability between hypervisor implementations, but nothing is standardised still, right ?

No, and it seems that you're further ahead than us in terms of implementation in this area. We're happy to review patches though, to make sure we end up with something that works for us both.

Will

#36 lore raw

Re: [PATCH v10 10/16] KVM: x86: Introduce KVM_GET_SHARED_PAGES_LIST ioctl

In reply to Will Deacon
Quoted 83 lines · Will Deacon

[+Marc]

Quoted 63 lines · Ashish Kalra
Quoted 50 lines · Sean Christopherson
Quoted 6 lines · Ashish Kalra
Quoted 5 lines · Steve Rutherford

On Thu, Feb 25, 2021 at 12:20 PM Ashish Kalra <[email protected]> wrote:
Thanks for grabbing the data!

I am fine with both paths. Sean has stated an explicit desire for hypercall exiting, so I think that would be the current consensus.

Yep, though it'd be good to get Paolo's input, too.

Quoted 4 lines
Quoted 4 lines

If we want to do hypercall exiting, this should be in a follow-up series where we implement something more generic, e.g. a hypercall exiting bitmap or hypercall exit list. If we are taking the hypercall exit route, we can drop the kvm side of the hypercall.

I don't think this is a good candidate for arbitrary hypercall interception. Or rather, I think hypercall interception should be an orthogonal implementation.

The guest, including guest firmware, needs to be aware that the hypercall is supported, and the ABI needs to be well-defined. Relying on userspace VMMs to implement a common ABI is an unnecessary risk.

We could make KVM's default behavior be a nop, i.e. have KVM enforce the ABI but require further VMM intervention. But, I just don't see the point, it would save only a few lines of code. It would also limit what KVM could do in the future, e.g. if KVM wanted to do its own bookkeeping _and_ exit to userspace, then mandatory interception would essentially make it impossible for KVM to do bookkeeping while still honoring the interception request.

However, I do think it would make sense to have the userspace exit be a generic exit type. But hey, we already have the necessary ABI defined for that! It's just not used anywhere.

	/* KVM_EXIT_HYPERCALL */
	struct {
		__u64 nr;
		__u64 args[6];
		__u64 ret;
		__u32 longmode;
		__u32 pad;
	} hypercall;
Quoted 2 lines
Quoted 2 lines

Userspace could also handle the MSR using MSR filters (would need to confirm that). Then userspace could also be in control of the cpuid bit.

An MSR is not a great fit; it's x86 specific and limited to 64 bits of data. The data limitation could be fudged by shoving data into non-standard GPRs, but that will result in truly heinous guest code, and extensibility issues.

Quoted 5 lines

The data limitation is a moot point, because the x86-only thing is a deal breaker. arm64's pKVM work has a near-identical use case for a guest to share memory with a host. I can't think of a clever way to avoid having to support TDX's and SNP's hypervisor-agnostic variants, but we can at least not have multiple KVM variants.

Looking at arm64's pKVM work, i see that it is a recently introduced RFC patch-set and probably relevant to arm64 nVHE hypervisor mode/implementation, and potentially makes sense as it adds guest memory protection as both host and guest kernels are running on the same privilege level ?

Quoted 8 lines

Though i do see that the pKVM stuff adds two hypercalls, specifically :

pkvm_create_mappings() ( I assume this is for setting shared memory regions between host and guest) & pkvm_create_private_mappings().

And the use-cases are quite similar to memory protection architectues use cases, for example, use with virtio devices, guest DMA I/O, etc.

These hypercalls are both private to the host kernel communicating with its hypervisor counterpart, so I don't think they're particularly relevant here. As far as I can see, the more useful thing is to allow the guest to communicate back to the host (and the VMM) that it has opened up a memory window, perhaps for virtio rings or some other shared memory.

We hacked this up as a prototype in the past:

Quoted 9 lines

https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fandroid-kvm.googlesource.com%2Flinux%2F%2B%2Fd12a9e2c12a52cf7140d40cd9fa092dc8a85fac9%255E%2521%2F&amp;data=04%7C01%7Cashish.kalra%40amd.com%7C7ae6bbd9fa6442f9edcc08d8de75d14b%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637503944913839841%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=Juon5nJ7BB6moTWYssRXOWrDOrYfZLmA%2BLrz3s12Ook%3D&amp;reserved=0

but that's all arm64-specific and if we're solving the same problem as you, then let's avoid arch-specific stuff if possible. The way in which the guest discovers the interface will be arch-specific (we already have a mechanism for that and some hypercalls are already allocated by specs from Arm), but the interface back to the VMM and some (most?) of the host handling could be shared.

I have started implementing a similar "hypercall to userspace" functionality for these DMA_SHARE/DMA_UNSHARE type of interfaces corresponding to SEV guest's add/remove shared regions on the x86 platform.

This does not implement a generic hypercall exiting infrastructure, mainly extends the KVM hypercall support to return back to userspace specifically for add/remove shared region hypercalls and then re-uses the complete userspace I/O callback functionality to resume the guest after returning back from userspace handling of the hypercall.

Looking fwd. to any comments/feedback/thoughts on the above.

Thanks,
Ashish

Quoted 11 lines
Quoted 5 lines

But, isn't this patch set still RFC, and though i agree that it adds an infrastructure for standardised communication between the host and it's guests for mutually controlled shared memory regions and surely adds some kind of portability between hypervisor implementations, but nothing is standardised still, right ?

No, and it seems that you're further ahead than us in terms of implementation in this area. We're happy to review patches though, to make sure we end up with something that works for us both.

Will

#37 lore raw

Re: [PATCH v10 10/16] KVM: x86: Introduce KVM_GET_SHARED_PAGES_LIST ioctl

Quoted 99 lines · Ashish Kalra
Quoted 83 lines · Will Deacon

[+Marc]

Quoted 63 lines · Ashish Kalra
Quoted 50 lines · Sean Christopherson
Quoted 6 lines · Ashish Kalra
Quoted 5 lines · Steve Rutherford

On Thu, Feb 25, 2021 at 12:20 PM Ashish Kalra <[email protected]> wrote:
Thanks for grabbing the data!

I am fine with both paths. Sean has stated an explicit desire for hypercall exiting, so I think that would be the current consensus.

Yep, though it'd be good to get Paolo's input, too.

Quoted 4 lines
Quoted 4 lines

If we want to do hypercall exiting, this should be in a follow-up series where we implement something more generic, e.g. a hypercall exiting bitmap or hypercall exit list. If we are taking the hypercall exit route, we can drop the kvm side of the hypercall.

I don't think this is a good candidate for arbitrary hypercall interception. Or rather, I think hypercall interception should be an orthogonal implementation.

The guest, including guest firmware, needs to be aware that the hypercall is supported, and the ABI needs to be well-defined. Relying on userspace VMMs to implement a common ABI is an unnecessary risk.

We could make KVM's default behavior be a nop, i.e. have KVM enforce the ABI but require further VMM intervention. But, I just don't see the point, it would save only a few lines of code. It would also limit what KVM could do in the future, e.g. if KVM wanted to do its own bookkeeping _and_ exit to userspace, then mandatory interception would essentially make it impossible for KVM to do bookkeeping while still honoring the interception request.

However, I do think it would make sense to have the userspace exit be a generic exit type. But hey, we already have the necessary ABI defined for that! It's just not used anywhere.

  /* KVM_EXIT_HYPERCALL */
  struct {
          __u64 nr;
          __u64 args[6];
          __u64 ret;
          __u32 longmode;
          __u32 pad;
  } hypercall;
Quoted 2 lines
Quoted 2 lines

Userspace could also handle the MSR using MSR filters (would need to confirm that). Then userspace could also be in control of the cpuid bit.

An MSR is not a great fit; it's x86 specific and limited to 64 bits of data. The data limitation could be fudged by shoving data into non-standard GPRs, but that will result in truly heinous guest code, and extensibility issues.

Quoted 5 lines

The data limitation is a moot point, because the x86-only thing is a deal breaker. arm64's pKVM work has a near-identical use case for a guest to share memory with a host. I can't think of a clever way to avoid having to support TDX's and SNP's hypervisor-agnostic variants, but we can at least not have multiple KVM variants.

Looking at arm64's pKVM work, i see that it is a recently introduced RFC patch-set and probably relevant to arm64 nVHE hypervisor mode/implementation, and potentially makes sense as it adds guest memory protection as both host and guest kernels are running on the same privilege level ?

Quoted 8 lines

Though i do see that the pKVM stuff adds two hypercalls, specifically :

pkvm_create_mappings() ( I assume this is for setting shared memory regions between host and guest) & pkvm_create_private_mappings().

And the use-cases are quite similar to memory protection architectues use cases, for example, use with virtio devices, guest DMA I/O, etc.

These hypercalls are both private to the host kernel communicating with its hypervisor counterpart, so I don't think they're particularly relevant here. As far as I can see, the more useful thing is to allow the guest to communicate back to the host (and the VMM) that it has opened up a memory window, perhaps for virtio rings or some other shared memory.

We hacked this up as a prototype in the past:

Quoted 9 lines

https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fandroid-kvm.googlesource.com%2Flinux%2F%2B%2Fd12a9e2c12a52cf7140d40cd9fa092dc8a85fac9%255E%2521%2F&amp;data=04%7C01%7Cashish.kalra%40amd.com%7C7ae6bbd9fa6442f9edcc08d8de75d14b%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637503944913839841%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=Juon5nJ7BB6moTWYssRXOWrDOrYfZLmA%2BLrz3s12Ook%3D&amp;reserved=0

but that's all arm64-specific and if we're solving the same problem as you, then let's avoid arch-specific stuff if possible. The way in which the guest discovers the interface will be arch-specific (we already have a mechanism for that and some hypercalls are already allocated by specs from Arm), but the interface back to the VMM and some (most?) of the host handling could be shared.

I have started implementing a similar "hypercall to userspace" functionality for these DMA_SHARE/DMA_UNSHARE type of interfaces corresponding to SEV guest's add/remove shared regions on the x86 platform.

Quoted 7 lines

This does not implement a generic hypercall exiting infrastructure, mainly extends the KVM hypercall support to return back to userspace specifically for add/remove shared region hypercalls and then re-uses the complete userspace I/O callback functionality to resume the guest after returning back from userspace handling of the hypercall.

Looking fwd. to any comments/feedback/thoughts on the above.

Others have mentioned a lack of appetite for generic hypercall intercepts, so this is the right approach.

Thanks,
Steve

#38 lore raw

Re: [PATCH v10 10/16] KVM: x86: Introduce KVM_GET_SHARED_PAGES_LIST ioctl

Quoted 50 lines · Steve Rutherford
Quoted 49 lines · Ashish Kalra
Quoted 47 lines · Will Deacon

[+Marc]

Quoted 44 lines · Ashish Kalra
Quoted 43 lines · Sean Christopherson
Quoted 6 lines · Ashish Kalra
Quoted 5 lines · Steve Rutherford

On Thu, Feb 25, 2021 at 12:20 PM Ashish Kalra <[email protected]> wrote:
Thanks for grabbing the data!

I am fine with both paths. Sean has stated an explicit desire for hypercall exiting, so I think that would be the current consensus.

Yep, though it'd be good to get Paolo's input, too.

Quoted 4 lines
Quoted 4 lines

If we want to do hypercall exiting, this should be in a follow-up series where we implement something more generic, e.g. a hypercall exiting bitmap or hypercall exit list. If we are taking the hypercall exit route, we can drop the kvm side of the hypercall.

I don't think this is a good candidate for arbitrary hypercall interception. Or rather, I think hypercall interception should be an orthogonal implementation.

The guest, including guest firmware, needs to be aware that the hypercall is supported, and the ABI needs to be well-defined. Relying on userspace VMMs to implement a common ABI is an unnecessary risk.

We could make KVM's default behavior be a nop, i.e. have KVM enforce the ABI but require further VMM intervention. But, I just don't see the point, it would save only a few lines of code. It would also limit what KVM could do in the future, e.g. if KVM wanted to do its own bookkeeping _and_ exit to userspace, then mandatory interception would essentially make it impossible for KVM to do bookkeeping while still honoring the interception request.

However, I do think it would make sense to have the userspace exit be a generic exit type. But hey, we already have the necessary ABI defined for that! It's just not used anywhere.

  /* KVM_EXIT_HYPERCALL */
  struct {
          __u64 nr;
          __u64 args[6];
          __u64 ret;
          __u32 longmode;
          __u32 pad;
  } hypercall;
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 2 lines
Quoted 2 lines

Userspace could also handle the MSR using MSR filters (would need to confirm that). Then userspace could also be in control of the cpuid bit.

An MSR is not a great fit; it's x86 specific and limited to 64 bits of data. The data limitation could be fudged by shoving data into non-standard GPRs, but that will result in truly heinous guest code, and extensibility issues.

We may also need to pass-through the MSR to userspace, as it is a part of this complete host (userspace/kernel), OVMF and guest kernel negotiation of the SEV live migration feature.

Host (userspace/kernel) advertises it's support for SEV live migration feature via the CPUID bits, which is queried by OVMF and which in turn adds a new UEFI runtime variable to indicate support for SEV live migration, which is later queried during guest kernel boot and accordingly the guest does a wrmrsl() to custom MSR to complete SEV live migration negotiation and enable it.

Now, the GET_SHARED_REGION_LIST ioctl returns error, until this MSR write enables SEV live migration, hence, preventing userspace to start live migration before the feature support has been negotiated and enabled on all the three components - host, guest OVMF and kernel.

But, now with this ioctl not existing anymore, we will need to pass-through the MSR to userspace too, for it to only initiate live migration once the feature negotiation has been completed.

Thanks,
Ashish

Quoted 55 lines
Quoted 43 lines
Quoted 29 lines
Quoted 12 lines
Quoted 5 lines

The data limitation is a moot point, because the x86-only thing is a deal breaker. arm64's pKVM work has a near-identical use case for a guest to share memory with a host. I can't think of a clever way to avoid having to support TDX's and SNP's hypervisor-agnostic variants, but we can at least not have multiple KVM variants.

Looking at arm64's pKVM work, i see that it is a recently introduced RFC patch-set and probably relevant to arm64 nVHE hypervisor mode/implementation, and potentially makes sense as it adds guest memory protection as both host and guest kernels are running on the same privilege level ?

Quoted 8 lines

Though i do see that the pKVM stuff adds two hypercalls, specifically :

pkvm_create_mappings() ( I assume this is for setting shared memory regions between host and guest) & pkvm_create_private_mappings().

And the use-cases are quite similar to memory protection architectues use cases, for example, use with virtio devices, guest DMA I/O, etc.

These hypercalls are both private to the host kernel communicating with its hypervisor counterpart, so I don't think they're particularly relevant here. As far as I can see, the more useful thing is to allow the guest to communicate back to the host (and the VMM) that it has opened up a memory window, perhaps for virtio rings or some other shared memory.

We hacked this up as a prototype in the past:

Quoted 9 lines

https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fandroid-kvm.googlesource.com%2Flinux%2F%2B%2Fd12a9e2c12a52cf7140d40cd9fa092dc8a85fac9%255E%2521%2F&amp;data=04%7C01%7Cashish.kalra%40amd.com%7C21099e2dd82c477005a008d8e4cf149e%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637510925361923243%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=JputIEFoae3HRO5jvvn%2FTpP8i%2FpMMfWSiZ8a2%2Bna5iQ%3D&amp;reserved=0

but that's all arm64-specific and if we're solving the same problem as you, then let's avoid arch-specific stuff if possible. The way in which the guest discovers the interface will be arch-specific (we already have a mechanism for that and some hypercalls are already allocated by specs from Arm), but the interface back to the VMM and some (most?) of the host handling could be shared.

I have started implementing a similar "hypercall to userspace" functionality for these DMA_SHARE/DMA_UNSHARE type of interfaces corresponding to SEV guest's add/remove shared regions on the x86 platform.

Quoted 7 lines

This does not implement a generic hypercall exiting infrastructure, mainly extends the KVM hypercall support to return back to userspace specifically for add/remove shared region hypercalls and then re-uses the complete userspace I/O callback functionality to resume the guest after returning back from userspace handling of the hypercall.

Looking fwd. to any comments/feedback/thoughts on the above.

Others have mentioned a lack of appetite for generic hypercall intercepts, so this is the right approach.

Thanks,
Steve

#39 lore raw

Re: [PATCH v10 10/16] KVM: x86: Introduce KVM_GET_SHARED_PAGES_LIST ioctl

Quoted 71 lines · Ashish Kalra
Quoted 50 lines · Steve Rutherford
Quoted 49 lines · Ashish Kalra
Quoted 47 lines · Will Deacon

[+Marc]

Quoted 44 lines · Ashish Kalra
Quoted 43 lines · Sean Christopherson
Quoted 6 lines · Ashish Kalra
Quoted 5 lines · Steve Rutherford

On Thu, Feb 25, 2021 at 12:20 PM Ashish Kalra <[email protected]> wrote:
Thanks for grabbing the data!

I am fine with both paths. Sean has stated an explicit desire for hypercall exiting, so I think that would be the current consensus.

Yep, though it'd be good to get Paolo's input, too.

Quoted 4 lines
Quoted 4 lines

If we want to do hypercall exiting, this should be in a follow-up series where we implement something more generic, e.g. a hypercall exiting bitmap or hypercall exit list. If we are taking the hypercall exit route, we can drop the kvm side of the hypercall.

I don't think this is a good candidate for arbitrary hypercall interception. Or rather, I think hypercall interception should be an orthogonal implementation.

The guest, including guest firmware, needs to be aware that the hypercall is supported, and the ABI needs to be well-defined. Relying on userspace VMMs to implement a common ABI is an unnecessary risk.

We could make KVM's default behavior be a nop, i.e. have KVM enforce the ABI but require further VMM intervention. But, I just don't see the point, it would save only a few lines of code. It would also limit what KVM could do in the future, e.g. if KVM wanted to do its own bookkeeping _and_ exit to userspace, then mandatory interception would essentially make it impossible for KVM to do bookkeeping while still honoring the interception request.

However, I do think it would make sense to have the userspace exit be a generic exit type. But hey, we already have the necessary ABI defined for that! It's just not used anywhere.

  /* KVM_EXIT_HYPERCALL */
  struct {
          __u64 nr;
          __u64 args[6];
          __u64 ret;
          __u32 longmode;
          __u32 pad;
  } hypercall;
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 2 lines
Quoted 2 lines

Userspace could also handle the MSR using MSR filters (would need to confirm that). Then userspace could also be in control of the cpuid bit.

An MSR is not a great fit; it's x86 specific and limited to 64 bits of data. The data limitation could be fudged by shoving data into non-standard GPRs, but that will result in truly heinous guest code, and extensibility issues.

We may also need to pass-through the MSR to userspace, as it is a part of this complete host (userspace/kernel), OVMF and guest kernel negotiation of the SEV live migration feature.

Host (userspace/kernel) advertises it's support for SEV live migration feature via the CPUID bits, which is queried by OVMF and which in turn adds a new UEFI runtime variable to indicate support for SEV live migration, which is later queried during guest kernel boot and accordingly the guest does a wrmrsl() to custom MSR to complete SEV live migration negotiation and enable it.

Quoted 8 lines

Now, the GET_SHARED_REGION_LIST ioctl returns error, until this MSR write enables SEV live migration, hence, preventing userspace to start live migration before the feature support has been negotiated and enabled on all the three components - host, guest OVMF and kernel.

But, now with this ioctl not existing anymore, we will need to pass-through the MSR to userspace too, for it to only initiate live migration once the feature negotiation has been completed.

Hey Ashish,

I can't tell if you were waiting for feedback on this before posting the follow-up patch series.

Here are a few options:
1) Add the MSR explicitly to the list of custom kvm MSRs, but don't have it hooked up anywhere. The expectation would be for the VMM to use msr intercepts to handle the reads and writes. If that seems weird, have svm_set_msr (or whatever) explicitly ignore it.
2) Add a getter and setter for the MSR. Only allow guests to use it if they are sev_guests with the requisite CPUID bit set.

I think I prefer the former, and it should work fine from my understanding of the msr intercepts implementation. I'm also open to other ideas. You could also have the MSR write trigger a KVM_EXIT of the same type as the hypercall, but have it just say "the msr value changed to XYZ", but that design sounds awkward.

Thanks,
Steve

#40 lore raw

Re: [PATCH v10 10/16] KVM: x86: Introduce KVM_GET_SHARED_PAGES_LIST ioctl

Hello Steve,

Quoted 77 lines · Steve Rutherford
Quoted 76 lines · Ashish Kalra
Quoted 50 lines · Steve Rutherford
Quoted 49 lines · Ashish Kalra
Quoted 47 lines · Will Deacon

[+Marc]

Quoted 44 lines · Ashish Kalra
Quoted 43 lines · Sean Christopherson
Quoted 6 lines · Ashish Kalra
Quoted 5 lines · Steve Rutherford

On Thu, Feb 25, 2021 at 12:20 PM Ashish Kalra <[email protected]> wrote:
Thanks for grabbing the data!

I am fine with both paths. Sean has stated an explicit desire for hypercall exiting, so I think that would be the current consensus.

Yep, though it'd be good to get Paolo's input, too.

Quoted 4 lines
Quoted 4 lines

If we want to do hypercall exiting, this should be in a follow-up series where we implement something more generic, e.g. a hypercall exiting bitmap or hypercall exit list. If we are taking the hypercall exit route, we can drop the kvm side of the hypercall.

I don't think this is a good candidate for arbitrary hypercall interception. Or rather, I think hypercall interception should be an orthogonal implementation.

The guest, including guest firmware, needs to be aware that the hypercall is supported, and the ABI needs to be well-defined. Relying on userspace VMMs to implement a common ABI is an unnecessary risk.

We could make KVM's default behavior be a nop, i.e. have KVM enforce the ABI but require further VMM intervention. But, I just don't see the point, it would save only a few lines of code. It would also limit what KVM could do in the future, e.g. if KVM wanted to do its own bookkeeping _and_ exit to userspace, then mandatory interception would essentially make it impossible for KVM to do bookkeeping while still honoring the interception request.

However, I do think it would make sense to have the userspace exit be a generic exit type. But hey, we already have the necessary ABI defined for that! It's just not used anywhere.

  /* KVM_EXIT_HYPERCALL */
  struct {
          __u64 nr;
          __u64 args[6];
          __u64 ret;
          __u32 longmode;
          __u32 pad;
  } hypercall;
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 2 lines
Quoted 2 lines

Userspace could also handle the MSR using MSR filters (would need to confirm that). Then userspace could also be in control of the cpuid bit.

An MSR is not a great fit; it's x86 specific and limited to 64 bits of data. The data limitation could be fudged by shoving data into non-standard GPRs, but that will result in truly heinous guest code, and extensibility issues.

We may also need to pass-through the MSR to userspace, as it is a part of this complete host (userspace/kernel), OVMF and guest kernel negotiation of the SEV live migration feature.

Host (userspace/kernel) advertises it's support for SEV live migration feature via the CPUID bits, which is queried by OVMF and which in turn adds a new UEFI runtime variable to indicate support for SEV live migration, which is later queried during guest kernel boot and accordingly the guest does a wrmrsl() to custom MSR to complete SEV live migration negotiation and enable it.

Now, the GET_SHARED_REGION_LIST ioctl returns error, until this MSR write enables SEV live migration, hence, preventing userspace to start live migration before the feature support has been negotiated and enabled on all the three components - host, guest OVMF and kernel.

Quoted 6 lines
Quoted 3 lines

But, now with this ioctl not existing anymore, we will need to pass-through the MSR to userspace too, for it to only initiate live migration once the feature negotiation has been completed.

I can't tell if you were waiting for feedback on this before posting the follow-up patch series.

Actually, i am going to post the follow-up patch series upstream early next week.

I have already added support for MSR handling and exit to userspace, the current implementation looks like this :

The custom MSR is hooked both in svm_get_msr() and svm_set_msr():

@@ -2800,6 +2800,17 @@ static int svm_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)

        case MSR_F10H_DECFG:
                msr_info->data = svm->msr_decfg;
                break;

+ case MSR_KVM_SEV_LIVE_MIGRATION:
+ if (!sev_guest(vcpu->kvm))
+ return 1;
+
+ if (!guest_cpuid_has(vcpu, KVM_FEATURE_SEV_LIVE_MIGRATION))
+ return 1;
+
+ /*
+ * Let userspace handle the MSR using MSR filters.
+ */
+ return KVM_MSR_RET_FILTERED;

So there is special check added for both sev_guest() and requisite CPUID bit set in guest CPUID, if either fails this will signal #GP to guest.

Otherwise, it returns MSR_FILTER return code, which will allow userspace to use msr intercepts to handle the reads and writes via userspace exits using KVM_EXIT_X86_RDMSR/KVM_EXIT_X86_WRMSR.

Let me know if you have any feedback/comments on the above handling.

Thanks,
Ashish

Quoted 17 lines

Here are a few options:
1) Add the MSR explicitly to the list of custom kvm MSRs, but don't have it hooked up anywhere. The expectation would be for the VMM to use msr intercepts to handle the reads and writes. If that seems weird, have svm_set_msr (or whatever) explicitly ignore it.
2) Add a getter and setter for the MSR. Only allow guests to use it if they are sev_guests with the requisite CPUID bit set.

I think I prefer the former, and it should work fine from my understanding of the msr intercepts implementation. I'm also open to other ideas. You could also have the MSR write trigger a KVM_EXIT of the same type as the hypercall, but have it just say "the msr value changed to XYZ", but that design sounds awkward.

Thanks,
Steve

#41 lore raw

Re: [PATCH v10 10/16] KVM: x86: Introduce KVM_GET_SHARED_PAGES_LIST ioctl

In reply to Sean Christopherson
Quoted 55 lines · Sean Christopherson

+Will and Quentin (arm64)

Moving the non-KVM x86 folks to bcc, I don't they care about KVM details at this point.

Quoted 6 lines · Ashish Kalra
Quoted 5 lines · Steve Rutherford

On Thu, Feb 25, 2021 at 12:20 PM Ashish Kalra <[email protected]> wrote:
Thanks for grabbing the data!

I am fine with both paths. Sean has stated an explicit desire for hypercall exiting, so I think that would be the current consensus.

Yep, though it'd be good to get Paolo's input, too.

Quoted 4 lines
Quoted 4 lines

If we want to do hypercall exiting, this should be in a follow-up series where we implement something more generic, e.g. a hypercall exiting bitmap or hypercall exit list. If we are taking the hypercall exit route, we can drop the kvm side of the hypercall.

I don't think this is a good candidate for arbitrary hypercall interception. Or rather, I think hypercall interception should be an orthogonal implementation.

The guest, including guest firmware, needs to be aware that the hypercall is supported, and the ABI needs to be well-defined. Relying on userspace VMMs to implement a common ABI is an unnecessary risk.

We could make KVM's default behavior be a nop, i.e. have KVM enforce the ABI but require further VMM intervention. But, I just don't see the point, it would save only a few lines of code. It would also limit what KVM could do in the future, e.g. if KVM wanted to do its own bookkeeping _and_ exit to userspace, then mandatory interception would essentially make it impossible for KVM to do bookkeeping while still honoring the interception request.

However, I do think it would make sense to have the userspace exit be a generic exit type. But hey, we already have the necessary ABI defined for that! It's just not used anywhere.

	/* KVM_EXIT_HYPERCALL */
	struct {
		__u64 nr;
		__u64 args[6];
		__u64 ret;
		__u32 longmode;
		__u32 pad;
	} hypercall;
Quoted 2 lines
Quoted 2 lines

Userspace could also handle the MSR using MSR filters (would need to confirm that). Then userspace could also be in control of the cpuid bit.

An MSR is not a great fit; it's x86 specific and limited to 64 bits of data. The data limitation could be fudged by shoving data into non-standard GPRs, but that will result in truly heinous guest code, and extensibility issues.

Quoted 6 lines

The data limitation is a moot point, because the x86-only thing is a deal breaker. arm64's pKVM work has a near-identical use case for a guest to share memory with a host. I can't think of a clever way to avoid having to support TDX's and SNP's hypervisor-agnostic variants, but we can at least not have multiple KVM variants.

Potentially, there is another reason for in-kernel hypercall handling considering SEV-SNP. In case of SEV-SNP the RMP table tracks the state of each guest page, for instance pages in hypervisor state, i.e., pages with C=0 and pages in guest valid state with C=1.

Now, there shouldn't be a need for page encryption status hypercalls on SEV-SNP as KVM can track & reference guest page status directly using the RMP table.

As KVM maintains the RMP table, therefore we will need SET/GET type of interfaces to provide the guest page encryption status to userspace.

For the above reason if we do in-kernel hypercall handling for page encryption status (which we probably won't require for SEV-SNP & correspondingly there will be no hypercall exiting), then we can implement a standard GET/SET ioctl interface to get/set the guest page encryption status for userspace, which will work across SEV, SEV-ES and SEV-SNP.

Thanks,
Ashish

#42 lore raw

Re: [PATCH v10 10/16] KVM: x86: Introduce KVM_GET_SHARED_PAGES_LIST ioctl

Quoted 63 lines · Ashish Kalra
Quoted 55 lines · Sean Christopherson

+Will and Quentin (arm64)

Moving the non-KVM x86 folks to bcc, I don't they care about KVM details at this point.

Quoted 6 lines · Ashish Kalra
Quoted 5 lines · Steve Rutherford

On Thu, Feb 25, 2021 at 12:20 PM Ashish Kalra <[email protected]> wrote:
Thanks for grabbing the data!

I am fine with both paths. Sean has stated an explicit desire for hypercall exiting, so I think that would be the current consensus.

Yep, though it'd be good to get Paolo's input, too.

Quoted 4 lines
Quoted 4 lines

If we want to do hypercall exiting, this should be in a follow-up series where we implement something more generic, e.g. a hypercall exiting bitmap or hypercall exit list. If we are taking the hypercall exit route, we can drop the kvm side of the hypercall.

I don't think this is a good candidate for arbitrary hypercall interception. Or rather, I think hypercall interception should be an orthogonal implementation.

The guest, including guest firmware, needs to be aware that the hypercall is supported, and the ABI needs to be well-defined. Relying on userspace VMMs to implement a common ABI is an unnecessary risk.

We could make KVM's default behavior be a nop, i.e. have KVM enforce the ABI but require further VMM intervention. But, I just don't see the point, it would save only a few lines of code. It would also limit what KVM could do in the future, e.g. if KVM wanted to do its own bookkeeping _and_ exit to userspace, then mandatory interception would essentially make it impossible for KVM to do bookkeeping while still honoring the interception request.

However, I do think it would make sense to have the userspace exit be a generic exit type. But hey, we already have the necessary ABI defined for that! It's just not used anywhere.

	/* KVM_EXIT_HYPERCALL */
	struct {
		__u64 nr;
		__u64 args[6];
		__u64 ret;
		__u32 longmode;
		__u32 pad;
	} hypercall;
Quoted 2 lines
Quoted 2 lines

Userspace could also handle the MSR using MSR filters (would need to confirm that). Then userspace could also be in control of the cpuid bit.

An MSR is not a great fit; it's x86 specific and limited to 64 bits of data. The data limitation could be fudged by shoving data into non-standard GPRs, but that will result in truly heinous guest code, and extensibility issues.

Quoted 6 lines

The data limitation is a moot point, because the x86-only thing is a deal breaker. arm64's pKVM work has a near-identical use case for a guest to share memory with a host. I can't think of a clever way to avoid having to support TDX's and SNP's hypervisor-agnostic variants, but we can at least not have multiple KVM variants.

Quoted 8 lines

Potentially, there is another reason for in-kernel hypercall handling considering SEV-SNP. In case of SEV-SNP the RMP table tracks the state of each guest page, for instance pages in hypervisor state, i.e., pages with C=0 and pages in guest valid state with C=1.

Now, there shouldn't be a need for page encryption status hypercalls on SEV-SNP as KVM can track & reference guest page status directly using the RMP table.

Relying on the RMP table itself would require locking the RMP table for an extended duration, and walking the entire RMP to find shared pages would be very inefficient.

Quoted 2 lines

As KVM maintains the RMP table, therefore we will need SET/GET type of interfaces to provide the guest page encryption status to userspace.

Hrm, somehow I temporarily forgot about SNP and TDX adding their own hypercalls for converting between shared and private. And in the case of TDX, the hypercall can't be trusted, i.e. is just a hint, otherwise the guest could induce a #MC in the host.

But, the different guest behavior doesn't require KVM to maintain a list/tree, e.g. adding a dedicated KVM_EXIT_* for notifying userspace of page encryption status changes would also suffice.

Actually, that made me think of another argument against maintaining a list in
KVM: there's no way to notify userspace that a page's status has changed. Userspace would need to query KVM to do GET_LIST after every GET_DIRTY. Obviously not a huge issue, but it does make migration slightly less efficient.

On a related topic, there are fatal race conditions that will require careful coordination between guest and host, and will effectively be wired into the ABI. SNP and TDX don't suffer these issues because host awareness of status is atomic with respect to the guest actually writing the page with the new encryption status.

For SEV live migration...

If the guest does the hypercall after writing the page, then the guest is hosed if it gets migrated while writing the page (scenario #1):

vCPU Userspace
zero_bytes[0:N]
<transfers written bytes as private instead of shared> <migrates vCPU>
zero_bytes[N+1:4095]
set_shared (dest)
kaboom!

If userspace does GET_DIRTY after GET_LIST, then the host would transfer bad data by consuming a stale list (scenario #2):

vCPU Userspace
get_list (from KVM or internally)
set_shared (src)
zero_page (src)
get_dirty
<transfers private data instead of shared> <migrates vCPU>
kaboom!

If both guest and host order things to avoid #1 and #2, the host can still migrate the wrong data (scenario #3):

vCPU Userspace
set_private
zero_bytes[0:4096]
get_dirty
set_shared (src)
get_list
<transfers as shared instead of private> <migrates vCPU>
set_private (dest)
kaboom!

Scenario #3 is unlikely, but plausible, e.g. if the guest bails from its conversion flow for whatever reason, after making the initial hypercall. Maybe it goes without saying, but to address #3, the guest must consider existing data as lost the instant it tells the host the page has been converted to a different type.

Quoted 3 lines

For the above reason if we do in-kernel hypercall handling for page encryption status (which we probably won't require for SEV-SNP & correspondingly there will be no hypercall exiting),

As above, that doesn't preclude KVM from exiting to userspace on conversion.

Quoted 3 lines

then we can implement a standard GET/SET ioctl interface to get/set the guest page encryption status for userspace, which will work across SEV, SEV-ES and SEV-SNP.

#43 lore raw

Re: [PATCH v10 10/16] KVM: x86: Introduce KVM_GET_SHARED_PAGES_LIST ioctl

Quoted 77 lines · Sean Christopherson
Quoted 63 lines · Ashish Kalra
Quoted 55 lines · Sean Christopherson

+Will and Quentin (arm64)

Moving the non-KVM x86 folks to bcc, I don't they care about KVM details at this point.

Quoted 6 lines · Ashish Kalra
Quoted 5 lines · Steve Rutherford

On Thu, Feb 25, 2021 at 12:20 PM Ashish Kalra <[email protected]> wrote:
Thanks for grabbing the data!

I am fine with both paths. Sean has stated an explicit desire for hypercall exiting, so I think that would be the current consensus.

Yep, though it'd be good to get Paolo's input, too.

Quoted 4 lines
Quoted 4 lines

If we want to do hypercall exiting, this should be in a follow-up series where we implement something more generic, e.g. a hypercall exiting bitmap or hypercall exit list. If we are taking the hypercall exit route, we can drop the kvm side of the hypercall.

I don't think this is a good candidate for arbitrary hypercall interception. Or rather, I think hypercall interception should be an orthogonal implementation.

The guest, including guest firmware, needs to be aware that the hypercall is supported, and the ABI needs to be well-defined. Relying on userspace VMMs to implement a common ABI is an unnecessary risk.

We could make KVM's default behavior be a nop, i.e. have KVM enforce the ABI but require further VMM intervention. But, I just don't see the point, it would save only a few lines of code. It would also limit what KVM could do in the future, e.g. if KVM wanted to do its own bookkeeping _and_ exit to userspace, then mandatory interception would essentially make it impossible for KVM to do bookkeeping while still honoring the interception request.

However, I do think it would make sense to have the userspace exit be a generic exit type. But hey, we already have the necessary ABI defined for that! It's just not used anywhere.

	/* KVM_EXIT_HYPERCALL */
	struct {
		__u64 nr;
		__u64 args[6];
		__u64 ret;
		__u32 longmode;
		__u32 pad;
	} hypercall;
Quoted 2 lines
Quoted 2 lines

Userspace could also handle the MSR using MSR filters (would need to confirm that). Then userspace could also be in control of the cpuid bit.

An MSR is not a great fit; it's x86 specific and limited to 64 bits of data. The data limitation could be fudged by shoving data into non-standard GPRs, but that will result in truly heinous guest code, and extensibility issues.

Quoted 6 lines

The data limitation is a moot point, because the x86-only thing is a deal breaker. arm64's pKVM work has a near-identical use case for a guest to share memory with a host. I can't think of a clever way to avoid having to support TDX's and SNP's hypervisor-agnostic variants, but we can at least not have multiple KVM variants.

Quoted 8 lines

Potentially, there is another reason for in-kernel hypercall handling considering SEV-SNP. In case of SEV-SNP the RMP table tracks the state of each guest page, for instance pages in hypervisor state, i.e., pages with C=0 and pages in guest valid state with C=1.

Now, there shouldn't be a need for page encryption status hypercalls on SEV-SNP as KVM can track & reference guest page status directly using the RMP table.

Relying on the RMP table itself would require locking the RMP table for an extended duration, and walking the entire RMP to find shared pages would be very inefficient.

Quoted 7 lines
Quoted 2 lines

As KVM maintains the RMP table, therefore we will need SET/GET type of interfaces to provide the guest page encryption status to userspace.

Hrm, somehow I temporarily forgot about SNP and TDX adding their own hypercalls for converting between shared and private. And in the case of TDX, the hypercall can't be trusted, i.e. is just a hint, otherwise the guest could induce a #MC in the host.

One question here, is this because if hypercall can cause direct modifications to the shared EPT, it can induce #MC in the host ?

Thanks,
Ashish

#44 lore raw

Re: [PATCH v10 10/16] KVM: x86: Introduce KVM_GET_SHARED_PAGES_LIST ioctl

In reply to Sean Christopherson
Quoted 99 lines · Sean Christopherson
Quoted 59 lines · Ashish Kalra
Quoted 52 lines · Sean Christopherson

+Will and Quentin (arm64)

Moving the non-KVM x86 folks to bcc, I don't they care about KVM details at this point.

Quoted 6 lines · Ashish Kalra
Quoted 5 lines · Steve Rutherford

On Thu, Feb 25, 2021 at 12:20 PM Ashish Kalra <[email protected]> wrote:
Thanks for grabbing the data!

I am fine with both paths. Sean has stated an explicit desire for hypercall exiting, so I think that would be the current consensus.

Yep, though it'd be good to get Paolo's input, too.

Quoted 4 lines
Quoted 4 lines

If we want to do hypercall exiting, this should be in a follow-up series where we implement something more generic, e.g. a hypercall exiting bitmap or hypercall exit list. If we are taking the hypercall exit route, we can drop the kvm side of the hypercall.

I don't think this is a good candidate for arbitrary hypercall interception. Or rather, I think hypercall interception should be an orthogonal implementation.

The guest, including guest firmware, needs to be aware that the hypercall is supported, and the ABI needs to be well-defined. Relying on userspace VMMs to implement a common ABI is an unnecessary risk.

We could make KVM's default behavior be a nop, i.e. have KVM enforce the ABI but require further VMM intervention. But, I just don't see the point, it would save only a few lines of code. It would also limit what KVM could do in the future, e.g. if KVM wanted to do its own bookkeeping _and_ exit to userspace, then mandatory interception would essentially make it impossible for KVM to do bookkeeping while still honoring the interception request.

However, I do think it would make sense to have the userspace exit be a generic exit type. But hey, we already have the necessary ABI defined for that! It's just not used anywhere.

	/* KVM_EXIT_HYPERCALL */
	struct {
		__u64 nr;
		__u64 args[6];
		__u64 ret;
		__u32 longmode;
		__u32 pad;
	} hypercall;
Quoted 2 lines
Quoted 2 lines

Userspace could also handle the MSR using MSR filters (would need to confirm that). Then userspace could also be in control of the cpuid bit.

An MSR is not a great fit; it's x86 specific and limited to 64 bits of data. The data limitation could be fudged by shoving data into non-standard GPRs, but that will result in truly heinous guest code, and extensibility issues.

Quoted 6 lines

The data limitation is a moot point, because the x86-only thing is a deal breaker. arm64's pKVM work has a near-identical use case for a guest to share memory with a host. I can't think of a clever way to avoid having to support TDX's and SNP's hypervisor-agnostic variants, but we can at least not have multiple KVM variants.

Quoted 8 lines

Potentially, there is another reason for in-kernel hypercall handling considering SEV-SNP. In case of SEV-SNP the RMP table tracks the state of each guest page, for instance pages in hypervisor state, i.e., pages with C=0 and pages in guest valid state with C=1.

Now, there shouldn't be a need for page encryption status hypercalls on SEV-SNP as KVM can track & reference guest page status directly using the RMP table.

Relying on the RMP table itself would require locking the RMP table for an extended duration, and walking the entire RMP to find shared pages would be very inefficient.

Quoted 2 lines

As KVM maintains the RMP table, therefore we will need SET/GET type of interfaces to provide the guest page encryption status to userspace.

Hrm, somehow I temporarily forgot about SNP and TDX adding their own hypercalls for converting between shared and private. And in the case of TDX, the hypercall can't be trusted, i.e. is just a hint, otherwise the guest could induce a #MC in the host.

But, the different guest behavior doesn't require KVM to maintain a list/tree, e.g. adding a dedicated KVM_EXIT_* for notifying userspace of page encryption status changes would also suffice.

Actually, that made me think of another argument against maintaining a list in
KVM: there's no way to notify userspace that a page's status has changed. Userspace would need to query KVM to do GET_LIST after every GET_DIRTY. Obviously not a huge issue, but it does make migration slightly less efficient.

On a related topic, there are fatal race conditions that will require careful coordination between guest and host, and will effectively be wired into the ABI. SNP and TDX don't suffer these issues because host awareness of status is atomic with respect to the guest actually writing the page with the new encryption status.

For SEV live migration...

If the guest does the hypercall after writing the page, then the guest is hosed if it gets migrated while writing the page (scenario #1):

Quoted 7 lines

vCPU Userspace
zero_bytes[0:N]
<transfers written bytes as private instead of shared> <migrates vCPU>
zero_bytes[N+1:4095]
set_shared (dest)
kaboom!

Maybe I am missing something, this is not any different from a normal operation inside a guest. Making a page shared/private in the page table does not update the content of the page itself. In your above case, I assume zero_bytes[N+1:4095] are written by the destination VM. The memory region was private in the source VM page table, so, those writes will be performed encrypted. The destination VM later changed the memory to shared, but nobody wrote to the memory after it has been transitioned to the  shared, so a reader of the memory should get ciphertext and unless there was a write after the set_shared (dest).

Quoted 11 lines

If userspace does GET_DIRTY after GET_LIST, then the host would transfer bad data by consuming a stale list (scenario #2):

vCPU Userspace
get_list (from KVM or internally)
set_shared (src)
zero_page (src)
get_dirty
<transfers private data instead of shared> <migrates vCPU>
kaboom!

I don't remember how things are done in recent Ashish Qemu/KVM patches but in previous series, the get_dirty() happens before the querying the encrypted state. There was some logic in VMM to resync the encrypted bitmap during the final migration stage and perform any additional data transfer since last sync.

Quoted 13 lines

If both guest and host order things to avoid #1 and #2, the host can still migrate the wrong data (scenario #3):

vCPU Userspace
set_private
zero_bytes[0:4096]
get_dirty
set_shared (src)
get_list
<transfers as shared instead of private> <migrates vCPU>
set_private (dest)
kaboom!

Since there was no write to the memory after the set_shared (src), so the content of the page should not have changed. After the set_private (dest), the caller should be seeing the same content written by the zero_bytes[0:4096]

Quoted 14 lines

Scenario #3 is unlikely, but plausible, e.g. if the guest bails from its conversion flow for whatever reason, after making the initial hypercall. Maybe it goes without saying, but to address #3, the guest must consider existing data as lost the instant it tells the host the page has been converted to a different type.

Quoted 3 lines

For the above reason if we do in-kernel hypercall handling for page encryption status (which we probably won't require for SEV-SNP & correspondingly there will be no hypercall exiting),

As above, that doesn't preclude KVM from exiting to userspace on conversion.

Quoted 3 lines

then we can implement a standard GET/SET ioctl interface to get/set the guest page encryption status for userspace, which will work across SEV, SEV-ES and SEV-SNP.

#45 lore raw

Re: [PATCH v10 10/16] KVM: x86: Introduce KVM_GET_SHARED_PAGES_LIST ioctl

Quoted 134 lines · Brijesh Singh
Quoted 99 lines · Sean Christopherson
Quoted 59 lines · Ashish Kalra
Quoted 52 lines · Sean Christopherson

+Will and Quentin (arm64)

Moving the non-KVM x86 folks to bcc, I don't they care about KVM details at this point.

Quoted 6 lines · Ashish Kalra
Quoted 5 lines · Steve Rutherford

On Thu, Feb 25, 2021 at 12:20 PM Ashish Kalra <[email protected]> wrote:
Thanks for grabbing the data!

I am fine with both paths. Sean has stated an explicit desire for hypercall exiting, so I think that would be the current consensus.

Yep, though it'd be good to get Paolo's input, too.

Quoted 4 lines
Quoted 4 lines

If we want to do hypercall exiting, this should be in a follow-up series where we implement something more generic, e.g. a hypercall exiting bitmap or hypercall exit list. If we are taking the hypercall exit route, we can drop the kvm side of the hypercall.

I don't think this is a good candidate for arbitrary hypercall interception. Or rather, I think hypercall interception should be an orthogonal implementation.

The guest, including guest firmware, needs to be aware that the hypercall is supported, and the ABI needs to be well-defined. Relying on userspace VMMs to implement a common ABI is an unnecessary risk.

We could make KVM's default behavior be a nop, i.e. have KVM enforce the ABI but require further VMM intervention. But, I just don't see the point, it would save only a few lines of code. It would also limit what KVM could do in the future, e.g. if KVM wanted to do its own bookkeeping _and_ exit to userspace, then mandatory interception would essentially make it impossible for KVM to do bookkeeping while still honoring the interception request.

However, I do think it would make sense to have the userspace exit be a generic exit type. But hey, we already have the necessary ABI defined for that! It's just not used anywhere.

	/* KVM_EXIT_HYPERCALL */
	struct {
		__u64 nr;
		__u64 args[6];
		__u64 ret;
		__u32 longmode;
		__u32 pad;
	} hypercall;
Quoted 2 lines
Quoted 2 lines

Userspace could also handle the MSR using MSR filters (would need to confirm that). Then userspace could also be in control of the cpuid bit.

An MSR is not a great fit; it's x86 specific and limited to 64 bits of data. The data limitation could be fudged by shoving data into non-standard GPRs, but that will result in truly heinous guest code, and extensibility issues.

Quoted 6 lines

The data limitation is a moot point, because the x86-only thing is a deal breaker. arm64's pKVM work has a near-identical use case for a guest to share memory with a host. I can't think of a clever way to avoid having to support TDX's and SNP's hypervisor-agnostic variants, but we can at least not have multiple KVM variants.

Quoted 8 lines

Potentially, there is another reason for in-kernel hypercall handling considering SEV-SNP. In case of SEV-SNP the RMP table tracks the state of each guest page, for instance pages in hypervisor state, i.e., pages with C=0 and pages in guest valid state with C=1.

Now, there shouldn't be a need for page encryption status hypercalls on SEV-SNP as KVM can track & reference guest page status directly using the RMP table.

Relying on the RMP table itself would require locking the RMP table for an extended duration, and walking the entire RMP to find shared pages would be very inefficient.

Quoted 2 lines

As KVM maintains the RMP table, therefore we will need SET/GET type of interfaces to provide the guest page encryption status to userspace.

Hrm, somehow I temporarily forgot about SNP and TDX adding their own hypercalls for converting between shared and private. And in the case of TDX, the hypercall can't be trusted, i.e. is just a hint, otherwise the guest could induce a #MC in the host.

But, the different guest behavior doesn't require KVM to maintain a list/tree, e.g. adding a dedicated KVM_EXIT_* for notifying userspace of page encryption status changes would also suffice.

Actually, that made me think of another argument against maintaining a list in
KVM: there's no way to notify userspace that a page's status has changed. Userspace would need to query KVM to do GET_LIST after every GET_DIRTY. Obviously not a huge issue, but it does make migration slightly less efficient.

On a related topic, there are fatal race conditions that will require careful coordination between guest and host, and will effectively be wired into the ABI. SNP and TDX don't suffer these issues because host awareness of status is atomic with respect to the guest actually writing the page with the new encryption status.

For SEV live migration...

If the guest does the hypercall after writing the page, then the guest is hosed if it gets migrated while writing the page (scenario #1):

Quoted 7 lines

vCPU Userspace
zero_bytes[0:N]
<transfers written bytes as private instead of shared> <migrates vCPU>
zero_bytes[N+1:4095]
set_shared (dest)
kaboom!

Maybe I am missing something, this is not any different from a normal operation inside a guest. Making a page shared/private in the page table does not update the content of the page itself. In your above case, I assume zero_bytes[N+1:4095] are written by the destination VM. The memory region was private in the source VM page table, so, those writes will be performed encrypted. The destination VM later changed the memory to shared, but nobody wrote to the memory after it has been transitioned to the  shared, so a reader of the memory should get ciphertext and unless there was a write after the set_shared (dest).

Quoted 11 lines

If userspace does GET_DIRTY after GET_LIST, then the host would transfer bad data by consuming a stale list (scenario #2):

vCPU Userspace
get_list (from KVM or internally)
set_shared (src)
zero_page (src)
get_dirty
<transfers private data instead of shared> <migrates vCPU>
kaboom!

Quoted 7 lines

I don't remember how things are done in recent Ashish Qemu/KVM patches but in previous series, the get_dirty() happens before the querying the encrypted state. There was some logic in VMM to resync the encrypted bitmap during the final migration stage and perform any additional data transfer since last sync.

Yes, we do that and in fact, we added logic in VMM to resync the encrypted bitmap after every migration iteration and if there is a difference in encrypted page states, then we perform additional data transfers corresponding to those changes.

Thanks,
Ashish

#46 lore raw

Re: [PATCH v10 10/16] KVM: x86: Introduce KVM_GET_SHARED_PAGES_LIST ioctl

In reply to Brijesh Singh
Quoted 157 lines · Brijesh Singh
Quoted 99 lines · Sean Christopherson
Quoted 59 lines · Ashish Kalra
Quoted 52 lines · Sean Christopherson

+Will and Quentin (arm64)

Moving the non-KVM x86 folks to bcc, I don't they care about KVM details at this point.

Quoted 6 lines · Ashish Kalra
Quoted 5 lines · Steve Rutherford

On Thu, Feb 25, 2021 at 12:20 PM Ashish Kalra <[email protected]> wrote:
Thanks for grabbing the data!

I am fine with both paths. Sean has stated an explicit desire for hypercall exiting, so I think that would be the current consensus.

Yep, though it'd be good to get Paolo's input, too.

Quoted 4 lines
Quoted 4 lines

If we want to do hypercall exiting, this should be in a follow-up series where we implement something more generic, e.g. a hypercall exiting bitmap or hypercall exit list. If we are taking the hypercall exit route, we can drop the kvm side of the hypercall.

I don't think this is a good candidate for arbitrary hypercall interception. Or rather, I think hypercall interception should be an orthogonal implementation.

The guest, including guest firmware, needs to be aware that the hypercall is supported, and the ABI needs to be well-defined. Relying on userspace VMMs to implement a common ABI is an unnecessary risk.

We could make KVM's default behavior be a nop, i.e. have KVM enforce the ABI but require further VMM intervention. But, I just don't see the point, it would save only a few lines of code. It would also limit what KVM could do in the future, e.g. if KVM wanted to do its own bookkeeping _and_ exit to userspace, then mandatory interception would essentially make it impossible for KVM to do bookkeeping while still honoring the interception request.

However, I do think it would make sense to have the userspace exit be a generic exit type. But hey, we already have the necessary ABI defined for that! It's just not used anywhere.

    /* KVM_EXIT_HYPERCALL */
    struct {
            __u64 nr;
            __u64 args[6];
            __u64 ret;
            __u32 longmode;
            __u32 pad;
    } hypercall;
Quoted 2 lines
Quoted 2 lines

Userspace could also handle the MSR using MSR filters (would need to confirm that). Then userspace could also be in control of the cpuid bit.

An MSR is not a great fit; it's x86 specific and limited to 64 bits of data. The data limitation could be fudged by shoving data into non-standard GPRs, but that will result in truly heinous guest code, and extensibility issues.

Quoted 6 lines

The data limitation is a moot point, because the x86-only thing is a deal breaker. arm64's pKVM work has a near-identical use case for a guest to share memory with a host. I can't think of a clever way to avoid having to support TDX's and SNP's hypervisor-agnostic variants, but we can at least not have multiple KVM variants.

Quoted 8 lines

Potentially, there is another reason for in-kernel hypercall handling considering SEV-SNP. In case of SEV-SNP the RMP table tracks the state of each guest page, for instance pages in hypervisor state, i.e., pages with C=0 and pages in guest valid state with C=1.

Now, there shouldn't be a need for page encryption status hypercalls on SEV-SNP as KVM can track & reference guest page status directly using the RMP table.

Relying on the RMP table itself would require locking the RMP table for an extended duration, and walking the entire RMP to find shared pages would be very inefficient.

Quoted 2 lines

As KVM maintains the RMP table, therefore we will need SET/GET type of interfaces to provide the guest page encryption status to userspace.

Hrm, somehow I temporarily forgot about SNP and TDX adding their own hypercalls for converting between shared and private. And in the case of TDX, the hypercall can't be trusted, i.e. is just a hint, otherwise the guest could induce a #MC in the host.

But, the different guest behavior doesn't require KVM to maintain a list/tree, e.g. adding a dedicated KVM_EXIT_* for notifying userspace of page encryption status changes would also suffice.

Actually, that made me think of another argument against maintaining a list in
KVM: there's no way to notify userspace that a page's status has changed. Userspace would need to query KVM to do GET_LIST after every GET_DIRTY. Obviously not a huge issue, but it does make migration slightly less efficient.

On a related topic, there are fatal race conditions that will require careful coordination between guest and host, and will effectively be wired into the ABI. SNP and TDX don't suffer these issues because host awareness of status is atomic with respect to the guest actually writing the page with the new encryption status.

For SEV live migration...

If the guest does the hypercall after writing the page, then the guest is hosed if it gets migrated while writing the page (scenario #1):

Quoted 7 lines

vCPU Userspace
zero_bytes[0:N]
<transfers written bytes as private instead of shared>
<migrates vCPU>
zero_bytes[N+1:4095]
set_shared (dest)
kaboom!

Maybe I am missing something, this is not any different from a normal operation inside a guest. Making a page shared/private in the page table does not update the content of the page itself. In your above case, I assume zero_bytes[N+1:4095] are written by the destination VM. The memory region was private in the source VM page table, so, those writes will be performed encrypted. The destination VM later changed the memory to shared, but nobody wrote to the memory after it has been transitioned to the shared, so a reader of the memory should get ciphertext and unless there was a write after the set_shared (dest).

Quoted 11 lines

If userspace does GET_DIRTY after GET_LIST, then the host would transfer bad data by consuming a stale list (scenario #2):

vCPU Userspace
get_list (from KVM or internally)
set_shared (src)
zero_page (src)
get_dirty
<transfers private data instead of shared> <migrates vCPU>
kaboom!

I don't remember how things are done in recent Ashish Qemu/KVM patches but in previous series, the get_dirty() happens before the querying the encrypted state. There was some logic in VMM to resync the encrypted bitmap during the final migration stage and perform any additional data transfer since last sync.

Quoted 13 lines

If both guest and host order things to avoid #1 and #2, the host can still migrate the wrong data (scenario #3):

vCPU Userspace
set_private
zero_bytes[0:4096]
get_dirty
set_shared (src)
get_list
<transfers as shared instead of private>
<migrates vCPU>
set_private (dest)
kaboom!

Quoted 4 lines

Since there was no write to the memory after the set_shared (src), so the content of the page should not have changed. After the set_private (dest), the caller should be seeing the same content written by the zero_bytes[0:4096]

I think Sean was going for the situation where the VM has moved to the destination, which would have changed the VEK. That way the guest would be decrypting the old ciphertext with the new (wrong) key.

Quoted 16 lines
Quoted 16 lines

Scenario #3 is unlikely, but plausible, e.g. if the guest bails from its conversion flow for whatever reason, after making the initial hypercall. Maybe it goes without saying, but to address #3, the guest must consider existing data as lost the instant it tells the host the page has been converted to a different type.

Quoted 3 lines

For the above reason if we do in-kernel hypercall handling for page encryption status (which we probably won't require for SEV-SNP & correspondingly there will be no hypercall exiting),

As above, that doesn't preclude KVM from exiting to userspace on conversion.

Quoted 3 lines

then we can implement a standard GET/SET ioctl interface to get/set the guest page encryption status for userspace, which will work across SEV, SEV-ES and SEV-SNP.

#47 lore raw

Re: [PATCH v10 10/16] KVM: x86: Introduce KVM_GET_SHARED_PAGES_LIST ioctl

Quoted 14 lines · Steve Rutherford
Quoted 13 lines · Brijesh Singh
Quoted 10 lines · Sean Christopherson

If the guest does the hypercall after writing the page, then the guest is hosed if it gets migrated while writing the page (scenario #1):

vCPU Userspace
zero_bytes[0:N]
<transfers written bytes as private instead of shared>
<migrates vCPU>
zero_bytes[N+1:4095]
set_shared (dest)
kaboom!

Quoted 9 lines
Quoted 9 lines

Maybe I am missing something, this is not any different from a normal operation inside a guest. Making a page shared/private in the page table does not update the content of the page itself. In your above case, I assume zero_bytes[N+1:4095] are written by the destination VM. The memory region was private in the source VM page table, so, those writes will be performed encrypted. The destination VM later changed the memory to shared, but nobody wrote to the memory after it has been transitioned to the shared, so a reader of the memory should get ciphertext and unless there was a write after the set_shared (dest).

Sorry, that wasn't clear, there's an implied page table update to make the page shared before zero_bytes.

Quoted 13 lines
Quoted 13 lines
Quoted 11 lines

If userspace does GET_DIRTY after GET_LIST, then the host would transfer bad data by consuming a stale list (scenario #2):

vCPU Userspace
get_list (from KVM or internally)
set_shared (src)
zero_page (src)
get_dirty
<transfers private data instead of shared> <migrates vCPU>
kaboom!

Quoted 5 lines
Quoted 5 lines

I don't remember how things are done in recent Ashish Qemu/KVM patches but in previous series, the get_dirty() happens before the querying the encrypted state. There was some logic in VMM to resync the encrypted bitmap during the final migration stage and perform any additional data transfer since last sync.

It's likely that Ashish's patches did the correct thing, I just wanted to point out that both host and guest need to do everything in a very specific order.

Quoted 15 lines
Quoted 15 lines
Quoted 13 lines

If both guest and host order things to avoid #1 and #2, the host can still migrate the wrong data (scenario #3):

vCPU Userspace
set_private
zero_bytes[0:4096]
get_dirty
set_shared (src)
get_list
<transfers as shared instead of private>
<migrates vCPU>
set_private (dest)
kaboom!

Quoted 8 lines
Quoted 4 lines

Since there was no write to the memory after the set_shared (src), so the content of the page should not have changed. After the set_private (dest), the caller should be seeing the same content written by the zero_bytes[0:4096]

I think Sean was going for the situation where the VM has moved to the destination, which would have changed the VEK. That way the guest would be decrypting the old ciphertext with the new (wrong) key.

I think that's what I was saying?

I was pointing out that the userspace VMM would see the page as "shared" and so read the guest memory directly instead of routing it through the PSP.

Anyways, my intent wasn't to point out a known issue anywhere. I was working through how GET_LIST would interact with GET_DIRTY_LOG to convince myself that the various flows were bombproof. I wanted to record those thoughts so that I can remind myself of the requirements when I inevitably forget them in the future.

Quoted 14 lines
Quoted 14 lines
Quoted 14 lines

Scenario #3 is unlikely, but plausible, e.g. if the guest bails from its conversion flow for whatever reason, after making the initial hypercall. Maybe it goes without saying, but to address #3, the guest must consider existing data as lost the instant it tells the host the page has been converted to a different type.

Quoted 3 lines

For the above reason if we do in-kernel hypercall handling for page encryption status (which we probably won't require for SEV-SNP & correspondingly there will be no hypercall exiting),

As above, that doesn't preclude KVM from exiting to userspace on conversion.

Quoted 3 lines

then we can implement a standard GET/SET ioctl interface to get/set the guest page encryption status for userspace, which will work across SEV, SEV-ES and SEV-SNP.

#48 lore raw

Re: [PATCH v10 10/16] KVM: x86: Introduce KVM_GET_SHARED_PAGES_LIST ioctl

In reply to Steve Rutherford
Quoted 162 lines · Steve Rutherford
Quoted 157 lines · Brijesh Singh
Quoted 102 lines · Sean Christopherson
Quoted 59 lines · Ashish Kalra
Quoted 52 lines · Sean Christopherson

+Will and Quentin (arm64)

Moving the non-KVM x86 folks to bcc, I don't they care about KVM details at this point.

Quoted 6 lines · Ashish Kalra
Quoted 5 lines · Steve Rutherford

On Thu, Feb 25, 2021 at 12:20 PM Ashish Kalra <[email protected]> wrote:
Thanks for grabbing the data!

I am fine with both paths. Sean has stated an explicit desire for hypercall exiting, so I think that would be the current consensus.

Yep, though it'd be good to get Paolo's input, too.

Quoted 4 lines
Quoted 4 lines

If we want to do hypercall exiting, this should be in a follow-up series where we implement something more generic, e.g. a hypercall exiting bitmap or hypercall exit list. If we are taking the hypercall exit route, we can drop the kvm side of the hypercall.

I don't think this is a good candidate for arbitrary hypercall interception. Or rather, I think hypercall interception should be an orthogonal implementation.

The guest, including guest firmware, needs to be aware that the hypercall is supported, and the ABI needs to be well-defined. Relying on userspace VMMs to implement a common ABI is an unnecessary risk.

We could make KVM's default behavior be a nop, i.e. have KVM enforce the ABI but require further VMM intervention. But, I just don't see the point, it would save only a few lines of code. It would also limit what KVM could do in the future, e.g. if KVM wanted to do its own bookkeeping _and_ exit to userspace, then mandatory interception would essentially make it impossible for KVM to do bookkeeping while still honoring the interception request.

However, I do think it would make sense to have the userspace exit be a generic exit type. But hey, we already have the necessary ABI defined for that! It's just not used anywhere.

   /* KVM_EXIT_HYPERCALL */
   struct {
           __u64 nr;
           __u64 args[6];
           __u64 ret;
           __u32 longmode;
           __u32 pad;
   } hypercall;
Quoted 2 lines
Quoted 2 lines

Userspace could also handle the MSR using MSR filters (would need to confirm that). Then userspace could also be in control of the cpuid bit.

An MSR is not a great fit; it's x86 specific and limited to 64 bits of data. The data limitation could be fudged by shoving data into non-standard GPRs, but that will result in truly heinous guest code, and extensibility issues.

Quoted 6 lines

The data limitation is a moot point, because the x86-only thing is a deal breaker. arm64's pKVM work has a near-identical use case for a guest to share memory with a host. I can't think of a clever way to avoid having to support TDX's and SNP's hypervisor-agnostic variants, but we can at least not have multiple KVM variants.

Quoted 8 lines

Potentially, there is another reason for in-kernel hypercall handling considering SEV-SNP. In case of SEV-SNP the RMP table tracks the state of each guest page, for instance pages in hypervisor state, i.e., pages with C=0 and pages in guest valid state with C=1.

Now, there shouldn't be a need for page encryption status hypercalls on SEV-SNP as KVM can track & reference guest page status directly using the RMP table.

Relying on the RMP table itself would require locking the RMP table for an extended duration, and walking the entire RMP to find shared pages would be very inefficient.

Quoted 2 lines

As KVM maintains the RMP table, therefore we will need SET/GET type of interfaces to provide the guest page encryption status to userspace.

Hrm, somehow I temporarily forgot about SNP and TDX adding their own hypercalls for converting between shared and private. And in the case of TDX, the hypercall can't be trusted, i.e. is just a hint, otherwise the guest could induce a #MC in the host.

But, the different guest behavior doesn't require KVM to maintain a list/tree, e.g. adding a dedicated KVM_EXIT_* for notifying userspace of page encryption status changes would also suffice.

Actually, that made me think of another argument against maintaining a list in
KVM: there's no way to notify userspace that a page's status has changed. Userspace would need to query KVM to do GET_LIST after every GET_DIRTY. Obviously not a huge issue, but it does make migration slightly less efficient.

On a related topic, there are fatal race conditions that will require careful coordination between guest and host, and will effectively be wired into the ABI. SNP and TDX don't suffer these issues because host awareness of status is atomic with respect to the guest actually writing the page with the new encryption status.

For SEV live migration...

If the guest does the hypercall after writing the page, then the guest is hosed if it gets migrated while writing the page (scenario #1):

Quoted 7 lines

vCPU Userspace
zero_bytes[0:N]
<transfers written bytes as private instead of shared>
<migrates vCPU>
zero_bytes[N+1:4095]
set_shared (dest)
kaboom!

Maybe I am missing something, this is not any different from a normal operation inside a guest. Making a page shared/private in the page table does not update the content of the page itself. In your above case, I assume zero_bytes[N+1:4095] are written by the destination VM. The memory region was private in the source VM page table, so, those writes will be performed encrypted. The destination VM later changed the memory to shared, but nobody wrote to the memory after it has been transitioned to the shared, so a reader of the memory should get ciphertext and unless there was a write after the set_shared (dest).

Quoted 11 lines

If userspace does GET_DIRTY after GET_LIST, then the host would transfer bad data by consuming a stale list (scenario #2):

vCPU Userspace
get_list (from KVM or internally)
set_shared (src)
zero_page (src)
get_dirty
<transfers private data instead of shared> <migrates vCPU>
kaboom!

I don't remember how things are done in recent Ashish Qemu/KVM patches but in previous series, the get_dirty() happens before the querying the encrypted state. There was some logic in VMM to resync the encrypted bitmap during the final migration stage and perform any additional data transfer since last sync.

Quoted 13 lines

If both guest and host order things to avoid #1 and #2, the host can still migrate the wrong data (scenario #3):

vCPU Userspace
set_private
zero_bytes[0:4096]
get_dirty
set_shared (src)
get_list
<transfers as shared instead of private>
<migrates vCPU>
set_private (dest)
kaboom!

Quoted 8 lines
Quoted 4 lines

Since there was no write to the memory after the set_shared (src), so the content of the page should not have changed. After the set_private (dest), the caller should be seeing the same content written by the zero_bytes[0:4096]

I think Sean was going for the situation where the VM has moved to the destination, which would have changed the VEK. That way the guest would be decrypting the old ciphertext with the new (wrong) key.

Quoted 1 line

But how can this happen, if a page is migrated as private , when it is received it will be decrypted using the transport key TEK and then re-encrypted using the destination VM’s VEK on the destination VM.

Thanks,
Ashish

Quoted 15 lines
Quoted 15 lines
Quoted 15 lines

Scenario #3 is unlikely, but plausible, e.g. if the guest bails from its conversion flow for whatever reason, after making the initial hypercall. Maybe it goes without saying, but to address #3, the guest must consider existing data as lost the instant it tells the host the page has been converted to a different type.

Quoted 3 lines

For the above reason if we do in-kernel hypercall handling for page encryption status (which we probably won't require for SEV-SNP & correspondingly there will be no hypercall exiting),

As above, that doesn't preclude KVM from exiting to userspace on conversion.

Quoted 3 lines

then we can implement a standard GET/SET ioctl interface to get/set the guest page encryption status for userspace, which will work across SEV, SEV-ES and SEV-SNP.

#49 lore raw

Re: [PATCH v10 10/16] KVM: x86: Introduce KVM_GET_SHARED_PAGES_LIST ioctl

Quoted 167 lines · Ashish
Quoted 167 lines · Steve Rutherford
Quoted 161 lines · Brijesh Singh
Quoted 102 lines · Sean Christopherson
Quoted 59 lines · Ashish Kalra
Quoted 52 lines · Sean Christopherson

+Will and Quentin (arm64)

Moving the non-KVM x86 folks to bcc, I don't they care about KVM details at this point.

Quoted 6 lines · Ashish Kalra
Quoted 5 lines · Steve Rutherford

On Thu, Feb 25, 2021 at 12:20 PM Ashish Kalra <[email protected]> wrote:
Thanks for grabbing the data!

I am fine with both paths. Sean has stated an explicit desire for hypercall exiting, so I think that would be the current consensus.

Yep, though it'd be good to get Paolo's input, too.

Quoted 4 lines
Quoted 4 lines

If we want to do hypercall exiting, this should be in a follow-up series where we implement something more generic, e.g. a hypercall exiting bitmap or hypercall exit list. If we are taking the hypercall exit route, we can drop the kvm side of the hypercall.

I don't think this is a good candidate for arbitrary hypercall interception. Or rather, I think hypercall interception should be an orthogonal implementation.

The guest, including guest firmware, needs to be aware that the hypercall is supported, and the ABI needs to be well-defined. Relying on userspace VMMs to implement a common ABI is an unnecessary risk.

We could make KVM's default behavior be a nop, i.e. have KVM enforce the ABI but require further VMM intervention. But, I just don't see the point, it would save only a few lines of code. It would also limit what KVM could do in the future, e.g. if KVM wanted to do its own bookkeeping _and_ exit to userspace, then mandatory interception would essentially make it impossible for KVM to do bookkeeping while still honoring the interception request.

However, I do think it would make sense to have the userspace exit be a generic exit type. But hey, we already have the necessary ABI defined for that! It's just not used anywhere.

   /* KVM_EXIT_HYPERCALL */
   struct {
           __u64 nr;
           __u64 args[6];
           __u64 ret;
           __u32 longmode;
           __u32 pad;
   } hypercall;
Quoted 2 lines
Quoted 2 lines

Userspace could also handle the MSR using MSR filters (would need to confirm that). Then userspace could also be in control of the cpuid bit.

An MSR is not a great fit; it's x86 specific and limited to 64 bits of data. The data limitation could be fudged by shoving data into non-standard GPRs, but that will result in truly heinous guest code, and extensibility issues.

Quoted 6 lines

The data limitation is a moot point, because the x86-only thing is a deal breaker. arm64's pKVM work has a near-identical use case for a guest to share memory with a host. I can't think of a clever way to avoid having to support TDX's and SNP's hypervisor-agnostic variants, but we can at least not have multiple KVM variants.

Quoted 8 lines

Potentially, there is another reason for in-kernel hypercall handling considering SEV-SNP. In case of SEV-SNP the RMP table tracks the state of each guest page, for instance pages in hypervisor state, i.e., pages with C=0 and pages in guest valid state with C=1.

Now, there shouldn't be a need for page encryption status hypercalls on SEV-SNP as KVM can track & reference guest page status directly using the RMP table.

Relying on the RMP table itself would require locking the RMP table for an extended duration, and walking the entire RMP to find shared pages would be very inefficient.

Quoted 2 lines

As KVM maintains the RMP table, therefore we will need SET/GET type of interfaces to provide the guest page encryption status to userspace.

Hrm, somehow I temporarily forgot about SNP and TDX adding their own hypercalls for converting between shared and private. And in the case of TDX, the hypercall can't be trusted, i.e. is just a hint, otherwise the guest could induce a #MC in the host.

But, the different guest behavior doesn't require KVM to maintain a list/tree, e.g. adding a dedicated KVM_EXIT_* for notifying userspace of page encryption status changes would also suffice.

Actually, that made me think of another argument against maintaining a list in
KVM: there's no way to notify userspace that a page's status has changed. Userspace would need to query KVM to do GET_LIST after every GET_DIRTY. Obviously not a huge issue, but it does make migration slightly less efficient.

On a related topic, there are fatal race conditions that will require careful coordination between guest and host, and will effectively be wired into the ABI. SNP and TDX don't suffer these issues because host awareness of status is atomic with respect to the guest actually writing the page with the new encryption status.

For SEV live migration...

If the guest does the hypercall after writing the page, then the guest is hosed if it gets migrated while writing the page (scenario #1):

Quoted 7 lines

vCPU Userspace
zero_bytes[0:N]
<transfers written bytes as private instead of shared>
<migrates vCPU>
zero_bytes[N+1:4095]
set_shared (dest)
kaboom!

Maybe I am missing something, this is not any different from a normal operation inside a guest. Making a page shared/private in the page table does not update the content of the page itself. In your above case, I assume zero_bytes[N+1:4095] are written by the destination VM. The memory region was private in the source VM page table, so, those writes will be performed encrypted. The destination VM later changed the memory to shared, but nobody wrote to the memory after it has been transitioned to the shared, so a reader of the memory should get ciphertext and unless there was a write after the set_shared (dest).

Quoted 11 lines

If userspace does GET_DIRTY after GET_LIST, then the host would transfer bad data by consuming a stale list (scenario #2):

vCPU Userspace
get_list (from KVM or internally)
set_shared (src)
zero_page (src)
get_dirty
<transfers private data instead of shared> <migrates vCPU>
kaboom!

I don't remember how things are done in recent Ashish Qemu/KVM patches but in previous series, the get_dirty() happens before the querying the encrypted state. There was some logic in VMM to resync the encrypted bitmap during the final migration stage and perform any additional data transfer since last sync.

Quoted 13 lines

If both guest and host order things to avoid #1 and #2, the host can still migrate the wrong data (scenario #3):

vCPU Userspace
set_private
zero_bytes[0:4096]
get_dirty
set_shared (src)
get_list
<transfers as shared instead of private>
<migrates vCPU>
set_private (dest)
kaboom!

Since there was no write to the memory after the set_shared (src), so the content of the page should not have changed. After the set_private (dest), the caller should be seeing the same content written by the zero_bytes[0:4096]

Quoted 7 lines
Quoted 4 lines

I think Sean was going for the situation where the VM has moved to the destination, which would have changed the VEK. That way the guest would be decrypting the old ciphertext with the new (wrong) key.

Quoted 1 line

But how can this happen, if a page is migrated as private , when it is received it will be decrypted using the transport key TEK and then re-encrypted using the destination VM’s VEK on the destination VM.

If, as in scenario #3 above, the page is set to shared just before being migrated. It would then be migrated in the clear, but be interpreted on the target as encrypted (since, immediately post-migration, the page is flipped to private without ever writing to the page). This is not a scenario that is expected to work, as it requires violating (currently unspoken?) invariants.

Thanks,
Steve

Quoted 18 lines

Thanks,
Ashish

Quoted 15 lines
Quoted 15 lines
Quoted 15 lines

Scenario #3 is unlikely, but plausible, e.g. if the guest bails from its conversion flow for whatever reason, after making the initial hypercall. Maybe it goes without saying, but to address #3, the guest must consider existing data as lost the instant it tells the host the page has been converted to a different type.

Quoted 3 lines

For the above reason if we do in-kernel hypercall handling for page encryption status (which we probably won't require for SEV-SNP & correspondingly there will be no hypercall exiting),

As above, that doesn't preclude KVM from exiting to userspace on conversion.

Quoted 3 lines

then we can implement a standard GET/SET ioctl interface to get/set the guest page encryption status for userspace, which will work across SEV, SEV-ES and SEV-SNP.

#50 lore raw

Re: [PATCH v10 10/16] KVM: x86: Introduce KVM_GET_SHARED_PAGES_LIST ioctl

In reply to Sean Christopherson
Quoted 140 lines · Sean Christopherson
Quoted 63 lines · Ashish Kalra
Quoted 55 lines · Sean Christopherson

+Will and Quentin (arm64)

Moving the non-KVM x86 folks to bcc, I don't they care about KVM details at this point.

Quoted 6 lines · Ashish Kalra
Quoted 5 lines · Steve Rutherford

On Thu, Feb 25, 2021 at 12:20 PM Ashish Kalra <[email protected]> wrote:
Thanks for grabbing the data!

I am fine with both paths. Sean has stated an explicit desire for hypercall exiting, so I think that would be the current consensus.

Yep, though it'd be good to get Paolo's input, too.

Quoted 4 lines
Quoted 4 lines

If we want to do hypercall exiting, this should be in a follow-up series where we implement something more generic, e.g. a hypercall exiting bitmap or hypercall exit list. If we are taking the hypercall exit route, we can drop the kvm side of the hypercall.

I don't think this is a good candidate for arbitrary hypercall interception. Or rather, I think hypercall interception should be an orthogonal implementation.

The guest, including guest firmware, needs to be aware that the hypercall is supported, and the ABI needs to be well-defined. Relying on userspace VMMs to implement a common ABI is an unnecessary risk.

We could make KVM's default behavior be a nop, i.e. have KVM enforce the ABI but require further VMM intervention. But, I just don't see the point, it would save only a few lines of code. It would also limit what KVM could do in the future, e.g. if KVM wanted to do its own bookkeeping _and_ exit to userspace, then mandatory interception would essentially make it impossible for KVM to do bookkeeping while still honoring the interception request.

However, I do think it would make sense to have the userspace exit be a generic exit type. But hey, we already have the necessary ABI defined for that! It's just not used anywhere.

    /* KVM_EXIT_HYPERCALL */
    struct {
            __u64 nr;
            __u64 args[6];
            __u64 ret;
            __u32 longmode;
            __u32 pad;
    } hypercall;
Quoted 2 lines
Quoted 2 lines

Userspace could also handle the MSR using MSR filters (would need to confirm that). Then userspace could also be in control of the cpuid bit.

An MSR is not a great fit; it's x86 specific and limited to 64 bits of data. The data limitation could be fudged by shoving data into non-standard GPRs, but that will result in truly heinous guest code, and extensibility issues.

Quoted 6 lines

The data limitation is a moot point, because the x86-only thing is a deal breaker. arm64's pKVM work has a near-identical use case for a guest to share memory with a host. I can't think of a clever way to avoid having to support TDX's and SNP's hypervisor-agnostic variants, but we can at least not have multiple KVM variants.

Quoted 8 lines

Potentially, there is another reason for in-kernel hypercall handling considering SEV-SNP. In case of SEV-SNP the RMP table tracks the state of each guest page, for instance pages in hypervisor state, i.e., pages with C=0 and pages in guest valid state with C=1.

Now, there shouldn't be a need for page encryption status hypercalls on SEV-SNP as KVM can track & reference guest page status directly using the RMP table.

Relying on the RMP table itself would require locking the RMP table for an extended duration, and walking the entire RMP to find shared pages would be very inefficient.

Quoted 2 lines

As KVM maintains the RMP table, therefore we will need SET/GET type of interfaces to provide the guest page encryption status to userspace.

Hrm, somehow I temporarily forgot about SNP and TDX adding their own hypercalls for converting between shared and private. And in the case of TDX, the hypercall can't be trusted, i.e. is just a hint, otherwise the guest could induce a #MC in the host.

But, the different guest behavior doesn't require KVM to maintain a list/tree, e.g. adding a dedicated KVM_EXIT_* for notifying userspace of page encryption status changes would also suffice.

Actually, that made me think of another argument against maintaining a list in
KVM: there's no way to notify userspace that a page's status has changed. Userspace would need to query KVM to do GET_LIST after every GET_DIRTY. Obviously not a huge issue, but it does make migration slightly less efficient.

On a related topic, there are fatal race conditions that will require careful coordination between guest and host, and will effectively be wired into the ABI. SNP and TDX don't suffer these issues because host awareness of status is atomic with respect to the guest actually writing the page with the new encryption status.

For SEV live migration...

If the guest does the hypercall after writing the page, then the guest is hosed if it gets migrated while writing the page (scenario #1):

vCPU Userspace
zero_bytes[0:N]
<transfers written bytes as private instead of shared> <migrates vCPU>
zero_bytes[N+1:4095]
set_shared (dest)
kaboom!

If userspace does GET_DIRTY after GET_LIST, then the host would transfer bad data by consuming a stale list (scenario #2):

vCPU Userspace
get_list (from KVM or internally)
set_shared (src)
zero_page (src)
get_dirty
<transfers private data instead of shared> <migrates vCPU>
kaboom!

If both guest and host order things to avoid #1 and #2, the host can still migrate the wrong data (scenario #3):

vCPU Userspace
set_private
zero_bytes[0:4096]
get_dirty
set_shared (src)
get_list
<transfers as shared instead of private> <migrates vCPU>
set_private (dest)
kaboom!

Quoted 5 lines

Scenario #3 is unlikely, but plausible, e.g. if the guest bails from its conversion flow for whatever reason, after making the initial hypercall. Maybe it goes without saying, but to address #3, the guest must consider existing data as lost the instant it tells the host the page has been converted to a different type.

This requires the guest to bail on the conversion flow, and subsequently treat the page as having it's previous value. The kernel does not currently do this. I don't think it should ever do this. Currently, each time the guest changes it's view of a page, it re-zeroes that page, and it does so after flipping the c-bit and calling the hypercall (IIRC). This is much more conservative than is necessary for supporting LM, but does suffice. I think the minimal requirement for LM support is "Flipping a page's encryption status (and advertising that flip to the host) twice is not a no-op. The kernel must reinitialize that page". This invariant should also be necessary for anything that moves pages around on the same host (COPY, SWAP_OUT, SWAP_IN).

Relatedly, there is no workaround if the kernel does in-place reencryption (since the page would be both encrypted and unencrypted). That said, I believe the spec affirmatively tells you not to do this outside of SME.

There's also no solution if the guest relies on the value of ciphertext, or on the value of "decrypted plaintext". The guest could detect live migration by noticing that a page's value from an unencrypted perspective has changed when it's value from an encrypted perspective has not (or the other way around). This would also imply that a very strange guest could be broken by live migration.

#51 lore raw

Re: [PATCH v10 10/16] KVM: x86: Introduce KVM_GET_SHARED_PAGES_LIST ioctl

In reply to Ashish Kalra
Quoted 9 lines · Ashish Kalra

From: Brijesh Singh <[email protected]>

The ioctl is used to retrieve a guest's shared pages list.

Cc Thomas Gleixner <[email protected]>
Cc Ingo Molnar <[email protected]>
Cc "H. Peter Anvin" <[email protected]>
Cc Paolo Bonzini <[email protected]>
Cc "Radim Krčmář" <[email protected]>

AFAIK, Radim is no longer involved with KVM, and his RedHat email is bouncing. Probably best to drop his Cc in future revisions.

#52 lore raw

PATCH v10 11/16 KVM: x86: Introduce KVM_SET_SHARED_PAGES_LIST ioctl

In reply to Ashish Kalra

From: Brijesh Singh <[email protected]>

The ioctl is used to setup the shared pages list for an incoming guest.

Cc Thomas Gleixner <[email protected]>
Cc Ingo Molnar <[email protected]>
Cc "H. Peter Anvin" <[email protected]>
Cc Paolo Bonzini <[email protected]>
Cc "Radim Krčmář" <[email protected]>
Cc Joerg Roedel <[email protected]>
Cc Borislav Petkov <[email protected]>
Cc Tom Lendacky <[email protected]>
---
 Documentation/virt/kvm/api.rst  | 20 +++++++++-
 arch/x86/include/asm/kvm_host.h |  2 +
 arch/x86/kvm/svm/sev.c          | 70 +++++++++++++++++++++++++++++++++
 arch/x86/kvm/svm/svm.c          |  1 +
 arch/x86/kvm/svm/svm.h          |  1 +
 arch/x86/kvm/x86.c              | 12 ++++++
 include/uapi/linux/kvm.h        |  1 +
 7 files changed, 106 insertions(+), 1 deletion(-)
Documentation/virt/kvm/api.rst +19 -1
index 59ef537c0cdd..efb4720733b4 100644
@@ -4701,6 +4701,25 @@ This list can be used during the guest migration. If the page
 is private then the userspace need to use SEV migration commands to transmit
 the page.
 
+4.126 KVM_SET_SHARED_PAGES_LIST (vm ioctl)
+---------------------------------------
+
+:Capability: basic
+:Architectures: x86
+:Type: vm ioctl
+:Parameters: struct kvm_shared_pages_list (in/out)
+:Returns: 0 on success, -1 on error
+
+/* for KVM_SET_SHARED_PAGES_LIST */
+struct kvm_shared_pages_list {
+	int __user *pnents;
+	void __user *buffer;
+	__u32 size;
+};
+
+During the guest live migration the outgoing guest exports its unencrypted
+memory regions list, the KVM_SET_SHARED_PAGES_LIST can be used to build the
+shared/unencrypted regions list for an incoming guest.
 
 4.125 KVM_S390_PV_COMMAND
 -------------------------
@@ -4855,7 +4874,6 @@ into user space.
 If a vCPU is in running state while this ioctl is invoked, the vCPU may
 experience inconsistent filtering behavior on MSR accesses.
 
-
 5. The kvm_run structure
 ========================
 
arch/x86/include/asm/kvm_host.h +2 -0
index cd354d830e13..f05b812b69bd 100644
@@ -1305,6 +1305,8 @@ struct kvm_x86_ops {
 				  unsigned long sz, unsigned long mode);
 	int (*get_shared_pages_list)(struct kvm *kvm,
 				     struct kvm_shared_pages_list *list);
+	int (*set_shared_pages_list)(struct kvm *kvm,
+				     struct kvm_shared_pages_list *list);
 };
 
 struct kvm_x86_nested_ops {
arch/x86/kvm/svm/sev.c +70 -0

76 lines not on this page · raw message

arch/x86/kvm/svm/svm.c +1 -0
index 533ce47ff158..58f89f83caab 100644
@@ -4539,6 +4539,7 @@ static struct kvm_x86_ops svm_x86_ops __initdata = {
 
 	.page_enc_status_hc = svm_page_enc_status_hc,
 	.get_shared_pages_list = svm_get_shared_pages_list,
+	.set_shared_pages_list = svm_set_shared_pages_list,
 };
 
 static struct kvm_x86_init_ops svm_init_ops __initdata = {
arch/x86/kvm/svm/svm.h +1 -0
index 6a777c61373c..066ca2a9f1e6 100644
@@ -478,6 +478,7 @@ void sync_nested_vmcb_control(struct vcpu_svm *svm);
 int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa,
 			   unsigned long npages, unsigned long enc);
 int svm_get_shared_pages_list(struct kvm *kvm, struct kvm_shared_pages_list *list);
+int svm_set_shared_pages_list(struct kvm *kvm, struct kvm_shared_pages_list *list);
 
 extern struct kvm_x86_nested_ops svm_nested_ops;
 
arch/x86/kvm/x86.c +12 -0
index acfec2ae1402..c119715c1034 100644
@@ -5731,6 +5731,18 @@ long kvm_arch_vm_ioctl(struct file *filp,
 			r = kvm_x86_ops.get_shared_pages_list(kvm, &list);
 		break;
 	}
+	case KVM_SET_SHARED_PAGES_LIST: {
+		struct kvm_shared_pages_list list;
+
+		r = -EFAULT;
+		if (copy_from_user(&list, argp, sizeof(list)))
+			goto out;
+
+		r = -ENOTTY;
+		if (kvm_x86_ops.set_shared_pages_list)
+			r = kvm_x86_ops.set_shared_pages_list(kvm, &list);
+		break;
+	}
 	default:
 		r = -ENOTTY;
 	}
include/uapi/linux/kvm.h +1 -0
index 0529ba80498a..f704b08c97f2 100644
@@ -1573,6 +1573,7 @@ struct kvm_pv_cmd {
 #define KVM_RESET_DIRTY_RINGS		_IO(KVMIO, 0xc7)
 
 #define KVM_GET_SHARED_PAGES_LIST	_IOW(KVMIO, 0xc8, struct kvm_shared_pages_list)
+#define KVM_SET_SHARED_PAGES_LIST	_IOW(KVMIO, 0xc9, struct kvm_shared_pages_list)
 
 /* Secure Encrypted Virtualization command */
 enum sev_cmd_id {
Signature · 1 line
2.17.1
#53 lore raw

PATCH v10 12/16 KVM: x86: Introduce new KVM_FEATURE_SEV_LIVE_MIGRATION feature & Custom MSR.

In reply to Ashish Kalra

From: Ashish Kalra <[email protected]>

Add new KVM_FEATURE_SEV_LIVE_MIGRATION feature for guest to check for host-side support for SEV live migration. Also add a new custom MSR_KVM_SEV_LIVE_MIGRATION for guest to enable the SEV live migration feature.

---
 Documentation/virt/kvm/cpuid.rst     |  5 +++++
 Documentation/virt/kvm/msr.rst       | 12 ++++++++++++
 arch/x86/include/uapi/asm/kvm_para.h |  4 ++++
 arch/x86/kvm/svm/sev.c               | 13 +++++++++++++
 arch/x86/kvm/svm/svm.c               | 16 ++++++++++++++++
 arch/x86/kvm/svm/svm.h               |  2 ++
 6 files changed, 52 insertions(+)
Documentation/virt/kvm/cpuid.rst +5 -0
index cf62162d4be2..0bdb6cdb12d3 100644
@@ -96,6 +96,11 @@ KVM_FEATURE_MSI_EXT_DEST_ID        15          guest checks this feature bit
                                                before using extended destination
                                                ID bits in MSI address bits 11-5.
 
+KVM_FEATURE_SEV_LIVE_MIGRATION     16          guest checks this feature bit before
+                                               using the page encryption state
+                                               hypercall to notify the page state
+                                               change
+
 KVM_FEATURE_CLOCKSOURCE_STABLE_BIT 24          host will warn if no guest-side
                                                per-cpu warps are expected in
                                                kvmclock
Documentation/virt/kvm/msr.rst +12 -0
index e37a14c323d2..020245d16087 100644
@@ -376,3 +376,15 @@ data:
 	write '1' to bit 0 of the MSR, this causes the host to re-scan its queue
 	and check if there are more notifications pending. The MSR is available
 	if KVM_FEATURE_ASYNC_PF_INT is present in CPUID.
+
+MSR_KVM_SEV_LIVE_MIGRATION:
+        0x4b564d08
+
+	Control SEV Live Migration features.
+
+data:
+        Bit 0 enables (1) or disables (0) host-side SEV Live Migration feature,
+        in other words, this is guest->host communication that it's properly
+        handling the shared pages list.
+
+        All other bits are reserved.
arch/x86/include/uapi/asm/kvm_para.h +4 -0
index 950afebfba88..f6bfa138874f 100644
@@ -33,6 +33,7 @@
 #define KVM_FEATURE_PV_SCHED_YIELD	13
 #define KVM_FEATURE_ASYNC_PF_INT	14
 #define KVM_FEATURE_MSI_EXT_DEST_ID	15
+#define KVM_FEATURE_SEV_LIVE_MIGRATION	16
 
 #define KVM_HINTS_REALTIME      0
 
@@ -54,6 +55,7 @@
 #define MSR_KVM_POLL_CONTROL	0x4b564d05
 #define MSR_KVM_ASYNC_PF_INT	0x4b564d06
 #define MSR_KVM_ASYNC_PF_ACK	0x4b564d07
+#define MSR_KVM_SEV_LIVE_MIGRATION	0x4b564d08
 
 struct kvm_steal_time {
 	__u64 steal;
@@ -136,4 +138,6 @@ struct kvm_vcpu_pv_apf_data {
 #define KVM_PV_EOI_ENABLED KVM_PV_EOI_MASK
 #define KVM_PV_EOI_DISABLED 0x0
 
+#define KVM_SEV_LIVE_MIGRATION_ENABLED BIT_ULL(0)
+
 #endif /* _UAPI_ASM_X86_KVM_PARA_H */
arch/x86/kvm/svm/sev.c +13 -0
index b0d324aed515..93f42b3d3e33 100644
@@ -1627,6 +1627,16 @@ int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa,
 	return ret;
 }
 
+void sev_update_migration_flags(struct kvm *kvm, u64 data)
+{
+	struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
+
+	if (!sev_guest(kvm))
+		return;
+
+	sev->live_migration_enabled = !!(data & KVM_SEV_LIVE_MIGRATION_ENABLED);
+}
+
 int svm_get_shared_pages_list(struct kvm *kvm,
 			      struct kvm_shared_pages_list *list)
 {
@@ -1639,6 +1649,9 @@ int svm_get_shared_pages_list(struct kvm *kvm,
 	if (!sev_guest(kvm))
 		return -ENOTTY;
 
+	if (!sev->live_migration_enabled)
+		return -EINVAL;
+
 	if (!list->size)
 		return -EINVAL;
 
arch/x86/kvm/svm/svm.c +16 -0
index 58f89f83caab..43ea5061926f 100644
@@ -2903,6 +2903,9 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr)
 		svm->msr_decfg = data;
 		break;
 	}
+	case MSR_KVM_SEV_LIVE_MIGRATION:
+		sev_update_migration_flags(vcpu->kvm, data);
+		break;
 	case MSR_IA32_APICBASE:
 		if (kvm_vcpu_apicv_active(vcpu))
 			avic_update_vapic_bar(to_svm(vcpu), data);
@@ -3976,6 +3979,19 @@ static void svm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu)
 			vcpu->arch.cr3_lm_rsvd_bits &= ~(1UL << (best->ebx & 0x3f));
 	}
 
+	/*
+	 * If SEV guest then enable the Live migration feature.
+	 */
+	if (sev_guest(vcpu->kvm)) {
+		struct kvm_cpuid_entry2 *best;
+
+		best = kvm_find_cpuid_entry(vcpu, KVM_CPUID_FEATURES, 0);
+		if (!best)
+			return;
+
+		best->eax |= (1 << KVM_FEATURE_SEV_LIVE_MIGRATION);
+	}
+
 	if (!kvm_vcpu_apicv_active(vcpu))
 		return;
 
arch/x86/kvm/svm/svm.h +2 -0
index 066ca2a9f1e6..e1bffc11e425 100644
@@ -79,6 +79,7 @@ struct kvm_sev_info {
 	unsigned long pages_locked; /* Number of pages locked */
 	struct list_head regions_list;  /* List of registered regions */
 	u64 ap_jump_table;	/* SEV-ES AP Jump Table address */
+	bool live_migration_enabled;
 	/* List and count of shared pages */
 	int shared_pages_list_count;
 	struct list_head shared_pages_list;
@@ -592,6 +593,7 @@ int svm_unregister_enc_region(struct kvm *kvm,
 void pre_sev_run(struct vcpu_svm *svm, int cpu);
 void __init sev_hardware_setup(void);
 void sev_hardware_teardown(void);
+void sev_update_migration_flags(struct kvm *kvm, u64 data);
 void sev_free_vcpu(struct kvm_vcpu *vcpu);
 int sev_handle_vmgexit(struct vcpu_svm *svm);
 int sev_es_string_io(struct vcpu_svm *svm, int size, unsigned int port, int in);
Signature · 1 line
2.17.1
#54 lore raw

Re: [PATCH v10 12/16] KVM: x86: Introduce new KVM_FEATURE_SEV_LIVE_MIGRATION feature & Custom MSR.

Quoted 86 lines · Ashish Kalra

From: Ashish Kalra <[email protected]>

Add new KVM_FEATURE_SEV_LIVE_MIGRATION feature for guest to check for host-side support for SEV live migration. Also add a new custom MSR_KVM_SEV_LIVE_MIGRATION for guest to enable the SEV live migration feature.

---
 Documentation/virt/kvm/cpuid.rst     |  5 +++++
 Documentation/virt/kvm/msr.rst       | 12 ++++++++++++
 arch/x86/include/uapi/asm/kvm_para.h |  4 ++++
 arch/x86/kvm/svm/sev.c               | 13 +++++++++++++
 arch/x86/kvm/svm/svm.c               | 16 ++++++++++++++++
 arch/x86/kvm/svm/svm.h               |  2 ++
 6 files changed, 52 insertions(+)
Documentation/virt/kvm/cpuid.rst +5 -0
index cf62162d4be2..0bdb6cdb12d3 100644
@@ -96,6 +96,11 @@ KVM_FEATURE_MSI_EXT_DEST_ID        15          guest checks this feature bit
                                                before using extended destination
                                                ID bits in MSI address bits 11-5.
 
+KVM_FEATURE_SEV_LIVE_MIGRATION     16          guest checks this feature bit before
+                                               using the page encryption state
+                                               hypercall to notify the page state
+                                               change
+
 KVM_FEATURE_CLOCKSOURCE_STABLE_BIT 24          host will warn if no guest-side
                                                per-cpu warps are expected in
                                                kvmclock
Documentation/virt/kvm/msr.rst +12 -0
index e37a14c323d2..020245d16087 100644
@@ -376,3 +376,15 @@ data:
        write '1' to bit 0 of the MSR, this causes the host to re-scan its queue
        and check if there are more notifications pending. The MSR is available
        if KVM_FEATURE_ASYNC_PF_INT is present in CPUID.
+
+MSR_KVM_SEV_LIVE_MIGRATION:
+        0x4b564d08
+
+       Control SEV Live Migration features.
+
+data:
+        Bit 0 enables (1) or disables (0) host-side SEV Live Migration feature,
+        in other words, this is guest->host communication that it's properly
+        handling the shared pages list.
+
+        All other bits are reserved.
arch/x86/include/uapi/asm/kvm_para.h +4 -0
index 950afebfba88..f6bfa138874f 100644
@@ -33,6 +33,7 @@
 #define KVM_FEATURE_PV_SCHED_YIELD     13
 #define KVM_FEATURE_ASYNC_PF_INT       14
 #define KVM_FEATURE_MSI_EXT_DEST_ID    15
+#define KVM_FEATURE_SEV_LIVE_MIGRATION 16
 
 #define KVM_HINTS_REALTIME      0
 
@@ -54,6 +55,7 @@
 #define MSR_KVM_POLL_CONTROL   0x4b564d05
 #define MSR_KVM_ASYNC_PF_INT   0x4b564d06
 #define MSR_KVM_ASYNC_PF_ACK   0x4b564d07
+#define MSR_KVM_SEV_LIVE_MIGRATION     0x4b564d08
 
 struct kvm_steal_time {
        __u64 steal;
@@ -136,4 +138,6 @@ struct kvm_vcpu_pv_apf_data {
 #define KVM_PV_EOI_ENABLED KVM_PV_EOI_MASK
 #define KVM_PV_EOI_DISABLED 0x0
 
+#define KVM_SEV_LIVE_MIGRATION_ENABLED BIT_ULL(0)
+
 #endif /* _UAPI_ASM_X86_KVM_PARA_H */
arch/x86/kvm/svm/sev.c +0 -0
index b0d324aed515..93f42b3d3e33 100644

arch/x86/kvm/svm/sev.c int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa, · line 1635

Quoted 9 lines
arch/x86/kvm/svm/sev.c +6 -0
@@ -1627,6 +1627,16 @@ int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa,
        return ret;
 }
 
+void sev_update_migration_flags(struct kvm *kvm, u64 data)
+{
+       struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
+
+       if (!sev_guest(kvm))
+               return;

This should assert that userspace wanted the guest to be able to make these calls (see more below).

Quoted 39 lines
arch/x86/kvm/svm/sev.c +7 -0
+
+       sev->live_migration_enabled = !!(data & KVM_SEV_LIVE_MIGRATION_ENABLED);
+}
+
 int svm_get_shared_pages_list(struct kvm *kvm,
                              struct kvm_shared_pages_list *list)
 {
@@ -1639,6 +1649,9 @@ int svm_get_shared_pages_list(struct kvm *kvm,
        if (!sev_guest(kvm))
                return -ENOTTY;
 
+       if (!sev->live_migration_enabled)
+               return -EINVAL;
+
        if (!list->size)
                return -EINVAL;
 
arch/x86/kvm/svm/svm.c +6 -0
index 58f89f83caab..43ea5061926f 100644
@@ -2903,6 +2903,9 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr)
                svm->msr_decfg = data;
                break;
        }
+       case MSR_KVM_SEV_LIVE_MIGRATION:
+               sev_update_migration_flags(vcpu->kvm, data);
+               break;
        case MSR_IA32_APICBASE:
                if (kvm_vcpu_apicv_active(vcpu))
                        avic_update_vapic_bar(to_svm(vcpu), data);
@@ -3976,6 +3979,19 @@ static void svm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu)
                        vcpu->arch.cr3_lm_rsvd_bits &= ~(1UL << (best->ebx & 0x3f));
        }
 
+       /*
+        * If SEV guest then enable the Live migration feature.
+        */

arch/x86/kvm/svm/svm.c static void svm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) · line 3994

Quoted 10 lines
arch/x86/kvm/svm/svm.c +10 -0
+       if (sev_guest(vcpu->kvm)) {
+               struct kvm_cpuid_entry2 *best;
+
+               best = kvm_find_cpuid_entry(vcpu, KVM_CPUID_FEATURES, 0);
+               if (!best)
+                       return;
+
+               best->eax |= (1 << KVM_FEATURE_SEV_LIVE_MIGRATION);
+       }
+

Looking at this, I believe the only way for this bit to get enabled is if userspace toggles it. There needs to be a way for userspace to identify if the kernel underneath them does, in fact, support SEV LM. I'm at risk for having misread these patches (it's a long series), but I don't see anything that communicates upwards.

This could go upward with the other paravirt features flags in cpuid.c. It could also be an explicit KVM Capability (checked through check_extension).

Userspace should then have a chance to decide whether or not this should be enabled. And when it's not enabled, the host should return a GP in response to the hypercall. This could be configured either through userspace stripping out the LM feature bit, or by calling a VM scoped enable cap (KVM_VM_IOCTL_ENABLE_CAP).

I believe the typical path for a feature like this to be configured would be to use ENABLE_CAP.

Quoted 27 lines
arch/x86/kvm/svm/svm.c +0 -0
        if (!kvm_vcpu_apicv_active(vcpu))
                return;
 
arch/x86/kvm/svm/svm.h +2 -1
index 066ca2a9f1e6..e1bffc11e425 100644
@@ -79,6 +79,7 @@ struct kvm_sev_info {
        unsigned long pages_locked; /* Number of pages locked */
        struct list_head regions_list;  /* List of registered regions */
        u64 ap_jump_table;      /* SEV-ES AP Jump Table address */
+       bool live_migration_enabled;
        /* List and count of shared pages */
        int shared_pages_list_count;
        struct list_head shared_pages_list;
@@ -592,6 +593,7 @@ int svm_unregister_enc_region(struct kvm *kvm,
 void pre_sev_run(struct vcpu_svm *svm, int cpu);
 void __init sev_hardware_setup(void);
 void sev_hardware_teardown(void);
+void sev_update_migration_flags(struct kvm *kvm, u64 data);
 void sev_free_vcpu(struct kvm_vcpu *vcpu);
 int sev_handle_vmgexit(struct vcpu_svm *svm);
 int sev_es_string_io(struct vcpu_svm *svm, int size, unsigned int port, int in);
--

2.17.1

#55 lore raw

Re: [PATCH v10 12/16] KVM: x86: Introduce new KVM_FEATURE_SEV_LIVE_MIGRATION feature & Custom MSR.

Hello Steve,

Quoted 160 lines · Steve Rutherford
Quoted 86 lines · Ashish Kalra

From: Ashish Kalra <[email protected]>

Add new KVM_FEATURE_SEV_LIVE_MIGRATION feature for guest to check for host-side support for SEV live migration. Also add a new custom MSR_KVM_SEV_LIVE_MIGRATION for guest to enable the SEV live migration feature.

---
 Documentation/virt/kvm/cpuid.rst     |  5 +++++
 Documentation/virt/kvm/msr.rst       | 12 ++++++++++++
 arch/x86/include/uapi/asm/kvm_para.h |  4 ++++
 arch/x86/kvm/svm/sev.c               | 13 +++++++++++++
 arch/x86/kvm/svm/svm.c               | 16 ++++++++++++++++
 arch/x86/kvm/svm/svm.h               |  2 ++
 6 files changed, 52 insertions(+)
Documentation/virt/kvm/cpuid.rst +5 -0
index cf62162d4be2..0bdb6cdb12d3 100644
@@ -96,6 +96,11 @@ KVM_FEATURE_MSI_EXT_DEST_ID        15          guest checks this feature bit
                                                before using extended destination
                                                ID bits in MSI address bits 11-5.
 
+KVM_FEATURE_SEV_LIVE_MIGRATION     16          guest checks this feature bit before
+                                               using the page encryption state
+                                               hypercall to notify the page state
+                                               change
+
 KVM_FEATURE_CLOCKSOURCE_STABLE_BIT 24          host will warn if no guest-side
                                                per-cpu warps are expected in
                                                kvmclock
Documentation/virt/kvm/msr.rst +12 -0
index e37a14c323d2..020245d16087 100644
@@ -376,3 +376,15 @@ data:
        write '1' to bit 0 of the MSR, this causes the host to re-scan its queue
        and check if there are more notifications pending. The MSR is available
        if KVM_FEATURE_ASYNC_PF_INT is present in CPUID.
+
+MSR_KVM_SEV_LIVE_MIGRATION:
+        0x4b564d08
+
+       Control SEV Live Migration features.
+
+data:
+        Bit 0 enables (1) or disables (0) host-side SEV Live Migration feature,
+        in other words, this is guest->host communication that it's properly
+        handling the shared pages list.
+
+        All other bits are reserved.
arch/x86/include/uapi/asm/kvm_para.h +4 -0
index 950afebfba88..f6bfa138874f 100644
@@ -33,6 +33,7 @@
 #define KVM_FEATURE_PV_SCHED_YIELD     13
 #define KVM_FEATURE_ASYNC_PF_INT       14
 #define KVM_FEATURE_MSI_EXT_DEST_ID    15
+#define KVM_FEATURE_SEV_LIVE_MIGRATION 16
 
 #define KVM_HINTS_REALTIME      0
 
@@ -54,6 +55,7 @@
 #define MSR_KVM_POLL_CONTROL   0x4b564d05
 #define MSR_KVM_ASYNC_PF_INT   0x4b564d06
 #define MSR_KVM_ASYNC_PF_ACK   0x4b564d07
+#define MSR_KVM_SEV_LIVE_MIGRATION     0x4b564d08
 
 struct kvm_steal_time {
        __u64 steal;
@@ -136,4 +138,6 @@ struct kvm_vcpu_pv_apf_data {
 #define KVM_PV_EOI_ENABLED KVM_PV_EOI_MASK
 #define KVM_PV_EOI_DISABLED 0x0
 
+#define KVM_SEV_LIVE_MIGRATION_ENABLED BIT_ULL(0)
+
 #endif /* _UAPI_ASM_X86_KVM_PARA_H */
arch/x86/kvm/svm/sev.c +0 -0
index b0d324aed515..93f42b3d3e33 100644

arch/x86/kvm/svm/sev.c int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa, · line 1635

Quoted 9 lines
arch/x86/kvm/svm/sev.c +6 -0
@@ -1627,6 +1627,16 @@ int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa,
        return ret;
 }
 
+void sev_update_migration_flags(struct kvm *kvm, u64 data)
+{
+       struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
+
+       if (!sev_guest(kvm))
+               return;

This should assert that userspace wanted the guest to be able to make these calls (see more below).

Quoted 39 lines
arch/x86/kvm/svm/sev.c +7 -0
+
+       sev->live_migration_enabled = !!(data & KVM_SEV_LIVE_MIGRATION_ENABLED);
+}
+
 int svm_get_shared_pages_list(struct kvm *kvm,
                              struct kvm_shared_pages_list *list)
 {
@@ -1639,6 +1649,9 @@ int svm_get_shared_pages_list(struct kvm *kvm,
        if (!sev_guest(kvm))
                return -ENOTTY;
 
+       if (!sev->live_migration_enabled)
+               return -EINVAL;
+
        if (!list->size)
                return -EINVAL;
 
arch/x86/kvm/svm/svm.c +6 -0
index 58f89f83caab..43ea5061926f 100644
@@ -2903,6 +2903,9 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr)
                svm->msr_decfg = data;
                break;
        }
+       case MSR_KVM_SEV_LIVE_MIGRATION:
+               sev_update_migration_flags(vcpu->kvm, data);
+               break;
        case MSR_IA32_APICBASE:
                if (kvm_vcpu_apicv_active(vcpu))
                        avic_update_vapic_bar(to_svm(vcpu), data);
@@ -3976,6 +3979,19 @@ static void svm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu)
                        vcpu->arch.cr3_lm_rsvd_bits &= ~(1UL << (best->ebx & 0x3f));
        }
 
+       /*
+        * If SEV guest then enable the Live migration feature.
+        */

arch/x86/kvm/svm/svm.c static void svm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) · line 3994

Quoted 10 lines
arch/x86/kvm/svm/svm.c +10 -0
+       if (sev_guest(vcpu->kvm)) {
+               struct kvm_cpuid_entry2 *best;
+
+               best = kvm_find_cpuid_entry(vcpu, KVM_CPUID_FEATURES, 0);
+               if (!best)
+                       return;
+
+               best->eax |= (1 << KVM_FEATURE_SEV_LIVE_MIGRATION);
+       }
+

Looking at this, I believe the only way for this bit to get enabled is if userspace toggles it. There needs to be a way for userspace to identify if the kernel underneath them does, in fact, support SEV LM. I'm at risk for having misread these patches (it's a long series), but I don't see anything that communicates upwards.

This could go upward with the other paravirt features flags in cpuid.c. It could also be an explicit KVM Capability (checked through check_extension).

Quoted 8 lines

Userspace should then have a chance to decide whether or not this should be enabled. And when it's not enabled, the host should return a GP in response to the hypercall. This could be configured either through userspace stripping out the LM feature bit, or by calling a VM scoped enable cap (KVM_VM_IOCTL_ENABLE_CAP).

I believe the typical path for a feature like this to be configured would be to use ENABLE_CAP.

I believe we have discussed and reviewed this earlier too.

To summarize this feature, the host indicates if it supports the Live Migration feature and the feature and the hypercall are only enabled on the host when the guest checks for this support and does a wrmsrl() to enable the feature. Also the guest will not make the hypercall if the host does not indicate support for it.

And these were your review comments on the above :
I see I misunderstood how the CPUID bits get passed
through: usermode can still override them. Forgot about the back and forth for CPUID with usermode.

So as you mentioned, userspace can still override these and it gets a chance to decide whether or not this should be enabled.

Thanks,
Ashish

Quoted 27 lines
Quoted 27 lines
arch/x86/kvm/svm/svm.c +0 -0
        if (!kvm_vcpu_apicv_active(vcpu))
                return;
 
arch/x86/kvm/svm/svm.h +2 -1
index 066ca2a9f1e6..e1bffc11e425 100644
@@ -79,6 +79,7 @@ struct kvm_sev_info {
        unsigned long pages_locked; /* Number of pages locked */
        struct list_head regions_list;  /* List of registered regions */
        u64 ap_jump_table;      /* SEV-ES AP Jump Table address */
+       bool live_migration_enabled;
        /* List and count of shared pages */
        int shared_pages_list_count;
        struct list_head shared_pages_list;
@@ -592,6 +593,7 @@ int svm_unregister_enc_region(struct kvm *kvm,
 void pre_sev_run(struct vcpu_svm *svm, int cpu);
 void __init sev_hardware_setup(void);
 void sev_hardware_teardown(void);
+void sev_update_migration_flags(struct kvm *kvm, u64 data);
 void sev_free_vcpu(struct kvm_vcpu *vcpu);
 int sev_handle_vmgexit(struct vcpu_svm *svm);
 int sev_es_string_io(struct vcpu_svm *svm, int size, unsigned int port, int in);
--

2.17.1

#56 lore raw

Re: [PATCH v10 12/16] KVM: x86: Introduce new KVM_FEATURE_SEV_LIVE_MIGRATION feature & Custom MSR.

Quoted 116 lines · Ashish Kalra

Hello Steve,

Quoted 112 lines · Steve Rutherford
Quoted 86 lines · Ashish Kalra

From: Ashish Kalra <[email protected]>

Add new KVM_FEATURE_SEV_LIVE_MIGRATION feature for guest to check for host-side support for SEV live migration. Also add a new custom MSR_KVM_SEV_LIVE_MIGRATION for guest to enable the SEV live migration feature.

---
 Documentation/virt/kvm/cpuid.rst     |  5 +++++
 Documentation/virt/kvm/msr.rst       | 12 ++++++++++++
 arch/x86/include/uapi/asm/kvm_para.h |  4 ++++
 arch/x86/kvm/svm/sev.c               | 13 +++++++++++++
 arch/x86/kvm/svm/svm.c               | 16 ++++++++++++++++
 arch/x86/kvm/svm/svm.h               |  2 ++
 6 files changed, 52 insertions(+)
Documentation/virt/kvm/cpuid.rst +5 -0
index cf62162d4be2..0bdb6cdb12d3 100644
@@ -96,6 +96,11 @@ KVM_FEATURE_MSI_EXT_DEST_ID        15          guest checks this feature bit
                                                before using extended destination
                                                ID bits in MSI address bits 11-5.
 
+KVM_FEATURE_SEV_LIVE_MIGRATION     16          guest checks this feature bit before
+                                               using the page encryption state
+                                               hypercall to notify the page state
+                                               change
+
 KVM_FEATURE_CLOCKSOURCE_STABLE_BIT 24          host will warn if no guest-side
                                                per-cpu warps are expected in
                                                kvmclock
Documentation/virt/kvm/msr.rst +12 -0
index e37a14c323d2..020245d16087 100644
@@ -376,3 +376,15 @@ data:
        write '1' to bit 0 of the MSR, this causes the host to re-scan its queue
        and check if there are more notifications pending. The MSR is available
        if KVM_FEATURE_ASYNC_PF_INT is present in CPUID.
+
+MSR_KVM_SEV_LIVE_MIGRATION:
+        0x4b564d08
+
+       Control SEV Live Migration features.
+
+data:
+        Bit 0 enables (1) or disables (0) host-side SEV Live Migration feature,
+        in other words, this is guest->host communication that it's properly
+        handling the shared pages list.
+
+        All other bits are reserved.
arch/x86/include/uapi/asm/kvm_para.h +4 -0
index 950afebfba88..f6bfa138874f 100644
@@ -33,6 +33,7 @@
 #define KVM_FEATURE_PV_SCHED_YIELD     13
 #define KVM_FEATURE_ASYNC_PF_INT       14
 #define KVM_FEATURE_MSI_EXT_DEST_ID    15
+#define KVM_FEATURE_SEV_LIVE_MIGRATION 16
 
 #define KVM_HINTS_REALTIME      0
 
@@ -54,6 +55,7 @@
 #define MSR_KVM_POLL_CONTROL   0x4b564d05
 #define MSR_KVM_ASYNC_PF_INT   0x4b564d06
 #define MSR_KVM_ASYNC_PF_ACK   0x4b564d07
+#define MSR_KVM_SEV_LIVE_MIGRATION     0x4b564d08
 
 struct kvm_steal_time {
        __u64 steal;
@@ -136,4 +138,6 @@ struct kvm_vcpu_pv_apf_data {
 #define KVM_PV_EOI_ENABLED KVM_PV_EOI_MASK
 #define KVM_PV_EOI_DISABLED 0x0
 
+#define KVM_SEV_LIVE_MIGRATION_ENABLED BIT_ULL(0)
+
 #endif /* _UAPI_ASM_X86_KVM_PARA_H */
arch/x86/kvm/svm/sev.c +0 -0
index b0d324aed515..93f42b3d3e33 100644

arch/x86/kvm/svm/sev.c int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa, · line 1635

Quoted 9 lines
arch/x86/kvm/svm/sev.c +6 -0
@@ -1627,6 +1627,16 @@ int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa,
        return ret;
 }
 
+void sev_update_migration_flags(struct kvm *kvm, u64 data)
+{
+       struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
+
+       if (!sev_guest(kvm))
+               return;

This should assert that userspace wanted the guest to be able to make these calls (see more below).

Quoted 12 lines
arch/x86/kvm/svm/sev.c +4 -0
+
+       sev->live_migration_enabled = !!(data & KVM_SEV_LIVE_MIGRATION_ENABLED);
+}
+
 int svm_get_shared_pages_list(struct kvm *kvm,
                              struct kvm_shared_pages_list *list)
 {
@@ -1639,6 +1649,9 @@ int svm_get_shared_pages_list(struct kvm *kvm,
        if (!sev_guest(kvm))
                return -ENOTTY;
 
Quoted 2 lines
Quoted 2 lines

arch/x86/kvm/svm/sev.c int svm_get_shared_pages_list(struct kvm *kvm, · line 1653

Quoted 2 lines
arch/x86/kvm/svm/sev.c +2 -0
+       if (!sev->live_migration_enabled)
+               return -EINVAL;

This is currently under guest control, so I'm not certain this is helpful. If I called this with otherwise valid parameters, and got back -EINVAL, I would probably think the bug is on my end. But it could be on the guest's end! I would probably drop this, but you could have KVM return an empty list of regions when this happens.

Alternatively, as explained below, this could call guest_pv_has instead.

Quoted 56 lines
Quoted 47 lines
Quoted 26 lines
arch/x86/kvm/svm/sev.c +1 -0
+
        if (!list->size)
                return -EINVAL;
 
arch/x86/kvm/svm/svm.c +6 -0
index 58f89f83caab..43ea5061926f 100644
@@ -2903,6 +2903,9 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr)
                svm->msr_decfg = data;
                break;
        }
+       case MSR_KVM_SEV_LIVE_MIGRATION:
+               sev_update_migration_flags(vcpu->kvm, data);
+               break;
        case MSR_IA32_APICBASE:
                if (kvm_vcpu_apicv_active(vcpu))
                        avic_update_vapic_bar(to_svm(vcpu), data);
@@ -3976,6 +3979,19 @@ static void svm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu)
                        vcpu->arch.cr3_lm_rsvd_bits &= ~(1UL << (best->ebx & 0x3f));
        }
 
+       /*
+        * If SEV guest then enable the Live migration feature.
+        */

arch/x86/kvm/svm/svm.c static void svm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) · line 3994

Quoted 10 lines
arch/x86/kvm/svm/svm.c +10 -0
+       if (sev_guest(vcpu->kvm)) {
+               struct kvm_cpuid_entry2 *best;
+
+               best = kvm_find_cpuid_entry(vcpu, KVM_CPUID_FEATURES, 0);
+               if (!best)
+                       return;
+
+               best->eax |= (1 << KVM_FEATURE_SEV_LIVE_MIGRATION);
+       }
+

Looking at this, I believe the only way for this bit to get enabled is if userspace toggles it. There needs to be a way for userspace to identify if the kernel underneath them does, in fact, support SEV LM. I'm at risk for having misread these patches (it's a long series), but I don't see anything that communicates upwards.

This could go upward with the other paravirt features flags in cpuid.c. It could also be an explicit KVM Capability (checked through check_extension).

Quoted 8 lines

Userspace should then have a chance to decide whether or not this should be enabled. And when it's not enabled, the host should return a GP in response to the hypercall. This could be configured either through userspace stripping out the LM feature bit, or by calling a VM scoped enable cap (KVM_VM_IOCTL_ENABLE_CAP).

I believe the typical path for a feature like this to be configured would be to use ENABLE_CAP.

Quoted 7 lines

I believe we have discussed and reviewed this earlier too.

To summarize this feature, the host indicates if it supports the Live Migration feature and the feature and the hypercall are only enabled on the host when the guest checks for this support and does a wrmsrl() to enable the feature. Also the guest will not make the hypercall if the host does not indicate support for it.

I've gone through and read this patch a bit more closely, and the surrounding code. Previously, I clearly misread this and the surrounding space.

What happens if the guest just writes to the MSR anyway? Even if it didn't receive a cue to do so? I believe the hypercall would still get invoked here, since the hypercall does not check if SEV live migration is enabled. Similarly, the MSR for enabling it is always available, even if userspace didn't ask for the cpuid bit to be set. This should not happen. Userspace should be in control of a new hypercall rolling out.

I believe my interpretation last time was that the cpuid bit was getting surfaced from the host kernel to host userspace, but I don't actually see that in this patch series. Another way to ask this question would be "How does userspace know the kernel they are on has this patch series?". It needs some way of checking whether or not the kernel underneath it supports SEV live migration. Technically, I think userspace could call get_cpuid, set_cpuid (with the same values), and then get_cpuid again, and it would be able to infer by checking the SEV LM feature flag in the KVM leaf. This seems a bit kludgy. Checking support should be easy.

An additional question is "how does userspace choose whether live migration is advertised to the guest"? I believe userspace's desire for a particular value of the paravirt feature flag in CPUID get's overridden when they call set cpuid, since the feature flag is set in svm_vcpu_after_set_cpuid regardless of what userspace asks for. Userspace should have a choice in the matter.

Looking at similar paravirt-y features, there's precedent for another way of doing this (may be preferred over CHECK_EXTENSION/ENABLE_CAP?):
this could call guest_pv_has before running the hypercall. The feature (KVM_FEATURE_SEV_LIVE_MIGRATION) would then need to be exposed with the other paravirt features in __do_cpuid_func. The function guest_pv_has would represent if userspace has decided to expose SEV live migration to the guest, and the sev->live_migration_enabled would indicate if the guest responded affirmatively to the CPUID bit.

The downside of using guest_pv_has is that, if pv enforcement is disabled, guest_pv_has will always return true, which seems a bit odd for a non-SEV guest. This isn't a deal breaker, but seems a bit odd for say, a guest that isn't even running SEV. Using CHECK_EXTENSION and ENABLE_CAP sidestep that. I'm also not certain I would call this a paravirt feature.

Quoted 10 lines

And these were your review comments on the above :
I see I misunderstood how the CPUID bits get passed
through: usermode can still override them. Forgot about the back and forth for CPUID with usermode.

So as you mentioned, userspace can still override these and it gets a chance to decide whether or not this should be enabled.

Thanks,
Ashish

Thanks,
Steve

#57 lore raw

Re: [PATCH v10 12/16] KVM: x86: Introduce new KVM_FEATURE_SEV_LIVE_MIGRATION feature & Custom MSR.

Hello Steve,

Let me first answer those queries which i can do immediately ...

Quoted 117 lines · Steve Rutherford
Quoted 116 lines · Ashish Kalra

Hello Steve,

Quoted 112 lines · Steve Rutherford
Quoted 86 lines · Ashish Kalra

From: Ashish Kalra <[email protected]>

Add new KVM_FEATURE_SEV_LIVE_MIGRATION feature for guest to check for host-side support for SEV live migration. Also add a new custom MSR_KVM_SEV_LIVE_MIGRATION for guest to enable the SEV live migration feature.

---
 Documentation/virt/kvm/cpuid.rst     |  5 +++++
 Documentation/virt/kvm/msr.rst       | 12 ++++++++++++
 arch/x86/include/uapi/asm/kvm_para.h |  4 ++++
 arch/x86/kvm/svm/sev.c               | 13 +++++++++++++
 arch/x86/kvm/svm/svm.c               | 16 ++++++++++++++++
 arch/x86/kvm/svm/svm.h               |  2 ++
 6 files changed, 52 insertions(+)
Documentation/virt/kvm/cpuid.rst +5 -0
index cf62162d4be2..0bdb6cdb12d3 100644
@@ -96,6 +96,11 @@ KVM_FEATURE_MSI_EXT_DEST_ID        15          guest checks this feature bit
                                                before using extended destination
                                                ID bits in MSI address bits 11-5.
 
+KVM_FEATURE_SEV_LIVE_MIGRATION     16          guest checks this feature bit before
+                                               using the page encryption state
+                                               hypercall to notify the page state
+                                               change
+
 KVM_FEATURE_CLOCKSOURCE_STABLE_BIT 24          host will warn if no guest-side
                                                per-cpu warps are expected in
                                                kvmclock
Documentation/virt/kvm/msr.rst +12 -0
index e37a14c323d2..020245d16087 100644
@@ -376,3 +376,15 @@ data:
        write '1' to bit 0 of the MSR, this causes the host to re-scan its queue
        and check if there are more notifications pending. The MSR is available
        if KVM_FEATURE_ASYNC_PF_INT is present in CPUID.
+
+MSR_KVM_SEV_LIVE_MIGRATION:
+        0x4b564d08
+
+       Control SEV Live Migration features.
+
+data:
+        Bit 0 enables (1) or disables (0) host-side SEV Live Migration feature,
+        in other words, this is guest->host communication that it's properly
+        handling the shared pages list.
+
+        All other bits are reserved.
arch/x86/include/uapi/asm/kvm_para.h +4 -0
index 950afebfba88..f6bfa138874f 100644
@@ -33,6 +33,7 @@
 #define KVM_FEATURE_PV_SCHED_YIELD     13
 #define KVM_FEATURE_ASYNC_PF_INT       14
 #define KVM_FEATURE_MSI_EXT_DEST_ID    15
+#define KVM_FEATURE_SEV_LIVE_MIGRATION 16
 
 #define KVM_HINTS_REALTIME      0
 
@@ -54,6 +55,7 @@
 #define MSR_KVM_POLL_CONTROL   0x4b564d05
 #define MSR_KVM_ASYNC_PF_INT   0x4b564d06
 #define MSR_KVM_ASYNC_PF_ACK   0x4b564d07
+#define MSR_KVM_SEV_LIVE_MIGRATION     0x4b564d08
 
 struct kvm_steal_time {
        __u64 steal;
@@ -136,4 +138,6 @@ struct kvm_vcpu_pv_apf_data {
 #define KVM_PV_EOI_ENABLED KVM_PV_EOI_MASK
 #define KVM_PV_EOI_DISABLED 0x0
 
+#define KVM_SEV_LIVE_MIGRATION_ENABLED BIT_ULL(0)
+
 #endif /* _UAPI_ASM_X86_KVM_PARA_H */
arch/x86/kvm/svm/sev.c +0 -0
index b0d324aed515..93f42b3d3e33 100644

arch/x86/kvm/svm/sev.c int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa, · line 1635

Quoted 9 lines
arch/x86/kvm/svm/sev.c +6 -0
@@ -1627,6 +1627,16 @@ int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa,
        return ret;
 }
 
+void sev_update_migration_flags(struct kvm *kvm, u64 data)
+{
+       struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
+
+       if (!sev_guest(kvm))
+               return;

This should assert that userspace wanted the guest to be able to make these calls (see more below).

Quoted 12 lines
arch/x86/kvm/svm/sev.c +4 -0
+
+       sev->live_migration_enabled = !!(data & KVM_SEV_LIVE_MIGRATION_ENABLED);
+}
+
 int svm_get_shared_pages_list(struct kvm *kvm,
                              struct kvm_shared_pages_list *list)
 {
@@ -1639,6 +1649,9 @@ int svm_get_shared_pages_list(struct kvm *kvm,
        if (!sev_guest(kvm))
                return -ENOTTY;
 
Quoted 8 lines
Quoted 2 lines
Quoted 2 lines

arch/x86/kvm/svm/sev.c int svm_get_shared_pages_list(struct kvm *kvm, · line 1653

Quoted 2 lines
arch/x86/kvm/svm/sev.c +2 -0
+       if (!sev->live_migration_enabled)
+               return -EINVAL;

This is currently under guest control, so I'm not certain this is helpful. If I called this with otherwise valid parameters, and got back -EINVAL, I would probably think the bug is on my end. But it could be on the guest's end! I would probably drop this, but you could have KVM return an empty list of regions when this happens.

You will get -EINVAL till the guest kernel has booted to a specific point, because live migration is enabled when guest kernel has checked for host support for live migration and also OVMF/UEFI support for live migration, as per this guest kernel code below :

arch/x86/kernel/kvm.c:

kvm_init_platform() -> check_kvm_sev_migration()
..

arch/x86/mm/mem_encrypt.c:

void __init check_kvm_sev_migration(void)

{
        if (sev_active() &&
            kvm_para_has_feature(KVM_FEATURE_SEV_LIVE_MIGRATION)) {
                unsigned long nr_pages;

                pr_info("KVM enable live migration\n");
                sev_live_migration_enabled = true;

                /*
                 * Ensure that _bss_decrypted section is marked as decrypted in the
                 * shared pages list.
                 */
                nr_pages = DIV_ROUND_UP(__end_bss_decrypted - __start_bss_decrypted,
                                        PAGE_SIZE);
                early_set_mem_enc_dec_hypercall((unsigned long)__start_bss_decrypted,
                                                nr_pages, 0);

                /*
                 * If not booted using EFI, enable Live migration support.
                 */
                if (!efi_enabled(EFI_BOOT))
                        wrmsrl(MSR_KVM_SEV_LIVE_MIGRATION,
                               KVM_SEV_LIVE_MIGRATION_ENABLED);
	} else {
                        pr_info("KVM enable live migration feature unsupported\n");
                }       

Later, setup_kvm_sev_migration() invoked via a late initcall, checks for live migration supported(setup above) and UEFI/OVMF support for live migration and then enables live migration on the host via the wrmrsl() :

static int __init setup_kvm_sev_migration(void)

{       
        efi_char16_t efi_sev_live_migration_enabled[] = L"SevLiveMigrationEnabled";
        efi_guid_t efi_variable_guid = MEM_ENCRYPT_GUID;
        efi_status_t status;
        unsigned long size;
        bool enabled;
        
        /*
         * check_kvm_sev_migration() invoked via kvm_init_platform() before
         * this callback would have setup the indicator that live migration
         * feature is supported/enabled.
         */
        if (!sev_live_migration_enabled)
                return 0;
        
        if (!efi_enabled(EFI_RUNTIME_SERVICES)) {
                pr_info("%s : EFI runtime services are not enabled\n", __func__);
                return 0;
        }

        size = sizeof(enabled);

        /* Get variable contents into buffer */
        status = efi.get_variable(efi_sev_live_migration_enabled,
                                  &efi_variable_guid, NULL, &size, &enabled);
                                  
        if (status == EFI_NOT_FOUND) {
                pr_info("%s : EFI live migration variable not found\n", __func__);
                return 0;
        }
        
        if (status != EFI_SUCCESS) {
                pr_info("%s : EFI variable retrieval failed\n", __func__);
                return 0;
        }
        
        if (enabled == 0) {
                pr_info("%s: live migration disabled in EFI\n", __func__);
                return 0;
	}

	pr_info("%s : live migration enabled in EFI\n", __func__);
        wrmsrl(MSR_KVM_SEV_LIVE_MIGRATION, KVM_SEV_LIVE_MIGRATION_ENABLED);

        return true;

This ensures that host/guest live migration negotation is completed only when both host and guest have support for it and also UEFI/OVMF supports it.

Please note, that live migration cannot be initiated before this negotation is complete, which makes sense, as we don't want to enable it till the host/guest negotation is complete and UEFI/OVMF support for it is checked.

So there is this window, till guest is booting and till the late initcall is invoked that live migration cannot be initiated, and KVM_GET_SHARED_PAGES ioctl will return -EINVAL till then.

Quoted 70 lines

Alternatively, as explained below, this could call guest_pv_has instead.

Quoted 56 lines
Quoted 47 lines
Quoted 26 lines
arch/x86/kvm/svm/sev.c +1 -0
+
        if (!list->size)
                return -EINVAL;
 
arch/x86/kvm/svm/svm.c +6 -0
index 58f89f83caab..43ea5061926f 100644
@@ -2903,6 +2903,9 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr)
                svm->msr_decfg = data;
                break;
        }
+       case MSR_KVM_SEV_LIVE_MIGRATION:
+               sev_update_migration_flags(vcpu->kvm, data);
+               break;
        case MSR_IA32_APICBASE:
                if (kvm_vcpu_apicv_active(vcpu))
                        avic_update_vapic_bar(to_svm(vcpu), data);
@@ -3976,6 +3979,19 @@ static void svm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu)
                        vcpu->arch.cr3_lm_rsvd_bits &= ~(1UL << (best->ebx & 0x3f));
        }
 
+       /*
+        * If SEV guest then enable the Live migration feature.
+        */

arch/x86/kvm/svm/svm.c static void svm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) · line 3994

Quoted 10 lines
arch/x86/kvm/svm/svm.c +10 -0
+       if (sev_guest(vcpu->kvm)) {
+               struct kvm_cpuid_entry2 *best;
+
+               best = kvm_find_cpuid_entry(vcpu, KVM_CPUID_FEATURES, 0);
+               if (!best)
+                       return;
+
+               best->eax |= (1 << KVM_FEATURE_SEV_LIVE_MIGRATION);
+       }
+

Looking at this, I believe the only way for this bit to get enabled is if userspace toggles it. There needs to be a way for userspace to identify if the kernel underneath them does, in fact, support SEV LM. I'm at risk for having misread these patches (it's a long series), but I don't see anything that communicates upwards.

This could go upward with the other paravirt features flags in cpuid.c. It could also be an explicit KVM Capability (checked through check_extension).

Quoted 8 lines

Userspace should then have a chance to decide whether or not this should be enabled. And when it's not enabled, the host should return a GP in response to the hypercall. This could be configured either through userspace stripping out the LM feature bit, or by calling a VM scoped enable cap (KVM_VM_IOCTL_ENABLE_CAP).

I believe the typical path for a feature like this to be configured would be to use ENABLE_CAP.

Quoted 7 lines

I believe we have discussed and reviewed this earlier too.

To summarize this feature, the host indicates if it supports the Live Migration feature and the feature and the hypercall are only enabled on the host when the guest checks for this support and does a wrmsrl() to enable the feature. Also the guest will not make the hypercall if the host does not indicate support for it.

I've gone through and read this patch a bit more closely, and the surrounding code. Previously, I clearly misread this and the surrounding space.

Quoted 7 lines

What happens if the guest just writes to the MSR anyway? Even if it didn't receive a cue to do so? I believe the hypercall would still get invoked here, since the hypercall does not check if SEV live migration is enabled. Similarly, the MSR for enabling it is always available, even if userspace didn't ask for the cpuid bit to be set. This should not happen. Userspace should be in control of a new hypercall rolling out.

No, the guest will not invoke hypercall until live migration support has been enabled on the guest as i described above. The hypercall invocation code does this check as shown below:

static void set_memory_enc_dec_hypercall(unsigned long vaddr, int npages,
bool enc)

{
        unsigned long sz = npages << PAGE_SHIFT;
        unsigned long vaddr_end, vaddr_next;

        if (!sev_live_migration_enabled)
                return;

...
...

Thanks,
Ashish

Quoted 49 lines

I believe my interpretation last time was that the cpuid bit was getting surfaced from the host kernel to host userspace, but I don't actually see that in this patch series. Another way to ask this question would be "How does userspace know the kernel they are on has this patch series?". It needs some way of checking whether or not the kernel underneath it supports SEV live migration. Technically, I think userspace could call get_cpuid, set_cpuid (with the same values), and then get_cpuid again, and it would be able to infer by checking the SEV LM feature flag in the KVM leaf. This seems a bit kludgy. Checking support should be easy.

An additional question is "how does userspace choose whether live migration is advertised to the guest"? I believe userspace's desire for a particular value of the paravirt feature flag in CPUID get's overridden when they call set cpuid, since the feature flag is set in svm_vcpu_after_set_cpuid regardless of what userspace asks for. Userspace should have a choice in the matter.

Looking at similar paravirt-y features, there's precedent for another way of doing this (may be preferred over CHECK_EXTENSION/ENABLE_CAP?):
this could call guest_pv_has before running the hypercall. The feature (KVM_FEATURE_SEV_LIVE_MIGRATION) would then need to be exposed with the other paravirt features in __do_cpuid_func. The function guest_pv_has would represent if userspace has decided to expose SEV live migration to the guest, and the sev->live_migration_enabled would indicate if the guest responded affirmatively to the CPUID bit.

The downside of using guest_pv_has is that, if pv enforcement is disabled, guest_pv_has will always return true, which seems a bit odd for a non-SEV guest. This isn't a deal breaker, but seems a bit odd for say, a guest that isn't even running SEV. Using CHECK_EXTENSION and ENABLE_CAP sidestep that. I'm also not certain I would call this a paravirt feature.

Quoted 10 lines

And these were your review comments on the above :
I see I misunderstood how the CPUID bits get passed
through: usermode can still override them. Forgot about the back and forth for CPUID with usermode.

So as you mentioned, userspace can still override these and it gets a chance to decide whether or not this should be enabled.

Thanks,
Ashish

Thanks,
Steve

#58 lore raw

Re: [PATCH v10 12/16] KVM: x86: Introduce new KVM_FEATURE_SEV_LIVE_MIGRATION feature & Custom MSR.

In reply to Steve Rutherford

Hello Steve,

Continued response to your queries, especially related to userspace control of SEV live migration feature :

Quoted 215 lines · Steve Rutherford
Quoted 116 lines · Ashish Kalra

Hello Steve,

Quoted 112 lines · Steve Rutherford
Quoted 86 lines · Ashish Kalra

From: Ashish Kalra <[email protected]>

Add new KVM_FEATURE_SEV_LIVE_MIGRATION feature for guest to check for host-side support for SEV live migration. Also add a new custom MSR_KVM_SEV_LIVE_MIGRATION for guest to enable the SEV live migration feature.

---
 Documentation/virt/kvm/cpuid.rst     |  5 +++++
 Documentation/virt/kvm/msr.rst       | 12 ++++++++++++
 arch/x86/include/uapi/asm/kvm_para.h |  4 ++++
 arch/x86/kvm/svm/sev.c               | 13 +++++++++++++
 arch/x86/kvm/svm/svm.c               | 16 ++++++++++++++++
 arch/x86/kvm/svm/svm.h               |  2 ++
 6 files changed, 52 insertions(+)
Documentation/virt/kvm/cpuid.rst +5 -0
index cf62162d4be2..0bdb6cdb12d3 100644
@@ -96,6 +96,11 @@ KVM_FEATURE_MSI_EXT_DEST_ID        15          guest checks this feature bit
                                                before using extended destination
                                                ID bits in MSI address bits 11-5.
 
+KVM_FEATURE_SEV_LIVE_MIGRATION     16          guest checks this feature bit before
+                                               using the page encryption state
+                                               hypercall to notify the page state
+                                               change
+
 KVM_FEATURE_CLOCKSOURCE_STABLE_BIT 24          host will warn if no guest-side
                                                per-cpu warps are expected in
                                                kvmclock
Documentation/virt/kvm/msr.rst +12 -0
index e37a14c323d2..020245d16087 100644
@@ -376,3 +376,15 @@ data:
        write '1' to bit 0 of the MSR, this causes the host to re-scan its queue
        and check if there are more notifications pending. The MSR is available
        if KVM_FEATURE_ASYNC_PF_INT is present in CPUID.
+
+MSR_KVM_SEV_LIVE_MIGRATION:
+        0x4b564d08
+
+       Control SEV Live Migration features.
+
+data:
+        Bit 0 enables (1) or disables (0) host-side SEV Live Migration feature,
+        in other words, this is guest->host communication that it's properly
+        handling the shared pages list.
+
+        All other bits are reserved.
arch/x86/include/uapi/asm/kvm_para.h +4 -0
index 950afebfba88..f6bfa138874f 100644
@@ -33,6 +33,7 @@
 #define KVM_FEATURE_PV_SCHED_YIELD     13
 #define KVM_FEATURE_ASYNC_PF_INT       14
 #define KVM_FEATURE_MSI_EXT_DEST_ID    15
+#define KVM_FEATURE_SEV_LIVE_MIGRATION 16
 
 #define KVM_HINTS_REALTIME      0
 
@@ -54,6 +55,7 @@
 #define MSR_KVM_POLL_CONTROL   0x4b564d05
 #define MSR_KVM_ASYNC_PF_INT   0x4b564d06
 #define MSR_KVM_ASYNC_PF_ACK   0x4b564d07
+#define MSR_KVM_SEV_LIVE_MIGRATION     0x4b564d08
 
 struct kvm_steal_time {
        __u64 steal;
@@ -136,4 +138,6 @@ struct kvm_vcpu_pv_apf_data {
 #define KVM_PV_EOI_ENABLED KVM_PV_EOI_MASK
 #define KVM_PV_EOI_DISABLED 0x0
 
+#define KVM_SEV_LIVE_MIGRATION_ENABLED BIT_ULL(0)
+
 #endif /* _UAPI_ASM_X86_KVM_PARA_H */
arch/x86/kvm/svm/sev.c +0 -0
index b0d324aed515..93f42b3d3e33 100644

arch/x86/kvm/svm/sev.c int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa, · line 1635

Quoted 9 lines
arch/x86/kvm/svm/sev.c +6 -0
@@ -1627,6 +1627,16 @@ int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa,
        return ret;
 }
 
+void sev_update_migration_flags(struct kvm *kvm, u64 data)
+{
+       struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
+
+       if (!sev_guest(kvm))
+               return;

This should assert that userspace wanted the guest to be able to make these calls (see more below).

Quoted 12 lines
arch/x86/kvm/svm/sev.c +4 -0
+
+       sev->live_migration_enabled = !!(data & KVM_SEV_LIVE_MIGRATION_ENABLED);
+}
+
 int svm_get_shared_pages_list(struct kvm *kvm,
                              struct kvm_shared_pages_list *list)
 {
@@ -1639,6 +1649,9 @@ int svm_get_shared_pages_list(struct kvm *kvm,
        if (!sev_guest(kvm))
                return -ENOTTY;
 
Quoted 2 lines
Quoted 2 lines

arch/x86/kvm/svm/sev.c int svm_get_shared_pages_list(struct kvm *kvm, · line 1653

Quoted 2 lines
arch/x86/kvm/svm/sev.c +2 -0
+       if (!sev->live_migration_enabled)
+               return -EINVAL;

This is currently under guest control, so I'm not certain this is helpful. If I called this with otherwise valid parameters, and got back -EINVAL, I would probably think the bug is on my end. But it could be on the guest's end! I would probably drop this, but you could have KVM return an empty list of regions when this happens.

Alternatively, as explained below, this could call guest_pv_has instead.

Quoted 56 lines
Quoted 47 lines
Quoted 26 lines
arch/x86/kvm/svm/sev.c +1 -0
+
        if (!list->size)
                return -EINVAL;
 
arch/x86/kvm/svm/svm.c +6 -0
index 58f89f83caab..43ea5061926f 100644
@@ -2903,6 +2903,9 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr)
                svm->msr_decfg = data;
                break;
        }
+       case MSR_KVM_SEV_LIVE_MIGRATION:
+               sev_update_migration_flags(vcpu->kvm, data);
+               break;
        case MSR_IA32_APICBASE:
                if (kvm_vcpu_apicv_active(vcpu))
                        avic_update_vapic_bar(to_svm(vcpu), data);
@@ -3976,6 +3979,19 @@ static void svm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu)
                        vcpu->arch.cr3_lm_rsvd_bits &= ~(1UL << (best->ebx & 0x3f));
        }
 
+       /*
+        * If SEV guest then enable the Live migration feature.
+        */

arch/x86/kvm/svm/svm.c static void svm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) · line 3994

Quoted 10 lines
arch/x86/kvm/svm/svm.c +10 -0
+       if (sev_guest(vcpu->kvm)) {
+               struct kvm_cpuid_entry2 *best;
+
+               best = kvm_find_cpuid_entry(vcpu, KVM_CPUID_FEATURES, 0);
+               if (!best)
+                       return;
+
+               best->eax |= (1 << KVM_FEATURE_SEV_LIVE_MIGRATION);
+       }
+

Looking at this, I believe the only way for this bit to get enabled is if userspace toggles it. There needs to be a way for userspace to identify if the kernel underneath them does, in fact, support SEV LM. I'm at risk for having misread these patches (it's a long series), but I don't see anything that communicates upwards.

This could go upward with the other paravirt features flags in cpuid.c. It could also be an explicit KVM Capability (checked through check_extension).

Quoted 8 lines

Userspace should then have a chance to decide whether or not this should be enabled. And when it's not enabled, the host should return a GP in response to the hypercall. This could be configured either through userspace stripping out the LM feature bit, or by calling a VM scoped enable cap (KVM_VM_IOCTL_ENABLE_CAP).

I believe the typical path for a feature like this to be configured would be to use ENABLE_CAP.

Quoted 7 lines

I believe we have discussed and reviewed this earlier too.

To summarize this feature, the host indicates if it supports the Live Migration feature and the feature and the hypercall are only enabled on the host when the guest checks for this support and does a wrmsrl() to enable the feature. Also the guest will not make the hypercall if the host does not indicate support for it.

I've gone through and read this patch a bit more closely, and the surrounding code. Previously, I clearly misread this and the surrounding space.

What happens if the guest just writes to the MSR anyway? Even if it didn't receive a cue to do so? I believe the hypercall would still get invoked here, since the hypercall does not check if SEV live migration is enabled. Similarly, the MSR for enabling it is always available, even if userspace didn't ask for the cpuid bit to be set. This should not happen. Userspace should be in control of a new hypercall rolling out.

I believe my interpretation last time was that the cpuid bit was getting surfaced from the host kernel to host userspace, but I don't actually see that in this patch series. Another way to ask this question would be "How does userspace know the kernel they are on has this patch series?". It needs some way of checking whether or not the kernel underneath it supports SEV live migration. Technically, I think userspace could call get_cpuid, set_cpuid (with the same values), and then get_cpuid again, and it would be able to infer by checking the SEV LM feature flag in the KVM leaf. This seems a bit kludgy. Checking support should be easy.

Quoted 7 lines

An additional question is "how does userspace choose whether live migration is advertised to the guest"? I believe userspace's desire for a particular value of the paravirt feature flag in CPUID get's overridden when they call set cpuid, since the feature flag is set in svm_vcpu_after_set_cpuid regardless of what userspace asks for. Userspace should have a choice in the matter.

To summarize, KVM (host) enables SEV live migration feature as following:

static void svm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) {
...

        /*
         * If SEV guest then enable the Live migration feature.
         */
        if (sev_guest(vcpu->kvm)) {
                struct kvm_cpuid_entry2 *best;

                best = kvm_find_cpuid_entry(vcpu, KVM_CPUID_FEATURES, 0);
                if (!best)
                        return;

                best->eax |= (1 << KVM_FEATURE_SEV_LIVE_MIGRATION);
        }

...
...

Later userspace can call cpuid(KVM_CPUID_FEATURES) and get the cpuid data and override it, for example, this is how Qemu userspace code currently fixups/overrides the KVM reported CPUID features :

target/i386/kvm/kvm.c:

uint32_t kvm_arch_get_supported_cpuid(KVMState *s, uint32_t function,
uint32_t index, int reg)
{
...
...

  cpuid = get_supported_cpuid(s);

  struct kvm_cpuid_entry2 *entry = cpuid_find_entry(cpuid, function, index);
  if (entry) {
      ret = cpuid_entry_get_reg(entry, reg);
  }

/* Fixups for the data returned by KVM, below */

...
...

  } else if (function == KVM_CPUID_FEATURES && reg == R_EAX) {
        /* kvm_pv_unhalt is reported by GET_SUPPORTED_CPUID, but it can't
         * be enabled without the in-kernel irqchip
         */
        if (!kvm_irqchip_in_kernel()) {
            ret &= ~(1U << KVM_FEATURE_PV_UNHALT);
        }
        if (kvm_irqchip_is_split()) {
            ret |= 1U << KVM_FEATURE_MSI_EXT_DEST_ID;
        }
    } else if (function == KVM_CPUID_FEATURES && reg == R_EDX) {
        ret |= 1U << KVM_HINTS_REALTIME;
    }
    return ret;

So you can use a similar approach to override
KVM_FEATURE_SEV_LIVE_MIGRATION feature.

Thanks,
Ashish

Quoted 30 lines

Looking at similar paravirt-y features, there's precedent for another way of doing this (may be preferred over CHECK_EXTENSION/ENABLE_CAP?):
this could call guest_pv_has before running the hypercall. The feature (KVM_FEATURE_SEV_LIVE_MIGRATION) would then need to be exposed with the other paravirt features in __do_cpuid_func. The function guest_pv_has would represent if userspace has decided to expose SEV live migration to the guest, and the sev->live_migration_enabled would indicate if the guest responded affirmatively to the CPUID bit.

The downside of using guest_pv_has is that, if pv enforcement is disabled, guest_pv_has will always return true, which seems a bit odd for a non-SEV guest. This isn't a deal breaker, but seems a bit odd for say, a guest that isn't even running SEV. Using CHECK_EXTENSION and ENABLE_CAP sidestep that. I'm also not certain I would call this a paravirt feature.

Quoted 10 lines

And these were your review comments on the above :
I see I misunderstood how the CPUID bits get passed
through: usermode can still override them. Forgot about the back and forth for CPUID with usermode.

So as you mentioned, userspace can still override these and it gets a chance to decide whether or not this should be enabled.

Thanks,
Ashish

Thanks,
Steve

#59 lore raw

Re: [PATCH v10 12/16] KVM: x86: Introduce new KVM_FEATURE_SEV_LIVE_MIGRATION feature & Custom MSR.

Hello Steve,

Quoted 228 lines · Ashish Kalra

Hello Steve,

Continued response to your queries, especially related to userspace control of SEV live migration feature :

Quoted 222 lines · Steve Rutherford
Quoted 116 lines · Ashish Kalra

Hello Steve,

Quoted 112 lines · Steve Rutherford
Quoted 86 lines · Ashish Kalra

From: Ashish Kalra <[email protected]>

Add new KVM_FEATURE_SEV_LIVE_MIGRATION feature for guest to check for host-side support for SEV live migration. Also add a new custom MSR_KVM_SEV_LIVE_MIGRATION for guest to enable the SEV live migration feature.

---
 Documentation/virt/kvm/cpuid.rst     |  5 +++++
 Documentation/virt/kvm/msr.rst       | 12 ++++++++++++
 arch/x86/include/uapi/asm/kvm_para.h |  4 ++++
 arch/x86/kvm/svm/sev.c               | 13 +++++++++++++
 arch/x86/kvm/svm/svm.c               | 16 ++++++++++++++++
 arch/x86/kvm/svm/svm.h               |  2 ++
 6 files changed, 52 insertions(+)
Documentation/virt/kvm/cpuid.rst +5 -0
index cf62162d4be2..0bdb6cdb12d3 100644
@@ -96,6 +96,11 @@ KVM_FEATURE_MSI_EXT_DEST_ID        15          guest checks this feature bit
                                                before using extended destination
                                                ID bits in MSI address bits 11-5.
 
+KVM_FEATURE_SEV_LIVE_MIGRATION     16          guest checks this feature bit before
+                                               using the page encryption state
+                                               hypercall to notify the page state
+                                               change
+
 KVM_FEATURE_CLOCKSOURCE_STABLE_BIT 24          host will warn if no guest-side
                                                per-cpu warps are expected in
                                                kvmclock
Documentation/virt/kvm/msr.rst +12 -0
index e37a14c323d2..020245d16087 100644
@@ -376,3 +376,15 @@ data:
        write '1' to bit 0 of the MSR, this causes the host to re-scan its queue
        and check if there are more notifications pending. The MSR is available
        if KVM_FEATURE_ASYNC_PF_INT is present in CPUID.
+
+MSR_KVM_SEV_LIVE_MIGRATION:
+        0x4b564d08
+
+       Control SEV Live Migration features.
+
+data:
+        Bit 0 enables (1) or disables (0) host-side SEV Live Migration feature,
+        in other words, this is guest->host communication that it's properly
+        handling the shared pages list.
+
+        All other bits are reserved.
arch/x86/include/uapi/asm/kvm_para.h +4 -0
index 950afebfba88..f6bfa138874f 100644
@@ -33,6 +33,7 @@
 #define KVM_FEATURE_PV_SCHED_YIELD     13
 #define KVM_FEATURE_ASYNC_PF_INT       14
 #define KVM_FEATURE_MSI_EXT_DEST_ID    15
+#define KVM_FEATURE_SEV_LIVE_MIGRATION 16
 
 #define KVM_HINTS_REALTIME      0
 
@@ -54,6 +55,7 @@
 #define MSR_KVM_POLL_CONTROL   0x4b564d05
 #define MSR_KVM_ASYNC_PF_INT   0x4b564d06
 #define MSR_KVM_ASYNC_PF_ACK   0x4b564d07
+#define MSR_KVM_SEV_LIVE_MIGRATION     0x4b564d08
 
 struct kvm_steal_time {
        __u64 steal;
@@ -136,4 +138,6 @@ struct kvm_vcpu_pv_apf_data {
 #define KVM_PV_EOI_ENABLED KVM_PV_EOI_MASK
 #define KVM_PV_EOI_DISABLED 0x0
 
+#define KVM_SEV_LIVE_MIGRATION_ENABLED BIT_ULL(0)
+
 #endif /* _UAPI_ASM_X86_KVM_PARA_H */
arch/x86/kvm/svm/sev.c +0 -0
index b0d324aed515..93f42b3d3e33 100644

arch/x86/kvm/svm/sev.c int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa, · line 1635

Quoted 9 lines
arch/x86/kvm/svm/sev.c +6 -0
@@ -1627,6 +1627,16 @@ int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa,
        return ret;
 }
 
+void sev_update_migration_flags(struct kvm *kvm, u64 data)
+{
+       struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
+
+       if (!sev_guest(kvm))
+               return;

This should assert that userspace wanted the guest to be able to make these calls (see more below).

Quoted 12 lines
arch/x86/kvm/svm/sev.c +4 -0
+
+       sev->live_migration_enabled = !!(data & KVM_SEV_LIVE_MIGRATION_ENABLED);
+}
+
 int svm_get_shared_pages_list(struct kvm *kvm,
                              struct kvm_shared_pages_list *list)
 {
@@ -1639,6 +1649,9 @@ int svm_get_shared_pages_list(struct kvm *kvm,
        if (!sev_guest(kvm))
                return -ENOTTY;
 
Quoted 2 lines
Quoted 2 lines

arch/x86/kvm/svm/sev.c int svm_get_shared_pages_list(struct kvm *kvm, · line 1653

Quoted 2 lines
arch/x86/kvm/svm/sev.c +2 -0
+       if (!sev->live_migration_enabled)
+               return -EINVAL;

This is currently under guest control, so I'm not certain this is helpful. If I called this with otherwise valid parameters, and got back -EINVAL, I would probably think the bug is on my end. But it could be on the guest's end! I would probably drop this, but you could have KVM return an empty list of regions when this happens.

Alternatively, as explained below, this could call guest_pv_has instead.

Quoted 56 lines
Quoted 47 lines
Quoted 26 lines
arch/x86/kvm/svm/sev.c +1 -0
+
        if (!list->size)
                return -EINVAL;
 
arch/x86/kvm/svm/svm.c +6 -0
index 58f89f83caab..43ea5061926f 100644
@@ -2903,6 +2903,9 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr)
                svm->msr_decfg = data;
                break;
        }
+       case MSR_KVM_SEV_LIVE_MIGRATION:
+               sev_update_migration_flags(vcpu->kvm, data);
+               break;
        case MSR_IA32_APICBASE:
                if (kvm_vcpu_apicv_active(vcpu))
                        avic_update_vapic_bar(to_svm(vcpu), data);
@@ -3976,6 +3979,19 @@ static void svm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu)
                        vcpu->arch.cr3_lm_rsvd_bits &= ~(1UL << (best->ebx & 0x3f));
        }
 
+       /*
+        * If SEV guest then enable the Live migration feature.
+        */

arch/x86/kvm/svm/svm.c static void svm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) · line 3994

Quoted 10 lines
arch/x86/kvm/svm/svm.c +10 -0
+       if (sev_guest(vcpu->kvm)) {
+               struct kvm_cpuid_entry2 *best;
+
+               best = kvm_find_cpuid_entry(vcpu, KVM_CPUID_FEATURES, 0);
+               if (!best)
+                       return;
+
+               best->eax |= (1 << KVM_FEATURE_SEV_LIVE_MIGRATION);
+       }
+

Looking at this, I believe the only way for this bit to get enabled is if userspace toggles it. There needs to be a way for userspace to identify if the kernel underneath them does, in fact, support SEV LM. I'm at risk for having misread these patches (it's a long series), but I don't see anything that communicates upwards.

This could go upward with the other paravirt features flags in cpuid.c. It could also be an explicit KVM Capability (checked through check_extension).

Quoted 8 lines

Userspace should then have a chance to decide whether or not this should be enabled. And when it's not enabled, the host should return a GP in response to the hypercall. This could be configured either through userspace stripping out the LM feature bit, or by calling a VM scoped enable cap (KVM_VM_IOCTL_ENABLE_CAP).

I believe the typical path for a feature like this to be configured would be to use ENABLE_CAP.

Quoted 7 lines

I believe we have discussed and reviewed this earlier too.

To summarize this feature, the host indicates if it supports the Live Migration feature and the feature and the hypercall are only enabled on the host when the guest checks for this support and does a wrmsrl() to enable the feature. Also the guest will not make the hypercall if the host does not indicate support for it.

I've gone through and read this patch a bit more closely, and the surrounding code. Previously, I clearly misread this and the surrounding space.

What happens if the guest just writes to the MSR anyway? Even if it didn't receive a cue to do so? I believe the hypercall would still get invoked here, since the hypercall does not check if SEV live migration is enabled. Similarly, the MSR for enabling it is always available, even if userspace didn't ask for the cpuid bit to be set. This should not happen. Userspace should be in control of a new hypercall rolling out.

I believe my interpretation last time was that the cpuid bit was getting surfaced from the host kernel to host userspace, but I don't actually see that in this patch series. Another way to ask this question would be "How does userspace know the kernel they are on has this patch series?". It needs some way of checking whether or not the kernel underneath it supports SEV live migration. Technically, I think userspace could call get_cpuid, set_cpuid (with the same values), and then get_cpuid again, and it would be able to infer by checking the SEV LM feature flag in the KVM leaf. This seems a bit kludgy. Checking support should be easy.

An additional question is "how does userspace choose whether live migration is advertised to the guest"? I believe userspace's desire for a particular value of the paravirt feature flag in CPUID get's overridden when they call set cpuid, since the feature flag is set in svm_vcpu_after_set_cpuid regardless of what userspace asks for. Userspace should have a choice in the matter.

Actually i did some more analysis of this, and i believe you are right about the above, feature flag gets set in svm_vcpu_after_set_cpuid.

So please ignore my comments below.

I am still analyzing this further.

Thanks,
Ashish

Quoted 99 lines

To summarize, KVM (host) enables SEV live migration feature as following:

static void svm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) {
...

        /*
         * If SEV guest then enable the Live migration feature.
         */
        if (sev_guest(vcpu->kvm)) {
                struct kvm_cpuid_entry2 *best;

                best = kvm_find_cpuid_entry(vcpu, KVM_CPUID_FEATURES, 0);
                if (!best)
                        return;

                best->eax |= (1 << KVM_FEATURE_SEV_LIVE_MIGRATION);
        }

...
...

Later userspace can call cpuid(KVM_CPUID_FEATURES) and get the cpuid data and override it, for example, this is how Qemu userspace code currently fixups/overrides the KVM reported CPUID features :

target/i386/kvm/kvm.c:

uint32_t kvm_arch_get_supported_cpuid(KVMState *s, uint32_t function,
uint32_t index, int reg)
{
...
...

  cpuid = get_supported_cpuid(s);

  struct kvm_cpuid_entry2 *entry = cpuid_find_entry(cpuid, function, index);
  if (entry) {
      ret = cpuid_entry_get_reg(entry, reg);
  }

/* Fixups for the data returned by KVM, below */

...
...

  } else if (function == KVM_CPUID_FEATURES && reg == R_EAX) {
        /* kvm_pv_unhalt is reported by GET_SUPPORTED_CPUID, but it can't
         * be enabled without the in-kernel irqchip
         */
        if (!kvm_irqchip_in_kernel()) {
            ret &= ~(1U << KVM_FEATURE_PV_UNHALT);
        }
        if (kvm_irqchip_is_split()) {
            ret |= 1U << KVM_FEATURE_MSI_EXT_DEST_ID;
        }
    } else if (function == KVM_CPUID_FEATURES && reg == R_EDX) {
        ret |= 1U << KVM_HINTS_REALTIME;
    }
    return ret;

So you can use a similar approach to override
KVM_FEATURE_SEV_LIVE_MIGRATION feature.

Thanks,
Ashish

Quoted 30 lines

Looking at similar paravirt-y features, there's precedent for another way of doing this (may be preferred over CHECK_EXTENSION/ENABLE_CAP?):
this could call guest_pv_has before running the hypercall. The feature (KVM_FEATURE_SEV_LIVE_MIGRATION) would then need to be exposed with the other paravirt features in __do_cpuid_func. The function guest_pv_has would represent if userspace has decided to expose SEV live migration to the guest, and the sev->live_migration_enabled would indicate if the guest responded affirmatively to the CPUID bit.

The downside of using guest_pv_has is that, if pv enforcement is disabled, guest_pv_has will always return true, which seems a bit odd for a non-SEV guest. This isn't a deal breaker, but seems a bit odd for say, a guest that isn't even running SEV. Using CHECK_EXTENSION and ENABLE_CAP sidestep that. I'm also not certain I would call this a paravirt feature.

Quoted 10 lines

And these were your review comments on the above :
I see I misunderstood how the CPUID bits get passed
through: usermode can still override them. Forgot about the back and forth for CPUID with usermode.

So as you mentioned, userspace can still override these and it gets a chance to decide whether or not this should be enabled.

Thanks,
Ashish

Thanks,
Steve

#60 lore raw

Re: [PATCH v10 12/16] KVM: x86: Introduce new KVM_FEATURE_SEV_LIVE_MIGRATION feature & Custom MSR.

Hello Steve,

Quoted 232 lines · Ashish Kalra

Hello Steve,

Quoted 228 lines · Ashish Kalra

Hello Steve,

Continued response to your queries, especially related to userspace control of SEV live migration feature :

Quoted 222 lines · Steve Rutherford
Quoted 116 lines · Ashish Kalra

Hello Steve,

Quoted 112 lines · Steve Rutherford
Quoted 86 lines · Ashish Kalra

From: Ashish Kalra <[email protected]>

Add new KVM_FEATURE_SEV_LIVE_MIGRATION feature for guest to check for host-side support for SEV live migration. Also add a new custom MSR_KVM_SEV_LIVE_MIGRATION for guest to enable the SEV live migration feature.

---
 Documentation/virt/kvm/cpuid.rst     |  5 +++++
 Documentation/virt/kvm/msr.rst       | 12 ++++++++++++
 arch/x86/include/uapi/asm/kvm_para.h |  4 ++++
 arch/x86/kvm/svm/sev.c               | 13 +++++++++++++
 arch/x86/kvm/svm/svm.c               | 16 ++++++++++++++++
 arch/x86/kvm/svm/svm.h               |  2 ++
 6 files changed, 52 insertions(+)
Documentation/virt/kvm/cpuid.rst +5 -0
index cf62162d4be2..0bdb6cdb12d3 100644
@@ -96,6 +96,11 @@ KVM_FEATURE_MSI_EXT_DEST_ID        15          guest checks this feature bit
                                                before using extended destination
                                                ID bits in MSI address bits 11-5.
 
+KVM_FEATURE_SEV_LIVE_MIGRATION     16          guest checks this feature bit before
+                                               using the page encryption state
+                                               hypercall to notify the page state
+                                               change
+
 KVM_FEATURE_CLOCKSOURCE_STABLE_BIT 24          host will warn if no guest-side
                                                per-cpu warps are expected in
                                                kvmclock
Documentation/virt/kvm/msr.rst +12 -0
index e37a14c323d2..020245d16087 100644
@@ -376,3 +376,15 @@ data:
        write '1' to bit 0 of the MSR, this causes the host to re-scan its queue
        and check if there are more notifications pending. The MSR is available
        if KVM_FEATURE_ASYNC_PF_INT is present in CPUID.
+
+MSR_KVM_SEV_LIVE_MIGRATION:
+        0x4b564d08
+
+       Control SEV Live Migration features.
+
+data:
+        Bit 0 enables (1) or disables (0) host-side SEV Live Migration feature,
+        in other words, this is guest->host communication that it's properly
+        handling the shared pages list.
+
+        All other bits are reserved.
arch/x86/include/uapi/asm/kvm_para.h +4 -0
index 950afebfba88..f6bfa138874f 100644
@@ -33,6 +33,7 @@
 #define KVM_FEATURE_PV_SCHED_YIELD     13
 #define KVM_FEATURE_ASYNC_PF_INT       14
 #define KVM_FEATURE_MSI_EXT_DEST_ID    15
+#define KVM_FEATURE_SEV_LIVE_MIGRATION 16
 
 #define KVM_HINTS_REALTIME      0
 
@@ -54,6 +55,7 @@
 #define MSR_KVM_POLL_CONTROL   0x4b564d05
 #define MSR_KVM_ASYNC_PF_INT   0x4b564d06
 #define MSR_KVM_ASYNC_PF_ACK   0x4b564d07
+#define MSR_KVM_SEV_LIVE_MIGRATION     0x4b564d08
 
 struct kvm_steal_time {
        __u64 steal;
@@ -136,4 +138,6 @@ struct kvm_vcpu_pv_apf_data {
 #define KVM_PV_EOI_ENABLED KVM_PV_EOI_MASK
 #define KVM_PV_EOI_DISABLED 0x0
 
+#define KVM_SEV_LIVE_MIGRATION_ENABLED BIT_ULL(0)
+
 #endif /* _UAPI_ASM_X86_KVM_PARA_H */
arch/x86/kvm/svm/sev.c +0 -0
index b0d324aed515..93f42b3d3e33 100644

arch/x86/kvm/svm/sev.c int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa, · line 1635

Quoted 9 lines
arch/x86/kvm/svm/sev.c +6 -0
@@ -1627,6 +1627,16 @@ int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa,
        return ret;
 }
 
+void sev_update_migration_flags(struct kvm *kvm, u64 data)
+{
+       struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
+
+       if (!sev_guest(kvm))
+               return;

This should assert that userspace wanted the guest to be able to make these calls (see more below).

Quoted 12 lines
arch/x86/kvm/svm/sev.c +4 -0
+
+       sev->live_migration_enabled = !!(data & KVM_SEV_LIVE_MIGRATION_ENABLED);
+}
+
 int svm_get_shared_pages_list(struct kvm *kvm,
                              struct kvm_shared_pages_list *list)
 {
@@ -1639,6 +1649,9 @@ int svm_get_shared_pages_list(struct kvm *kvm,
        if (!sev_guest(kvm))
                return -ENOTTY;
 
Quoted 2 lines
Quoted 2 lines

arch/x86/kvm/svm/sev.c int svm_get_shared_pages_list(struct kvm *kvm, · line 1653

Quoted 2 lines
arch/x86/kvm/svm/sev.c +2 -0
+       if (!sev->live_migration_enabled)
+               return -EINVAL;

This is currently under guest control, so I'm not certain this is helpful. If I called this with otherwise valid parameters, and got back -EINVAL, I would probably think the bug is on my end. But it could be on the guest's end! I would probably drop this, but you could have KVM return an empty list of regions when this happens.

Alternatively, as explained below, this could call guest_pv_has instead.

Quoted 56 lines
Quoted 47 lines
Quoted 26 lines
arch/x86/kvm/svm/sev.c +1 -0
+
        if (!list->size)
                return -EINVAL;
 
arch/x86/kvm/svm/svm.c +6 -0
index 58f89f83caab..43ea5061926f 100644
@@ -2903,6 +2903,9 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr)
                svm->msr_decfg = data;
                break;
        }
+       case MSR_KVM_SEV_LIVE_MIGRATION:
+               sev_update_migration_flags(vcpu->kvm, data);
+               break;
        case MSR_IA32_APICBASE:
                if (kvm_vcpu_apicv_active(vcpu))
                        avic_update_vapic_bar(to_svm(vcpu), data);
@@ -3976,6 +3979,19 @@ static void svm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu)
                        vcpu->arch.cr3_lm_rsvd_bits &= ~(1UL << (best->ebx & 0x3f));
        }
 
+       /*
+        * If SEV guest then enable the Live migration feature.
+        */

arch/x86/kvm/svm/svm.c static void svm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) · line 3994

Quoted 10 lines
arch/x86/kvm/svm/svm.c +10 -0
+       if (sev_guest(vcpu->kvm)) {
+               struct kvm_cpuid_entry2 *best;
+
+               best = kvm_find_cpuid_entry(vcpu, KVM_CPUID_FEATURES, 0);
+               if (!best)
+                       return;
+
+               best->eax |= (1 << KVM_FEATURE_SEV_LIVE_MIGRATION);
+       }
+

Looking at this, I believe the only way for this bit to get enabled is if userspace toggles it. There needs to be a way for userspace to identify if the kernel underneath them does, in fact, support SEV LM. I'm at risk for having misread these patches (it's a long series), but I don't see anything that communicates upwards.

This could go upward with the other paravirt features flags in cpuid.c. It could also be an explicit KVM Capability (checked through check_extension).

Quoted 8 lines

Userspace should then have a chance to decide whether or not this should be enabled. And when it's not enabled, the host should return a GP in response to the hypercall. This could be configured either through userspace stripping out the LM feature bit, or by calling a VM scoped enable cap (KVM_VM_IOCTL_ENABLE_CAP).

I believe the typical path for a feature like this to be configured would be to use ENABLE_CAP.

Quoted 7 lines

I believe we have discussed and reviewed this earlier too.

To summarize this feature, the host indicates if it supports the Live Migration feature and the feature and the hypercall are only enabled on the host when the guest checks for this support and does a wrmsrl() to enable the feature. Also the guest will not make the hypercall if the host does not indicate support for it.

I've gone through and read this patch a bit more closely, and the surrounding code. Previously, I clearly misread this and the surrounding space.

What happens if the guest just writes to the MSR anyway? Even if it didn't receive a cue to do so? I believe the hypercall would still get invoked here, since the hypercall does not check if SEV live migration is enabled. Similarly, the MSR for enabling it is always available, even if userspace didn't ask for the cpuid bit to be set. This should not happen. Userspace should be in control of a new hypercall rolling out.

I believe my interpretation last time was that the cpuid bit was getting surfaced from the host kernel to host userspace, but I don't actually see that in this patch series. Another way to ask this question would be "How does userspace know the kernel they are on has this patch series?". It needs some way of checking whether or not the kernel underneath it supports SEV live migration. Technically, I think userspace could call get_cpuid, set_cpuid (with the same values), and then get_cpuid again, and it would be able to infer by checking the SEV LM feature flag in the KVM leaf. This seems a bit kludgy. Checking support should be easy.

An additional question is "how does userspace choose whether live migration is advertised to the guest"? I believe userspace's desire for a particular value of the paravirt feature flag in CPUID get's overridden when they call set cpuid, since the feature flag is set in svm_vcpu_after_set_cpuid regardless of what userspace asks for. Userspace should have a choice in the matter.

Quoted 3 lines

Actually i did some more analysis of this, and i believe you are right about the above, feature flag gets set in svm_vcpu_after_set_cpuid.

As you mentioned above and as i confirmed in my previous email, calling KVM_SET_CPUID2 vcpu ioctl will always set the live migration feature flag for the vCPU.

This is what will be queried by the guest to enable the kernel's live migration feature and to start making hypercalls.

Now, i want to understand why do you want the userspace to have a choice in this matter ?

After all, it is the userspace which actually initiates the live migration process, so doesn't it have the final choice in this matter ?

Even if this feature is reported by host, the guest only uses it to enable and make page encryption status hypercalls and the host's shared pages list gets setup accordingly.

But unless userspace calls KVM_GET_SHARED_PAGES_LIST ioctl and initiates live migration process, the above is simply enabling the guest to make hypercalls whenever a page's encryption status is changed.

Thanks,
Ashish

#61 lore raw

Re: [PATCH v10 12/16] KVM: x86: Introduce new KVM_FEATURE_SEV_LIVE_MIGRATION feature & Custom MSR.

Hi Ashish,

Quoted 244 lines · Ashish Kalra

Hello Steve,

Quoted 232 lines · Ashish Kalra

Hello Steve,

Quoted 228 lines · Ashish Kalra

Hello Steve,

Continued response to your queries, especially related to userspace control of SEV live migration feature :

Quoted 222 lines · Steve Rutherford
Quoted 116 lines · Ashish Kalra

Hello Steve,

Quoted 112 lines · Steve Rutherford
Quoted 86 lines · Ashish Kalra

From: Ashish Kalra <[email protected]>

Add new KVM_FEATURE_SEV_LIVE_MIGRATION feature for guest to check for host-side support for SEV live migration. Also add a new custom MSR_KVM_SEV_LIVE_MIGRATION for guest to enable the SEV live migration feature.

---
 Documentation/virt/kvm/cpuid.rst     |  5 +++++
 Documentation/virt/kvm/msr.rst       | 12 ++++++++++++
 arch/x86/include/uapi/asm/kvm_para.h |  4 ++++
 arch/x86/kvm/svm/sev.c               | 13 +++++++++++++
 arch/x86/kvm/svm/svm.c               | 16 ++++++++++++++++
 arch/x86/kvm/svm/svm.h               |  2 ++
 6 files changed, 52 insertions(+)
Documentation/virt/kvm/cpuid.rst +5 -0
index cf62162d4be2..0bdb6cdb12d3 100644
@@ -96,6 +96,11 @@ KVM_FEATURE_MSI_EXT_DEST_ID        15          guest checks this feature bit
                                                before using extended destination
                                                ID bits in MSI address bits 11-5.
 
+KVM_FEATURE_SEV_LIVE_MIGRATION     16          guest checks this feature bit before
+                                               using the page encryption state
+                                               hypercall to notify the page state
+                                               change
+
 KVM_FEATURE_CLOCKSOURCE_STABLE_BIT 24          host will warn if no guest-side
                                                per-cpu warps are expected in
                                                kvmclock
Documentation/virt/kvm/msr.rst +12 -0
index e37a14c323d2..020245d16087 100644
@@ -376,3 +376,15 @@ data:
        write '1' to bit 0 of the MSR, this causes the host to re-scan its queue
        and check if there are more notifications pending. The MSR is available
        if KVM_FEATURE_ASYNC_PF_INT is present in CPUID.
+
+MSR_KVM_SEV_LIVE_MIGRATION:
+        0x4b564d08
+
+       Control SEV Live Migration features.
+
+data:
+        Bit 0 enables (1) or disables (0) host-side SEV Live Migration feature,
+        in other words, this is guest->host communication that it's properly
+        handling the shared pages list.
+
+        All other bits are reserved.
arch/x86/include/uapi/asm/kvm_para.h +4 -0
index 950afebfba88..f6bfa138874f 100644
@@ -33,6 +33,7 @@
 #define KVM_FEATURE_PV_SCHED_YIELD     13
 #define KVM_FEATURE_ASYNC_PF_INT       14
 #define KVM_FEATURE_MSI_EXT_DEST_ID    15
+#define KVM_FEATURE_SEV_LIVE_MIGRATION 16
 
 #define KVM_HINTS_REALTIME      0
 
@@ -54,6 +55,7 @@
 #define MSR_KVM_POLL_CONTROL   0x4b564d05
 #define MSR_KVM_ASYNC_PF_INT   0x4b564d06
 #define MSR_KVM_ASYNC_PF_ACK   0x4b564d07
+#define MSR_KVM_SEV_LIVE_MIGRATION     0x4b564d08
 
 struct kvm_steal_time {
        __u64 steal;
@@ -136,4 +138,6 @@ struct kvm_vcpu_pv_apf_data {
 #define KVM_PV_EOI_ENABLED KVM_PV_EOI_MASK
 #define KVM_PV_EOI_DISABLED 0x0
 
+#define KVM_SEV_LIVE_MIGRATION_ENABLED BIT_ULL(0)
+
 #endif /* _UAPI_ASM_X86_KVM_PARA_H */
arch/x86/kvm/svm/sev.c +0 -0
index b0d324aed515..93f42b3d3e33 100644

arch/x86/kvm/svm/sev.c int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa, · line 1635

Quoted 9 lines
arch/x86/kvm/svm/sev.c +6 -0
@@ -1627,6 +1627,16 @@ int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa,
        return ret;
 }
 
+void sev_update_migration_flags(struct kvm *kvm, u64 data)
+{
+       struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
+
+       if (!sev_guest(kvm))
+               return;

This should assert that userspace wanted the guest to be able to make these calls (see more below).

Quoted 12 lines
arch/x86/kvm/svm/sev.c +4 -0
+
+       sev->live_migration_enabled = !!(data & KVM_SEV_LIVE_MIGRATION_ENABLED);
+}
+
 int svm_get_shared_pages_list(struct kvm *kvm,
                              struct kvm_shared_pages_list *list)
 {
@@ -1639,6 +1649,9 @@ int svm_get_shared_pages_list(struct kvm *kvm,
        if (!sev_guest(kvm))
                return -ENOTTY;
 
Quoted 2 lines
Quoted 2 lines

arch/x86/kvm/svm/sev.c int svm_get_shared_pages_list(struct kvm *kvm, · line 1653

Quoted 2 lines
arch/x86/kvm/svm/sev.c +2 -0
+       if (!sev->live_migration_enabled)
+               return -EINVAL;

This is currently under guest control, so I'm not certain this is helpful. If I called this with otherwise valid parameters, and got back -EINVAL, I would probably think the bug is on my end. But it could be on the guest's end! I would probably drop this, but you could have KVM return an empty list of regions when this happens.

Alternatively, as explained below, this could call guest_pv_has instead.

Quoted 56 lines
Quoted 47 lines
Quoted 26 lines
arch/x86/kvm/svm/sev.c +1 -0
+
        if (!list->size)
                return -EINVAL;
 
arch/x86/kvm/svm/svm.c +6 -0
index 58f89f83caab..43ea5061926f 100644
@@ -2903,6 +2903,9 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr)
                svm->msr_decfg = data;
                break;
        }
+       case MSR_KVM_SEV_LIVE_MIGRATION:
+               sev_update_migration_flags(vcpu->kvm, data);
+               break;
        case MSR_IA32_APICBASE:
                if (kvm_vcpu_apicv_active(vcpu))
                        avic_update_vapic_bar(to_svm(vcpu), data);
@@ -3976,6 +3979,19 @@ static void svm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu)
                        vcpu->arch.cr3_lm_rsvd_bits &= ~(1UL << (best->ebx & 0x3f));
        }
 
+       /*
+        * If SEV guest then enable the Live migration feature.
+        */

arch/x86/kvm/svm/svm.c static void svm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) · line 3994

Quoted 10 lines
arch/x86/kvm/svm/svm.c +10 -0
+       if (sev_guest(vcpu->kvm)) {
+               struct kvm_cpuid_entry2 *best;
+
+               best = kvm_find_cpuid_entry(vcpu, KVM_CPUID_FEATURES, 0);
+               if (!best)
+                       return;
+
+               best->eax |= (1 << KVM_FEATURE_SEV_LIVE_MIGRATION);
+       }
+

Looking at this, I believe the only way for this bit to get enabled is if userspace toggles it. There needs to be a way for userspace to identify if the kernel underneath them does, in fact, support SEV LM. I'm at risk for having misread these patches (it's a long series), but I don't see anything that communicates upwards.

This could go upward with the other paravirt features flags in cpuid.c. It could also be an explicit KVM Capability (checked through check_extension).

Quoted 8 lines

Userspace should then have a chance to decide whether or not this should be enabled. And when it's not enabled, the host should return a GP in response to the hypercall. This could be configured either through userspace stripping out the LM feature bit, or by calling a VM scoped enable cap (KVM_VM_IOCTL_ENABLE_CAP).

I believe the typical path for a feature like this to be configured would be to use ENABLE_CAP.

Quoted 7 lines

I believe we have discussed and reviewed this earlier too.

To summarize this feature, the host indicates if it supports the Live Migration feature and the feature and the hypercall are only enabled on the host when the guest checks for this support and does a wrmsrl() to enable the feature. Also the guest will not make the hypercall if the host does not indicate support for it.

I've gone through and read this patch a bit more closely, and the surrounding code. Previously, I clearly misread this and the surrounding space.

What happens if the guest just writes to the MSR anyway? Even if it didn't receive a cue to do so? I believe the hypercall would still get invoked here, since the hypercall does not check if SEV live migration is enabled. Similarly, the MSR for enabling it is always available, even if userspace didn't ask for the cpuid bit to be set. This should not happen. Userspace should be in control of a new hypercall rolling out.

I believe my interpretation last time was that the cpuid bit was getting surfaced from the host kernel to host userspace, but I don't actually see that in this patch series. Another way to ask this question would be "How does userspace know the kernel they are on has this patch series?". It needs some way of checking whether or not the kernel underneath it supports SEV live migration. Technically, I think userspace could call get_cpuid, set_cpuid (with the same values), and then get_cpuid again, and it would be able to infer by checking the SEV LM feature flag in the KVM leaf. This seems a bit kludgy. Checking support should be easy.

An additional question is "how does userspace choose whether live migration is advertised to the guest"? I believe userspace's desire for a particular value of the paravirt feature flag in CPUID get's overridden when they call set cpuid, since the feature flag is set in svm_vcpu_after_set_cpuid regardless of what userspace asks for. Userspace should have a choice in the matter.

Quoted 3 lines

Actually i did some more analysis of this, and i believe you are right about the above, feature flag gets set in svm_vcpu_after_set_cpuid.

As you mentioned above and as i confirmed in my previous email, calling KVM_SET_CPUID2 vcpu ioctl will always set the live migration feature flag for the vCPU.

Quoted 5 lines

This is what will be queried by the guest to enable the kernel's live migration feature and to start making hypercalls.

Now, i want to understand why do you want the userspace to have a choice in this matter ?

Kernel rollout risk is a pretty big factor:
1) Feature flagging is a pretty common risk mitigation for new features.
2) Without userspace being able to intervene, the kernel rollout becomes a feature rollout.

IIUC, as soon as new VMs started running on this host kernel, they would immediately start calling the hypercall if they had the guest patches, even if they did not do so on older versions of the host kernel.

Quoted 4 lines

After all, it is the userspace which actually initiates the live migration process, so doesn't it have the final choice in this matter ?

With the current implementation, userspace has the final say in the migration, but not the final say in whether or not that particular hypercall is used by the guest. If a customer showed up, and said "don't have my guest migrate", there is no way for the host to tell the guest "hey, we're not even listening to what you're sending over the hypercall". IIRC, there is an SEV Policy bit for migration enablement, but even if it were set to false, that guest would still update the host about its unencrypted regions.

Right now, the host can't even remove the feature bit from CPUID (since its desire would be overridden post-set), so it doesn't have the ability to tell the guest to hang up the phone. And even if we could tell the guest through CPUID, if the guest ignored what we told it, it could still send data down anyway! If there were a bug in this implementation that we missed, the only way to avoid it would be to roll out a new kernel, which is pretty heavy handed. If you could just disable the feature (or never enable it in the first place), that would be much less costly.

Quoted 11 lines

Even if this feature is reported by host, the guest only uses it to enable and make page encryption status hypercalls and the host's shared pages list gets setup accordingly.

But unless userspace calls KVM_GET_SHARED_PAGES_LIST ioctl and initiates live migration process, the above is simply enabling the guest to make hypercalls whenever a page's encryption status is changed.

Thanks,
Ashish

Thanks,
Steve

#62 lore raw

Re: [PATCH v10 12/16] KVM: x86: Introduce new KVM_FEATURE_SEV_LIVE_MIGRATION feature & Custom MSR.

Hello Steve,

Quoted 275 lines · Steve Rutherford

Hi Ashish,

Quoted 244 lines · Ashish Kalra

Hello Steve,

Quoted 232 lines · Ashish Kalra

Hello Steve,

Quoted 228 lines · Ashish Kalra

Hello Steve,

Continued response to your queries, especially related to userspace control of SEV live migration feature :

Quoted 222 lines · Steve Rutherford
Quoted 116 lines · Ashish Kalra

Hello Steve,

Quoted 112 lines · Steve Rutherford
Quoted 86 lines · Ashish Kalra

From: Ashish Kalra <[email protected]>

Add new KVM_FEATURE_SEV_LIVE_MIGRATION feature for guest to check for host-side support for SEV live migration. Also add a new custom MSR_KVM_SEV_LIVE_MIGRATION for guest to enable the SEV live migration feature.

---
 Documentation/virt/kvm/cpuid.rst     |  5 +++++
 Documentation/virt/kvm/msr.rst       | 12 ++++++++++++
 arch/x86/include/uapi/asm/kvm_para.h |  4 ++++
 arch/x86/kvm/svm/sev.c               | 13 +++++++++++++
 arch/x86/kvm/svm/svm.c               | 16 ++++++++++++++++
 arch/x86/kvm/svm/svm.h               |  2 ++
 6 files changed, 52 insertions(+)
Documentation/virt/kvm/cpuid.rst +5 -0
index cf62162d4be2..0bdb6cdb12d3 100644
@@ -96,6 +96,11 @@ KVM_FEATURE_MSI_EXT_DEST_ID        15          guest checks this feature bit
                                                before using extended destination
                                                ID bits in MSI address bits 11-5.
 
+KVM_FEATURE_SEV_LIVE_MIGRATION     16          guest checks this feature bit before
+                                               using the page encryption state
+                                               hypercall to notify the page state
+                                               change
+
 KVM_FEATURE_CLOCKSOURCE_STABLE_BIT 24          host will warn if no guest-side
                                                per-cpu warps are expected in
                                                kvmclock
Documentation/virt/kvm/msr.rst +12 -0
index e37a14c323d2..020245d16087 100644
@@ -376,3 +376,15 @@ data:
        write '1' to bit 0 of the MSR, this causes the host to re-scan its queue
        and check if there are more notifications pending. The MSR is available
        if KVM_FEATURE_ASYNC_PF_INT is present in CPUID.
+
+MSR_KVM_SEV_LIVE_MIGRATION:
+        0x4b564d08
+
+       Control SEV Live Migration features.
+
+data:
+        Bit 0 enables (1) or disables (0) host-side SEV Live Migration feature,
+        in other words, this is guest->host communication that it's properly
+        handling the shared pages list.
+
+        All other bits are reserved.
arch/x86/include/uapi/asm/kvm_para.h +4 -0
index 950afebfba88..f6bfa138874f 100644
@@ -33,6 +33,7 @@
 #define KVM_FEATURE_PV_SCHED_YIELD     13
 #define KVM_FEATURE_ASYNC_PF_INT       14
 #define KVM_FEATURE_MSI_EXT_DEST_ID    15
+#define KVM_FEATURE_SEV_LIVE_MIGRATION 16
 
 #define KVM_HINTS_REALTIME      0
 
@@ -54,6 +55,7 @@
 #define MSR_KVM_POLL_CONTROL   0x4b564d05
 #define MSR_KVM_ASYNC_PF_INT   0x4b564d06
 #define MSR_KVM_ASYNC_PF_ACK   0x4b564d07
+#define MSR_KVM_SEV_LIVE_MIGRATION     0x4b564d08
 
 struct kvm_steal_time {
        __u64 steal;
@@ -136,4 +138,6 @@ struct kvm_vcpu_pv_apf_data {
 #define KVM_PV_EOI_ENABLED KVM_PV_EOI_MASK
 #define KVM_PV_EOI_DISABLED 0x0
 
+#define KVM_SEV_LIVE_MIGRATION_ENABLED BIT_ULL(0)
+
 #endif /* _UAPI_ASM_X86_KVM_PARA_H */
arch/x86/kvm/svm/sev.c +0 -0
index b0d324aed515..93f42b3d3e33 100644

arch/x86/kvm/svm/sev.c int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa, · line 1635

Quoted 9 lines
arch/x86/kvm/svm/sev.c +6 -0
@@ -1627,6 +1627,16 @@ int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa,
        return ret;
 }
 
+void sev_update_migration_flags(struct kvm *kvm, u64 data)
+{
+       struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
+
+       if (!sev_guest(kvm))
+               return;

This should assert that userspace wanted the guest to be able to make these calls (see more below).

Quoted 12 lines
arch/x86/kvm/svm/sev.c +4 -0
+
+       sev->live_migration_enabled = !!(data & KVM_SEV_LIVE_MIGRATION_ENABLED);
+}
+
 int svm_get_shared_pages_list(struct kvm *kvm,
                              struct kvm_shared_pages_list *list)
 {
@@ -1639,6 +1649,9 @@ int svm_get_shared_pages_list(struct kvm *kvm,
        if (!sev_guest(kvm))
                return -ENOTTY;
 
Quoted 2 lines
Quoted 2 lines

arch/x86/kvm/svm/sev.c int svm_get_shared_pages_list(struct kvm *kvm, · line 1653

Quoted 2 lines
arch/x86/kvm/svm/sev.c +2 -0
+       if (!sev->live_migration_enabled)
+               return -EINVAL;

This is currently under guest control, so I'm not certain this is helpful. If I called this with otherwise valid parameters, and got back -EINVAL, I would probably think the bug is on my end. But it could be on the guest's end! I would probably drop this, but you could have KVM return an empty list of regions when this happens.

Alternatively, as explained below, this could call guest_pv_has instead.

Quoted 56 lines
Quoted 47 lines
Quoted 26 lines
arch/x86/kvm/svm/sev.c +1 -0
+
        if (!list->size)
                return -EINVAL;
 
arch/x86/kvm/svm/svm.c +6 -0
index 58f89f83caab..43ea5061926f 100644
@@ -2903,6 +2903,9 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr)
                svm->msr_decfg = data;
                break;
        }
+       case MSR_KVM_SEV_LIVE_MIGRATION:
+               sev_update_migration_flags(vcpu->kvm, data);
+               break;
        case MSR_IA32_APICBASE:
                if (kvm_vcpu_apicv_active(vcpu))
                        avic_update_vapic_bar(to_svm(vcpu), data);
@@ -3976,6 +3979,19 @@ static void svm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu)
                        vcpu->arch.cr3_lm_rsvd_bits &= ~(1UL << (best->ebx & 0x3f));
        }
 
+       /*
+        * If SEV guest then enable the Live migration feature.
+        */

arch/x86/kvm/svm/svm.c static void svm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) · line 3994

Quoted 10 lines
arch/x86/kvm/svm/svm.c +10 -0
+       if (sev_guest(vcpu->kvm)) {
+               struct kvm_cpuid_entry2 *best;
+
+               best = kvm_find_cpuid_entry(vcpu, KVM_CPUID_FEATURES, 0);
+               if (!best)
+                       return;
+
+               best->eax |= (1 << KVM_FEATURE_SEV_LIVE_MIGRATION);
+       }
+

Looking at this, I believe the only way for this bit to get enabled is if userspace toggles it. There needs to be a way for userspace to identify if the kernel underneath them does, in fact, support SEV LM. I'm at risk for having misread these patches (it's a long series), but I don't see anything that communicates upwards.

This could go upward with the other paravirt features flags in cpuid.c. It could also be an explicit KVM Capability (checked through check_extension).

Quoted 8 lines

Userspace should then have a chance to decide whether or not this should be enabled. And when it's not enabled, the host should return a GP in response to the hypercall. This could be configured either through userspace stripping out the LM feature bit, or by calling a VM scoped enable cap (KVM_VM_IOCTL_ENABLE_CAP).

I believe the typical path for a feature like this to be configured would be to use ENABLE_CAP.

Quoted 7 lines

I believe we have discussed and reviewed this earlier too.

To summarize this feature, the host indicates if it supports the Live Migration feature and the feature and the hypercall are only enabled on the host when the guest checks for this support and does a wrmsrl() to enable the feature. Also the guest will not make the hypercall if the host does not indicate support for it.

I've gone through and read this patch a bit more closely, and the surrounding code. Previously, I clearly misread this and the surrounding space.

What happens if the guest just writes to the MSR anyway? Even if it didn't receive a cue to do so? I believe the hypercall would still get invoked here, since the hypercall does not check if SEV live migration is enabled. Similarly, the MSR for enabling it is always available, even if userspace didn't ask for the cpuid bit to be set. This should not happen. Userspace should be in control of a new hypercall rolling out.

I believe my interpretation last time was that the cpuid bit was getting surfaced from the host kernel to host userspace, but I don't actually see that in this patch series. Another way to ask this question would be "How does userspace know the kernel they are on has this patch series?". It needs some way of checking whether or not the kernel underneath it supports SEV live migration. Technically, I think userspace could call get_cpuid, set_cpuid (with the same values), and then get_cpuid again, and it would be able to infer by checking the SEV LM feature flag in the KVM leaf. This seems a bit kludgy. Checking support should be easy.

An additional question is "how does userspace choose whether live migration is advertised to the guest"? I believe userspace's desire for a particular value of the paravirt feature flag in CPUID get's overridden when they call set cpuid, since the feature flag is set in svm_vcpu_after_set_cpuid regardless of what userspace asks for. Userspace should have a choice in the matter.

Quoted 3 lines

Actually i did some more analysis of this, and i believe you are right about the above, feature flag gets set in svm_vcpu_after_set_cpuid.

As you mentioned above and as i confirmed in my previous email, calling KVM_SET_CPUID2 vcpu ioctl will always set the live migration feature flag for the vCPU.

Quoted 5 lines

This is what will be queried by the guest to enable the kernel's live migration feature and to start making hypercalls.

Now, i want to understand why do you want the userspace to have a choice in this matter ?

Kernel rollout risk is a pretty big factor:
1) Feature flagging is a pretty common risk mitigation for new features.
2) Without userspace being able to intervene, the kernel rollout becomes a feature rollout.

IIUC, as soon as new VMs started running on this host kernel, they would immediately start calling the hypercall if they had the guest patches, even if they did not do so on older versions of the host kernel.

Quoted 4 lines

After all, it is the userspace which actually initiates the live migration process, so doesn't it have the final choice in this matter ?

With the current implementation, userspace has the final say in the migration, but not the final say in whether or not that particular hypercall is used by the guest. If a customer showed up, and said "don't have my guest migrate", there is no way for the host to tell the guest "hey, we're not even listening to what you're sending over the hypercall". IIRC, there is an SEV Policy bit for migration enablement, but even if it were set to false, that guest would still update the host about its unencrypted regions.

Quoted 10 lines

Right now, the host can't even remove the feature bit from CPUID (since its desire would be overridden post-set), so it doesn't have the ability to tell the guest to hang up the phone. And even if we could tell the guest through CPUID, if the guest ignored what we told it, it could still send data down anyway! If there were a bug in this implementation that we missed, the only way to avoid it would be to roll out a new kernel, which is pretty heavy handed. If you could just disable the feature (or never enable it in the first place), that would be much less costly.

We can remove the implicit enabling of this live migration feature from svm_vcpu_after_set_cpuid() callback invoked afer KVM_SET_CPUID2 ioctl, and let this feature flag be controlled by the userspace VMM/qemu.

Userspace can set this feature flag explicitly by calling the KVM_SET_CPUID2 ioctl and enable this feature whenever it is ready to do so.

I have tested this as part of Qemu code :

int kvm_arch_init_vcpu(CPUState *cs)
{
...
...

        c->function = KVM_CPUID_FEATURES | kvm_base;
        c->eax = env->features[FEAT_KVM];
        c->eax |= (1 << KVM_FEATURE_SEV_LIVE_MIGRATION);

...
...

    r = kvm_vcpu_ioctl(cs, KVM_SET_CPUID2, &cpuid_data);

...

Let me know if this addresses your concerns.

Thanks,
Ashish

#63 lore raw

Re: [PATCH v10 12/16] KVM: x86: Introduce new KVM_FEATURE_SEV_LIVE_MIGRATION feature & Custom MSR.

Hi Ashish,

Quoted 21 lines · Ashish Kalra

Hello Steve,

We can remove the implicit enabling of this live migration feature from svm_vcpu_after_set_cpuid() callback invoked afer KVM_SET_CPUID2 ioctl, and let this feature flag be controlled by the userspace VMM/qemu.

Userspace can set this feature flag explicitly by calling the KVM_SET_CPUID2 ioctl and enable this feature whenever it is ready to do so.

I have tested this as part of Qemu code :

int kvm_arch_init_vcpu(CPUState *cs)
{
...
...

        c->function = KVM_CPUID_FEATURES | kvm_base;
        c->eax = env->features[FEAT_KVM];
        c->eax |= (1 << KVM_FEATURE_SEV_LIVE_MIGRATION);
Quoted 7 lines

...
...

    r = kvm_vcpu_ioctl(cs, KVM_SET_CPUID2, &cpuid_data);

...

Let me know if this addresses your concerns.

Removing implicit enablement is one part of the equation.
The other two are:
1) Host userspace being able to ask the kernel if it supports SEV Live Migration
2) Host userspace being able to disable access to the MSR/hypercall

Feature flagging for paravirt features is pretty complicated, since you need all three parties to negotiate (host userspace/host kernel/guest), and every single one has veto power. In the end, the feature should only be available to the guest if every single party says yes.

For an example of how to handle 1), the new feature flag could be checked when asking the kernel which cpuid bits it supports by adding it to the list of features that the kernel mentions in KVM_GET_SUPPORTED_CPUID.

For example (in KVM's arch/x86/kvm/cpuid.c):
case KVM_CPUID_FEATURES:
==========
entry->eax = (1 << KVM_FEATURE_CLOCKSOURCE) |

    (1 << KVM_FEATURE_NOP_IO_DELAY) |
...
    (1 << KVM_FEATURE_PV_SCHED_YIELD) |

+ (1 << KVM_FEATURE_ASYNC_PF_INT) |
- (1 << KVM_FEATURE_ASYNC_PF_INT);
+ (1 << KVM_FEATURE_SEV_LIVE_MIGRATION);
==========

Without this, userspace has to infer if the kernel it is on supports that flag.

For an example of how to handle 2), in the new msr handler, KVM should throw a GP if (!guest_pv_has(vcpu, KVM_FEATURE_SEV_LIVE_MIGRATION)) (it can do this by returning th. The issue here is "what if the guest ignores CPUID and calls the MSR/hypercall anyway". This is a less important issue as it requires the guest to be malicious, but still worth resolving. Additionally, the hypercall itself should check if the MSR has been toggled by the guest.

Thanks,
Steve

#64 lore raw

Re: [PATCH v10 12/16] KVM: x86: Introduce new KVM_FEATURE_SEV_LIVE_MIGRATION feature & Custom MSR.

Quoted 60 lines · Steve Rutherford

Hi Ashish,

Quoted 21 lines · Ashish Kalra

Hello Steve,

We can remove the implicit enabling of this live migration feature from svm_vcpu_after_set_cpuid() callback invoked afer KVM_SET_CPUID2 ioctl, and let this feature flag be controlled by the userspace VMM/qemu.

Userspace can set this feature flag explicitly by calling the KVM_SET_CPUID2 ioctl and enable this feature whenever it is ready to do so.

I have tested this as part of Qemu code :

int kvm_arch_init_vcpu(CPUState *cs)
{
...
...

        c->function = KVM_CPUID_FEATURES | kvm_base;
        c->eax = env->features[FEAT_KVM];
        c->eax |= (1 << KVM_FEATURE_SEV_LIVE_MIGRATION);
Quoted 7 lines

...
...

    r = kvm_vcpu_ioctl(cs, KVM_SET_CPUID2, &cpuid_data);

...

Let me know if this addresses your concerns.

Removing implicit enablement is one part of the equation.
The other two are:
1) Host userspace being able to ask the kernel if it supports SEV Live Migration
2) Host userspace being able to disable access to the MSR/hypercall

Feature flagging for paravirt features is pretty complicated, since you need all three parties to negotiate (host userspace/host kernel/guest), and every single one has veto power. In the end, the feature should only be available to the guest if every single party says yes.

For an example of how to handle 1), the new feature flag could be checked when asking the kernel which cpuid bits it supports by adding it to the list of features that the kernel mentions in KVM_GET_SUPPORTED_CPUID.

For example (in KVM's arch/x86/kvm/cpuid.c):
case KVM_CPUID_FEATURES:
==========
entry->eax = (1 << KVM_FEATURE_CLOCKSOURCE) |

    (1 << KVM_FEATURE_NOP_IO_DELAY) |
...
    (1 << KVM_FEATURE_PV_SCHED_YIELD) |
(quoted diff) +2 -1
+  (1 << KVM_FEATURE_ASYNC_PF_INT) |
-   (1 << KVM_FEATURE_ASYNC_PF_INT);
+  (1 << KVM_FEATURE_SEV_LIVE_MIGRATION);

==========

Quoted 5 lines

Without this, userspace has to infer if the kernel it is on supports that flag.

For an example of how to handle 2), in the new msr handler, KVM should throw a GP if (!guest_pv_has(vcpu, KVM_FEATURE_SEV_LIVE_MIGRATION)) (it can do this by returning th. The issue here is "what if the guest

Correction: (it can do this by returning 1).

#65 lore raw

Re: [PATCH v10 12/16] KVM: x86: Introduce new KVM_FEATURE_SEV_LIVE_MIGRATION feature & Custom MSR.

In reply to Ashish Kalra
Quoted 32 lines · Ashish Kalra
arch/x86/include/uapi/asm/kvm_para.h +4 -0
index 950afebfba88..f6bfa138874f 100644
@@ -33,6 +33,7 @@
 #define KVM_FEATURE_PV_SCHED_YIELD	13
 #define KVM_FEATURE_ASYNC_PF_INT	14
 #define KVM_FEATURE_MSI_EXT_DEST_ID	15
+#define KVM_FEATURE_SEV_LIVE_MIGRATION	16
 
 #define KVM_HINTS_REALTIME      0
 
@@ -54,6 +55,7 @@
 #define MSR_KVM_POLL_CONTROL	0x4b564d05
 #define MSR_KVM_ASYNC_PF_INT	0x4b564d06
 #define MSR_KVM_ASYNC_PF_ACK	0x4b564d07
+#define MSR_KVM_SEV_LIVE_MIGRATION	0x4b564d08
 
 struct kvm_steal_time {
 	__u64 steal;
@@ -136,4 +138,6 @@ struct kvm_vcpu_pv_apf_data {
 #define KVM_PV_EOI_ENABLED KVM_PV_EOI_MASK
 #define KVM_PV_EOI_DISABLED 0x0
 
+#define KVM_SEV_LIVE_MIGRATION_ENABLED BIT_ULL(0)
+
 #endif /* _UAPI_ASM_X86_KVM_PARA_H */
arch/x86/kvm/svm/sev.c +0 -0
index b0d324aed515..93f42b3d3e33 100644

arch/x86/kvm/svm/sev.c int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa, · line 1631

Quoted 5 lines
arch/x86/kvm/svm/sev.c +2 -0
@@ -1627,6 +1627,16 @@ int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa,
 	return ret;
 }
 
+void sev_update_migration_flags(struct kvm *kvm, u64 data)
+{

I don't see the point for a helper. It's actually going to make the code less readable once proper error handling is added. Given that it's not static and exposed via svm.h, without an external user, I assume this got left behind when the implicit enabling was removed.

arch/x86/kvm/svm/sev.c int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa, · line 1634

Quoted 3 lines
arch/x86/kvm/svm/sev.c +3 -0
+	struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
+
+	if (!sev_guest(kvm))

I 100% agree with Steve, this needs to check guest_cpuid_has() in addition to sev_guest(). And it should return '1', i.e. signal #GP to the guest, not silently eat the bad WRMSR.

arch/x86/kvm/svm/sev.c int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa, · line 1637

Quoted 3 lines
arch/x86/kvm/svm/sev.c +3 -0
+		return;
+
+	sev->live_migration_enabled = !!(data & KVM_SEV_LIVE_MIGRATION_ENABLED);

The value needs to be checked as well, i.e. all bits except LIVE_MIGRATION... should to be reserved to zero.

arch/x86/kvm/svm/sev.c int svm_get_shared_pages_list(struct kvm *kvm, · line 1653

Quoted 11 lines
arch/x86/kvm/svm/sev.c +4 -0
+}
+
 int svm_get_shared_pages_list(struct kvm *kvm,
 			      struct kvm_shared_pages_list *list)
 {
@@ -1639,6 +1649,9 @@ int svm_get_shared_pages_list(struct kvm *kvm,
 	if (!sev_guest(kvm))
 		return -ENOTTY;
 
+	if (!sev->live_migration_enabled)
+		return -EINVAL;

EINVAL is a weird return value for something that is controlled by the guest, especially since it's possible for the guest to support migration, just not yet. EBUSY maybe? EOPNOTSUPP?

Quoted 9 lines
arch/x86/kvm/svm/sev.c +1 -0
+
 	if (!list->size)
 		return -EINVAL;
 
arch/x86/kvm/svm/svm.c +0 -0
index 58f89f83caab..43ea5061926f 100644

arch/x86/kvm/svm/svm.c static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr) · line 2908

Quoted 6 lines
arch/x86/kvm/svm/svm.c +3 -0
@@ -2903,6 +2903,9 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr)
 		svm->msr_decfg = data;
 		break;
 	}
+	case MSR_KVM_SEV_LIVE_MIGRATION:
+		sev_update_migration_flags(vcpu->kvm, data);
+		break;

There shuld be a svm_get_msr() entry as well, I don't see any reason to prevent the guest from reading the MSR.

Quoted 8 lines
arch/x86/kvm/svm/svm.c +1 -0
 	case MSR_IA32_APICBASE:
 		if (kvm_vcpu_apicv_active(vcpu))
 			avic_update_vapic_bar(to_svm(vcpu), data);
@@ -3976,6 +3979,19 @@ static void svm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu)
 			vcpu->arch.cr3_lm_rsvd_bits &= ~(1UL << (best->ebx & 0x3f));
 	}
 
+	/*

arch/x86/kvm/svm/svm.c static void svm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) · line 3992

Quoted 10 lines
arch/x86/kvm/svm/svm.c +10 -0
+	 * If SEV guest then enable the Live migration feature.
+	 */
+	if (sev_guest(vcpu->kvm)) {
+		struct kvm_cpuid_entry2 *best;
+
+		best = kvm_find_cpuid_entry(vcpu, KVM_CPUID_FEATURES, 0);
+		if (!best)
+			return;
+
+		best->eax |= (1 << KVM_FEATURE_SEV_LIVE_MIGRATION);

Again echoing Steve's concern, userspace is the ultimate authority on what features are exposed to the VM. I don't see any motivation for forcing live migration to be enabled.

And as I believe was pointed out elsewhere, this bit needs to be advertised to userspace via kvm_cpu_caps.

Quoted 28 lines
arch/x86/kvm/svm/svm.c +2 -0
+	}
+
 	if (!kvm_vcpu_apicv_active(vcpu))
 		return;
 
arch/x86/kvm/svm/svm.h +2 -0
index 066ca2a9f1e6..e1bffc11e425 100644
@@ -79,6 +79,7 @@ struct kvm_sev_info {
 	unsigned long pages_locked; /* Number of pages locked */
 	struct list_head regions_list;  /* List of registered regions */
 	u64 ap_jump_table;	/* SEV-ES AP Jump Table address */
+	bool live_migration_enabled;
 	/* List and count of shared pages */
 	int shared_pages_list_count;
 	struct list_head shared_pages_list;
@@ -592,6 +593,7 @@ int svm_unregister_enc_region(struct kvm *kvm,
 void pre_sev_run(struct vcpu_svm *svm, int cpu);
 void __init sev_hardware_setup(void);
 void sev_hardware_teardown(void);
+void sev_update_migration_flags(struct kvm *kvm, u64 data);
 void sev_free_vcpu(struct kvm_vcpu *vcpu);
 int sev_handle_vmgexit(struct vcpu_svm *svm);
 int sev_es_string_io(struct vcpu_svm *svm, int size, unsigned int port, int in);
Signature · 1 line
2.17.1
#66 lore raw

PATCH v10 13/16 EFI: Introduce the new AMD Memory Encryption GUID.

In reply to Ashish Kalra

From: Ashish Kalra <[email protected]>

Introduce a new AMD Memory Encryption GUID which is currently used for defining a new UEFI environment variable which indicates UEFI/OVMF support for the SEV live migration feature. This variable is setup when UEFI/OVMF detects host/hypervisor support for SEV live migration and later this variable is read by the kernel using EFI runtime services to verify if OVMF supports the live migration feature.

---
 include/linux/efi.h | 1 +
 1 file changed, 1 insertion(+)
include/linux/efi.h +1 -0
index 763b816ba19c..ae47db882bee 100644
@@ -362,6 +362,7 @@ void efi_native_runtime_setup(void);
 
 /* OEM GUIDs */
 #define DELLEMC_EFI_RCI2_TABLE_GUID		EFI_GUID(0x2d9f28a2, 0xa886, 0x456a,  0x97, 0xa8, 0xf1, 0x1e, 0xf2, 0x4f, 0xf4, 0x55)
+#define MEM_ENCRYPT_GUID			EFI_GUID(0x0cf29b71, 0x9e51, 0x433a,  0xa3, 0xb7, 0x81, 0xf3, 0xab, 0x16, 0xb8, 0x75)
 
 typedef struct {
 	efi_guid_t guid;
Signature · 1 line
2.17.1
#67 lore raw

PATCH v10 14/16 KVM: x86: Add guest support for detecting and enabling SEV Live Migration feature.

In reply to Ashish Kalra

From: Ashish Kalra <[email protected]>

The guest support for detecting and enabling SEV Live migration feature uses the following logic :

- kvm_init_plaform() invokes check_kvm_sev_migration() which checks if its booted under the EFI

- If not EFI,

i) check for the KVM_FEATURE_CPUID

ii) if CPUID reports that migration is supported, issue a wrmsrl()
to enable the SEV live migration support

- If EFI,

i) check for the KVM_FEATURE_CPUID

ii) If CPUID reports that migration is supported, read the UEFI variable which
indicates OVMF support for live migration

iii) the variable indicates live migration is supported, issue a wrmsrl() to
enable the SEV live migration support

The EFI live migration check is done using a late_initcall() callback.

Also, ensure that _bss_decrypted section is marked as decrypted in the shared pages list.

---
 arch/x86/include/asm/mem_encrypt.h |  8 +++++
 arch/x86/kernel/kvm.c              | 52 ++++++++++++++++++++++++++++++
 arch/x86/mm/mem_encrypt.c          | 41 +++++++++++++++++++++++
 3 files changed, 101 insertions(+)
arch/x86/include/asm/mem_encrypt.h +8 -0
index 31c4df123aa0..19b77f3a62dc 100644
@@ -21,6 +21,7 @@
 extern u64 sme_me_mask;
 extern u64 sev_status;
 extern bool sev_enabled;
+extern bool sev_live_migration_enabled;
 
 void sme_encrypt_execute(unsigned long encrypted_kernel_vaddr,
 			 unsigned long decrypted_kernel_vaddr,
@@ -44,8 +45,11 @@ void __init sme_enable(struct boot_params *bp);
 
 int __init early_set_memory_decrypted(unsigned long vaddr, unsigned long size);
 int __init early_set_memory_encrypted(unsigned long vaddr, unsigned long size);
+void __init early_set_mem_enc_dec_hypercall(unsigned long vaddr, int npages,
+					    bool enc);
 
 void __init mem_encrypt_free_decrypted_mem(void);
+void __init check_kvm_sev_migration(void);
 
 /* Architecture __weak replacement functions */
 void __init mem_encrypt_init(void);
@@ -60,6 +64,7 @@ bool sev_es_active(void);
 #else	/* !CONFIG_AMD_MEM_ENCRYPT */
 
 #define sme_me_mask	0ULL
+#define sev_live_migration_enabled	false
 
 static inline void __init sme_early_encrypt(resource_size_t paddr,
 					    unsigned long size) { }
@@ -84,8 +89,11 @@ static inline int __init
 early_set_memory_decrypted(unsigned long vaddr, unsigned long size) { return 0; }
 static inline int __init
 early_set_memory_encrypted(unsigned long vaddr, unsigned long size) { return 0; }
+static inline void __init
+early_set_mem_enc_dec_hypercall(unsigned long vaddr, int npages, bool enc) {}
 
 static inline void mem_encrypt_free_decrypted_mem(void) { }
+static inline void check_kvm_sev_migration(void) { }
 
 #define __bss_decrypted
 
arch/x86/kernel/kvm.c +52 -0

70 lines not on this page · raw message

arch/x86/mm/mem_encrypt.c +41 -0

71 lines not on this page · raw message

Signature · 1 line
2.17.1
#68 lore raw

Re: [PATCH v10 14/16] KVM: x86: Add guest support for detecting and enabling SEV Live Migration feature.

Quoted 1 line · Ashish Kalra

From: Ashish Kalra <[email protected]>

...

Quoted 4 lines
 arch/x86/include/asm/mem_encrypt.h |  8 +++++
 arch/x86/kernel/kvm.c              | 52 ++++++++++++++++++++++++++++++
 arch/x86/mm/mem_encrypt.c          | 41 +++++++++++++++++++++++
 3 files changed, 101 insertions(+)

Please use "x86/kvm:" in the shortlog for guest side changes so that reviewers can easily differentiate between host and guest patches. Past changes haven't exactly been consistent for guest changes, but "x86/kvm:" is the most popular at a glance, and IMO aligns best with other kernel (non-KVM) changes.

#69 lore raw

PATCH v10 15/16 KVM: x86: Add kexec support for SEV Live Migration.

In reply to Ashish Kalra

From: Ashish Kalra <[email protected]>

Reset the host's shared pages list related to kernel specific page encryption status settings before we load a new kernel by kexec. We cannot reset the complete shared pages list here as we need to retain the UEFI/OVMF firmware specific settings.

The host's shared pages list is maintained for the guest to keep track of all unencrypted guest memory regions, therefore we need to explicitly mark all shared pages as encrypted again before rebooting into the new guest kernel.

---
 arch/x86/kernel/kvm.c | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)
arch/x86/kernel/kvm.c +28 -0
index c4b8029c1442..d61156db7797 100644
@@ -39,6 +39,7 @@
 #include <asm/cpuidle_haltpoll.h>
 #include <asm/ptrace.h>
 #include <asm/svm.h>
+#include <asm/e820/api.h>
 
 DEFINE_STATIC_KEY_FALSE(kvm_async_pf_enabled);
 
@@ -384,6 +385,33 @@ static void kvm_pv_guest_cpu_reboot(void *unused)
 	 */
 	if (kvm_para_has_feature(KVM_FEATURE_PV_EOI))
 		wrmsrl(MSR_KVM_PV_EOI_EN, 0);
+	/*
+	 * Reset the host's shared pages list related to kernel
+	 * specific page encryption status settings before we load a
+	 * new kernel by kexec. NOTE: We cannot reset the complete
+	 * shared pages list here as we need to retain the
+	 * UEFI/OVMF firmware specific settings.
+	 */
+	if (sev_live_migration_enabled & (smp_processor_id() == 0)) {
+		int i;
+		unsigned long nr_pages;
+
+		for (i = 0; i < e820_table->nr_entries; i++) {
+			struct e820_entry *entry = &e820_table->entries[i];
+			unsigned long start_pfn;
+			unsigned long end_pfn;
+
+			if (entry->type != E820_TYPE_RAM)
+				continue;
+
+			start_pfn = entry->addr >> PAGE_SHIFT;
+			end_pfn = (entry->addr + entry->size) >> PAGE_SHIFT;
+			nr_pages = DIV_ROUND_UP(entry->size, PAGE_SIZE);
+
+			kvm_sev_hypercall3(KVM_HC_PAGE_ENC_STATUS,
+					   entry->addr, nr_pages, 1);
+		}
+	}
 	kvm_pv_disable_apf();
 	kvm_disable_steal_time();
 }
Signature · 1 line
2.17.1
#70 lore raw

Re: [PATCH v10 15/16] KVM: x86: Add kexec support for SEV Live Migration.

Thank you for the patch! Perhaps something to improve:

Hi Ashish,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on v5.11-rc6]
[also build test WARNING on next-20210125]
[cannot apply to kvm/linux-next tip/x86/mm tip/x86/core]
[If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Ashish-Kalra/KVM-SVM-Add-KVM_SEV-SEND_START-command/20210204-093647
base:    1048ba83fb1c00cd24172e23e8263972f6b5d9ac
config: i386-randconfig-r034-20210202 (attached as .config)
compiler: gcc-9 (Debian 9.3.0-15) 9.3.0
reproduce (this is a W=1 build):
        # https://github.com/0day-ci/linux/commit/51c7205a5d0abf98f52da67fcf7a223c521f9693
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Ashish-Kalra/KVM-SVM-Add-KVM_SEV-SEND_START-command/20210204-093647
        git checkout 51c7205a5d0abf98f52da67fcf7a223c521f9693
        # save the attached .config to linux build tree
        make W=1 ARCH=i386 

If you fix the issue, kindly add following tag as appropriate

All warnings (new ones prefixed by >>):

arch/x86/kernel/kvm.c: In function 'kvm_pv_guest_cpu_reboot':

>> arch/x86/kernel/kvm.c:402:18: warning: variable 'end_pfn' set but not used [-Wunused-but-set-variable]
     402 |    unsigned long end_pfn;
         |                  ^~~~~~~
>> arch/x86/kernel/kvm.c:401:18: warning: variable 'start_pfn' set but not used [-Wunused-but-set-variable]
     401 |    unsigned long start_pfn;
         |                  ^~~~~~~~~

vim +/end_pfn +402 arch/x86/kernel/kvm.c

378
379 static void kvm_pv_guest_cpu_reboot(void *unused)
380 {
381 /*
382 * We disable PV EOI before we load a new kernel by kexec,
383 * since MSR_KVM_PV_EOI_EN stores a pointer into old kernel's memory.
384 * New kernel can re-enable when it boots.
385 */
386 if (kvm_para_has_feature(KVM_FEATURE_PV_EOI))
387 wrmsrl(MSR_KVM_PV_EOI_EN, 0);
388 /*
389 * Reset the host's shared pages list related to kernel
390 * specific page encryption status settings before we load a
391 * new kernel by kexec. NOTE: We cannot reset the complete
392 * shared pages list here as we need to retain the
393 * UEFI/OVMF firmware specific settings.
394 */
395 if (sev_live_migration_enabled & (smp_processor_id() == 0)) {
396 int i;
397 unsigned long nr_pages;
398
399 for (i = 0; i < e820_table->nr_entries; i++) {
400 struct e820_entry *entry = &e820_table->entries[i];

 > 401				unsigned long start_pfn;
 > 402				unsigned long end_pfn;

403
404 if (entry->type != E820_TYPE_RAM)
405 continue;
406
407 start_pfn = entry->addr >> PAGE_SHIFT;
408 end_pfn = (entry->addr + entry->size) >> PAGE_SHIFT;
409 nr_pages = DIV_ROUND_UP(entry->size, PAGE_SIZE);
410
411 kvm_sev_hypercall3(KVM_HC_PAGE_ENC_STATUS,
412 entry->addr, nr_pages, 1);
413 }
414 }
415 kvm_pv_disable_apf();
416 kvm_disable_steal_time();
417 }
418

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org

#71 lore raw

PATCH v10 16/16 KVM: SVM: Bypass DBG_DECRYPT API calls for unencrypted guest memory.

In reply to Ashish Kalra

From: Ashish Kalra <[email protected]>

For all unencrypted guest memory regions such as S/W IOTLB bounce buffers and for guest regions marked as "__bss_decrypted", ensure that DBG_DECRYPT API calls are bypassed.
The guest memory regions encryption status is referenced using the shared pages list.

---
 arch/x86/kvm/svm/sev.c | 126 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 126 insertions(+)
arch/x86/kvm/svm/sev.c +126 -0
index 93f42b3d3e33..fa3fbbb73b33 100644
@@ -888,6 +888,117 @@ static int __sev_dbg_encrypt_user(struct kvm *kvm, unsigned long paddr,
 	return ret;
 }
 
+static struct kvm_memory_slot *hva_to_memslot(struct kvm *kvm,
+					      unsigned long hva)
+{
+	struct kvm_memslots *slots = kvm_memslots(kvm);
+	struct kvm_memory_slot *memslot;
+
+	kvm_for_each_memslot(memslot, slots) {
+		if (hva >= memslot->userspace_addr &&
+		    hva < memslot->userspace_addr +
+			      (memslot->npages << PAGE_SHIFT))
+			return memslot;
+	}
+
+	return NULL;
+}
+
+static bool hva_to_gfn(struct kvm *kvm, unsigned long hva, gfn_t *gfn)
+{
+	struct kvm_memory_slot *memslot;
+	gpa_t gpa_offset;
+
+	memslot = hva_to_memslot(kvm, hva);
+	if (!memslot)
+		return false;
+
+	gpa_offset = hva - memslot->userspace_addr;
+	*gfn = ((memslot->base_gfn << PAGE_SHIFT) + gpa_offset) >> PAGE_SHIFT;
+
+	return true;
+}
+
+static bool is_unencrypted_region(gfn_t gfn_start, gfn_t gfn_end,
+				  struct list_head *head)
+{
+	struct shared_region *pos;
+
+	list_for_each_entry(pos, head, list)
+		if (gfn_start >= pos->gfn_start &&
+		    gfn_end <= pos->gfn_end)
+			return true;
+
+	return false;
+}
+
+static int handle_unencrypted_region(struct kvm *kvm,
+				     unsigned long vaddr,
+				     unsigned long vaddr_end,
+				     unsigned long dst_vaddr,
+				     unsigned int size,
+				     bool *is_decrypted)
+{
+	struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
+	struct page *page = NULL;
+	gfn_t gfn_start, gfn_end;
+	int len, s_off, d_off;
+	int srcu_idx;
+	int ret = 0;
+
+	/* ensure hva_to_gfn translations remain valid */
+	srcu_idx = srcu_read_lock(&kvm->srcu);
+
+	if (!hva_to_gfn(kvm, vaddr, &gfn_start)) {
+		srcu_read_unlock(&kvm->srcu, srcu_idx);
+		return -EINVAL;
+	}
+
+	if (!hva_to_gfn(kvm, vaddr_end, &gfn_end)) {
+		srcu_read_unlock(&kvm->srcu, srcu_idx);
+		return -EINVAL;
+	}
+
+	if (sev->shared_pages_list_count) {
+		if (is_unencrypted_region(gfn_start, gfn_end,
+					  &sev->shared_pages_list)) {
+			page = alloc_page(GFP_KERNEL);
+			if (!page) {
+				srcu_read_unlock(&kvm->srcu, srcu_idx);
+				return -ENOMEM;
+			}
+
+			/*
+			 * Since user buffer may not be page aligned, calculate the
+			 * offset within the page.
+			 */
+			s_off = vaddr & ~PAGE_MASK;
+			d_off = dst_vaddr & ~PAGE_MASK;
+			len = min_t(size_t, (PAGE_SIZE - s_off), size);
+
+			if (copy_from_user(page_address(page),
+					   (void __user *)(uintptr_t)vaddr, len)) {
+				__free_page(page);
+				srcu_read_unlock(&kvm->srcu, srcu_idx);
+				return -EFAULT;
+			}
+
+			if (copy_to_user((void __user *)(uintptr_t)dst_vaddr,
+					 page_address(page), len)) {
+				ret = -EFAULT;
+			}
+
+			__free_page(page);
+			srcu_read_unlock(&kvm->srcu, srcu_idx);
+			*is_decrypted = true;
+			return ret;
+		}
+	}
+	srcu_read_unlock(&kvm->srcu, srcu_idx);
+	*is_decrypted = false;
+	return ret;
+}
+
 static int sev_dbg_crypt(struct kvm *kvm, struct kvm_sev_cmd *argp, bool dec)
 {
 	unsigned long vaddr, vaddr_end, next_vaddr;
@@ -917,6 +1028,20 @@ static int sev_dbg_crypt(struct kvm *kvm, struct kvm_sev_cmd *argp, bool dec)
 	for (; vaddr < vaddr_end; vaddr = next_vaddr) {
 		int len, s_off, d_off;
 
+		if (dec) {
+			bool is_already_decrypted;
+
+			ret = handle_unencrypted_region(kvm,
+							vaddr,
+							vaddr_end,
+							dst_vaddr,
+							size,
+							&is_already_decrypted);
+
+			if (ret || is_already_decrypted)
+				goto already_decrypted;
+		}
+
 		/* lock userspace source and destination page */
 		src_p = sev_pin_memory(kvm, vaddr & PAGE_MASK, PAGE_SIZE, &n, 0);
 		if (IS_ERR(src_p))
@@ -961,6 +1086,7 @@ static int sev_dbg_crypt(struct kvm *kvm, struct kvm_sev_cmd *argp, bool dec)
 		sev_unpin_memory(kvm, src_p, n);
 		sev_unpin_memory(kvm, dst_p, n);
 
+already_decrypted:
 		if (ret)
 			goto err;
 
Signature · 1 line
2.17.1
#72 lore raw

Re: [PATCH v10 16/16] KVM: SVM: Bypass DBG_DECRYPT API calls for unencrypted guest memory.

Thank you for the patch! Perhaps something to improve:

Hi Ashish,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on v5.11-rc6]
[also build test WARNING on next-20210125]
[cannot apply to kvm/linux-next tip/x86/mm tip/x86/core]
[If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Ashish-Kalra/KVM-SVM-Add-KVM_SEV-SEND_START-command/20210204-093647
base:    1048ba83fb1c00cd24172e23e8263972f6b5d9ac
config: i386-randconfig-r015-20210209 (attached as .config)
compiler: gcc-9 (Debian 9.3.0-15) 9.3.0
reproduce (this is a W=1 build):
        # https://github.com/0day-ci/linux/commit/c086cd5491c6b84adcffedc1bf798df758b18080
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Ashish-Kalra/KVM-SVM-Add-KVM_SEV-SEND_START-command/20210204-093647
        git checkout c086cd5491c6b84adcffedc1bf798df758b18080
        # save the attached .config to linux build tree
        make W=1 ARCH=i386 

If you fix the issue, kindly add following tag as appropriate

All warnings (new ones prefixed by >>):

arch/x86/kvm/svm/sev.c: In function 'handle_unencrypted_region':

>> arch/x86/kvm/svm/sev.c:945:18: warning: variable 'd_off' set but not used [-Wunused-but-set-variable]
     945 |  int len, s_off, d_off;
         |                  ^~~~~

vim +/d_off +945 arch/x86/kvm/svm/sev.c

934
935 static int handle_unencrypted_region(struct kvm *kvm,

   936					     unsigned long vaddr,
   937					     unsigned long vaddr_end,
   938					     unsigned long dst_vaddr,
   939					     unsigned int size,
   940					     bool *is_decrypted)
   941	{
   942		struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
   943		struct page *page = NULL;
   944		gfn_t gfn_start, gfn_end;
 > 945		int len, s_off, d_off;

946 int srcu_idx;
947 int ret = 0;
948
949 /* ensure hva_to_gfn translations remain valid */
950 srcu_idx = srcu_read_lock(&kvm->srcu);
951
952 if (!hva_to_gfn(kvm, vaddr, &gfn_start)) {
953 srcu_read_unlock(&kvm->srcu, srcu_idx);
954 return -EINVAL;
955 }
956
957 if (!hva_to_gfn(kvm, vaddr_end, &gfn_end)) {
958 srcu_read_unlock(&kvm->srcu, srcu_idx);
959 return -EINVAL;
960 }
961
962 if (sev->shared_pages_list_count) {
963 if (is_unencrypted_region(gfn_start, gfn_end,
964 &sev->shared_pages_list)) {
965 page = alloc_page(GFP_KERNEL);
966 if (!page) {
967 srcu_read_unlock(&kvm->srcu, srcu_idx);
968 return -ENOMEM;
969 }
970
971 /*
972 * Since user buffer may not be page aligned, calculate the
973 * offset within the page.
974 */
975 s_off = vaddr & ~PAGE_MASK;
976 d_off = dst_vaddr & ~PAGE_MASK;
977 len = min_t(size_t, (PAGE_SIZE - s_off), size);
978
979 if (copy_from_user(page_address(page),
980 (void __user *)(uintptr_t)vaddr, len)) {
981 __free_page(page);
982 srcu_read_unlock(&kvm->srcu, srcu_idx);
983 return -EFAULT;
984 }
985
986 if (copy_to_user((void __user *)(uintptr_t)dst_vaddr,
987 page_address(page), len)) {
988 ret = -EFAULT;
989 }
990
991 __free_page(page);
992 srcu_read_unlock(&kvm->srcu, srcu_idx);
993 *is_decrypted = true;
994 return ret;
995 }
996 }
997 srcu_read_unlock(&kvm->srcu, srcu_idx);
998 *is_decrypted = false;
999 return ret;
1000 }
1001

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org