.../virt/kvm/x86/amd-memory-encryption.rst | 18 ++++++ arch/x86/include/uapi/asm/kvm.h | 7 +++ arch/x86/kvm/svm/sev.c | 60 +++++++++++++++++++ drivers/crypto/ccp/sev-dev.c | 1 + include/linux/psp-sev.h | 28 +++++++++ 5 files changed, 114 insertions(+)
Add support for retrieving the SEV-SNP attestation report via the
SNP_HV_REPORT_REQ firmware command and expose it through a new KVM
ioctl for SNP guests.
Signed-off-by: Thomas Courrege <thomas.courrege@vates.tech>
---
.../virt/kvm/x86/amd-memory-encryption.rst | 18 ++++++
arch/x86/include/uapi/asm/kvm.h | 7 +++
arch/x86/kvm/svm/sev.c | 60 +++++++++++++++++++
drivers/crypto/ccp/sev-dev.c | 1 +
include/linux/psp-sev.h | 28 +++++++++
5 files changed, 114 insertions(+)
diff --git a/Documentation/virt/kvm/x86/amd-memory-encryption.rst b/Documentation/virt/kvm/x86/amd-memory-encryption.rst
index 1ddb6a86ce7f..f473e9304634 100644
--- a/Documentation/virt/kvm/x86/amd-memory-encryption.rst
+++ b/Documentation/virt/kvm/x86/amd-memory-encryption.rst
@@ -572,6 +572,24 @@ Returns: 0 on success, -negative on error
See SNP_LAUNCH_FINISH in the SEV-SNP specification [snp-fw-abi]_ for further
details on the input parameters in ``struct kvm_sev_snp_launch_finish``.
+21. KVM_SEV_SNP_GET_HV_REPORT
+-----------------------------
+
+The KVM_SEV_SNP_GET_HV_REPORT command requests the hypervisor-generated
+SNP attestation report. This report is produced by the PSP using the
+HV-SIGNED key selected by the caller.
+
+Parameters (in): struct kvm_sev_snp_hv_report_req
+
+Returns: 0 on success, -negative on error
+
+::
+ struct kvm_sev_snp_hv_report_req {
+ __u8 key_sel;
+ __u64 report_uaddr;
+ __u64 report_len;
+ };
+
Device attribute API
====================
diff --git a/arch/x86/include/uapi/asm/kvm.h b/arch/x86/include/uapi/asm/kvm.h
index d420c9c066d4..ff034668cac4 100644
--- a/arch/x86/include/uapi/asm/kvm.h
+++ b/arch/x86/include/uapi/asm/kvm.h
@@ -742,6 +742,7 @@ enum sev_cmd_id {
KVM_SEV_SNP_LAUNCH_START = 100,
KVM_SEV_SNP_LAUNCH_UPDATE,
KVM_SEV_SNP_LAUNCH_FINISH,
+ KVM_SEV_SNP_HV_REPORT_REQ,
KVM_SEV_NR_MAX,
};
@@ -870,6 +871,12 @@ struct kvm_sev_receive_update_data {
__u32 pad2;
};
+struct kvm_sev_snp_hv_report_req {
+ __u8 key_sel;
+ __u64 report_uaddr;
+ __u64 report_len;
+};
+
struct kvm_sev_snp_launch_start {
__u64 policy;
__u8 gosvw[16];
diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index 0835c664fbfd..4ab572d970a4 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -2253,6 +2253,63 @@ static int snp_launch_start(struct kvm *kvm, struct kvm_sev_cmd *argp)
return rc;
}
+static int sev_snp_report_request(struct kvm *kvm, struct kvm_sev_cmd *argp)
+{
+ struct kvm_sev_info *sev = to_kvm_sev_info(kvm);
+ struct sev_data_snp_hv_report_req data;
+ struct kvm_sev_snp_hv_report_req params;
+ void __user *u_report;
+ void __user *u_params = u64_to_user_ptr(argp->data);
+ struct sev_data_snp_msg_report_rsp *report_rsp = NULL;
+ int ret;
+
+ if (!sev_snp_guest(kvm))
+ return -ENOTTY;
+
+ if (copy_from_user(¶ms, u_params, sizeof(params)))
+ return -EFAULT;
+
+ /* A report uses 1184 bytes */
+ if (params.report_len < 1184)
+ return -ENOSPC;
+
+ memset(&data, 0, sizeof(data));
+
+ u_report = u64_to_user_ptr(params.report_uaddr);
+ if (!u_report)
+ return -EINVAL;
+
+ report_rsp = snp_alloc_firmware_page(GFP_KERNEL_ACCOUNT | __GFP_ZERO);
+ if (!report_rsp)
+ return -ENOMEM;
+
+ data.len = sizeof(data);
+ data.hv_report_paddr = __psp_pa(report_rsp);
+ data.key_sel = params.key_sel;
+
+ data.gctx_addr = __psp_pa(sev->snp_context);
+ ret = sev_issue_cmd(kvm, SEV_CMD_SNP_HV_REPORT_REQ, &data,
+ &argp->error);
+
+ if (ret)
+ goto e_free_rsp;
+
+ params.report_len = report_rsp->report_size;
+ if (copy_to_user(u_params, ¶ms, sizeof(params)))
+ ret = -EFAULT;
+
+ if (params.report_len < report_rsp->report_size) {
+ ret = -ENOSPC;
+ /* report is located right after rsp */
+ } else if (copy_to_user(u_report, report_rsp + 1, report_rsp->report_size)) {
+ ret = -EFAULT;
+ }
+
+e_free_rsp:
+ snp_free_firmware_page(report_rsp);
+ return ret;
+}
+
struct sev_gmem_populate_args {
__u8 type;
int sev_fd;
@@ -2664,6 +2721,9 @@ int sev_mem_enc_ioctl(struct kvm *kvm, void __user *argp)
case KVM_SEV_SNP_LAUNCH_FINISH:
r = snp_launch_finish(kvm, &sev_cmd);
break;
+ case KVM_SEV_SNP_HV_REPORT_REQ:
+ r = sev_snp_report_request(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 0d13d47c164b..5236d5ee19ac 100644
--- a/drivers/crypto/ccp/sev-dev.c
+++ b/drivers/crypto/ccp/sev-dev.c
@@ -251,6 +251,7 @@ static int sev_cmd_buffer_len(int cmd)
case SEV_CMD_SNP_COMMIT: return sizeof(struct sev_data_snp_commit);
case SEV_CMD_SNP_FEATURE_INFO: return sizeof(struct sev_data_snp_feature_info);
case SEV_CMD_SNP_VLEK_LOAD: return sizeof(struct sev_user_data_snp_vlek_load);
+ case SEV_CMD_SNP_HV_REPORT_REQ: return sizeof(struct sev_data_snp_hv_report_req);
default: return 0;
}
diff --git a/include/linux/psp-sev.h b/include/linux/psp-sev.h
index e0dbcb4b4fd9..c382edc8713a 100644
--- a/include/linux/psp-sev.h
+++ b/include/linux/psp-sev.h
@@ -91,6 +91,7 @@ enum sev_cmd {
SEV_CMD_SNP_GCTX_CREATE = 0x093,
SEV_CMD_SNP_GUEST_REQUEST = 0x094,
SEV_CMD_SNP_ACTIVATE_EX = 0x095,
+ SEV_CMD_SNP_HV_REPORT_REQ = 0x096,
SEV_CMD_SNP_LAUNCH_START = 0x0A0,
SEV_CMD_SNP_LAUNCH_UPDATE = 0x0A1,
SEV_CMD_SNP_LAUNCH_FINISH = 0x0A2,
@@ -554,6 +555,33 @@ struct sev_data_attestation_report {
u32 len; /* In/Out */
} __packed;
+/**
+ * struct sev_data_snp_hv_report_req - SNP_HV_REPORT_REQ command params
+ *
+ * @len: length of the command buffer in bytes
+ * @key_sel: Selects which key to use for generating the signature.
+ * @gctx_addr: System physical address of guest context page
+ * @hv_report_paddr: System physical address where MSG_EXPORT_RSP will be written
+ */
+struct sev_data_snp_hv_report_req {
+ u32 len; /* In */
+ u32 key_sel:2; /* In */
+ u32 rsvd:30;
+ u64 gctx_addr; /* In */
+ u64 hv_report_paddr; /* In */
+} __packed;
+/**
+ * struct sev_data_snp_msg_export_rsp
+ *
+ * @status: Status : 0h: Success. 16h: Invalid parameters.
+ * @report_size: Size in bytes of the attestation report
+ */
+struct sev_data_snp_msg_report_rsp {
+ u32 status; /* Out */
+ u32 report_size; /* Out */
+ u8 rsvd[24];
+} __packed;
+
/**
* struct sev_data_snp_download_firmware - SNP_DOWNLOAD_FIRMWARE command params
*
--
2.52.0
"Thomas Courrege" <thomas.courrege@vates.tech> writes:
> Add support for retrieving the SEV-SNP attestation report via the
> SNP_HV_REPORT_REQ firmware command and expose it through a new KVM
> ioctl for SNP guests.
>
> Signed-off-by: Thomas Courrege <thomas.courrege@vates.tech>
> ---
> .../virt/kvm/x86/amd-memory-encryption.rst | 18 ++++++
> arch/x86/include/uapi/asm/kvm.h | 7 +++
> arch/x86/kvm/svm/sev.c | 60 +++++++++++++++++++
> drivers/crypto/ccp/sev-dev.c | 1 +
> include/linux/psp-sev.h | 28 +++++++++
> 5 files changed, 114 insertions(+)
>
> diff --git a/Documentation/virt/kvm/x86/amd-memory-encryption.rst b/Documentation/virt/kvm/x86/amd-memory-encryption.rst
> index 1ddb6a86ce7f..f473e9304634 100644
> --- a/Documentation/virt/kvm/x86/amd-memory-encryption.rst
> +++ b/Documentation/virt/kvm/x86/amd-memory-encryption.rst
> @@ -572,6 +572,24 @@ Returns: 0 on success, -negative on error
> See SNP_LAUNCH_FINISH in the SEV-SNP specification [snp-fw-abi]_ for further
> details on the input parameters in ``struct kvm_sev_snp_launch_finish``.
>
> +21. KVM_SEV_SNP_GET_HV_REPORT
> +-----------------------------
The ioctl name is KVM_SEV_SNP_HV_REPORT_REQ in code but documented as
KVM_SEV_SNP_GET_HV_REPORT
> +
> +The KVM_SEV_SNP_GET_HV_REPORT command requests the hypervisor-generated
> +SNP attestation report. This report is produced by the PSP using the
> +HV-SIGNED key selected by the caller.
> +
> +Parameters (in): struct kvm_sev_snp_hv_report_req
> +
> +Returns: 0 on success, -negative on error
> +
> +::
> + struct kvm_sev_snp_hv_report_req {
> + __u8 key_sel;
> + __u64 report_uaddr;
> + __u64 report_len;
> + };
> +
Should we document the valid values of key_sel here?
> Device attribute API
> ====================
>
> diff --git a/arch/x86/include/uapi/asm/kvm.h b/arch/x86/include/uapi/asm/kvm.h
> index d420c9c066d4..ff034668cac4 100644
> --- a/arch/x86/include/uapi/asm/kvm.h
> +++ b/arch/x86/include/uapi/asm/kvm.h
> @@ -742,6 +742,7 @@ enum sev_cmd_id {
> KVM_SEV_SNP_LAUNCH_START = 100,
> KVM_SEV_SNP_LAUNCH_UPDATE,
> KVM_SEV_SNP_LAUNCH_FINISH,
> + KVM_SEV_SNP_HV_REPORT_REQ,
>
> KVM_SEV_NR_MAX,
> };
> @@ -870,6 +871,12 @@ struct kvm_sev_receive_update_data {
> __u32 pad2;
> };
>
> +struct kvm_sev_snp_hv_report_req {
> + __u8 key_sel;
> + __u64 report_uaddr;
> + __u64 report_len;
> +};
> +
> struct kvm_sev_snp_launch_start {
> __u64 policy;
> __u8 gosvw[16];
> diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
> index 0835c664fbfd..4ab572d970a4 100644
> --- a/arch/x86/kvm/svm/sev.c
> +++ b/arch/x86/kvm/svm/sev.c
> @@ -2253,6 +2253,63 @@ static int snp_launch_start(struct kvm *kvm, struct kvm_sev_cmd *argp)
> return rc;
> }
>
> +static int sev_snp_report_request(struct kvm *kvm, struct kvm_sev_cmd
> *argp)
s/sev_snp_report_request/sev_snp_hv_report_request/
> +{
> + struct kvm_sev_info *sev = to_kvm_sev_info(kvm);
> + struct sev_data_snp_hv_report_req data;
> + struct kvm_sev_snp_hv_report_req params;
> + void __user *u_report;
> + void __user *u_params = u64_to_user_ptr(argp->data);
> + struct sev_data_snp_msg_report_rsp *report_rsp = NULL;
> + int ret;
Variable declarations: Not in reverse Christmas tree order (preferred
but not mandatory per KVM x86 guidelines)
> +
> + if (!sev_snp_guest(kvm))
> + return -ENOTTY;
> +
> + if (copy_from_user(¶ms, u_params, sizeof(params)))
> + return -EFAULT;
> +
> + /* A report uses 1184 bytes */
> + if (params.report_len < 1184)
Can we use a define (ATTESTATION_REPORT_SIZE) for the magic number?
> + return -ENOSPC;
> +
> + memset(&data, 0, sizeof(data));
> +
> + u_report = u64_to_user_ptr(params.report_uaddr);
> + if (!u_report)
> + return -EINVAL;
> +
> + report_rsp = snp_alloc_firmware_page(GFP_KERNEL_ACCOUNT | __GFP_ZERO);
> + if (!report_rsp)
> + return -ENOMEM;
> +
> + data.len = sizeof(data);
> + data.hv_report_paddr = __psp_pa(report_rsp);
> + data.key_sel = params.key_sel;
> +
> + data.gctx_addr = __psp_pa(sev->snp_context);
> + ret = sev_issue_cmd(kvm, SEV_CMD_SNP_HV_REPORT_REQ, &data,
> + &argp->error);
> +
> + if (ret)
> + goto e_free_rsp;
> +
> + params.report_len = report_rsp->report_size;
> + if (copy_to_user(u_params, ¶ms, sizeof(params)))
> + ret = -EFAULT;
> +
> + if (params.report_len < report_rsp->report_size) {
> + ret = -ENOSPC;
> + /* report is located right after rsp */
I think the above comment is for "report_rsp + 1", but is embedded in
the if { }, that is confusing.
> + } else if (copy_to_user(u_report, report_rsp + 1, report_rsp->report_size)) {
> + ret = -EFAULT;
> + }
> +
> +e_free_rsp:
Should we zero the report_rsp before freeing (contains sensitive
attestation data) ?
memzero_explicit(report_rsp, PAGE_SIZE);
Regards
Nikunj
> + snp_free_firmware_page(report_rsp);
> + return ret;
> +}
> +
> struct sev_gmem_populate_args {
> __u8 type;
> int sev_fd;
> @@ -2664,6 +2721,9 @@ int sev_mem_enc_ioctl(struct kvm *kvm, void __user *argp)
> case KVM_SEV_SNP_LAUNCH_FINISH:
> r = snp_launch_finish(kvm, &sev_cmd);
> break;
> + case KVM_SEV_SNP_HV_REPORT_REQ:
> + r = sev_snp_report_request(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 0d13d47c164b..5236d5ee19ac 100644
> --- a/drivers/crypto/ccp/sev-dev.c
> +++ b/drivers/crypto/ccp/sev-dev.c
> @@ -251,6 +251,7 @@ static int sev_cmd_buffer_len(int cmd)
> case SEV_CMD_SNP_COMMIT: return sizeof(struct sev_data_snp_commit);
> case SEV_CMD_SNP_FEATURE_INFO: return sizeof(struct sev_data_snp_feature_info);
> case SEV_CMD_SNP_VLEK_LOAD: return sizeof(struct sev_user_data_snp_vlek_load);
> + case SEV_CMD_SNP_HV_REPORT_REQ: return sizeof(struct sev_data_snp_hv_report_req);
> default: return 0;
> }
>
> diff --git a/include/linux/psp-sev.h b/include/linux/psp-sev.h
> index e0dbcb4b4fd9..c382edc8713a 100644
> --- a/include/linux/psp-sev.h
> +++ b/include/linux/psp-sev.h
> @@ -91,6 +91,7 @@ enum sev_cmd {
> SEV_CMD_SNP_GCTX_CREATE = 0x093,
> SEV_CMD_SNP_GUEST_REQUEST = 0x094,
> SEV_CMD_SNP_ACTIVATE_EX = 0x095,
> + SEV_CMD_SNP_HV_REPORT_REQ = 0x096,
> SEV_CMD_SNP_LAUNCH_START = 0x0A0,
> SEV_CMD_SNP_LAUNCH_UPDATE = 0x0A1,
> SEV_CMD_SNP_LAUNCH_FINISH = 0x0A2,
> @@ -554,6 +555,33 @@ struct sev_data_attestation_report {
> u32 len; /* In/Out */
> } __packed;
>
> +/**
> + * struct sev_data_snp_hv_report_req - SNP_HV_REPORT_REQ command params
> + *
> + * @len: length of the command buffer in bytes
> + * @key_sel: Selects which key to use for generating the signature.
> + * @gctx_addr: System physical address of guest context page
> + * @hv_report_paddr: System physical address where MSG_EXPORT_RSP will be written
> + */
> +struct sev_data_snp_hv_report_req {
> + u32 len; /* In */
> + u32 key_sel:2; /* In */
> + u32 rsvd:30;
> + u64 gctx_addr; /* In */
> + u64 hv_report_paddr; /* In */
> +} __packed;
> +/**
> + * struct sev_data_snp_msg_export_rsp
> + *
> + * @status: Status : 0h: Success. 16h: Invalid parameters.
> + * @report_size: Size in bytes of the attestation report
> + */
> +struct sev_data_snp_msg_report_rsp {
> + u32 status; /* Out */
> + u32 report_size; /* Out */
> + u8 rsvd[24];
> +} __packed;
> +
> /**
> * struct sev_data_snp_download_firmware - SNP_DOWNLOAD_FIRMWARE command params
> *
> --
> 2.52.0
© 2016 - 2025 Red Hat, Inc.