[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <30242a68-25f5-4e92-b776-f3eb6f137c31@amd.com>
Date: Tue, 2 Dec 2025 13:28:53 -0600
From: Tom Lendacky <thomas.lendacky@....com>
To: Thomas Courrege <thomas.courrege@...es.tech>, pbonzini@...hat.com,
seanjc@...gle.com, corbet@....net, ashish.kalra@....com, john.allen@....com,
herbert@...dor.apana.org.au, nikunj@....com
Cc: x86@...nel.org, kvm@...r.kernel.org, linux-kernel@...r.kernel.org,
linux-crypto@...r.kernel.org
Subject: Re: [PATCH v2] KVM: SEV: Add KVM_SEV_SNP_HV_REPORT_REQ command
On 12/1/25 09:19, Thomas Courrege wrote:
> 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@...es.tech>
> ---
> .../virt/kvm/x86/amd-memory-encryption.rst | 25 ++++++++
> arch/x86/include/uapi/asm/kvm.h | 7 +++
> arch/x86/kvm/svm/sev.c | 61 +++++++++++++++++++
> drivers/crypto/ccp/sev-dev.c | 1 +
> include/linux/psp-sev.h | 31 ++++++++++
> 5 files changed, 125 insertions(+)
>
> diff --git a/Documentation/virt/kvm/x86/amd-memory-encryption.rst b/Documentation/virt/kvm/x86/amd-memory-encryption.rst
> index 1ddb6a86ce7f..b3ee25718938 100644
> --- a/Documentation/virt/kvm/x86/amd-memory-encryption.rst
> +++ b/Documentation/virt/kvm/x86/amd-memory-encryption.rst
> @@ -572,6 +572,31 @@ 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_HV_REPORT_REQ
> +-----------------------------
> +
> +The KVM_SEV_SNP_HV_REPORT_REQ command requests the hypervisor-generated
> +SNP attestation report. This report is produced by the PSP using the
> +HV-SIGNED key selected by the caller.
> +
> +The ``key_sel`` field indicates which key the platform will use to sign the
> +report:
> + * ``0``: If VLEK is installed, sign with VLEK. Otherwise, sign with VCEK.
> + * ``1``: Sign with VCEK.
> + * ``2``: Sign with VLEK.
> + * Other values are reserved.
> +
> +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;
You should add 7 bytes of padding here.
> + __u64 report_uaddr;
> + __u64 report_len;
Additionally, there should be some padding at the end of the struct for
future expansion/use (see the other structs for examples).
> +};
> +
> 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..62f17f4eab42 100644
> --- a/arch/x86/kvm/svm/sev.c
> +++ b/arch/x86/kvm/svm/sev.c
> @@ -2253,6 +2253,64 @@ static int snp_launch_start(struct kvm *kvm, struct kvm_sev_cmd *argp)
> return rc;
> }
>
> +static int sev_snp_hv_report_request(struct kvm *kvm, struct kvm_sev_cmd *argp)
> +{
> + struct sev_data_snp_msg_report_rsp *report_rsp = NULL;
> + struct sev_data_snp_hv_report_req data;
> + struct kvm_sev_snp_hv_report_req params;
> + struct kvm_sev_info *sev = to_kvm_sev_info(kvm);
> + void __user *u_report;
> + void __user *u_params = u64_to_user_ptr(argp->data);
> + int ret;
> +
> + if (!sev_snp_guest(kvm))
> + return -ENOTTY;
> +
> + if (copy_from_user(¶ms, u_params, sizeof(params)))
> + return -EFAULT;
> +
> + if (params.report_len < SEV_SNP_ATTESTATION_REPORT_SIZE)
> + return -ENOSPC;
See my comment below after the SEV_SNP_ATTESTATION_REPORT_SIZE #define.
Here you should check that report_len is at least the size of the report
response struct without the report, e.g., sizeof(*report_rsp).
> +
> + memset(&data, 0, sizeof(data));
Should be able to do '= {0}' in the declaration and eliminate the memset.
> +
> + 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);
Just set these in the order they are in the struct and without blank
lines in between them, e.g.:
data.len = ...
data.key_sel = ...
data.gctx_paddr = ...
data.hv_report_paddr = ...
> + ret = sev_issue_cmd(kvm, SEV_CMD_SNP_HV_REPORT_REQ, &data,
> + &argp->error);
> +
Remove the blank line.
> + if (ret)
> + goto e_free_rsp;
> +
> + params.report_len = report_rsp->report_size;
> + if (copy_to_user(u_params, ¶ms, sizeof(params)))
> + ret = -EFAULT;
Rather than updating the report_len, you should just include the full
MSG_REPORT_RSP structure. The spec doesn't indicate if all failures will
be reported via the command return code vs being in the status field of
the response struct.
The length check and copy size would then be:
sizeof(*report_rsp) + report_rsp->report_size
Then if there isn't enough room you copy the 32 bytes of the report
response struct that will indicate how much memory is needed.
> +
> + if (params.report_len < report_rsp->report_size) {
> + ret = -ENOSPC;
> + } else if (copy_to_user(u_report, report_rsp + 1, report_rsp->report_size)) {
> + /* report is located right after rsp */
> + ret = -EFAULT;
> + }
rsp_size = sizeof(*report_rsp);
if (!report_rsp->status)
rsp_size += report_rsp->report_size;
if (params.report_len < rsp_size) {
rsp_size = sizeof(*report_rsp);
ret = -ENOSPC;
}
if (copy_to_user(u_report, report_rsp, rsp_size))
ret = -EFAULT;
> +
> +e_free_rsp:
> + /* contains sensitive data */
> + memzero_explicit(report_rsp, PAGE_SIZE);
Does it? What is sensitive that needs to be cleared?
> + snp_free_firmware_page(report_rsp);
> + return ret;
> +}
> +
> struct sev_gmem_populate_args {
> __u8 type;
> int sev_fd;
> @@ -2664,6 +2722,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_hv_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..0e635feb7671 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,36 @@ 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;
Please use the following format for bitfields:
u32 key_sel :2,
rsvd :30;
It makes it easier to read/understand.
> + u64 gctx_addr; /* In */
> + u64 hv_report_paddr; /* In */
> +} __packed;
> +
> +#define SEV_SNP_ATTESTATION_REPORT_SIZE 1184
I don't think this should be specified as such in case anything changes
in the future. Plan on just allocating a page like you already do. The
SNP ABI does imply that the response can't cross a 4K boundary (similar
to how the guest attestation response explicitly states that the
response can't cross a 4K boundary). Only upon return of the report, do
you then check the size and see if userspace has allocated enough memory.
> +
> +/**
> + * 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];
You could add a u8 report[]; here to show that a variable size report
follows.
Thanks,
Tom
> +} __packed;
> +
> /**
> * struct sev_data_snp_download_firmware - SNP_DOWNLOAD_FIRMWARE command params
> *
Powered by blists - more mailing lists