Add AMD SEV guest live migration support
Subsystems (9)
v6 cover Add AMD SEV guest live migration support
From: Ashish Kalra <[email protected]>
The series add support for AMD SEV guest live migration commands. To protect the confidentiality of an SEV protected guest memory while in transit we need to use the SEV commands defined in SEV API spec [1].
SEV guest VMs have the concept of private and shared memory. Private memory is encrypted with the guest-specific key, while shared memory may be encrypted with hypervisor key. The commands provided by the SEV FW are meant to be used for the private memory only. The patch series introduces a new hypercall. The guest OS can use this hypercall to notify the page encryption status. If the page is encrypted with guest specific-key then we use SEV command during the migration. If page is not encrypted then fallback to default.
The patch adds new ioctls KVM_{SET,GET}_PAGE_ENC_BITMAP. The ioctl can be used by the qemu to get the page encrypted bitmap. Qemu can consult this bitmap during the migration to know whether the page is encrypted.
[1] https://developer.amd.com/wp-content/resources/55766.PDF
Changes since v5:
- Fix build errors as
Changes since v4:
- Host support has been added to extend KVM capabilities/feature bits to include a new KVM_FEATURE_SEV_LIVE_MIGRATION, which the guest can query for host-side support for SEV live migration and a new custom MSR MSR_KVM_SEV_LIVE_MIG_EN is added for guest to enable the SEV live migration feature.
- Ensure that _bss_decrypted section is marked as decrypted in the page encryption bitmap.
- Fixing KVM_GET_PAGE_ENC_BITMAP ioctl to return the correct bitmap as per the number of pages being requested by the user. Ensure that we only copy bmap->num_pages bytes in the userspace buffer, if bmap->num_pages is not byte aligned we read the trailing bits from the userspace and copy those bits as is. This fixes guest page(s) corruption issues observed after migration completion.
- Add kexec support for SEV Live Migration to reset the host's page encryption bitmap related to kernel specific page encryption status settings before we load a new kernel by kexec. We cannot reset the complete page encryption bitmap here as we need to retain the UEFI/OVMF firmware specific settings.
Changes since v3:
- Rebasing to mainline and testing.
- Adding a new KVM_PAGE_ENC_BITMAP_RESET ioctl, which resets the page encryption bitmap on a guest reboot event.
- Adding a more reliable sanity check for GPA range being passed to the hypercall to ensure that guest MMIO ranges are also marked in the page encryption bitmap.
Changes since v2:
- reset the page encryption bitmap on vcpu reboot
Changes since v1:
- Add support to share the page encryption between the source and target machine.
- Fix review feedbacks from Tom Lendacky.
- Add check to limit the session blob length.
- Update KVM_GET_PAGE_ENC_BITMAP icotl to use the base_gfn instead of the memory slot when querying the bitmap.
Ashish Kalra (3):
KVM: x86: Introduce KVM_PAGE_ENC_BITMAP_RESET ioctl
KVM: x86: Introduce new KVM_FEATURE_SEV_LIVE_MIGRATION feature &
Custom MSR.
KVM: x86: Add kexec support for SEV Live Migration.
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.rst | 62 ++ Documentation/virt/kvm/cpuid.rst | 4 + Documentation/virt/kvm/hypercalls.rst | 15 + Documentation/virt/kvm/msr.rst | 10 + arch/x86/include/asm/kvm_host.h | 10 + arch/x86/include/asm/kvm_para.h | 12 + arch/x86/include/asm/paravirt.h | 10 + arch/x86/include/asm/paravirt_types.h | 2 + arch/x86/include/uapi/asm/kvm_para.h | 5 + arch/x86/kernel/kvm.c | 32 + arch/x86/kernel/paravirt.c | 1 + arch/x86/kvm/cpuid.c | 3 +- arch/x86/kvm/svm.c | 699 +++++++++++++++++- arch/x86/kvm/vmx/vmx.c | 1 + arch/x86/kvm/x86.c | 43 ++ arch/x86/mm/mem_encrypt.c | 69 +- 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 + 21 files changed, 1157 insertions(+), 10 deletions(-)
Signature · 1 line
2.17.1
PATCH v6 1/14 KVM: SVM: Add KVM_SEV SEND_START command
From: Brijesh Singh <[email protected]>
The command is used to create an outgoing SEV guest encryption context.
--- .../virt/kvm/amd-memory-encryption.rst | 27 ++++ arch/x86/kvm/svm.c | 128 ++++++++++++++++++ include/linux/psp-sev.h | 8 +- include/uapi/linux/kvm.h | 12 ++ 4 files changed, 171 insertions(+), 4 deletions(-)
Documentation/virt/kvm/amd-memory-encryption.rst +27 -0
@@ -263,6 +263,33 @@ Returns: 0 on success, -negative on error __u32 trans_len; }; +10. KVM_SEV_SEND_START +---------------------- + +The KVM_SEV_SEND_START command can be used by the hypervisor to create an +outgoing guest encryption context. + +Parameters (in): struct kvm_sev_send_start + +Returns: 0 on success, -negative on error + +:: + struct kvm_sev_send_start { + __u32 policy; /* guest policy */ + + __u64 pdh_cert_uaddr; /* platform Diffie-Hellman certificate */ + __u32 pdh_cert_len; + + __u64 plat_certs_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 +128 -0
140 lines not on this page · raw message
include/linux/psp-sev.h +4 -4
@@ -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
@@ -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
Re: [PATCH v6 01/14] KVM: SVM: Add KVM_SEV SEND_START command
Quoted 41 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]>Reviewed-by Steve Rutherford <[email protected]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[email protected]>--- .../virt/kvm/amd-memory-encryption.rst | 27 ++++ arch/x86/kvm/svm.c | 128 ++++++++++++++++++ include/linux/psp-sev.h | 8 +- include/uapi/linux/kvm.h | 12 ++ 4 files changed, 171 insertions(+), 4 deletions(-)Documentation/virt/kvm/amd-memory-encryption.rst +8
-0@@ -263,6 +263,33 @@ Returns: 0 on success, -negative on error __u32 trans_len; }; +10. KVM_SEV_SEND_START +---------------------- + +The KVM_SEV_SEND_START command can be used by the hypervisor to create an +outgoing guest encryption context. + +Parameters (in): struct kvm_sev_send_start +
Documentation/virt/kvm/amd-memory-encryption.rst Returns: 0 on success, -negative on error · line 283
Quoted 10 lines
Documentation/virt/kvm/amd-memory-encryption.rst +10
-0+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 */
Could this please be changed to plat_certs_uaddr, as it is referred to in the rest of the code?
Documentation/virt/kvm/amd-memory-encryption.rst Returns: 0 on success, -negative on error · line 287
Quoted 4 lines
Documentation/virt/kvm/amd-memory-encryption.rst +4
-0+ __u32 plat_certs_len; + + __u64 amd_certs_uaddr; /* AMD certificate */ + __u32 amd_cert_len;
Could this please be changed to amd_certs_len, as it is referred to in the rest of the code?
Quoted 111 lines
Documentation/virt/kvm/amd-memory-encryption.rst +5
-0+ + __u64 session_uaddr; /* Guest session information */ + __u32 session_len; + }; + References ==========arch/x86/kvm/svm.c +95
-0@@ -7149,6 +7149,131 @@ 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; +int ret; + +if (!sev_guest (kvm)) +return -ENOTTY ; + +if (copy_from_user (¶ms, (void __user *)(uintptr_t )argp->data, +sizeof (struct kvm_sev_send_start ))) +return -EFAULT ; + +/* if session_len is zero, userspace wants to query the session length */ +if (!params.session_len) +return __sev_send_start_query_session_length (kvm, argp, + ¶ms); + +/* 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); + 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; +
arch/x86/kvm/svm.c static int sev_launch_secret(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 7256
Quoted 10 lines
arch/x86/kvm/svm.c +10
-0+ ret =sev_issue_cmd (kvm,SEV_CMD_SEND_START , data, &argp->error); + +if (ret) + goto e_free; + +if (copy_to_user ((void __user *)(uintptr_t ) params.session_uaddr, + session_data, params.session_len)) { + ret = -EFAULT ; + goto e_free; + }
To optimize the amount of data being copied to user space, could the above section of code changed as follows?
params.session_len = data->session_len;
if (copy_to_user((void __user *)(uintptr_t) params.session_uaddr,
session_data, params.session_len)) {
ret = -EFAULT;
goto e_free;
}arch/x86/kvm/svm.c static int sev_launch_secret(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 7262
Quoted 6 lines
arch/x86/kvm/svm.c +6
-0+ + params.policy = data->policy; + params.session_len = data->session_len; +if (copy_to_user ((void __user *)(uintptr_t )argp->data, ¶ms, +sizeof (struct kvm_sev_send_start ))) + ret = -EFAULT ;
Since the only fields that are changed in the kvm_sev_send_start structure are session_len and policy, why do we need to copy the entire structure back to the user? Why not just those two values? Please see the changes proposed to kvm_sev_send_start structure further below to accomplish this.
params.policy = data->policy; if (copy_to_user((void __user *)(uintptr_t)argp->data, ¶ms, sizeof(params.policy) + sizeof(params.session_len)) ret = -EFAULT;
Quoted 56 lines
arch/x86/kvm/svm.c +17
-0+ +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;@@ -7193,6 +7318,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@@ -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 +1
-0@@ -1558,6 +1558,18 @@ struct kvm_sev_dbg {__u32 len; }; +struct kvm_sev_send_start {
include/uapi/linux/kvm.h struct kvm_sev_dbg { · line 1571
Quoted 10 lines
include/uapi/linux/kvm.h +10
-0+__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; +};
Redo this structure as below:
struct kvm_sev_send_start {
__u32 policy;
__u32 session_len;
__u64 session_uaddr;
__u64 pdh_cert_uaddr;
__u32 pdh_cert_len;
__u64 plat_certs_uaddr;
__u32 plat_certs_len;
__u64 amd_certs_uaddr;
__u32 amd_certs_len;
};Or as below, just to make it look better.
struct kvm_sev_send_start {
__u32 policy;
__u32 session_len;
__u64 session_uaddr;
__u32 pdh_cert_len;
__u64 pdh_cert_uaddr;
__u32 plat_certs_len;
__u64 plat_certs_uaddr;
__u32 amd_certs_len;
__u64 amd_certs_uaddr;
};Quoted 7 lines
include/uapi/linux/kvm.h +1
-0+#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
Re: [PATCH v6 01/14] KVM: SVM: Add KVM_SEV SEND_START command
Hi Venu,
Thanks for the feedback.
Quoted 186 lines · Venu Busireddy
Quoted 41 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]>Reviewed-by Steve Rutherford <[email protected]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[email protected]>--- .../virt/kvm/amd-memory-encryption.rst | 27 ++++ arch/x86/kvm/svm.c | 128 ++++++++++++++++++ include/linux/psp-sev.h | 8 +- include/uapi/linux/kvm.h | 12 ++ 4 files changed, 171 insertions(+), 4 deletions(-)Documentation/virt/kvm/amd-memory-encryption.rst +8
-0@@ -263,6 +263,33 @@ Returns: 0 on success, -negative on error __u32 trans_len; }; +10. KVM_SEV_SEND_START +---------------------- + +The KVM_SEV_SEND_START command can be used by the hypervisor to create an +outgoing guest encryption context. + +Parameters (in): struct kvm_sev_send_start +Documentation/virt/kvm/amd-memory-encryption.rst Returns: 0 on success, -negative on error · line 283
Quoted 10 lines
Documentation/virt/kvm/amd-memory-encryption.rst +10
-0+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 */Could this please be changed to
plat_certs_uaddr, as it is referred to in the rest of the code?Documentation/virt/kvm/amd-memory-encryption.rst Returns: 0 on success, -negative on error · line 287
Quoted 4 lines
Documentation/virt/kvm/amd-memory-encryption.rst +4
-0+ __u32 plat_certs_len; + + __u64 amd_certs_uaddr; /* AMD certificate */ + __u32 amd_cert_len;Could this please be changed to
amd_certs_len, as it is referred to in the rest of the code?Quoted 111 lines
Documentation/virt/kvm/amd-memory-encryption.rst +5
-0+ + __u64 session_uaddr; /* Guest session information */ + __u32 session_len; + }; + References ==========arch/x86/kvm/svm.c +95
-0@@ -7149,6 +7149,131 @@ 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; +int ret; + +if (!sev_guest (kvm)) +return -ENOTTY ; + +if (copy_from_user (¶ms, (void __user *)(uintptr_t )argp->data, +sizeof (struct kvm_sev_send_start ))) +return -EFAULT ; + +/* if session_len is zero, userspace wants to query the session length */ +if (!params.session_len) +return __sev_send_start_query_session_length (kvm, argp, + ¶ms); + +/* 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); + 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; +arch/x86/kvm/svm.c static int sev_launch_secret(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 7256
Quoted 10 lines
arch/x86/kvm/svm.c +10
-0+ ret =sev_issue_cmd (kvm,SEV_CMD_SEND_START , data, &argp->error); + +if (ret) + goto e_free; + +if (copy_to_user ((void __user *)(uintptr_t ) params.session_uaddr, + session_data, params.session_len)) { + ret = -EFAULT ; + goto e_free; + }To optimize the amount of data being copied to user space, could the above section of code changed as follows?
Quoted 6 lines
params.session_len = data->session_len;
if (copy_to_user((void __user *)(uintptr_t) params.session_uaddr,
session_data, params.session_len)) {
ret = -EFAULT;
goto e_free;
}We should not be using the data->session_len, it will cause -EFAULT when user has not allocated enough space in the session_uaddr. Lets consider the case where user passes session_len=10 but firmware thinks the session length should be 64. In that case the data->session_len will contains a value of 64 but userspace has allocated space for 10 bytes and copy_to_user() will fail. If we are really concern about the amount of data getting copied to userspace then use min_t(size_t, params.session_len, data->session_len).
Quoted 10 lines
arch/x86/kvm/svm.c static int sev_launch_secret(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 7262
Quoted 6 lines
arch/x86/kvm/svm.c +6
-0+ + params.policy = data->policy; + params.session_len = data->session_len; +if (copy_to_user ((void __user *)(uintptr_t )argp->data, ¶ms, +sizeof (struct kvm_sev_send_start ))) + ret = -EFAULT ;Since the only fields that are changed in the
kvm_sev_send_startstructure aresession_lenand policy, why do we need to copy the entire structure back to the user? Why not just those two values? Please see the changes proposed tokvm_sev_send_startstructure further below to accomplish this.
I think we also need to consider the code readability while saving the CPU cycles. This is very small structure. By duplicating into two calls #1 copy params.policy and #2 copy params.session_len we will add more CPU cycle. And, If we get creative and rearrange the structure then code readability is lost because now the copy will depend on how the structure is layout in the memory.
Quoted 87 lines
params.policy = data->policy; if (copy_to_user((void __user *)(uintptr_t)argp->data, ¶ms, sizeof(params.policy) + sizeof(params.session_len)) ret = -EFAULT;Quoted 56 lines
arch/x86/kvm/svm.c +17
-0+ +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;@@ -7193,6 +7318,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@@ -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 +1
-0@@ -1558,6 +1558,18 @@ struct kvm_sev_dbg {__u32 len; }; +struct kvm_sev_send_start {include/uapi/linux/kvm.h struct kvm_sev_dbg { · line 1571
Quoted 10 lines
include/uapi/linux/kvm.h +10
-0+__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; +};Redo this structure as below:
struct kvm_sev_send_start { __u32 policy; __u32 session_len; __u64 session_uaddr; __u64 pdh_cert_uaddr; __u32 pdh_cert_len; __u64 plat_certs_uaddr; __u32 plat_certs_len; __u64 amd_certs_uaddr; __u32 amd_certs_len; };Or as below, just to make it look better.
Quoted 12 lines
struct kvm_sev_send_start {
__u32 policy;
__u32 session_len;
__u64 session_uaddr;
__u32 pdh_cert_len;
__u64 pdh_cert_uaddr;
__u32 plat_certs_len;
__u64 plat_certs_uaddr;
__u32 amd_certs_len;
__u64 amd_certs_uaddr;
};Wherever applicable, I tried best to not divert from the SEV spec structure layout. Anyone who is reading the SEV FW spec will see a similar structure layout in the KVM/PSP header files. I would prefer to stick to that approach.
Quoted 7 lines
Quoted 7 lines
include/uapi/linux/kvm.h +1
-0+#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
Re: [PATCH v6 01/14] KVM: SVM: Add KVM_SEV SEND_START command
Quoted 199 lines · Brijesh Singh
Hi Venu,
Thanks for the feedback.
Quoted 186 lines · Venu Busireddy
Quoted 41 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]>Reviewed-by Steve Rutherford <[email protected]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[email protected]>--- .../virt/kvm/amd-memory-encryption.rst | 27 ++++ arch/x86/kvm/svm.c | 128 ++++++++++++++++++ include/linux/psp-sev.h | 8 +- include/uapi/linux/kvm.h | 12 ++ 4 files changed, 171 insertions(+), 4 deletions(-)Documentation/virt/kvm/amd-memory-encryption.rst +8
-0@@ -263,6 +263,33 @@ Returns: 0 on success, -negative on error __u32 trans_len; }; +10. KVM_SEV_SEND_START +---------------------- + +The KVM_SEV_SEND_START command can be used by the hypervisor to create an +outgoing guest encryption context. + +Parameters (in): struct kvm_sev_send_start +Documentation/virt/kvm/amd-memory-encryption.rst Returns: 0 on success, -negative on error · line 283
Quoted 10 lines
Documentation/virt/kvm/amd-memory-encryption.rst +10
-0+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 */Could this please be changed to
plat_certs_uaddr, as it is referred to in the rest of the code?Documentation/virt/kvm/amd-memory-encryption.rst Returns: 0 on success, -negative on error · line 287
Quoted 4 lines
Documentation/virt/kvm/amd-memory-encryption.rst +4
-0+ __u32 plat_certs_len; + + __u64 amd_certs_uaddr; /* AMD certificate */ + __u32 amd_cert_len;Could this please be changed to
amd_certs_len, as it is referred to in the rest of the code?Quoted 111 lines
Documentation/virt/kvm/amd-memory-encryption.rst +5
-0+ + __u64 session_uaddr; /* Guest session information */ + __u32 session_len; + }; + References ==========arch/x86/kvm/svm.c +95
-0@@ -7149,6 +7149,131 @@ 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; +int ret; + +if (!sev_guest (kvm)) +return -ENOTTY ; + +if (copy_from_user (¶ms, (void __user *)(uintptr_t )argp->data, +sizeof (struct kvm_sev_send_start ))) +return -EFAULT ; + +/* if session_len is zero, userspace wants to query the session length */ +if (!params.session_len) +return __sev_send_start_query_session_length (kvm, argp, + ¶ms); + +/* 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); + 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; +arch/x86/kvm/svm.c static int sev_launch_secret(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 7256
Quoted 10 lines
arch/x86/kvm/svm.c +10
-0+ ret =sev_issue_cmd (kvm,SEV_CMD_SEND_START , data, &argp->error); + +if (ret) + goto e_free; + +if (copy_to_user ((void __user *)(uintptr_t ) params.session_uaddr, + session_data, params.session_len)) { + ret = -EFAULT ; + goto e_free; + }To optimize the amount of data being copied to user space, could the above section of code changed as follows?
Quoted 6 lines
params.session_len = data->session_len; if (copy_to_user((void __user *)(uintptr_t) params.session_uaddr, session_data, params.session_len)) { ret = -EFAULT; goto e_free; }
Quoted 8 lines
We should not be using the
data->session_len, it will cause-EFAULTwhen user has not allocated enough space in thesession_uaddr. Lets consider the case where user passes session_len=10 but firmware thinks the session length should be 64. In that case thedata->session_lenwill contains a value of 64 but userspace has allocated space for 10 bytes andcopy_to_user()will fail. If we are really concern about the amount of data getting copied to userspace then usemin_t(size_t, params.session_len, data->session_len).
We are allocating a buffer of params.session_len size and passing that buffer, and that length via data->session_len, to the firmware. Why would the firmware set data->session_len to a larger value, in spite of telling it that the buffer is only params.session_len long? I thought that only the reverse is possible, that is, the user sets the params.session_len to the MAX, but the session data is actually smaller than that size.
Also, if for whatever reason the firmware sets data->session_len to a larger value than what is passed, what is the user space expected to do when the call returns? If the user space tries to access params.session_len amount of data, it will possibly get a memory access violation, because it did not originally allocate that large a buffer.
If we do go with using min_t(size_t, params.session_len, data->session_len), then params.session_len should also be set to the smaller of the two, right?
Quoted 11 lines
Quoted 10 lines
arch/x86/kvm/svm.c static int sev_launch_secret(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 7262
Quoted 6 lines
arch/x86/kvm/svm.c +6
-0+ + params.policy = data->policy; + params.session_len = data->session_len; +if (copy_to_user ((void __user *)(uintptr_t )argp->data, ¶ms, +sizeof (struct kvm_sev_send_start ))) + ret = -EFAULT ;Since the only fields that are changed in the
kvm_sev_send_startstructure aresession_lenand policy, why do we need to copy the entire structure back to the user? Why not just those two values? Please see the changes proposed tokvm_sev_send_startstructure further below to accomplish this.
Quoted 6 lines
I think we also need to consider the code readability while saving the CPU cycles. This is very small structure. By duplicating into two calls #1 copy params.policy and #2 copy params.session_len we will add more CPU cycle. And, If we get creative and rearrange the structure then code readability is lost because now the copy will depend on how the structure is layout in the memory.
I was not recommending splitting that call into two. That would certainly be more expensive, than copying the entire structure. That is the reason why I suggested reordering the members of kvm_sev_send_start. Isn't there plenty of code where structures are defined in a way to keep the data movement efficient? :-)
Please see my other comment below.
Quoted 101 lines
Quoted 88 lines
params.policy = data->policy; if (copy_to_user((void __user *)(uintptr_t)argp->data, ¶ms, sizeof(params.policy) + sizeof(params.session_len)) ret = -EFAULT;Quoted 56 lines
arch/x86/kvm/svm.c +17
-0+ +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;@@ -7193,6 +7318,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@@ -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 +1
-0@@ -1558,6 +1558,18 @@ struct kvm_sev_dbg {__u32 len; }; +struct kvm_sev_send_start {include/uapi/linux/kvm.h struct kvm_sev_dbg { · line 1571
Quoted 10 lines
include/uapi/linux/kvm.h +10
-0+__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; +};Redo this structure as below:
struct kvm_sev_send_start { __u32 policy; __u32 session_len; __u64 session_uaddr; __u64 pdh_cert_uaddr; __u32 pdh_cert_len; __u64 plat_certs_uaddr; __u32 plat_certs_len; __u64 amd_certs_uaddr; __u32 amd_certs_len; };Or as below, just to make it look better.
Quoted 12 lines
struct kvm_sev_send_start { __u32 policy; __u32 session_len; __u64 session_uaddr; __u32 pdh_cert_len; __u64 pdh_cert_uaddr; __u32 plat_certs_len; __u64 plat_certs_uaddr; __u32 amd_certs_len; __u64 amd_certs_uaddr; };
Quoted 4 lines
Wherever applicable, I tried best to not divert from the SEV spec structure layout. Anyone who is reading the SEV FW spec will see a similar structure layout in the KVM/PSP header files. I would prefer to stick to that approach.
This structure is in uapi, and is anyway different from the sev_data_send_start, right? Does it really need to stay close to the firmware structure layout? Just because the firmware folks thought of a structure layout, that should not prevent our code to be efficient.
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
include/uapi/linux/kvm.h +1
-0+#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
Re: [PATCH v6 01/14] KVM: SVM: Add KVM_SEV SEND_START command
Quoted 207 lines · Venu Busireddy
Quoted 198 lines · Brijesh Singh
Hi Venu,
Thanks for the feedback.
Quoted 186 lines · Venu Busireddy
Quoted 41 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]>Reviewed-by Steve Rutherford <[email protected]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[email protected]>--- .../virt/kvm/amd-memory-encryption.rst | 27 ++++ arch/x86/kvm/svm.c | 128 ++++++++++++++++++ include/linux/psp-sev.h | 8 +- include/uapi/linux/kvm.h | 12 ++ 4 files changed, 171 insertions(+), 4 deletions(-)Documentation/virt/kvm/amd-memory-encryption.rst +8
-0@@ -263,6 +263,33 @@ Returns: 0 on success, -negative on error __u32 trans_len; }; +10. KVM_SEV_SEND_START +---------------------- + +The KVM_SEV_SEND_START command can be used by the hypervisor to create an +outgoing guest encryption context. + +Parameters (in): struct kvm_sev_send_start +Documentation/virt/kvm/amd-memory-encryption.rst Returns: 0 on success, -negative on error · line 283
Quoted 10 lines
Documentation/virt/kvm/amd-memory-encryption.rst +10
-0+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 */Could this please be changed to
plat_certs_uaddr, as it is referred to in the rest of the code?Documentation/virt/kvm/amd-memory-encryption.rst Returns: 0 on success, -negative on error · line 287
Quoted 4 lines
Documentation/virt/kvm/amd-memory-encryption.rst +4
-0+ __u32 plat_certs_len; + + __u64 amd_certs_uaddr; /* AMD certificate */ + __u32 amd_cert_len;Could this please be changed to
amd_certs_len, as it is referred to in the rest of the code?Quoted 111 lines
Documentation/virt/kvm/amd-memory-encryption.rst +5
-0+ + __u64 session_uaddr; /* Guest session information */ + __u32 session_len; + }; + References ==========arch/x86/kvm/svm.c +95
-0@@ -7149,6 +7149,131 @@ 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; +int ret; + +if (!sev_guest (kvm)) +return -ENOTTY ; + +if (copy_from_user (¶ms, (void __user *)(uintptr_t )argp->data, +sizeof (struct kvm_sev_send_start ))) +return -EFAULT ; + +/* if session_len is zero, userspace wants to query the session length */ +if (!params.session_len) +return __sev_send_start_query_session_length (kvm, argp, + ¶ms); + +/* 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); + 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; +arch/x86/kvm/svm.c static int sev_launch_secret(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 7256
Quoted 10 lines
arch/x86/kvm/svm.c +10
-0+ ret =sev_issue_cmd (kvm,SEV_CMD_SEND_START , data, &argp->error); + +if (ret) + goto e_free; + +if (copy_to_user ((void __user *)(uintptr_t ) params.session_uaddr, + session_data, params.session_len)) { + ret = -EFAULT ; + goto e_free; + }To optimize the amount of data being copied to user space, could the above section of code changed as follows?
Quoted 6 lines
params.session_len = data->session_len; if (copy_to_user((void __user *)(uintptr_t) params.session_uaddr, session_data, params.session_len)) { ret = -EFAULT; goto e_free; }Quoted 8 lines
We should not be using the
data->session_len, it will cause-EFAULTwhen user has not allocated enough space in thesession_uaddr. Lets consider the case where user passes session_len=10 but firmware thinks the session length should be 64. In that case thedata->session_lenwill contains a value of 64 but userspace has allocated space for 10 bytes andcopy_to_user()will fail. If we are really concern about the amount of data getting copied to userspace then usemin_t(size_t, params.session_len, data->session_len).
Quoted 6 lines
We are allocating a buffer of params.session_len size and passing that buffer, and that length via
data->session_len, to the firmware. Why would the firmware setdata->session_lento a larger value, in spite of telling it that the buffer is only params.session_len long? I thought that only the reverse is possible, that is, the user sets the params.session_len to the MAX, but the session data is actually smaller than that size.
The question is, how does a userspace know the session length ? One method is you can precalculate a value based on your firmware version and have userspace pass that, or another approach is set params.session_len = 0 and query it from the FW. The FW spec allow to query the length, please see the spec. In the qemu patches I choose second approach. This is because session blob can change from one FW version to another and I tried to avoid calculating or hardcoding the length for a one version of the FW. You can certainly choose the first method. We want to ensure that kernel interface works on the both cases.
Quoted 149 lines
Also, if for whatever reason the firmware sets
data->session_lento a larger value than what is passed, what is the user space expected to do when the call returns? If the user space tries to access params.session_len amount of data, it will possibly get a memory access violation, because it did not originally allocate that large a buffer.If we do go with using
min_t(size_t, params.session_len, data->session_len), then params.session_len should also be set to the smaller of the two, right?Quoted 10 lines
Quoted 10 lines
arch/x86/kvm/svm.c static int sev_launch_secret(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 7262
Quoted 6 lines
arch/x86/kvm/svm.c +6
-0+ + params.policy = data->policy; + params.session_len = data->session_len; +if (copy_to_user ((void __user *)(uintptr_t )argp->data, ¶ms, +sizeof (struct kvm_sev_send_start ))) + ret = -EFAULT ;Since the only fields that are changed in the
kvm_sev_send_startstructure aresession_lenand policy, why do we need to copy the entire structure back to the user? Why not just those two values? Please see the changes proposed tokvm_sev_send_startstructure further below to accomplish this.Quoted 6 lines
I think we also need to consider the code readability while saving the CPU cycles. This is very small structure. By duplicating into two calls #1 copy params.policy and #2 copy params.session_len we will add more CPU cycle. And, If we get creative and rearrange the structure then code readability is lost because now the copy will depend on how the structure is layout in the memory.
I was not recommending splitting that call into two. That would certainly be more expensive, than copying the entire structure. That is the reason why I suggested reordering the members of
kvm_sev_send_start. Isn't there plenty of code where structures are defined in a way to keep the data movement efficient? :-)Please see my other comment below.
Quoted 98 lines
Quoted 86 lines
params.policy = data->policy; if (copy_to_user((void __user *)(uintptr_t)argp->data, ¶ms, sizeof(params.policy) + sizeof(params.session_len)) ret = -EFAULT;Quoted 56 lines
arch/x86/kvm/svm.c +17
-0+ +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;@@ -7193,6 +7318,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@@ -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 +1
-0@@ -1558,6 +1558,18 @@ struct kvm_sev_dbg {__u32 len; }; +struct kvm_sev_send_start {include/uapi/linux/kvm.h struct kvm_sev_dbg { · line 1571
Quoted 10 lines
include/uapi/linux/kvm.h +10
-0+__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; +};Redo this structure as below:
struct kvm_sev_send_start { __u32 policy; __u32 session_len; __u64 session_uaddr; __u64 pdh_cert_uaddr; __u32 pdh_cert_len; __u64 plat_certs_uaddr; __u32 plat_certs_len; __u64 amd_certs_uaddr; __u32 amd_certs_len; };Or as below, just to make it look better.
Quoted 12 lines
struct kvm_sev_send_start { __u32 policy; __u32 session_len; __u64 session_uaddr; __u32 pdh_cert_len; __u64 pdh_cert_uaddr; __u32 plat_certs_len; __u64 plat_certs_uaddr; __u32 amd_certs_len; __u64 amd_certs_uaddr; };Quoted 4 lines
Wherever applicable, I tried best to not divert from the SEV spec structure layout. Anyone who is reading the SEV FW spec will see a similar structure layout in the KVM/PSP header files. I would prefer to stick to that approach.
This structure is in uapi, and is anyway different from the
sev_data_send_start, right? Does it really need to stay close to the firmware structure layout? Just because the firmware folks thought of a structure layout, that should not prevent our code to be efficient.Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
include/uapi/linux/kvm.h +1
-0+#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
Re: [PATCH v6 01/14] KVM: SVM: Add KVM_SEV SEND_START command
Quoted 217 lines · Brijesh Singh
Quoted 207 lines · Venu Busireddy
Quoted 198 lines · Brijesh Singh
Hi Venu,
Thanks for the feedback.
Quoted 186 lines · Venu Busireddy
Quoted 41 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]>Reviewed-by Steve Rutherford <[email protected]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[email protected]>--- .../virt/kvm/amd-memory-encryption.rst | 27 ++++ arch/x86/kvm/svm.c | 128 ++++++++++++++++++ include/linux/psp-sev.h | 8 +- include/uapi/linux/kvm.h | 12 ++ 4 files changed, 171 insertions(+), 4 deletions(-)Documentation/virt/kvm/amd-memory-encryption.rst +8
-0@@ -263,6 +263,33 @@ Returns: 0 on success, -negative on error __u32 trans_len; }; +10. KVM_SEV_SEND_START +---------------------- + +The KVM_SEV_SEND_START command can be used by the hypervisor to create an +outgoing guest encryption context. + +Parameters (in): struct kvm_sev_send_start +Documentation/virt/kvm/amd-memory-encryption.rst Returns: 0 on success, -negative on error · line 283
Quoted 10 lines
Documentation/virt/kvm/amd-memory-encryption.rst +10
-0+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 */Could this please be changed to
plat_certs_uaddr, as it is referred to in the rest of the code?Documentation/virt/kvm/amd-memory-encryption.rst Returns: 0 on success, -negative on error · line 287
Quoted 4 lines
Documentation/virt/kvm/amd-memory-encryption.rst +4
-0+ __u32 plat_certs_len; + + __u64 amd_certs_uaddr; /* AMD certificate */ + __u32 amd_cert_len;Could this please be changed to
amd_certs_len, as it is referred to in the rest of the code?Quoted 111 lines
Documentation/virt/kvm/amd-memory-encryption.rst +5
-0+ + __u64 session_uaddr; /* Guest session information */ + __u32 session_len; + }; + References ==========arch/x86/kvm/svm.c +95
-0@@ -7149,6 +7149,131 @@ 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; +int ret; + +if (!sev_guest (kvm)) +return -ENOTTY ; + +if (copy_from_user (¶ms, (void __user *)(uintptr_t )argp->data, +sizeof (struct kvm_sev_send_start ))) +return -EFAULT ; + +/* if session_len is zero, userspace wants to query the session length */ +if (!params.session_len) +return __sev_send_start_query_session_length (kvm, argp, + ¶ms); + +/* 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); + 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; +arch/x86/kvm/svm.c static int sev_launch_secret(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 7256
Quoted 10 lines
arch/x86/kvm/svm.c +10
-0+ ret =sev_issue_cmd (kvm,SEV_CMD_SEND_START , data, &argp->error); + +if (ret) + goto e_free; + +if (copy_to_user ((void __user *)(uintptr_t ) params.session_uaddr, + session_data, params.session_len)) { + ret = -EFAULT ; + goto e_free; + }To optimize the amount of data being copied to user space, could the above section of code changed as follows?
Quoted 6 lines
params.session_len = data->session_len; if (copy_to_user((void __user *)(uintptr_t) params.session_uaddr, session_data, params.session_len)) { ret = -EFAULT; goto e_free; }Quoted 8 lines
We should not be using the
data->session_len, it will cause-EFAULTwhen user has not allocated enough space in thesession_uaddr. Lets consider the case where user passes session_len=10 but firmware thinks the session length should be 64. In that case thedata->session_lenwill contains a value of 64 but userspace has allocated space for 10 bytes andcopy_to_user()will fail. If we are really concern about the amount of data getting copied to userspace then usemin_t(size_t, params.session_len, data->session_len).Quoted 6 lines
We are allocating a buffer of params.session_len size and passing that buffer, and that length via
data->session_len, to the firmware. Why would the firmware setdata->session_lento a larger value, in spite of telling it that the buffer is only params.session_len long? I thought that only the reverse is possible, that is, the user sets the params.session_len to the MAX, but the session data is actually smaller than that size.
Quoted 9 lines
The question is, how does a userspace know the session length ? One method is you can precalculate a value based on your firmware version and have userspace pass that, or another approach is set params.session_len = 0 and query it from the FW. The FW spec allow to query the length, please see the spec. In the qemu patches I choose second approach. This is because session blob can change from one FW version to another and I tried to avoid calculating or hardcoding the length for a one version of the FW. You can certainly choose the first method. We want to ensure that kernel interface works on the both cases.
I like the fact that you have already implemented the functionality to facilitate the user space to obtain the session length from the firmware (by setting params.session_len to 0). However, I am trying to address the case where the user space sets the params.session_len to a size smaller than the size needed.
Let me put it differently. Let us say that the session blob needs 128 bytes, but the user space sets params.session_len to 16. That results in us allocating a buffer of 16 bytes, and set data->session_len to 16.
What does the firmware do now?
Does it copy 128 bytes into data->session_address, or, does it copy 16 bytes?
If it copies 128 bytes, we most certainly will end up with a kernel crash.
If it copies 16 bytes, then what does it set in data->session_len? 16, or 128? If 16, everything is good. If 128, we end up causing memory access violation for the user space.
Perhaps, this can be dealt a little differently? Why not always call sev_issue_cmd(kvm, SEV_CMD_SEND_START, ...) with zeroed out data? Then, if the user space has set params.session_len to 0, we return with the needed params.session_len. Otherwise, we check if params.session_len is large enough, and if not, we return -EINVAL?
Quoted 151 lines
Quoted 151 lines
Also, if for whatever reason the firmware sets
data->session_lento a larger value than what is passed, what is the user space expected to do when the call returns? If the user space tries to access params.session_len amount of data, it will possibly get a memory access violation, because it did not originally allocate that large a buffer.If we do go with using
min_t(size_t, params.session_len, data->session_len), then params.session_len should also be set to the smaller of the two, right?Quoted 10 lines
Quoted 10 lines
arch/x86/kvm/svm.c static int sev_launch_secret(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 7262
Quoted 6 lines
arch/x86/kvm/svm.c +6
-0+ + params.policy = data->policy; + params.session_len = data->session_len; +if (copy_to_user ((void __user *)(uintptr_t )argp->data, ¶ms, +sizeof (struct kvm_sev_send_start ))) + ret = -EFAULT ;Since the only fields that are changed in the
kvm_sev_send_startstructure aresession_lenand policy, why do we need to copy the entire structure back to the user? Why not just those two values? Please see the changes proposed tokvm_sev_send_startstructure further below to accomplish this.Quoted 6 lines
I think we also need to consider the code readability while saving the CPU cycles. This is very small structure. By duplicating into two calls #1 copy params.policy and #2 copy params.session_len we will add more CPU cycle. And, If we get creative and rearrange the structure then code readability is lost because now the copy will depend on how the structure is layout in the memory.
I was not recommending splitting that call into two. That would certainly be more expensive, than copying the entire structure. That is the reason why I suggested reordering the members of
kvm_sev_send_start. Isn't there plenty of code where structures are defined in a way to keep the data movement efficient? :-)Please see my other comment below.
Quoted 98 lines
Quoted 86 lines
params.policy = data->policy; if (copy_to_user((void __user *)(uintptr_t)argp->data, ¶ms, sizeof(params.policy) + sizeof(params.session_len)) ret = -EFAULT;Quoted 56 lines
arch/x86/kvm/svm.c +17
-0+ +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;@@ -7193,6 +7318,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@@ -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 +1
-0@@ -1558,6 +1558,18 @@ struct kvm_sev_dbg {__u32 len; }; +struct kvm_sev_send_start {include/uapi/linux/kvm.h struct kvm_sev_dbg { · line 1571
Quoted 10 lines
include/uapi/linux/kvm.h +10
-0+__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; +};Redo this structure as below:
struct kvm_sev_send_start { __u32 policy; __u32 session_len; __u64 session_uaddr; __u64 pdh_cert_uaddr; __u32 pdh_cert_len; __u64 plat_certs_uaddr; __u32 plat_certs_len; __u64 amd_certs_uaddr; __u32 amd_certs_len; };Or as below, just to make it look better.
Quoted 12 lines
struct kvm_sev_send_start { __u32 policy; __u32 session_len; __u64 session_uaddr; __u32 pdh_cert_len; __u64 pdh_cert_uaddr; __u32 plat_certs_len; __u64 plat_certs_uaddr; __u32 amd_certs_len; __u64 amd_certs_uaddr; };Quoted 4 lines
Wherever applicable, I tried best to not divert from the SEV spec structure layout. Anyone who is reading the SEV FW spec will see a similar structure layout in the KVM/PSP header files. I would prefer to stick to that approach.
This structure is in uapi, and is anyway different from the
sev_data_send_start, right? Does it really need to stay close to the firmware structure layout? Just because the firmware folks thought of a structure layout, that should not prevent our code to be efficient.Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
include/uapi/linux/kvm.h +1
-0+#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
Re: [PATCH v6 01/14] KVM: SVM: Add KVM_SEV SEND_START command
On 4/2/20 1:57 PM, Venu Busireddy wrote:
[snip]...
Quoted 21 lines
Quoted 9 lines
The question is, how does a userspace know the session length ? One method is you can precalculate a value based on your firmware version and have userspace pass that, or another approach is set params.session_len = 0 and query it from the FW. The FW spec allow to query the length, please see the spec. In the qemu patches I choose second approach. This is because session blob can change from one FW version to another and I tried to avoid calculating or hardcoding the length for a one version of the FW. You can certainly choose the first method. We want to ensure that kernel interface works on the both cases.
I like the fact that you have already implemented the functionality to facilitate the user space to obtain the session length from the firmware (by setting params.session_len to 0). However, I am trying to address the case where the user space sets the params.session_len to a size smaller than the size needed.
Let me put it differently. Let us say that the session blob needs 128 bytes, but the user space sets params.session_len to 16. That results in us allocating a buffer of 16 bytes, and set
data->session_lento 16.What does the firmware do now?
Quoted 8 lines
Does it copy 128 bytes into
data->session_address, or, does it copy 16 bytes?If it copies 128 bytes, we most certainly will end up with a kernel crash.
If it copies 16 bytes, then what does it set in
data->session_len? 16, or 128? If 16, everything is good. If 128, we end up causing memory access violation for the user space.
My interpretation of the spec is, if user provided length is smaller than the FW expected length then FW will reports an error with data->session_len set to the expected length. In other words, it should *not* copy anything into the session buffer in the event of failure. If FW is touching memory beyond what is specified in the session_len then its FW bug and we can't do much from kernel. Am I missing something ?
Quoted 6 lines
Perhaps, this can be dealt a little differently? Why not always call
sev_issue_cmd(kvm, SEV_CMD_SEND_START, ...)with zeroed out data? Then, if the user space has set params.session_len to 0, we return with the needed params.session_len. Otherwise, we check if params.session_len is large enough, and if not, we return-EINVAL?
Re: [PATCH v6 01/14] KVM: SVM: Add KVM_SEV SEND_START command
Quoted 30 lines · Brijesh Singh
On 4/2/20 1:57 PM, Venu Busireddy wrote:
[snip]...Quoted 26 lines
Quoted 9 lines
The question is, how does a userspace know the session length ? One method is you can precalculate a value based on your firmware version and have userspace pass that, or another approach is set params.session_len = 0 and query it from the FW. The FW spec allow to query the length, please see the spec. In the qemu patches I choose second approach. This is because session blob can change from one FW version to another and I tried to avoid calculating or hardcoding the length for a one version of the FW. You can certainly choose the first method. We want to ensure that kernel interface works on the both cases.
I like the fact that you have already implemented the functionality to facilitate the user space to obtain the session length from the firmware (by setting params.session_len to 0). However, I am trying to address the case where the user space sets the params.session_len to a size smaller than the size needed.
Let me put it differently. Let us say that the session blob needs 128 bytes, but the user space sets params.session_len to 16. That results in us allocating a buffer of 16 bytes, and set
data->session_lento 16.What does the firmware do now?
Does it copy 128 bytes into
data->session_address, or, does it copy 16 bytes?If it copies 128 bytes, we most certainly will end up with a kernel crash.
Quoted 8 lines
Quoted 3 lines
If it copies 16 bytes, then what does it set in
data->session_len? 16, or 128? If 16, everything is good. If 128, we end up causing memory access violation for the user space.My interpretation of the spec is, if user provided length is smaller than the FW expected length then FW will reports an error with
data->session_lenset to the expected length. In other words, it should *not* copy anything into the session buffer in the event of failure.
That is good, and expected behavior.
Quoted 2 lines
If FW is touching memory beyond what is specified in the
session_lenthen its FW bug and we can't do much from kernel.
Agreed. But let us assume that the firmware is not touching memory that it is not supposed to.
Quoted 1 line
Am I missing something ?
I believe you are agreeing that if the session blob needs 128 bytes and user space sets params.session_len to 16, the firmware does not copy any data to data->session_address, and sets data->session_len to 128.
Now, when we return, won't the user space try to access 128 bytes (params.session_len) of data in params.session_uaddr, and crash? Because, instead of returning an error that buffer is not large enough, we return the call successfully!
That is why I was suggesting the following, which you seem to have missed.
Quoted 5 lines
Quoted 5 lines
Perhaps, this can be dealt a little differently? Why not always call
sev_issue_cmd(kvm, SEV_CMD_SEND_START, ...)with zeroed out data? Then, if the user space has set params.session_len to 0, we return with the needed params.session_len. Otherwise, we check if params.session_len is large enough, and if not, we return-EINVAL?
Doesn't the above approach address all scenarios?
Re: [PATCH v6 01/14] KVM: SVM: Add KVM_SEV SEND_START command
Quoted 45 lines · Venu Busireddy
Quoted 29 lines · Brijesh Singh
On 4/2/20 1:57 PM, Venu Busireddy wrote:
[snip]...Quoted 26 lines
Quoted 9 lines
The question is, how does a userspace know the session length ? One method is you can precalculate a value based on your firmware version and have userspace pass that, or another approach is set params.session_len = 0 and query it from the FW. The FW spec allow to query the length, please see the spec. In the qemu patches I choose second approach. This is because session blob can change from one FW version to another and I tried to avoid calculating or hardcoding the length for a one version of the FW. You can certainly choose the first method. We want to ensure that kernel interface works on the both cases.
I like the fact that you have already implemented the functionality to facilitate the user space to obtain the session length from the firmware (by setting params.session_len to 0). However, I am trying to address the case where the user space sets the params.session_len to a size smaller than the size needed.
Let me put it differently. Let us say that the session blob needs 128 bytes, but the user space sets params.session_len to 16. That results in us allocating a buffer of 16 bytes, and set
data->session_lento 16.What does the firmware do now?
Does it copy 128 bytes into
data->session_address, or, does it copy 16 bytes?If it copies 128 bytes, we most certainly will end up with a kernel crash.
Quoted 7 lines
Quoted 3 lines
If it copies 16 bytes, then what does it set in
data->session_len? 16, or 128? If 16, everything is good. If 128, we end up causing memory access violation for the user space.My interpretation of the spec is, if user provided length is smaller than the FW expected length then FW will reports an error with
data->session_lenset to the expected length. In other words, it should *not* copy anything into the session buffer in the event of failure.That is good, and expected behavior.
Quoted 2 lines
If FW is touching memory beyond what is specified in the
session_lenthen its FW bug and we can't do much from kernel.Agreed. But let us assume that the firmware is not touching memory that it is not supposed to.
Quoted 1 line
Am I missing something ?
Quoted 8 lines
I believe you are agreeing that if the session blob needs 128 bytes and user space sets params.session_len to 16, the firmware does not copy any data to
data->session_address, and setsdata->session_lento 128.Now, when we return, won't the user space try to access 128 bytes (params.session_len) of data in params.session_uaddr, and crash? Because, instead of returning an error that buffer is not large enough, we return the call successfully!
Ah, so the main issue is we should not be going to e_free on error. If session_len is less than the expected len then FW will return an error. In the case of an error we can skip copying the session_data into userspace buffer but we still need to pass the session_len and policy back to the userspace.
+ ret = sev_issue_cmd(kvm, SEV_CMD_SEND_START, data, &argp->error);
+
+ if (ret)
+ goto e_free;
+
If user space gets an error then it can decode it further to get additional information (e.g buffer too small).
Quoted 11 lines
That is why I was suggesting the following, which you seem to have missed.
Quoted 5 lines
Quoted 5 lines
Perhaps, this can be dealt a little differently? Why not always call
sev_issue_cmd(kvm, SEV_CMD_SEND_START, ...)with zeroed out data? Then, if the user space has set params.session_len to 0, we return with the needed params.session_len. Otherwise, we check if params.session_len is large enough, and if not, we return-EINVAL?Doesn't the above approach address all scenarios?
Re: [PATCH v6 01/14] KVM: SVM: Add KVM_SEV SEND_START command
Quoted 57 lines · Brijesh Singh
Quoted 45 lines · Venu Busireddy
Quoted 29 lines · Brijesh Singh
On 4/2/20 1:57 PM, Venu Busireddy wrote:
[snip]...Quoted 26 lines
Quoted 9 lines
The question is, how does a userspace know the session length ? One method is you can precalculate a value based on your firmware version and have userspace pass that, or another approach is set params.session_len = 0 and query it from the FW. The FW spec allow to query the length, please see the spec. In the qemu patches I choose second approach. This is because session blob can change from one FW version to another and I tried to avoid calculating or hardcoding the length for a one version of the FW. You can certainly choose the first method. We want to ensure that kernel interface works on the both cases.
I like the fact that you have already implemented the functionality to facilitate the user space to obtain the session length from the firmware (by setting params.session_len to 0). However, I am trying to address the case where the user space sets the params.session_len to a size smaller than the size needed.
Let me put it differently. Let us say that the session blob needs 128 bytes, but the user space sets params.session_len to 16. That results in us allocating a buffer of 16 bytes, and set
data->session_lento 16.What does the firmware do now?
Does it copy 128 bytes into
data->session_address, or, does it copy 16 bytes?If it copies 128 bytes, we most certainly will end up with a kernel crash.
Quoted 7 lines
Quoted 3 lines
If it copies 16 bytes, then what does it set in
data->session_len? 16, or 128? If 16, everything is good. If 128, we end up causing memory access violation for the user space.My interpretation of the spec is, if user provided length is smaller than the FW expected length then FW will reports an error with
data->session_lenset to the expected length. In other words, it should *not* copy anything into the session buffer in the event of failure.That is good, and expected behavior.
Quoted 2 lines
If FW is touching memory beyond what is specified in the
session_lenthen its FW bug and we can't do much from kernel.Agreed. But let us assume that the firmware is not touching memory that it is not supposed to.
Quoted 1 line
Am I missing something ?
Quoted 8 lines
I believe you are agreeing that if the session blob needs 128 bytes and user space sets params.session_len to 16, the firmware does not copy any data to
data->session_address, and setsdata->session_lento 128.Now, when we return, won't the user space try to access 128 bytes (params.session_len) of data in params.session_uaddr, and crash? Because, instead of returning an error that buffer is not large enough, we return the call successfully!
Quoted 5 lines
Ah, so the main issue is we should not be going to
e_freeon error. Ifsession_lenis less than the expected len then FW will return an error. In the case of an error we can skip copying thesession_datainto userspace buffer but we still need to pass thesession_lenand policy back to the userspace.
Sure, that is one way to fix the problem.
Quoted 21 lines
(quoted diff) +5
-0+ ret = sev_issue_cmd(kvm, SEV_CMD_SEND_START, data, &argp->error); + + if (ret) + goto e_free; +If user space gets an error then it can decode it further to get additional information (e.g buffer too small).
Quoted 11 lines
That is why I was suggesting the following, which you seem to have missed.
Quoted 5 lines
Quoted 5 lines
Perhaps, this can be dealt a little differently? Why not always call
sev_issue_cmd(kvm, SEV_CMD_SEND_START, ...)with zeroed out data? Then, if the user space has set params.session_len to 0, we return with the needed params.session_len. Otherwise, we check if params.session_len is large enough, and if not, we return-EINVAL?Doesn't the above approach address all scenarios?
Re: [PATCH v6 01/14] KVM: SVM: Add KVM_SEV SEND_START command
In reply to Ashish KalraQuoted 30 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]>Reviewed-by Steve Rutherford <[email protected]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[email protected]>--- .../virt/kvm/amd-memory-encryption.rst | 27 ++++ arch/x86/kvm/svm.c | 128 ++++++++++++++++++ include/linux/psp-sev.h | 8 +- include/uapi/linux/kvm.h | 12 ++ 4 files changed, 171 insertions(+), 4 deletions(-)Documentation/virt/kvm/amd-memory-encryption.rst +0
-0
Documentation/virt/kvm/amd-memory-encryption.rst Returns: 0 on success, -negative on error · line 270
Quoted 8 lines
Documentation/virt/kvm/amd-memory-encryption.rst +5
-0@@ -263,6 +263,33 @@ Returns: 0 on success, -negative on error __u32 trans_len; }; +10. KVM_SEV_SEND_START +---------------------- + +The KVM_SEV_SEND_START command can be used by the hypervisor to create an +outgoing guest encryption context.
Shouldn't we mention that this command is also used to save the guest to the disk ?
Quoted 30 lines
Documentation/virt/kvm/amd-memory-encryption.rst +22
-0+ +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 +0
-0
arch/x86/kvm/svm.c static int sev_launch_secret(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 7154
Quoted 6 lines
arch/x86/kvm/svm.c +3
-0@@ -7149,6 +7149,131 @@ 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,
__sev_query_send_start_session_length a better name perhaps ?
arch/x86/kvm/svm.c static int sev_launch_secret(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 7166
Quoted 12 lines
arch/x86/kvm/svm.c +12
-0+ 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);
We are not checking ret here as we are assuming that the command will always be successful. Is there any chance that sev->handle can be junk and should we have an ASSERT for it ?
arch/x86/kvm/svm.c static int sev_launch_secret(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 7177
Quoted 11 lines
arch/x86/kvm/svm.c +11
-0+ + 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)
For readability and ease of cscope searches, isn't it better to append "svm" to all these functions ?
It seems svm_sev_enabled() is an example of an appropriate naming style.
Quoted 14 lines
arch/x86/kvm/svm.c +14
-0+{ +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; +int ret; + +if (!sev_guest (kvm)) +return -ENOTTY ; + +if (copy_from_user (¶ms, (void __user *)(uintptr_t )argp->data, +sizeof (struct kvm_sev_send_start ))) +return -EFAULT ;
arch/x86/kvm/svm.c static int sev_launch_secret(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 7201
Quoted 10 lines
arch/x86/kvm/svm.c +10
-0+ +/* if session_len is zero, userspace wants to query the session length */ +if (!params.session_len) +return __sev_send_start_query_session_length (kvm, argp, + ¶ms); + +/* 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 ;
What if params.plat_certs_uaddr or params.amd_certs_uaddr is NULL ?
arch/x86/kvm/svm.c static int sev_launch_secret(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 7208
Quoted 7 lines
arch/x86/kvm/svm.c +7
-0+ +/* 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 */
You haven't added comments for plat_cert and amd_cert. Also, it's much more readable if you add block comments like,
/*
* PDH cert
*/
Quoted 86 lines
arch/x86/kvm/svm.c +71
-0+ 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); + 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); + +if (ret) + goto e_free; + +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, ¶ms, +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;@@ -7193,6 +7318,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 +0
-0
include/linux/psp-sev.h struct sev_data_send_start { · line 327
Quoted 7 lines
include/linux/psp-sev.h +2
-2@@ -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 */
It seems that the 'platform certificate' and the 'amd_certificate' are single entities, meaning only copy is there for the particular platform and particular the AMD product. Why are these plural then ?
Quoted 31 lines
include/linux/psp-sev.h +2
-2u32 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@@ -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)
Re: [PATCH v6 01/14] KVM: SVM: Add KVM_SEV SEND_START command
Quoted 27 lines · Krish Sadhukhan
Quoted 25 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]>Reviewed-by Steve Rutherford <[email protected]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[email protected]>---
.../virt/kvm/amd-memory-encryption.rst | 27 ++++ arch/x86/kvm/svm.c | 128 ++++++++++++++++++ include/linux/psp-sev.h | 8 +- include/uapi/linux/kvm.h | 12 ++4 files changed, 171 insertions(+), 4 deletions(-)
Quoted 16 lines
Quoted 12 lines
Documentation/virt/kvm/amd-memory-encryption.rst +0
-0@@ -263,6 +263,33 @@ Returns: 0 on success, -negative on error __u32 trans_len; };+10.
KVM_SEV_SEND_START
+----------------------Documentation/virt/kvm/amd-memory-encryption.rst +2
-0+ +The KVM_SEV_SEND_START command can be used by the hypervisor toQuoted 2 lines
create an
+outgoing guest encryption context.Shouldn't we mention that this command is also used to save the guest to the disk ?
No, because this command is not used for saving to the disk.
Quoted 52 lines
Quoted 38 lines
Documentation/virt/kvm/amd-memory-encryption.rst +10
-0+ +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; /* platformDiffie-Hellman certificate */
+__u32pdh_cert_len;
+
+__u64plat_certs_uadr; /* platform certificate chain */
+__u32plat_certs_len;Documentation/virt/kvm/amd-memory-encryption.rst +5
-0+ + __u64 amd_certs_uaddr; /* AMD certificate */ + __u32 amd_cert_len; + + __u64 session_uaddr; /* Guest sessioninformation */
+__u32session_len;
+ };
+
References
==========
diff --gita/arch/x86/kvm/svm.cb/arch/x86/kvm/svm.cindex50d1ebafe0b3..63d172e974ad100644arch/x86/kvm/svm.c +0
-0@@ -7149,6 +7149,131 @@ static int sev_launch_secret(struct kvm *kvm, struct kvm_sev_cmd *argp) return ret; }+/* Userspace wants to query session length. */
arch/x86/kvm/svm.c +2
-0+static int +__sev_send_start_query_session_length(struct kvm *kvm, structQuoted 1 line
kvm_sev_cmd*argp,
__sev_query_send_start_session_lengtha better name perhaps ?Quoted 9 lines
+ struct
kvm_sev_send_start*params)arch/x86/kvm/svm.c +8
-0+{ + 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;
Quoted 8 lines
Quoted 3 lines
+
+data->handle=sev->handle;
+ ret =sev_issue_cmd(kvm, SEV_CMD_SEND_START, data, &argp->error);We are not checking ret here as we are assuming that the command will always be successful. Is there any chance that
sev->handlecan be junk and should we have an ASSERT for it ?
Both failure and success of this command need to be propagated to the userspace. In the case of failure the FW may provide additional information which also need to be propagated to the userspace hence on failure we should not be asserting.
Quoted 17 lines
arch/x86/kvm/svm.c static int sev_launch_secret(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 7177
Quoted 12 lines
arch/x86/kvm/svm.c +11
-0+ + 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)For readability and ease of cscope searches, isn't it better to append "svm" to all these functions ?
Quoted 1 line
It seems
svm_sev_enabled()is an example of an appropriate naming style.
I don't have strong opinion to prepend or append "svm" to all these functions, it can be done outside this series. I personally don't see any strong reason to append svm at this time. The SEV is applicable to the SVM file only. There is a pending patch which moves all the SEV stuff in svm/sev.c.
Quoted 180 lines
Quoted 19 lines
arch/x86/kvm/svm.c +16
-0+{ + 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; + int ret; + + if (!sev_guest(kvm)) + return -ENOTTY; + + if (copy_from_user(¶ms, (void __user *)(uintptr_t)argp->data, + sizeof(struct kvm_sev_send_start))) + return -EFAULT; + + /* if session_len is zero, userspace wants to query the sessionlength */
+ if (!params.session_len)Quoted 8 lines
+ return
__sev_send_start_query_session_length(kvm, argp, + ¶ms);
+
+ /* 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;What if params.plat_certs_uaddr or params.amd_certs_uaddr is NULL ?
arch/x86/kvm/svm.c static int sev_launch_secret(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 7208
Quoted 7 lines
arch/x86/kvm/svm.c +7
-0+ + /* 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 */You haven't added comments for
plat_certandamd_cert. Also, it's much more readable if you add block comments like,/*
* PDH cert
*/
Quoted 87 lines
+
pdh_cert=psp_copy_user_blob(params.pdh_cert_uaddr, + params.pdh_cert_len);
+ if (IS_ERR(pdh_cert)) {
+ ret =PTR_ERR(pdh_cert);
+ gotoe_free_session;
+ }arch/x86/kvm/svm.c +62
-0+ + 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); + 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); + + if (ret) + goto e_free; + + 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, ¶ms, + 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){
structkvm_sev_cmdsev_cmd;arch/x86/kvm/svm.c +0
-0@@ -7193,6 +7318,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;
+ caseKVM_SEV_SEND_START:
+ r =sev_send_start(kvm, &sev_cmd);
+ break;
default:
r =-EINVAL;
goto out;include/linux/psp-sev.h +0
-0@@ -323,11 +323,11 @@ struct sev_data_send_start {Quoted 7 lines
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 */It seems that the 'platform certificate' and the '
amd_certificate' are single entities, meaning only copy is there for the particular platform and particular the AMD product. Why are these plural then ?Quoted 30 lines
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 +11
-0@@ -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)
PATCH v6 2/14 KVM: SVM: Add KVM_SEND_UPDATE_DATA command
In reply to Ashish KalraFrom: Brijesh Singh <[email protected]>
The command is used for encrypting the guest memory region using the encryption context created with KVM_SEV_SEND_START.
--- .../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
@@ -290,6 +290,30 @@ Returns: 0 on success, -negative on error __u32 session_len; }; +11. KVM_SEV_SEND_UPDATE_DATA +---------------------------- + +The KVM_SEV_SEND_UPDATE_DATA command can be used by the hypervisor to encrypt the +outgoing guest memory region with the encryption context creating using +KVM_SEV_SEND_START. + +Parameters (in): struct kvm_sev_send_update_data + +Returns: 0 on success, -negative on error + +:: + + struct kvm_sev_launch_send_update_data { + __u64 hdr_uaddr; /* userspace address containing the packet header */ + __u32 hdr_len; + + __u64 guest_uaddr; /* the source memory region to be encrypted */ + __u32 guest_len; + + __u64 trans_uaddr; /* the destition memory region */ + __u32 trans_len; + }; + References ==========
arch/x86/kvm/svm.c +132 -4
166 lines not on this page · raw message
include/uapi/linux/kvm.h +9 -0
@@ -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
Re: [PATCH v6 02/14] KVM: SVM: Add KVM_SEND_UPDATE_DATA command
Quoted 193 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]>Reviewed-by Steve Rutherford <[email protected]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[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@@ -290,6 +290,30 @@ Returns: 0 on success, -negative on error __u32 session_len; }; +11. KVM_SEV_SEND_UPDATE_DATA +---------------------------- + +The KVM_SEV_SEND_UPDATE_DATA command can be used by the hypervisor to encrypt the +outgoing guest memory region with the encryption context creating using +KVM_SEV_SEND_START. + +Parameters (in): struct kvm_sev_send_update_data + +Returns: 0 on success, -negative on error + +:: + + struct kvm_sev_launch_send_update_data { + __u64 hdr_uaddr; /* userspace address containing the packet header */ + __u32 hdr_len; + + __u64 guest_uaddr; /* the source memory region to be encrypted */ + __u32 guest_len; + + __u64 trans_uaddr; /* the destition memory region */ + __u32 trans_len; + }; + References ==========arch/x86/kvm/svm.c +101
-4@@ -428,6 +428,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)@@ -1232,16 +1233,22 @@ static int avic_ga_log_notifier(u32 ga_tag)static __init int sev_hardware_setup (void ) {struct sev_user_data_status *status; +u32 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 );@@ -7274,6 +7281,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 (¶ms, (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, ¶ms); + +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; +
arch/x86/kvm/svm.c static int sev_send_start(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 7383
Quoted 10 lines
arch/x86/kvm/svm.c +10
-0+ 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;
Shouldn't this be
goto e_free;
?
Quoted 54 lines
arch/x86/kvm/svm.c +21
-0+ } + +/* 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;@@ -7321,6 +7446,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 +9
-0@@ -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
Re: [PATCH v6 02/14] KVM: SVM: Add KVM_SEND_UPDATE_DATA command
In reply to Ashish KalraQuoted 79 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]>Reviewed-by Steve Rutherford <[email protected]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[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@@ -290,6 +290,30 @@ Returns: 0 on success, -negative on error __u32 session_len; }; +11. KVM_SEV_SEND_UPDATE_DATA +---------------------------- + +The KVM_SEV_SEND_UPDATE_DATA command can be used by the hypervisor to encrypt the +outgoing guest memory region with the encryption context creating using +KVM_SEV_SEND_START. + +Parameters (in): struct kvm_sev_send_update_data + +Returns: 0 on success, -negative on error + +:: + + struct kvm_sev_launch_send_update_data { + __u64 hdr_uaddr; /* userspace address containing the packet header */ + __u32 hdr_len; + + __u64 guest_uaddr; /* the source memory region to be encrypted */ + __u32 guest_len; + + __u64 trans_uaddr; /* the destition memory region */ + __u32 trans_len; + }; + References ==========arch/x86/kvm/svm.c +2
-0@@ -428,6 +428,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)@@ -1232,16 +1233,22 @@ static int avic_ga_log_notifier(u32 ga_tag)static __init int sev_hardware_setup (void ) {struct sev_user_data_status *status; +u32 eax, ebx;int rc;
arch/x86/kvm/svm.c static int avic_ga_log_notifier(u32 ga_tag) · line 1246
Quoted 10 lines
arch/x86/kvm/svm.c +8
-2-/* 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);
Will max_sev_asid and the max number of guests supported be the same number always ?
Quoted 43 lines
arch/x86/kvm/svm.c +30
-2if (!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 );@@ -7274,6 +7281,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) +{
arch/x86/kvm/svm.c static int sev_send_start(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 7322
Quoted 10 lines
arch/x86/kvm/svm.c +10
-0+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 ;
Do we need to check the following conditions here ?
"The platform must be in the PSTATE.WORKING state. The guest must be in the GSTATE.SUPDATE state."
Quoted 38 lines
arch/x86/kvm/svm.c +38
-0+ +if (copy_from_user (¶ms, (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, ¶ms); + +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;
arch/x86/kvm/svm.c static int sev_send_start(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 7370
Quoted 10 lines
arch/x86/kvm/svm.c +10
-0+ + 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;
Why not name the variable 'sev_cbit_mask' instead of sev_me_mask ?
Quoted 54 lines
arch/x86/kvm/svm.c +34
-0+ 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;@@ -7321,6 +7446,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 +2
-0@@ -1570,6 +1570,15 @@ struct kvm_sev_send_start {__u32 session_len; }; +struct kvm_sev_send_update_data { +__u64 hdr_uaddr;
include/uapi/linux/kvm.h struct kvm_sev_send_start { · line 1584
Quoted 10 lines
include/uapi/linux/kvm.h +7
-0+__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)
PATCH v6 3/14 KVM: SVM: Add KVM_SEV_SEND_FINISH command
In reply to Ashish KalraFrom: Brijesh Singh <[email protected]>
The command is used to finailize the encryption context created with KVM_SEV_SEND_START command.
--- .../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
@@ -314,6 +314,14 @@ Returns: 0 on success, -negative on error __u32 trans_len; }; +12. KVM_SEV_SEND_FINISH +------------------------ + +After completion of the migration flow, the KVM_SEV_SEND_FINISH command can be +issued by the hypervisor to delete the encryption context. + +Returns: 0 on success, -negative on error + References ==========
arch/x86/kvm/svm.c +23 -0
@@ -7399,6 +7399,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;
@@ -7449,6 +7469,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
Re: [PATCH v6 03/14] KVM: SVM: Add KVM_SEV_SEND_FINISH command
Quoted 29 lines · Ashish Kalra
From: Brijesh Singh <[email protected]>
The command is used to finailize the encryption context created with
KVM_SEV_SEND_STARTcommand.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]>Reviewed-by Steve Rutherford <[email protected]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[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 +0
-0
Documentation/virt/kvm/amd-memory-encryption.rst Returns: 0 on success, -negative on error · line 323
Quoted 10 lines
Documentation/virt/kvm/amd-memory-encryption.rst +7
-0@@ -314,6 +314,14 @@ Returns: 0 on success, -negative on error __u32 trans_len; }; +12. KVM_SEV_SEND_FINISH +------------------------ + +After completion of the migration flow, the KVM_SEV_SEND_FINISH command can be +issued by the hypervisor to delete the encryption context. + +Returns: 0 on success, -negative on error
Didn't notice this earlier. I would suggest changing all occurrences of "-negative" to either "negative" or "less than 0" in this file.
Quoted 48 lines
Documentation/virt/kvm/amd-memory-encryption.rst +1
-0+ References ==========arch/x86/kvm/svm.c +23
-0@@ -7399,6 +7399,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;@@ -7449,6 +7469,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
Re: [PATCH v6 03/14] KVM: SVM: Add KVM_SEV_SEND_FINISH command
In reply to Ashish KalraQuoted 75 lines · Ashish Kalra
From: Brijesh Singh <[email protected]>
The command is used to finailize the encryption context created with
KVM_SEV_SEND_STARTcommand.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]>Reviewed-by Steve Rutherford <[email protected]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[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@@ -314,6 +314,14 @@ Returns: 0 on success, -negative on error __u32 trans_len; }; +12. KVM_SEV_SEND_FINISH +------------------------ + +After completion of the migration flow, the KVM_SEV_SEND_FINISH command can be +issued by the hypervisor to delete the encryption context. + +Returns: 0 on success, -negative on error + References ==========arch/x86/kvm/svm.c +20
-0@@ -7399,6 +7399,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;
arch/x86/kvm/svm.c static int svm_mem_enc_op(struct kvm *kvm, void __user *argp) · line 7477
Quoted 9 lines
arch/x86/kvm/svm.c +3
-0@@ -7449,6 +7469,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;
PATCH v6 4/14 KVM: SVM: Add support for KVM_SEV_RECEIVE_START command
In reply to Ashish KalraFrom: 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.
--- .../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
@@ -322,6 +322,35 @@ issued by the hypervisor to delete the encryption context. Returns: 0 on success, -negative on error +13. KVM_SEV_RECEIVE_START +------------------------ + +The KVM_SEV_RECEIVE_START command is used for creating the memory encryption +context for an incoming SEV guest. To create the encryption context, the user must +provide a guest policy, the platform public Diffie-Hellman (PDH) key and session +information. + +Parameters: struct kvm_sev_receive_start (in/out) + +Returns: 0 on success, -negative on error + +:: + + struct kvm_sev_receive_start { + __u32 handle; /* if zero then firmware creates a new handle */ + __u32 policy; /* guest's policy */ + + __u64 pdh_uaddr; /* userspace address pointing to the PDH key */ + __u32 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
@@ -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
Re: [PATCH v6 04/14] KVM: SVM: Add support for KVM_SEV_RECEIVE_START command
Quoted 44 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]>Reviewed-by Steve Rutherford <[email protected]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[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 +10
-0@@ -322,6 +322,35 @@ issued by the hypervisor to delete the encryption context. Returns: 0 on success, -negative on error +13. KVM_SEV_RECEIVE_START +------------------------ + +The KVM_SEV_RECEIVE_START command is used for creating the memory encryption +context for an incoming SEV guest. To create the encryption context, the user must +provide a guest policy, the platform public Diffie-Hellman (PDH) key and session +information. + +Parameters: struct kvm_sev_receive_start (in/out) +
Documentation/virt/kvm/amd-memory-encryption.rst issued by the hypervisor to delete the encryption context. · line 344
Quoted 10 lines
Documentation/virt/kvm/amd-memory-encryption.rst +10
-0+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;
Could dh_len be changed to pdh_len, to match the names in kvm_sev_receive_start in include/uapi/linux/kvm.h?
Documentation/virt/kvm/amd-memory-encryption.rst issued by the hypervisor to delete the encryption context. · line 346
Quoted 2 lines
Documentation/virt/kvm/amd-memory-encryption.rst +2
-0+ + __u64 session_addr; /* userspace address which points to the guest session information */
Also, session_addr to session_uaddr?
Quoted 132 lines
Documentation/virt/kvm/amd-memory-encryption.rst +7
-0+ __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@@ -7419,6 +7419,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 (¶ms, (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; + 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 */ + ret =__sev_issue_cmd (argp->sev_fd,SEV_CMD_RECEIVE_START , start, + error); +if (ret) + goto e_free; + +/* Bind ASID to this guest */ + 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, + ¶ms,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;@@ -7472,6 +7550,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
-0@@ -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
Re: [PATCH v6 04/14] KVM: SVM: Add support for KVM_SEV_RECEIVE_START command
In reply to Ashish KalraQuoted 170 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]>Reviewed-by Steve Rutherford <[email protected]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[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@@ -322,6 +322,35 @@ issued by the hypervisor to delete the encryption context. Returns: 0 on success, -negative on error +13. KVM_SEV_RECEIVE_START +------------------------ + +The KVM_SEV_RECEIVE_START command is used for creating the memory encryption +context for an incoming SEV guest. To create the encryption context, the user must +provide a guest policy, the platform public Diffie-Hellman (PDH) key and session +information. + +Parameters: struct kvm_sev_receive_start (in/out) + +Returns: 0 on success, -negative on error + +:: + + struct kvm_sev_receive_start { + __u32 handle; /* if zero then firmware creates a new handle */ + __u32 policy; /* guest's policy */ + + __u64 pdh_uaddr; /* userspace address pointing to the PDH key */ + __u32 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@@ -7419,6 +7419,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 (¶ms, (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; + 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 */ + ret =__sev_issue_cmd (argp->sev_fd,SEV_CMD_RECEIVE_START , start, + error); +if (ret) + goto e_free; + +/* Bind ASID to this guest */ + 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, + ¶ms,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;@@ -7472,6 +7550,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 +0
-0
include/uapi/linux/kvm.h struct kvm_sev_send_update_data { · line 1586
Quoted 8 lines
include/uapi/linux/kvm.h +5
-0@@ -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;
Why not 'pdh_cert_uaddr' and 'pdh_cert_len' ? That's the naming convention you have followed in previous patches.
include/uapi/linux/kvm.h struct kvm_sev_send_update_data { · line 1593
Quoted 7 lines
include/uapi/linux/kvm.h +4
-0+__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)
PATCH v6 5/14 KVM: SVM: Add KVM_SEV_RECEIVE_UPDATE_DATA command
In reply to Ashish KalraFrom: Brijesh Singh <[email protected]>
The command is used for copying the incoming buffer into the SEV guest memory space.
--- .../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
@@ -351,6 +351,30 @@ On success, the 'handle' field contains a new handle and on error, a negative va For more details, see SEV spec Section 6.12. +14. KVM_SEV_RECEIVE_UPDATE_DATA +---------------------------- + +The KVM_SEV_RECEIVE_UPDATE_DATA command can be used by the hypervisor to copy +the incoming buffers into the guest memory region with encryption context +created during the KVM_SEV_RECEIVE_START. + +Parameters (in): struct kvm_sev_receive_update_data + +Returns: 0 on success, -negative on error + +:: + + struct kvm_sev_launch_receive_update_data { + __u64 hdr_uaddr; /* userspace address containing the packet header */ + __u32 hdr_len; + + __u64 guest_uaddr; /* the destination guest memory region */ + __u32 guest_len; + + __u64 trans_uaddr; /* the incoming buffer memory region */ + __u32 trans_len; + }; + References ==========
arch/x86/kvm/svm.c +79 -0
91 lines not on this page · raw message
include/uapi/linux/kvm.h +9 -0
@@ -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
Re: [PATCH v6 05/14] KVM: SVM: Add KVM_SEV_RECEIVE_UPDATE_DATA command
Quoted 166 lines · 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]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[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@@ -351,6 +351,30 @@ On success, the 'handle' field contains a new handle and on error, a negative va For more details, see SEV spec Section 6.12. +14. KVM_SEV_RECEIVE_UPDATE_DATA +---------------------------- + +The KVM_SEV_RECEIVE_UPDATE_DATA command can be used by the hypervisor to copy +the incoming buffers into the guest memory region with encryption context +created during the KVM_SEV_RECEIVE_START. + +Parameters (in): struct kvm_sev_receive_update_data + +Returns: 0 on success, -negative on error + +:: + + struct kvm_sev_launch_receive_update_data { + __u64 hdr_uaddr; /* userspace address containing the packet header */ + __u32 hdr_len; + + __u64 guest_uaddr; /* the destination guest memory region */ + __u32 guest_len; + + __u64 trans_uaddr; /* the incoming buffer memory region */ + __u32 trans_len; + }; + References ==========arch/x86/kvm/svm.c +79
-0@@ -7497,6 +7497,82 @@ static int sev_receive_start(struct kvm *kvm, struct kvm_sev_cmd *argp)return ret; } +static int sev_receive_update_data (struct kvm *kvm,struct kvm_sev_cmd *argp) +{ +struct kvm_sev_info *sev = &to_kvm_svm (kvm)->sev_info; +struct kvm_sev_receive_update_data params; +struct sev_data_receive_update_data *data; +void *hdr =NULL , *trans =NULL ; +struct page **guest_page; +unsigned long n; +int ret, offset; + +if (!sev_guest (kvm)) +return -EINVAL ; + +if (copy_from_user (¶ms, (void __user *)(uintptr_t )argp->data, +sizeof (struct kvm_sev_receive_update_data ))) +return -EFAULT ; + +if (!params.hdr_uaddr || !params.hdr_len || + !params.guest_uaddr || !params.guest_len || + !params.trans_uaddr || !params.trans_len) +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 ; + + hdr =psp_copy_user_blob (params.hdr_uaddr, params.hdr_len); +if (IS_ERR (hdr)) +return PTR_ERR (hdr); + + trans =psp_copy_user_blob (params.trans_uaddr, params.trans_len); +if (IS_ERR (trans)) { + ret =PTR_ERR (trans); + goto e_free_hdr; + } + + ret = -ENOMEM ; + data =kzalloc (sizeof (*data),GFP_KERNEL ); +if (!data) + goto e_free_trans; + + data->hdr_address =__psp_pa (hdr); + data->hdr_len = params.hdr_len; + data->trans_address =__psp_pa (trans); + data->trans_len = params.trans_len; + +/* Pin guest memory */ + ret = -EFAULT ; + guest_page =sev_pin_memory (kvm, params.guest_uaddr &PAGE_MASK , +PAGE_SIZE , &n,0 ); +if (!guest_page) + goto e_free; + +/* The RECEIVE_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_RECEIVE_UPDATE_DATA , data, + &argp->error); + +sev_unpin_memory (kvm, guest_page, n); + +e_free: +kfree (data); +e_free_trans: +kfree (trans); +e_free_hdr: +kfree (hdr); + +return ret; +} +static int svm_mem_enc_op (struct kvm *kvm,void __user *argp) {struct kvm_sev_cmd sev_cmd;@@ -7553,6 +7629,9 @@ static int svm_mem_enc_op(struct kvm *kvm, void __user *argp)case KVM_SEV_RECEIVE_START : r =sev_receive_start (kvm, &sev_cmd);break ; +case KVM_SEV_RECEIVE_UPDATE_DATA : + r =sev_receive_update_data (kvm, &sev_cmd); +break ;default : r = -EINVAL ; goto out;include/uapi/linux/kvm.h +2
-0@@ -1588,6 +1588,15 @@ struct kvm_sev_receive_start {__u32 session_len; }; +struct kvm_sev_receive_update_data { +__u64 hdr_uaddr;
include/uapi/linux/kvm.h struct kvm_sev_receive_start { · line 1602
Quoted 10 lines
include/uapi/linux/kvm.h +7
-0+__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)
Re: [PATCH v6 05/14] KVM: SVM: Add KVM_SEV_RECEIVE_UPDATE_DATA command
In reply to Ashish KalraQuoted 5 lines · Ashish Kalra
From: Brijesh Singh <[email protected]>
The command is used for copying the incoming buffer into the SEV guest memory space.
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]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[email protected]>
Quoted 161 lines
--- .../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@@ -351,6 +351,30 @@ On success, the 'handle' field contains a new handle and on error, a negative va For more details, see SEV spec Section 6.12. +14. KVM_SEV_RECEIVE_UPDATE_DATA +---------------------------- + +The KVM_SEV_RECEIVE_UPDATE_DATA command can be used by the hypervisor to copy +the incoming buffers into the guest memory region with encryption context +created during the KVM_SEV_RECEIVE_START. + +Parameters (in): struct kvm_sev_receive_update_data + +Returns: 0 on success, -negative on error + +:: + + struct kvm_sev_launch_receive_update_data { + __u64 hdr_uaddr; /* userspace address containing the packet header */ + __u32 hdr_len; + + __u64 guest_uaddr; /* the destination guest memory region */ + __u32 guest_len; + + __u64 trans_uaddr; /* the incoming buffer memory region */ + __u32 trans_len; + }; + References ==========arch/x86/kvm/svm.c +79
-0@@ -7497,6 +7497,82 @@ static int sev_receive_start(struct kvm *kvm, struct kvm_sev_cmd *argp)return ret; } +static int sev_receive_update_data (struct kvm *kvm,struct kvm_sev_cmd *argp) +{ +struct kvm_sev_info *sev = &to_kvm_svm (kvm)->sev_info; +struct kvm_sev_receive_update_data params; +struct sev_data_receive_update_data *data; +void *hdr =NULL , *trans =NULL ; +struct page **guest_page; +unsigned long n; +int ret, offset; + +if (!sev_guest (kvm)) +return -EINVAL ; + +if (copy_from_user (¶ms, (void __user *)(uintptr_t )argp->data, +sizeof (struct kvm_sev_receive_update_data ))) +return -EFAULT ; + +if (!params.hdr_uaddr || !params.hdr_len || + !params.guest_uaddr || !params.guest_len || + !params.trans_uaddr || !params.trans_len) +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 ; + + hdr =psp_copy_user_blob (params.hdr_uaddr, params.hdr_len); +if (IS_ERR (hdr)) +return PTR_ERR (hdr); + + trans =psp_copy_user_blob (params.trans_uaddr, params.trans_len); +if (IS_ERR (trans)) { + ret =PTR_ERR (trans); + goto e_free_hdr; + } + + ret = -ENOMEM ; + data =kzalloc (sizeof (*data),GFP_KERNEL ); +if (!data) + goto e_free_trans; + + data->hdr_address =__psp_pa (hdr); + data->hdr_len = params.hdr_len; + data->trans_address =__psp_pa (trans); + data->trans_len = params.trans_len; + +/* Pin guest memory */ + ret = -EFAULT ; + guest_page =sev_pin_memory (kvm, params.guest_uaddr &PAGE_MASK , +PAGE_SIZE , &n,0 ); +if (!guest_page) + goto e_free; + +/* The RECEIVE_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_RECEIVE_UPDATE_DATA , data, + &argp->error); + +sev_unpin_memory (kvm, guest_page, n); + +e_free: +kfree (data); +e_free_trans: +kfree (trans); +e_free_hdr: +kfree (hdr); + +return ret; +} +static int svm_mem_enc_op (struct kvm *kvm,void __user *argp) {struct kvm_sev_cmd sev_cmd;@@ -7553,6 +7629,9 @@ static int svm_mem_enc_op(struct kvm *kvm, void __user *argp)case KVM_SEV_RECEIVE_START : r =sev_receive_start (kvm, &sev_cmd);break ; +case KVM_SEV_RECEIVE_UPDATE_DATA : + r =sev_receive_update_data (kvm, &sev_cmd); +break ;default : r = -EINVAL ; goto out;include/uapi/linux/kvm.h +9
-0@@ -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
Steve Rutherford
Reviewed-byRe: [PATCH v6 05/14] KVM: SVM: Add KVM_SEV_RECEIVE_UPDATE_DATA command
Quoted 90 lines · Venu Busireddy
Quoted 5 lines · Ashish Kalra
From: Brijesh Singh <[email protected]>
The command is used for copying the incoming buffer into the SEV guest memory space.
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]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[email protected]>Reviewed-by Venu Busireddy <[email protected]>Quoted 67 lines
--- .../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@@ -351,6 +351,30 @@ On success, the 'handle' field contains a new handle and on error, a negative va For more details, see SEV spec Section 6.12. +14. KVM_SEV_RECEIVE_UPDATE_DATA +---------------------------- + +The KVM_SEV_RECEIVE_UPDATE_DATA command can be used by the hypervisor to copy +the incoming buffers into the guest memory region with encryption context +created during the KVM_SEV_RECEIVE_START. + +Parameters (in): struct kvm_sev_receive_update_data + +Returns: 0 on success, -negative on error + +:: + + struct kvm_sev_launch_receive_update_data { + __u64 hdr_uaddr; /* userspace address containing the packet header */ + __u32 hdr_len; + + __u64 guest_uaddr; /* the destination guest memory region */ + __u32 guest_len; + + __u64 trans_uaddr; /* the incoming buffer memory region */ + __u32 trans_len; + }; + References ==========arch/x86/kvm/svm.c +18
-0@@ -7497,6 +7497,82 @@ static int sev_receive_start(struct kvm *kvm, struct kvm_sev_cmd *argp)return ret; } +static int sev_receive_update_data (struct kvm *kvm,struct kvm_sev_cmd *argp) +{ +struct kvm_sev_info *sev = &to_kvm_svm (kvm)->sev_info; +struct kvm_sev_receive_update_data params; +struct sev_data_receive_update_data *data; +void *hdr =NULL , *trans =NULL ; +struct page **guest_page; +unsigned long n; +int ret, offset; + +if (!sev_guest (kvm)) +return -EINVAL ; + +if (copy_from_user (¶ms, (void __user *)(uintptr_t )argp->data, +sizeof (struct kvm_sev_receive_update_data ))) +return -EFAULT ; + + if (!params.hdr_uaddr || !params.hdr_len ||
Quoted 8 lines
arch/x86/kvm/svm.c static int sev_receive_start(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 7525
Quoted 8 lines
arch/x86/kvm/svm.c +8
-0+ !params.guest_uaddr || !params.guest_len || + !params.trans_uaddr || !params.trans_len) + 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 ;
Check for overflow.
Quoted 72 lines
Quoted 72 lines
arch/x86/kvm/svm.c +53
-0+ + hdr =psp_copy_user_blob (params.hdr_uaddr, params.hdr_len); +if (IS_ERR (hdr)) +return PTR_ERR (hdr); + + trans =psp_copy_user_blob (params.trans_uaddr, params.trans_len); +if (IS_ERR (trans)) { + ret =PTR_ERR (trans); + goto e_free_hdr; + } + + ret = -ENOMEM ; + data =kzalloc (sizeof (*data),GFP_KERNEL ); +if (!data) + goto e_free_trans; + + data->hdr_address =__psp_pa (hdr); + data->hdr_len = params.hdr_len; + data->trans_address =__psp_pa (trans); + data->trans_len = params.trans_len; + +/* Pin guest memory */ + ret = -EFAULT ; + guest_page =sev_pin_memory (kvm, params.guest_uaddr &PAGE_MASK , +PAGE_SIZE , &n,0 ); +if (!guest_page) + goto e_free; + +/* The RECEIVE_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_RECEIVE_UPDATE_DATA , data, + &argp->error); + +sev_unpin_memory (kvm, guest_page, n); + +e_free: +kfree (data); +e_free_trans: +kfree (trans); +e_free_hdr: +kfree (hdr); + +return ret; +} +static int svm_mem_enc_op (struct kvm *kvm,void __user *argp) {struct kvm_sev_cmd sev_cmd;@@ -7553,6 +7629,9 @@ static int svm_mem_enc_op(struct kvm *kvm, void __user *argp)case KVM_SEV_RECEIVE_START : r =sev_receive_start (kvm, &sev_cmd);break ; +case KVM_SEV_RECEIVE_UPDATE_DATA : + r =sev_receive_update_data (kvm, &sev_cmd); +break ;default : r = -EINVAL ; goto out;include/uapi/linux/kvm.h +0
-0@@ -1588,6 +1588,15 @@ struct kvm_sev_receive_start {__u32 session_len; };
Quoted 15 lines
Quoted 13 lines
include/uapi/linux/kvm.h +9
-1+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) --Quoted 2 lines
2.17.1
Otherwise looks fine to my eye.
PATCH v6 6/14 KVM: SVM: Add KVM_SEV_RECEIVE_FINISH command
In reply to Ashish KalraFrom: Brijesh Singh <[email protected]>
The command finalize the guest receiving process and make the SEV guest ready for the execution.
--- .../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
@@ -375,6 +375,14 @@ Returns: 0 on success, -negative on error __u32 trans_len; }; +15. KVM_SEV_RECEIVE_FINISH +------------------------ + +After completion of the migration flow, the KVM_SEV_RECEIVE_FINISH command can be +issued by the hypervisor to make the guest ready for execution. + +Returns: 0 on success, -negative on error + References ==========
arch/x86/kvm/svm.c +23 -0
@@ -7573,6 +7573,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;
@@ -7632,6 +7652,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
Re: [PATCH v6 06/14] KVM: SVM: Add KVM_SEV_RECEIVE_FINISH command
Quoted 48 lines · 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]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[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@@ -375,6 +375,14 @@ Returns: 0 on success, -negative on error __u32 trans_len; }; +15. KVM_SEV_RECEIVE_FINISH +------------------------ + +After completion of the migration flow, the KVM_SEV_RECEIVE_FINISH command can be +issued by the hypervisor to make the guest ready for execution. + +Returns: 0 on success, -negative on error + References ==========arch/x86/kvm/svm.c +0
-0@@ -7573,6 +7573,26 @@ static int sev_receive_update_data(struct kvm *kvm, struct kvm_sev_cmd *argp)return ret;
arch/x86/kvm/svm.c static int sev_receive_update_data(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 7583
Quoted 10 lines
arch/x86/kvm/svm.c +8
-0} +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 ;
Noticed this in earlier patches too. Is -ENOTTY the best return value? Aren't one of -ENXIO, or -ENODEV, or -EINVAL a better choice? What is the rationale for using -ENOTTY?
Quoted 28 lines
arch/x86/kvm/svm.c +15
-0+ + 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;@@ -7632,6 +7652,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
Re: [PATCH v6 06/14] KVM: SVM: Add KVM_SEV_RECEIVE_FINISH command
In reply to Ashish KalraQuoted 74 lines · 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]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[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@@ -375,6 +375,14 @@ Returns: 0 on success, -negative on error __u32 trans_len; }; +15. KVM_SEV_RECEIVE_FINISH +------------------------ + +After completion of the migration flow, the KVM_SEV_RECEIVE_FINISH command can be +issued by the hypervisor to make the guest ready for execution. + +Returns: 0 on success, -negative on error + References ==========arch/x86/kvm/svm.c +20
-0@@ -7573,6 +7573,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;
arch/x86/kvm/svm.c static int svm_mem_enc_op(struct kvm *kvm, void __user *argp) · line 7660
Quoted 9 lines
arch/x86/kvm/svm.c +3
-0@@ -7632,6 +7652,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;
Steve Rutherford
Reviewed-byRe: [PATCH v6 06/14] KVM: SVM: Add KVM_SEV_RECEIVE_FINISH command
Quoted 79 lines · Krish Sadhukhan
Quoted 76 lines · 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]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[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@@ -375,6 +375,14 @@ Returns: 0 on success, -negative on error __u32 trans_len; }; +15. KVM_SEV_RECEIVE_FINISH +------------------------ + +After completion of the migration flow, the KVM_SEV_RECEIVE_FINISH command can be +issued by the hypervisor to make the guest ready for execution. + +Returns: 0 on success, -negative on error + References ==========arch/x86/kvm/svm.c +20
-0@@ -7573,6 +7573,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;@@ -7632,6 +7652,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);
Quoted 8 lines
arch/x86/kvm/svm.c static int svm_mem_enc_op(struct kvm *kvm, void __user *argp) · line 7660
Quoted 7 lines
arch/x86/kvm/svm.c +3
-0break ; +case KVM_SEV_RECEIVE_FINISH : + r =sev_receive_finish (kvm, &sev_cmd); +break ;default : r = -EINVAL ; goto out;Reviewed-by Krish Sadhukhan <[email protected]>
As to ENOTTY, man page for ioctl translates it as "The specified request does not apply to the kind of object that the file descriptor fd references", which seems appropriate here.
PATCH v6 7/14 KVM: x86: Add AMD SEV specific Hypercall3
In reply to Ashish KalraFrom: Brijesh Singh <[email protected]>
KVM hypercall framework relies on alternative framework to patch the VMCALL -> VMMCALL on AMD platform. If a hypercall is made before apply_alternative() is called then it defaults to VMCALL. The approach works fine on non SEV guest. A VMCALL would causes #UD, and hypervisor will be able to decode the instruction and do the right things. But when SEV is active, guest memory is encrypted with guest key and hypervisor will not be able to decode the instruction bytes.
Add SEV specific hypercall3, it unconditionally uses VMMCALL. The hypercall will be used by the SEV guest to notify encrypted pages to the hypervisor.
--- arch/x86/include/asm/kvm_para.h | 12 ++++++++++++ 1 file changed, 12 insertions(+)
arch/x86/include/asm/kvm_para.h +12 -0
@@ -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; + + asmvolatile ("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
Re: [PATCH v6 07/14] KVM: x86: Add AMD SEV specific Hypercall3
Quoted 6 lines · 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
^^^^^^ cause
Quoted 5 lines
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
^^ which
Quoted 15 lines
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]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[email protected]>
Quoted 30 lines
--- arch/x86/include/asm/kvm_para.h | 12 ++++++++++++ 1 file changed, 12 insertions(+)arch/x86/include/asm/kvm_para.h +12
-0@@ -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; + + asmvolatile ("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
Re: [PATCH v6 07/14] KVM: x86: Add AMD SEV specific Hypercall3
In reply to Ashish KalraQuoted 5 lines · 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()
s/apply_alternative/apply_alternatives/
Quoted 39 lines
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]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[email protected]>--- arch/x86/include/asm/kvm_para.h | 12 ++++++++++++ 1 file changed, 12 insertions(+)arch/x86/include/asm/kvm_para.h +5
-0@@ -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; +
arch/x86/include/asm/kvm_para.h static inline long kvm_hypercall4(unsigned int nr, unsigned long p1, · line 101
Quoted 10 lines
arch/x86/include/asm/kvm_para.h +7
-0+ asmvolatile ("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 );
Steve Rutherford
Reviewed-byRe: [PATCH v6 07/14] KVM: x86: Add AMD SEV specific Hypercall3
Quoted 44 lines · Krish Sadhukhan
Quoted 5 lines · 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()s/apply_alternative/apply_alternatives/
Quoted 34 lines
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]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[email protected]>--- arch/x86/include/asm/kvm_para.h | 12 ++++++++++++ 1 file changed, 12 insertions(+)arch/x86/include/asm/kvm_para.h +0
-0@@ -84,6 +84,18 @@ static inline long kvm_hypercall4(unsigned int nr, unsigned long p1,return ret; }
Quoted 16 lines
Quoted 5 lines
arch/x86/include/asm/kvm_para.h +5
-0+static inline long kvm_sev_hypercall3 (unsigned int nr,unsigned long p1, +unsigned long p2,unsigned long p3) +{ +long ret; +arch/x86/include/asm/kvm_para.h static inline long kvm_hypercall4(unsigned int nr, unsigned long p1, · line 101
Quoted 10 lines
arch/x86/include/asm/kvm_para.h +7
-0+ asmvolatile ("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 );Reviewed-by Krish Sadhukhan <[email protected]>
Nit: I'd personally have named this kvm_vmmcall3, since it's about invoking a particular instruction. The usage happens to be for SEV.
PATCH v6 8/14 KVM: X86: Introduce KVM_HC_PAGE_ENC_STATUS hypercall
In reply to Ashish KalraFrom: 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.
--- Documentation/virt/kvm/hypercalls.rst | 15 +++++ arch/x86/include/asm/kvm_host.h | 2 + arch/x86/kvm/svm.c | 95 +++++++++++++++++++++++++++ arch/x86/kvm/vmx/vmx.c | 1 + arch/x86/kvm/x86.c | 6 ++ include/uapi/linux/kvm_para.h | 1 + 6 files changed, 120 insertions(+)
Documentation/virt/kvm/hypercalls.rst +15 -0
@@ -169,3 +169,18 @@ a0: destination APIC ID :Usage example: When sending a call-function IPI-many to vCPUs, yield if any of the IPI target vCPUs was preempted. + + +8. KVM_HC_PAGE_ENC_STATUS +------------------------- +:Architecture: x86 +:Status: active +:Purpose: Notify the encryption status changes in guest page table (SEV guest) + +a0: the guest physical address of the start page +a1: the number of pages +a2: encryption attribute + + Where: + * 1: Encryption attribute is set + * 0: Encryption attribute is cleared
arch/x86/include/asm/kvm_host.h +2 -0
@@ -1267,6 +1267,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 +95 -0
119 lines not on this page · raw message
arch/x86/kvm/vmx/vmx.c +1 -0
@@ -8001,6 +8001,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
@@ -7564,6 +7564,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
@@ -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
Re: [PATCH v6 08/14] KVM: X86: Introduce KVM_HC_PAGE_ENC_STATUS hypercall
Quoted 7 lines · Ashish Kalra
From: Brijesh Singh <[email protected]>
This hypercall is used by the SEV guest to notify a change in the page encryption status to the hypervisor. The hypercall should be invoked only when the encryption attribute is changed from encrypted -> decrypted and vice versa. By default all guest pages are considered encrypted.
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]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[email protected]>
Quoted 216 lines
--- Documentation/virt/kvm/hypercalls.rst | 15 +++++ arch/x86/include/asm/kvm_host.h | 2 + arch/x86/kvm/svm.c | 95 +++++++++++++++++++++++++++ arch/x86/kvm/vmx/vmx.c | 1 + arch/x86/kvm/x86.c | 6 ++ include/uapi/linux/kvm_para.h | 1 + 6 files changed, 120 insertions(+)Documentation/virt/kvm/hypercalls.rst +15
-0@@ -169,3 +169,18 @@ a0: destination APIC ID :Usage example: When sending a call-function IPI-many to vCPUs, yield if any of the IPI target vCPUs was preempted. + + +8. KVM_HC_PAGE_ENC_STATUS +------------------------- +:Architecture: x86 +:Status: active +:Purpose: Notify the encryption status changes in guest page table (SEV guest) + +a0: the guest physical address of the start page +a1: the number of pages +a2: encryption attribute + + Where: + * 1: Encryption attribute is set + * 0: Encryption attribute is clearedarch/x86/include/asm/kvm_host.h +2
-0@@ -1267,6 +1267,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 +95
-0@@ -136,6 +136,8 @@ struct kvm_sev_info {int fd;/* SEV device fd */ unsigned long pages_locked;/* Number of pages locked */ struct list_head regions_list;/* List of registered regions */ +unsigned long *page_enc_bmap; +unsigned long page_enc_bmap_size; };struct kvm_svm {@@ -1991,6 +1993,9 @@ static void sev_vm_destroy(struct kvm *kvm)sev_unbind_asid (kvm, sev->handle);sev_asid_free (sev->asid); + +kvfree (sev->page_enc_bmap); + sev->page_enc_bmap =NULL ; }static void avic_vm_destroy (struct kvm *kvm)@@ -7593,6 +7598,94 @@ static int sev_receive_finish(struct kvm *kvm, struct kvm_sev_cmd *argp)return ret; } +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); +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); +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; +kvm_pfn_t pfn_start, pfn_end; +gfn_t gfn_start, gfn_end; +int ret; + +if (!sev_guest (kvm)) +return -EINVAL ; + +if (!npages) +return 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); + +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;@@ -7995,6 +8088,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@@ -8001,6 +8001,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@@ -7564,6 +7564,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@@ -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
Re: [PATCH v6 08/14] KVM: X86: Introduce KVM_HC_PAGE_ENC_STATUS hypercall
In reply to Ashish KalraQuoted 94 lines · Ashish Kalra
From: Brijesh Singh <[email protected]>
This hypercall is used by the SEV guest to notify a change in the page encryption status to the hypervisor. The hypercall should be invoked only when the encryption attribute is changed from encrypted -> decrypted and vice versa. By default all guest pages are considered encrypted.
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]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[email protected]>--- Documentation/virt/kvm/hypercalls.rst | 15 +++++ arch/x86/include/asm/kvm_host.h | 2 + arch/x86/kvm/svm.c | 95 +++++++++++++++++++++++++++ arch/x86/kvm/vmx/vmx.c | 1 + arch/x86/kvm/x86.c | 6 ++ include/uapi/linux/kvm_para.h | 1 + 6 files changed, 120 insertions(+)Documentation/virt/kvm/hypercalls.rst +15
-0@@ -169,3 +169,18 @@ a0: destination APIC ID :Usage example: When sending a call-function IPI-many to vCPUs, yield if any of the IPI target vCPUs was preempted. + + +8. KVM_HC_PAGE_ENC_STATUS +------------------------- +:Architecture: x86 +:Status: active +:Purpose: Notify the encryption status changes in guest page table (SEV guest) + +a0: the guest physical address of the start page +a1: the number of pages +a2: encryption attribute + + Where: + * 1: Encryption attribute is set + * 0: Encryption attribute is clearedarch/x86/include/asm/kvm_host.h +2
-0@@ -1267,6 +1267,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 +7
-0@@ -136,6 +136,8 @@ struct kvm_sev_info {int fd;/* SEV device fd */ unsigned long pages_locked;/* Number of pages locked */ struct list_head regions_list;/* List of registered regions */ +unsigned long *page_enc_bmap; +unsigned long page_enc_bmap_size; };struct kvm_svm {@@ -1991,6 +1993,9 @@ static void sev_vm_destroy(struct kvm *kvm)sev_unbind_asid (kvm, sev->handle);sev_asid_free (sev->asid); + +kvfree (sev->page_enc_bmap); + sev->page_enc_bmap =NULL ; }static void avic_vm_destroy (struct kvm *kvm)@@ -7593,6 +7598,94 @@ static int sev_receive_finish(struct kvm *kvm, struct kvm_sev_cmd *argp)return ret; } +static int sev_resize_page_enc_bitmap (struct kvm *kvm,unsigned long new_size) +{
arch/x86/kvm/svm.c static int sev_receive_finish(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 7612
Quoted 10 lines
arch/x86/kvm/svm.c +10
-0+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);
Just wondering why we can't directly modify sev->page_enc_bmap.
Quoted 48 lines
arch/x86/kvm/svm.c +48
-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); +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; +kvm_pfn_t pfn_start, pfn_end; +gfn_t gfn_start, gfn_end; +int ret; + +if (!sev_guest (kvm)) +return -EINVAL ; + +if (!npages) +return 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); + +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. +*/
arch/x86/kvm/svm.c static int sev_receive_finish(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 7670
Quoted 10 lines
arch/x86/kvm/svm.c +10
-0+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 ; + }
It seems is_error_noslot_pfn() covers both cases - i) gfn slot is absent, ii) failure to translate to pfn. So do we still need is_noslot_pfn() ?
Quoted 22 lines
arch/x86/kvm/svm.c +18
-0+ +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;
arch/x86/kvm/svm.c static struct kvm_x86_ops svm_x86_ops __ro_after_init = { · line 8092
Quoted 5 lines
arch/x86/kvm/svm.c +2
-0@@ -7995,6 +8088,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,
Why not place it where other encryption ops are located ?
...
.mem_enc_unreg_region
+ .page_enc_status_hc = svm_page_enc_status_hc
Quoted 44 lines
arch/x86/kvm/svm.c +0
-0};static int __init svm_init (void )arch/x86/kvm/vmx/vmx.c +1
-0@@ -8001,6 +8001,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@@ -7564,6 +7564,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@@ -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
Re: [PATCH v6 08/14] KVM: X86: Introduce KVM_HC_PAGE_ENC_STATUS hypercall
Quoted 94 lines · Krish Sadhukhan
Quoted 92 lines · Ashish Kalra
From: Brijesh Singh <[email protected]>
This hypercall is used by the SEV guest to notify a change in the page encryption status to the hypervisor. The hypercall should be invoked only when the encryption attribute is changed from encrypted -> decrypted and vice versa. By default all guest pages are considered encrypted.
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]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[email protected]>--- Documentation/virt/kvm/hypercalls.rst | 15 +++++ arch/x86/include/asm/kvm_host.h | 2 + arch/x86/kvm/svm.c | 95 +++++++++++++++++++++++++++ arch/x86/kvm/vmx/vmx.c | 1 + arch/x86/kvm/x86.c | 6 ++ include/uapi/linux/kvm_para.h | 1 + 6 files changed, 120 insertions(+)Documentation/virt/kvm/hypercalls.rst +15
-0@@ -169,3 +169,18 @@ a0: destination APIC ID :Usage example: When sending a call-function IPI-many to vCPUs, yield if any of the IPI target vCPUs was preempted. + + +8. KVM_HC_PAGE_ENC_STATUS +------------------------- +:Architecture: x86 +:Status: active +:Purpose: Notify the encryption status changes in guest page table (SEV guest) + +a0: the guest physical address of the start page +a1: the number of pages +a2: encryption attribute + + Where: + * 1: Encryption attribute is set + * 0: Encryption attribute is clearedarch/x86/include/asm/kvm_host.h +2
-0@@ -1267,6 +1267,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 +12
-0@@ -136,6 +136,8 @@ struct kvm_sev_info {int fd;/* SEV device fd */ unsigned long pages_locked;/* Number of pages locked */ struct list_head regions_list;/* List of registered regions */ +unsigned long *page_enc_bmap; +unsigned long page_enc_bmap_size; };struct kvm_svm {@@ -1991,6 +1993,9 @@ static void sev_vm_destroy(struct kvm *kvm)sev_unbind_asid (kvm, sev->handle);sev_asid_free (sev->asid); + +kvfree (sev->page_enc_bmap); + sev->page_enc_bmap =NULL ; }static void avic_vm_destroy (struct kvm *kvm)@@ -7593,6 +7598,94 @@ static int sev_receive_finish(struct kvm *kvm, struct kvm_sev_cmd *argp)return ret; } +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)
Quoted 9 lines
arch/x86/kvm/svm.c static int sev_receive_finish(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 7612
Quoted 5 lines
arch/x86/kvm/svm.c +5
-0+return 0 ; + + sz =ALIGN (new_size,BITS_PER_LONG ) /8 ; + + map =vmalloc (sz);Just wondering why we can't directly modify
sev->page_enc_bmap.
Because the page_enc_bitmap needs to be re-sized here, it needs to be expanded here.
Quoted 54 lines
Quoted 54 lines
arch/x86/kvm/svm.c +54
-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); +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; +kvm_pfn_t pfn_start, pfn_end; +gfn_t gfn_start, gfn_end; +int ret; + +if (!sev_guest (kvm)) +return -EINVAL ; + +if (!npages) +return 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); + +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
Quoted 9 lines
arch/x86/kvm/svm.c static int sev_receive_finish(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 7662
Quoted 4 lines
arch/x86/kvm/svm.c +4
-0+* to the page encryption bitmap. +*/ +return -EINVAL ; + }It seems
is_error_noslot_pfn()covers both cases - i) gfn slot is absent, ii) failure to translate to pfn. So do we still needis_noslot_pfn()?
We do need to check for !is_noslot_pfn(..) additionally as the MMIO ranges will not be having a slot allocated.
Thanks,
Ashish
Quoted 77 lines
Quoted 22 lines
arch/x86/kvm/svm.c +18
-0+ +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;arch/x86/kvm/svm.c static struct kvm_x86_ops svm_x86_ops __ro_after_init = { · line 8091
Quoted 4 lines
arch/x86/kvm/svm.c +2
-0@@ -7995,6 +8088,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,Why not place it where other encryption ops are located ?
...
.mem_enc_unreg_region
+ .page_enc_status_hc =
svm_page_enc_status_hcQuoted 41 lines
arch/x86/kvm/svm.c +0
-0};static int __init svm_init (void )arch/x86/kvm/vmx/vmx.c +1
-0@@ -8001,6 +8001,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@@ -7564,6 +7564,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@@ -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
Re: [PATCH v6 08/14] KVM: X86: Introduce KVM_HC_PAGE_ENC_STATUS hypercall
Quoted 105 lines · Ashish Kalra
Quoted 94 lines · Krish Sadhukhan
Quoted 92 lines · Ashish Kalra
From: Brijesh Singh <[email protected]>
This hypercall is used by the SEV guest to notify a change in the page encryption status to the hypervisor. The hypercall should be invoked only when the encryption attribute is changed from encrypted -> decrypted and vice versa. By default all guest pages are considered encrypted.
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]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[email protected]>--- Documentation/virt/kvm/hypercalls.rst | 15 +++++ arch/x86/include/asm/kvm_host.h | 2 + arch/x86/kvm/svm.c | 95 +++++++++++++++++++++++++++ arch/x86/kvm/vmx/vmx.c | 1 + arch/x86/kvm/x86.c | 6 ++ include/uapi/linux/kvm_para.h | 1 + 6 files changed, 120 insertions(+)Documentation/virt/kvm/hypercalls.rst +15
-0@@ -169,3 +169,18 @@ a0: destination APIC ID :Usage example: When sending a call-function IPI-many to vCPUs, yield if any of the IPI target vCPUs was preempted. + + +8. KVM_HC_PAGE_ENC_STATUS +------------------------- +:Architecture: x86 +:Status: active +:Purpose: Notify the encryption status changes in guest page table (SEV guest) + +a0: the guest physical address of the start page +a1: the number of pages +a2: encryption attribute + + Where: + * 1: Encryption attribute is set + * 0: Encryption attribute is clearedarch/x86/include/asm/kvm_host.h +2
-0@@ -1267,6 +1267,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 +12
-0@@ -136,6 +136,8 @@ struct kvm_sev_info {int fd;/* SEV device fd */ unsigned long pages_locked;/* Number of pages locked */ struct list_head regions_list;/* List of registered regions */ +unsigned long *page_enc_bmap; +unsigned long page_enc_bmap_size; };struct kvm_svm {@@ -1991,6 +1993,9 @@ static void sev_vm_destroy(struct kvm *kvm)sev_unbind_asid (kvm, sev->handle);sev_asid_free (sev->asid); + +kvfree (sev->page_enc_bmap); + sev->page_enc_bmap =NULL ; }static void avic_vm_destroy (struct kvm *kvm)@@ -7593,6 +7598,94 @@ static int sev_receive_finish(struct kvm *kvm, struct kvm_sev_cmd *argp)return ret; } +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)Quoted 9 lines
arch/x86/kvm/svm.c static int sev_receive_finish(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 7612
Quoted 5 lines
arch/x86/kvm/svm.c +5
-0+return 0 ; + + sz =ALIGN (new_size,BITS_PER_LONG ) /8 ; + + map =vmalloc (sz);Just wondering why we can't directly modify
sev->page_enc_bmap.
Quoted 3 lines
Because the
page_enc_bitmapneeds to be re-sized here, it needs to be expanded here.
I don't believe there is anything is like a realloc() kind of equivalent for the kmalloc() interfaces.
Thanks,
Ashish
Quoted 147 lines
Quoted 54 lines
Quoted 54 lines
arch/x86/kvm/svm.c +54
-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); +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; +kvm_pfn_t pfn_start, pfn_end; +gfn_t gfn_start, gfn_end; +int ret; + +if (!sev_guest (kvm)) +return -EINVAL ; + +if (!npages) +return 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); + +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 Quoted 9 lines
arch/x86/kvm/svm.c static int sev_receive_finish(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 7662
Quoted 4 lines
arch/x86/kvm/svm.c +4
-0+* to the page encryption bitmap. +*/ +return -EINVAL ; + }It seems
is_error_noslot_pfn()covers both cases - i) gfn slot is absent, ii) failure to translate to pfn. So do we still needis_noslot_pfn()?We do need to check for !is_noslot_pfn(..) additionally as the MMIO ranges will not be having a slot allocated.
Thanks,
AshishQuoted 77 lines
Quoted 22 lines
arch/x86/kvm/svm.c +18
-0+ +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;arch/x86/kvm/svm.c static struct kvm_x86_ops svm_x86_ops __ro_after_init = { · line 8091
Quoted 4 lines
arch/x86/kvm/svm.c +2
-0@@ -7995,6 +8088,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,Why not place it where other encryption ops are located ?
...
.mem_enc_unreg_region
+ .page_enc_status_hc =
svm_page_enc_status_hcQuoted 41 lines
arch/x86/kvm/svm.c +0
-0};static int __init svm_init (void )arch/x86/kvm/vmx/vmx.c +1
-0@@ -8001,6 +8001,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@@ -7564,6 +7564,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@@ -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
Krish Sadhukhan
Reviewed-byRe: [PATCH v6 08/14] KVM: X86: Introduce KVM_HC_PAGE_ENC_STATUS hypercall
Quoted 103 lines · Ashish Kalra
Quoted 102 lines · Ashish Kalra
Quoted 101 lines · Krish Sadhukhan
Quoted 87 lines · Ashish Kalra
From: Brijesh Singh <[email protected]>
This hypercall is used by the SEV guest to notify a change in the page encryption status to the hypervisor. The hypercall should be invoked only when the encryption attribute is changed from encrypted -> decrypted and vice versa. By default all guest pages are considered encrypted.
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]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[email protected]>--- Documentation/virt/kvm/hypercalls.rst | 15 +++++ arch/x86/include/asm/kvm_host.h | 2 + arch/x86/kvm/svm.c | 95 +++++++++++++++++++++++++++ arch/x86/kvm/vmx/vmx.c | 1 + arch/x86/kvm/x86.c | 6 ++ include/uapi/linux/kvm_para.h | 1 + 6 files changed, 120 insertions(+)Documentation/virt/kvm/hypercalls.rst +15
-0@@ -169,3 +169,18 @@ a0: destination APIC ID :Usage example: When sending a call-function IPI-many to vCPUs, yield if any of the IPI target vCPUs was preempted. + + +8. KVM_HC_PAGE_ENC_STATUS +------------------------- +:Architecture: x86 +:Status: active +:Purpose: Notify the encryption status changes in guest page table (SEV guest) + +a0: the guest physical address of the start page +a1: the number of pages +a2: encryption attribute + + Where: + * 1: Encryption attribute is set + * 0: Encryption attribute is clearedarch/x86/include/asm/kvm_host.h +2
-0@@ -1267,6 +1267,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 +7
-0@@ -136,6 +136,8 @@ struct kvm_sev_info {int fd;/* SEV device fd */ unsigned long pages_locked;/* Number of pages locked */ struct list_head regions_list;/* List of registered regions */ +unsigned long *page_enc_bmap; +unsigned long page_enc_bmap_size; };struct kvm_svm {@@ -1991,6 +1993,9 @@ static void sev_vm_destroy(struct kvm *kvm)sev_unbind_asid (kvm, sev->handle);sev_asid_free (sev->asid); + +kvfree (sev->page_enc_bmap); + sev->page_enc_bmap =NULL ; }static void avic_vm_destroy (struct kvm *kvm)@@ -7593,6 +7598,94 @@ static int sev_receive_finish(struct kvm *kvm, struct kvm_sev_cmd *argp)return ret; } +static int sev_resize_page_enc_bitmap (struct kvm *kvm,unsigned long new_size) +{arch/x86/kvm/svm.c static int sev_receive_finish(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 7611
Quoted 10 lines
arch/x86/kvm/svm.c +10
-0+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);Just wondering why we can't directly modify
sev->page_enc_bmap.
Quoted 2 lines
Quoted 2 lines
Because the
page_enc_bitmapneeds to be re-sized here, it needs to be expanded here.
OK.
Quoted 70 lines
I don't believe there is anything is like a
realloc()kind of equivalent for thekmalloc()interfaces.Thanks,
AshishQuoted 64 lines
Quoted 53 lines
Quoted 53 lines
arch/x86/kvm/svm.c +53
-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); +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; +kvm_pfn_t pfn_start, pfn_end; +gfn_t gfn_start, gfn_end; +int ret; + +if (!sev_guest (kvm)) +return -EINVAL ; + +if (!npages) +return 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); + +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)) { +/* Quoted 9 lines
arch/x86/kvm/svm.c static int sev_receive_finish(struct kvm *kvm, struct kvm_sev_cmd *argp) · line 7662
Quoted 5 lines
arch/x86/kvm/svm.c +5
-0+* Allow guest MMIO range(s) to be added +* to the page encryption bitmap. +*/ +return -EINVAL ; + }It seems
is_error_noslot_pfn()covers both cases - i) gfn slot is absent, ii) failure to translate to pfn. So do we still needis_noslot_pfn()?We do need to check for !is_noslot_pfn(..) additionally as the MMIO ranges will not be having a slot allocated.
The comments above is_error_noslot_pfn() seem to indicate that it covers both cases, "not in slot" and "failure to translate"...
Quoted 80 lines
Quoted 80 lines
Thanks,
AshishQuoted 76 lines
Quoted 22 lines
arch/x86/kvm/svm.c +18
-0+ +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;arch/x86/kvm/svm.c static struct kvm_x86_ops svm_x86_ops __ro_after_init = { · line 8091
Quoted 4 lines
arch/x86/kvm/svm.c +2
-0@@ -7995,6 +8088,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,Why not place it where other encryption ops are located ?
...
.mem_enc_unreg_region
+ .page_enc_status_hc =
svm_page_enc_status_hcQuoted 41 lines
arch/x86/kvm/svm.c +0
-0};static int __init svm_init (void )arch/x86/kvm/vmx/vmx.c +1
-0@@ -8001,6 +8001,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@@ -7564,6 +7564,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@@ -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
Re: [PATCH v6 08/14] KVM: X86: Introduce KVM_HC_PAGE_ENC_STATUS hypercall
In reply to Ashish KalraQuoted 58 lines · Ashish Kalra
From: Brijesh Singh <[email protected]>
This hypercall is used by the SEV guest to notify a change in the page encryption status to the hypervisor. The hypercall should be invoked only when the encryption attribute is changed from encrypted -> decrypted and vice versa. By default all guest pages are considered encrypted.
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]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[email protected]>--- Documentation/virt/kvm/hypercalls.rst | 15 +++++ arch/x86/include/asm/kvm_host.h | 2 + arch/x86/kvm/svm.c | 95 +++++++++++++++++++++++++++ arch/x86/kvm/vmx/vmx.c | 1 + arch/x86/kvm/x86.c | 6 ++ include/uapi/linux/kvm_para.h | 1 + 6 files changed, 120 insertions(+)Documentation/virt/kvm/hypercalls.rst +15
-0@@ -169,3 +169,18 @@ a0: destination APIC ID :Usage example: When sending a call-function IPI-many to vCPUs, yield if any of the IPI target vCPUs was preempted. + + +8. KVM_HC_PAGE_ENC_STATUS +------------------------- +:Architecture: x86 +:Status: active +:Purpose: Notify the encryption status changes in guest page table (SEV guest) + +a0: the guest physical address of the start page +a1: the number of pages +a2: encryption attribute + + Where: + * 1: Encryption attribute is set + * 0: Encryption attribute is clearedarch/x86/include/asm/kvm_host.h +0
-0
arch/x86/include/asm/kvm_host.h struct kvm_x86_ops { · line 1271
Quoted 5 lines
arch/x86/include/asm/kvm_host.h +2
-0@@ -1267,6 +1267,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);
Nit: spell out size instead of sz.
Quoted 172 lines
arch/x86/include/asm/kvm_host.h +0
-0};struct kvm_arch_async_pf {arch/x86/kvm/svm.c +95
-0@@ -136,6 +136,8 @@ struct kvm_sev_info {int fd;/* SEV device fd */ unsigned long pages_locked;/* Number of pages locked */ struct list_head regions_list;/* List of registered regions */ +unsigned long *page_enc_bmap; +unsigned long page_enc_bmap_size; };struct kvm_svm {@@ -1991,6 +1993,9 @@ static void sev_vm_destroy(struct kvm *kvm)sev_unbind_asid (kvm, sev->handle);sev_asid_free (sev->asid); + +kvfree (sev->page_enc_bmap); + sev->page_enc_bmap =NULL ; }static void avic_vm_destroy (struct kvm *kvm)@@ -7593,6 +7598,94 @@ static int sev_receive_finish(struct kvm *kvm, struct kvm_sev_cmd *argp)return ret; } +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); +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); +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; +kvm_pfn_t pfn_start, pfn_end; +gfn_t gfn_start, gfn_end; +int ret; + +if (!sev_guest (kvm)) +return -EINVAL ; + +if (!npages) +return 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); + +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;@@ -7995,6 +8088,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@@ -8001,6 +8001,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@@ -7564,6 +7564,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
-1@@ -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 --
Quoted 2 lines
2.17.1
I'm still not excited by the dynamic resizing. I believe the guest hypercall can be called in atomic contexts, which makes me particularly unexcited to see a potentially large vmalloc on the host followed by filling the buffer. Particularly when the buffer might be non-trivial in size (~1MB per 32GB, per some back of the envelope math).
I'd like to see an enable cap for preallocating this. Yes, the first call might not be the right value because of hotplug, but won't the VMM know when hotplug is happening? If the VMM asks for the wrong size, and does not update the size correctly before granting the VM access to more RAM, that seems like the VMM's fault.
Re: [PATCH v6 08/14] KVM: X86: Introduce KVM_HC_PAGE_ENC_STATUS hypercall
Hello Steve,
Quoted 234 lines · Steve Rutherford
Quoted 58 lines · Ashish Kalra
From: Brijesh Singh <[email protected]>
This hypercall is used by the SEV guest to notify a change in the page encryption status to the hypervisor. The hypercall should be invoked only when the encryption attribute is changed from encrypted -> decrypted and vice versa. By default all guest pages are considered encrypted.
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]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[email protected]>--- Documentation/virt/kvm/hypercalls.rst | 15 +++++ arch/x86/include/asm/kvm_host.h | 2 + arch/x86/kvm/svm.c | 95 +++++++++++++++++++++++++++ arch/x86/kvm/vmx/vmx.c | 1 + arch/x86/kvm/x86.c | 6 ++ include/uapi/linux/kvm_para.h | 1 + 6 files changed, 120 insertions(+)Documentation/virt/kvm/hypercalls.rst +15
-0@@ -169,3 +169,18 @@ a0: destination APIC ID :Usage example: When sending a call-function IPI-many to vCPUs, yield if any of the IPI target vCPUs was preempted. + + +8. KVM_HC_PAGE_ENC_STATUS +------------------------- +:Architecture: x86 +:Status: active +:Purpose: Notify the encryption status changes in guest page table (SEV guest) + +a0: the guest physical address of the start page +a1: the number of pages +a2: encryption attribute + + Where: + * 1: Encryption attribute is set + * 0: Encryption attribute is clearedarch/x86/include/asm/kvm_host.h +0
-0arch/x86/include/asm/kvm_host.h struct kvm_x86_ops { · line 1271
Quoted 5 lines
arch/x86/include/asm/kvm_host.h +2
-0@@ -1267,6 +1267,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);Nit: spell out size instead of sz.
Quoted 169 lines
arch/x86/include/asm/kvm_host.h +0
-0};struct kvm_arch_async_pf {arch/x86/kvm/svm.c +95
-0@@ -136,6 +136,8 @@ struct kvm_sev_info {int fd;/* SEV device fd */ unsigned long pages_locked;/* Number of pages locked */ struct list_head regions_list;/* List of registered regions */ +unsigned long *page_enc_bmap; +unsigned long page_enc_bmap_size; };struct kvm_svm {@@ -1991,6 +1993,9 @@ static void sev_vm_destroy(struct kvm *kvm)sev_unbind_asid (kvm, sev->handle);sev_asid_free (sev->asid); + +kvfree (sev->page_enc_bmap); + sev->page_enc_bmap =NULL ; }static void avic_vm_destroy (struct kvm *kvm)@@ -7593,6 +7598,94 @@ static int sev_receive_finish(struct kvm *kvm, struct kvm_sev_cmd *argp)return ret; } +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); +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); +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; +kvm_pfn_t pfn_start, pfn_end; +gfn_t gfn_start, gfn_end; +int ret; + +if (!sev_guest (kvm)) +return -EINVAL ; + +if (!npages) +return 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); + +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;@@ -7995,6 +8088,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@@ -8001,6 +8001,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@@ -7564,6 +7564,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@@ -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
Quoted 13 lines
Quoted 5 lines
include/uapi/linux/kvm_para.h +0
-1/* * hypercalls use architecture specific -- 2.17.1
I'm still not excited by the dynamic resizing. I believe the guest hypercall can be called in atomic contexts, which makes me particularly unexcited to see a potentially large vmalloc on the host followed by filling the buffer. Particularly when the buffer might be non-trivial in size (~1MB per 32GB, per some back of the envelope math).
I think looking at more practical situations, most hypercalls will happen during the boot stage, when device specific initializations are happening, so typically the maximum page encryption bitmap size would be allocated early enough.
In fact, initial hypercalls made by OVMF will probably allocate the maximum page bitmap size even before the kernel comes up, especially as they will be setting up page enc/dec status for MMIO, ROM, ACPI regions, PCI device memory, etc., and most importantly for "non-existent" high memory range (which will probably be the maximum size page encryption bitmap allocated/resized).
Let me know if you have different thoughts on this ?
Quoted 5 lines
I'd like to see an enable cap for preallocating this. Yes, the first call might not be the right value because of hotplug, but won't the VMM know when hotplug is happening? If the VMM asks for the wrong size, and does not update the size correctly before granting the VM access to more RAM, that seems like the VMM's fault.o
Thanks,
Ashish
Re: [PATCH v6 08/14] KVM: X86: Introduce KVM_HC_PAGE_ENC_STATUS hypercall
Quoted 257 lines · Ashish Kalra
Hello Steve,
Quoted 234 lines · Steve Rutherford
Quoted 58 lines · Ashish Kalra
From: Brijesh Singh <[email protected]>
This hypercall is used by the SEV guest to notify a change in the page encryption status to the hypervisor. The hypercall should be invoked only when the encryption attribute is changed from encrypted -> decrypted and vice versa. By default all guest pages are considered encrypted.
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]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[email protected]>--- Documentation/virt/kvm/hypercalls.rst | 15 +++++ arch/x86/include/asm/kvm_host.h | 2 + arch/x86/kvm/svm.c | 95 +++++++++++++++++++++++++++ arch/x86/kvm/vmx/vmx.c | 1 + arch/x86/kvm/x86.c | 6 ++ include/uapi/linux/kvm_para.h | 1 + 6 files changed, 120 insertions(+)Documentation/virt/kvm/hypercalls.rst +15
-0@@ -169,3 +169,18 @@ a0: destination APIC ID :Usage example: When sending a call-function IPI-many to vCPUs, yield if any of the IPI target vCPUs was preempted. + + +8. KVM_HC_PAGE_ENC_STATUS +------------------------- +:Architecture: x86 +:Status: active +:Purpose: Notify the encryption status changes in guest page table (SEV guest) + +a0: the guest physical address of the start page +a1: the number of pages +a2: encryption attribute + + Where: + * 1: Encryption attribute is set + * 0: Encryption attribute is clearedarch/x86/include/asm/kvm_host.h +0
-0arch/x86/include/asm/kvm_host.h struct kvm_x86_ops { · line 1271
Quoted 5 lines
arch/x86/include/asm/kvm_host.h +2
-0@@ -1267,6 +1267,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);Nit: spell out size instead of sz.
Quoted 169 lines
arch/x86/include/asm/kvm_host.h +0
-0};struct kvm_arch_async_pf {arch/x86/kvm/svm.c +95
-0@@ -136,6 +136,8 @@ struct kvm_sev_info {int fd;/* SEV device fd */ unsigned long pages_locked;/* Number of pages locked */ struct list_head regions_list;/* List of registered regions */ +unsigned long *page_enc_bmap; +unsigned long page_enc_bmap_size; };struct kvm_svm {@@ -1991,6 +1993,9 @@ static void sev_vm_destroy(struct kvm *kvm)sev_unbind_asid (kvm, sev->handle);sev_asid_free (sev->asid); + +kvfree (sev->page_enc_bmap); + sev->page_enc_bmap =NULL ; }static void avic_vm_destroy (struct kvm *kvm)@@ -7593,6 +7598,94 @@ static int sev_receive_finish(struct kvm *kvm, struct kvm_sev_cmd *argp)return ret; } +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); +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); +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; +kvm_pfn_t pfn_start, pfn_end; +gfn_t gfn_start, gfn_end; +int ret; + +if (!sev_guest (kvm)) +return -EINVAL ; + +if (!npages) +return 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); + +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;@@ -7995,6 +8088,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@@ -8001,6 +8001,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@@ -7564,6 +7564,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@@ -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 12Quoted 13 lines
Quoted 5 lines
include/uapi/linux/kvm_para.h +0
-1/* * hypercalls use architecture specific -- 2.17.1
I'm still not excited by the dynamic resizing. I believe the guest hypercall can be called in atomic contexts, which makes me particularly unexcited to see a potentially large vmalloc on the host followed by filling the buffer. Particularly when the buffer might be non-trivial in size (~1MB per 32GB, per some back of the envelope math).
I think looking at more practical situations, most hypercalls will happen during the boot stage, when device specific initializations are happening, so typically the maximum page encryption bitmap size would be allocated early enough.
Quoted 8 lines
In fact, initial hypercalls made by OVMF will probably allocate the maximum page bitmap size even before the kernel comes up, especially as they will be setting up page enc/dec status for MMIO, ROM, ACPI regions, PCI device memory, etc., and most importantly for "non-existent" high memory range (which will probably be the maximum size page encryption bitmap allocated/resized).
Let me know if you have different thoughts on this ?
Hi Ashish,
If this is not an issue in practice, we can just move past this. If we are basically guaranteed that OVMF will trigger hypercalls that expand the bitmap beyond the top of memory, then, yes, that should work. That leaves me slightly nervous that OVMF might regress since it's not obvious that calling a hypercall beyond the top of memory would be "required" for avoiding a somewhat indirectly related issue in guest kernels.
Adding a kvm_enable_cap doesn't seem particularly complicated and side steps all of these concerns, so I still prefer it. Caveat, haven't reviewed the patches about the feature bits yet: the enable cap would also make it possible for kernels that support live migration to avoid advertising live migration if host usermode does not want it to be advertised. This seems pretty important, since hosts that don't plan to live migrate should have the ability to tell the guest to stop calling.
Thanks,
Steve
Re: [PATCH v6 08/14] KVM: X86: Introduce KVM_HC_PAGE_ENC_STATUS hypercall
Quoted 264 lines · Steve Rutherford
Quoted 253 lines · Ashish Kalra
Hello Steve,
Quoted 233 lines · Steve Rutherford
Quoted 57 lines · Ashish Kalra
From: Brijesh Singh <[email protected]>
This hypercall is used by the SEV guest to notify a change in the page encryption status to the hypervisor. The hypercall should be invoked only when the encryption attribute is changed from encrypted -> decrypted and vice versa. By default all guest pages are considered encrypted.
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]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[email protected]>--- Documentation/virt/kvm/hypercalls.rst | 15 +++++ arch/x86/include/asm/kvm_host.h | 2 + arch/x86/kvm/svm.c | 95 +++++++++++++++++++++++++++ arch/x86/kvm/vmx/vmx.c | 1 + arch/x86/kvm/x86.c | 6 ++ include/uapi/linux/kvm_para.h | 1 + 6 files changed, 120 insertions(+)Documentation/virt/kvm/hypercalls.rst +15
-0@@ -169,3 +169,18 @@ a0: destination APIC ID :Usage example: When sending a call-function IPI-many to vCPUs, yield if any of the IPI target vCPUs was preempted. + + +8. KVM_HC_PAGE_ENC_STATUS +------------------------- +:Architecture: x86 +:Status: active +:Purpose: Notify the encryption status changes in guest page table (SEV guest) + +a0: the guest physical address of the start page +a1: the number of pages +a2: encryption attribute + + Where: + * 1: Encryption attribute is set + * 0: Encryption attribute is clearedarch/x86/include/asm/kvm_host.h +0
-0arch/x86/include/asm/kvm_host.h struct kvm_x86_ops { · line 1271
Quoted 5 lines
arch/x86/include/asm/kvm_host.h +2
-0@@ -1267,6 +1267,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);Nit: spell out size instead of sz.
Quoted 169 lines
arch/x86/include/asm/kvm_host.h +0
-0};struct kvm_arch_async_pf {arch/x86/kvm/svm.c +95
-0@@ -136,6 +136,8 @@ struct kvm_sev_info {int fd;/* SEV device fd */ unsigned long pages_locked;/* Number of pages locked */ struct list_head regions_list;/* List of registered regions */ +unsigned long *page_enc_bmap; +unsigned long page_enc_bmap_size; };struct kvm_svm {@@ -1991,6 +1993,9 @@ static void sev_vm_destroy(struct kvm *kvm)sev_unbind_asid (kvm, sev->handle);sev_asid_free (sev->asid); + +kvfree (sev->page_enc_bmap); + sev->page_enc_bmap =NULL ; }static void avic_vm_destroy (struct kvm *kvm)@@ -7593,6 +7598,94 @@ static int sev_receive_finish(struct kvm *kvm, struct kvm_sev_cmd *argp)return ret; } +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); +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); +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; +kvm_pfn_t pfn_start, pfn_end; +gfn_t gfn_start, gfn_end; +int ret; + +if (!sev_guest (kvm)) +return -EINVAL ; + +if (!npages) +return 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); + +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;@@ -7995,6 +8088,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@@ -8001,6 +8001,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@@ -7564,6 +7564,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@@ -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 12Quoted 12 lines
Quoted 5 lines
include/uapi/linux/kvm_para.h +0
-1/* * hypercalls use architecture specific -- 2.17.1
I'm still not excited by the dynamic resizing. I believe the guest hypercall can be called in atomic contexts, which makes me particularly unexcited to see a potentially large vmalloc on the host followed by filling the buffer. Particularly when the buffer might be non-trivial in size (~1MB per 32GB, per some back of the envelope math).
I think looking at more practical situations, most hypercalls will happen during the boot stage, when device specific initializations are happening, so typically the maximum page encryption bitmap size would be allocated early enough.
Quoted 8 lines
In fact, initial hypercalls made by OVMF will probably allocate the maximum page bitmap size even before the kernel comes up, especially as they will be setting up page enc/dec status for MMIO, ROM, ACPI regions, PCI device memory, etc., and most importantly for "non-existent" high memory range (which will probably be the maximum size page encryption bitmap allocated/resized).
Let me know if you have different thoughts on this ?
Hi Ashish,
Quoted 7 lines
If this is not an issue in practice, we can just move past this. If we are basically guaranteed that OVMF will trigger hypercalls that expand the bitmap beyond the top of memory, then, yes, that should work. That leaves me slightly nervous that OVMF might regress since it's not obvious that calling a hypercall beyond the top of memory would be "required" for avoiding a somewhat indirectly related issue in guest kernels.
If possible then we should try to avoid growing/shrinking the bitmap . Today OVMF may not be accessing beyond memory but a malicious guest could send a hypercall down which can trigger a huge memory allocation on the host side and may eventually cause denial of service for other. I am in favor if we can find some solution to handle this case. How about Steve's suggestion about VMM making a call down to the kernel to tell how big the bitmap should be? Initially it should be equal to the guest RAM and if VMM ever did the memory expansion then it can send down another notification to increase the bitmap ?
Optionally, instead of adding a new ioctl, I was wondering if we can extend the kvm_arch_prepare_memory_region() to make svm specific x86_ops which can take read the userspace provided memory region and calculate the amount of guest RAM managed by the KVM and grow/shrink the bitmap based on that information. I have not looked deep enough to see if its doable but if it can work then we can avoid adding yet another ioctl.
Quoted 12 lines
Adding a
kvm_enable_capdoesn't seem particularly complicated and side steps all of these concerns, so I still prefer it. Caveat, haven't reviewed the patches about the feature bits yet: the enable cap would also make it possible for kernels that support live migration to avoid advertising live migration if host usermode does not want it to be advertised. This seems pretty important, since hosts that don't plan to live migrate should have the ability to tell the guest to stop calling.Thanks,
Steve
Re: [PATCH v6 08/14] KVM: X86: Introduce KVM_HC_PAGE_ENC_STATUS hypercall
Quoted 276 lines · Brijesh Singh
Quoted 264 lines · Steve Rutherford
Quoted 253 lines · Ashish Kalra
Hello Steve,
Quoted 233 lines · Steve Rutherford
Quoted 57 lines · Ashish Kalra
From: Brijesh Singh <[email protected]>
This hypercall is used by the SEV guest to notify a change in the page encryption status to the hypervisor. The hypercall should be invoked only when the encryption attribute is changed from encrypted -> decrypted and vice versa. By default all guest pages are considered encrypted.
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]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[email protected]>--- Documentation/virt/kvm/hypercalls.rst | 15 +++++ arch/x86/include/asm/kvm_host.h | 2 + arch/x86/kvm/svm.c | 95 +++++++++++++++++++++++++++ arch/x86/kvm/vmx/vmx.c | 1 + arch/x86/kvm/x86.c | 6 ++ include/uapi/linux/kvm_para.h | 1 + 6 files changed, 120 insertions(+)Documentation/virt/kvm/hypercalls.rst +15
-0@@ -169,3 +169,18 @@ a0: destination APIC ID :Usage example: When sending a call-function IPI-many to vCPUs, yield if any of the IPI target vCPUs was preempted. + + +8. KVM_HC_PAGE_ENC_STATUS +------------------------- +:Architecture: x86 +:Status: active +:Purpose: Notify the encryption status changes in guest page table (SEV guest) + +a0: the guest physical address of the start page +a1: the number of pages +a2: encryption attribute + + Where: + * 1: Encryption attribute is set + * 0: Encryption attribute is clearedarch/x86/include/asm/kvm_host.h +0
-0arch/x86/include/asm/kvm_host.h struct kvm_x86_ops { · line 1271
Quoted 5 lines
arch/x86/include/asm/kvm_host.h +2
-0@@ -1267,6 +1267,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);Nit: spell out size instead of sz.
Quoted 169 lines
arch/x86/include/asm/kvm_host.h +0
-0};struct kvm_arch_async_pf {arch/x86/kvm/svm.c +95
-0@@ -136,6 +136,8 @@ struct kvm_sev_info {int fd;/* SEV device fd */ unsigned long pages_locked;/* Number of pages locked */ struct list_head regions_list;/* List of registered regions */ +unsigned long *page_enc_bmap; +unsigned long page_enc_bmap_size; };struct kvm_svm {@@ -1991,6 +1993,9 @@ static void sev_vm_destroy(struct kvm *kvm)sev_unbind_asid (kvm, sev->handle);sev_asid_free (sev->asid); + +kvfree (sev->page_enc_bmap); + sev->page_enc_bmap =NULL ; }static void avic_vm_destroy (struct kvm *kvm)@@ -7593,6 +7598,94 @@ static int sev_receive_finish(struct kvm *kvm, struct kvm_sev_cmd *argp)return ret; } +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); +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); +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; +kvm_pfn_t pfn_start, pfn_end; +gfn_t gfn_start, gfn_end; +int ret; + +if (!sev_guest (kvm)) +return -EINVAL ; + +if (!npages) +return 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); + +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;@@ -7995,6 +8088,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@@ -8001,6 +8001,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@@ -7564,6 +7564,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@@ -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 12Quoted 12 lines
Quoted 5 lines
include/uapi/linux/kvm_para.h +0
-1/* * hypercalls use architecture specific -- 2.17.1
I'm still not excited by the dynamic resizing. I believe the guest hypercall can be called in atomic contexts, which makes me particularly unexcited to see a potentially large vmalloc on the host followed by filling the buffer. Particularly when the buffer might be non-trivial in size (~1MB per 32GB, per some back of the envelope math).
I think looking at more practical situations, most hypercalls will happen during the boot stage, when device specific initializations are happening, so typically the maximum page encryption bitmap size would be allocated early enough.
Quoted 8 lines
In fact, initial hypercalls made by OVMF will probably allocate the maximum page bitmap size even before the kernel comes up, especially as they will be setting up page enc/dec status for MMIO, ROM, ACPI regions, PCI device memory, etc., and most importantly for "non-existent" high memory range (which will probably be the maximum size page encryption bitmap allocated/resized).
Let me know if you have different thoughts on this ?
Hi Ashish,
Quoted 7 lines
If this is not an issue in practice, we can just move past this. If we are basically guaranteed that OVMF will trigger hypercalls that expand the bitmap beyond the top of memory, then, yes, that should work. That leaves me slightly nervous that OVMF might regress since it's not obvious that calling a hypercall beyond the top of memory would be "required" for avoiding a somewhat indirectly related issue in guest kernels.
Quoted 4 lines
If possible then we should try to avoid growing/shrinking the bitmap . Today OVMF may not be accessing beyond memory but a malicious guest could send a hypercall down which can trigger a huge memory allocation on the host side and may eventually cause denial of service for other.
Nice catch! Was just writing up an email about this.
Quoted 6 lines
I am in favor if we can find some solution to handle this case. How about Steve's suggestion about VMM making a call down to the kernel to tell how big the bitmap should be? Initially it should be equal to the guest RAM and if VMM ever did the memory expansion then it can send down another notification to increase the bitmap ?
Quoted 6 lines
Optionally, instead of adding a new ioctl, I was wondering if we can extend the
kvm_arch_prepare_memory_region()to make svm specificx86_opswhich can take read the userspace provided memory region and calculate the amount of guest RAM managed by the KVM and grow/shrink the bitmap based on that information. I have not looked deep enough to see if its doable but if it can work then we can avoid adding yet another ioctl.
We also have the set bitmap ioctl in a later patch in this series. We could also use the set ioctl for initialization (it's a little excessive for initialization since there will be an additional ephemeral allocation and a few additional buffer copies, but that's probably fine). An enable_cap has the added benefit of probably being necessary anyway so usermode can disable the migration feature flag.
In general, userspace is going to have to be in direct control of the buffer and its size.
Re: [PATCH v6 08/14] KVM: X86: Introduce KVM_HC_PAGE_ENC_STATUS hypercall
Hello Steve, Brijesh,
Quoted 301 lines · Steve Rutherford
Quoted 276 lines · Brijesh Singh
Quoted 264 lines · Steve Rutherford
Quoted 253 lines · Ashish Kalra
Hello Steve,
Quoted 233 lines · Steve Rutherford
Quoted 57 lines · Ashish Kalra
From: Brijesh Singh <[email protected]>
This hypercall is used by the SEV guest to notify a change in the page encryption status to the hypervisor. The hypercall should be invoked only when the encryption attribute is changed from encrypted -> decrypted and vice versa. By default all guest pages are considered encrypted.
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]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[email protected]>--- Documentation/virt/kvm/hypercalls.rst | 15 +++++ arch/x86/include/asm/kvm_host.h | 2 + arch/x86/kvm/svm.c | 95 +++++++++++++++++++++++++++ arch/x86/kvm/vmx/vmx.c | 1 + arch/x86/kvm/x86.c | 6 ++ include/uapi/linux/kvm_para.h | 1 + 6 files changed, 120 insertions(+)Documentation/virt/kvm/hypercalls.rst +15
-0@@ -169,3 +169,18 @@ a0: destination APIC ID :Usage example: When sending a call-function IPI-many to vCPUs, yield if any of the IPI target vCPUs was preempted. + + +8. KVM_HC_PAGE_ENC_STATUS +------------------------- +:Architecture: x86 +:Status: active +:Purpose: Notify the encryption status changes in guest page table (SEV guest) + +a0: the guest physical address of the start page +a1: the number of pages +a2: encryption attribute + + Where: + * 1: Encryption attribute is set + * 0: Encryption attribute is clearedarch/x86/include/asm/kvm_host.h +0
-0arch/x86/include/asm/kvm_host.h struct kvm_x86_ops { · line 1271
Quoted 5 lines
arch/x86/include/asm/kvm_host.h +2
-0@@ -1267,6 +1267,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);Nit: spell out size instead of sz.
Quoted 169 lines
arch/x86/include/asm/kvm_host.h +0
-0};struct kvm_arch_async_pf {arch/x86/kvm/svm.c +95
-0@@ -136,6 +136,8 @@ struct kvm_sev_info {int fd;/* SEV device fd */ unsigned long pages_locked;/* Number of pages locked */ struct list_head regions_list;/* List of registered regions */ +unsigned long *page_enc_bmap; +unsigned long page_enc_bmap_size; };struct kvm_svm {@@ -1991,6 +1993,9 @@ static void sev_vm_destroy(struct kvm *kvm)sev_unbind_asid (kvm, sev->handle);sev_asid_free (sev->asid); + +kvfree (sev->page_enc_bmap); + sev->page_enc_bmap =NULL ; }static void avic_vm_destroy (struct kvm *kvm)@@ -7593,6 +7598,94 @@ static int sev_receive_finish(struct kvm *kvm, struct kvm_sev_cmd *argp)return ret; } +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); +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); +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; +kvm_pfn_t pfn_start, pfn_end; +gfn_t gfn_start, gfn_end; +int ret; + +if (!sev_guest (kvm)) +return -EINVAL ; + +if (!npages) +return 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); + +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;@@ -7995,6 +8088,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@@ -8001,6 +8001,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@@ -7564,6 +7564,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@@ -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 12Quoted 12 lines
Quoted 5 lines
include/uapi/linux/kvm_para.h +0
-1/* * hypercalls use architecture specific -- 2.17.1
I'm still not excited by the dynamic resizing. I believe the guest hypercall can be called in atomic contexts, which makes me particularly unexcited to see a potentially large vmalloc on the host followed by filling the buffer. Particularly when the buffer might be non-trivial in size (~1MB per 32GB, per some back of the envelope math).
I think looking at more practical situations, most hypercalls will happen during the boot stage, when device specific initializations are happening, so typically the maximum page encryption bitmap size would be allocated early enough.
Quoted 8 lines
In fact, initial hypercalls made by OVMF will probably allocate the maximum page bitmap size even before the kernel comes up, especially as they will be setting up page enc/dec status for MMIO, ROM, ACPI regions, PCI device memory, etc., and most importantly for "non-existent" high memory range (which will probably be the maximum size page encryption bitmap allocated/resized).
Let me know if you have different thoughts on this ?
Hi Ashish,
Quoted 7 lines
If this is not an issue in practice, we can just move past this. If we are basically guaranteed that OVMF will trigger hypercalls that expand the bitmap beyond the top of memory, then, yes, that should work. That leaves me slightly nervous that OVMF might regress since it's not obvious that calling a hypercall beyond the top of memory would be "required" for avoiding a somewhat indirectly related issue in guest kernels.
Quoted 4 lines
If possible then we should try to avoid growing/shrinking the bitmap . Today OVMF may not be accessing beyond memory but a malicious guest could send a hypercall down which can trigger a huge memory allocation on the host side and may eventually cause denial of service for other.
Nice catch! Was just writing up an email about this.
Quoted 6 lines
I am in favor if we can find some solution to handle this case. How about Steve's suggestion about VMM making a call down to the kernel to tell how big the bitmap should be? Initially it should be equal to the guest RAM and if VMM ever did the memory expansion then it can send down another notification to increase the bitmap ?
Quoted 6 lines
Optionally, instead of adding a new ioctl, I was wondering if we can extend the
kvm_arch_prepare_memory_region()to make svm specificx86_opswhich can take read the userspace provided memory region and calculate the amount of guest RAM managed by the KVM and grow/shrink the bitmap based on that information. I have not looked deep enough to see if its doable but if it can work then we can avoid adding yet another ioctl.We also have the set bitmap ioctl in a later patch in this series. We could also use the set ioctl for initialization (it's a little excessive for initialization since there will be an additional ephemeral allocation and a few additional buffer copies, but that's probably fine). An
enable_caphas the added benefit of probably being necessary anyway so usermode can disable the migration feature flag.
Quoted 2 lines
In general, userspace is going to have to be in direct control of the buffer and its size.
My only practical concern about setting a static bitmap size based on guest memory is about the hypercalls being made initially by OVMF to set page enc/dec status for ROM, ACPI regions and especially the non-existent high memory range. The new ioctl will statically setup bitmap size to whatever guest RAM is specified, say 4G, 8G, etc., but the OVMF hypercall for non-existent memory will try to do a hypercall for guest physical memory range like ~6G->64G (for 4G guest RAM setup), this hypercall will basically have to just return doing nothing, because the allocated bitmap won't have this guest physical range available ?
Also, hypercalls for ROM, ACPI, device regions and any memory holes within the static bitmap setup as per guest RAM config will work, but what about hypercalls for any device regions beyond the guest RAM config ?
Thanks,
Ashish
Re: [PATCH v6 08/14] KVM: X86: Introduce KVM_HC_PAGE_ENC_STATUS hypercall
Quoted 318 lines · Ashish Kalra
Hello Steve, Brijesh,
Quoted 301 lines · Steve Rutherford
Quoted 276 lines · Brijesh Singh
Quoted 264 lines · Steve Rutherford
Quoted 253 lines · Ashish Kalra
Hello Steve,
Quoted 233 lines · Steve Rutherford
Quoted 57 lines · Ashish Kalra
From: Brijesh Singh <[email protected]>
This hypercall is used by the SEV guest to notify a change in the page encryption status to the hypervisor. The hypercall should be invoked only when the encryption attribute is changed from encrypted -> decrypted and vice versa. By default all guest pages are considered encrypted.
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]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[email protected]>--- Documentation/virt/kvm/hypercalls.rst | 15 +++++ arch/x86/include/asm/kvm_host.h | 2 + arch/x86/kvm/svm.c | 95 +++++++++++++++++++++++++++ arch/x86/kvm/vmx/vmx.c | 1 + arch/x86/kvm/x86.c | 6 ++ include/uapi/linux/kvm_para.h | 1 + 6 files changed, 120 insertions(+)Documentation/virt/kvm/hypercalls.rst +15
-0@@ -169,3 +169,18 @@ a0: destination APIC ID :Usage example: When sending a call-function IPI-many to vCPUs, yield if any of the IPI target vCPUs was preempted. + + +8. KVM_HC_PAGE_ENC_STATUS +------------------------- +:Architecture: x86 +:Status: active +:Purpose: Notify the encryption status changes in guest page table (SEV guest) + +a0: the guest physical address of the start page +a1: the number of pages +a2: encryption attribute + + Where: + * 1: Encryption attribute is set + * 0: Encryption attribute is clearedarch/x86/include/asm/kvm_host.h +0
-0arch/x86/include/asm/kvm_host.h struct kvm_x86_ops { · line 1271
Quoted 5 lines
arch/x86/include/asm/kvm_host.h +2
-0@@ -1267,6 +1267,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);Nit: spell out size instead of sz.
Quoted 169 lines
arch/x86/include/asm/kvm_host.h +0
-0};struct kvm_arch_async_pf {arch/x86/kvm/svm.c +95
-0@@ -136,6 +136,8 @@ struct kvm_sev_info {int fd;/* SEV device fd */ unsigned long pages_locked;/* Number of pages locked */ struct list_head regions_list;/* List of registered regions */ +unsigned long *page_enc_bmap; +unsigned long page_enc_bmap_size; };struct kvm_svm {@@ -1991,6 +1993,9 @@ static void sev_vm_destroy(struct kvm *kvm)sev_unbind_asid (kvm, sev->handle);sev_asid_free (sev->asid); + +kvfree (sev->page_enc_bmap); + sev->page_enc_bmap =NULL ; }static void avic_vm_destroy (struct kvm *kvm)@@ -7593,6 +7598,94 @@ static int sev_receive_finish(struct kvm *kvm, struct kvm_sev_cmd *argp)return ret; } +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); +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); +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; +kvm_pfn_t pfn_start, pfn_end; +gfn_t gfn_start, gfn_end; +int ret; + +if (!sev_guest (kvm)) +return -EINVAL ; + +if (!npages) +return 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); + +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;@@ -7995,6 +8088,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@@ -8001,6 +8001,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@@ -7564,6 +7564,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@@ -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 12Quoted 12 lines
Quoted 5 lines
include/uapi/linux/kvm_para.h +0
-1/* * hypercalls use architecture specific -- 2.17.1
I'm still not excited by the dynamic resizing. I believe the guest hypercall can be called in atomic contexts, which makes me particularly unexcited to see a potentially large vmalloc on the host followed by filling the buffer. Particularly when the buffer might be non-trivial in size (~1MB per 32GB, per some back of the envelope math).
I think looking at more practical situations, most hypercalls will happen during the boot stage, when device specific initializations are happening, so typically the maximum page encryption bitmap size would be allocated early enough.
Quoted 8 lines
In fact, initial hypercalls made by OVMF will probably allocate the maximum page bitmap size even before the kernel comes up, especially as they will be setting up page enc/dec status for MMIO, ROM, ACPI regions, PCI device memory, etc., and most importantly for "non-existent" high memory range (which will probably be the maximum size page encryption bitmap allocated/resized).
Let me know if you have different thoughts on this ?
Hi Ashish,
Quoted 7 lines
If this is not an issue in practice, we can just move past this. If we are basically guaranteed that OVMF will trigger hypercalls that expand the bitmap beyond the top of memory, then, yes, that should work. That leaves me slightly nervous that OVMF might regress since it's not obvious that calling a hypercall beyond the top of memory would be "required" for avoiding a somewhat indirectly related issue in guest kernels.
Quoted 4 lines
If possible then we should try to avoid growing/shrinking the bitmap . Today OVMF may not be accessing beyond memory but a malicious guest could send a hypercall down which can trigger a huge memory allocation on the host side and may eventually cause denial of service for other.
Nice catch! Was just writing up an email about this.
Quoted 6 lines
I am in favor if we can find some solution to handle this case. How about Steve's suggestion about VMM making a call down to the kernel to tell how big the bitmap should be? Initially it should be equal to the guest RAM and if VMM ever did the memory expansion then it can send down another notification to increase the bitmap ?
Quoted 6 lines
Optionally, instead of adding a new ioctl, I was wondering if we can extend the
kvm_arch_prepare_memory_region()to make svm specificx86_opswhich can take read the userspace provided memory region and calculate the amount of guest RAM managed by the KVM and grow/shrink the bitmap based on that information. I have not looked deep enough to see if its doable but if it can work then we can avoid adding yet another ioctl.We also have the set bitmap ioctl in a later patch in this series. We could also use the set ioctl for initialization (it's a little excessive for initialization since there will be an additional ephemeral allocation and a few additional buffer copies, but that's probably fine). An
enable_caphas the added benefit of probably being necessary anyway so usermode can disable the migration feature flag.Quoted 2 lines
In general, userspace is going to have to be in direct control of the buffer and its size.
My only practical concern about setting a static bitmap size based on guest memory is about the hypercalls being made initially by OVMF to set page enc/dec status for ROM, ACPI regions and especially the non-existent high memory range. The new ioctl will statically setup bitmap size to whatever guest RAM is specified, say 4G, 8G, etc., but the OVMF hypercall for non-existent memory will try to do a hypercall for guest physical memory range like ~6G->64G (for 4G guest RAM setup), this hypercall will basically have to just return doing nothing, because the allocated bitmap won't have this guest physical range available ?
Quoted 6 lines
Also, hypercalls for ROM, ACPI, device regions and any memory holes within the static bitmap setup as per guest RAM config will work, but what about hypercalls for any device regions beyond the guest RAM config ?
Thanks,
Ashish
I'm not super familiar with what the address beyond the top of ram is used for. If the memory is not backed by RAM, will it even matter for migration? Sounds like the encryption for SEV won't even apply to it. If we don't need to know what the c-bit state of an address is, we don't need to track it. It doesn't hurt to track it (which is why I'm not super concerned about tracking the memory holes).
Re: [PATCH v6 08/14] KVM: X86: Introduce KVM_HC_PAGE_ENC_STATUS hypercall
Quoted 305 lines · Steve Rutherford
Quoted 304 lines · Ashish Kalra
Hello Steve, Brijesh,
Quoted 301 lines · Steve Rutherford
Quoted 274 lines · Brijesh Singh
Quoted 264 lines · Steve Rutherford
Quoted 253 lines · Ashish Kalra
Hello Steve,
Quoted 233 lines · Steve Rutherford
Quoted 57 lines · Ashish Kalra
From: Brijesh Singh <[email protected]>
This hypercall is used by the SEV guest to notify a change in the page encryption status to the hypervisor. The hypercall should be invoked only when the encryption attribute is changed from encrypted -> decrypted and vice versa. By default all guest pages are considered encrypted.
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]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[email protected]>--- Documentation/virt/kvm/hypercalls.rst | 15 +++++ arch/x86/include/asm/kvm_host.h | 2 + arch/x86/kvm/svm.c | 95 +++++++++++++++++++++++++++ arch/x86/kvm/vmx/vmx.c | 1 + arch/x86/kvm/x86.c | 6 ++ include/uapi/linux/kvm_para.h | 1 + 6 files changed, 120 insertions(+)Documentation/virt/kvm/hypercalls.rst +15
-0@@ -169,3 +169,18 @@ a0: destination APIC ID :Usage example: When sending a call-function IPI-many to vCPUs, yield if any of the IPI target vCPUs was preempted. + + +8. KVM_HC_PAGE_ENC_STATUS +------------------------- +:Architecture: x86 +:Status: active +:Purpose: Notify the encryption status changes in guest page table (SEV guest) + +a0: the guest physical address of the start page +a1: the number of pages +a2: encryption attribute + + Where: + * 1: Encryption attribute is set + * 0: Encryption attribute is clearedarch/x86/include/asm/kvm_host.h +0
-0arch/x86/include/asm/kvm_host.h struct kvm_x86_ops { · line 1271
Quoted 5 lines
arch/x86/include/asm/kvm_host.h +2
-0@@ -1267,6 +1267,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);Nit: spell out size instead of sz.
Quoted 169 lines
arch/x86/include/asm/kvm_host.h +0
-0};struct kvm_arch_async_pf {arch/x86/kvm/svm.c +95
-0@@ -136,6 +136,8 @@ struct kvm_sev_info {int fd;/* SEV device fd */ unsigned long pages_locked;/* Number of pages locked */ struct list_head regions_list;/* List of registered regions */ +unsigned long *page_enc_bmap; +unsigned long page_enc_bmap_size; };struct kvm_svm {@@ -1991,6 +1993,9 @@ static void sev_vm_destroy(struct kvm *kvm)sev_unbind_asid (kvm, sev->handle);sev_asid_free (sev->asid); + +kvfree (sev->page_enc_bmap); + sev->page_enc_bmap =NULL ; }static void avic_vm_destroy (struct kvm *kvm)@@ -7593,6 +7598,94 @@ static int sev_receive_finish(struct kvm *kvm, struct kvm_sev_cmd *argp)return ret; } +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); +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); +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; +kvm_pfn_t pfn_start, pfn_end; +gfn_t gfn_start, gfn_end; +int ret; + +if (!sev_guest (kvm)) +return -EINVAL ; + +if (!npages) +return 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); + +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;@@ -7995,6 +8088,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@@ -8001,6 +8001,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@@ -7564,6 +7564,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@@ -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 12Quoted 12 lines
Quoted 5 lines
include/uapi/linux/kvm_para.h +0
-1/* * hypercalls use architecture specific -- 2.17.1
I'm still not excited by the dynamic resizing. I believe the guest hypercall can be called in atomic contexts, which makes me particularly unexcited to see a potentially large vmalloc on the host followed by filling the buffer. Particularly when the buffer might be non-trivial in size (~1MB per 32GB, per some back of the envelope math).
I think looking at more practical situations, most hypercalls will happen during the boot stage, when device specific initializations are happening, so typically the maximum page encryption bitmap size would be allocated early enough.
Quoted 8 lines
In fact, initial hypercalls made by OVMF will probably allocate the maximum page bitmap size even before the kernel comes up, especially as they will be setting up page enc/dec status for MMIO, ROM, ACPI regions, PCI device memory, etc., and most importantly for "non-existent" high memory range (which will probably be the maximum size page encryption bitmap allocated/resized).
Let me know if you have different thoughts on this ?
Hi Ashish,
Quoted 7 lines
If this is not an issue in practice, we can just move past this. If we are basically guaranteed that OVMF will trigger hypercalls that expand the bitmap beyond the top of memory, then, yes, that should work. That leaves me slightly nervous that OVMF might regress since it's not obvious that calling a hypercall beyond the top of memory would be "required" for avoiding a somewhat indirectly related issue in guest kernels.
Quoted 4 lines
If possible then we should try to avoid growing/shrinking the bitmap . Today OVMF may not be accessing beyond memory but a malicious guest could send a hypercall down which can trigger a huge memory allocation on the host side and may eventually cause denial of service for other.
Nice catch! Was just writing up an email about this.
Quoted 6 lines
I am in favor if we can find some solution to handle this case. How about Steve's suggestion about VMM making a call down to the kernel to tell how big the bitmap should be? Initially it should be equal to the guest RAM and if VMM ever did the memory expansion then it can send down another notification to increase the bitmap ?
Quoted 6 lines
Optionally, instead of adding a new ioctl, I was wondering if we can extend the
kvm_arch_prepare_memory_region()to make svm specificx86_opswhich can take read the userspace provided memory region and calculate the amount of guest RAM managed by the KVM and grow/shrink the bitmap based on that information. I have not looked deep enough to see if its doable but if it can work then we can avoid adding yet another ioctl.We also have the set bitmap ioctl in a later patch in this series. We could also use the set ioctl for initialization (it's a little excessive for initialization since there will be an additional ephemeral allocation and a few additional buffer copies, but that's probably fine). An
enable_caphas the added benefit of probably being necessary anyway so usermode can disable the migration feature flag.In general, userspace is going to have to be in direct control of the buffer and its size.
Quoted 9 lines
Quoted 9 lines
My only practical concern about setting a static bitmap size based on guest memory is about the hypercalls being made initially by OVMF to set page enc/dec status for ROM, ACPI regions and especially the non-existent high memory range. The new ioctl will statically setup bitmap size to whatever guest RAM is specified, say 4G, 8G, etc., but the OVMF hypercall for non-existent memory will try to do a hypercall for guest physical memory range like ~6G->64G (for 4G guest RAM setup), this hypercall will basically have to just return doing nothing, because the allocated bitmap won't have this guest physical range available ?
IMO, Ovmf issuing a hypercall beyond the guest RAM is simple wrong, it should *not* do that. There was a feature request I submitted sometime back to Tianocore https://bugzilla.tianocore.org/show_bug.cgi?id=623 as I saw this coming in future. I tried highlighting the problem in the MdeModulePkg that it does not provide a notifier to tell OVMF when core creates the MMIO holes etc. It was not a big problem with the SEV initially because we were never getting down to hypervisor to do something about those non-existent regions. But with the migration its now important that we should restart the discussion with UEFI folks and see what can be done. In the kernel patches we should do what is right for the kernel and not workaround the Ovmf limitation.
Quoted 12 lines
Quoted 6 lines
Also, hypercalls for ROM, ACPI, device regions and any memory holes within the static bitmap setup as per guest RAM config will work, but what about hypercalls for any device regions beyond the guest RAM config ?
Thanks,
AshishI'm not super familiar with what the address beyond the top of ram is used for. If the memory is not backed by RAM, will it even matter for migration? Sounds like the encryption for SEV won't even apply to it. If we don't need to know what the c-bit state of an address is, we don't need to track it. It doesn't hurt to track it (which is why I'm not super concerned about tracking the memory holes).
Re: [PATCH v6 08/14] KVM: X86: Introduce KVM_HC_PAGE_ENC_STATUS hypercall
Hello Brijesh,
Quoted 318 lines · Brijesh Singh
Quoted 305 lines · Steve Rutherford
Quoted 304 lines · Ashish Kalra
Hello Steve, Brijesh,
Quoted 301 lines · Steve Rutherford
Quoted 274 lines · Brijesh Singh
Quoted 264 lines · Steve Rutherford
Quoted 253 lines · Ashish Kalra
Hello Steve,
Quoted 233 lines · Steve Rutherford
Quoted 57 lines · Ashish Kalra
From: Brijesh Singh <[email protected]>
This hypercall is used by the SEV guest to notify a change in the page encryption status to the hypervisor. The hypercall should be invoked only when the encryption attribute is changed from encrypted -> decrypted and vice versa. By default all guest pages are considered encrypted.
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]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[email protected]>--- Documentation/virt/kvm/hypercalls.rst | 15 +++++ arch/x86/include/asm/kvm_host.h | 2 + arch/x86/kvm/svm.c | 95 +++++++++++++++++++++++++++ arch/x86/kvm/vmx/vmx.c | 1 + arch/x86/kvm/x86.c | 6 ++ include/uapi/linux/kvm_para.h | 1 + 6 files changed, 120 insertions(+)Documentation/virt/kvm/hypercalls.rst +15
-0@@ -169,3 +169,18 @@ a0: destination APIC ID :Usage example: When sending a call-function IPI-many to vCPUs, yield if any of the IPI target vCPUs was preempted. + + +8. KVM_HC_PAGE_ENC_STATUS +------------------------- +:Architecture: x86 +:Status: active +:Purpose: Notify the encryption status changes in guest page table (SEV guest) + +a0: the guest physical address of the start page +a1: the number of pages +a2: encryption attribute + + Where: + * 1: Encryption attribute is set + * 0: Encryption attribute is clearedarch/x86/include/asm/kvm_host.h +0
-0arch/x86/include/asm/kvm_host.h struct kvm_x86_ops { · line 1271
Quoted 5 lines
arch/x86/include/asm/kvm_host.h +2
-0@@ -1267,6 +1267,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);Nit: spell out size instead of sz.
Quoted 169 lines
arch/x86/include/asm/kvm_host.h +0
-0};struct kvm_arch_async_pf {arch/x86/kvm/svm.c +95
-0@@ -136,6 +136,8 @@ struct kvm_sev_info {int fd;/* SEV device fd */ unsigned long pages_locked;/* Number of pages locked */ struct list_head regions_list;/* List of registered regions */ +unsigned long *page_enc_bmap; +unsigned long page_enc_bmap_size; };struct kvm_svm {@@ -1991,6 +1993,9 @@ static void sev_vm_destroy(struct kvm *kvm)sev_unbind_asid (kvm, sev->handle);sev_asid_free (sev->asid); + +kvfree (sev->page_enc_bmap); + sev->page_enc_bmap =NULL ; }static void avic_vm_destroy (struct kvm *kvm)@@ -7593,6 +7598,94 @@ static int sev_receive_finish(struct kvm *kvm, struct kvm_sev_cmd *argp)return ret; } +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); +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); +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; +kvm_pfn_t pfn_start, pfn_end; +gfn_t gfn_start, gfn_end; +int ret; + +if (!sev_guest (kvm)) +return -EINVAL ; + +if (!npages) +return 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); + +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;@@ -7995,6 +8088,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@@ -8001,6 +8001,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@@ -7564,6 +7564,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@@ -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 12Quoted 12 lines
Quoted 5 lines
include/uapi/linux/kvm_para.h +0
-1/* * hypercalls use architecture specific -- 2.17.1
I'm still not excited by the dynamic resizing. I believe the guest hypercall can be called in atomic contexts, which makes me particularly unexcited to see a potentially large vmalloc on the host followed by filling the buffer. Particularly when the buffer might be non-trivial in size (~1MB per 32GB, per some back of the envelope math).
I think looking at more practical situations, most hypercalls will happen during the boot stage, when device specific initializations are happening, so typically the maximum page encryption bitmap size would be allocated early enough.
Quoted 8 lines
In fact, initial hypercalls made by OVMF will probably allocate the maximum page bitmap size even before the kernel comes up, especially as they will be setting up page enc/dec status for MMIO, ROM, ACPI regions, PCI device memory, etc., and most importantly for "non-existent" high memory range (which will probably be the maximum size page encryption bitmap allocated/resized).
Let me know if you have different thoughts on this ?
Hi Ashish,
Quoted 7 lines
If this is not an issue in practice, we can just move past this. If we are basically guaranteed that OVMF will trigger hypercalls that expand the bitmap beyond the top of memory, then, yes, that should work. That leaves me slightly nervous that OVMF might regress since it's not obvious that calling a hypercall beyond the top of memory would be "required" for avoiding a somewhat indirectly related issue in guest kernels.
Quoted 4 lines
If possible then we should try to avoid growing/shrinking the bitmap . Today OVMF may not be accessing beyond memory but a malicious guest could send a hypercall down which can trigger a huge memory allocation on the host side and may eventually cause denial of service for other.
Nice catch! Was just writing up an email about this.
Quoted 6 lines
I am in favor if we can find some solution to handle this case. How about Steve's suggestion about VMM making a call down to the kernel to tell how big the bitmap should be? Initially it should be equal to the guest RAM and if VMM ever did the memory expansion then it can send down another notification to increase the bitmap ?
Quoted 6 lines
Optionally, instead of adding a new ioctl, I was wondering if we can extend the
kvm_arch_prepare_memory_region()to make svm specificx86_opswhich can take read the userspace provided memory region and calculate the amount of guest RAM managed by the KVM and grow/shrink the bitmap based on that information. I have not looked deep enough to see if its doable but if it can work then we can avoid adding yet another ioctl.We also have the set bitmap ioctl in a later patch in this series. We could also use the set ioctl for initialization (it's a little excessive for initialization since there will be an additional ephemeral allocation and a few additional buffer copies, but that's probably fine). An
enable_caphas the added benefit of probably being necessary anyway so usermode can disable the migration feature flag.In general, userspace is going to have to be in direct control of the buffer and its size.
Quoted 9 lines
Quoted 9 lines
My only practical concern about setting a static bitmap size based on guest memory is about the hypercalls being made initially by OVMF to set page enc/dec status for ROM, ACPI regions and especially the non-existent high memory range. The new ioctl will statically setup bitmap size to whatever guest RAM is specified, say 4G, 8G, etc., but the OVMF hypercall for non-existent memory will try to do a hypercall for guest physical memory range like ~6G->64G (for 4G guest RAM setup), this hypercall will basically have to just return doing nothing, because the allocated bitmap won't have this guest physical range available ?
Quoted 11 lines
IMO, Ovmf issuing a hypercall beyond the guest RAM is simple wrong, it should *not* do that. There was a feature request I submitted sometime back to Tianocore https://bugzilla.tianocore.org/show_bug.cgi?id=623 as I saw this coming in future. I tried highlighting the problem in the MdeModulePkg that it does not provide a notifier to tell OVMF when core creates the MMIO holes etc. It was not a big problem with the SEV initially because we were never getting down to hypervisor to do something about those non-existent regions. But with the migration its now important that we should restart the discussion with UEFI folks and see what can be done. In the kernel patches we should do what is right for the kernel and not workaround the Ovmf limitation.
Ok, this makes sense. I will start exploring kvm_arch_prepare_memory_region() to see if it can assist in computing the guest RAM or otherwise i will look at adding a new ioctl interface for the same.
Thanks,
Ashish
Quoted 14 lines
Quoted 14 lines
Quoted 8 lines
Also, hypercalls for ROM, ACPI, device regions and any memory holes within the static bitmap setup as per guest RAM config will work, but what about hypercalls for any device regions beyond the guest RAM config ?
Thanks,
AshishI'm not super familiar with what the address beyond the top of ram is used for. If the memory is not backed by RAM, will it even matter for migration? Sounds like the encryption for SEV won't even apply to it. If we don't need to know what the c-bit state of an address is, we don't need to track it. It doesn't hurt to track it (which is why I'm not super concerned about tracking the memory holes).
Re: [PATCH v6 08/14] KVM: X86: Introduce KVM_HC_PAGE_ENC_STATUS hypercall
Hello Brijesh, Steve,
Quoted 333 lines · Ashish Kalra
Hello Brijesh,
Quoted 318 lines · Brijesh Singh
Quoted 305 lines · Steve Rutherford
Quoted 304 lines · Ashish Kalra
Hello Steve, Brijesh,
Quoted 301 lines · Steve Rutherford
Quoted 274 lines · Brijesh Singh
Quoted 264 lines · Steve Rutherford
Quoted 253 lines · Ashish Kalra
Hello Steve,
Quoted 233 lines · Steve Rutherford
Quoted 57 lines · Ashish Kalra
From: Brijesh Singh <[email protected]>
This hypercall is used by the SEV guest to notify a change in the page encryption status to the hypervisor. The hypercall should be invoked only when the encryption attribute is changed from encrypted -> decrypted and vice versa. By default all guest pages are considered encrypted.
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]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[email protected]>--- Documentation/virt/kvm/hypercalls.rst | 15 +++++ arch/x86/include/asm/kvm_host.h | 2 + arch/x86/kvm/svm.c | 95 +++++++++++++++++++++++++++ arch/x86/kvm/vmx/vmx.c | 1 + arch/x86/kvm/x86.c | 6 ++ include/uapi/linux/kvm_para.h | 1 + 6 files changed, 120 insertions(+)Documentation/virt/kvm/hypercalls.rst +15
-0@@ -169,3 +169,18 @@ a0: destination APIC ID :Usage example: When sending a call-function IPI-many to vCPUs, yield if any of the IPI target vCPUs was preempted. + + +8. KVM_HC_PAGE_ENC_STATUS +------------------------- +:Architecture: x86 +:Status: active +:Purpose: Notify the encryption status changes in guest page table (SEV guest) + +a0: the guest physical address of the start page +a1: the number of pages +a2: encryption attribute + + Where: + * 1: Encryption attribute is set + * 0: Encryption attribute is clearedarch/x86/include/asm/kvm_host.h +0
-0arch/x86/include/asm/kvm_host.h struct kvm_x86_ops { · line 1271
Quoted 5 lines
arch/x86/include/asm/kvm_host.h +2
-0@@ -1267,6 +1267,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);Nit: spell out size instead of sz.
Quoted 169 lines
arch/x86/include/asm/kvm_host.h +0
-0};struct kvm_arch_async_pf {arch/x86/kvm/svm.c +95
-0@@ -136,6 +136,8 @@ struct kvm_sev_info {int fd;/* SEV device fd */ unsigned long pages_locked;/* Number of pages locked */ struct list_head regions_list;/* List of registered regions */ +unsigned long *page_enc_bmap; +unsigned long page_enc_bmap_size; };struct kvm_svm {@@ -1991,6 +1993,9 @@ static void sev_vm_destroy(struct kvm *kvm)sev_unbind_asid (kvm, sev->handle);sev_asid_free (sev->asid); + +kvfree (sev->page_enc_bmap); + sev->page_enc_bmap =NULL ; }static void avic_vm_destroy (struct kvm *kvm)@@ -7593,6 +7598,94 @@ static int sev_receive_finish(struct kvm *kvm, struct kvm_sev_cmd *argp)return ret; } +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); +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); +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; +kvm_pfn_t pfn_start, pfn_end; +gfn_t gfn_start, gfn_end; +int ret; + +if (!sev_guest (kvm)) +return -EINVAL ; + +if (!npages) +return 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); + +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;@@ -7995,6 +8088,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@@ -8001,6 +8001,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@@ -7564,6 +7564,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@@ -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 12Quoted 12 lines
Quoted 5 lines
include/uapi/linux/kvm_para.h +0
-1/* * hypercalls use architecture specific -- 2.17.1
I'm still not excited by the dynamic resizing. I believe the guest hypercall can be called in atomic contexts, which makes me particularly unexcited to see a potentially large vmalloc on the host followed by filling the buffer. Particularly when the buffer might be non-trivial in size (~1MB per 32GB, per some back of the envelope math).
I think looking at more practical situations, most hypercalls will happen during the boot stage, when device specific initializations are happening, so typically the maximum page encryption bitmap size would be allocated early enough.
Quoted 8 lines
In fact, initial hypercalls made by OVMF will probably allocate the maximum page bitmap size even before the kernel comes up, especially as they will be setting up page enc/dec status for MMIO, ROM, ACPI regions, PCI device memory, etc., and most importantly for "non-existent" high memory range (which will probably be the maximum size page encryption bitmap allocated/resized).
Let me know if you have different thoughts on this ?
Hi Ashish,
Quoted 7 lines
If this is not an issue in practice, we can just move past this. If we are basically guaranteed that OVMF will trigger hypercalls that expand the bitmap beyond the top of memory, then, yes, that should work. That leaves me slightly nervous that OVMF might regress since it's not obvious that calling a hypercall beyond the top of memory would be "required" for avoiding a somewhat indirectly related issue in guest kernels.
Quoted 4 lines
If possible then we should try to avoid growing/shrinking the bitmap . Today OVMF may not be accessing beyond memory but a malicious guest could send a hypercall down which can trigger a huge memory allocation on the host side and may eventually cause denial of service for other.
Nice catch! Was just writing up an email about this.
Quoted 6 lines
I am in favor if we can find some solution to handle this case. How about Steve's suggestion about VMM making a call down to the kernel to tell how big the bitmap should be? Initially it should be equal to the guest RAM and if VMM ever did the memory expansion then it can send down another notification to increase the bitmap ?
Quoted 6 lines
Optionally, instead of adding a new ioctl, I was wondering if we can extend the
kvm_arch_prepare_memory_region()to make svm specificx86_opswhich can take read the userspace provided memory region and calculate the amount of guest RAM managed by the KVM and grow/shrink the bitmap based on that information. I have not looked deep enough to see if its doable but if it can work then we can avoid adding yet another ioctl.We also have the set bitmap ioctl in a later patch in this series. We could also use the set ioctl for initialization (it's a little excessive for initialization since there will be an additional ephemeral allocation and a few additional buffer copies, but that's probably fine). An
enable_caphas the added benefit of probably being necessary anyway so usermode can disable the migration feature flag.In general, userspace is going to have to be in direct control of the buffer and its size.
Quoted 9 lines
Quoted 9 lines
My only practical concern about setting a static bitmap size based on guest memory is about the hypercalls being made initially by OVMF to set page enc/dec status for ROM, ACPI regions and especially the non-existent high memory range. The new ioctl will statically setup bitmap size to whatever guest RAM is specified, say 4G, 8G, etc., but the OVMF hypercall for non-existent memory will try to do a hypercall for guest physical memory range like ~6G->64G (for 4G guest RAM setup), this hypercall will basically have to just return doing nothing, because the allocated bitmap won't have this guest physical range available ?
Quoted 11 lines
IMO, Ovmf issuing a hypercall beyond the guest RAM is simple wrong, it should *not* do that. There was a feature request I submitted sometime back to Tianocore https://bugzilla.tianocore.org/show_bug.cgi?id=623 as I saw this coming in future. I tried highlighting the problem in the MdeModulePkg that it does not provide a notifier to tell OVMF when core creates the MMIO holes etc. It was not a big problem with the SEV initially because we were never getting down to hypervisor to do something about those non-existent regions. But with the migration its now important that we should restart the discussion with UEFI folks and see what can be done. In the kernel patches we should do what is right for the kernel and not workaround the Ovmf limitation.
Quoted 5 lines
Ok, this makes sense. I will start exploring
kvm_arch_prepare_memory_region()to see if it can assist in computing the guest RAM or otherwise i will look at adding a new ioctl interface for the same.
I looked at kvm_arch_prepare_memory_region() and kvm_arch_commit_memory_region() and kvm_arch_commit_memory_region() looks to be ideal to use for this.
I walked the kvm_memslots in this function and i can compute the approximate guest RAM mapped by KVM, though, i get the guest RAM size as "twice" the configured size because of the two address spaces on x86 KVM, i believe there is one additional address space for SMM/SMRAM use.
I don't think we have a use case of migrating a SEV guest with SMM support ?
Considering that, i believe that i can just compute the guest RAM size using memslots for address space #0 and use that to grow/shrink the bitmap.
As you mentioned i will need to add a new SVM specific x86_ops to callback as part of kvm_arch_commit_memory_region() which will in-turn call sev_resize_page_enc_bitmap().
Thanks,
Ashish
Quoted 14 lines
Quoted 14 lines
Quoted 14 lines
Quoted 8 lines
Also, hypercalls for ROM, ACPI, device regions and any memory holes within the static bitmap setup as per guest RAM config will work, but what about hypercalls for any device regions beyond the guest RAM config ?
Thanks,
AshishI'm not super familiar with what the address beyond the top of ram is used for. If the memory is not backed by RAM, will it even matter for migration? Sounds like the encryption for SEV won't even apply to it. If we don't need to know what the c-bit state of an address is, we don't need to track it. It doesn't hurt to track it (which is why I'm not super concerned about tracking the memory holes).
Re: [PATCH v6 08/14] KVM: X86: Introduce KVM_HC_PAGE_ENC_STATUS hypercall
Quoted 358 lines · Ashish Kalra
Hello Brijesh, Steve,
Quoted 333 lines · Ashish Kalra
Hello Brijesh,
Quoted 318 lines · Brijesh Singh
Quoted 305 lines · Steve Rutherford
Quoted 304 lines · Ashish Kalra
Hello Steve, Brijesh,
Quoted 301 lines · Steve Rutherford
Quoted 274 lines · Brijesh Singh
Quoted 264 lines · Steve Rutherford
Quoted 253 lines · Ashish Kalra
Hello Steve,
Quoted 233 lines · Steve Rutherford
Quoted 57 lines · Ashish Kalra
From: Brijesh Singh <[email protected]>
This hypercall is used by the SEV guest to notify a change in the page encryption status to the hypervisor. The hypercall should be invoked only when the encryption attribute is changed from encrypted -> decrypted and vice versa. By default all guest pages are considered encrypted.
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]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[email protected]>--- Documentation/virt/kvm/hypercalls.rst | 15 +++++ arch/x86/include/asm/kvm_host.h | 2 + arch/x86/kvm/svm.c | 95 +++++++++++++++++++++++++++ arch/x86/kvm/vmx/vmx.c | 1 + arch/x86/kvm/x86.c | 6 ++ include/uapi/linux/kvm_para.h | 1 + 6 files changed, 120 insertions(+)Documentation/virt/kvm/hypercalls.rst +15
-0@@ -169,3 +169,18 @@ a0: destination APIC ID :Usage example: When sending a call-function IPI-many to vCPUs, yield if any of the IPI target vCPUs was preempted. + + +8. KVM_HC_PAGE_ENC_STATUS +------------------------- +:Architecture: x86 +:Status: active +:Purpose: Notify the encryption status changes in guest page table (SEV guest) + +a0: the guest physical address of the start page +a1: the number of pages +a2: encryption attribute + + Where: + * 1: Encryption attribute is set + * 0: Encryption attribute is clearedarch/x86/include/asm/kvm_host.h +0
-0arch/x86/include/asm/kvm_host.h struct kvm_x86_ops { · line 1271
Quoted 5 lines
arch/x86/include/asm/kvm_host.h +2
-0@@ -1267,6 +1267,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);Nit: spell out size instead of sz.
Quoted 169 lines
arch/x86/include/asm/kvm_host.h +0
-0};struct kvm_arch_async_pf {arch/x86/kvm/svm.c +95
-0@@ -136,6 +136,8 @@ struct kvm_sev_info {int fd;/* SEV device fd */ unsigned long pages_locked;/* Number of pages locked */ struct list_head regions_list;/* List of registered regions */ +unsigned long *page_enc_bmap; +unsigned long page_enc_bmap_size; };struct kvm_svm {@@ -1991,6 +1993,9 @@ static void sev_vm_destroy(struct kvm *kvm)sev_unbind_asid (kvm, sev->handle);sev_asid_free (sev->asid); + +kvfree (sev->page_enc_bmap); + sev->page_enc_bmap =NULL ; }static void avic_vm_destroy (struct kvm *kvm)@@ -7593,6 +7598,94 @@ static int sev_receive_finish(struct kvm *kvm, struct kvm_sev_cmd *argp)return ret; } +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); +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); +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; +kvm_pfn_t pfn_start, pfn_end; +gfn_t gfn_start, gfn_end; +int ret; + +if (!sev_guest (kvm)) +return -EINVAL ; + +if (!npages) +return 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); + +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;@@ -7995,6 +8088,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@@ -8001,6 +8001,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@@ -7564,6 +7564,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@@ -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 12Quoted 12 lines
Quoted 5 lines
include/uapi/linux/kvm_para.h +0
-1/* * hypercalls use architecture specific -- 2.17.1
I'm still not excited by the dynamic resizing. I believe the guest hypercall can be called in atomic contexts, which makes me particularly unexcited to see a potentially large vmalloc on the host followed by filling the buffer. Particularly when the buffer might be non-trivial in size (~1MB per 32GB, per some back of the envelope math).
I think looking at more practical situations, most hypercalls will happen during the boot stage, when device specific initializations are happening, so typically the maximum page encryption bitmap size would be allocated early enough.
Quoted 8 lines
In fact, initial hypercalls made by OVMF will probably allocate the maximum page bitmap size even before the kernel comes up, especially as they will be setting up page enc/dec status for MMIO, ROM, ACPI regions, PCI device memory, etc., and most importantly for "non-existent" high memory range (which will probably be the maximum size page encryption bitmap allocated/resized).
Let me know if you have different thoughts on this ?
Hi Ashish,
Quoted 7 lines
If this is not an issue in practice, we can just move past this. If we are basically guaranteed that OVMF will trigger hypercalls that expand the bitmap beyond the top of memory, then, yes, that should work. That leaves me slightly nervous that OVMF might regress since it's not obvious that calling a hypercall beyond the top of memory would be "required" for avoiding a somewhat indirectly related issue in guest kernels.
Quoted 4 lines
If possible then we should try to avoid growing/shrinking the bitmap . Today OVMF may not be accessing beyond memory but a malicious guest could send a hypercall down which can trigger a huge memory allocation on the host side and may eventually cause denial of service for other.
Nice catch! Was just writing up an email about this.
Quoted 6 lines
I am in favor if we can find some solution to handle this case. How about Steve's suggestion about VMM making a call down to the kernel to tell how big the bitmap should be? Initially it should be equal to the guest RAM and if VMM ever did the memory expansion then it can send down another notification to increase the bitmap ?
Quoted 6 lines
Optionally, instead of adding a new ioctl, I was wondering if we can extend the
kvm_arch_prepare_memory_region()to make svm specificx86_opswhich can take read the userspace provided memory region and calculate the amount of guest RAM managed by the KVM and grow/shrink the bitmap based on that information. I have not looked deep enough to see if its doable but if it can work then we can avoid adding yet another ioctl.We also have the set bitmap ioctl in a later patch in this series. We could also use the set ioctl for initialization (it's a little excessive for initialization since there will be an additional ephemeral allocation and a few additional buffer copies, but that's probably fine). An
enable_caphas the added benefit of probably being necessary anyway so usermode can disable the migration feature flag.In general, userspace is going to have to be in direct control of the buffer and its size.
Quoted 9 lines
Quoted 9 lines
My only practical concern about setting a static bitmap size based on guest memory is about the hypercalls being made initially by OVMF to set page enc/dec status for ROM, ACPI regions and especially the non-existent high memory range. The new ioctl will statically setup bitmap size to whatever guest RAM is specified, say 4G, 8G, etc., but the OVMF hypercall for non-existent memory will try to do a hypercall for guest physical memory range like ~6G->64G (for 4G guest RAM setup), this hypercall will basically have to just return doing nothing, because the allocated bitmap won't have this guest physical range available ?
Quoted 11 lines
IMO, Ovmf issuing a hypercall beyond the guest RAM is simple wrong, it should *not* do that. There was a feature request I submitted sometime back to Tianocore https://bugzilla.tianocore.org/show_bug.cgi?id=623 as I saw this coming in future. I tried highlighting the problem in the MdeModulePkg that it does not provide a notifier to tell OVMF when core creates the MMIO holes etc. It was not a big problem with the SEV initially because we were never getting down to hypervisor to do something about those non-existent regions. But with the migration its now important that we should restart the discussion with UEFI folks and see what can be done. In the kernel patches we should do what is right for the kernel and not workaround the Ovmf limitation.
Quoted 5 lines
Ok, this makes sense. I will start exploring
kvm_arch_prepare_memory_region()to see if it can assist in computing the guest RAM or otherwise i will look at adding a new ioctl interface for the same.I looked at
kvm_arch_prepare_memory_region()andkvm_arch_commit_memory_region()andkvm_arch_commit_memory_region()looks to be ideal to use for this.I walked the
kvm_memslotsin this function and i can compute the approximate guest RAM mapped by KVM, though, i get the guest RAM size as "twice" the configured size because of the two address spaces on x86 KVM, i believe there is one additional address space for SMM/SMRAM use.I don't think we have a use case of migrating a SEV guest with SMM support ?
Considering that, i believe that i can just compute the guest RAM size using memslots for address space #0 and use that to grow/shrink the bitmap.
Quoted 15 lines
As you mentioned i will need to add a new SVM specific
x86_opsto callback as part ofkvm_arch_commit_memory_region()which will in-turn callsev_resize_page_enc_bitmap().Thanks,
AshishQuoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Also, hypercalls for ROM, ACPI, device regions and any memory holes within the static bitmap setup as per guest RAM config will work, but what about hypercalls for any device regions beyond the guest RAM config ?
Thanks,
Ashish
Supporting Migration of SEV VM with SMM seems unnecessary, but I wouldn't go out of my way to make it harder to fix. The bitmap is as_id unaware already (as it likely should be, since the PA space is shared), so I would personally ignore summing up the sizes, and instead look for the highest PA that is mapped by a memslot. If I'm not mistaken, the address spaces should (more or less) entirely overlap. This will be suboptimal if the PA space is sparse, but that would be weird anyway.
You could also go back to a suggestion I had much earlier and have the memslots own the bitmaps. I believe there is space to add flags for enabling and disabling features like this on memslots. Basically, you could treat this in the same way as dirty bitmaps, which seem pretty similar. This would work well even if PA space were sparse.
The current patch set is nowhere near sufficient for supporting SMM migration securely, which is fine: we would need to separate out access to the hypercall for particular pages, so that non-SMM could not corrupt SMM. And then the code in SMM would also need to support the hypercall if it ever used shared pages. Until someone asks for this I don't believe it is worth doing. If we put the bitmaps on the memslots, we could probably limit access to a memslot's bitmap to vcpus that can access that memslot. This seems unnecessary for now.
--Steve
PATCH v6 9/14 KVM: x86: Introduce KVM_GET_PAGE_ENC_BITMAP ioctl
In reply to Ashish KalraFrom: Brijesh Singh <[email protected]>
The ioctl can be used to retrieve page encryption bitmap for a given gfn range.
Return the correct bitmap as per the number of pages being requested by the user. Ensure that we only copy bmap->num_pages bytes in the userspace buffer, if bmap->num_pages is not byte aligned we read the trailing bits from the userspace and copy those bits as is.
--- Documentation/virt/kvm/api.rst | 27 +++++++++++++ arch/x86/include/asm/kvm_host.h | 2 + arch/x86/kvm/svm.c | 71 +++++++++++++++++++++++++++++++++ arch/x86/kvm/x86.c | 12 ++++++ include/uapi/linux/kvm.h | 12 ++++++ 5 files changed, 124 insertions(+)
Documentation/virt/kvm/api.rst +27 -0
@@ -4648,6 +4648,33 @@ This ioctl resets VCPU registers and control structures according to the clear cpu reset definition in the POP. However, the cpu is not put into ESA mode. This reset is a superset of the initial reset. +4.125 KVM_GET_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
@@ -1269,6 +1269,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 +71 -0
83 lines not on this page · raw message
arch/x86/kvm/x86.c +12 -0
@@ -5226,6 +5226,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
@@ -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, 0xc5, struct kvm_page_enc_bitmap) +/* Secure Encrypted Virtualization command */ enum sev_cmd_id {/* Guest initialization commands */
Signature · 1 line
2.17.1
Re: [PATCH v6 09/14] KVM: x86: Introduce KVM_GET_PAGE_ENC_BITMAP ioctl
Quoted 10 lines · Ashish Kalra
From: Brijesh Singh <[email protected]>
The ioctl can be used to retrieve page encryption bitmap for a given gfn range.
Return the correct bitmap as per the number of pages being requested by the user. Ensure that we only copy
bmap->num_pagesbytes in the userspace buffer, ifbmap->num_pagesis not byte aligned we read the trailing bits from the userspace and copy those bits as is.
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]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[email protected]>
With the suggestions below...
Quoted 26 lines
--- Documentation/virt/kvm/api.rst | 27 +++++++++++++ arch/x86/include/asm/kvm_host.h | 2 + arch/x86/kvm/svm.c | 71 +++++++++++++++++++++++++++++++++ arch/x86/kvm/x86.c | 12 ++++++ include/uapi/linux/kvm.h | 12 ++++++ 5 files changed, 124 insertions(+)Documentation/virt/kvm/api.rst +10
-0@@ -4648,6 +4648,33 @@ This ioctl resets VCPU registers and control structures according to the clear cpu reset definition in the POP. However, the cpu is not put into ESA mode. This reset is a superset of the initial reset. +4.125 KVM_GET_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 */
Documentation/virt/kvm/api.rst This ioctl resets VCPU registers and control structures according to · line 4670
Quoted 10 lines
Documentation/virt/kvm/api.rst +10
-0+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
s/have concept/have the concept/
Documentation/virt/kvm/api.rst This ioctl resets VCPU registers and control structures according to · line 4671
Quoted 1 line
Documentation/virt/kvm/api.rst +1
-0+page is encrypted with the guest-specific key, while shared page may
s/page is/pages are/
s/shared page/the shared pages/
Documentation/virt/kvm/api.rst This ioctl resets VCPU registers and control structures according to · line 4674
Quoted 3 lines
Documentation/virt/kvm/api.rst +3
-0+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
s/, if/. If/
Documentation/virt/kvm/api.rst This ioctl resets VCPU registers and control structures according to · line 4675
Quoted 1 line
Documentation/virt/kvm/api.rst +1
-0+is private then userspace need to use SEV migration commands to transmit
s/then userspace need/then the userspace needs/
Quoted 163 lines
Documentation/virt/kvm/api.rst +2
-0+the page. + 5. The kvm_run structure ========================arch/x86/include/asm/kvm_host.h +2
-0@@ -1269,6 +1269,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 +71
-0@@ -7686,6 +7686,76 @@ 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 sz, i, sz_bytes; +unsigned long *bitmap; +int ret, n; + +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 ) /BITS_PER_BYTE ; + 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 ; + + n = bmap->num_pages %BITS_PER_BYTE ; + sz_bytes =ALIGN (bmap->num_pages,BITS_PER_BYTE ) /BITS_PER_BYTE ; + +/* +* Return the correct bitmap as per the number of pages being +* requested by the user. Ensure that we only copy bmap->num_pages +* bytes in the userspace buffer, if bmap->num_pages is not byte +* aligned we read the trailing bits from the userspace and copy +* those bits as is. +*/ + +if (n) { +unsigned char *bitmap_kernel = (unsigned char *)bitmap; +unsigned char bitmap_user; +unsigned long offset, mask; + + offset = bmap->num_pages /BITS_PER_BYTE ; +if (copy_from_user (&bitmap_user, bmap->enc_bitmap + offset, +sizeof (unsigned char ))) + goto out; + + mask =GENMASK (n -1 ,0 ); + bitmap_user &= ~mask; + bitmap_kernel[offset] &= mask; + bitmap_kernel[offset] |= bitmap_user; + } + +if (copy_to_user (bmap->enc_bitmap, bitmap, sz_bytes)) + 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;@@ -8090,6 +8160,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@@ -5226,6 +5226,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@@ -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, 0xc5, struct kvm_page_enc_bitmap) +/* Secure Encrypted Virtualization command */ enum sev_cmd_id {/* Guest initialization commands */ Signature · 1 line
2.17.1
Re: [PATCH v6 09/14] KVM: x86: Introduce KVM_GET_PAGE_ENC_BITMAP ioctl
In reply to Ashish KalraQuoted 74 lines · Ashish Kalra
From: Brijesh Singh <[email protected]>
The ioctl can be used to retrieve page encryption bitmap for a given gfn range.
Return the correct bitmap as per the number of pages being requested by the user. Ensure that we only copy
bmap->num_pagesbytes in the userspace buffer, ifbmap->num_pagesis not byte aligned we read the trailing bits from the userspace and copy those bits as is.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]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[email protected]>--- Documentation/virt/kvm/api.rst | 27 +++++++++++++ arch/x86/include/asm/kvm_host.h | 2 + arch/x86/kvm/svm.c | 71 +++++++++++++++++++++++++++++++++ arch/x86/kvm/x86.c | 12 ++++++ include/uapi/linux/kvm.h | 12 ++++++ 5 files changed, 124 insertions(+)Documentation/virt/kvm/api.rst +27
-0@@ -4648,6 +4648,33 @@ This ioctl resets VCPU registers and control structures according to the clear cpu reset definition in the POP. However, the cpu is not put into ESA mode. This reset is a superset of the initial reset. +4.125 KVM_GET_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 +0
-0
arch/x86/include/asm/kvm_host.h struct kvm_x86_ops { · line 1273
Quoted 5 lines
arch/x86/include/asm/kvm_host.h +2
-0@@ -1269,6 +1269,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);
Looking back at the previous patch, it seems that these two are basically the setter/getter action for page encryption, though one is implemented as a hypercall while the other as an ioctl. If we consider the setter/getter aspect, isn't it better to have some sort of symmetry in the naming of the ops ? For example,
set_page_enc_hc
get_page_enc_ioctl
Quoted 14 lines
arch/x86/include/asm/kvm_host.h +0
-0};struct kvm_arch_async_pf {arch/x86/kvm/svm.c +3
-0@@ -7686,6 +7686,76 @@ 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) +{
arch/x86/kvm/svm.c static int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa, · line 7701
Quoted 10 lines
arch/x86/kvm/svm.c +10
-0+struct kvm_sev_info *sev = &to_kvm_svm (kvm)->sev_info; +unsigned long gfn_start, gfn_end; +unsigned long sz, i, sz_bytes; +unsigned long *bitmap; +int ret, n; + +if (!sev_guest (kvm)) +return -ENOTTY ; + + gfn_start = bmap->start_gfn;
What if bmap->start_gfn is junk ?
Quoted 23 lines
arch/x86/kvm/svm.c +23
-0+ gfn_end = gfn_start + bmap->num_pages; + + sz =ALIGN (bmap->num_pages,BITS_PER_LONG ) /BITS_PER_BYTE ; + 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 ; + + n = bmap->num_pages %BITS_PER_BYTE ; + sz_bytes =ALIGN (bmap->num_pages,BITS_PER_BYTE ) /BITS_PER_BYTE ;
arch/x86/kvm/svm.c static int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa, · line 7734
Quoted 10 lines
arch/x86/kvm/svm.c +10
-0+ +/* +* Return the correct bitmap as per the number of pages being +* requested by the user. Ensure that we only copy bmap->num_pages +* bytes in the userspace buffer, if bmap->num_pages is not byte +* aligned we read the trailing bits from the userspace and copy +* those bits as is. +*/ + +if (n) {
Is it better to check for 'num_pages' at the beginning of the function rather than coming this far if bmap->num_pages is zero ?
arch/x86/kvm/svm.c static int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa, · line 7735
Quoted 1 line
arch/x86/kvm/svm.c +1
-0+unsigned char *bitmap_kernel = (unsigned char *)bitmap;
Just trying to understand why you need this extra variable instead of using 'bitmap' directly.
Quoted 5 lines
arch/x86/kvm/svm.c +5
-0+unsigned char bitmap_user; +unsigned long offset, mask; + + offset = bmap->num_pages /BITS_PER_BYTE ; + if (copy_from_user(&bitmap_user, bmap->enc_bitmap + offset,
arch/x86/kvm/svm.c static int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa, · line 7750
Quoted 10 lines
arch/x86/kvm/svm.c +10
-0+ sizeof(unsigned char))) + goto out; + + mask =GENMASK (n -1 ,0 ); + bitmap_user &= ~mask; + bitmap_kernel[offset] &= mask; + bitmap_kernel[offset] |= bitmap_user; + } + +if (copy_to_user (bmap->enc_bitmap, bitmap, sz_bytes))
If 'n' is zero, we are still copying stuff back to the user. Is that what is expected from userland ?
Another point. Since copy_from_user() was done in the caller, isn't it better to move this to the caller to keep a symmetry ?
Quoted 72 lines
arch/x86/kvm/svm.c +9
-0+ 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;@@ -8090,6 +8160,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@@ -5226,6 +5226,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@@ -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, 0xc5, struct kvm_page_enc_bitmap) +/* Secure Encrypted Virtualization command */ enum sev_cmd_id {/* Guest initialization commands */
Re: [PATCH v6 09/14] KVM: x86: Introduce KVM_GET_PAGE_ENC_BITMAP ioctl
Quoted 87 lines · Krish Sadhukhan
Quoted 72 lines · Ashish Kalra
From: Brijesh Singh <[email protected]>
The ioctl can be used to retrieve page encryption bitmap for a given gfn range.
Return the correct bitmap as per the number of pages being requested by the user. Ensure that we only copy
bmap->num_pagesbytes in the userspace buffer, ifbmap->num_pagesis not byte aligned we read the trailing bits from the userspace and copy those bits as is.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]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[email protected]>--- Documentation/virt/kvm/api.rst | 27 +++++++++++++ arch/x86/include/asm/kvm_host.h | 2 + arch/x86/kvm/svm.c | 71 +++++++++++++++++++++++++++++++++ arch/x86/kvm/x86.c | 12 ++++++ include/uapi/linux/kvm.h | 12 ++++++ 5 files changed, 124 insertions(+)Documentation/virt/kvm/api.rst +27
-0@@ -4648,6 +4648,33 @@ This ioctl resets VCPU registers and control structures according to the clear cpu reset definition in the POP. However, the cpu is not put into ESA mode. This reset is a superset of the initial reset. +4.125 KVM_GET_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 +0
-0arch/x86/include/asm/kvm_host.h struct kvm_x86_ops { · line 1273
Quoted 5 lines
arch/x86/include/asm/kvm_host.h +2
-0@@ -1269,6 +1269,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);Looking back at the previous patch, it seems that these two are basically the setter/getter action for page encryption, though one is implemented as a hypercall while the other as an ioctl. If we consider the setter/getter aspect, isn't it better to have some sort of symmetry in the naming of the ops ? For example,
Quoted 5 lines
set_page_enc_hc
get_page_enc_ioctlQuoted 1 line
arch/x86/include/asm/kvm_host.h +0
-0};
These are named as per their usage. While the page_enc_status_hc is a hypercall used by a guest to mark the page encryption bitmap, the other ones are ioctl interfaces used by Qemu (or Qemu alternative) to get/set the page encryption bitmaps, so these are named accordingly.
Quoted 54 lines
Quoted 11 lines
arch/x86/include/asm/kvm_host.h +0
-0struct kvm_arch_async_pf {arch/x86/kvm/svm.c +3
-0@@ -7686,6 +7686,76 @@ 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) +{arch/x86/kvm/svm.c static int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa, · line 7700
Quoted 10 lines
arch/x86/kvm/svm.c +10
-0+struct kvm_sev_info *sev = &to_kvm_svm (kvm)->sev_info; +unsigned long gfn_start, gfn_end; +unsigned long sz, i, sz_bytes; +unsigned long *bitmap; +int ret, n; + +if (!sev_guest (kvm)) +return -ENOTTY ; + + gfn_start = bmap->start_gfn;What if
bmap->start_gfnis junk ?Quoted 29 lines
arch/x86/kvm/svm.c +29
-0+ gfn_end = gfn_start + bmap->num_pages; + + sz =ALIGN (bmap->num_pages,BITS_PER_LONG ) /BITS_PER_BYTE ; + 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 ; + + n = bmap->num_pages %BITS_PER_BYTE ; + sz_bytes =ALIGN (bmap->num_pages,BITS_PER_BYTE ) /BITS_PER_BYTE ; + +/* +* Return the correct bitmap as per the number of pages being +* requested by the user. Ensure that we only copy bmap->num_pages +* bytes in the userspace buffer, if bmap->num_pages is not byte +* aligned we read the trailing bits from the userspace and copy
Quoted 9 lines
arch/x86/kvm/svm.c static int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa, · line 7734
Quoted 4 lines
arch/x86/kvm/svm.c +4
-0+* those bits as is. +*/ + +if (n) {Is it better to check for '
num_pages' at the beginning of the function rather than coming this far ifbmap->num_pagesis zero ?
This is not checking for "num_pages", this is basically checking if bmap->num_pages is not byte aligned.
Quoted 6 lines
arch/x86/kvm/svm.c static int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa, · line 7735
Quoted 1 line
arch/x86/kvm/svm.c +1
-0+unsigned char *bitmap_kernel = (unsigned char *)bitmap;Just trying to understand why you need this extra variable instead of using 'bitmap' directly.
Makes the code much more readable/understandable.
Quoted 20 lines
Quoted 5 lines
arch/x86/kvm/svm.c +5
-0+unsigned char bitmap_user; +unsigned long offset, mask; + + offset = bmap->num_pages /BITS_PER_BYTE ; + if (copy_from_user(&bitmap_user, bmap->enc_bitmap + offset,arch/x86/kvm/svm.c static int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa, · line 7750
Quoted 10 lines
arch/x86/kvm/svm.c +10
-0+ sizeof(unsigned char))) + goto out; + + mask =GENMASK (n -1 ,0 ); + bitmap_user &= ~mask; + bitmap_kernel[offset] &= mask; + bitmap_kernel[offset] |= bitmap_user; + } + +if (copy_to_user (bmap->enc_bitmap, bitmap, sz_bytes))If 'n' is zero, we are still copying stuff back to the user. Is that what is expected from userland ?
Quoted 3 lines
Another point. Since
copy_from_user()was done in the caller, isn't it better to move this to the caller to keep a symmetry ?
As per the comments above, please note if n is not zero that means bmap->num_pages is not byte aligned so we read the trailing bits from the userspace and copy those bits as is. If n is zero, then bmap->num_pages is correctly aligned and we copy all the bytes back.
Thanks,
Ashish
Quoted 68 lines
Quoted 68 lines
arch/x86/kvm/svm.c +9
-0+ 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;@@ -8090,6 +8160,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@@ -5226,6 +5226,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@@ -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, 0xc5, struct kvm_page_enc_bitmap) +/* Secure Encrypted Virtualization command */ enum sev_cmd_id {/* Guest initialization commands */
Krish Sadhukhan
Reviewed-byRe: [PATCH v6 09/14] KVM: x86: Introduce KVM_GET_PAGE_ENC_BITMAP ioctl
Quoted 88 lines · Ashish Kalra
Quoted 87 lines · Krish Sadhukhan
Quoted 72 lines · Ashish Kalra
From: Brijesh Singh <[email protected]>
The ioctl can be used to retrieve page encryption bitmap for a given gfn range.
Return the correct bitmap as per the number of pages being requested by the user. Ensure that we only copy
bmap->num_pagesbytes in the userspace buffer, ifbmap->num_pagesis not byte aligned we read the trailing bits from the userspace and copy those bits as is.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]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[email protected]>--- Documentation/virt/kvm/api.rst | 27 +++++++++++++ arch/x86/include/asm/kvm_host.h | 2 + arch/x86/kvm/svm.c | 71 +++++++++++++++++++++++++++++++++ arch/x86/kvm/x86.c | 12 ++++++ include/uapi/linux/kvm.h | 12 ++++++ 5 files changed, 124 insertions(+)Documentation/virt/kvm/api.rst +27
-0@@ -4648,6 +4648,33 @@ This ioctl resets VCPU registers and control structures according to the clear cpu reset definition in the POP. However, the cpu is not put into ESA mode. This reset is a superset of the initial reset. +4.125 KVM_GET_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 +0
-0arch/x86/include/asm/kvm_host.h struct kvm_x86_ops { · line 1273
Quoted 5 lines
arch/x86/include/asm/kvm_host.h +2
-0@@ -1269,6 +1269,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);Looking back at the previous patch, it seems that these two are basically the setter/getter action for page encryption, though one is implemented as a hypercall while the other as an ioctl. If we consider the setter/getter aspect, isn't it better to have some sort of symmetry in the naming of the ops ? For example,
set_page_enc_hc
Quoted 7 lines
Quoted 3 lines
get_page_enc_ioctlQuoted 1 line
arch/x86/include/asm/kvm_host.h +0
-0};These are named as per their usage. While the
page_enc_status_hcis a hypercall used by a guest to mark the page encryption bitmap, the other ones are ioctl interfaces used by Qemu (or Qemu alternative) to get/set the page encryption bitmaps, so these are named accordingly.
OK.
Please rename 'set_page_enc_hc' to 'set_page_enc_hypercall' to match 'patch_hypercall'.
Quoted 170 lines
Quoted 53 lines
Quoted 12 lines
arch/x86/include/asm/kvm_host.h +0
-0struct kvm_arch_async_pf {arch/x86/kvm/svm.c +3
-0@@ -7686,6 +7686,76 @@ 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) +{arch/x86/kvm/svm.c static int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa, · line 7700
Quoted 10 lines
arch/x86/kvm/svm.c +10
-0+struct kvm_sev_info *sev = &to_kvm_svm (kvm)->sev_info; +unsigned long gfn_start, gfn_end; +unsigned long sz, i, sz_bytes; +unsigned long *bitmap; +int ret, n; + +if (!sev_guest (kvm)) +return -ENOTTY ; + + gfn_start = bmap->start_gfn;What if
bmap->start_gfnis junk ?Quoted 28 lines
arch/x86/kvm/svm.c +28
-0+ gfn_end = gfn_start + bmap->num_pages; + + sz =ALIGN (bmap->num_pages,BITS_PER_LONG ) /BITS_PER_BYTE ; + 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 ; + + n = bmap->num_pages %BITS_PER_BYTE ; + sz_bytes =ALIGN (bmap->num_pages,BITS_PER_BYTE ) /BITS_PER_BYTE ; + +/* +* Return the correct bitmap as per the number of pages being +* requested by the user. Ensure that we only copy bmap->num_pages +* bytes in the userspace buffer, if bmap->num_pages is not byte Quoted 9 lines
arch/x86/kvm/svm.c static int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa, · line 7734
Quoted 5 lines
arch/x86/kvm/svm.c +5
-0+* aligned we read the trailing bits from the userspace and copy +* those bits as is. +*/ + +if (n) {Is it better to check for '
num_pages' at the beginning of the function rather than coming this far ifbmap->num_pagesis zero ?This is not checking for "
num_pages", this is basically checking ifbmap->num_pagesis not byte aligned.Quoted 5 lines
arch/x86/kvm/svm.c static int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa, · line 7735
Quoted 1 line
arch/x86/kvm/svm.c +1
-0+unsigned char *bitmap_kernel = (unsigned char *)bitmap;Just trying to understand why you need this extra variable instead of using 'bitmap' directly.
Makes the code much more readable/understandable.
Quoted 19 lines
Quoted 5 lines
arch/x86/kvm/svm.c +5
-0+unsigned char bitmap_user; +unsigned long offset, mask; + + offset = bmap->num_pages /BITS_PER_BYTE ; + if (copy_from_user(&bitmap_user, bmap->enc_bitmap + offset,arch/x86/kvm/svm.c static int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa, · line 7750
Quoted 10 lines
arch/x86/kvm/svm.c +10
-0+ sizeof(unsigned char))) + goto out; + + mask =GENMASK (n -1 ,0 ); + bitmap_user &= ~mask; + bitmap_kernel[offset] &= mask; + bitmap_kernel[offset] |= bitmap_user; + } + +if (copy_to_user (bmap->enc_bitmap, bitmap, sz_bytes))If 'n' is zero, we are still copying stuff back to the user. Is that what is expected from userland ?
Quoted 3 lines
Another point. Since
copy_from_user()was done in the caller, isn't it better to move this to the caller to keep a symmetry ?As per the comments above, please note if n is not zero that means
bmap->num_pagesis not byte aligned so we read the trailing bits from the userspace and copy those bits as is. If n is zero, thenbmap->num_pagesis correctly aligned and we copy all the bytes back.Thanks,
AshishQuoted 68 lines
Quoted 68 lines
arch/x86/kvm/svm.c +9
-0+ 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;@@ -8090,6 +8160,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@@ -5226,6 +5226,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@@ -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, 0xc5, struct kvm_page_enc_bitmap) +/* Secure Encrypted Virtualization command */ enum sev_cmd_id {/* Guest initialization commands */
Re: [PATCH v6 09/14] KVM: x86: Introduce KVM_GET_PAGE_ENC_BITMAP ioctl
In reply to Krish SadhukhanQuoted 181 lines · Krish Sadhukhan
Quoted 72 lines · Ashish Kalra
From: Brijesh Singh <[email protected]>
The ioctl can be used to retrieve page encryption bitmap for a given gfn range.
Return the correct bitmap as per the number of pages being requested by the user. Ensure that we only copy
bmap->num_pagesbytes in the userspace buffer, ifbmap->num_pagesis not byte aligned we read the trailing bits from the userspace and copy those bits as is.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]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[email protected]>--- Documentation/virt/kvm/api.rst | 27 +++++++++++++ arch/x86/include/asm/kvm_host.h | 2 + arch/x86/kvm/svm.c | 71 +++++++++++++++++++++++++++++++++ arch/x86/kvm/x86.c | 12 ++++++ include/uapi/linux/kvm.h | 12 ++++++ 5 files changed, 124 insertions(+)Documentation/virt/kvm/api.rst +27
-0@@ -4648,6 +4648,33 @@ This ioctl resets VCPU registers and control structures according to the clear cpu reset definition in the POP. However, the cpu is not put into ESA mode. This reset is a superset of the initial reset. +4.125 KVM_GET_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 +0
-0arch/x86/include/asm/kvm_host.h struct kvm_x86_ops { · line 1273
Quoted 5 lines
arch/x86/include/asm/kvm_host.h +2
-0@@ -1269,6 +1269,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);Looking back at the previous patch, it seems that these two are basically the setter/getter action for page encryption, though one is implemented as a hypercall while the other as an ioctl. If we consider the setter/getter aspect, isn't it better to have some sort of symmetry in the naming of the ops ? For example,
set_page_enc_hc
get_page_enc_ioctlQuoted 12 lines
arch/x86/include/asm/kvm_host.h +0
-0};struct kvm_arch_async_pf {arch/x86/kvm/svm.c +3
-0@@ -7686,6 +7686,76 @@ 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) +{arch/x86/kvm/svm.c static int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa, · line 7700
Quoted 10 lines
arch/x86/kvm/svm.c +10
-0+struct kvm_sev_info *sev = &to_kvm_svm (kvm)->sev_info; +unsigned long gfn_start, gfn_end; +unsigned long sz, i, sz_bytes; +unsigned long *bitmap; +int ret, n; + +if (!sev_guest (kvm)) +return -ENOTTY ; + + gfn_start = bmap->start_gfn;What if
bmap->start_gfnis junk ?Quoted 23 lines
arch/x86/kvm/svm.c +23
-0+ gfn_end = gfn_start + bmap->num_pages; + + sz =ALIGN (bmap->num_pages,BITS_PER_LONG ) /BITS_PER_BYTE ; + 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 ; + + n = bmap->num_pages %BITS_PER_BYTE ; + sz_bytes =ALIGN (bmap->num_pages,BITS_PER_BYTE ) /BITS_PER_BYTE ;arch/x86/kvm/svm.c static int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa, · line 7734
Quoted 10 lines
arch/x86/kvm/svm.c +10
-0+ +/* +* Return the correct bitmap as per the number of pages being +* requested by the user. Ensure that we only copy bmap->num_pages +* bytes in the userspace buffer, if bmap->num_pages is not byte +* aligned we read the trailing bits from the userspace and copy +* those bits as is. +*/ + +if (n) {Is it better to check for '
num_pages' at the beginning of the function rather than coming this far ifbmap->num_pagesis zero ?arch/x86/kvm/svm.c static int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa, · line 7735
Quoted 1 line
arch/x86/kvm/svm.c +1
-0+unsigned char *bitmap_kernel = (unsigned char *)bitmap;Just trying to understand why you need this extra variable instead of using 'bitmap' directly.
Quoted 5 lines
arch/x86/kvm/svm.c +5
-0+unsigned char bitmap_user; +unsigned long offset, mask; + + offset = bmap->num_pages /BITS_PER_BYTE ; + if (copy_from_user(&bitmap_user, bmap->enc_bitmap + offset,arch/x86/kvm/svm.c static int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa, · line 7750
Quoted 10 lines
arch/x86/kvm/svm.c +10
-0+ sizeof(unsigned char))) + goto out; + + mask =GENMASK (n -1 ,0 ); + bitmap_user &= ~mask; + bitmap_kernel[offset] &= mask; + bitmap_kernel[offset] |= bitmap_user; + } + +if (copy_to_user (bmap->enc_bitmap, bitmap, sz_bytes))If 'n' is zero, we are still copying stuff back to the user. Is that what is expected from userland ?
Quoted 2 lines
Another point. Since
copy_from_user()was done in the caller, isn't it better to move this to the caller to keep a symmetry ?
That would need the interface of .get_page_enc_bitmap to change, to pass back the local bitmap to the caller for use in copy_to_user() and then free it up. I think it is better to call copy_to_user() here and free the bitmap before returning.
Quoted 69 lines
Quoted 69 lines
arch/x86/kvm/svm.c +9
-0+ 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;@@ -8090,6 +8160,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@@ -5226,6 +5226,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@@ -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, 0xc5, struct kvm_page_enc_bitmap) +/* Secure Encrypted Virtualization command */ enum sev_cmd_id {/* Guest initialization commands */
Re: [PATCH v6 09/14] KVM: x86: Introduce KVM_GET_PAGE_ENC_BITMAP ioctl
Quoted 182 lines · Venu Busireddy
Quoted 181 lines · Krish Sadhukhan
Quoted 72 lines · Ashish Kalra
From: Brijesh Singh <[email protected]>
The ioctl can be used to retrieve page encryption bitmap for a given gfn range.
Return the correct bitmap as per the number of pages being requested by the user. Ensure that we only copy
bmap->num_pagesbytes in the userspace buffer, ifbmap->num_pagesis not byte aligned we read the trailing bits from the userspace and copy those bits as is.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]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[email protected]>--- Documentation/virt/kvm/api.rst | 27 +++++++++++++ arch/x86/include/asm/kvm_host.h | 2 + arch/x86/kvm/svm.c | 71 +++++++++++++++++++++++++++++++++ arch/x86/kvm/x86.c | 12 ++++++ include/uapi/linux/kvm.h | 12 ++++++ 5 files changed, 124 insertions(+)Documentation/virt/kvm/api.rst +27
-0@@ -4648,6 +4648,33 @@ This ioctl resets VCPU registers and control structures according to the clear cpu reset definition in the POP. However, the cpu is not put into ESA mode. This reset is a superset of the initial reset. +4.125 KVM_GET_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 +0
-0arch/x86/include/asm/kvm_host.h struct kvm_x86_ops { · line 1273
Quoted 5 lines
arch/x86/include/asm/kvm_host.h +2
-0@@ -1269,6 +1269,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);Looking back at the previous patch, it seems that these two are basically the setter/getter action for page encryption, though one is implemented as a hypercall while the other as an ioctl. If we consider the setter/getter aspect, isn't it better to have some sort of symmetry in the naming of the ops ? For example,
set_page_enc_hc
get_page_enc_ioctlQuoted 12 lines
arch/x86/include/asm/kvm_host.h +0
-0};struct kvm_arch_async_pf {arch/x86/kvm/svm.c +3
-0@@ -7686,6 +7686,76 @@ 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) +{arch/x86/kvm/svm.c static int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa, · line 7700
Quoted 10 lines
arch/x86/kvm/svm.c +10
-0+struct kvm_sev_info *sev = &to_kvm_svm (kvm)->sev_info; +unsigned long gfn_start, gfn_end; +unsigned long sz, i, sz_bytes; +unsigned long *bitmap; +int ret, n; + +if (!sev_guest (kvm)) +return -ENOTTY ; + + gfn_start = bmap->start_gfn;What if
bmap->start_gfnis junk ?Quoted 23 lines
arch/x86/kvm/svm.c +23
-0+ gfn_end = gfn_start + bmap->num_pages; + + sz =ALIGN (bmap->num_pages,BITS_PER_LONG ) /BITS_PER_BYTE ; + 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 ; + + n = bmap->num_pages %BITS_PER_BYTE ; + sz_bytes =ALIGN (bmap->num_pages,BITS_PER_BYTE ) /BITS_PER_BYTE ;arch/x86/kvm/svm.c static int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa, · line 7734
Quoted 10 lines
arch/x86/kvm/svm.c +10
-0+ +/* +* Return the correct bitmap as per the number of pages being +* requested by the user. Ensure that we only copy bmap->num_pages +* bytes in the userspace buffer, if bmap->num_pages is not byte +* aligned we read the trailing bits from the userspace and copy +* those bits as is. +*/ + +if (n) {Is it better to check for '
num_pages' at the beginning of the function rather than coming this far ifbmap->num_pagesis zero ?arch/x86/kvm/svm.c static int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa, · line 7735
Quoted 1 line
arch/x86/kvm/svm.c +1
-0+unsigned char *bitmap_kernel = (unsigned char *)bitmap;Just trying to understand why you need this extra variable instead of using 'bitmap' directly.
Quoted 5 lines
arch/x86/kvm/svm.c +5
-0+unsigned char bitmap_user; +unsigned long offset, mask; + + offset = bmap->num_pages /BITS_PER_BYTE ; + if (copy_from_user(&bitmap_user, bmap->enc_bitmap + offset,arch/x86/kvm/svm.c static int svm_page_enc_status_hc(struct kvm *kvm, unsigned long gpa, · line 7750
Quoted 10 lines
arch/x86/kvm/svm.c +10
-0+ sizeof(unsigned char))) + goto out; + + mask =GENMASK (n -1 ,0 ); + bitmap_user &= ~mask; + bitmap_kernel[offset] &= mask; + bitmap_kernel[offset] |= bitmap_user; + } + +if (copy_to_user (bmap->enc_bitmap, bitmap, sz_bytes))If 'n' is zero, we are still copying stuff back to the user. Is that what is expected from userland ?
Quoted 8 lines
Quoted 2 lines
Another point. Since
copy_from_user()was done in the caller, isn't it better to move this to the caller to keep a symmetry ?That would need the interface of .get_page_enc_bitmap to change, to pass back the local bitmap to the caller for use in
copy_to_user()and then free it up. I think it is better to callcopy_to_user()here and free the bitmap before returning.
As i replied in my earlier response to this patch, please note that as per comments above, here we are checking if bmap->num_pages is not byte aligned and if not then we read the trailing bits from the userspace and copy those bits as is.
Thanks,
Ashish
Quoted 69 lines
Quoted 69 lines
Quoted 69 lines
arch/x86/kvm/svm.c +9
-0+ 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;@@ -8090,6 +8160,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@@ -5226,6 +5226,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@@ -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, 0xc5, struct kvm_page_enc_bitmap) +/* Secure Encrypted Virtualization command */ enum sev_cmd_id {/* Guest initialization commands */
PATCH v6 10/14 mm: x86: Invoke hypercall when page encryption status is changed
In reply to Ashish KalraFrom: 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.
--- arch/x86/include/asm/paravirt.h | 10 +++++ arch/x86/include/asm/paravirt_types.h | 2 + arch/x86/kernel/paravirt.c | 1 + arch/x86/mm/mem_encrypt.c | 57 ++++++++++++++++++++++++++- arch/x86/mm/pat/set_memory.c | 7 ++++ 5 files changed, 76 insertions(+), 1 deletion(-)
arch/x86/include/asm/paravirt.h +10 -0
@@ -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) {
@@ -946,6 +952,10 @@ static inline void paravirt_arch_dup_mmap(struct mm_struct *oldmm,static inline void paravirt_arch_exit_mmap (struct mm_struct *mm) { } + +static inline void page_encryption_changed (unsigned long vaddr,int npages,bool enc) +{ +}#endif #endif /* __ASSEMBLY__ */ #endif /* _ASM_X86_PARAVIRT_H */
arch/x86/include/asm/paravirt_types.h +2 -0
@@ -215,6 +215,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
@@ -367,6 +367,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
@@ -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
Re: [PATCH v6 10/14] mm: x86: Invoke hypercall when page encryption status is changed
Quoted 124 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.
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]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[email protected]>--- arch/x86/include/asm/paravirt.h | 10 +++++ arch/x86/include/asm/paravirt_types.h | 2 + arch/x86/kernel/paravirt.c | 1 + arch/x86/mm/mem_encrypt.c | 57 ++++++++++++++++++++++++++- arch/x86/mm/pat/set_memory.c | 7 ++++ 5 files changed, 76 insertions(+), 1 deletion(-)arch/x86/include/asm/paravirt.h +10
-0@@ -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) {@@ -946,6 +952,10 @@ static inline void paravirt_arch_dup_mmap(struct mm_struct *oldmm,static inline void paravirt_arch_exit_mmap (struct mm_struct *mm) { } + +static inline void page_encryption_changed (unsigned long vaddr,int npages,bool enc) +{ +}#endif #endif /* __ASSEMBLY__ */ #endif /* _ASM_X86_PARAVIRT_H */ arch/x86/include/asm/paravirt_types.h +2
-0@@ -215,6 +215,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@@ -367,6 +367,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 +22
-0@@ -19,6 +19,7 @@#include <linux/kernel.h> #include <linux/bitops.h> #include <linux/dma-mapping.h> +#include <linux/kvm_para.h> #include <asm/tlbflush.h> #include <asm/fixmap.h> @@ -29,6 +30,7 @@#include <asm/processor-flags.h> #include <asm/msr.h> #include <asm/cmdline.h> +#include <asm/kvm_para.h> #include "mm_internal.h" @@ -196,6 +198,47 @@ void __init sme_early_init(void) swiotlb_force =SWIOTLB_FORCE ; } +static void set_memory_enc_dec_hypercall (unsigned long vaddr,int npages, +bool enc) +{ +unsigned long sz = npages <<PAGE_SHIFT ; +unsigned long vaddr_end, vaddr_next; + + vaddr_end = vaddr + sz; + +for (; vaddr < vaddr_end; vaddr = vaddr_next) { +int psize, pmask, level; +unsigned long pfn; +pte_t *kpte; + + kpte =lookup_address (vaddr, &level); +if (!kpte ||pte_none (*kpte)) +return ; + +switch (level) { +case PG_LEVEL_4K : + pfn =pte_pfn (*kpte);
arch/x86/mm/mem_encrypt.c void __init sme_early_init(void) · line 230
Quoted 10 lines
arch/x86/mm/mem_encrypt.c +10
-0+break ; +case PG_LEVEL_2M : + pfn =pmd_pfn (*(pmd_t *)kpte); +break ; +case PG_LEVEL_1G : + pfn =pud_pfn (*(pud_t *)kpte); +break ; +default : +return ; + }
Is it possible to re-use the code in __set_clr_pte_enc() ?
Quoted 30 lines
arch/x86/mm/mem_encrypt.c +13
-1+ + psize =page_level_size (level); + pmask =page_level_mask (level); + +kvm_sev_hypercall3 (KVM_HC_PAGE_ENC_STATUS , + pfn <<PAGE_SHIFT , psize >>PAGE_SHIFT , enc); + + vaddr_next = (vaddr & pmask) + psize; + } +} +static void __init __set_clr_pte_enc (pte_t *kpte,int level,bool enc) {pgprot_t old_prot, new_prot;@@ -253,12 +296,13 @@ static void __init __set_clr_pte_enc(pte_t *kpte, int level, bool enc)static int __init early_set_memory_enc_dec (unsigned long vaddr,unsigned long size,bool enc) { -unsigned long vaddr_end, vaddr_next; +unsigned long vaddr_end, vaddr_next, start;unsigned long psize, pmask;int split_page_size_mask;int level, ret;pte_t *kpte; + start = vaddr; vaddr_next = vaddr; vaddr_end = vaddr + size;
arch/x86/mm/mem_encrypt.c static int __init early_set_memory_enc_dec(unsigned long vaddr, · line 361
Quoted 5 lines
arch/x86/mm/mem_encrypt.c +2
-0@@ -313,6 +357,8 @@ static int __init early_set_memory_enc_dec(unsigned long vaddr, ret =0 ; +set_memory_enc_dec_hypercall (start,PAGE_ALIGN (size) >>PAGE_SHIFT , + enc);
If I haven't missed anything, it seems early_set_memory_encrypted() doesn't have a caller. So is there a possibility that we can end up calling it in non-SEV context and hence do we need to have the sev_active() guard here ?
Quoted 44 lines
arch/x86/mm/mem_encrypt.c +9
-0out:__flush_tlb_all ();return ret;@@ -451,6 +497,15 @@ void __init mem_encrypt_init(void)if (sev_active ())static_branch_enable (&sev_enable_key); +#ifdef CONFIG_PARAVIRT +/* +* With SEV, we need to make a hypercall when page encryption state is +* changed. +*/ +if (sev_active ()) + pv_ops.mmu.page_encryption_changed = set_memory_enc_dec_hypercall; +#endif +pr_info ("AMD %s active\n" ,sev_active () ?"Secure Encrypted Virtualization (SEV)" :"Secure Memory Encryption (SME)" );arch/x86/mm/pat/set_memory.c +7
-0@@ -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; }
Re: [PATCH v6 10/14] mm: x86: Invoke hypercall when page encryption status is changed
Quoted 159 lines · Krish Sadhukhan
Quoted 115 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.
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]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[email protected]>--- arch/x86/include/asm/paravirt.h | 10 +++++ arch/x86/include/asm/paravirt_types.h | 2 + arch/x86/kernel/paravirt.c | 1 + arch/x86/mm/mem_encrypt.c | 57 ++++++++++++++++++++++++++- arch/x86/mm/pat/set_memory.c | 7 ++++ 5 files changed, 76 insertions(+), 1 deletion(-)arch/x86/include/asm/paravirt.h +10
-0@@ -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) {@@ -946,6 +952,10 @@ static inline void paravirt_arch_dup_mmap(struct mm_struct *oldmm,static inline void paravirt_arch_exit_mmap (struct mm_struct *mm) { } + +static inline void page_encryption_changed (unsigned long vaddr,int npages,bool enc) +{ +}#endif #endif /* __ASSEMBLY__ */ #endif /* _ASM_X86_PARAVIRT_H */ arch/x86/include/asm/paravirt_types.h +2
-0@@ -215,6 +215,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@@ -367,6 +367,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 +22
-0@@ -19,6 +19,7 @@#include <linux/kernel.h> #include <linux/bitops.h> #include <linux/dma-mapping.h> +#include <linux/kvm_para.h> #include <asm/tlbflush.h> #include <asm/fixmap.h> @@ -29,6 +30,7 @@#include <asm/processor-flags.h> #include <asm/msr.h> #include <asm/cmdline.h> +#include <asm/kvm_para.h> #include "mm_internal.h" @@ -196,6 +198,47 @@ void __init sme_early_init(void) swiotlb_force =SWIOTLB_FORCE ; } +static void set_memory_enc_dec_hypercall (unsigned long vaddr,int npages, +bool enc) +{ +unsigned long sz = npages <<PAGE_SHIFT ; +unsigned long vaddr_end, vaddr_next; + + vaddr_end = vaddr + sz; + +for (; vaddr < vaddr_end; vaddr = vaddr_next) { +int psize, pmask, level; +unsigned long pfn; +pte_t *kpte; + + kpte =lookup_address (vaddr, &level); +if (!kpte ||pte_none (*kpte)) +return ; + +switch (level) { +case PG_LEVEL_4K : + pfn =pte_pfn (*kpte);arch/x86/mm/mem_encrypt.c void __init sme_early_init(void) · line 229
Quoted 10 lines
arch/x86/mm/mem_encrypt.c +10
-0+break ; +case PG_LEVEL_2M : + pfn =pmd_pfn (*(pmd_t *)kpte); +break ; +case PG_LEVEL_1G : + pfn =pud_pfn (*(pud_t *)kpte); +break ; +default : +return ; + }Is it possible to re-use the code in
__set_clr_pte_enc()?Quoted 28 lines
arch/x86/mm/mem_encrypt.c +13
-1+ + psize =page_level_size (level); + pmask =page_level_mask (level); + +kvm_sev_hypercall3 (KVM_HC_PAGE_ENC_STATUS , + pfn <<PAGE_SHIFT , psize >>PAGE_SHIFT , enc); + + vaddr_next = (vaddr & pmask) + psize; + } +} +static void __init __set_clr_pte_enc (pte_t *kpte,int level,bool enc) {pgprot_t old_prot, new_prot;@@ -253,12 +296,13 @@ static void __init __set_clr_pte_enc(pte_t *kpte, int level, bool enc)static int __init early_set_memory_enc_dec (unsigned long vaddr,unsigned long size,bool enc) { -unsigned long vaddr_end, vaddr_next; +unsigned long vaddr_end, vaddr_next, start;unsigned long psize, pmask;int split_page_size_mask;int level, ret;pte_t *kpte; + start = vaddr; vaddr_next = vaddr; vaddr_end = vaddr + size;@@ -313,6 +357,8 @@ static int __init early_set_memory_enc_dec(unsigned long vaddr,
Quoted 9 lines
arch/x86/mm/mem_encrypt.c static int __init early_set_memory_enc_dec(unsigned long vaddr, · line 361
Quoted 3 lines
arch/x86/mm/mem_encrypt.c +2
-0ret =0 ; +set_memory_enc_dec_hypercall (start,PAGE_ALIGN (size) >>PAGE_SHIFT , + enc);If I haven't missed anything, it seems
early_set_memory_encrypted()doesn't have a caller. So is there a possibility that we can end up calling it in non-SEV context and hence do we need to have thesev_active()guard here ?
As of now early_set_memory_encrypted() is not used, but early_set_memory_decrypted() is used in __set_percpu_decrypted() and that is called with the sev_active() check.
Thanks,
Ashish
Quoted 39 lines
Quoted 39 lines
arch/x86/mm/mem_encrypt.c +9
-0out:__flush_tlb_all ();return ret;@@ -451,6 +497,15 @@ void __init mem_encrypt_init(void)if (sev_active ())static_branch_enable (&sev_enable_key); +#ifdef CONFIG_PARAVIRT +/* +* With SEV, we need to make a hypercall when page encryption state is +* changed. +*/ +if (sev_active ()) + pv_ops.mmu.page_encryption_changed = set_memory_enc_dec_hypercall; +#endif +pr_info ("AMD %s active\n" ,sev_active () ?"Secure Encrypted Virtualization (SEV)" :"Secure Memory Encryption (SME)" );arch/x86/mm/pat/set_memory.c +7
-0@@ -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; }
Re: [PATCH v6 10/14] mm: x86: Invoke hypercall when page encryption status is changed
In reply to Ashish KalraQuoted 4 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
s/need/needs/
Quoted 15 lines
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]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[email protected]>
Quoted 197 lines
--- arch/x86/include/asm/paravirt.h | 10 +++++ arch/x86/include/asm/paravirt_types.h | 2 + arch/x86/kernel/paravirt.c | 1 + arch/x86/mm/mem_encrypt.c | 57 ++++++++++++++++++++++++++- arch/x86/mm/pat/set_memory.c | 7 ++++ 5 files changed, 76 insertions(+), 1 deletion(-)arch/x86/include/asm/paravirt.h +10
-0@@ -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) {@@ -946,6 +952,10 @@ static inline void paravirt_arch_dup_mmap(struct mm_struct *oldmm,static inline void paravirt_arch_exit_mmap (struct mm_struct *mm) { } + +static inline void page_encryption_changed (unsigned long vaddr,int npages,bool enc) +{ +}#endif #endif /* __ASSEMBLY__ */ #endif /* _ASM_X86_PARAVIRT_H */ arch/x86/include/asm/paravirt_types.h +2
-0@@ -215,6 +215,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@@ -367,6 +367,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@@ -19,6 +19,7 @@#include <linux/kernel.h> #include <linux/bitops.h> #include <linux/dma-mapping.h> +#include <linux/kvm_para.h> #include <asm/tlbflush.h> #include <asm/fixmap.h> @@ -29,6 +30,7 @@#include <asm/processor-flags.h> #include <asm/msr.h> #include <asm/cmdline.h> +#include <asm/kvm_para.h> #include "mm_internal.h" @@ -196,6 +198,47 @@ void __init sme_early_init(void) swiotlb_force =SWIOTLB_FORCE ; } +static void set_memory_enc_dec_hypercall (unsigned long vaddr,int npages, +bool enc) +{ +unsigned long sz = npages <<PAGE_SHIFT ; +unsigned long vaddr_end, vaddr_next; + + vaddr_end = vaddr + sz; + +for (; vaddr < vaddr_end; vaddr = vaddr_next) { +int psize, pmask, level; +unsigned long pfn; +pte_t *kpte; + + kpte =lookup_address (vaddr, &level); +if (!kpte ||pte_none (*kpte)) +return ; + +switch (level) { +case PG_LEVEL_4K : + pfn =pte_pfn (*kpte); +break ; +case PG_LEVEL_2M : + pfn =pmd_pfn (*(pmd_t *)kpte); +break ; +case PG_LEVEL_1G : + pfn =pud_pfn (*(pud_t *)kpte); +break ; +default : +return ; + } + + psize =page_level_size (level); + pmask =page_level_mask (level); + +kvm_sev_hypercall3 (KVM_HC_PAGE_ENC_STATUS , + pfn <<PAGE_SHIFT , psize >>PAGE_SHIFT , enc); + + vaddr_next = (vaddr & pmask) + psize; + } +} +static void __init __set_clr_pte_enc (pte_t *kpte,int level,bool enc) {pgprot_t old_prot, new_prot;@@ -253,12 +296,13 @@ static void __init __set_clr_pte_enc(pte_t *kpte, int level, bool enc)static int __init early_set_memory_enc_dec (unsigned long vaddr,unsigned long size,bool enc) { -unsigned long vaddr_end, vaddr_next; +unsigned long vaddr_end, vaddr_next, start;unsigned long psize, pmask;int split_page_size_mask;int level, ret;pte_t *kpte; + start = vaddr; vaddr_next = vaddr; vaddr_end = vaddr + size;@@ -313,6 +357,8 @@ static int __init early_set_memory_enc_dec(unsigned long vaddr, ret =0 ; +set_memory_enc_dec_hypercall (start,PAGE_ALIGN (size) >>PAGE_SHIFT , + enc); out:__flush_tlb_all ();return ret;@@ -451,6 +497,15 @@ void __init mem_encrypt_init(void)if (sev_active ())static_branch_enable (&sev_enable_key); +#ifdef CONFIG_PARAVIRT +/* +* With SEV, we need to make a hypercall when page encryption state is +* changed. +*/ +if (sev_active ()) + pv_ops.mmu.page_encryption_changed = set_memory_enc_dec_hypercall; +#endif +pr_info ("AMD %s active\n" ,sev_active () ?"Secure Encrypted Virtualization (SEV)" :"Secure Memory Encryption (SME)" );arch/x86/mm/pat/set_memory.c +7
-0@@ -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
PATCH v6 11/14 KVM: x86: Introduce KVM_SET_PAGE_ENC_BITMAP ioctl
In reply to Ashish KalraFrom: Brijesh Singh <[email protected]>
The ioctl can be used to set page encryption bitmap for an incoming guest.
--- Documentation/virt/kvm/api.rst | 22 +++++++++++++++++ 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, 79 insertions(+)
Documentation/virt/kvm/api.rst +22 -0
@@ -4675,6 +4675,28 @@ 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.126 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
@@ -1271,6 +1271,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
@@ -5238,6 +5238,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
@@ -1489,6 +1489,7 @@ struct kvm_enc_region {#define KVM_S390_CLEAR_RESET _IO(KVMIO, 0xc4)#define KVM_GET_PAGE_ENC_BITMAP _IOW(KVMIO, 0xc5, struct kvm_page_enc_bitmap) +#define KVM_SET_PAGE_ENC_BITMAP _IOW(KVMIO, 0xc6, struct kvm_page_enc_bitmap)/* Secure Encrypted Virtualization command */ enum sev_cmd_id {
Signature · 1 line
2.17.1
Re: [PATCH v6 11/14] KVM: x86: Introduce KVM_SET_PAGE_ENC_BITMAP ioctl
Quoted 84 lines · 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]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[email protected]>--- Documentation/virt/kvm/api.rst | 22 +++++++++++++++++ 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, 79 insertions(+)Documentation/virt/kvm/api.rst +22
-0@@ -4675,6 +4675,28 @@ 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.126 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@@ -1271,6 +1271,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 +4
-0@@ -7756,6 +7756,47 @@ static int svm_get_page_enc_bitmap(struct kvm *kvm,return ret; } +static int svm_set_page_enc_bitmap (struct kvm *kvm, +struct kvm_page_enc_bitmap *bmap) +{ +struct kvm_sev_info *sev = &to_kvm_svm (kvm)->sev_info;
arch/x86/kvm/svm.c static int svm_get_page_enc_bitmap(struct kvm *kvm, · line 7772
Quoted 10 lines
arch/x86/kvm/svm.c +10
-0+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;
Same comment as the previous one. Do we continue if num_pages is zero ?
Quoted 66 lines
arch/x86/kvm/svm.c +28
-0+ + sz =ALIGN (bmap->num_pages,BITS_PER_LONG ) /8 ; + bitmap =kmalloc (sz,GFP_KERNEL ); +if (!bitmap) +return -ENOMEM ; + + ret = -EFAULT ; +if (copy_from_user (bitmap, bmap->enc_bitmap, sz)) + goto out; + +mutex_lock (&kvm->lock); + ret =sev_resize_page_enc_bitmap (kvm, gfn_end); +if (ret) + goto unlock; + + i = gfn_start; +for_each_clear_bit_from (i, bitmap, (gfn_end - gfn_start)) +clear_bit (i + gfn_start, sev->page_enc_bmap); + + ret =0 ; +unlock: +mutex_unlock (&kvm->lock); +out: +kfree (bitmap); +return ret; +} +static int svm_mem_enc_op (struct kvm *kvm,void __user *argp) {struct kvm_sev_cmd sev_cmd;@@ -8161,6 +8202,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, };static int __init svm_init (void )arch/x86/kvm/x86.c +12
-0@@ -5238,6 +5238,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 +0
-0
include/uapi/linux/kvm.h struct kvm_enc_region { · line 1495
Quoted 7 lines
include/uapi/linux/kvm.h +1
-0@@ -1489,6 +1489,7 @@ struct kvm_enc_region {#define KVM_S390_CLEAR_RESET _IO(KVMIO, 0xc4)#define KVM_GET_PAGE_ENC_BITMAP _IOW(KVMIO, 0xc5, struct kvm_page_enc_bitmap) +#define KVM_SET_PAGE_ENC_BITMAP _IOW(KVMIO, 0xc6, struct kvm_page_enc_bitmap)/* Secure Encrypted Virtualization command */ enum sev_cmd_id {
Re: [PATCH v6 11/14] KVM: x86: Introduce KVM_SET_PAGE_ENC_BITMAP ioctl
In reply to Ashish KalraQuoted 5 lines · Ashish Kalra
From: Brijesh Singh <[email protected]>
The ioctl can be used to set page encryption bitmap for an incoming guest.
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]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[email protected]>
Quoted 152 lines
--- Documentation/virt/kvm/api.rst | 22 +++++++++++++++++ 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, 79 insertions(+)Documentation/virt/kvm/api.rst +22
-0@@ -4675,6 +4675,28 @@ 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.126 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@@ -1271,6 +1271,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@@ -7756,6 +7756,47 @@ static int svm_get_page_enc_bitmap(struct kvm *kvm,return ret; } +static int svm_set_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 ; + + ret = -EFAULT ; +if (copy_from_user (bitmap, bmap->enc_bitmap, sz)) + goto out; + +mutex_lock (&kvm->lock); + ret =sev_resize_page_enc_bitmap (kvm, gfn_end); +if (ret) + goto unlock; + + i = gfn_start; +for_each_clear_bit_from (i, bitmap, (gfn_end - gfn_start)) +clear_bit (i + gfn_start, sev->page_enc_bmap); + + ret =0 ; +unlock: +mutex_unlock (&kvm->lock); +out: +kfree (bitmap); +return ret; +} +static int svm_mem_enc_op (struct kvm *kvm,void __user *argp) {struct kvm_sev_cmd sev_cmd;@@ -8161,6 +8202,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, };static int __init svm_init (void )arch/x86/kvm/x86.c +12
-0@@ -5238,6 +5238,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@@ -1489,6 +1489,7 @@ struct kvm_enc_region {#define KVM_S390_CLEAR_RESET _IO(KVMIO, 0xc4)#define KVM_GET_PAGE_ENC_BITMAP _IOW(KVMIO, 0xc5, struct kvm_page_enc_bitmap) +#define KVM_SET_PAGE_ENC_BITMAP _IOW(KVMIO, 0xc6, struct kvm_page_enc_bitmap)/* Secure Encrypted Virtualization command */ enum sev_cmd_id {Signature · 1 line
2.17.1
Re: [PATCH v6 11/14] KVM: x86: Introduce KVM_SET_PAGE_ENC_BITMAP ioctl
In reply to Ashish KalraQuoted 97 lines · 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]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[email protected]>--- Documentation/virt/kvm/api.rst | 22 +++++++++++++++++ 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, 79 insertions(+)Documentation/virt/kvm/api.rst +22
-0@@ -4675,6 +4675,28 @@ 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.126 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@@ -1271,6 +1271,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 +16
-0@@ -7756,6 +7756,47 @@ static int svm_get_page_enc_bitmap(struct kvm *kvm,return ret; } +static int svm_set_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 ;
arch/x86/kvm/svm.c static int svm_get_page_enc_bitmap(struct kvm *kvm, · line 7784
Quoted 10 lines
arch/x86/kvm/svm.c +10
-0+ bitmap =kmalloc (sz,GFP_KERNEL ); +if (!bitmap) +return -ENOMEM ; + + ret = -EFAULT ; +if (copy_from_user (bitmap, bmap->enc_bitmap, sz)) + goto out; + +mutex_lock (&kvm->lock); + ret =sev_resize_page_enc_bitmap (kvm, gfn_end);
I realize now that usermode could use this for initializing the minimum size of the enc bitmap, which probably solves my issue from the other thread.
arch/x86/kvm/svm.c static int svm_get_page_enc_bitmap(struct kvm *kvm, · line 7790
Quoted 6 lines
arch/x86/kvm/svm.c +6
-0+if (ret) + goto unlock; + + i = gfn_start; +for_each_clear_bit_from (i, bitmap, (gfn_end - gfn_start)) +clear_bit (i + gfn_start, sev->page_enc_bmap);
This API seems a bit strange, since it can only clear bits. I would expect "set" to force the values to match the values passed down, instead of only ensuring that cleared bits in the input are also cleared in the kernel.
This should copy the values from userspace (and fix up the ends since byte alignment makes that complicated), instead of iterating in this way.
Quoted 58 lines
arch/x86/kvm/svm.c +10
-0+ + ret =0 ; +unlock: +mutex_unlock (&kvm->lock); +out: +kfree (bitmap); +return ret; +} +static int svm_mem_enc_op (struct kvm *kvm,void __user *argp) {struct kvm_sev_cmd sev_cmd;@@ -8161,6 +8202,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, };static int __init svm_init (void )arch/x86/kvm/x86.c +12
-0@@ -5238,6 +5238,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
-1@@ -1489,6 +1489,7 @@ struct kvm_enc_region {#define KVM_S390_CLEAR_RESET _IO(KVMIO, 0xc4)#define KVM_GET_PAGE_ENC_BITMAP _IOW(KVMIO, 0xc5, struct kvm_page_enc_bitmap) +#define KVM_SET_PAGE_ENC_BITMAP _IOW(KVMIO, 0xc6, struct kvm_page_enc_bitmap)/* Secure Encrypted Virtualization command */ enum sev_cmd_id { --2.17.1
Re: [PATCH v6 11/14] KVM: x86: Introduce KVM_SET_PAGE_ENC_BITMAP ioctl
Hello Steve,
Quoted 117 lines · Steve Rutherford
Quoted 97 lines · 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]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[email protected]>--- Documentation/virt/kvm/api.rst | 22 +++++++++++++++++ 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, 79 insertions(+)Documentation/virt/kvm/api.rst +22
-0@@ -4675,6 +4675,28 @@ 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.126 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@@ -1271,6 +1271,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 +16
-0@@ -7756,6 +7756,47 @@ static int svm_get_page_enc_bitmap(struct kvm *kvm,return ret; } +static int svm_set_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 ;arch/x86/kvm/svm.c static int svm_get_page_enc_bitmap(struct kvm *kvm, · line 7784
Quoted 10 lines
arch/x86/kvm/svm.c +10
-0+ bitmap =kmalloc (sz,GFP_KERNEL ); +if (!bitmap) +return -ENOMEM ; + + ret = -EFAULT ; +if (copy_from_user (bitmap, bmap->enc_bitmap, sz)) + goto out; + +mutex_lock (&kvm->lock); + ret =sev_resize_page_enc_bitmap (kvm, gfn_end);I realize now that usermode could use this for initializing the minimum size of the enc bitmap, which probably solves my issue from the other thread.
arch/x86/kvm/svm.c static int svm_get_page_enc_bitmap(struct kvm *kvm, · line 7790
Quoted 6 lines
arch/x86/kvm/svm.c +6
-0+if (ret) + goto unlock; + + i = gfn_start; +for_each_clear_bit_from (i, bitmap, (gfn_end - gfn_start)) +clear_bit (i + gfn_start, sev->page_enc_bmap);
Quoted 5 lines
This API seems a bit strange, since it can only clear bits. I would expect "set" to force the values to match the values passed down, instead of only ensuring that cleared bits in the input are also cleared in the kernel.
The sev_resize_page_enc_bitmap() will allocate a new bitmap and set it to all 0xFF's, therefore, the code here simply clears the bits in the bitmap as per the cleared bits in the input.
Thanks,
Ashish
Quoted 61 lines
This should copy the values from userspace (and fix up the ends since byte alignment makes that complicated), instead of iterating in this way.
Quoted 58 lines
arch/x86/kvm/svm.c +10
-0+ + ret =0 ; +unlock: +mutex_unlock (&kvm->lock); +out: +kfree (bitmap); +return ret; +} +static int svm_mem_enc_op (struct kvm *kvm,void __user *argp) {struct kvm_sev_cmd sev_cmd;@@ -8161,6 +8202,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, };static int __init svm_init (void )arch/x86/kvm/x86.c +12
-0@@ -5238,6 +5238,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
-1@@ -1489,6 +1489,7 @@ struct kvm_enc_region {#define KVM_S390_CLEAR_RESET _IO(KVMIO, 0xc4)#define KVM_GET_PAGE_ENC_BITMAP _IOW(KVMIO, 0xc5, struct kvm_page_enc_bitmap) +#define KVM_SET_PAGE_ENC_BITMAP _IOW(KVMIO, 0xc6, struct kvm_page_enc_bitmap)/* Secure Encrypted Virtualization command */ enum sev_cmd_id { --2.17.1
Re: [PATCH v6 11/14] KVM: x86: Introduce KVM_SET_PAGE_ENC_BITMAP ioctl
Quoted 127 lines · Ashish Kalra
Hello Steve,
Quoted 117 lines · Steve Rutherford
Quoted 97 lines · 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]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[email protected]>--- Documentation/virt/kvm/api.rst | 22 +++++++++++++++++ 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, 79 insertions(+)Documentation/virt/kvm/api.rst +22
-0@@ -4675,6 +4675,28 @@ 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.126 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@@ -1271,6 +1271,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 +16
-0@@ -7756,6 +7756,47 @@ static int svm_get_page_enc_bitmap(struct kvm *kvm,return ret; } +static int svm_set_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 ;arch/x86/kvm/svm.c static int svm_get_page_enc_bitmap(struct kvm *kvm, · line 7784
Quoted 10 lines
arch/x86/kvm/svm.c +10
-0+ bitmap =kmalloc (sz,GFP_KERNEL ); +if (!bitmap) +return -ENOMEM ; + + ret = -EFAULT ; +if (copy_from_user (bitmap, bmap->enc_bitmap, sz)) + goto out; + +mutex_lock (&kvm->lock); + ret =sev_resize_page_enc_bitmap (kvm, gfn_end);I realize now that usermode could use this for initializing the minimum size of the enc bitmap, which probably solves my issue from the other thread.
arch/x86/kvm/svm.c static int svm_get_page_enc_bitmap(struct kvm *kvm, · line 7790
Quoted 6 lines
arch/x86/kvm/svm.c +6
-0+if (ret) + goto unlock; + + i = gfn_start; +for_each_clear_bit_from (i, bitmap, (gfn_end - gfn_start)) +clear_bit (i + gfn_start, sev->page_enc_bmap);Quoted 5 lines
This API seems a bit strange, since it can only clear bits. I would expect "set" to force the values to match the values passed down, instead of only ensuring that cleared bits in the input are also cleared in the kernel.
Quoted 3 lines
The
sev_resize_page_enc_bitmap()will allocate a new bitmap and set it to all 0xFF's, therefore, the code here simply clears the bits in the bitmap as per the cleared bits in the input.
If I'm not mistaken, resize only reinitializes the newly extended part of the buffer, and copies the old values for the rest.
With the API you proposed you could probably reimplement a normal set call by calling get, then reset, and then set, but this feels cumbersome.
--Steve
Re: [PATCH v6 11/14] KVM: x86: Introduce KVM_SET_PAGE_ENC_BITMAP ioctl
Hello Steve,
Quoted 132 lines · Steve Rutherford
Quoted 127 lines · Ashish Kalra
Hello Steve,
Quoted 117 lines · Steve Rutherford
Quoted 97 lines · 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]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[email protected]>--- Documentation/virt/kvm/api.rst | 22 +++++++++++++++++ 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, 79 insertions(+)Documentation/virt/kvm/api.rst +22
-0@@ -4675,6 +4675,28 @@ 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.126 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@@ -1271,6 +1271,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 +16
-0@@ -7756,6 +7756,47 @@ static int svm_get_page_enc_bitmap(struct kvm *kvm,return ret; } +static int svm_set_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 ;arch/x86/kvm/svm.c static int svm_get_page_enc_bitmap(struct kvm *kvm, · line 7784
Quoted 10 lines
arch/x86/kvm/svm.c +10
-0+ bitmap =kmalloc (sz,GFP_KERNEL ); +if (!bitmap) +return -ENOMEM ; + + ret = -EFAULT ; +if (copy_from_user (bitmap, bmap->enc_bitmap, sz)) + goto out; + +mutex_lock (&kvm->lock); + ret =sev_resize_page_enc_bitmap (kvm, gfn_end);I realize now that usermode could use this for initializing the minimum size of the enc bitmap, which probably solves my issue from the other thread.
arch/x86/kvm/svm.c static int svm_get_page_enc_bitmap(struct kvm *kvm, · line 7790
Quoted 6 lines
arch/x86/kvm/svm.c +6
-0+if (ret) + goto unlock; + + i = gfn_start; +for_each_clear_bit_from (i, bitmap, (gfn_end - gfn_start)) +clear_bit (i + gfn_start, sev->page_enc_bmap);Quoted 5 lines
This API seems a bit strange, since it can only clear bits. I would expect "set" to force the values to match the values passed down, instead of only ensuring that cleared bits in the input are also cleared in the kernel.
Quoted 3 lines
The
sev_resize_page_enc_bitmap()will allocate a new bitmap and set it to all 0xFF's, therefore, the code here simply clears the bits in the bitmap as per the cleared bits in the input.
Quoted 6 lines
If I'm not mistaken, resize only reinitializes the newly extended part of the buffer, and copies the old values for the rest.
With the API you proposed you could probably reimplement a normal set call by calling get, then reset, and then set, but this feels cumbersome.
As i mentioned earlier, the set api is basically meant for the incoming VM, the resize will initialize the incoming VM's bitmap to all 0xFF's and as there won't be any bitmap allocated initially on the incoming VM, therefore, the bitmap copy will not do anything and the clear_bit later will clear the incoming VM's bits as per the input.
Thanks,
Ashish
Re: [PATCH v6 11/14] KVM: x86: Introduce KVM_SET_PAGE_ENC_BITMAP ioctl
Quoted 143 lines · Ashish Kalra
Hello Steve,
Quoted 132 lines · Steve Rutherford
Quoted 127 lines · Ashish Kalra
Hello Steve,
Quoted 117 lines · Steve Rutherford
Quoted 97 lines · 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]>Signed-off-by Brijesh Singh <[email protected]>Signed-off-by Ashish Kalra <[email protected]>--- Documentation/virt/kvm/api.rst | 22 +++++++++++++++++ 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, 79 insertions(+)Documentation/virt/kvm/api.rst +22
-0@@ -4675,6 +4675,28 @@ 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.126 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@@ -1271,6 +1271,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 +16
-0@@ -7756,6 +7756,47 @@ static int svm_get_page_enc_bitmap(struct kvm *kvm,return ret; } +static int svm_set_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 ;arch/x86/kvm/svm.c static int svm_get_page_enc_bitmap(struct kvm *kvm, · line 7784
Quoted 10 lines
arch/x86/kvm/svm.c +10
-0+ bitmap =kmalloc (sz,GFP_KERNEL ); +if (!bitmap) +return -ENOMEM ; + + ret = -EFAULT ; +if (copy_from_user (bitmap, bmap->enc_bitmap, sz)) + goto out; + +mutex_lock (&kvm->lock); + ret =sev_resize_page_enc_bitmap (kvm, gfn_end);I realize now that usermode could use this for initializing the minimum size of the enc bitmap, which probably solves my issue from the other thread.
arch/x86/kvm/svm.c static int svm_get_page_enc_bitmap(struct kvm *kvm, · line 7790
Quoted 6 lines
arch/x86/kvm/svm.c +6
-0+if (ret) + goto unlock; + + i = gfn_start; +for_each_clear_bit_from (i, bitmap, (gfn_end - gfn_start)) +clear_bit (i + gfn_start, sev->page_enc_bmap);Quoted 5 lines
This API seems a bit strange, since it can only clear bits. I would expect "set" to force the values to match the values passed down, instead of only ensuring that cleared bits in the input are also cleared in the kernel.
Quoted 3 lines
The
sev_resize_page_enc_bitmap()will allocate a new bitmap and set it to all 0xFF's, therefore, the code here simply clears the bits in the bitmap as per the cleared bits in the input.Quoted 6 lines
If I'm not mistaken, resize only reinitializes the newly extended part of the buffer, and copies the old values for the rest.
With the API you proposed you could probably reimplement a normal set call by calling get, then reset, and then set, but this feels cumbersome.
Quoted 5 lines
As i mentioned earlier, the set api is basically meant for the incoming VM, the resize will initialize the incoming VM's bitmap to all 0xFF's and as there won't be any bitmap allocated initially on the incoming VM, therefore, the bitmap copy will not do anything and the
clear_bitlater will clear the incoming VM's bits as per the input.
The documentation does not make that super clear. A typical set call in the KVM API let's you go to any state, not just a subset of states. Yes, this works in the common case of migrating a VM to a particular target, once. I find the behavior of the current API surprising. I prefer APIs that are unsurprising. If I were to not have read the code, it would be very easy for me to have assumed it worked like a normal set call. You could rename the ioctl something like "CLEAR_BITS", but a set based API is more common.
Thanks,
Steve
PATCH v6 12/14 KVM: x86: Introduce KVM_PAGE_ENC_BITMAP_RESET ioctl
In reply to Ashish KalraFrom: 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.rst | 13 +++++++++++++ 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, 37 insertions(+)
Documentation/virt/kvm/api.rst +13 -0
@@ -4698,6 +4698,19 @@ 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.127 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
@@ -1273,6 +1273,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
@@ -7797,6 +7797,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;
@@ -8203,6 +8218,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
@@ -5250,6 +5250,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
@@ -1490,6 +1490,7 @@ struct kvm_enc_region {#define KVM_GET_PAGE_ENC_BITMAP _IOW(KVMIO, 0xc5, struct kvm_page_enc_bitmap)#define KVM_SET_PAGE_ENC_BITMAP _IOW(KVMIO, 0xc6, struct kvm_page_enc_bitmap) +#define KVM_PAGE_ENC_BITMAP_RESET _IO(KVMIO, 0xc7)/* Secure Encrypted Virtualization command */ enum sev_cmd_id {
Signature · 1 line
2.17.1
Re: [PATCH v6 12/14] KVM: x86: Introduce KVM_PAGE_ENC_BITMAP_RESET ioctl
Quoted 79 lines · 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.
Signed-off-by Ashish Kalra <[email protected]>--- Documentation/virt/kvm/api.rst | 13 +++++++++++++ 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, 37 insertions(+)Documentation/virt/kvm/api.rst +13
-0@@ -4698,6 +4698,19 @@ 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.127 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@@ -1273,6 +1273,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 +15
-0@@ -7797,6 +7797,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;
arch/x86/kvm/svm.c static struct kvm_x86_ops svm_x86_ops __ro_after_init = { · line 8221
Quoted 4 lines
arch/x86/kvm/svm.c +1
-0@@ -8203,6 +8218,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,
We don't need to initialize the intel ops to NULL ? It's not initialized in the previous patch either.
Quoted 25 lines
arch/x86/kvm/svm.c +0
-0};static int __init svm_init (void )arch/x86/kvm/x86.c +6
-0@@ -5250,6 +5250,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 +0
-0
include/uapi/linux/kvm.h struct kvm_enc_region { · line 1496
Quoted 7 lines
include/uapi/linux/kvm.h +1
-0@@ -1490,6 +1490,7 @@ struct kvm_enc_region {#define KVM_GET_PAGE_ENC_BITMAP _IOW(KVMIO, 0xc5, struct kvm_page_enc_bitmap)#define KVM_SET_PAGE_ENC_BITMAP _IOW(KVMIO, 0xc6, struct kvm_page_enc_bitmap) +#define KVM_PAGE_ENC_BITMAP_RESET _IO(KVMIO, 0xc7)/* Secure Encrypted Virtualization command */ enum sev_cmd_id {