Commit bf42b02b authored by Paolo Bonzini's avatar Paolo Bonzini

KVM: SEV: Do COPY_ENC_CONTEXT_FROM with both VMs locked

Now that we have a facility to lock two VMs with deadlock
protection, use it for the creation of mirror VMs as well.  One of
COPY_ENC_CONTEXT_FROM(dst, src) and COPY_ENC_CONTEXT_FROM(src, dst)
would always fail, so the combination is nonsensical and it is okay to
return -EBUSY if it is attempted.

This sidesteps the question of what happens if a VM is
MOVE_ENC_CONTEXT_FROM'd at the same time as it is
COPY_ENC_CONTEXT_FROM'd: the locking prevents that from
happening.

Cc: Peter Gonda <pgonda@google.com>
Cc: Sean Christopherson <seanjc@google.com>
Reviewed-by: default avatarSean Christopherson <seanjc@google.com>
Message-Id: <20211123005036.2954379-10-pbonzini@redhat.com>
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent dc79c9f4
...@@ -1955,77 +1955,59 @@ int svm_vm_copy_asid_from(struct kvm *kvm, unsigned int source_fd) ...@@ -1955,77 +1955,59 @@ int svm_vm_copy_asid_from(struct kvm *kvm, unsigned int source_fd)
{ {
struct file *source_kvm_file; struct file *source_kvm_file;
struct kvm *source_kvm; struct kvm *source_kvm;
struct kvm_sev_info source_sev, *mirror_sev; struct kvm_sev_info *source_sev, *mirror_sev;
int ret; int ret;
source_kvm_file = fget(source_fd); source_kvm_file = fget(source_fd);
if (!file_is_kvm(source_kvm_file)) { if (!file_is_kvm(source_kvm_file)) {
ret = -EBADF; ret = -EBADF;
goto e_source_put; goto e_source_fput;
} }
source_kvm = source_kvm_file->private_data; source_kvm = source_kvm_file->private_data;
mutex_lock(&source_kvm->lock); ret = sev_lock_two_vms(kvm, source_kvm);
if (ret)
if (!sev_guest(source_kvm)) { goto e_source_fput;
ret = -EINVAL;
goto e_source_unlock;
}
/* Mirrors of mirrors should work, but let's not get silly */ /*
if (is_mirroring_enc_context(source_kvm) || source_kvm == kvm) { * Mirrors of mirrors should work, but let's not get silly. Also
* disallow out-of-band SEV/SEV-ES init if the target is already an
* SEV guest, or if vCPUs have been created. KVM relies on vCPUs being
* created after SEV/SEV-ES initialization, e.g. to init intercepts.
*/
if (sev_guest(kvm) || !sev_guest(source_kvm) ||
is_mirroring_enc_context(source_kvm) || kvm->created_vcpus) {
ret = -EINVAL; ret = -EINVAL;
goto e_source_unlock; goto e_unlock;
} }
memcpy(&source_sev, &to_kvm_svm(source_kvm)->sev_info,
sizeof(source_sev));
/* /*
* The mirror kvm holds an enc_context_owner ref so its asid can't * The mirror kvm holds an enc_context_owner ref so its asid can't
* disappear until we're done with it * disappear until we're done with it
*/ */
source_sev = &to_kvm_svm(source_kvm)->sev_info;
kvm_get_kvm(source_kvm); kvm_get_kvm(source_kvm);
fput(source_kvm_file);
mutex_unlock(&source_kvm->lock);
mutex_lock(&kvm->lock);
/*
* Disallow out-of-band SEV/SEV-ES init if the target is already an
* SEV guest, or if vCPUs have been created. KVM relies on vCPUs being
* created after SEV/SEV-ES initialization, e.g. to init intercepts.
*/
if (sev_guest(kvm) || kvm->created_vcpus) {
ret = -EINVAL;
goto e_mirror_unlock;
}
/* Set enc_context_owner and copy its encryption context over */ /* Set enc_context_owner and copy its encryption context over */
mirror_sev = &to_kvm_svm(kvm)->sev_info; mirror_sev = &to_kvm_svm(kvm)->sev_info;
mirror_sev->enc_context_owner = source_kvm; mirror_sev->enc_context_owner = source_kvm;
mirror_sev->active = true; mirror_sev->active = true;
mirror_sev->asid = source_sev.asid; mirror_sev->asid = source_sev->asid;
mirror_sev->fd = source_sev.fd; mirror_sev->fd = source_sev->fd;
mirror_sev->es_active = source_sev.es_active; mirror_sev->es_active = source_sev->es_active;
mirror_sev->handle = source_sev.handle; mirror_sev->handle = source_sev->handle;
INIT_LIST_HEAD(&mirror_sev->regions_list); INIT_LIST_HEAD(&mirror_sev->regions_list);
ret = 0;
/* /*
* Do not copy ap_jump_table. Since the mirror does not share the same * Do not copy ap_jump_table. Since the mirror does not share the same
* KVM contexts as the original, and they may have different * KVM contexts as the original, and they may have different
* memory-views. * memory-views.
*/ */
mutex_unlock(&kvm->lock); e_unlock:
return 0; sev_unlock_two_vms(kvm, source_kvm);
e_source_fput:
e_mirror_unlock:
mutex_unlock(&kvm->lock);
kvm_put_kvm(source_kvm);
return ret;
e_source_unlock:
mutex_unlock(&source_kvm->lock);
e_source_put:
if (source_kvm_file) if (source_kvm_file)
fput(source_kvm_file); fput(source_kvm_file);
return ret; return ret;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment