|
38 | 38 | #define GHCB_VERSION_DEFAULT 2ULL |
39 | 39 | #define GHCB_VERSION_MIN 1ULL |
40 | 40 |
|
41 | | -#define GHCB_HV_FT_SUPPORTED GHCB_HV_FT_SNP |
| 41 | +#define GHCB_HV_FT_SUPPORTED (GHCB_HV_FT_SNP | GHCB_HV_FT_SNP_AP_CREATION) |
42 | 42 |
|
43 | 43 | /* enable/disable SEV support */ |
44 | 44 | static bool sev_enabled = true; |
@@ -3267,6 +3267,13 @@ static int sev_es_validate_vmgexit(struct vcpu_svm *svm) |
3267 | 3267 | if (!kvm_ghcb_sw_scratch_is_valid(svm)) |
3268 | 3268 | goto vmgexit_err; |
3269 | 3269 | break; |
| 3270 | + case SVM_VMGEXIT_AP_CREATION: |
| 3271 | + if (!sev_snp_guest(vcpu->kvm)) |
| 3272 | + goto vmgexit_err; |
| 3273 | + if (lower_32_bits(control->exit_info_1) != SVM_VMGEXIT_AP_DESTROY) |
| 3274 | + if (!kvm_ghcb_rax_is_valid(svm)) |
| 3275 | + goto vmgexit_err; |
| 3276 | + break; |
3270 | 3277 | case SVM_VMGEXIT_NMI_COMPLETE: |
3271 | 3278 | case SVM_VMGEXIT_AP_HLT_LOOP: |
3272 | 3279 | case SVM_VMGEXIT_AP_JUMP_TABLE: |
@@ -3701,6 +3708,205 @@ static int snp_begin_psc(struct vcpu_svm *svm, struct psc_buffer *psc) |
3701 | 3708 | unreachable(); |
3702 | 3709 | } |
3703 | 3710 |
|
| 3711 | +static int __sev_snp_update_protected_guest_state(struct kvm_vcpu *vcpu) |
| 3712 | +{ |
| 3713 | + struct vcpu_svm *svm = to_svm(vcpu); |
| 3714 | + |
| 3715 | + WARN_ON(!mutex_is_locked(&svm->sev_es.snp_vmsa_mutex)); |
| 3716 | + |
| 3717 | + /* Mark the vCPU as offline and not runnable */ |
| 3718 | + vcpu->arch.pv.pv_unhalted = false; |
| 3719 | + vcpu->arch.mp_state = KVM_MP_STATE_HALTED; |
| 3720 | + |
| 3721 | + /* Clear use of the VMSA */ |
| 3722 | + svm->vmcb->control.vmsa_pa = INVALID_PAGE; |
| 3723 | + |
| 3724 | + if (VALID_PAGE(svm->sev_es.snp_vmsa_gpa)) { |
| 3725 | + gfn_t gfn = gpa_to_gfn(svm->sev_es.snp_vmsa_gpa); |
| 3726 | + struct kvm_memory_slot *slot; |
| 3727 | + kvm_pfn_t pfn; |
| 3728 | + |
| 3729 | + slot = gfn_to_memslot(vcpu->kvm, gfn); |
| 3730 | + if (!slot) |
| 3731 | + return -EINVAL; |
| 3732 | + |
| 3733 | + /* |
| 3734 | + * The new VMSA will be private memory guest memory, so |
| 3735 | + * retrieve the PFN from the gmem backend. |
| 3736 | + */ |
| 3737 | + if (kvm_gmem_get_pfn(vcpu->kvm, slot, gfn, &pfn, NULL)) |
| 3738 | + return -EINVAL; |
| 3739 | + |
| 3740 | + /* |
| 3741 | + * From this point forward, the VMSA will always be a |
| 3742 | + * guest-mapped page rather than the initial one allocated |
| 3743 | + * by KVM in svm->sev_es.vmsa. In theory, svm->sev_es.vmsa |
| 3744 | + * could be free'd and cleaned up here, but that involves |
| 3745 | + * cleanups like wbinvd_on_all_cpus() which would ideally |
| 3746 | + * be handled during teardown rather than guest boot. |
| 3747 | + * Deferring that also allows the existing logic for SEV-ES |
| 3748 | + * VMSAs to be re-used with minimal SNP-specific changes. |
| 3749 | + */ |
| 3750 | + svm->sev_es.snp_has_guest_vmsa = true; |
| 3751 | + |
| 3752 | + /* Use the new VMSA */ |
| 3753 | + svm->vmcb->control.vmsa_pa = pfn_to_hpa(pfn); |
| 3754 | + |
| 3755 | + /* Mark the vCPU as runnable */ |
| 3756 | + vcpu->arch.pv.pv_unhalted = false; |
| 3757 | + vcpu->arch.mp_state = KVM_MP_STATE_RUNNABLE; |
| 3758 | + |
| 3759 | + svm->sev_es.snp_vmsa_gpa = INVALID_PAGE; |
| 3760 | + |
| 3761 | + /* |
| 3762 | + * gmem pages aren't currently migratable, but if this ever |
| 3763 | + * changes then care should be taken to ensure |
| 3764 | + * svm->sev_es.vmsa is pinned through some other means. |
| 3765 | + */ |
| 3766 | + kvm_release_pfn_clean(pfn); |
| 3767 | + } |
| 3768 | + |
| 3769 | + /* |
| 3770 | + * When replacing the VMSA during SEV-SNP AP creation, |
| 3771 | + * mark the VMCB dirty so that full state is always reloaded. |
| 3772 | + */ |
| 3773 | + vmcb_mark_all_dirty(svm->vmcb); |
| 3774 | + |
| 3775 | + return 0; |
| 3776 | +} |
| 3777 | + |
| 3778 | +/* |
| 3779 | + * Invoked as part of svm_vcpu_reset() processing of an init event. |
| 3780 | + */ |
| 3781 | +void sev_snp_init_protected_guest_state(struct kvm_vcpu *vcpu) |
| 3782 | +{ |
| 3783 | + struct vcpu_svm *svm = to_svm(vcpu); |
| 3784 | + int ret; |
| 3785 | + |
| 3786 | + if (!sev_snp_guest(vcpu->kvm)) |
| 3787 | + return; |
| 3788 | + |
| 3789 | + mutex_lock(&svm->sev_es.snp_vmsa_mutex); |
| 3790 | + |
| 3791 | + if (!svm->sev_es.snp_ap_waiting_for_reset) |
| 3792 | + goto unlock; |
| 3793 | + |
| 3794 | + svm->sev_es.snp_ap_waiting_for_reset = false; |
| 3795 | + |
| 3796 | + ret = __sev_snp_update_protected_guest_state(vcpu); |
| 3797 | + if (ret) |
| 3798 | + vcpu_unimpl(vcpu, "snp: AP state update on init failed\n"); |
| 3799 | + |
| 3800 | +unlock: |
| 3801 | + mutex_unlock(&svm->sev_es.snp_vmsa_mutex); |
| 3802 | +} |
| 3803 | + |
| 3804 | +static int sev_snp_ap_creation(struct vcpu_svm *svm) |
| 3805 | +{ |
| 3806 | + struct kvm_sev_info *sev = &to_kvm_svm(svm->vcpu.kvm)->sev_info; |
| 3807 | + struct kvm_vcpu *vcpu = &svm->vcpu; |
| 3808 | + struct kvm_vcpu *target_vcpu; |
| 3809 | + struct vcpu_svm *target_svm; |
| 3810 | + unsigned int request; |
| 3811 | + unsigned int apic_id; |
| 3812 | + bool kick; |
| 3813 | + int ret; |
| 3814 | + |
| 3815 | + request = lower_32_bits(svm->vmcb->control.exit_info_1); |
| 3816 | + apic_id = upper_32_bits(svm->vmcb->control.exit_info_1); |
| 3817 | + |
| 3818 | + /* Validate the APIC ID */ |
| 3819 | + target_vcpu = kvm_get_vcpu_by_id(vcpu->kvm, apic_id); |
| 3820 | + if (!target_vcpu) { |
| 3821 | + vcpu_unimpl(vcpu, "vmgexit: invalid AP APIC ID [%#x] from guest\n", |
| 3822 | + apic_id); |
| 3823 | + return -EINVAL; |
| 3824 | + } |
| 3825 | + |
| 3826 | + ret = 0; |
| 3827 | + |
| 3828 | + target_svm = to_svm(target_vcpu); |
| 3829 | + |
| 3830 | + /* |
| 3831 | + * The target vCPU is valid, so the vCPU will be kicked unless the |
| 3832 | + * request is for CREATE_ON_INIT. For any errors at this stage, the |
| 3833 | + * kick will place the vCPU in an non-runnable state. |
| 3834 | + */ |
| 3835 | + kick = true; |
| 3836 | + |
| 3837 | + mutex_lock(&target_svm->sev_es.snp_vmsa_mutex); |
| 3838 | + |
| 3839 | + target_svm->sev_es.snp_vmsa_gpa = INVALID_PAGE; |
| 3840 | + target_svm->sev_es.snp_ap_waiting_for_reset = true; |
| 3841 | + |
| 3842 | + /* Interrupt injection mode shouldn't change for AP creation */ |
| 3843 | + if (request < SVM_VMGEXIT_AP_DESTROY) { |
| 3844 | + u64 sev_features; |
| 3845 | + |
| 3846 | + sev_features = vcpu->arch.regs[VCPU_REGS_RAX]; |
| 3847 | + sev_features ^= sev->vmsa_features; |
| 3848 | + |
| 3849 | + if (sev_features & SVM_SEV_FEAT_INT_INJ_MODES) { |
| 3850 | + vcpu_unimpl(vcpu, "vmgexit: invalid AP injection mode [%#lx] from guest\n", |
| 3851 | + vcpu->arch.regs[VCPU_REGS_RAX]); |
| 3852 | + ret = -EINVAL; |
| 3853 | + goto out; |
| 3854 | + } |
| 3855 | + } |
| 3856 | + |
| 3857 | + switch (request) { |
| 3858 | + case SVM_VMGEXIT_AP_CREATE_ON_INIT: |
| 3859 | + kick = false; |
| 3860 | + fallthrough; |
| 3861 | + case SVM_VMGEXIT_AP_CREATE: |
| 3862 | + if (!page_address_valid(vcpu, svm->vmcb->control.exit_info_2)) { |
| 3863 | + vcpu_unimpl(vcpu, "vmgexit: invalid AP VMSA address [%#llx] from guest\n", |
| 3864 | + svm->vmcb->control.exit_info_2); |
| 3865 | + ret = -EINVAL; |
| 3866 | + goto out; |
| 3867 | + } |
| 3868 | + |
| 3869 | + /* |
| 3870 | + * Malicious guest can RMPADJUST a large page into VMSA which |
| 3871 | + * will hit the SNP erratum where the CPU will incorrectly signal |
| 3872 | + * an RMP violation #PF if a hugepage collides with the RMP entry |
| 3873 | + * of VMSA page, reject the AP CREATE request if VMSA address from |
| 3874 | + * guest is 2M aligned. |
| 3875 | + */ |
| 3876 | + if (IS_ALIGNED(svm->vmcb->control.exit_info_2, PMD_SIZE)) { |
| 3877 | + vcpu_unimpl(vcpu, |
| 3878 | + "vmgexit: AP VMSA address [%llx] from guest is unsafe as it is 2M aligned\n", |
| 3879 | + svm->vmcb->control.exit_info_2); |
| 3880 | + ret = -EINVAL; |
| 3881 | + goto out; |
| 3882 | + } |
| 3883 | + |
| 3884 | + target_svm->sev_es.snp_vmsa_gpa = svm->vmcb->control.exit_info_2; |
| 3885 | + break; |
| 3886 | + case SVM_VMGEXIT_AP_DESTROY: |
| 3887 | + break; |
| 3888 | + default: |
| 3889 | + vcpu_unimpl(vcpu, "vmgexit: invalid AP creation request [%#x] from guest\n", |
| 3890 | + request); |
| 3891 | + ret = -EINVAL; |
| 3892 | + break; |
| 3893 | + } |
| 3894 | + |
| 3895 | +out: |
| 3896 | + if (kick) { |
| 3897 | + kvm_make_request(KVM_REQ_UPDATE_PROTECTED_GUEST_STATE, target_vcpu); |
| 3898 | + |
| 3899 | + if (target_vcpu->arch.mp_state == KVM_MP_STATE_UNINITIALIZED) |
| 3900 | + kvm_make_request(KVM_REQ_UNBLOCK, target_vcpu); |
| 3901 | + |
| 3902 | + kvm_vcpu_kick(target_vcpu); |
| 3903 | + } |
| 3904 | + |
| 3905 | + mutex_unlock(&target_svm->sev_es.snp_vmsa_mutex); |
| 3906 | + |
| 3907 | + return ret; |
| 3908 | +} |
| 3909 | + |
3704 | 3910 | static int sev_handle_vmgexit_msr_protocol(struct vcpu_svm *svm) |
3705 | 3911 | { |
3706 | 3912 | struct vmcb_control_area *control = &svm->vmcb->control; |
@@ -3966,6 +4172,15 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu) |
3966 | 4172 |
|
3967 | 4173 | ret = snp_begin_psc(svm, svm->sev_es.ghcb_sa); |
3968 | 4174 | break; |
| 4175 | + case SVM_VMGEXIT_AP_CREATION: |
| 4176 | + ret = sev_snp_ap_creation(svm); |
| 4177 | + if (ret) { |
| 4178 | + ghcb_set_sw_exit_info_1(svm->sev_es.ghcb, 2); |
| 4179 | + ghcb_set_sw_exit_info_2(svm->sev_es.ghcb, GHCB_ERR_INVALID_INPUT); |
| 4180 | + } |
| 4181 | + |
| 4182 | + ret = 1; |
| 4183 | + break; |
3969 | 4184 | case SVM_VMGEXIT_UNSUPPORTED_EVENT: |
3970 | 4185 | vcpu_unimpl(vcpu, |
3971 | 4186 | "vmgexit: unsupported event - exit_info_1=%#llx, exit_info_2=%#llx\n", |
@@ -4060,7 +4275,7 @@ static void sev_es_init_vmcb(struct vcpu_svm *svm) |
4060 | 4275 | * the VMSA will be NULL if this vCPU is the destination for intrahost |
4061 | 4276 | * migration, and will be copied later. |
4062 | 4277 | */ |
4063 | | - if (svm->sev_es.vmsa) |
| 4278 | + if (svm->sev_es.vmsa && !svm->sev_es.snp_has_guest_vmsa) |
4064 | 4279 | svm->vmcb->control.vmsa_pa = __pa(svm->sev_es.vmsa); |
4065 | 4280 |
|
4066 | 4281 | /* Can't intercept CR register access, HV can't modify CR registers */ |
@@ -4136,6 +4351,8 @@ void sev_es_vcpu_reset(struct vcpu_svm *svm) |
4136 | 4351 | set_ghcb_msr(svm, GHCB_MSR_SEV_INFO((__u64)sev->ghcb_version, |
4137 | 4352 | GHCB_VERSION_MIN, |
4138 | 4353 | sev_enc_bit)); |
| 4354 | + |
| 4355 | + mutex_init(&svm->sev_es.snp_vmsa_mutex); |
4139 | 4356 | } |
4140 | 4357 |
|
4141 | 4358 | void sev_es_prepare_switch_to_guest(struct vcpu_svm *svm, struct sev_es_save_area *hostsa) |
@@ -4247,6 +4464,16 @@ struct page *snp_safe_alloc_page(struct kvm_vcpu *vcpu) |
4247 | 4464 | return p; |
4248 | 4465 | } |
4249 | 4466 |
|
| 4467 | +void sev_vcpu_unblocking(struct kvm_vcpu *vcpu) |
| 4468 | +{ |
| 4469 | + if (!sev_snp_guest(vcpu->kvm)) |
| 4470 | + return; |
| 4471 | + |
| 4472 | + if (kvm_test_request(KVM_REQ_UPDATE_PROTECTED_GUEST_STATE, vcpu) && |
| 4473 | + vcpu->arch.mp_state == KVM_MP_STATE_UNINITIALIZED) |
| 4474 | + vcpu->arch.mp_state = KVM_MP_STATE_RUNNABLE; |
| 4475 | +} |
| 4476 | + |
4250 | 4477 | void sev_handle_rmp_fault(struct kvm_vcpu *vcpu, gpa_t gpa, u64 error_code) |
4251 | 4478 | { |
4252 | 4479 | struct kvm_memory_slot *slot; |
|
0 commit comments