The command initate the process to launch this guest into SEV-enabled mode. For more information on command structure see [1], section 6.1 [1] http://support.amd.com/TechDocs/55766_SEV-KM%20API_Spec.pdf Signed-off-by: Brijesh Singh <brijesh.singh@xxxxxxx> --- arch/x86/kvm/svm.c | 212 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 209 insertions(+), 3 deletions(-) diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c index dcee635..0b6da4a 100644 --- a/arch/x86/kvm/svm.c +++ b/arch/x86/kvm/svm.c @@ -265,6 +265,9 @@ static unsigned long *sev_asid_bitmap; static int sev_asid_new(void); static void sev_asid_free(int asid); +static void sev_deactivate_handle(unsigned int handle); +static void sev_decommission_handle(unsigned int handle); +static int sev_activate_asid(unsigned int handle, int asid, int *psp_ret); static void svm_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0); static void svm_flush_tlb(struct kvm_vcpu *vcpu); @@ -1645,9 +1648,18 @@ static void sev_uninit_vcpu(struct vcpu_svm *svm) svm_sev_unref(); - for_each_possible_cpu(cpu) { - sd = per_cpu(svm_data, cpu); - sd->sev_vmcb[asid] = NULL; + /* when reference count reaches to zero then free SEV asid and + * deactivate psp handle + */ + if (!svm_sev_ref_count()) { + sev_deactivate_handle(svm_sev_handle()); + sev_decommission_handle(svm_sev_handle()); + sev_asid_free(svm_sev_asid()); + + for_each_possible_cpu(cpu) { + sd = per_cpu(svm_data, cpu); + sd->sev_vmcb[asid] = NULL; + } } } @@ -5196,6 +5208,198 @@ static void sev_asid_free(int asid) clear_bit(asid, sev_asid_bitmap); } +static void sev_decommission_handle(unsigned int handle) +{ + int ret, psp_ret; + struct psp_data_decommission *decommission; + + decommission = kzalloc(sizeof(*decommission), GFP_KERNEL); + if (!decommission) + return; + + decommission->hdr.buffer_len = sizeof(*decommission); + decommission->handle = handle; + ret = psp_guest_decommission(decommission, &psp_ret); + if (ret) + printk(KERN_ERR "SEV: DECOMISSION ret=%d (%#010x)\n", + ret, psp_ret); + + kfree(decommission); +} + +static void sev_deactivate_handle(unsigned int handle) +{ + int ret, psp_ret; + struct psp_data_deactivate *deactivate; + + deactivate = kzalloc(sizeof(*deactivate), GFP_KERNEL); + if (!deactivate) + return; + + deactivate->hdr.buffer_len = sizeof(*deactivate); + deactivate->handle = handle; + ret = psp_guest_deactivate(deactivate, &psp_ret); + if (ret) { + printk(KERN_ERR "SEV: DEACTIVATE ret=%d (%#010x)\n", + ret, psp_ret); + goto buffer_free; + } + + wbinvd_on_all_cpus(); + + ret = psp_guest_df_flush(&psp_ret); + if (ret) + printk(KERN_ERR "SEV: DF_FLUSH ret=%d (%#010x)\n", + ret, psp_ret); + +buffer_free: + kfree(deactivate); +} + +static int sev_activate_asid(unsigned int handle, int asid, int *psp_ret) +{ + int ret; + struct psp_data_activate *activate; + + wbinvd_on_all_cpus(); + + ret = psp_guest_df_flush(psp_ret); + if (ret) { + printk(KERN_ERR "SEV: DF_FLUSH ret=%d (%#010x)\n", + ret, *psp_ret); + return ret; + } + + activate = kzalloc(sizeof(*activate), GFP_KERNEL); + if (!activate) + return -ENOMEM; + + activate->hdr.buffer_len = sizeof(*activate); + activate->handle = handle; + activate->asid = asid; + ret = psp_guest_activate(activate, psp_ret); + if (ret) + printk(KERN_ERR "SEV: ACTIVATE ret=%d (%#010x)\n", + ret, *psp_ret); + kfree(activate); + return ret; +} + +static int sev_pre_start(struct kvm *kvm, int *asid) +{ + int ret; + + /* If guest has active psp handle then deactivate before calling + * launch start. + */ + if (kvm_sev_guest()) { + sev_deactivate_handle(kvm_sev_handle()); + sev_decommission_handle(kvm_sev_handle()); + *asid = kvm->arch.sev_info.asid; /* reuse the asid */ + ret = 0; + } else { + /* Allocate new asid for this launch */ + ret = sev_asid_new(); + if (ret < 0) { + printk(KERN_ERR "SEV: failed to allocate asid\n"); + return ret; + } + *asid = ret; + ret = 0; + } + + return ret; +} + +static int sev_post_start(struct kvm *kvm, int asid, int handle, int *psp_ret) +{ + int ret; + + /* activate asid */ + ret = sev_activate_asid(handle, asid, psp_ret); + if (ret) + return ret; + + kvm->arch.sev_info.handle = handle; + kvm->arch.sev_info.asid = asid; + + return 0; +} + +static int sev_launch_start(struct kvm *kvm, + struct kvm_sev_launch_start __user *arg, + int *psp_ret) +{ + int ret, asid; + struct kvm_sev_launch_start params; + struct psp_data_launch_start *start; + + /* Get parameter from the user */ + if (copy_from_user(¶ms, arg, sizeof(*arg))) + return -EFAULT; + + start = kzalloc(sizeof(*start), GFP_KERNEL); + if (!start) + return -ENOMEM; + + ret = sev_pre_start(kvm, &asid); + if (ret) + goto err_1; + + start->hdr.buffer_len = sizeof(*start); + start->flags = params.flags; + start->policy = params.policy; + start->handle = params.handle; + memcpy(start->nonce, ¶ms.nonce, sizeof(start->nonce)); + memcpy(start->dh_pub_qx, ¶ms.dh_pub_qx, sizeof(start->dh_pub_qx)); + memcpy(start->dh_pub_qy, ¶ms.dh_pub_qy, sizeof(start->dh_pub_qy)); + + /* launch start */ + ret = psp_guest_launch_start(start, psp_ret); + if (ret) { + printk(KERN_ERR "SEV: LAUNCH_START ret=%d (%#010x)\n", + ret, *psp_ret); + goto err_2; + } + + ret = sev_post_start(kvm, asid, start->handle, psp_ret); + if (ret) + goto err_2; + + kfree(start); + return 0; + +err_2: + sev_asid_free(asid); +err_1: + kfree(start); + return ret; +} + +static int amd_sev_issue_cmd(struct kvm *kvm, + struct kvm_sev_issue_cmd __user *user_data) +{ + int r = -ENOTTY; + struct kvm_sev_issue_cmd arg; + + if (copy_from_user(&arg, user_data, sizeof(struct kvm_sev_issue_cmd))) + return -EFAULT; + + switch (arg.cmd) { + case KVM_SEV_LAUNCH_START: { + r = sev_launch_start(kvm, (void *)arg.opaque, + &arg.ret_code); + break; + } + default: + break; + } + + if (copy_to_user(user_data, &arg, sizeof(struct kvm_sev_issue_cmd))) + r = -EFAULT; + return r; +} + static struct kvm_x86_ops svm_x86_ops __ro_after_init = { .cpu_has_kvm_support = has_svm, .disabled_by_bios = is_disabled, @@ -5313,6 +5517,8 @@ static struct kvm_x86_ops svm_x86_ops __ro_after_init = { .pmu_ops = &amd_pmu_ops, .deliver_posted_interrupt = svm_deliver_avic_intr, + + .sev_issue_cmd = amd_sev_issue_cmd, }; static int __init svm_init(void) -- To unsubscribe from this list: send the line "unsubscribe linux-crypto" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html