Re: [PATCH] KVM/SVM: add support for SEV attestation command

From: Tom Lendacky
Date: Tue Dec 08 2020 - 11:44:40 EST


On 12/4/20 3:28 PM, Brijesh Singh wrote:
> The SEV FW version >= 0.23 added a new command that can be used to query
> the attestation report containing the SHA-256 digest of the guest memory
> encrypted through the KVM_SEV_LAUNCH_UPDATE_{DATA, VMSA} commands and
> sign the report with the Platform Endorsement Key (PEK).
>
> See the SEV FW API spec section 6.8 for more details.
>
> Note there already exist a command (KVM_SEV_LAUNCH_MEASURE) that can be
> used to get the SHA-256 digest. The main difference between the
> KVM_SEV_LAUNCH_MEASURE and KVM_SEV_ATTESTATION_REPORT is that the later
> can be called while the guest is running and the measurement value is
> signed with PEK.
>
> Cc: James Bottomley <jejb@xxxxxxxxxxxxx>
> Cc: Tom Lendacky <Thomas.Lendacky@xxxxxxx>
> Cc: David Rientjes <rientjes@xxxxxxxxxx>
> Cc: Paolo Bonzini <pbonzini@xxxxxxxxxx>
> Cc: Sean Christopherson <seanjc@xxxxxxxxxx>
> Cc: Borislav Petkov <bp@xxxxxxxxx>
> Cc: John Allen <john.allen@xxxxxxx>
> Cc: Herbert Xu <herbert@xxxxxxxxxxxxxxxxxxx>
> Cc: linux-crypto@xxxxxxxxxxxxxxx
> Signed-off-by: Brijesh Singh <brijesh.singh@xxxxxxx>

Reviewed-by: Tom Lendacky <thomas.lendacky@xxxxxxx>

Not sure if Paolo or Herbert would like the crypto/psp changes to be split
out from the kvm changes as separate patches or not.

Thanks,
Tom

> ---
> .../virt/kvm/amd-memory-encryption.rst | 21 ++++++
> arch/x86/kvm/svm/sev.c | 71 +++++++++++++++++++
> drivers/crypto/ccp/sev-dev.c | 1 +
> include/linux/psp-sev.h | 17 +++++
> include/uapi/linux/kvm.h | 8 +++
> 5 files changed, 118 insertions(+)
>
> diff --git a/Documentation/virt/kvm/amd-memory-encryption.rst b/Documentation/virt/kvm/amd-memory-encryption.rst
> index 09a8f2a34e39..4c6685d0fddd 100644
> --- a/Documentation/virt/kvm/amd-memory-encryption.rst
> +++ b/Documentation/virt/kvm/amd-memory-encryption.rst
> @@ -263,6 +263,27 @@ Returns: 0 on success, -negative on error
> __u32 trans_len;
> };
>
> +10. KVM_SEV_GET_ATTESATION_REPORT
> +---------------------------------
> +
> +The KVM_SEV_GET_ATTESATION_REPORT command can be used by the hypervisor to query the attestation
> +report containing the SHA-256 digest of the guest memory and VMSA passed through the KVM_SEV_LAUNCH
> +commands and signed with the PEK. The digest returned by the command should match the digest
> +used by the guest owner with the KVM_SEV_LAUNCH_MEASURE.
> +
> +Parameters (in): struct kvm_sev_attestation
> +
> +Returns: 0 on success, -negative on error
> +
> +::
> +
> + struct kvm_sev_attestation_report {
> + __u8 mnonce[16]; /* A random mnonce that will be placed in the report */
> +
> + __u64 uaddr; /* userspace address where the report should be copied */
> + __u32 len;
> + };
> +
> References
> ==========
>
> diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
> index 566f4d18185b..c4d3ee6be362 100644
> --- a/arch/x86/kvm/svm/sev.c
> +++ b/arch/x86/kvm/svm/sev.c
> @@ -927,6 +927,74 @@ static int sev_launch_secret(struct kvm *kvm, struct kvm_sev_cmd *argp)
> return ret;
> }
>
> +static int sev_get_attestation_report(struct kvm *kvm, struct kvm_sev_cmd *argp)
> +{
> + void __user *report = (void __user *)(uintptr_t)argp->data;
> + struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
> + struct sev_data_attestation_report *data;
> + struct kvm_sev_attestation_report params;
> + void __user *p;
> + void *blob = NULL;
> + int ret;
> +
> + if (!sev_guest(kvm))
> + return -ENOTTY;
> +
> + if (copy_from_user(&params, (void __user *)(uintptr_t)argp->data, sizeof(params)))
> + return -EFAULT;
> +
> + data = kzalloc(sizeof(*data), GFP_KERNEL_ACCOUNT);
> + if (!data)
> + return -ENOMEM;
> +
> + /* User wants to query the blob length */
> + if (!params.len)
> + goto cmd;
> +
> + p = (void __user *)(uintptr_t)params.uaddr;
> + if (p) {
> + if (params.len > SEV_FW_BLOB_MAX_SIZE) {
> + ret = -EINVAL;
> + goto e_free;
> + }
> +
> + ret = -ENOMEM;
> + blob = kmalloc(params.len, GFP_KERNEL);
> + if (!blob)
> + goto e_free;
> +
> + data->address = __psp_pa(blob);
> + data->len = params.len;
> + memcpy(data->mnonce, params.mnonce, sizeof(params.mnonce));
> + }
> +cmd:
> + data->handle = sev->handle;
> + ret = sev_issue_cmd(kvm, SEV_CMD_ATTESTATION_REPORT, data, &argp->error);
> + /*
> + * If we query the session length, FW responded with expected data.
> + */
> + if (!params.len)
> + goto done;
> +
> + if (ret)
> + goto e_free_blob;
> +
> + if (blob) {
> + if (copy_to_user(p, blob, params.len))
> + ret = -EFAULT;
> + }
> +
> +done:
> + params.len = data->len;
> + if (copy_to_user(report, &params, sizeof(params)))
> + ret = -EFAULT;
> +e_free_blob:
> + kfree(blob);
> +e_free:
> + kfree(data);
> + return ret;
> +}
> +
> int svm_mem_enc_op(struct kvm *kvm, void __user *argp)
> {
> struct kvm_sev_cmd sev_cmd;
> @@ -971,6 +1039,9 @@ 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_GET_ATTESTATION_REPORT:
> + r = sev_get_attestation_report(kvm, &sev_cmd);
> + break;
> default:
> r = -EINVAL;
> goto out;
> diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c
> index 476113e12489..cb9b4c4e371e 100644
> --- a/drivers/crypto/ccp/sev-dev.c
> +++ b/drivers/crypto/ccp/sev-dev.c
> @@ -128,6 +128,7 @@ static int sev_cmd_buffer_len(int cmd)
> case SEV_CMD_LAUNCH_UPDATE_SECRET: return sizeof(struct sev_data_launch_secret);
> case SEV_CMD_DOWNLOAD_FIRMWARE: return sizeof(struct sev_data_download_firmware);
> case SEV_CMD_GET_ID: return sizeof(struct sev_data_get_id);
> + case SEV_CMD_ATTESTATION_REPORT: return sizeof(struct sev_data_attestation_report);
> default: return 0;
> }
>
> diff --git a/include/linux/psp-sev.h b/include/linux/psp-sev.h
> index 49d155cd2dfe..b801ead1e2bb 100644
> --- a/include/linux/psp-sev.h
> +++ b/include/linux/psp-sev.h
> @@ -66,6 +66,7 @@ enum sev_cmd {
> SEV_CMD_LAUNCH_MEASURE = 0x033,
> SEV_CMD_LAUNCH_UPDATE_SECRET = 0x034,
> SEV_CMD_LAUNCH_FINISH = 0x035,
> + SEV_CMD_ATTESTATION_REPORT = 0x036,
>
> /* Guest migration commands (outgoing) */
> SEV_CMD_SEND_START = 0x040,
> @@ -483,6 +484,22 @@ struct sev_data_dbg {
> u32 len; /* In */
> } __packed;
>
> +/**
> + * struct sev_data_attestation_report - SEV_ATTESTATION_REPORT command parameters
> + *
> + * @handle: handle of the VM
> + * @mnonce: a random nonce that will be included in the report.
> + * @address: physical address where the report will be copied.
> + * @len: length of the physical buffer.
> + */
> +struct sev_data_attestation_report {
> + u32 handle; /* In */
> + u32 reserved;
> + u64 address; /* In */
> + u8 mnonce[16]; /* In */
> + u32 len; /* In/Out */
> +} __packed;
> +
> #ifdef CONFIG_CRYPTO_DEV_SP_PSP
>
> /**
> diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
> index ca41220b40b8..d3385f7f08a2 100644
> --- a/include/uapi/linux/kvm.h
> +++ b/include/uapi/linux/kvm.h
> @@ -1585,6 +1585,8 @@ enum sev_cmd_id {
> KVM_SEV_DBG_ENCRYPT,
> /* Guest certificates commands */
> KVM_SEV_CERT_EXPORT,
> + /* Attestation report */
> + KVM_SEV_GET_ATTESTATION_REPORT,
>
> KVM_SEV_NR_MAX,
> };
> @@ -1637,6 +1639,12 @@ struct kvm_sev_dbg {
> __u32 len;
> };
>
> +struct kvm_sev_attestation_report {
> + __u8 mnonce[16];
> + __u64 uaddr;
> + __u32 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)
>