Skip to contentSkip to conversation

SEV Live Migration Patchset.

PATCH Superseded Ashish Kalra 7 participants 49 messages Feb 13, 2020 – Mar 19, 2020

Subsystems (7)
Documentation x86 KVM KVM x86 +3 more subsystems

v1 of 13 Superseded Latest is v13 →

lore
Share & tools
lore
#0 lore raw

cover SEV Live Migration Patchset.

From: Ashish Kalra <[email protected]>

This patchset adds support for SEV Live Migration on KVM/QEMU.

Ashish Kalra (1):
KVM: x86: Introduce KVM_PAGE_ENC_BITMAP_RESET ioctl

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
KVM: x86: Introduce KVM_GET_PAGE_ENC_BITMAP ioctl
mm: x86: Invoke hypercall when page encryption status is changed
KVM: x86: Introduce KVM_SET_PAGE_ENC_BITMAP ioctl

 .../virt/kvm/amd-memory-encryption.rst        | 120 ++++
 Documentation/virt/kvm/api.txt                |  59 ++
 Documentation/virt/kvm/hypercalls.txt         |  14 +
 arch/x86/include/asm/kvm_host.h               |   7 +
 arch/x86/include/asm/kvm_para.h               |  12 +
 arch/x86/include/asm/paravirt.h               |   6 +
 arch/x86/include/asm/paravirt_types.h         |   2 +
 arch/x86/kernel/paravirt.c                    |   1 +
 arch/x86/kvm/svm.c                            | 662 +++++++++++++++++-
 arch/x86/kvm/vmx/vmx.c                        |   1 +
 arch/x86/kvm/x86.c                            |  36 +
 arch/x86/mm/mem_encrypt.c                     |  57 +-
 arch/x86/mm/pat/set_memory.c                  |   7 +
 include/linux/psp-sev.h                       |   8 +-
 include/uapi/linux/kvm.h                      |  53 ++
 include/uapi/linux/kvm_para.h                 |   1 +
 16 files changed, 1037 insertions(+), 9 deletions(-)
Signature · 1 line
2.17.1
#1 lore raw

PATCH 1/12 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.

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        |  27 ++++
 arch/x86/kvm/svm.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 d18c97b4e140..826911f41f3b 100644
@@ -238,6 +238,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_uadr;        /* platform certificate chain */
+                __u32 plat_certs_len;
+
+                __u64 amd_certs_uaddr;        /* AMD certificate */
+                __u32 amd_cert_len;
+
+                __u64 session_uaddr;          /* Guest session information */
+                __u32 session_len;
+        };
+
 References
 ==========
 
arch/x86/kvm/svm.c +125 -0

137 lines not on this page · raw message

include/linux/psp-sev.h +4 -4
index 5167bf2bfc75..9f63b9d48b63 100644
@@ -323,11 +323,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 4b95f9a31a2f..17bef4c245e1 100644
@@ -1558,6 +1558,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

Re: [PATCH 01/12] KVM: SVM: Add KVM_SEV SEND_START command

Quoted 103 lines · Ashish Kalra

From: Brijesh Singh <[email protected]>

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

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        |  27 ++++
 arch/x86/kvm/svm.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 d18c97b4e140..826911f41f3b 100644
@@ -238,6 +238,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_uadr;        /* platform certificate chain */
+                __u32 plat_certs_len;
+
+                __u64 amd_certs_uaddr;        /* AMD certificate */
+                __u32 amd_cert_len;
+
+                __u64 session_uaddr;          /* Guest session information */
+                __u32 session_len;
+        };
+
 References
 ==========
 
arch/x86/kvm/svm.c +32 -0
index a3e32d61d60c..3a7e2cac51de 100644
@@ -7140,6 +7140,128 @@ static int sev_launch_secret(struct kvm *kvm, struct kvm_sev_cmd *argp)
        return ret;
 }
 
+/* Userspace wants to query session length. */
+static int
+__sev_send_start_query_session_length(struct kvm *kvm, struct kvm_sev_cmd *argp,
+                                     struct kvm_sev_send_start *params)
+{
+       struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
+       struct sev_data_send_start *data;
+       int ret;
+
+       data = kzalloc(sizeof(*data), GFP_KERNEL_ACCOUNT);
+       if (data == NULL)
+               return -ENOMEM;
+
+       data->handle = sev->handle;
+       ret = sev_issue_cmd(kvm, SEV_CMD_SEND_START, data, &argp->error);
+
+       params->session_len = data->session_len;
+       if (copy_to_user((void __user *)(uintptr_t)argp->data, params,
+                               sizeof(struct kvm_sev_send_start)))
+               ret = -EFAULT;
+
+       kfree(data);
+       return ret;
+}
+
+static int sev_send_start(struct kvm *kvm, struct kvm_sev_cmd *argp)
+{
+       struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
+       struct sev_data_send_start *data;
+       struct kvm_sev_send_start params;
+       void *amd_certs, *session_data;
+       void *pdh_cert, *plat_certs;

arch/x86/kvm/svm.c static int sev_launch_secret(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 7184

Quoted 10 lines
arch/x86/kvm/svm.c +10 -0
+       int ret;
+
+       if (!sev_guest(kvm))
+               return -ENOTTY;
+
+       if (copy_from_user(&params, (void __user *)(uintptr_t)argp->data,
+                               sizeof(struct kvm_sev_send_start)))
+               return -EFAULT;
+
+       /* if session_len is zero, userspace wants t query the session length */

/t/to/

arch/x86/kvm/svm.c static int sev_launch_secret(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 7187

Quoted 4 lines
arch/x86/kvm/svm.c +3 -0
+       if (!params.session_len)
+               return __sev_send_start_query_session_length(kvm, argp,
+                               &params);

Document this behavior with the command.

Quoted 41 lines
arch/x86/kvm/svm.c +41 -0
+
+       /* some sanity checks */
+       if (!params.pdh_cert_uaddr || !params.pdh_cert_len ||
+           !params.session_uaddr || params.session_len > SEV_FW_BLOB_MAX_SIZE)
+               return -EINVAL;
+
+       /* allocate the memory to hold the session data blob */
+       session_data = kmalloc(params.session_len, GFP_KERNEL_ACCOUNT);
+       if (!session_data)
+               return -ENOMEM;
+
+       /* copy the certificate blobs from userspace */
+       pdh_cert = psp_copy_user_blob(params.pdh_cert_uaddr,
+                               params.pdh_cert_len);
+       if (IS_ERR(pdh_cert)) {
+               ret = PTR_ERR(pdh_cert);
+               goto e_free_session;
+       }
+
+       plat_certs = psp_copy_user_blob(params.plat_certs_uaddr,
+                               params.plat_certs_len);
+       if (IS_ERR(plat_certs)) {
+               ret = PTR_ERR(plat_certs);
+               goto e_free_pdh;
+       }
+
+       amd_certs = psp_copy_user_blob(params.amd_certs_uaddr,
+                               params.amd_certs_len);
+       if (IS_ERR(amd_certs)) {
+               ret = PTR_ERR(amd_certs);
+               goto e_free_plat_cert;
+       }
+
+       data = kzalloc(sizeof(*data), GFP_KERNEL_ACCOUNT);
+       if (data == NULL) {
+               ret = -ENOMEM;
+               goto e_free_amd_cert;
+       }
+
+       /* populate the FW SEND_START field with system physical address */
+       data->pdh_cert_address = __psp_pa(pdh_cert);

arch/x86/kvm/svm.c static int sev_launch_secret(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 7238

Quoted 10 lines
arch/x86/kvm/svm.c +10 -0
+       data->pdh_cert_len = params.pdh_cert_len;
+       data->plat_certs_address = __psp_pa(plat_certs);
+       data->plat_certs_len = params.plat_certs_len;
+       data->amd_certs_address = __psp_pa(amd_certs);
+       data->amd_certs_len = params.amd_certs_len;
+       data->session_address = __psp_pa(session_data);
+       data->session_len = params.session_len;
+       data->handle = sev->handle;
+
+       ret = sev_issue_cmd(kvm, SEV_CMD_SEND_START, data, &argp->error);

sev_issue_cmd can fail. I think you want to handle those errors here (e.g. it can return -ebadf or a number of others). Right now they could get clobbered by a later copy_to_user error.

It's also worth documenting what the error argp->error is filled in with. I didn't see anything in the docs mentioning the status codes (may have missed it).

Quoted 83 lines
arch/x86/kvm/svm.c +29 -0
+
+       if (copy_to_user((void __user *)(uintptr_t) params.session_uaddr,
+                       session_data, params.session_len)) {
+               ret = -EFAULT;
+               goto e_free;
+       }
+
+       params.policy = data->policy;
+       params.session_len = data->session_len;
+       if (copy_to_user((void __user *)(uintptr_t)argp->data, &params,
+                               sizeof(struct kvm_sev_send_start)))
+               ret = -EFAULT;
+
+e_free:
+       kfree(data);
+e_free_amd_cert:
+       kfree(amd_certs);
+e_free_plat_cert:
+       kfree(plat_certs);
+e_free_pdh:
+       kfree(pdh_cert);
+e_free_session:
+       kfree(session_data);
+       return ret;
+}
+
 static int svm_mem_enc_op(struct kvm *kvm, void __user *argp)
 {
        struct kvm_sev_cmd sev_cmd;
@@ -7181,6 +7303,9 @@ static int svm_mem_enc_op(struct kvm *kvm, void __user *argp)
        case KVM_SEV_LAUNCH_SECRET:
                r = sev_launch_secret(kvm, &sev_cmd);
                break;
+       case KVM_SEV_SEND_START:
+               r = sev_send_start(kvm, &sev_cmd);
+               break;
        default:
                r = -EINVAL;
                goto out;
include/linux/psp-sev.h +4 -4
index 5167bf2bfc75..9f63b9d48b63 100644
@@ -323,11 +323,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 -1
index 4b95f9a31a2f..17bef4c245e1 100644
@@ -1558,6 +1558,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)
--
Quoted 2 lines

2.17.1

Looks pretty reasonable overall.

#3 lore raw

Re: [PATCH 01/12] KVM: SVM: Add KVM_SEV SEND_START command

Quoted 178 lines · Steve Rutherford
Quoted 103 lines · Ashish Kalra

From: Brijesh Singh <[email protected]>

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

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        |  27 ++++
 arch/x86/kvm/svm.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 d18c97b4e140..826911f41f3b 100644
@@ -238,6 +238,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_uadr;        /* platform certificate chain */
+                __u32 plat_certs_len;
+
+                __u64 amd_certs_uaddr;        /* AMD certificate */
+                __u32 amd_cert_len;
+
+                __u64 session_uaddr;          /* Guest session information */
+                __u32 session_len;
+        };
+
 References
 ==========
 
arch/x86/kvm/svm.c +32 -0
index a3e32d61d60c..3a7e2cac51de 100644
@@ -7140,6 +7140,128 @@ static int sev_launch_secret(struct kvm *kvm, struct kvm_sev_cmd *argp)
        return ret;
 }
 
+/* Userspace wants to query session length. */
+static int
+__sev_send_start_query_session_length(struct kvm *kvm, struct kvm_sev_cmd *argp,
+                                     struct kvm_sev_send_start *params)
+{
+       struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
+       struct sev_data_send_start *data;
+       int ret;
+
+       data = kzalloc(sizeof(*data), GFP_KERNEL_ACCOUNT);
+       if (data == NULL)
+               return -ENOMEM;
+
+       data->handle = sev->handle;
+       ret = sev_issue_cmd(kvm, SEV_CMD_SEND_START, data, &argp->error);
+
+       params->session_len = data->session_len;
+       if (copy_to_user((void __user *)(uintptr_t)argp->data, params,
+                               sizeof(struct kvm_sev_send_start)))
+               ret = -EFAULT;
+
+       kfree(data);
+       return ret;
+}
+
+static int sev_send_start(struct kvm *kvm, struct kvm_sev_cmd *argp)
+{
+       struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
+       struct sev_data_send_start *data;
+       struct kvm_sev_send_start params;
+       void *amd_certs, *session_data;
+       void *pdh_cert, *plat_certs;

arch/x86/kvm/svm.c static int sev_launch_secret(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 7184

Quoted 10 lines
arch/x86/kvm/svm.c +10 -0
+       int ret;
+
+       if (!sev_guest(kvm))
+               return -ENOTTY;
+
+       if (copy_from_user(&params, (void __user *)(uintptr_t)argp->data,
+                               sizeof(struct kvm_sev_send_start)))
+               return -EFAULT;
+
+       /* if session_len is zero, userspace wants t query the session length */

/t/to/

arch/x86/kvm/svm.c static int sev_launch_secret(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 7187

Quoted 4 lines
arch/x86/kvm/svm.c +3 -0
+       if (!params.session_len)
+               return __sev_send_start_query_session_length(kvm, argp,
+                               &params);

Document this behavior with the command.

Quoted 41 lines
arch/x86/kvm/svm.c +41 -0
+
+       /* some sanity checks */
+       if (!params.pdh_cert_uaddr || !params.pdh_cert_len ||
+           !params.session_uaddr || params.session_len > SEV_FW_BLOB_MAX_SIZE)
+               return -EINVAL;
+
+       /* allocate the memory to hold the session data blob */
+       session_data = kmalloc(params.session_len, GFP_KERNEL_ACCOUNT);
+       if (!session_data)
+               return -ENOMEM;
+
+       /* copy the certificate blobs from userspace */
+       pdh_cert = psp_copy_user_blob(params.pdh_cert_uaddr,
+                               params.pdh_cert_len);
+       if (IS_ERR(pdh_cert)) {
+               ret = PTR_ERR(pdh_cert);
+               goto e_free_session;
+       }
+
+       plat_certs = psp_copy_user_blob(params.plat_certs_uaddr,
+                               params.plat_certs_len);
+       if (IS_ERR(plat_certs)) {
+               ret = PTR_ERR(plat_certs);
+               goto e_free_pdh;
+       }
+
+       amd_certs = psp_copy_user_blob(params.amd_certs_uaddr,
+                               params.amd_certs_len);
+       if (IS_ERR(amd_certs)) {
+               ret = PTR_ERR(amd_certs);
+               goto e_free_plat_cert;
+       }
+
+       data = kzalloc(sizeof(*data), GFP_KERNEL_ACCOUNT);
+       if (data == NULL) {
+               ret = -ENOMEM;
+               goto e_free_amd_cert;
+       }
+
+       /* populate the FW SEND_START field with system physical address */
+       data->pdh_cert_address = __psp_pa(pdh_cert);

arch/x86/kvm/svm.c static int sev_launch_secret(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 7238

Quoted 10 lines
arch/x86/kvm/svm.c +10 -0
+       data->pdh_cert_len = params.pdh_cert_len;
+       data->plat_certs_address = __psp_pa(plat_certs);
+       data->plat_certs_len = params.plat_certs_len;
+       data->amd_certs_address = __psp_pa(amd_certs);
+       data->amd_certs_len = params.amd_certs_len;
+       data->session_address = __psp_pa(session_data);
+       data->session_len = params.session_len;
+       data->handle = sev->handle;
+
+       ret = sev_issue_cmd(kvm, SEV_CMD_SEND_START, data, &argp->error);

sev_issue_cmd can fail. I think you want to handle those errors here (e.g. it can return -ebadf or a number of others). Right now they could get clobbered by a later copy_to_user error.

Quoted 10 lines

It's also worth documenting what the error argp->error is filled in with. I didn't see anything in the docs mentioning the status codes (may have missed it).

Quoted 6 lines
arch/x86/kvm/svm.c +6 -0
+
+       if (copy_to_user((void __user *)(uintptr_t) params.session_uaddr,
+                       session_data, params.session_len)) {
+               ret = -EFAULT;
+               goto e_free;
+       }

One additional aspect, which also comes up for other commands, is that it's not clear if the command succeeded if you get back -EFAULT. Any of the copy_to_users could have failed, on either side of the sev_issue_cmd call.

If userspace filled in the error with a reserved value (e.g. 0xFFFF0000, which is larger than the largest possible error code), it could observe that that value was clobbered and infer that sev_issue_cmd succeeded/failed/etc. This is particularly error prone since the success code is zero, which is almost certainly what people will initialize the error field as, unless they go out of their way.

I think the cleanest answer would be to write in a reserved value to the error at the start of sev_send_* and have sev_issue_command clobber that value with the expected value. This way, userspace can know which GSTATE the guest transitioned to, even if it sees -EFAULT.

Quoted 81 lines
Quoted 77 lines
arch/x86/kvm/svm.c +23 -0
+
+       params.policy = data->policy;
+       params.session_len = data->session_len;
+       if (copy_to_user((void __user *)(uintptr_t)argp->data, &params,
+                               sizeof(struct kvm_sev_send_start)))
+               ret = -EFAULT;
+
+e_free:
+       kfree(data);
+e_free_amd_cert:
+       kfree(amd_certs);
+e_free_plat_cert:
+       kfree(plat_certs);
+e_free_pdh:
+       kfree(pdh_cert);
+e_free_session:
+       kfree(session_data);
+       return ret;
+}
+
 static int svm_mem_enc_op(struct kvm *kvm, void __user *argp)
 {
        struct kvm_sev_cmd sev_cmd;
@@ -7181,6 +7303,9 @@ static int svm_mem_enc_op(struct kvm *kvm, void __user *argp)
        case KVM_SEV_LAUNCH_SECRET:
                r = sev_launch_secret(kvm, &sev_cmd);
                break;
+       case KVM_SEV_SEND_START:
+               r = sev_send_start(kvm, &sev_cmd);
+               break;
        default:
                r = -EINVAL;
                goto out;
include/linux/psp-sev.h +4 -4
index 5167bf2bfc75..9f63b9d48b63 100644
@@ -323,11 +323,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 -1
index 4b95f9a31a2f..17bef4c245e1 100644
@@ -1558,6 +1558,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)
--
Quoted 2 lines

2.17.1

Looks pretty reasonable overall.

#4 lore raw

PATCH 2/12 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.c                            | 136 +++++++++++++++++-
 include/uapi/linux/kvm.h                      |   9 ++
 3 files changed, 165 insertions(+), 4 deletions(-)
Documentation/virt/kvm/amd-memory-encryption.rst +24 -0
index 826911f41f3b..0f1c3860360f 100644
@@ -265,6 +265,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.c +132 -4

166 lines not on this page · raw message

include/uapi/linux/kvm.h +9 -0
index 17bef4c245e1..d9dc81bb9c55 100644
@@ -1570,6 +1570,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
#5 lore raw

Re: [PATCH 02/12] KVM: SVM: Add KVM_SEND_UPDATE_DATA command

Quoted 240 lines · 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.c                            | 136 +++++++++++++++++-
 include/uapi/linux/kvm.h                      |   9 ++
 3 files changed, 165 insertions(+), 4 deletions(-)
Documentation/virt/kvm/amd-memory-encryption.rst +24 -0
index 826911f41f3b..0f1c3860360f 100644
@@ -265,6 +265,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.c +132 -4
index 3a7e2cac51de..ae97f774e979 100644
@@ -426,6 +426,7 @@ static DECLARE_RWSEM(sev_deactivate_lock);
 static DEFINE_MUTEX(sev_bitmap_lock);
 static unsigned int max_sev_asid;
 static unsigned int min_sev_asid;
+static unsigned long sev_me_mask;
 static unsigned long *sev_asid_bitmap;
 static unsigned long *sev_reclaim_asid_bitmap;
 #define __sme_page_pa(x) __sme_set(page_to_pfn(x) << PAGE_SHIFT)
@@ -1231,16 +1232,22 @@ static int avic_ga_log_notifier(u32 ga_tag)
 static __init int sev_hardware_setup(void)
 {
        struct sev_user_data_status *status;
+       int eax, ebx;
        int rc;
 
-       /* Maximum number of encrypted guests supported simultaneously */
-       max_sev_asid = cpuid_ecx(0x8000001F);
+       /*
+        * Query the memory encryption information.
+        *  EBX:  Bit 0:5 Pagetable bit position used to indicate encryption
+        *  (aka Cbit).
+        *  ECX:  Maximum number of encrypted guests supported simultaneously.
+        *  EDX:  Minimum ASID value that should be used for SEV guest.
+        */
+       cpuid(0x8000001f, &eax, &ebx, &max_sev_asid, &min_sev_asid);
 
        if (!max_sev_asid)
                return 1;
 
-       /* Minimum ASID value that should be used for SEV guest */
-       min_sev_asid = cpuid_edx(0x8000001F);
+       sev_me_mask = 1UL << (ebx & 0x3f);
 
        /* Initialize SEV ASID bitmaps */
        sev_asid_bitmap = bitmap_zalloc(max_sev_asid, GFP_KERNEL);
@@ -7262,6 +7269,124 @@ static int sev_send_start(struct kvm *kvm, struct kvm_sev_cmd *argp)
        return ret;
 }
 
+/* Userspace wants to query either header or trans length. */
+static int
+__sev_send_update_data_query_lengths(struct kvm *kvm, struct kvm_sev_cmd *argp,
+                                    struct kvm_sev_send_update_data *params)
+{
+       struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
+       struct sev_data_send_update_data *data;
+       int ret;
+
+       data = kzalloc(sizeof(*data), GFP_KERNEL_ACCOUNT);
+       if (!data)
+               return -ENOMEM;
+
+       data->handle = sev->handle;
+       ret = sev_issue_cmd(kvm, SEV_CMD_SEND_UPDATE_DATA, data, &argp->error);
+
+       params->hdr_len = data->hdr_len;
+       params->trans_len = data->trans_len;
+
+       if (copy_to_user((void __user *)(uintptr_t)argp->data, params,
+                        sizeof(struct kvm_sev_send_update_data)))
+               ret = -EFAULT;
+
+       kfree(data);
+       return ret;
+}
+
+static int sev_send_update_data(struct kvm *kvm, struct kvm_sev_cmd *argp)
+{
+       struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
+       struct sev_data_send_update_data *data;
+       struct kvm_sev_send_update_data params;
+       void *hdr, *trans_data;
+       struct page **guest_page;
+       unsigned long n;
+       int ret, offset;
+
+       if (!sev_guest(kvm))
+               return -ENOTTY;
+
+       if (copy_from_user(&params, (void __user *)(uintptr_t)argp->data,
+                       sizeof(struct kvm_sev_send_update_data)))
+               return -EFAULT;
+
+       /* userspace wants to query either header or trans length */
+       if (!params.trans_len || !params.hdr_len)
+               return __sev_send_update_data_query_lengths(kvm, argp, &params);
+
+       if (!params.trans_uaddr || !params.guest_uaddr ||
+           !params.guest_len || !params.hdr_uaddr)
+               return -EINVAL;
+
+
+       /* Check if we are crossing the page boundary */
+       offset = params.guest_uaddr & (PAGE_SIZE - 1);
+       if ((params.guest_len + offset > PAGE_SIZE))
+               return -EINVAL;
+
+       /* Pin guest memory */
+       guest_page = sev_pin_memory(kvm, params.guest_uaddr & PAGE_MASK,
+                                   PAGE_SIZE, &n, 0);
+       if (!guest_page)
+               return -EFAULT;
+
+       /* allocate memory for header and transport buffer */
+       ret = -ENOMEM;
+       hdr = kmalloc(params.hdr_len, GFP_KERNEL_ACCOUNT);
+       if (!hdr)
+               goto e_unpin;
+
+       trans_data = kmalloc(params.trans_len, GFP_KERNEL_ACCOUNT);
+       if (!trans_data)
+               goto e_free_hdr;
+
+       data = kzalloc(sizeof(*data), GFP_KERNEL);
+       if (!data)
+               goto e_free_trans_data;
+
+       data->hdr_address = __psp_pa(hdr);
+       data->hdr_len = params.hdr_len;
+       data->trans_address = __psp_pa(trans_data);
+       data->trans_len = params.trans_len;
+
+       /* The SEND_UPDATE_DATA command requires C-bit to be always set. */
+       data->guest_address = (page_to_pfn(guest_page[0]) << PAGE_SHIFT) +
+                               offset;
+       data->guest_address |= sev_me_mask;
+       data->guest_len = params.guest_len;
+       data->handle = sev->handle;
+
+       ret = sev_issue_cmd(kvm, SEV_CMD_SEND_UPDATE_DATA, data, &argp->error);
+
+       if (ret)
+               goto e_free;
+
+       /* copy transport buffer to user space */
+       if (copy_to_user((void __user *)(uintptr_t)params.trans_uaddr,
+                        trans_data, params.trans_len)) {
+               ret = -EFAULT;
+               goto e_unpin;
+       }
+
+       /* Copy packet header to userspace. */
+       ret = copy_to_user((void __user *)(uintptr_t)params.hdr_uaddr, hdr,
+                               params.hdr_len);
+
+e_free:
+       kfree(data);
+e_free_trans_data:
+       kfree(trans_data);
+e_free_hdr:
+       kfree(hdr);
+e_unpin:
+       sev_unpin_memory(kvm, guest_page, n);
+
+       return ret;
+}
+
 static int svm_mem_enc_op(struct kvm *kvm, void __user *argp)
 {
        struct kvm_sev_cmd sev_cmd;
@@ -7306,6 +7431,9 @@ static int svm_mem_enc_op(struct kvm *kvm, void __user *argp)
        case KVM_SEV_SEND_START:
                r = sev_send_start(kvm, &sev_cmd);
                break;
+       case KVM_SEV_SEND_UPDATE_DATA:
+               r = sev_send_update_data(kvm, &sev_cmd);
+               break;
        default:
                r = -EINVAL;
                goto out;
include/uapi/linux/kvm.h +0 -0
index 17bef4c245e1..d9dc81bb9c55 100644
@@ -1570,6 +1570,15 @@ struct kvm_sev_send_start {
        __u32 session_len;

include/uapi/linux/kvm.h struct kvm_sev_send_start { · line 1580

Quoted 10 lines
include/uapi/linux/kvm.h +8 -0
 };
 
+struct kvm_sev_send_update_data {
+       __u64 hdr_uaddr;
+       __u32 hdr_len;
+       __u64 guest_uaddr;
+       __u32 guest_len;
+       __u64 trans_uaddr;
+       __u32 trans_len;
+};

Input from others is welcome here, but I'd put the padding in intentionally (explicitly fill in the reserved u8s between *_len and *_uaddr). I had to double check that this pattern was intentional and matched the SEV spec.

Quoted 7 lines
include/uapi/linux/kvm.h +1 -1
+
 #define KVM_DEV_ASSIGN_ENABLE_IOMMU    (1 << 0)
 #define KVM_DEV_ASSIGN_PCI_2_3         (1 << 1)
 #define KVM_DEV_ASSIGN_MASK_INTX       (1 << 2)
--

2.17.1

High level: this looks good. Same comments on documenting the magic parameters for querying as the prior patch, and also the -EFAULT behavior.

Thanks,
Steve

#6 lore raw

Re: [PATCH 02/12] KVM: SVM: Add KVM_SEND_UPDATE_DATA command

Quoted 8 lines · Steve Rutherford
Quoted 8 lines
include/uapi/linux/kvm.h +0 -0
index 17bef4c245e1..d9dc81bb9c55 100644
@@ -1570,6 +1570,15 @@ struct kvm_sev_send_start {
        __u32 session_len;
 };
 
Quoted 12 lines

include/uapi/linux/kvm.h struct kvm_sev_send_start { · line 1580

Quoted 8 lines
include/uapi/linux/kvm.h +8 -0
+struct kvm_sev_send_update_data {
+       __u64 hdr_uaddr;
+       __u32 hdr_len;
+       __u64 guest_uaddr;
+       __u32 guest_len;
+       __u64 trans_uaddr;
+       __u32 trans_len;
+};

Input from others is welcome here, but I'd put the padding in intentionally (explicitly fill in the reserved u8s between *_len and *_uaddr). I had to double check that this pattern was intentional and matched the SEV spec.

struct kvm_sev_send_update_data{} is used to get/send the information from/to userspace, while struct sev_data_send_update_data{} has the paddings and is packed as required by the SEV spec.

Thanks,
Ashish

#7 lore raw

PATCH 3/12 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.c                            | 23 +++++++++++++++++++
 2 files changed, 31 insertions(+)
Documentation/virt/kvm/amd-memory-encryption.rst +8 -0
index 0f1c3860360f..f22f09ad72bd 100644
@@ -289,6 +289,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.c +23 -0
index ae97f774e979..c55c1865f9e0 100644
@@ -7387,6 +7387,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;
+}
+
 static int svm_mem_enc_op(struct kvm *kvm, void __user *argp)
 {
 	struct kvm_sev_cmd sev_cmd;
@@ -7434,6 +7454,9 @@ static 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
Reviewed-by on v1 3/12
#8 lore raw

Re: [PATCH 03/12] KVM: SVM: Add KVM_SEV_SEND_FINISH command

Quoted 85 lines · 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.c                            | 23 +++++++++++++++++++
 2 files changed, 31 insertions(+)
Documentation/virt/kvm/amd-memory-encryption.rst +8 -0
index 0f1c3860360f..f22f09ad72bd 100644
@@ -289,6 +289,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.c +23 -1
index ae97f774e979..c55c1865f9e0 100644
@@ -7387,6 +7387,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;
+}
+
 static int svm_mem_enc_op(struct kvm *kvm, void __user *argp)
 {
        struct kvm_sev_cmd sev_cmd;
@@ -7434,6 +7454,9 @@ static 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;
--
Quoted 2 lines

2.17.1

#9 lore raw

PATCH 4/12 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.c                            | 81 +++++++++++++++++++
 include/uapi/linux/kvm.h                      |  9 +++
 3 files changed, 119 insertions(+)
Documentation/virt/kvm/amd-memory-encryption.rst +29 -0
index f22f09ad72bd..4b882fb681fa 100644
@@ -297,6 +297,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 dh_len;
+
+                __u64 session_addr;     /* 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.c +81 -0

93 lines not on this page · raw message

include/uapi/linux/kvm.h +9 -0
index d9dc81bb9c55..74764b9db5fa 100644
@@ -1579,6 +1579,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
#10 lore raw

Re: [PATCH 04/12] KVM: SVM: Add support for KVM_SEV_RECEIVE_START command

Quoted 111 lines · 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.c                            | 81 +++++++++++++++++++
 include/uapi/linux/kvm.h                      |  9 +++
 3 files changed, 119 insertions(+)
Documentation/virt/kvm/amd-memory-encryption.rst +29 -0
index f22f09ad72bd..4b882fb681fa 100644
@@ -297,6 +297,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 dh_len;
+
+                __u64 session_addr;     /* 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.c +37 -0
index c55c1865f9e0..3b766f386c84 100644
@@ -7407,6 +7407,84 @@ static int sev_send_finish(struct kvm *kvm, struct kvm_sev_cmd *argp)
        return ret;
 }
 
+static int sev_receive_start(struct kvm *kvm, struct kvm_sev_cmd *argp)
+{
+       struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
+       struct sev_data_receive_start *start;
+       struct kvm_sev_receive_start params;
+       int *error = &argp->error;
+       void *session_data;
+       void *pdh_data;
+       int ret;
+
+       if (!sev_guest(kvm))
+               return -ENOTTY;
+
+       /* Get parameter from the userspace */
+       if (copy_from_user(&params, (void __user *)(uintptr_t)argp->data,
+                       sizeof(struct kvm_sev_receive_start)))
+               return -EFAULT;
+
+       /* some sanity checks */
+       if (!params.pdh_uaddr || !params.pdh_len ||
+           !params.session_uaddr || !params.session_len)
+               return -EINVAL;
+
+       pdh_data = psp_copy_user_blob(params.pdh_uaddr, params.pdh_len);
+       if (IS_ERR(pdh_data))
+               return PTR_ERR(pdh_data);
+
+       session_data = psp_copy_user_blob(params.session_uaddr,
+                       params.session_len);
+       if (IS_ERR(session_data)) {
+               ret = PTR_ERR(session_data);
+               goto e_free_pdh;
+       }
+
+       ret = -ENOMEM;
+       start = kzalloc(sizeof(*start), GFP_KERNEL);
+       if (!start)

arch/x86/kvm/svm.c static int sev_send_finish(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 7456

Quoted 10 lines
arch/x86/kvm/svm.c +10 -0
+               goto e_free_session;
+
+       start->handle = params.handle;
+       start->policy = params.policy;
+       start->pdh_cert_address = __psp_pa(pdh_data);
+       start->pdh_cert_len = params.pdh_len;
+       start->session_address = __psp_pa(session_data);
+       start->session_len = params.session_len;
+
+       /* create memory encryption context */

Set ret to a different value here, since otherwise this will look like -ENOMEM.

arch/x86/kvm/svm.c static int sev_send_finish(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 7462

Quoted 6 lines
arch/x86/kvm/svm.c +6 -0
+       ret = __sev_issue_cmd(argp->sev_fd, SEV_CMD_RECEIVE_START, start,
+                               error);
+       if (ret)
+               goto e_free;
+
+       /* Bind ASID to this guest */

Ideally, set ret to another distinct value, since the error spaces for these commands overlap, so you won't be sure which had the problem. You also wouldn't be sure if one succeeded and the other failed vs both failing.

Quoted 61 lines
arch/x86/kvm/svm.c +28 -0
+       ret = sev_bind_asid(kvm, start->handle, error);
+       if (ret)
+               goto e_free;
+
+       params.handle = start->handle;
+       if (copy_to_user((void __user *)(uintptr_t)argp->data,
+                        &params, sizeof(struct kvm_sev_receive_start))) {
+               ret = -EFAULT;
+               sev_unbind_asid(kvm, start->handle);
+               goto e_free;
+       }
+
+       sev->handle = start->handle;
+       sev->fd = argp->sev_fd;
+
+e_free:
+       kfree(start);
+e_free_session:
+       kfree(session_data);
+e_free_pdh:
+       kfree(pdh_data);
+
+       return ret;
+}
+
 static int svm_mem_enc_op(struct kvm *kvm, void __user *argp)
 {
        struct kvm_sev_cmd sev_cmd;
@@ -7457,6 +7535,9 @@ static int svm_mem_enc_op(struct kvm *kvm, void __user *argp)
        case KVM_SEV_SEND_FINISH:
                r = sev_send_finish(kvm, &sev_cmd);
                break;
+       case KVM_SEV_RECEIVE_START:
+               r = sev_receive_start(kvm, &sev_cmd);
+               break;
        default:
                r = -EINVAL;
                goto out;
include/uapi/linux/kvm.h +9 -1
index d9dc81bb9c55..74764b9db5fa 100644
@@ -1579,6 +1579,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)
--

2.17.1

#11 lore raw

Re: [PATCH 04/12] KVM: SVM: Add support for KVM_SEV_RECEIVE_START command

Quoted 41 lines · Steve Rutherford
Quoted 41 lines
arch/x86/kvm/svm.c +41 -0
+static int sev_receive_start(struct kvm *kvm, struct kvm_sev_cmd *argp)
+{
+       struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
+       struct sev_data_receive_start *start;
+       struct kvm_sev_receive_start params;
+       int *error = &argp->error;
+       void *session_data;
+       void *pdh_data;
+       int ret;
+
+       if (!sev_guest(kvm))
+               return -ENOTTY;
+
+       /* Get parameter from the userspace */
+       if (copy_from_user(&params, (void __user *)(uintptr_t)argp->data,
+                       sizeof(struct kvm_sev_receive_start)))
+               return -EFAULT;
+
+       /* some sanity checks */
+       if (!params.pdh_uaddr || !params.pdh_len ||
+           !params.session_uaddr || !params.session_len)
+               return -EINVAL;
+
+       pdh_data = psp_copy_user_blob(params.pdh_uaddr, params.pdh_len);
+       if (IS_ERR(pdh_data))
+               return PTR_ERR(pdh_data);
+
+       session_data = psp_copy_user_blob(params.session_uaddr,
+                       params.session_len);
+       if (IS_ERR(session_data)) {
+               ret = PTR_ERR(session_data);
+               goto e_free_pdh;
+       }
+
+       ret = -ENOMEM;
+       start = kzalloc(sizeof(*start), GFP_KERNEL);
+       if (!start)
+               goto e_free_session;
+
+       start->handle = params.handle;
+       start->policy = params.policy;
Quoted 8 lines

arch/x86/kvm/svm.c static int sev_send_finish(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 7456

Quoted 6 lines
arch/x86/kvm/svm.c +6 -0
+       start->pdh_cert_address = __psp_pa(pdh_data);
+       start->pdh_cert_len = params.pdh_len;
+       start->session_address = __psp_pa(session_data);
+       start->session_len = params.session_len;
+
+       /* create memory encryption context */

Set ret to a different value here, since otherwise this will look like -ENOMEM.

But, ret will be the value returned by __sev_issue_cmd(), so why will it look like -ENOMEM ?

Quoted 8 lines

arch/x86/kvm/svm.c static int sev_send_finish(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 7462

Quoted 7 lines
arch/x86/kvm/svm.c +6 -0
+       ret = __sev_issue_cmd(argp->sev_fd, SEV_CMD_RECEIVE_START, start,
+                               error);
+       if (ret)
+               goto e_free;
+
+       /* Bind ASID to this guest */
Quoted 4 lines

Ideally, set ret to another distinct value, since the error spaces for these commands overlap, so you won't be sure which had the problem. You also wouldn't be sure if one succeeded and the other failed vs both failing.

Both commands "may" return the same error code as set by sev_do_cmd(), but then we need that very specific error code, sev_do_cmd() can't return different error codes for each command it is issuing ?

Quoted 5 lines

arch/x86/kvm/svm.c static int sev_send_finish(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 7466

Quoted 5 lines
arch/x86/kvm/svm.c +4 -0
+       ret = sev_bind_asid(kvm, start->handle, error);
+       if (ret)
+               goto e_free;
+

Thanks,
Ashish

Reviewed-by
#12 lore raw

Re: [PATCH 04/12] KVM: SVM: Add support for KVM_SEV_RECEIVE_START command

Quoted 3 lines · Ashish Kalra

But, ret will be the value returned by __sev_issue_cmd(), so why will it look like -ENOMEM ?

My bad, this is fine.

Quoted 9 lines
Quoted 9 lines
Quoted 8 lines
arch/x86/kvm/svm.c +6 -0
+       ret = __sev_issue_cmd(argp->sev_fd, SEV_CMD_RECEIVE_START, start,
+                               error);
+       if (ret)
+               goto e_free;
+
+       /* Bind ASID to this guest */
Quoted 8 lines
Quoted 4 lines

Ideally, set ret to another distinct value, since the error spaces for these commands overlap, so you won't be sure which had the problem. You also wouldn't be sure if one succeeded and the other failed vs both failing.

Both commands "may" return the same error code as set by sev_do_cmd(), but then we need that very specific error code, sev_do_cmd() can't return different error codes for each command it is issuing ?

I'll try to separate my comment into two levels: High level response, and pragmatic response.

--- High level ---
At the end of the day, I want to be able to handle these errors in a reasonable way. As often as possible, I'd like userspace to be able to see a set of errors and know what to do in response. I find this particularly important for migration, where you are mucking around with a live VM with customer data you don't want to lose.

One red flag for me is when one pair of {errno, SEV error code} corresponds to two distinct situations. For example, when, in another patch in this series, {EFAULT, SUCCESS} could have corresponded to either the command succeeding or the command never having run. Seems like a pretty wide range of possibilities for a single error value.

I want to try to give the return codes scrutiny now, since we are probably going to be stuck with maintaining them indefinitely, even if there are mistakes.

--- Pragmatic ---
There's probably a strong argument that most situations like this don't matter, since there's nothing you can do about an error except kill the VM (or not continue migrating) anyway. I'm pretty open to this argument. In particular, looking at SEV RECEIVE START, I think you could throw away this attempt at creating a migration target, and just make a new one (pretty much without consequence), so I think my comment on this particular patch is moot. You can't cancel the SEND START so you will be stuck working with this particular destination host, but you can mint a new target VM via SEV RECEIVE START.

Looking at the earlier patches, older commands seem to have the same ambiguity. The command SEV LAUNCH START also has identical errors that could be sourced from either of two commands. Seems like we're already committed to ambiguity being ok.

Given that I have no further comments on this particular patch:

Quoted 10 lines
Quoted 6 lines

arch/x86/kvm/svm.c static int sev_send_finish(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 7466

Quoted 6 lines
arch/x86/kvm/svm.c +4 -0
+       ret = sev_bind_asid(kvm, start->handle, error);
+       if (ret)
+               goto e_free;
+

Thanks,
Ashish

#13 lore raw

PATCH 5/12 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.c                            | 79 +++++++++++++++++++
 include/uapi/linux/kvm.h                      |  9 +++
 3 files changed, 112 insertions(+)
Documentation/virt/kvm/amd-memory-encryption.rst +24 -0
index 4b882fb681fa..52fca9e258dc 100644
@@ -326,6 +326,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.c +79 -0

91 lines not on this page · raw message

include/uapi/linux/kvm.h +9 -0
index 74764b9db5fa..4e80c57a3182 100644
@@ -1588,6 +1588,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
#14 lore raw

PATCH 6/12 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.c                            | 23 +++++++++++++++++++
 2 files changed, 31 insertions(+)
Documentation/virt/kvm/amd-memory-encryption.rst +8 -0
index 52fca9e258dc..4dbcb22bcd55 100644
@@ -350,6 +350,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.c +23 -0
index 907c59ca74ad..d86b02bece3a 100644
@@ -7561,6 +7561,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;
+}
+
 static int svm_mem_enc_op(struct kvm *kvm, void __user *argp)
 {
 	struct kvm_sev_cmd sev_cmd;
@@ -7617,6 +7637,9 @@ static 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
#15 lore raw

PATCH 7/12 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.

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/kvm_para.h | 12 ++++++++++++
 1 file changed, 12 insertions(+)
arch/x86/include/asm/kvm_para.h +12 -0
index 9b4df6eaa11a..6c09255633a4 100644
@@ -84,6 +84,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
#16 lore raw

PATCH 8/12 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.

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.txt | 14 ++++
 arch/x86/include/asm/kvm_host.h       |  2 +
 arch/x86/kvm/svm.c                    | 94 +++++++++++++++++++++++++++
 arch/x86/kvm/vmx/vmx.c                |  1 +
 arch/x86/kvm/x86.c                    |  6 ++
 include/uapi/linux/kvm_para.h         |  1 +
 6 files changed, 118 insertions(+)
Documentation/virt/kvm/hypercalls.txt +14 -0
index 5f6d291bd004..8ff0e4adcb13 100644
@@ -152,3 +152,17 @@ 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 4dffbc10d3f8..4ae7293033b2 100644
@@ -1256,6 +1256,8 @@ struct kvm_x86_ops {
 
 	bool (*apic_init_signal_blocked)(struct kvm_vcpu *vcpu);
 	int (*enable_direct_tlbflush)(struct kvm_vcpu *vcpu);
+	int (*page_enc_status_hc)(struct kvm *kvm, unsigned long gpa,
+				  unsigned long sz, unsigned long mode);
 };
 
 struct kvm_arch_async_pf {
arch/x86/kvm/svm.c +94 -0

118 lines not on this page · raw message

arch/x86/kvm/vmx/vmx.c +1 -0
index 9a6664886f2e..7963f2979fdf 100644
@@ -7879,6 +7879,7 @@ static struct kvm_x86_ops vmx_x86_ops __ro_after_init = {
 	.nested_get_evmcs_version = NULL,
 	.need_emulation_on_page_fault = vmx_need_emulation_on_page_fault,
 	.apic_init_signal_blocked = vmx_apic_init_signal_blocked,
+	.page_enc_status_hc = NULL,
 };
 
 static void vmx_cleanup_l1d_flush(void)
arch/x86/kvm/x86.c +6 -0
index fbabb2f06273..298627fa3d39 100644
@@ -7547,6 +7547,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
#17 lore raw

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

Quoted 12 lines · Ashish Kalra
arch/x86/kvm/svm.c +12 -0
+static int sev_resize_page_enc_bitmap(struct kvm *kvm, unsigned long new_size)
+{
+       struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
+       unsigned long *map;
+       unsigned long sz;
+
+       if (sev->page_enc_bmap_size >= new_size)
+               return 0;
+
+       sz = ALIGN(new_size, BITS_PER_LONG) / 8;
+
+       map = vmalloc(sz);

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

Quoted 10 lines
arch/x86/kvm/svm.c +10 -0
+       if (!map) {
+               pr_err_once("Failed to allocate encrypted bitmap size %lx\n",
+                               sz);
+               return -ENOMEM;
+       }
+
+       /* mark the page encrypted (by default) */
+       memset(map, 0xff, sz);
+
+       bitmap_copy(map, sev->page_enc_bmap, sev->page_enc_bmap_size);

Personally, I would do the arithmetic and swap the memset(map, 0xff, sz); for memset(map + sev->page_enc_bmap_size, 0xff, sz - sev->page_enc_bmap_size);, but gcc might be smart enough to do this for you.

Quoted 21 lines
arch/x86/kvm/svm.c +21 -0
+       kvfree(sev->page_enc_bmap);
+
+       sev->page_enc_bmap = map;
+       sev->page_enc_bmap_size = new_size;
+
+       return 0;
+}
+
+static int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa,
+                                 unsigned long npages, unsigned long enc)
+{
+       struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
+       gfn_t gfn_start, gfn_end;
+       int ret;
+
+       if (!sev_guest(kvm))
+               return -EINVAL;
+
+       if (!npages)
+               return 0;
+

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

Quoted 10 lines
arch/x86/kvm/svm.c +10 -0
+       gfn_start = gpa_to_gfn(gpa);
+       gfn_end = gfn_start + npages;
+
+       /* out of bound access error check */
+       if (gfn_end <= gfn_start)
+               return -EINVAL;
+
+       /* lets make sure that gpa exist in our memslot */
+       pfn_start = gfn_to_pfn(kvm, gfn_start);
+       pfn_end = gfn_to_pfn(kvm, gfn_end);

I believe these functions assume as_id==0, which is probably fine in practice. If one were to want to migrate a VM with SMM support (which I believe is the only current usage of non-zero as_ids), it feels like SMM would need to be in control of its own c-bit tracking, but that doesn't seem super feasible (otherwise the guest kernel could corrupt SMM by passing invalid c-bit statuses). I'm not certain anyone wants SMM with SEV anyway?

Quoted 65 lines
arch/x86/kvm/svm.c +36 -0
+
+       if (is_error_noslot_pfn(pfn_start) && !is_noslot_pfn(pfn_start)) {
+               /*
+                * Allow guest MMIO range(s) to be added
+                * to the page encryption bitmap.
+                */
+               return -EINVAL;
+       }
+
+       if (is_error_noslot_pfn(pfn_end) && !is_noslot_pfn(pfn_end)) {
+               /*
+                * Allow guest MMIO range(s) to be added
+                * to the page encryption bitmap.
+                */
+               return -EINVAL;
+       }
+
+       mutex_lock(&kvm->lock);
+       ret = sev_resize_page_enc_bitmap(kvm, gfn_end);
+       if (ret)
+               goto unlock;
+
+       if (enc)
+               __bitmap_set(sev->page_enc_bmap, gfn_start,
+                               gfn_end - gfn_start);
+       else
+               __bitmap_clear(sev->page_enc_bmap, gfn_start,
+                               gfn_end - gfn_start);
+
+unlock:
+       mutex_unlock(&kvm->lock);
+       return ret;
+}
+
 static int svm_mem_enc_op(struct kvm *kvm, void __user *argp)
 {
        struct kvm_sev_cmd sev_cmd;
@@ -7972,6 +8064,8 @@ static struct kvm_x86_ops svm_x86_ops __ro_after_init = {
        .need_emulation_on_page_fault = svm_need_emulation_on_page_fault,
 
        .apic_init_signal_blocked = svm_apic_init_signal_blocked,
+
+       .page_enc_status_hc = svm_page_enc_status_hc,
 };
 
 static int __init svm_init(void)
arch/x86/kvm/vmx/vmx.c +1 -0
index 9a6664886f2e..7963f2979fdf 100644
@@ -7879,6 +7879,7 @@ static struct kvm_x86_ops vmx_x86_ops __ro_after_init = {
        .nested_get_evmcs_version = NULL,
        .need_emulation_on_page_fault = vmx_need_emulation_on_page_fault,
        .apic_init_signal_blocked = vmx_apic_init_signal_blocked,
+       .page_enc_status_hc = NULL,
 };
 
 static void vmx_cleanup_l1d_flush(void)
arch/x86/kvm/x86.c +0 -0
index fbabb2f06273..298627fa3d39 100644
@@ -7547,6 +7547,12 @@ int kvm_emulate_hypercall(struct kvm_vcpu *vcpu)
                kvm_sched_yield(vcpu->kvm, a0);
                ret = 0;

arch/x86/kvm/x86.c int kvm_emulate_hypercall(struct kvm_vcpu *vcpu) · line 7558

Quoted 10 lines
arch/x86/kvm/x86.c +6 -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;

Add a cap to kvm_vm_ioctl_enable_cap so that the vmm can configure whether or not this hypercall is offered. Moving to an enable cap would also allow the vmm to pass down the expected size of the c-bit tracking buffer, so that you don't need to handle dynamic resizing in response to guest hypercall, otherwise KVM will sporadically start copying around large buffers when working with large VMs.

Stepping back a bit, I'm a little surprised by the fact that you don't treat the c-bit buffers the same way as the dirty tracking buffers and put them alongside the memslots. That's probably more effort, and the strategy of using one large buffer should work fine (assuming you don't need to support non-zero as_ids).

#18 lore raw

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

Quoted 18 lines · Steve Rutherford
Quoted 17 lines · Ashish Kalra

static void vmx_cleanup_l1d_flush(void)

arch/x86/kvm/x86.c +6 -0
index fbabb2f06273..298627fa3d39 100644
@@ -7547,6 +7547,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:
Quoted 9 lines

arch/x86/kvm/x86.c int kvm_emulate_hypercall(struct kvm_vcpu *vcpu) · line 7558

Quoted 2 lines
arch/x86/kvm/x86.c +0 -0
                ret = -KVM_ENOSYS;
                break;

Add a cap to kvm_vm_ioctl_enable_cap so that the vmm can configure whether or not this hypercall is offered. Moving to an enable cap would also allow the vmm to pass down the expected size of the c-bit tracking buffer, so that you don't need to handle dynamic resizing in response to guest hypercall, otherwise KVM will sporadically start copying around large buffers when working with large VMs.

Yes, that is something we have been looking at adding.

But, how will VMM know the expected size of the c-bit tracking buffer ?

The guest kernel and firmware make the hypercall to mark page encryption status and depending on the GPA range being marked, the kernel's page encryption bitmap needs to be dynamically resized as response to the guest hypercall.

Quoted 5 lines

Stepping back a bit, I'm a little surprised by the fact that you don't treat the c-bit buffers the same way as the dirty tracking buffers and put them alongside the memslots. That's probably more effort, and the strategy of using one large buffer should work fine (assuming you don't need to support non-zero as_ids).

Thanks,
Ashish

#19 lore raw

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

Quoted 29 lines · Ashish Kalra
Quoted 18 lines · Steve Rutherford
Quoted 17 lines · Ashish Kalra

static void vmx_cleanup_l1d_flush(void)

arch/x86/kvm/x86.c +6 -0
index fbabb2f06273..298627fa3d39 100644
@@ -7547,6 +7547,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:
Quoted 9 lines

arch/x86/kvm/x86.c int kvm_emulate_hypercall(struct kvm_vcpu *vcpu) · line 7558

Quoted 2 lines
arch/x86/kvm/x86.c +0 -0
                ret = -KVM_ENOSYS;
                break;

Add a cap to kvm_vm_ioctl_enable_cap so that the vmm can configure whether or not this hypercall is offered. Moving to an enable cap would also allow the vmm to pass down the expected size of the c-bit tracking buffer, so that you don't need to handle dynamic resizing in response to guest hypercall, otherwise KVM will sporadically start copying around large buffers when working with large VMs.

Quoted 9 lines

Yes, that is something we have been looking at adding.

But, how will VMM know the expected size of the c-bit tracking buffer ?

The guest kernel and firmware make the hypercall to mark page encryption status and depending on the GPA range being marked, the kernel's page encryption bitmap needs to be dynamically resized as response to the guest hypercall.

Discussed this with Brijesh, though KVM can provide a hint about the expected (max.) size of the c-bit tracking buffer, but there is still an issue for hotplugged guest memory, hence dynamically sized encryption bitmap is probably the right approach.

Thanks,
Ashish

#20 lore raw

PATCH 9/12 KVM: x86: Introduce KVM_GET_PAGE_ENC_BITMAP ioctl

In reply to Ashish Kalra

From: Brijesh Singh <[email protected]>

The ioctl can be used to retrieve page encryption bitmap for a given gfn range.

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.txt  | 27 +++++++++++++++++++++
 arch/x86/include/asm/kvm_host.h |  2 ++
 arch/x86/kvm/svm.c              | 43 +++++++++++++++++++++++++++++++++
 arch/x86/kvm/x86.c              | 12 +++++++++
 include/uapi/linux/kvm.h        | 12 +++++++++
 5 files changed, 96 insertions(+)
Documentation/virt/kvm/api.txt +27 -0
index c6e1ce5d40de..053aecfabe74 100644
@@ -4213,6 +4213,33 @@ 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.120 KVM_GET_PAGE_ENC_BITMAP (vm ioctl)
+
+Capability: basic
+Architectures: x86
+Type: vm ioctl
+Parameters: struct kvm_page_enc_bitmap (in/out)
+Returns: 0 on success, -1 on error
+
+/* for KVM_GET_PAGE_ENC_BITMAP */
+struct kvm_page_enc_bitmap {
+	__u64 start_gfn;
+	__u64 num_pages;
+	union {
+		void __user *enc_bitmap; /* one bit per page */
+		__u64 padding2;
+	};
+};
+
+The encrypted VMs have concept of private and shared pages. The private
+page is encrypted with the guest-specific key, while shared page may
+be encrypted with the hypervisor key. The KVM_GET_PAGE_ENC_BITMAP can
+be used to get the bitmap indicating whether the guest page is private
+or shared. The bitmap can be used during the guest migration, if the page
+is private then userspace need to use SEV migration commands to transmit
+the page.
+
+
 5. The kvm_run structure
 ------------------------
 
arch/x86/include/asm/kvm_host.h +2 -0
index 4ae7293033b2..a6882c5214b4 100644
@@ -1258,6 +1258,8 @@ struct kvm_x86_ops {
 	int (*enable_direct_tlbflush)(struct kvm_vcpu *vcpu);
 	int (*page_enc_status_hc)(struct kvm *kvm, unsigned long gpa,
 				  unsigned long sz, unsigned long mode);
+	int (*get_page_enc_bitmap)(struct kvm *kvm,
+				struct kvm_page_enc_bitmap *bmap);
 };
 
 struct kvm_arch_async_pf {
arch/x86/kvm/svm.c +43 -0

55 lines not on this page · raw message

arch/x86/kvm/x86.c +12 -0
index 298627fa3d39..e955f886ee17 100644
@@ -5213,6 +5213,18 @@ long kvm_arch_vm_ioctl(struct file *filp,
 	case KVM_SET_PMU_EVENT_FILTER:
 		r = kvm_vm_ioctl_set_pmu_event_filter(kvm, argp);
 		break;
+	case KVM_GET_PAGE_ENC_BITMAP: {
+		struct kvm_page_enc_bitmap bitmap;
+
+		r = -EFAULT;
+		if (copy_from_user(&bitmap, argp, sizeof(bitmap)))
+			goto out;
+
+		r = -ENOTTY;
+		if (kvm_x86_ops->get_page_enc_bitmap)
+			r = kvm_x86_ops->get_page_enc_bitmap(kvm, &bitmap);
+		break;
+	}
 	default:
 		r = -ENOTTY;
 	}
include/uapi/linux/kvm.h +12 -0
index 4e80c57a3182..9377b26c5f4e 100644
@@ -500,6 +500,16 @@ struct kvm_dirty_log {
 	};
 };
 
+/* for KVM_GET_PAGE_ENC_BITMAP */
+struct kvm_page_enc_bitmap {
+	__u64 start_gfn;
+	__u64 num_pages;
+	union {
+		void __user *enc_bitmap; /* one bit per page */
+		__u64 padding2;
+	};
+};
+
 /* for KVM_CLEAR_DIRTY_LOG */
 struct kvm_clear_dirty_log {
 	__u32 slot;
@@ -1478,6 +1488,8 @@ struct kvm_enc_region {
 #define KVM_S390_NORMAL_RESET	_IO(KVMIO,   0xc3)
 #define KVM_S390_CLEAR_RESET	_IO(KVMIO,   0xc4)
 
+#define KVM_GET_PAGE_ENC_BITMAP	_IOW(KVMIO, 0xc2, struct kvm_page_enc_bitmap)
+
 /* Secure Encrypted Virtualization command */
 enum sev_cmd_id {
 	/* Guest initialization commands */
Signature · 1 line
2.17.1
#21 lore raw

Re: [PATCH 09/12] KVM: x86: Introduce KVM_GET_PAGE_ENC_BITMAP ioctl

Quoted 5 lines · Ashish Kalra

From: Brijesh Singh <[email protected]>

The ioctl can be used to retrieve page encryption bitmap for a given gfn range.

Quoted 13 lines
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]>

This patch does not apply to upstream Linux version 5.5.6.

<snip>
Applying: KVM: x86: Introduce KVM_GET_PAGE_ENC_BITMAP ioctl
error: patch failed: Documentation/virt/kvm/api.txt:4213
error: Documentation/virt/kvm/api.txt: patch does not apply
error: patch failed: include/uapi/linux/kvm.h:1478
error: include/uapi/linux/kvm.h: patch does not apply Patch failed at 0009 KVM: x86: Introduce KVM_GET_PAGE_ENC_BITMAP ioctl <snip>

Which kernel version does this patch series apply to, cleanly?

Thanks,

Venu

Quoted 176 lines
---
 Documentation/virt/kvm/api.txt  | 27 +++++++++++++++++++++
 arch/x86/include/asm/kvm_host.h |  2 ++
 arch/x86/kvm/svm.c              | 43 +++++++++++++++++++++++++++++++++
 arch/x86/kvm/x86.c              | 12 +++++++++
 include/uapi/linux/kvm.h        | 12 +++++++++
 5 files changed, 96 insertions(+)
Documentation/virt/kvm/api.txt +27 -0
index c6e1ce5d40de..053aecfabe74 100644
@@ -4213,6 +4213,33 @@ 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.120 KVM_GET_PAGE_ENC_BITMAP (vm ioctl)
+
+Capability: basic
+Architectures: x86
+Type: vm ioctl
+Parameters: struct kvm_page_enc_bitmap (in/out)
+Returns: 0 on success, -1 on error
+
+/* for KVM_GET_PAGE_ENC_BITMAP */
+struct kvm_page_enc_bitmap {
+	__u64 start_gfn;
+	__u64 num_pages;
+	union {
+		void __user *enc_bitmap; /* one bit per page */
+		__u64 padding2;
+	};
+};
+
+The encrypted VMs have concept of private and shared pages. The private
+page is encrypted with the guest-specific key, while shared page may
+be encrypted with the hypervisor key. The KVM_GET_PAGE_ENC_BITMAP can
+be used to get the bitmap indicating whether the guest page is private
+or shared. The bitmap can be used during the guest migration, if the page
+is private then userspace need to use SEV migration commands to transmit
+the page.
+
+
 5. The kvm_run structure
 ------------------------
 
arch/x86/include/asm/kvm_host.h +2 -0
index 4ae7293033b2..a6882c5214b4 100644
@@ -1258,6 +1258,8 @@ struct kvm_x86_ops {
 	int (*enable_direct_tlbflush)(struct kvm_vcpu *vcpu);
 	int (*page_enc_status_hc)(struct kvm *kvm, unsigned long gpa,
 				  unsigned long sz, unsigned long mode);
+	int (*get_page_enc_bitmap)(struct kvm *kvm,
+				struct kvm_page_enc_bitmap *bmap);
 };
 
 struct kvm_arch_async_pf {
arch/x86/kvm/svm.c +43 -0
index f09791109075..f1c8806a97c6 100644
@@ -7673,6 +7673,48 @@ static int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa,
 	return ret;
 }
 
+static int svm_get_page_enc_bitmap(struct kvm *kvm,
+				   struct kvm_page_enc_bitmap *bmap)
+{
+	struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
+	unsigned long gfn_start, gfn_end;
+	unsigned long *bitmap;
+	unsigned long sz, i;
+	int ret;
+
+	if (!sev_guest(kvm))
+		return -ENOTTY;
+
+	gfn_start = bmap->start_gfn;
+	gfn_end = gfn_start + bmap->num_pages;
+
+	sz = ALIGN(bmap->num_pages, BITS_PER_LONG) / 8;
+	bitmap = kmalloc(sz, GFP_KERNEL);
+	if (!bitmap)
+		return -ENOMEM;
+
+	/* by default all pages are marked encrypted */
+	memset(bitmap, 0xff, sz);
+
+	mutex_lock(&kvm->lock);
+	if (sev->page_enc_bmap) {
+		i = gfn_start;
+		for_each_clear_bit_from(i, sev->page_enc_bmap,
+				      min(sev->page_enc_bmap_size, gfn_end))
+			clear_bit(i - gfn_start, bitmap);
+	}
+	mutex_unlock(&kvm->lock);
+
+	ret = -EFAULT;
+	if (copy_to_user(bmap->enc_bitmap, bitmap, sz))
+		goto out;
+
+	ret = 0;
+out:
+	kfree(bitmap);
+	return ret;
+}
+
 static int svm_mem_enc_op(struct kvm *kvm, void __user *argp)
 {
 	struct kvm_sev_cmd sev_cmd;
@@ -8066,6 +8108,7 @@ static struct kvm_x86_ops svm_x86_ops __ro_after_init = {
 	.apic_init_signal_blocked = svm_apic_init_signal_blocked,
 
 	.page_enc_status_hc = svm_page_enc_status_hc,
+	.get_page_enc_bitmap = svm_get_page_enc_bitmap,
 };
 
 static int __init svm_init(void)
arch/x86/kvm/x86.c +12 -0
index 298627fa3d39..e955f886ee17 100644
@@ -5213,6 +5213,18 @@ long kvm_arch_vm_ioctl(struct file *filp,
 	case KVM_SET_PMU_EVENT_FILTER:
 		r = kvm_vm_ioctl_set_pmu_event_filter(kvm, argp);
 		break;
+	case KVM_GET_PAGE_ENC_BITMAP: {
+		struct kvm_page_enc_bitmap bitmap;
+
+		r = -EFAULT;
+		if (copy_from_user(&bitmap, argp, sizeof(bitmap)))
+			goto out;
+
+		r = -ENOTTY;
+		if (kvm_x86_ops->get_page_enc_bitmap)
+			r = kvm_x86_ops->get_page_enc_bitmap(kvm, &bitmap);
+		break;
+	}
 	default:
 		r = -ENOTTY;
 	}
include/uapi/linux/kvm.h +12 -0
index 4e80c57a3182..9377b26c5f4e 100644
@@ -500,6 +500,16 @@ struct kvm_dirty_log {
 	};
 };
 
+/* for KVM_GET_PAGE_ENC_BITMAP */
+struct kvm_page_enc_bitmap {
+	__u64 start_gfn;
+	__u64 num_pages;
+	union {
+		void __user *enc_bitmap; /* one bit per page */
+		__u64 padding2;
+	};
+};
+
 /* for KVM_CLEAR_DIRTY_LOG */
 struct kvm_clear_dirty_log {
 	__u32 slot;
@@ -1478,6 +1488,8 @@ struct kvm_enc_region {
 #define KVM_S390_NORMAL_RESET	_IO(KVMIO,   0xc3)
 #define KVM_S390_CLEAR_RESET	_IO(KVMIO,   0xc4)
 
+#define KVM_GET_PAGE_ENC_BITMAP	_IOW(KVMIO, 0xc2, struct kvm_page_enc_bitmap)
+
 /* Secure Encrypted Virtualization command */
 enum sev_cmd_id {
 	/* Guest initialization commands */
Signature · 1 line
2.17.1
#22 lore raw

Re: [PATCH 09/12] KVM: x86: Introduce KVM_GET_PAGE_ENC_BITMAP ioctl

Quoted 31 lines · Venu Busireddy
Quoted 5 lines · Ashish Kalra

From: Brijesh Singh <[email protected]>

The ioctl can be used to retrieve page encryption bitmap for a given gfn range.

Quoted 13 lines
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]>

This patch does not apply to upstream Linux version 5.5.6.

<snip>
Applying: KVM: x86: Introduce KVM_GET_PAGE_ENC_BITMAP ioctl
error: patch failed: Documentation/virt/kvm/api.txt:4213
error: Documentation/virt/kvm/api.txt: patch does not apply
error: patch failed: include/uapi/linux/kvm.h:1478
error: include/uapi/linux/kvm.h: patch does not apply Patch failed at 0009 KVM: x86: Introduce KVM_GET_PAGE_ENC_BITMAP ioctl <snip>

Quoted 1 line

Which kernel version does this patch series apply to, cleanly?

Tried git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git.
With that, patch 08/12 fails with the top of that tree, as well as tag v5.6-rc3.

Quoted 180 lines

Thanks,

Venu

Quoted 176 lines
---
 Documentation/virt/kvm/api.txt  | 27 +++++++++++++++++++++
 arch/x86/include/asm/kvm_host.h |  2 ++
 arch/x86/kvm/svm.c              | 43 +++++++++++++++++++++++++++++++++
 arch/x86/kvm/x86.c              | 12 +++++++++
 include/uapi/linux/kvm.h        | 12 +++++++++
 5 files changed, 96 insertions(+)
Documentation/virt/kvm/api.txt +27 -0
index c6e1ce5d40de..053aecfabe74 100644
@@ -4213,6 +4213,33 @@ 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.120 KVM_GET_PAGE_ENC_BITMAP (vm ioctl)
+
+Capability: basic
+Architectures: x86
+Type: vm ioctl
+Parameters: struct kvm_page_enc_bitmap (in/out)
+Returns: 0 on success, -1 on error
+
+/* for KVM_GET_PAGE_ENC_BITMAP */
+struct kvm_page_enc_bitmap {
+	__u64 start_gfn;
+	__u64 num_pages;
+	union {
+		void __user *enc_bitmap; /* one bit per page */
+		__u64 padding2;
+	};
+};
+
+The encrypted VMs have concept of private and shared pages. The private
+page is encrypted with the guest-specific key, while shared page may
+be encrypted with the hypervisor key. The KVM_GET_PAGE_ENC_BITMAP can
+be used to get the bitmap indicating whether the guest page is private
+or shared. The bitmap can be used during the guest migration, if the page
+is private then userspace need to use SEV migration commands to transmit
+the page.
+
+
 5. The kvm_run structure
 ------------------------
 
arch/x86/include/asm/kvm_host.h +2 -0
index 4ae7293033b2..a6882c5214b4 100644
@@ -1258,6 +1258,8 @@ struct kvm_x86_ops {
 	int (*enable_direct_tlbflush)(struct kvm_vcpu *vcpu);
 	int (*page_enc_status_hc)(struct kvm *kvm, unsigned long gpa,
 				  unsigned long sz, unsigned long mode);
+	int (*get_page_enc_bitmap)(struct kvm *kvm,
+				struct kvm_page_enc_bitmap *bmap);
 };
 
 struct kvm_arch_async_pf {
arch/x86/kvm/svm.c +43 -0
index f09791109075..f1c8806a97c6 100644
@@ -7673,6 +7673,48 @@ static int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa,
 	return ret;
 }
 
+static int svm_get_page_enc_bitmap(struct kvm *kvm,
+				   struct kvm_page_enc_bitmap *bmap)
+{
+	struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
+	unsigned long gfn_start, gfn_end;
+	unsigned long *bitmap;
+	unsigned long sz, i;
+	int ret;
+
+	if (!sev_guest(kvm))
+		return -ENOTTY;
+
+	gfn_start = bmap->start_gfn;
+	gfn_end = gfn_start + bmap->num_pages;
+
+	sz = ALIGN(bmap->num_pages, BITS_PER_LONG) / 8;
+	bitmap = kmalloc(sz, GFP_KERNEL);
+	if (!bitmap)
+		return -ENOMEM;
+
+	/* by default all pages are marked encrypted */
+	memset(bitmap, 0xff, sz);
+
+	mutex_lock(&kvm->lock);
+	if (sev->page_enc_bmap) {
+		i = gfn_start;
+		for_each_clear_bit_from(i, sev->page_enc_bmap,
+				      min(sev->page_enc_bmap_size, gfn_end))
+			clear_bit(i - gfn_start, bitmap);
+	}
+	mutex_unlock(&kvm->lock);
+
+	ret = -EFAULT;
+	if (copy_to_user(bmap->enc_bitmap, bitmap, sz))
+		goto out;
+
+	ret = 0;
+out:
+	kfree(bitmap);
+	return ret;
+}
+
 static int svm_mem_enc_op(struct kvm *kvm, void __user *argp)
 {
 	struct kvm_sev_cmd sev_cmd;
@@ -8066,6 +8108,7 @@ static struct kvm_x86_ops svm_x86_ops __ro_after_init = {
 	.apic_init_signal_blocked = svm_apic_init_signal_blocked,
 
 	.page_enc_status_hc = svm_page_enc_status_hc,
+	.get_page_enc_bitmap = svm_get_page_enc_bitmap,
 };
 
 static int __init svm_init(void)
arch/x86/kvm/x86.c +12 -0
index 298627fa3d39..e955f886ee17 100644
@@ -5213,6 +5213,18 @@ long kvm_arch_vm_ioctl(struct file *filp,
 	case KVM_SET_PMU_EVENT_FILTER:
 		r = kvm_vm_ioctl_set_pmu_event_filter(kvm, argp);
 		break;
+	case KVM_GET_PAGE_ENC_BITMAP: {
+		struct kvm_page_enc_bitmap bitmap;
+
+		r = -EFAULT;
+		if (copy_from_user(&bitmap, argp, sizeof(bitmap)))
+			goto out;
+
+		r = -ENOTTY;
+		if (kvm_x86_ops->get_page_enc_bitmap)
+			r = kvm_x86_ops->get_page_enc_bitmap(kvm, &bitmap);
+		break;
+	}
 	default:
 		r = -ENOTTY;
 	}
include/uapi/linux/kvm.h +12 -0
index 4e80c57a3182..9377b26c5f4e 100644
@@ -500,6 +500,16 @@ struct kvm_dirty_log {
 	};
 };
 
+/* for KVM_GET_PAGE_ENC_BITMAP */
+struct kvm_page_enc_bitmap {
+	__u64 start_gfn;
+	__u64 num_pages;
+	union {
+		void __user *enc_bitmap; /* one bit per page */
+		__u64 padding2;
+	};
+};
+
 /* for KVM_CLEAR_DIRTY_LOG */
 struct kvm_clear_dirty_log {
 	__u32 slot;
@@ -1478,6 +1488,8 @@ struct kvm_enc_region {
 #define KVM_S390_NORMAL_RESET	_IO(KVMIO,   0xc3)
 #define KVM_S390_CLEAR_RESET	_IO(KVMIO,   0xc4)
 
+#define KVM_GET_PAGE_ENC_BITMAP	_IOW(KVMIO, 0xc2, struct kvm_page_enc_bitmap)
+
 /* Secure Encrypted Virtualization command */
 enum sev_cmd_id {
 	/* Guest initialization commands */
Signature · 1 line
2.17.1
#23 lore raw

Re: [PATCH 09/12] KVM: x86: Introduce KVM_GET_PAGE_ENC_BITMAP ioctl

The patch series should apply to tag v5.6-rc1.

I will be posting the v5 of this patchset next week, which should apply on top of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git.

Thanks,
Ashish

Quoted 218 lines · Venu Busireddy
Quoted 31 lines · Venu Busireddy
Quoted 5 lines · Ashish Kalra

From: Brijesh Singh <[email protected]>

The ioctl can be used to retrieve page encryption bitmap for a given gfn range.

Quoted 13 lines
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]>

This patch does not apply to upstream Linux version 5.5.6.

<snip>
Applying: KVM: x86: Introduce KVM_GET_PAGE_ENC_BITMAP ioctl
error: patch failed: Documentation/virt/kvm/api.txt:4213
error: Documentation/virt/kvm/api.txt: patch does not apply
error: patch failed: include/uapi/linux/kvm.h:1478
error: include/uapi/linux/kvm.h: patch does not apply Patch failed at 0009 KVM: x86: Introduce KVM_GET_PAGE_ENC_BITMAP ioctl <snip>

Quoted 1 line

Which kernel version does this patch series apply to, cleanly?

Tried git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git.
With that, patch 08/12 fails with the top of that tree, as well as tag v5.6-rc3.

Quoted 180 lines

Thanks,

Venu

Quoted 176 lines
---
 Documentation/virt/kvm/api.txt  | 27 +++++++++++++++++++++
 arch/x86/include/asm/kvm_host.h |  2 ++
 arch/x86/kvm/svm.c              | 43 +++++++++++++++++++++++++++++++++
 arch/x86/kvm/x86.c              | 12 +++++++++
 include/uapi/linux/kvm.h        | 12 +++++++++
 5 files changed, 96 insertions(+)
Documentation/virt/kvm/api.txt +27 -0
index c6e1ce5d40de..053aecfabe74 100644
@@ -4213,6 +4213,33 @@ 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.120 KVM_GET_PAGE_ENC_BITMAP (vm ioctl)
+
+Capability: basic
+Architectures: x86
+Type: vm ioctl
+Parameters: struct kvm_page_enc_bitmap (in/out)
+Returns: 0 on success, -1 on error
+
+/* for KVM_GET_PAGE_ENC_BITMAP */
+struct kvm_page_enc_bitmap {
+	__u64 start_gfn;
+	__u64 num_pages;
+	union {
+		void __user *enc_bitmap; /* one bit per page */
+		__u64 padding2;
+	};
+};
+
+The encrypted VMs have concept of private and shared pages. The private
+page is encrypted with the guest-specific key, while shared page may
+be encrypted with the hypervisor key. The KVM_GET_PAGE_ENC_BITMAP can
+be used to get the bitmap indicating whether the guest page is private
+or shared. The bitmap can be used during the guest migration, if the page
+is private then userspace need to use SEV migration commands to transmit
+the page.
+
+
 5. The kvm_run structure
 ------------------------
 
arch/x86/include/asm/kvm_host.h +2 -0
index 4ae7293033b2..a6882c5214b4 100644
@@ -1258,6 +1258,8 @@ struct kvm_x86_ops {
 	int (*enable_direct_tlbflush)(struct kvm_vcpu *vcpu);
 	int (*page_enc_status_hc)(struct kvm *kvm, unsigned long gpa,
 				  unsigned long sz, unsigned long mode);
+	int (*get_page_enc_bitmap)(struct kvm *kvm,
+				struct kvm_page_enc_bitmap *bmap);
 };
 
 struct kvm_arch_async_pf {
arch/x86/kvm/svm.c +43 -0
index f09791109075..f1c8806a97c6 100644
@@ -7673,6 +7673,48 @@ static int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa,
 	return ret;
 }
 
+static int svm_get_page_enc_bitmap(struct kvm *kvm,
+				   struct kvm_page_enc_bitmap *bmap)
+{
+	struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
+	unsigned long gfn_start, gfn_end;
+	unsigned long *bitmap;
+	unsigned long sz, i;
+	int ret;
+
+	if (!sev_guest(kvm))
+		return -ENOTTY;
+
+	gfn_start = bmap->start_gfn;
+	gfn_end = gfn_start + bmap->num_pages;
+
+	sz = ALIGN(bmap->num_pages, BITS_PER_LONG) / 8;
+	bitmap = kmalloc(sz, GFP_KERNEL);
+	if (!bitmap)
+		return -ENOMEM;
+
+	/* by default all pages are marked encrypted */
+	memset(bitmap, 0xff, sz);
+
+	mutex_lock(&kvm->lock);
+	if (sev->page_enc_bmap) {
+		i = gfn_start;
+		for_each_clear_bit_from(i, sev->page_enc_bmap,
+				      min(sev->page_enc_bmap_size, gfn_end))
+			clear_bit(i - gfn_start, bitmap);
+	}
+	mutex_unlock(&kvm->lock);
+
+	ret = -EFAULT;
+	if (copy_to_user(bmap->enc_bitmap, bitmap, sz))
+		goto out;
+
+	ret = 0;
+out:
+	kfree(bitmap);
+	return ret;
+}
+
 static int svm_mem_enc_op(struct kvm *kvm, void __user *argp)
 {
 	struct kvm_sev_cmd sev_cmd;
@@ -8066,6 +8108,7 @@ static struct kvm_x86_ops svm_x86_ops __ro_after_init = {
 	.apic_init_signal_blocked = svm_apic_init_signal_blocked,
 
 	.page_enc_status_hc = svm_page_enc_status_hc,
+	.get_page_enc_bitmap = svm_get_page_enc_bitmap,
 };
 
 static int __init svm_init(void)
arch/x86/kvm/x86.c +12 -0
index 298627fa3d39..e955f886ee17 100644
@@ -5213,6 +5213,18 @@ long kvm_arch_vm_ioctl(struct file *filp,
 	case KVM_SET_PMU_EVENT_FILTER:
 		r = kvm_vm_ioctl_set_pmu_event_filter(kvm, argp);
 		break;
+	case KVM_GET_PAGE_ENC_BITMAP: {
+		struct kvm_page_enc_bitmap bitmap;
+
+		r = -EFAULT;
+		if (copy_from_user(&bitmap, argp, sizeof(bitmap)))
+			goto out;
+
+		r = -ENOTTY;
+		if (kvm_x86_ops->get_page_enc_bitmap)
+			r = kvm_x86_ops->get_page_enc_bitmap(kvm, &bitmap);
+		break;
+	}
 	default:
 		r = -ENOTTY;
 	}
include/uapi/linux/kvm.h +12 -0
index 4e80c57a3182..9377b26c5f4e 100644
@@ -500,6 +500,16 @@ struct kvm_dirty_log {
 	};
 };
 
+/* for KVM_GET_PAGE_ENC_BITMAP */
+struct kvm_page_enc_bitmap {
+	__u64 start_gfn;
+	__u64 num_pages;
+	union {
+		void __user *enc_bitmap; /* one bit per page */
+		__u64 padding2;
+	};
+};
+
 /* for KVM_CLEAR_DIRTY_LOG */
 struct kvm_clear_dirty_log {
 	__u32 slot;
@@ -1478,6 +1488,8 @@ struct kvm_enc_region {
 #define KVM_S390_NORMAL_RESET	_IO(KVMIO,   0xc3)
 #define KVM_S390_CLEAR_RESET	_IO(KVMIO,   0xc4)
 
+#define KVM_GET_PAGE_ENC_BITMAP	_IOW(KVMIO, 0xc2, struct kvm_page_enc_bitmap)
+
 /* Secure Encrypted Virtualization command */
 enum sev_cmd_id {
 	/* Guest initialization commands */
Signature · 1 line
2.17.1
#24 lore raw

PATCH 10/12 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 need 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       |  6 +++
 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, 72 insertions(+), 1 deletion(-)
arch/x86/include/asm/paravirt.h +6 -0
index 86e7317eb31f..407104613b06 100644
@@ -78,6 +78,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)
 {
arch/x86/include/asm/paravirt_types.h +2 -0
index 84812964d3dd..5ff03ac9a5f8 100644
@@ -211,6 +211,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 789f5e4f89de..8953447f327c 100644
@@ -362,6 +362,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 c4aedd00c1ba..86b7804129fc 100644
@@ -26,6 +26,7 @@
 #include <asm/proto.h>
 #include <asm/memtype.h>
 #include <asm/set_memory.h>
+#include <asm/paravirt.h>
 
 #include "../mm_internal.h"
 
@@ -1987,6 +1988,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
#25 lore raw

Re: [PATCH 10/12] mm: x86: Invoke hypercall when page encryption status is changed

Quoted 6 lines · Ashish Kalra

From: Brijesh Singh <[email protected]>

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

What happens if the guest memory status doesn't match what the hypervisor thinks it is? What happens if the guest gets migrated between the hypercall and the associated flushes?

#26 lore raw

Re: [PATCH 10/12] mm: x86: Invoke hypercall when page encryption status is changed

Quoted 11 lines · Andy Lutomirski
Quoted 11 lines · Ashish Kalra
Quoted 2 lines

From: Brijesh Singh <[email protected]>

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

What happens if the guest memory status doesn't match what the hypervisor thinks it is? What happens if the guest gets migrated between the hypercall and the associated flushes?

This is basically same as the dirty page tracking and logging being done during Live Migration. As with dirty page tracking and logging we maintain a page encryption bitmap in the kernel which keeps tracks of guest's page encrypted/decrypted state changes and this bitmap is sync'ed regularly from kernel to qemu and also during the live migration process, therefore any dirty pages whose encryption status will change during migration, should also have their page status updated when the page encryption bitmap is sync'ed.

Also i think that when the amount of dirty pages reach a low threshold, QEMU stops the source VM and then transfers all the remaining dirty pages, so at that point, there will also be a final sync of the page encryption bitmap, there won't be any hypercalls after this as the source VM has been stopped and the remaining VM state gets transferred.

Thanks,
Ashish

maintainer
#27 lore raw

Re: [PATCH 10/12] mm: x86: Invoke hypercall when page encryption status is changed

Quoted 23 lines · Ashish Kalra
Quoted 11 lines · Andy Lutomirski
Quoted 11 lines · Ashish Kalra
Quoted 2 lines

From: Brijesh Singh <[email protected]>

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

What happens if the guest memory status doesn't match what the hypervisor thinks it is? What happens if the guest gets migrated between the hypercall and the associated flushes?

This is basically same as the dirty page tracking and logging being done during Live Migration. As with dirty page tracking and logging we maintain a page encryption bitmap in the kernel which keeps tracks of guest's page encrypted/decrypted state changes and this bitmap is sync'ed regularly from kernel to qemu and also during the live migration process, therefore any dirty pages whose encryption status will change during migration, should also have their page status updated when the page encryption bitmap is sync'ed.

Quoted 5 lines

Also i think that when the amount of dirty pages reach a low threshold, QEMU stops the source VM and then transfers all the remaining dirty pages, so at that point, there will also be a final sync of the page encryption bitmap, there won't be any hypercalls after this as the source VM has been stopped and the remaining VM state gets transferred.

And have you ensured that, in the inevitable race when a guest gets migrated part way through an encryption state change, that no data corruption occurs?

#28 lore raw

Re: [PATCH 10/12] mm: x86: Invoke hypercall when page encryption status is changed

Quoted 30 lines · Andy Lutomirski
Quoted 23 lines · Ashish Kalra
Quoted 11 lines · Andy Lutomirski
Quoted 11 lines · Ashish Kalra
Quoted 2 lines

From: Brijesh Singh <[email protected]>

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

What happens if the guest memory status doesn't match what the hypervisor thinks it is? What happens if the guest gets migrated between the hypercall and the associated flushes?

This is basically same as the dirty page tracking and logging being done during Live Migration. As with dirty page tracking and logging we maintain a page encryption bitmap in the kernel which keeps tracks of guest's page encrypted/decrypted state changes and this bitmap is sync'ed regularly from kernel to qemu and also during the live migration process, therefore any dirty pages whose encryption status will change during migration, should also have their page status updated when the page encryption bitmap is sync'ed.

Quoted 5 lines

Also i think that when the amount of dirty pages reach a low threshold, QEMU stops the source VM and then transfers all the remaining dirty pages, so at that point, there will also be a final sync of the page encryption bitmap, there won't be any hypercalls after this as the source VM has been stopped and the remaining VM state gets transferred.

Quoted 3 lines

And have you ensured that, in the inevitable race when a guest gets migrated part way through an encryption state change, that no data corruption occurs?

We ensure that we send the page encryption state bitmap to the destination VM at migration completion and when the remaining amount of RAM/dirty pages are flushed. Also as the source VM is stopped before this flush of remaining blocks occur, so any encryption state change hypercalls would have been completed before that.

Thanks,
Ashish

#29 lore raw

Re: [PATCH 10/12] mm: x86: Invoke hypercall when page encryption status is changed

In reply to Ashish Kalra
Quoted 6 lines · Ashish Kalra

From: Brijesh Singh <[email protected]>

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

One messy aspect, which I think is fine in practice, is that this presumes that pages are either treated as encrypted or decrypted. If also done on SEV, the in-place re-encryption supported by SME would break SEV migration. Linux doesn't do this now on SEV, and I don't have an intuition for why Linux might want this, but we will need to ensure it is never done in order to ensure that migration works down the line. I don't believe the AMD manual promises this will work anyway.

Something feels a bit wasteful about having all future kernels universally announce c-bit status when SEV is enabled, even if KVM isn't listening, since it may be too old (or just not want to know). Might be worth eliding the hypercalls if you get ENOSYS back? There might be a better way of passing paravirt config metadata across than just trying and seeing if the hypercall succeeds, but I'm not super familiar with it.

#30 lore raw

Re: [PATCH 10/12] mm: x86: Invoke hypercall when page encryption status is changed

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

From: Brijesh Singh <[email protected]>

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

One messy aspect, which I think is fine in practice, is that this presumes that pages are either treated as encrypted or decrypted. If also done on SEV, the in-place re-encryption supported by SME would break SEV migration. Linux doesn't do this now on SEV, and I don't have an intuition for why Linux might want this, but we will need to ensure it is never done in order to ensure that migration works down the line. I don't believe the AMD manual promises this will work anyway.

Quoted 7 lines

Something feels a bit wasteful about having all future kernels universally announce c-bit status when SEV is enabled, even if KVM isn't listening, since it may be too old (or just not want to know). Might be worth eliding the hypercalls if you get ENOSYS back? There might be a better way of passing paravirt config metadata across than just trying and seeing if the hypercall succeeds, but I'm not super familiar with it.

I actually think this should be a hard requirement to merge this. The host needs to tell the guest that it supports this particular migration strategy and the guest needs to tell the host that it is using it. And the guest needs a way to tell the host that it’s *not* using it right now due to kexec, for example.

I’m still uneasy about a guest being migrated in the window where the hypercall tracking and the page encryption bit don’t match. I guess maybe corruption in this window doesn’t matter?

#31 lore raw

Re: [PATCH 10/12] mm: x86: Invoke hypercall when page encryption status is changed

Quoted 30 lines · Andy Lutomirski
Quoted 22 lines · Steve Rutherford
Quoted 6 lines · Ashish Kalra

From: Brijesh Singh <[email protected]>

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

One messy aspect, which I think is fine in practice, is that this presumes that pages are either treated as encrypted or decrypted. If also done on SEV, the in-place re-encryption supported by SME would break SEV migration. Linux doesn't do this now on SEV, and I don't have an intuition for why Linux might want this, but we will need to ensure it is never done in order to ensure that migration works down the line. I don't believe the AMD manual promises this will work anyway.

Quoted 7 lines

Something feels a bit wasteful about having all future kernels universally announce c-bit status when SEV is enabled, even if KVM isn't listening, since it may be too old (or just not want to know). Might be worth eliding the hypercalls if you get ENOSYS back? There might be a better way of passing paravirt config metadata across than just trying and seeing if the hypercall succeeds, but I'm not super familiar with it.

Quoted 3 lines

I actually think this should be a hard requirement to merge this. The host needs to tell the guest that it supports this particular migration strategy and the guest needs to tell the host that it is using it. And the guest needs a way to tell the host that it’s *not* using it right now due to kexec, for example.

I’m still uneasy about a guest being migrated in the window where the hypercall tracking and the page encryption bit don’t match. I guess maybe corruption in this window doesn’t matter?

It does matter, since you don't want to accidentally clear the dirty bit when you are migrating the page from the wrong perspective. Treating pages with dirty c-bits as dirty pages should solve this problem. It's probably reasonable to expect userspace to handle this? Downside is that you would then need ~3 copies of the c-bit tracking
buffer: one as the kernel version, one as the "old" usermode version, and one as the "current" usermode version (which is smaller, since you can fetch smaller sections than the full buffer). The kernel could probably directly twiddle the dirty tracking bits and avoid the extra userspace version, but this doesn't seem required.

That said, this does balloon the c-bit tracking overhead. Tracking 100GB of guest pages requires 3MB per instance of these buffers, which isn't that bad but also isn't free (assuming my back of the envelope math is right).

#32 lore raw

Re: [PATCH 10/12] mm: x86: Invoke hypercall when page encryption status is changed

In reply to Andy Lutomirski
Quoted 29 lines · Andy Lutomirski
Quoted 21 lines · Steve Rutherford
Quoted 6 lines · Ashish Kalra

From: Brijesh Singh <[email protected]>

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

One messy aspect, which I think is fine in practice, is that this presumes that pages are either treated as encrypted or decrypted. If also done on SEV, the in-place re-encryption supported by SME would break SEV migration. Linux doesn't do this now on SEV, and I don't have an intuition for why Linux might want this, but we will need to ensure it is never done in order to ensure that migration works down the line. I don't believe the AMD manual promises this will work anyway.

Quoted 7 lines

Something feels a bit wasteful about having all future kernels universally announce c-bit status when SEV is enabled, even if KVM isn't listening, since it may be too old (or just not want to know). Might be worth eliding the hypercalls if you get ENOSYS back? There might be a better way of passing paravirt config metadata across than just trying and seeing if the hypercall succeeds, but I'm not super familiar with it.

Quoted 4 lines

I actually think this should be a hard requirement to merge this. The host needs to tell the guest that it supports this particular migration strategy and the guest needs to tell the host that it is using it. And the guest needs a way to tell the host that it’s *not* using it right now due to kexec, for example.

I’m still uneasy about a guest being migrated in the window where the hypercall tracking and the page encryption bit don’t match. I guess maybe corruption in this window doesn’t matter?

I don't think there is a corruption issue here. Let's consider the below case:

1) A page is transmitted as C=1 (encrypted)

2) During the migration window, the page encryption bit is changed to C=0 (decrypted)

3) #2 will cause a change in page table memory, thus dirty memory the tracker will create retransmission of the page table memory.

4) The page itself will not be re-transmitted because there was no change to the content of the page.

On destination, the read from the page will get the ciphertext.

The encryption bit change in the page table is used on the next access. The user of the page needs to ensure that data is written with the correct encryption bit before reading.

thanks

#33 lore raw

Re: [PATCH 10/12] mm: x86: Invoke hypercall when page encryption status is changed

Quoted 52 lines · Brijesh Singh
Quoted 29 lines · Andy Lutomirski
Quoted 21 lines · Steve Rutherford
Quoted 6 lines · Ashish Kalra

From: Brijesh Singh <[email protected]>

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

One messy aspect, which I think is fine in practice, is that this presumes that pages are either treated as encrypted or decrypted. If also done on SEV, the in-place re-encryption supported by SME would break SEV migration. Linux doesn't do this now on SEV, and I don't have an intuition for why Linux might want this, but we will need to ensure it is never done in order to ensure that migration works down the line. I don't believe the AMD manual promises this will work anyway.

Quoted 7 lines

Something feels a bit wasteful about having all future kernels universally announce c-bit status when SEV is enabled, even if KVM isn't listening, since it may be too old (or just not want to know). Might be worth eliding the hypercalls if you get ENOSYS back? There might be a better way of passing paravirt config metadata across than just trying and seeing if the hypercall succeeds, but I'm not super familiar with it.

Quoted 4 lines

I actually think this should be a hard requirement to merge this. The host needs to tell the guest that it supports this particular migration strategy and the guest needs to tell the host that it is using it. And the guest needs a way to tell the host that it’s *not* using it right now due to kexec, for example.

I’m still uneasy about a guest being migrated in the window where the hypercall tracking and the page encryption bit don’t match. I guess maybe corruption in this window doesn’t matter?

I don't think there is a corruption issue here. Let's consider the below case:

1) A page is transmitted as C=1 (encrypted)

2) During the migration window, the page encryption bit is changed to C=0 (decrypted)

3) #2 will cause a change in page table memory, thus dirty memory the tracker will create retransmission of the page table memory.

4) The page itself will not be re-transmitted because there was no change to the content of the page.

Quoted 7 lines

On destination, the read from the page will get the ciphertext.

The encryption bit change in the page table is used on the next access. The user of the page needs to ensure that data is written with the correct encryption bit before reading.

thanks

I think the issue results from a slightly different perspective than the one you are using. I think the situation Andy is interested in is when a c-bit change and a write happen close in time. There are five events, and the ordering matters:
1) Guest dirties the c-bit in the guest
2) Guest dirties the page
3) Host userspace observes the c-bit logs
4) Host userspace observes the page dirty logs
5) Host transmits the page

If these are reordered to:
3) Host userspace observes the c-bit logs
1) Guest dirties the c-bit in the guest
2) Guest dirties the page
4) Host userspace observes the page dirty logs
5) Host transmits the page (from the wrong c-bit perspective!)

Then the host will transmit a page with the wrong c-bit status and clear the dirty bit for that page. If the guest page is not retransmitted incidentally later, then this page will be corrupted.

If you treat pages with dirty c-bits as dirty pages, then you will check the c-bit logs later and observe the dirty c-bit and retransmit. There might be some cleverness around enforcing that you always fetch the c-bit logs after fetching the dirty logs, but I haven't convinced myself that this works yet. I think it might, since then the c-bits are at least as fresh as the dirty bits.

The main uncertainty that comes to mind for that strategy is if, on multi-vCPU VMs, the page dirtying event (from the new c-bit perspective) and the c-bit status change hypercall can themselves race. If a write from the new c-bit perspective can arrive before the c-bit status change arrives in the c-bit logs, we will need to treat pages with dirty c-bits as dirty pages.

Note that I do agree that if the c-bit status flips, and no one writes to the page, it doesn't really matter if you retransmit that page. If a guest wants to read nonsense, it can.

#34 lore raw

Re: [PATCH 10/12] mm: x86: Invoke hypercall when page encryption status is changed

Quoted 83 lines · Steve Rutherford
Quoted 52 lines · Brijesh Singh
Quoted 29 lines · Andy Lutomirski
Quoted 21 lines · Steve Rutherford
Quoted 6 lines · Ashish Kalra

From: Brijesh Singh <[email protected]>

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

One messy aspect, which I think is fine in practice, is that this presumes that pages are either treated as encrypted or decrypted. If also done on SEV, the in-place re-encryption supported by SME would break SEV migration. Linux doesn't do this now on SEV, and I don't have an intuition for why Linux might want this, but we will need to ensure it is never done in order to ensure that migration works down the line. I don't believe the AMD manual promises this will work anyway.

Quoted 7 lines

Something feels a bit wasteful about having all future kernels universally announce c-bit status when SEV is enabled, even if KVM isn't listening, since it may be too old (or just not want to know). Might be worth eliding the hypercalls if you get ENOSYS back? There might be a better way of passing paravirt config metadata across than just trying and seeing if the hypercall succeeds, but I'm not super familiar with it.

Quoted 4 lines

I actually think this should be a hard requirement to merge this. The host needs to tell the guest that it supports this particular migration strategy and the guest needs to tell the host that it is using it. And the guest needs a way to tell the host that it’s *not* using it right now due to kexec, for example.

I’m still uneasy about a guest being migrated in the window where the hypercall tracking and the page encryption bit don’t match. I guess maybe corruption in this window doesn’t matter?

I don't think there is a corruption issue here. Let's consider the below case:

1) A page is transmitted as C=1 (encrypted)

2) During the migration window, the page encryption bit is changed to C=0 (decrypted)

3) #2 will cause a change in page table memory, thus dirty memory the tracker will create retransmission of the page table memory.

4) The page itself will not be re-transmitted because there was no change to the content of the page.

Quoted 7 lines

On destination, the read from the page will get the ciphertext.

The encryption bit change in the page table is used on the next access. The user of the page needs to ensure that data is written with the correct encryption bit before reading.

thanks

I think the issue results from a slightly different perspective than the one you are using. I think the situation Andy is interested in is when a c-bit change and a write happen close in time. There are five events, and the ordering matters:
1) Guest dirties the c-bit in the guest
2) Guest dirties the page
3) Host userspace observes the c-bit logs
4) Host userspace observes the page dirty logs
5) Host transmits the page

If these are reordered to:
3) Host userspace observes the c-bit logs
1) Guest dirties the c-bit in the guest
2) Guest dirties the page
4) Host userspace observes the page dirty logs
5) Host transmits the page (from the wrong c-bit perspective!)

Then the host will transmit a page with the wrong c-bit status and clear the dirty bit for that page. If the guest page is not retransmitted incidentally later, then this page will be corrupted.

Quoted 7 lines

If you treat pages with dirty c-bits as dirty pages, then you will check the c-bit logs later and observe the dirty c-bit and retransmit. There might be some cleverness around enforcing that you always fetch the c-bit logs after fetching the dirty logs, but I haven't convinced myself that this works yet. I think it might, since then the c-bits are at least as fresh as the dirty bits.

Unlike the dirty log, the c-bit log maintains the complete state. So, I think it is the Host userspace responsibility to ensure that it either keeps track of any c-bit log changes since it last sync'ed. During the migration, after pausing the guest it can get the recent c-bit log and compare if something has changed since it last sync'ed. If so, then retransmit the page with new c-bit state.

Quoted 7 lines

The main uncertainty that comes to mind for that strategy is if, on multi-vCPU VMs, the page dirtying event (from the new c-bit perspective) and the c-bit status change hypercall can themselves race. If a write from the new c-bit perspective can arrive before the c-bit status change arrives in the c-bit logs, we will need to treat pages with dirty c-bits as dirty pages.

I believe if host userspace tracks the changes in the c-bit log since it last synced then this problem can be avoided. Do you think we should consider tracking the last sync changes in KVM or let the host userspace handle it.

Quoted 4 lines

Note that I do agree that if the c-bit status flips, and no one writes to the page, it doesn't really matter if you retransmit that page. If a guest wants to read nonsense, it can.

#35 lore raw

Re: [PATCH 10/12] mm: x86: Invoke hypercall when page encryption status is changed

Quoted 110 lines · Brijesh Singh
Quoted 83 lines · Steve Rutherford
Quoted 52 lines · Brijesh Singh
Quoted 29 lines · Andy Lutomirski
Quoted 21 lines · Steve Rutherford
Quoted 6 lines · Ashish Kalra

From: Brijesh Singh <[email protected]>

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

One messy aspect, which I think is fine in practice, is that this presumes that pages are either treated as encrypted or decrypted. If also done on SEV, the in-place re-encryption supported by SME would break SEV migration. Linux doesn't do this now on SEV, and I don't have an intuition for why Linux might want this, but we will need to ensure it is never done in order to ensure that migration works down the line. I don't believe the AMD manual promises this will work anyway.

Quoted 7 lines

Something feels a bit wasteful about having all future kernels universally announce c-bit status when SEV is enabled, even if KVM isn't listening, since it may be too old (or just not want to know). Might be worth eliding the hypercalls if you get ENOSYS back? There might be a better way of passing paravirt config metadata across than just trying and seeing if the hypercall succeeds, but I'm not super familiar with it.

Quoted 4 lines

I actually think this should be a hard requirement to merge this. The host needs to tell the guest that it supports this particular migration strategy and the guest needs to tell the host that it is using it. And the guest needs a way to tell the host that it’s *not* using it right now due to kexec, for example.

I’m still uneasy about a guest being migrated in the window where the hypercall tracking and the page encryption bit don’t match. I guess maybe corruption in this window doesn’t matter?

I don't think there is a corruption issue here. Let's consider the below case:

1) A page is transmitted as C=1 (encrypted)

2) During the migration window, the page encryption bit is changed to C=0 (decrypted)

3) #2 will cause a change in page table memory, thus dirty memory the tracker will create retransmission of the page table memory.

4) The page itself will not be re-transmitted because there was no change to the content of the page.

Quoted 7 lines

On destination, the read from the page will get the ciphertext.

The encryption bit change in the page table is used on the next access. The user of the page needs to ensure that data is written with the correct encryption bit before reading.

thanks

I think the issue results from a slightly different perspective than the one you are using. I think the situation Andy is interested in is when a c-bit change and a write happen close in time. There are five events, and the ordering matters:
1) Guest dirties the c-bit in the guest
2) Guest dirties the page
3) Host userspace observes the c-bit logs
4) Host userspace observes the page dirty logs
5) Host transmits the page

If these are reordered to:
3) Host userspace observes the c-bit logs
1) Guest dirties the c-bit in the guest
2) Guest dirties the page
4) Host userspace observes the page dirty logs
5) Host transmits the page (from the wrong c-bit perspective!)

Then the host will transmit a page with the wrong c-bit status and clear the dirty bit for that page. If the guest page is not retransmitted incidentally later, then this page will be corrupted.

Quoted 7 lines

If you treat pages with dirty c-bits as dirty pages, then you will check the c-bit logs later and observe the dirty c-bit and retransmit. There might be some cleverness around enforcing that you always fetch the c-bit logs after fetching the dirty logs, but I haven't convinced myself that this works yet. I think it might, since then the c-bits are at least as fresh as the dirty bits.

Unlike the dirty log, the c-bit log maintains the complete state. So, I think it is the Host userspace responsibility to ensure that it either keeps track of any c-bit log changes since it last sync'ed. During the migration, after pausing the guest it can get the recent c-bit log and compare if something has changed since it last sync'ed. If so, then retransmit the page with new c-bit state.

Quoted 7 lines

The main uncertainty that comes to mind for that strategy is if, on multi-vCPU VMs, the page dirtying event (from the new c-bit perspective) and the c-bit status change hypercall can themselves race. If a write from the new c-bit perspective can arrive before the c-bit status change arrives in the c-bit logs, we will need to treat pages with dirty c-bits as dirty pages.

Quoted 4 lines

I believe if host userspace tracks the changes in the c-bit log since it last synced then this problem can be avoided. Do you think we should consider tracking the last sync changes in KVM or let the host userspace handle it.

Punting this off to userspace to handle works. If storing the old c-bit statuses in userspace becomes a memory issue (unlikely), we can fix that down the line.

Andy, are your concerns about the raceyness of c-bit tracking resolved?

#36 lore raw

Re: [PATCH 10/12] mm: x86: Invoke hypercall when page encryption status is changed

Quoted 119 lines · Steve Rutherford
Quoted 110 lines · Brijesh Singh
Quoted 87 lines · Steve Rutherford
Quoted 52 lines · Brijesh Singh
Quoted 29 lines · Andy Lutomirski
Quoted 21 lines · Steve Rutherford
Quoted 6 lines · Ashish Kalra

From: Brijesh Singh <[email protected]>

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

One messy aspect, which I think is fine in practice, is that this presumes that pages are either treated as encrypted or decrypted. If also done on SEV, the in-place re-encryption supported by SME would break SEV migration. Linux doesn't do this now on SEV, and I don't have an intuition for why Linux might want this, but we will need to ensure it is never done in order to ensure that migration works down the line. I don't believe the AMD manual promises this will work anyway.

Quoted 7 lines

Something feels a bit wasteful about having all future kernels universally announce c-bit status when SEV is enabled, even if KVM isn't listening, since it may be too old (or just not want to know). Might be worth eliding the hypercalls if you get ENOSYS back? There might be a better way of passing paravirt config metadata across than just trying and seeing if the hypercall succeeds, but I'm not super familiar with it.

Quoted 4 lines

I actually think this should be a hard requirement to merge this. The host needs to tell the guest that it supports this particular migration strategy and the guest needs to tell the host that it is using it. And the guest needs a way to tell the host that it’s *not* using it right now due to kexec, for example.

I’m still uneasy about a guest being migrated in the window where the hypercall tracking and the page encryption bit don’t match. I guess maybe corruption in this window doesn’t matter?

I don't think there is a corruption issue here. Let's consider the below case:

1) A page is transmitted as C=1 (encrypted)

2) During the migration window, the page encryption bit is changed to C=0 (decrypted)

3) #2 will cause a change in page table memory, thus dirty memory the tracker will create retransmission of the page table memory.

4) The page itself will not be re-transmitted because there was no change to the content of the page.

Quoted 7 lines

On destination, the read from the page will get the ciphertext.

The encryption bit change in the page table is used on the next access. The user of the page needs to ensure that data is written with the correct encryption bit before reading.

thanks

I think the issue results from a slightly different perspective than the one you are using. I think the situation Andy is interested in is when a c-bit change and a write happen close in time. There are five events, and the ordering matters:
1) Guest dirties the c-bit in the guest
2) Guest dirties the page
3) Host userspace observes the c-bit logs
4) Host userspace observes the page dirty logs
5) Host transmits the page

If these are reordered to:
3) Host userspace observes the c-bit logs
1) Guest dirties the c-bit in the guest
2) Guest dirties the page
4) Host userspace observes the page dirty logs
5) Host transmits the page (from the wrong c-bit perspective!)

Then the host will transmit a page with the wrong c-bit status and clear the dirty bit for that page. If the guest page is not retransmitted incidentally later, then this page will be corrupted.

Quoted 7 lines

If you treat pages with dirty c-bits as dirty pages, then you will check the c-bit logs later and observe the dirty c-bit and retransmit. There might be some cleverness around enforcing that you always fetch the c-bit logs after fetching the dirty logs, but I haven't convinced myself that this works yet. I think it might, since then the c-bits are at least as fresh as the dirty bits.

Unlike the dirty log, the c-bit log maintains the complete state. So, I think it is the Host userspace responsibility to ensure that it either keeps track of any c-bit log changes since it last sync'ed. During the migration, after pausing the guest it can get the recent c-bit log and compare if something has changed since it last sync'ed. If so, then retransmit the page with new c-bit state.

Quoted 7 lines

The main uncertainty that comes to mind for that strategy is if, on multi-vCPU VMs, the page dirtying event (from the new c-bit perspective) and the c-bit status change hypercall can themselves race. If a write from the new c-bit perspective can arrive before the c-bit status change arrives in the c-bit logs, we will need to treat pages with dirty c-bits as dirty pages.

Quoted 4 lines

I believe if host userspace tracks the changes in the c-bit log since it last synced then this problem can be avoided. Do you think we should consider tracking the last sync changes in KVM or let the host userspace handle it.

Quoted 5 lines

Punting this off to userspace to handle works. If storing the old c-bit statuses in userspace becomes a memory issue (unlikely), we can fix that down the line.

Andy, are your concerns about the raceyness of c-bit tracking resolved?

Probably, as long as the guest doesn’t play nasty games with trying to read its own ciphertext.

#37 lore raw

PATCH 11/12 KVM: x86: Introduce KVM_SET_PAGE_ENC_BITMAP ioctl

In reply to Ashish Kalra

From: Brijesh Singh <[email protected]>

The ioctl can be used to set page encryption bitmap 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.txt  | 21 +++++++++++++++++
 arch/x86/include/asm/kvm_host.h |  2 ++
 arch/x86/kvm/svm.c              | 42 +++++++++++++++++++++++++++++++++
 arch/x86/kvm/x86.c              | 12 ++++++++++
 include/uapi/linux/kvm.h        |  1 +
 5 files changed, 78 insertions(+)
Documentation/virt/kvm/api.txt +21 -0
index 053aecfabe74..d4e29a457e80 100644
@@ -4239,6 +4239,27 @@ or shared. The bitmap can be used during the guest migration, if the page
 is private then userspace need to use SEV migration commands to transmit
 the page.
 
+4.121 KVM_SET_PAGE_ENC_BITMAP (vm ioctl)
+
+Capability: basic
+Architectures: x86
+Type: vm ioctl
+Parameters: struct kvm_page_enc_bitmap (in/out)
+Returns: 0 on success, -1 on error
+
+/* for KVM_SET_PAGE_ENC_BITMAP */
+struct kvm_page_enc_bitmap {
+	__u64 start_gfn;
+	__u64 num_pages;
+	union {
+		void __user *enc_bitmap; /* one bit per page */
+		__u64 padding2;
+	};
+};
+
+During the guest live migration the outgoing guest exports its page encryption
+bitmap, the KVM_SET_PAGE_ENC_BITMAP can be used to build the page encryption
+bitmap for an incoming guest.
 
 5. The kvm_run structure
 ------------------------
arch/x86/include/asm/kvm_host.h +2 -0
index a6882c5214b4..698ea92290af 100644
@@ -1260,6 +1260,8 @@ struct kvm_x86_ops {
 				  unsigned long sz, unsigned long mode);
 	int (*get_page_enc_bitmap)(struct kvm *kvm,
 				struct kvm_page_enc_bitmap *bmap);
+	int (*set_page_enc_bitmap)(struct kvm *kvm,
+				struct kvm_page_enc_bitmap *bmap);
 };
 
 struct kvm_arch_async_pf {
arch/x86/kvm/svm.c +42 -0

54 lines not on this page · raw message

arch/x86/kvm/x86.c +12 -0
index e955f886ee17..a1ac5b8c5cd7 100644
@@ -5225,6 +5225,18 @@ long kvm_arch_vm_ioctl(struct file *filp,
 			r = kvm_x86_ops->get_page_enc_bitmap(kvm, &bitmap);
 		break;
 	}
+	case KVM_SET_PAGE_ENC_BITMAP: {
+		struct kvm_page_enc_bitmap bitmap;
+
+		r = -EFAULT;
+		if (copy_from_user(&bitmap, argp, sizeof(bitmap)))
+			goto out;
+
+		r = -ENOTTY;
+		if (kvm_x86_ops->set_page_enc_bitmap)
+			r = kvm_x86_ops->set_page_enc_bitmap(kvm, &bitmap);
+		break;
+	}
 	default:
 		r = -ENOTTY;
 	}
include/uapi/linux/kvm.h +1 -0
index 9377b26c5f4e..2f36afd11e0e 100644
@@ -1489,6 +1489,7 @@ struct kvm_enc_region {
 #define KVM_S390_CLEAR_RESET	_IO(KVMIO,   0xc4)
 
 #define KVM_GET_PAGE_ENC_BITMAP	_IOW(KVMIO, 0xc2, struct kvm_page_enc_bitmap)
+#define KVM_SET_PAGE_ENC_BITMAP	_IOW(KVMIO, 0xc3, struct kvm_page_enc_bitmap)
 
 /* Secure Encrypted Virtualization command */
 enum sev_cmd_id {
Signature · 1 line
2.17.1
#38 lore raw

PATCH 12/12 KVM: x86: Introduce KVM_PAGE_ENC_BITMAP_RESET ioctl

In reply to Ashish Kalra

From: Ashish Kalra <[email protected]>

This ioctl can be used by the application to reset the page encryption bitmap managed by the KVM driver. A typical usage for this ioctl is on VM reboot, on reboot, we must reinitialize the bitmap.

---
 Documentation/virt/kvm/api.txt  | 11 +++++++++++
 arch/x86/include/asm/kvm_host.h |  1 +
 arch/x86/kvm/svm.c              | 16 ++++++++++++++++
 arch/x86/kvm/x86.c              |  6 ++++++
 include/uapi/linux/kvm.h        |  1 +
 5 files changed, 35 insertions(+)
Documentation/virt/kvm/api.txt +11 -0
index d4e29a457e80..bf0fd3c2ea07 100644
@@ -4261,6 +4261,17 @@ During the guest live migration the outgoing guest exports its page encryption
 bitmap, the KVM_SET_PAGE_ENC_BITMAP can be used to build the page encryption
 bitmap for an incoming guest.
 
+4.122 KVM_PAGE_ENC_BITMAP_RESET (vm ioctl)
+
+Capability: basic
+Architectures: x86
+Type: vm ioctl
+Parameters: none
+Returns: 0 on success, -1 on error
+
+The KVM_PAGE_ENC_BITMAP_RESET is used to reset the guest's page encryption
+bitmap during guest reboot and this is only done on the guest's boot vCPU.
+
 5. The kvm_run structure
 ------------------------
 
arch/x86/include/asm/kvm_host.h +1 -0
index 698ea92290af..746c9c84d14a 100644
@@ -1262,6 +1262,7 @@ struct kvm_x86_ops {
 				struct kvm_page_enc_bitmap *bmap);
 	int (*set_page_enc_bitmap)(struct kvm *kvm,
 				struct kvm_page_enc_bitmap *bmap);
+	int (*reset_page_enc_bitmap)(struct kvm *kvm);
 };
 
 struct kvm_arch_async_pf {
arch/x86/kvm/svm.c +16 -0
index a710a6a2d18c..1659539b1873 100644
@@ -7756,6 +7756,21 @@ static int svm_set_page_enc_bitmap(struct kvm *kvm,
 	return ret;
 }
 
+static int svm_reset_page_enc_bitmap(struct kvm *kvm)
+{
+	struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
+
+	if (!sev_guest(kvm))
+		return -ENOTTY;
+
+	mutex_lock(&kvm->lock);
+	/* by default all pages should be marked encrypted */
+	if (sev->page_enc_bmap_size)
+		bitmap_fill(sev->page_enc_bmap, sev->page_enc_bmap_size);
+	mutex_unlock(&kvm->lock);
+	return 0;
+}
+
 static int svm_mem_enc_op(struct kvm *kvm, void __user *argp)
 {
 	struct kvm_sev_cmd sev_cmd;
@@ -8151,6 +8166,7 @@ static struct kvm_x86_ops svm_x86_ops __ro_after_init = {
 	.page_enc_status_hc = svm_page_enc_status_hc,
 	.get_page_enc_bitmap = svm_get_page_enc_bitmap,
 	.set_page_enc_bitmap = svm_set_page_enc_bitmap,
+	.reset_page_enc_bitmap = svm_reset_page_enc_bitmap,
 };
 
 static int __init svm_init(void)
arch/x86/kvm/x86.c +6 -0
index a1ac5b8c5cd7..eeb2a3dfeb02 100644
@@ -5237,6 +5237,12 @@ long kvm_arch_vm_ioctl(struct file *filp,
 			r = kvm_x86_ops->set_page_enc_bitmap(kvm, &bitmap);
 		break;
 	}
+	case KVM_PAGE_ENC_BITMAP_RESET: {
+		r = -ENOTTY;
+		if (kvm_x86_ops->reset_page_enc_bitmap)
+			r = kvm_x86_ops->reset_page_enc_bitmap(kvm);
+		break;
+	}
 	default:
 		r = -ENOTTY;
 	}
include/uapi/linux/kvm.h +1 -0
index 2f36afd11e0e..4001c22cb36b 100644
@@ -1490,6 +1490,7 @@ struct kvm_enc_region {
 
 #define KVM_GET_PAGE_ENC_BITMAP	_IOW(KVMIO, 0xc2, struct kvm_page_enc_bitmap)
 #define KVM_SET_PAGE_ENC_BITMAP	_IOW(KVMIO, 0xc3, struct kvm_page_enc_bitmap)
+#define KVM_PAGE_ENC_BITMAP_RESET	_IO(KVMIO, 0xc4)
 
 /* Secure Encrypted Virtualization command */
 enum sev_cmd_id {
Signature · 1 line
2.17.1
maintainer
#39 lore raw
In reply to Ashish Kalra
Quoted 4 lines · Ashish Kalra

From: Ashish Kalra <[email protected]>

This patchset adds support for SEV Live Migration on KVM/QEMU.

I skimmed this all and I don't see any description of how this all works.

Does any of this address the mess in svm_register_enc_region()? Right now, when QEMU (or a QEMU alternative) wants to allocate some memory to be used for guest encrypted pages, it mmap()s some memory and the kernel does get_user_pages_fast() on it. The pages are kept pinned for the lifetime of the mapping. This is not at all okay. Let's see:

- The memory is pinned and it doesn't play well with the Linux memory
management code. You just wrote a big patch set to migrate the pages to a whole different machines, but we apparently can't even migrate them to a different NUMA node or even just a different address. And good luck swapping it out.

- The memory is still mapped in the QEMU process, and that mapping is
incoherent with actual guest access to the memory. It's nice that KVM clflushes it so that, in principle, everything might actually work, but this is gross. We should not be exposing incoherent mappings to userspace.

Perhaps all this fancy infrastructure you're writing for migration and all this new API surface could also teach the kernel how to migrate pages from a guest *to the same guest* so we don't need to pin pages forever. And perhaps you could put some thought into how to improve the API so that it doesn't involve nonsensical incoherent mappings.

(To be blunt: if I had noticed how the SEV code worked before it was merged, I would have NAKed it. It's too late now to retroactively remove it from the kernel, but perhaps we could try not to pile more complexity on top of the unfortunate foundation we have.)

#40 lore raw
Quoted 26 lines · Andy Lutomirski
Quoted 4 lines · Ashish Kalra

From: Ashish Kalra <[email protected]>

This patchset adds support for SEV Live Migration on KVM/QEMU.

I skimmed this all and I don't see any description of how this all works.

Does any of this address the mess in svm_register_enc_region()? Right now, when QEMU (or a QEMU alternative) wants to allocate some memory to be used for guest encrypted pages, it mmap()s some memory and the kernel does get_user_pages_fast() on it. The pages are kept pinned for the lifetime of the mapping. This is not at all okay. Let's see:

- The memory is pinned and it doesn't play well with the Linux memory
management code. You just wrote a big patch set to migrate the pages to a whole different machines, but we apparently can't even migrate them to a different NUMA node or even just a different address. And good luck swapping it out.

- The memory is still mapped in the QEMU process, and that mapping is
incoherent with actual guest access to the memory. It's nice that KVM clflushes it so that, in principle, everything might actually work, but this is gross. We should not be exposing incoherent mappings to userspace.

Quoted 5 lines

Perhaps all this fancy infrastructure you're writing for migration and all this new API surface could also teach the kernel how to migrate pages from a guest *to the same guest* so we don't need to pin pages forever. And perhaps you could put some thought into how to improve the API so that it doesn't involve nonsensical incoherent mappings.o

As a different key is used to encrypt memory in each VM, the hypervisor can't simply copy the the ciphertext from one VM to another to migrate the VM. Therefore, the AMD SEV Key Management API provides a new sets of function which the hypervisor can use to package a guest page for migration, while maintaining the confidentiality provided by AMD SEV.

There is a new page encryption bitmap created in the kernel which keeps tracks of encrypted/decrypted state of guest's pages and this bitmap is updated by a new hypercall interface provided to the guest kernel and firmware.

KVM_GET_PAGE_ENC_BITMAP ioctl can be used to get the guest page encryption bitmap. The bitmap can be used to check if the given guest page is private or shared.

During the migration flow, the SEND_START is called on the source hypervisor to create an outgoing encryption context. The SEV guest policy dictates whether the certificate passed through the migrate-set-parameters command will be validated. SEND_UPDATE_DATA is called to encrypt the guest private pages. After migration is completed, SEND_FINISH is called to destroy the encryption context and make the VM non-runnable to protect it against cloning.

On the target machine, RECEIVE_START is called first to create an incoming encryption context. The RECEIVE_UPDATE_DATA is called to copy the received encrypted page into guest memory. After migration has completed, RECEIVE_FINISH is called to make the VM runnable.

Thanks,
Ashish

maintainer
#41 lore raw
Quoted 56 lines · Ashish Kalra
Quoted 26 lines · Andy Lutomirski
Quoted 4 lines · Ashish Kalra

From: Ashish Kalra <[email protected]>

This patchset adds support for SEV Live Migration on KVM/QEMU.

I skimmed this all and I don't see any description of how this all works.

Does any of this address the mess in svm_register_enc_region()? Right now, when QEMU (or a QEMU alternative) wants to allocate some memory to be used for guest encrypted pages, it mmap()s some memory and the kernel does get_user_pages_fast() on it. The pages are kept pinned for the lifetime of the mapping. This is not at all okay. Let's see:

- The memory is pinned and it doesn't play well with the Linux memory
management code. You just wrote a big patch set to migrate the pages to a whole different machines, but we apparently can't even migrate them to a different NUMA node or even just a different address. And good luck swapping it out.

- The memory is still mapped in the QEMU process, and that mapping is
incoherent with actual guest access to the memory. It's nice that KVM clflushes it so that, in principle, everything might actually work, but this is gross. We should not be exposing incoherent mappings to userspace.

Quoted 5 lines

Perhaps all this fancy infrastructure you're writing for migration and all this new API surface could also teach the kernel how to migrate pages from a guest *to the same guest* so we don't need to pin pages forever. And perhaps you could put some thought into how to improve the API so that it doesn't involve nonsensical incoherent mappings.o

As a different key is used to encrypt memory in each VM, the hypervisor can't simply copy the the ciphertext from one VM to another to migrate the VM. Therefore, the AMD SEV Key Management API provides a new sets of function which the hypervisor can use to package a guest page for migration, while maintaining the confidentiality provided by AMD SEV.

There is a new page encryption bitmap created in the kernel which keeps tracks of encrypted/decrypted state of guest's pages and this bitmap is updated by a new hypercall interface provided to the guest kernel and firmware.

KVM_GET_PAGE_ENC_BITMAP ioctl can be used to get the guest page encryption bitmap. The bitmap can be used to check if the given guest page is private or shared.

During the migration flow, the SEND_START is called on the source hypervisor to create an outgoing encryption context. The SEV guest policy dictates whether the certificate passed through the migrate-set-parameters command will be validated. SEND_UPDATE_DATA is called to encrypt the guest private pages. After migration is completed, SEND_FINISH is called to destroy the encryption context and make the VM non-runnable to protect it against cloning.

Quoted 5 lines

On the target machine, RECEIVE_START is called first to create an incoming encryption context. The RECEIVE_UPDATE_DATA is called to copy the received encrypted page into guest memory. After migration has completed, RECEIVE_FINISH is called to make the VM runnable.

Thanks! This belongs somewhere in the patch set.

You still haven't answered my questions about the existing coherency issues and whether the same infrastructure can be used to migrate guest pages within the same machine.

Also, you're making guest-side and host-side changes. What ensures that you don't try to migrate a guest that doesn't support the hypercall for encryption state tracking?

#42 lore raw
Quoted 63 lines · Andy Lutomirski
Quoted 56 lines · Ashish Kalra
Quoted 26 lines · Andy Lutomirski
Quoted 4 lines · Ashish Kalra

From: Ashish Kalra <[email protected]>

This patchset adds support for SEV Live Migration on KVM/QEMU.

I skimmed this all and I don't see any description of how this all works.

Does any of this address the mess in svm_register_enc_region()? Right now, when QEMU (or a QEMU alternative) wants to allocate some memory to be used for guest encrypted pages, it mmap()s some memory and the kernel does get_user_pages_fast() on it. The pages are kept pinned for the lifetime of the mapping. This is not at all okay. Let's see:

- The memory is pinned and it doesn't play well with the Linux memory
management code. You just wrote a big patch set to migrate the pages to a whole different machines, but we apparently can't even migrate them to a different NUMA node or even just a different address. And good luck swapping it out.

- The memory is still mapped in the QEMU process, and that mapping is
incoherent with actual guest access to the memory. It's nice that KVM clflushes it so that, in principle, everything might actually work, but this is gross. We should not be exposing incoherent mappings to userspace.

Quoted 5 lines

Perhaps all this fancy infrastructure you're writing for migration and all this new API surface could also teach the kernel how to migrate pages from a guest *to the same guest* so we don't need to pin pages forever. And perhaps you could put some thought into how to improve the API so that it doesn't involve nonsensical incoherent mappings.o

As a different key is used to encrypt memory in each VM, the hypervisor can't simply copy the the ciphertext from one VM to another to migrate the VM. Therefore, the AMD SEV Key Management API provides a new sets of function which the hypervisor can use to package a guest page for migration, while maintaining the confidentiality provided by AMD SEV.

There is a new page encryption bitmap created in the kernel which keeps tracks of encrypted/decrypted state of guest's pages and this bitmap is updated by a new hypercall interface provided to the guest kernel and firmware.

KVM_GET_PAGE_ENC_BITMAP ioctl can be used to get the guest page encryption bitmap. The bitmap can be used to check if the given guest page is private or shared.

During the migration flow, the SEND_START is called on the source hypervisor to create an outgoing encryption context. The SEV guest policy dictates whether the certificate passed through the migrate-set-parameters command will be validated. SEND_UPDATE_DATA is called to encrypt the guest private pages. After migration is completed, SEND_FINISH is called to destroy the encryption context and make the VM non-runnable to protect it against cloning.

Quoted 5 lines

On the target machine, RECEIVE_START is called first to create an incoming encryption context. The RECEIVE_UPDATE_DATA is called to copy the received encrypted page into guest memory. After migration has completed, RECEIVE_FINISH is called to make the VM runnable.

Quoted 5 lines

Thanks! This belongs somewhere in the patch set.

You still haven't answered my questions about the existing coherency issues and whether the same infrastructure can be used to migrate guest pages within the same machine.

Page Migration and Live Migration are separate features and one of my colleagues is currently working on making page migration possible and removing SEV Guest pinning requirements.

Quoted 4 lines

Also, you're making guest-side and host-side changes. What ensures that you don't try to migrate a guest that doesn't support the hypercall for encryption state tracking?

This is a good question and it is still an open-ended question. There are two possibilities here: guest does not have any unencrypted pages (for e.g booting 32-bit) and so it does not make any hypercalls, and the other possibility is that the guest does not have support for the newer hypercall.

In the first case, all the guest pages are then assumed to be encrypted and live migration happens as such.

For the second case, we have been discussing this internally,
and one option is to extend the KVM capabilites/feature bits to check for this ?

Thanks,
Ashish

#43 lore raw

I think this patch series might be incompatible with kexec, but I'm not sure since I'm really not familiar with that subsystem. It seems like you need to explicitly mark all shared pages as encrypted again before rebooting into the new kernel. If you don't do this, and the newly booted kernel believes RAM is encrypted and has always been, you won't flip formerly decrypted shared device pages back to encrypted. For example, if the swiotlb moves, you need to tell KVM that the old swiotlb pages are now encrypted.

I could imagine this being handled implicitly by the way kexec handles freeing memory, but I would find this surprising. I only see enlightenment for handling SME's interaction with the initial image in the kexec implementation.

Quoted 93 lines · Ashish Kalra
Quoted 63 lines · Andy Lutomirski
Quoted 56 lines · Ashish Kalra
Quoted 26 lines · Andy Lutomirski
Quoted 4 lines · Ashish Kalra

From: Ashish Kalra <[email protected]>

This patchset adds support for SEV Live Migration on KVM/QEMU.

I skimmed this all and I don't see any description of how this all works.

Does any of this address the mess in svm_register_enc_region()? Right now, when QEMU (or a QEMU alternative) wants to allocate some memory to be used for guest encrypted pages, it mmap()s some memory and the kernel does get_user_pages_fast() on it. The pages are kept pinned for the lifetime of the mapping. This is not at all okay. Let's see:

- The memory is pinned and it doesn't play well with the Linux memory
management code. You just wrote a big patch set to migrate the pages to a whole different machines, but we apparently can't even migrate them to a different NUMA node or even just a different address. And good luck swapping it out.

- The memory is still mapped in the QEMU process, and that mapping is
incoherent with actual guest access to the memory. It's nice that KVM clflushes it so that, in principle, everything might actually work, but this is gross. We should not be exposing incoherent mappings to userspace.

Quoted 5 lines

Perhaps all this fancy infrastructure you're writing for migration and all this new API surface could also teach the kernel how to migrate pages from a guest *to the same guest* so we don't need to pin pages forever. And perhaps you could put some thought into how to improve the API so that it doesn't involve nonsensical incoherent mappings.o

As a different key is used to encrypt memory in each VM, the hypervisor can't simply copy the the ciphertext from one VM to another to migrate the VM. Therefore, the AMD SEV Key Management API provides a new sets of function which the hypervisor can use to package a guest page for migration, while maintaining the confidentiality provided by AMD SEV.

There is a new page encryption bitmap created in the kernel which keeps tracks of encrypted/decrypted state of guest's pages and this bitmap is updated by a new hypercall interface provided to the guest kernel and firmware.

KVM_GET_PAGE_ENC_BITMAP ioctl can be used to get the guest page encryption bitmap. The bitmap can be used to check if the given guest page is private or shared.

During the migration flow, the SEND_START is called on the source hypervisor to create an outgoing encryption context. The SEV guest policy dictates whether the certificate passed through the migrate-set-parameters command will be validated. SEND_UPDATE_DATA is called to encrypt the guest private pages. After migration is completed, SEND_FINISH is called to destroy the encryption context and make the VM non-runnable to protect it against cloning.

Quoted 5 lines

On the target machine, RECEIVE_START is called first to create an incoming encryption context. The RECEIVE_UPDATE_DATA is called to copy the received encrypted page into guest memory. After migration has completed, RECEIVE_FINISH is called to make the VM runnable.

Quoted 5 lines

Thanks! This belongs somewhere in the patch set.

You still haven't answered my questions about the existing coherency issues and whether the same infrastructure can be used to migrate guest pages within the same machine.

Page Migration and Live Migration are separate features and one of my colleagues is currently working on making page migration possible and removing SEV Guest pinning requirements.

Quoted 4 lines

Also, you're making guest-side and host-side changes. What ensures that you don't try to migrate a guest that doesn't support the hypercall for encryption state tracking?

This is a good question and it is still an open-ended question. There are two possibilities here: guest does not have any unencrypted pages (for e.g booting 32-bit) and so it does not make any hypercalls, and the other possibility is that the guest does not have support for the newer hypercall.

In the first case, all the guest pages are then assumed to be encrypted and live migration happens as such.

For the second case, we have been discussing this internally,
and one option is to extend the KVM capabilites/feature bits to check for this ?

Thanks,
Ashish

#44 lore raw
Quoted 9 lines · Steve Rutherford

I think this patch series might be incompatible with kexec, but I'm not sure since I'm really not familiar with that subsystem. It seems like you need to explicitly mark all shared pages as encrypted again before rebooting into the new kernel. If you don't do this, and the newly booted kernel believes RAM is encrypted and has always been, you won't flip formerly decrypted shared device pages back to encrypted. For example, if the swiotlb moves, you need to tell KVM that the old swiotlb pages are now encrypted.

Quoted 5 lines

I could imagine this being handled implicitly by the way kexec handles freeing memory, but I would find this surprising. I only see enlightenment for handling SME's interaction with the initial image in the kexec implementation.

I believe that there already is enlightenment for SEV handling in kexec implementation, but you are right as there is no support currently for resetting the page encryption bitmap in the SEV handling code in kexec, similar to what we do for a guest reboot event.

This support probably needs to be added as part of the arch_kexec_{post,pre}_{alloc,free}_pages() interface.

Thanks,
Ashish

Quoted 94 lines · Ashish Kalra
Quoted 93 lines · Andy Lutomirski
Quoted 63 lines · Ashish Kalra
Quoted 56 lines · Andy Lutomirski
Quoted 26 lines · Ashish Kalra
Quoted 4 lines

From: Ashish Kalra <[email protected]>

This patchset adds support for SEV Live Migration on KVM/QEMU.

I skimmed this all and I don't see any description of how this all works.

Does any of this address the mess in svm_register_enc_region()? Right now, when QEMU (or a QEMU alternative) wants to allocate some memory to be used for guest encrypted pages, it mmap()s some memory and the kernel does get_user_pages_fast() on it. The pages are kept pinned for the lifetime of the mapping. This is not at all okay. Let's see:

- The memory is pinned and it doesn't play well with the Linux memory
management code. You just wrote a big patch set to migrate the pages to a whole different machines, but we apparently can't even migrate them to a different NUMA node or even just a different address. And good luck swapping it out.

- The memory is still mapped in the QEMU process, and that mapping is
incoherent with actual guest access to the memory. It's nice that KVM clflushes it so that, in principle, everything might actually work, but this is gross. We should not be exposing incoherent mappings to userspace.

Quoted 5 lines

Perhaps all this fancy infrastructure you're writing for migration and all this new API surface could also teach the kernel how to migrate pages from a guest *to the same guest* so we don't need to pin pages forever. And perhaps you could put some thought into how to improve the API so that it doesn't involve nonsensical incoherent mappings.o

As a different key is used to encrypt memory in each VM, the hypervisor can't simply copy the the ciphertext from one VM to another to migrate the VM. Therefore, the AMD SEV Key Management API provides a new sets of function which the hypervisor can use to package a guest page for migration, while maintaining the confidentiality provided by AMD SEV.

There is a new page encryption bitmap created in the kernel which keeps tracks of encrypted/decrypted state of guest's pages and this bitmap is updated by a new hypercall interface provided to the guest kernel and firmware.

KVM_GET_PAGE_ENC_BITMAP ioctl can be used to get the guest page encryption bitmap. The bitmap can be used to check if the given guest page is private or shared.

During the migration flow, the SEND_START is called on the source hypervisor to create an outgoing encryption context. The SEV guest policy dictates whether the certificate passed through the migrate-set-parameters command will be validated. SEND_UPDATE_DATA is called to encrypt the guest private pages. After migration is completed, SEND_FINISH is called to destroy the encryption context and make the VM non-runnable to protect it against cloning.

Quoted 5 lines

On the target machine, RECEIVE_START is called first to create an incoming encryption context. The RECEIVE_UPDATE_DATA is called to copy the received encrypted page into guest memory. After migration has completed, RECEIVE_FINISH is called to make the VM runnable.

Quoted 5 lines

Thanks! This belongs somewhere in the patch set.

You still haven't answered my questions about the existing coherency issues and whether the same infrastructure can be used to migrate guest pages within the same machine.

Page Migration and Live Migration are separate features and one of my colleagues is currently working on making page migration possible and removing SEV Guest pinning requirements.

Quoted 4 lines

Also, you're making guest-side and host-side changes. What ensures that you don't try to migrate a guest that doesn't support the hypercall for encryption state tracking?

This is a good question and it is still an open-ended question. There are two possibilities here: guest does not have any unencrypted pages (for e.g booting 32-bit) and so it does not make any hypercalls, and the other possibility is that the guest does not have support for the newer hypercall.

In the first case, all the guest pages are then assumed to be encrypted and live migration happens as such.

For the second case, we have been discussing this internally,
and one option is to extend the KVM capabilites/feature bits to check for this ?

Thanks,
Ashish

maintainer
#45 lore raw
In reply to Ashish Kalra
Quoted 9 lines · Ashish Kalra
Quoted 3 lines

Also, you're making guest-side and host-side changes. What ensures that you don't try to migrate a guest that doesn't support the hypercall for encryption state tracking?

This is a good question and it is still an open-ended question. There are two possibilities here: guest does not have any unencrypted pages (for e.g booting 32-bit) and so it does not make any hypercalls, and the other possibility is that the guest does not have support for the newer hypercall.

Quoted 5 lines

In the first case, all the guest pages are then assumed to be encrypted and live migration happens as such.

For the second case, we have been discussing this internally,
and one option is to extend the KVM capabilites/feature bits to check for this ?

You could extend the hypercall to completely block live migration (e.g. a0=a1=~0, a2=0 to unblock or 1 to block). The KVM_GET_PAGE_ENC_BITMAP ioctl can also return the blocked/unblocked state.

Paolo

#46 lore raw

Hello Paolo,

Quoted 13 lines · Paolo Bonzini
Quoted 12 lines · Ashish Kalra
Quoted 3 lines

Also, you're making guest-side and host-side changes. What ensures that you don't try to migrate a guest that doesn't support the hypercall for encryption state tracking?

This is a good question and it is still an open-ended question. There are two possibilities here: guest does not have any unencrypted pages (for e.g booting 32-bit) and so it does not make any hypercalls, and the other possibility is that the guest does not have support for the newer hypercall.

In the first case, all the guest pages are then assumed to be encrypted and live migration happens as such.

Quoted 7 lines
Quoted 2 lines

For the second case, we have been discussing this internally,
and one option is to extend the KVM capabilites/feature bits to check for this ?

You could extend the hypercall to completely block live migration (e.g. a0=a1=~0, a2=0 to unblock or 1 to block). The KVM_GET_PAGE_ENC_BITMAP ioctl can also return the blocked/unblocked state.

Currently i have added a new KVM para feature "KVM_FEATURE_SEV_LIVE_MIGRATION" to indicate host support for the SEV live migration feature and a custom KVM MSR "MSR_KVM_SEV_LIVE_MIG_EN" for the guest to enable SEV live migration. The MSR also has other flags for future SEV live migration extensions.

Thanks,
Ashish

maintainer
#47 lore raw
Quoted 11 lines · Ashish Kalra
Quoted 6 lines
Quoted 2 lines

For the second case, we have been discussing this internally,
and one option is to extend the KVM capabilites/feature bits to check for this ?

You could extend the hypercall to completely block live migration (e.g. a0=a1=~0, a2=0 to unblock or 1 to block). The KVM_GET_PAGE_ENC_BITMAP ioctl can also return the blocked/unblocked state.

Currently i have added a new KVM para feature "KVM_FEATURE_SEV_LIVE_MIGRATION" to indicate host support for the SEV live migration feature and a custom KVM MSR "MSR_KVM_SEV_LIVE_MIG_EN" for the guest to enable SEV live migration. The MSR also has other flags for future SEV live migration extensions.

Ok, that would be fine too. Thanks!

Paolo

#48 lore raw
In reply to Andy Lutomirski

Hi Ashish,

Quoted 4 lines · Andy Lutomirski
Quoted 3 lines · Ashish Kalra

From: Ashish Kalra <[email protected]>

This patchset adds support for SEV Live Migration on KVM/QEMU.

I think you should some explanation in cover letter so that we get the full context of the patch. You can see a reference cover-letter in previous submissions.

https://marc.info/?l=kvm&m=156278967226011&w=2

The patch series is also missing the revision number and change log, these information will helper reviewer to know what is changed since previous version and which feedback are addressed in this version.

Thanks