Re: [PATCH v13 09/26] KVM: SEV: Add KVM_SEV_SNP_LAUNCH_START command

From: Sean Christopherson
Date: Wed Apr 24 2024 - 17:40:16 EST


On Thu, Apr 18, 2024, Michael Roth wrote:
> +static inline bool sev_version_greater_or_equal(u8 major, u8 minor)
> +{
> + if (major < SNP_POLICY_API_MAJOR)
> + return true;
> +
> + if (major == SNP_POLICY_API_MAJOR && minor <= SNP_POLICY_API_MINOR)
> + return true;
> +
> + return false;
> +}
> +
> +static int snp_launch_start(struct kvm *kvm, struct kvm_sev_cmd *argp)
> +{
> + struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
> + struct sev_data_snp_launch_start start = {0};
> + struct kvm_sev_snp_launch_start params;
> + u8 major, minor;
> + int rc;
> +
> + if (!sev_snp_guest(kvm))
> + return -ENOTTY;
> +
> + if (copy_from_user(&params, u64_to_user_ptr(argp->data), sizeof(params)))
> + return -EFAULT;
> +
> + /* Don't allow userspace to allocate memory for more than 1 SNP context. */
> + if (sev->snp_context) {
> + pr_debug("SEV-SNP context already exists. Refusing to allocate an additional one.\n");

What's the plan with all these printks? There are far too many in this series.
Some might be useful, but many of them have no business landing upstream.

> + return -EINVAL;
> + }
> +
> + sev->snp_context = snp_context_create(kvm, argp);
> + if (!sev->snp_context)
> + return -ENOTTY;
> +
> + if (params.policy & ~SNP_POLICY_MASK_VALID) {
> + pr_debug("SEV-SNP hypervisor does not support requested policy %llx (supported %llx).\n",

What does "SEV-SNP hypervisor" even mean?

> + params.policy, SNP_POLICY_MASK_VALID);
> + return -EINVAL;
> + }
> +
> + if (!(params.policy & SNP_POLICY_MASK_RSVD_MBO)) {
> + pr_debug("SEV-SNP hypervisor does not support requested policy %llx (must be set %llx).\n",
> + params.policy, SNP_POLICY_MASK_RSVD_MBO);
> + return -EINVAL;
> + }
> +
> + if (params.policy & SNP_POLICY_MASK_SINGLE_SOCKET) {
> + pr_debug("SEV-SNP hypervisor does not support limiting guests to a single socket.\n");
> + return -EINVAL;
> + }
> +
> + if (!(params.policy & SNP_POLICY_MASK_SMT)) {
> + pr_debug("SEV-SNP hypervisor does not support limiting guests to a single SMT thread.\n");
> + return -EINVAL;
> + }
> +
> + major = (params.policy & SNP_POLICY_MASK_API_MAJOR);
> + minor = (params.policy & SNP_POLICY_MASK_API_MINOR);
> + if (!sev_version_greater_or_equal(major, minor)) {

Why does this need a someone weirdly named helper? Isn't this just?

if (major < SNP_POLICY_API_MAJOR ||
(major == SNP_POLICY_API_MAJOR && minor < SNP_POLICY_API_MINOR))

> + pr_debug("SEV-SNP hypervisor does not support requested version %d.%d (have %d,%d).\n",
> + major, minor, SNP_POLICY_API_MAJOR, SNP_POLICY_API_MINOR);
> + return -EINVAL;
> + }
> +
> + start.gctx_paddr = __psp_pa(sev->snp_context);
> + start.policy = params.policy;
> + memcpy(start.gosvw, params.gosvw, sizeof(params.gosvw));
> + rc = __sev_issue_cmd(argp->sev_fd, SEV_CMD_SNP_LAUNCH_START, &start, &argp->error);
> + if (rc) {
> + pr_debug("SEV_CMD_SNP_LAUNCH_START firmware command failed, rc %d\n", rc);
> + goto e_free_context;
> + }
> +
> + sev->fd = argp->sev_fd;
> + rc = snp_bind_asid(kvm, &argp->error);
> + if (rc) {
> + pr_debug("Failed to bind ASID to SEV-SNP context, rc %d\n", rc);
> + goto e_free_context;
> + }
> +
> + return 0;
> +
> +e_free_context:
> + snp_decommission_context(kvm);
> +
> + return rc;
> +}
> +
> int sev_mem_enc_ioctl(struct kvm *kvm, void __user *argp)
> {
> struct kvm_sev_cmd sev_cmd;
> @@ -1999,6 +2154,15 @@ int sev_mem_enc_ioctl(struct kvm *kvm, void __user *argp)
> goto out;
> }
>
> + /*
> + * Once KVM_SEV_INIT2 initializes a KVM instance as an SNP guest, only
> + * allow the use of SNP-specific commands.
> + */
> + if (sev_snp_guest(kvm) && sev_cmd.id < KVM_SEV_SNP_LAUNCH_START) {
> + r = -EPERM;
> + goto out;
> + }
> +
> switch (sev_cmd.id) {
> case KVM_SEV_ES_INIT:
> if (!sev_es_enabled) {
> @@ -2063,6 +2227,9 @@ int sev_mem_enc_ioctl(struct kvm *kvm, void __user *argp)
> case KVM_SEV_RECEIVE_FINISH:
> r = sev_receive_finish(kvm, &sev_cmd);
> break;
> + case KVM_SEV_SNP_LAUNCH_START:
> + r = snp_launch_start(kvm, &sev_cmd);
> + break;
> default:
> r = -EINVAL;
> goto out;
> @@ -2258,6 +2425,33 @@ int sev_vm_copy_enc_context_from(struct kvm *kvm, unsigned int source_fd)
> return ret;
> }
>
> +static int snp_decommission_context(struct kvm *kvm)
> +{
> + struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
> + struct sev_data_snp_addr data = {};
> + int ret;
> +
> + /* If context is not created then do nothing */
> + if (!sev->snp_context)
> + return 0;
> +
> + data.address = __sme_pa(sev->snp_context);
> + down_write(&sev_deactivate_lock);
> + ret = sev_do_cmd(SEV_CMD_SNP_DECOMMISSION, &data, NULL);
> + if (WARN_ONCE(ret, "failed to release guest context")) {

WARN here, or WARN in the caller, not both. And if you warn here, this can be

down_write(&sev_deactivate_lock);
ret = sev_do_cmd(SEV_CMD_SNP_DECOMMISSION, &data, NULL);
up_write(&sev_deactivate_lock);

if (WARN_ONCE(ret, "..."))

> + up_write(&sev_deactivate_lock);
> + return ret;
> + }
> +
> + up_write(&sev_deactivate_lock);
> +
> + /* free the context page now */

This doesn't seem like a particularly useful comment. What would be useful is
a comment explaining the "decommission" unbinds the ASID.

> + snp_free_firmware_page(sev->snp_context);
> + sev->snp_context = NULL;
> +
> + return 0;
> +}
> +
> void sev_vm_destroy(struct kvm *kvm)
> {
> struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
> @@ -2299,7 +2493,15 @@ void sev_vm_destroy(struct kvm *kvm)
> }
> }
>
> - sev_unbind_asid(kvm, sev->handle);
> + if (sev_snp_guest(kvm)) {
> + if (snp_decommission_context(kvm)) {
> + WARN_ONCE(1, "Failed to free SNP guest context, leaking asid!\n");

WARN on the actually failure, not '1'. And a newline isn't needed.

if (WARN_ONCE(snp_decommission_context(kvm)
"Failed to free SNP guest context, leaking asid!"))
return;

> + return;
> + }
> + } else {
> + sev_unbind_asid(kvm, sev->handle);
> + }
> +
> sev_asid_free(sev);
> }
>
> diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h
> index 7f2e9c7fc4ca..0654fc91d4db 100644
> --- a/arch/x86/kvm/svm/svm.h
> +++ b/arch/x86/kvm/svm/svm.h
> @@ -92,6 +92,7 @@ struct kvm_sev_info {
> struct list_head mirror_entry; /* Use as a list entry of mirrors */
> struct misc_cg *misc_cg; /* For misc cgroup accounting */
> atomic_t migration_in_progress;
> + void *snp_context; /* SNP guest context page */
> };
>
> struct kvm_svm {
> --
> 2.25.1
>