diff options
Diffstat (limited to 'arch/x86/kvm/svm/sev.c')
| -rw-r--r-- | arch/x86/kvm/svm/sev.c | 112 | 
1 files changed, 73 insertions, 39 deletions
diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 75fa6dd268f0..7c392873626f 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -260,6 +260,8 @@ static int sev_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp)  	INIT_LIST_HEAD(&sev->regions_list);  	INIT_LIST_HEAD(&sev->mirror_vms); +	kvm_set_apicv_inhibit(kvm, APICV_INHIBIT_REASON_SEV); +  	return 0;  e_free: @@ -465,6 +467,7 @@ static void sev_clflush_pages(struct page *pages[], unsigned long npages)  		page_virtual = kmap_atomic(pages[i]);  		clflush_cache_range(page_virtual, PAGE_SIZE);  		kunmap_atomic(page_virtual); +		cond_resched();  	}  } @@ -1591,24 +1594,51 @@ static void sev_unlock_two_vms(struct kvm *dst_kvm, struct kvm *src_kvm)  	atomic_set_release(&src_sev->migration_in_progress, 0);  } +/* vCPU mutex subclasses.  */ +enum sev_migration_role { +	SEV_MIGRATION_SOURCE = 0, +	SEV_MIGRATION_TARGET, +	SEV_NR_MIGRATION_ROLES, +}; -static int sev_lock_vcpus_for_migration(struct kvm *kvm) +static int sev_lock_vcpus_for_migration(struct kvm *kvm, +					enum sev_migration_role role)  {  	struct kvm_vcpu *vcpu;  	unsigned long i, j; +	bool first = true;  	kvm_for_each_vcpu(i, vcpu, kvm) { -		if (mutex_lock_killable(&vcpu->mutex)) +		if (mutex_lock_killable_nested(&vcpu->mutex, role))  			goto out_unlock; + +		if (first) { +			/* +			 * Reset the role to one that avoids colliding with +			 * the role used for the first vcpu mutex. +			 */ +			role = SEV_NR_MIGRATION_ROLES; +			first = false; +		} else { +			mutex_release(&vcpu->mutex.dep_map, _THIS_IP_); +		}  	}  	return 0;  out_unlock: + +	first = true;  	kvm_for_each_vcpu(j, vcpu, kvm) {  		if (i == j)  			break; +		if (first) +			first = false; +		else +			mutex_acquire(&vcpu->mutex.dep_map, role, 0, _THIS_IP_); + +  		mutex_unlock(&vcpu->mutex);  	}  	return -EINTR; @@ -1618,8 +1648,15 @@ static void sev_unlock_vcpus_for_migration(struct kvm *kvm)  {  	struct kvm_vcpu *vcpu;  	unsigned long i; +	bool first = true;  	kvm_for_each_vcpu(i, vcpu, kvm) { +		if (first) +			first = false; +		else +			mutex_acquire(&vcpu->mutex.dep_map, +				      SEV_NR_MIGRATION_ROLES, 0, _THIS_IP_); +  		mutex_unlock(&vcpu->mutex);  	}  } @@ -1745,10 +1782,10 @@ int sev_vm_move_enc_context_from(struct kvm *kvm, unsigned int source_fd)  		charged = true;  	} -	ret = sev_lock_vcpus_for_migration(kvm); +	ret = sev_lock_vcpus_for_migration(kvm, SEV_MIGRATION_SOURCE);  	if (ret)  		goto out_dst_cgroup; -	ret = sev_lock_vcpus_for_migration(source_kvm); +	ret = sev_lock_vcpus_for_migration(source_kvm, SEV_MIGRATION_TARGET);  	if (ret)  		goto out_dst_vcpu; @@ -2223,51 +2260,47 @@ int sev_cpu_init(struct svm_cpu_data *sd)   * Pages used by hardware to hold guest encrypted state must be flushed before   * returning them to the system.   */ -static void sev_flush_guest_memory(struct vcpu_svm *svm, void *va, -				   unsigned long len) +static void sev_flush_encrypted_page(struct kvm_vcpu *vcpu, void *va)  { +	int asid = to_kvm_svm(vcpu->kvm)->sev_info.asid; +  	/* -	 * If hardware enforced cache coherency for encrypted mappings of the -	 * same physical page is supported, nothing to do. +	 * Note!  The address must be a kernel address, as regular page walk +	 * checks are performed by VM_PAGE_FLUSH, i.e. operating on a user +	 * address is non-deterministic and unsafe.  This function deliberately +	 * takes a pointer to deter passing in a user address.  	 */ -	if (boot_cpu_has(X86_FEATURE_SME_COHERENT)) -		return; +	unsigned long addr = (unsigned long)va;  	/* -	 * If the VM Page Flush MSR is supported, use it to flush the page -	 * (using the page virtual address and the guest ASID). +	 * If CPU enforced cache coherency for encrypted mappings of the +	 * same physical page is supported, use CLFLUSHOPT instead. NOTE: cache +	 * flush is still needed in order to work properly with DMA devices.  	 */ -	if (boot_cpu_has(X86_FEATURE_VM_PAGE_FLUSH)) { -		struct kvm_sev_info *sev; -		unsigned long va_start; -		u64 start, stop; - -		/* Align start and stop to page boundaries. */ -		va_start = (unsigned long)va; -		start = (u64)va_start & PAGE_MASK; -		stop = PAGE_ALIGN((u64)va_start + len); - -		if (start < stop) { -			sev = &to_kvm_svm(svm->vcpu.kvm)->sev_info; +	if (boot_cpu_has(X86_FEATURE_SME_COHERENT)) { +		clflush_cache_range(va, PAGE_SIZE); +		return; +	} -			while (start < stop) { -				wrmsrl(MSR_AMD64_VM_PAGE_FLUSH, -				       start | sev->asid); +	/* +	 * VM Page Flush takes a host virtual address and a guest ASID.  Fall +	 * back to WBINVD if this faults so as not to make any problems worse +	 * by leaving stale encrypted data in the cache. +	 */ +	if (WARN_ON_ONCE(wrmsrl_safe(MSR_AMD64_VM_PAGE_FLUSH, addr | asid))) +		goto do_wbinvd; -				start += PAGE_SIZE; -			} +	return; -			return; -		} +do_wbinvd: +	wbinvd_on_all_cpus(); +} -		WARN(1, "Address overflow, using WBINVD\n"); -	} +void sev_guest_memory_reclaimed(struct kvm *kvm) +{ +	if (!sev_guest(kvm)) +		return; -	/* -	 * Hardware should always have one of the above features, -	 * but if not, use WBINVD and issue a warning. -	 */ -	WARN_ONCE(1, "Using WBINVD to flush guest memory\n");  	wbinvd_on_all_cpus();  } @@ -2281,7 +2314,8 @@ void sev_free_vcpu(struct kvm_vcpu *vcpu)  	svm = to_svm(vcpu);  	if (vcpu->arch.guest_state_protected) -		sev_flush_guest_memory(svm, svm->sev_es.vmsa, PAGE_SIZE); +		sev_flush_encrypted_page(vcpu, svm->sev_es.vmsa); +  	__free_page(virt_to_page(svm->sev_es.vmsa));  	if (svm->sev_es.ghcb_sa_free)  | 
