Skip to content

Commit b566393

Browse files
pgondabonzini
authored andcommitted
KVM: SEV: Add support for SEV intra host migration
For SEV to work with intra host migration, contents of the SEV info struct such as the ASID (used to index the encryption key in the AMD SP) and the list of memory regions need to be transferred to the target VM. This change adds a commands for a target VMM to get a source SEV VM's sev info. Signed-off-by: Peter Gonda <[email protected]> Suggested-by: Sean Christopherson <[email protected]> Reviewed-by: Marc Orr <[email protected]> Cc: Marc Orr <[email protected]> Cc: Paolo Bonzini <[email protected]> Cc: Sean Christopherson <[email protected]> Cc: David Rientjes <[email protected]> Cc: Dr. David Alan Gilbert <[email protected]> Cc: Brijesh Singh <[email protected]> Cc: Tom Lendacky <[email protected]> Cc: Vitaly Kuznetsov <[email protected]> Cc: Wanpeng Li <[email protected]> Cc: Jim Mattson <[email protected]> Cc: Joerg Roedel <[email protected]> Cc: Thomas Gleixner <[email protected]> Cc: Ingo Molnar <[email protected]> Cc: Borislav Petkov <[email protected]> Cc: "H. Peter Anvin" <[email protected]> Cc: [email protected] Cc: [email protected] Message-Id: <[email protected]> Signed-off-by: Paolo Bonzini <[email protected]>
1 parent 91b692a commit b566393

File tree

7 files changed

+177
-0
lines changed

7 files changed

+177
-0
lines changed

Documentation/virt/kvm/api.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6911,6 +6911,20 @@ MAP_SHARED mmap will result in an -EINVAL return.
69116911
When enabled the VMM may make use of the ``KVM_ARM_MTE_COPY_TAGS`` ioctl to
69126912
perform a bulk copy of tags to/from the guest.
69136913

6914+
7.29 KVM_CAP_VM_MOVE_ENC_CONTEXT_FROM
6915+
-------------------------------------
6916+
6917+
Architectures: x86 SEV enabled
6918+
Type: vm
6919+
Parameters: args[0] is the fd of the source vm
6920+
Returns: 0 on success
6921+
6922+
This capability enables userspace to migrate the encryption context from the VM
6923+
indicated by the fd to the VM this is called on.
6924+
6925+
This is intended to support intra-host migration of VMs between userspace VMMs,
6926+
upgrading the VMM process without interrupting the guest.
6927+
69146928
8. Other capabilities.
69156929
======================
69166930

arch/x86/include/asm/kvm_host.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1476,6 +1476,7 @@ struct kvm_x86_ops {
14761476
int (*mem_enc_reg_region)(struct kvm *kvm, struct kvm_enc_region *argp);
14771477
int (*mem_enc_unreg_region)(struct kvm *kvm, struct kvm_enc_region *argp);
14781478
int (*vm_copy_enc_context_from)(struct kvm *kvm, unsigned int source_fd);
1479+
int (*vm_move_enc_context_from)(struct kvm *kvm, unsigned int source_fd);
14791480

14801481
int (*get_msr_feature)(struct kvm_msr_entry *entry);
14811482

arch/x86/kvm/svm/sev.c

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1532,6 +1532,158 @@ static bool cmd_allowed_from_miror(u32 cmd_id)
15321532
return false;
15331533
}
15341534

1535+
static int sev_lock_for_migration(struct kvm *kvm)
1536+
{
1537+
struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
1538+
1539+
/*
1540+
* Bail if this VM is already involved in a migration to avoid deadlock
1541+
* between two VMs trying to migrate to/from each other.
1542+
*/
1543+
if (atomic_cmpxchg_acquire(&sev->migration_in_progress, 0, 1))
1544+
return -EBUSY;
1545+
1546+
mutex_lock(&kvm->lock);
1547+
1548+
return 0;
1549+
}
1550+
1551+
static void sev_unlock_after_migration(struct kvm *kvm)
1552+
{
1553+
struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
1554+
1555+
mutex_unlock(&kvm->lock);
1556+
atomic_set_release(&sev->migration_in_progress, 0);
1557+
}
1558+
1559+
1560+
static int sev_lock_vcpus_for_migration(struct kvm *kvm)
1561+
{
1562+
struct kvm_vcpu *vcpu;
1563+
int i, j;
1564+
1565+
kvm_for_each_vcpu(i, vcpu, kvm) {
1566+
if (mutex_lock_killable(&vcpu->mutex))
1567+
goto out_unlock;
1568+
}
1569+
1570+
return 0;
1571+
1572+
out_unlock:
1573+
kvm_for_each_vcpu(j, vcpu, kvm) {
1574+
if (i == j)
1575+
break;
1576+
1577+
mutex_unlock(&vcpu->mutex);
1578+
}
1579+
return -EINTR;
1580+
}
1581+
1582+
static void sev_unlock_vcpus_for_migration(struct kvm *kvm)
1583+
{
1584+
struct kvm_vcpu *vcpu;
1585+
int i;
1586+
1587+
kvm_for_each_vcpu(i, vcpu, kvm) {
1588+
mutex_unlock(&vcpu->mutex);
1589+
}
1590+
}
1591+
1592+
static void sev_migrate_from(struct kvm_sev_info *dst,
1593+
struct kvm_sev_info *src)
1594+
{
1595+
dst->active = true;
1596+
dst->asid = src->asid;
1597+
dst->handle = src->handle;
1598+
dst->pages_locked = src->pages_locked;
1599+
1600+
src->asid = 0;
1601+
src->active = false;
1602+
src->handle = 0;
1603+
src->pages_locked = 0;
1604+
1605+
if (dst->misc_cg != src->misc_cg)
1606+
sev_misc_cg_uncharge(src);
1607+
1608+
put_misc_cg(src->misc_cg);
1609+
src->misc_cg = NULL;
1610+
1611+
INIT_LIST_HEAD(&dst->regions_list);
1612+
list_replace_init(&src->regions_list, &dst->regions_list);
1613+
}
1614+
1615+
int svm_vm_migrate_from(struct kvm *kvm, unsigned int source_fd)
1616+
{
1617+
struct kvm_sev_info *dst_sev = &to_kvm_svm(kvm)->sev_info;
1618+
struct kvm_sev_info *src_sev;
1619+
struct file *source_kvm_file;
1620+
struct kvm *source_kvm;
1621+
int ret;
1622+
1623+
ret = sev_lock_for_migration(kvm);
1624+
if (ret)
1625+
return ret;
1626+
1627+
if (sev_guest(kvm)) {
1628+
ret = -EINVAL;
1629+
goto out_unlock;
1630+
}
1631+
1632+
source_kvm_file = fget(source_fd);
1633+
if (!file_is_kvm(source_kvm_file)) {
1634+
ret = -EBADF;
1635+
goto out_fput;
1636+
}
1637+
1638+
source_kvm = source_kvm_file->private_data;
1639+
ret = sev_lock_for_migration(source_kvm);
1640+
if (ret)
1641+
goto out_fput;
1642+
1643+
if (!sev_guest(source_kvm) || sev_es_guest(source_kvm)) {
1644+
ret = -EINVAL;
1645+
goto out_source;
1646+
}
1647+
1648+
src_sev = &to_kvm_svm(source_kvm)->sev_info;
1649+
dst_sev->misc_cg = get_current_misc_cg();
1650+
if (dst_sev->misc_cg != src_sev->misc_cg) {
1651+
ret = sev_misc_cg_try_charge(dst_sev);
1652+
if (ret)
1653+
goto out_dst_put_cgroup;
1654+
}
1655+
1656+
ret = sev_lock_vcpus_for_migration(kvm);
1657+
if (ret)
1658+
goto out_dst_cgroup;
1659+
ret = sev_lock_vcpus_for_migration(source_kvm);
1660+
if (ret)
1661+
goto out_dst_vcpu;
1662+
1663+
sev_migrate_from(dst_sev, src_sev);
1664+
kvm_vm_dead(source_kvm);
1665+
ret = 0;
1666+
1667+
sev_unlock_vcpus_for_migration(source_kvm);
1668+
out_dst_vcpu:
1669+
sev_unlock_vcpus_for_migration(kvm);
1670+
out_dst_cgroup:
1671+
if (ret < 0) {
1672+
sev_misc_cg_uncharge(dst_sev);
1673+
out_dst_put_cgroup:
1674+
put_misc_cg(dst_sev->misc_cg);
1675+
dst_sev->misc_cg = NULL;
1676+
}
1677+
out_source:
1678+
sev_unlock_after_migration(source_kvm);
1679+
out_fput:
1680+
if (source_kvm_file)
1681+
fput(source_kvm_file);
1682+
out_unlock:
1683+
sev_unlock_after_migration(kvm);
1684+
return ret;
1685+
}
1686+
15351687
int svm_mem_enc_op(struct kvm *kvm, void __user *argp)
15361688
{
15371689
struct kvm_sev_cmd sev_cmd;

arch/x86/kvm/svm/svm.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4699,6 +4699,7 @@ static struct kvm_x86_ops svm_x86_ops __initdata = {
46994699
.mem_enc_unreg_region = svm_unregister_enc_region,
47004700

47014701
.vm_copy_enc_context_from = svm_vm_copy_asid_from,
4702+
.vm_move_enc_context_from = svm_vm_migrate_from,
47024703

47034704
.can_emulate_instruction = svm_can_emulate_instruction,
47044705

arch/x86/kvm/svm/svm.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ struct kvm_sev_info {
8080
u64 ap_jump_table; /* SEV-ES AP Jump Table address */
8181
struct kvm *enc_context_owner; /* Owner of copied encryption context */
8282
struct misc_cg *misc_cg; /* For misc cgroup accounting */
83+
atomic_t migration_in_progress;
8384
};
8485

8586
struct kvm_svm {
@@ -562,6 +563,7 @@ int svm_register_enc_region(struct kvm *kvm,
562563
int svm_unregister_enc_region(struct kvm *kvm,
563564
struct kvm_enc_region *range);
564565
int svm_vm_copy_asid_from(struct kvm *kvm, unsigned int source_fd);
566+
int svm_vm_migrate_from(struct kvm *kvm, unsigned int source_fd);
565567
void pre_sev_run(struct vcpu_svm *svm, int cpu);
566568
void __init sev_set_cpu_caps(void);
567569
void __init sev_hardware_setup(void);

arch/x86/kvm/x86.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5845,6 +5845,12 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
58455845
if (kvm_x86_ops.vm_copy_enc_context_from)
58465846
r = kvm_x86_ops.vm_copy_enc_context_from(kvm, cap->args[0]);
58475847
return r;
5848+
case KVM_CAP_VM_MOVE_ENC_CONTEXT_FROM:
5849+
r = -EINVAL;
5850+
if (kvm_x86_ops.vm_move_enc_context_from)
5851+
r = kvm_x86_ops.vm_move_enc_context_from(
5852+
kvm, cap->args[0]);
5853+
return r;
58485854
case KVM_CAP_EXIT_HYPERCALL:
58495855
if (cap->args[0] & ~KVM_EXIT_HYPERCALL_VALID_MASK) {
58505856
r = -EINVAL;

include/uapi/linux/kvm.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1130,6 +1130,7 @@ struct kvm_ppc_resize_hpt {
11301130
#define KVM_CAP_BINARY_STATS_FD 203
11311131
#define KVM_CAP_EXIT_ON_EMULATION_FAILURE 204
11321132
#define KVM_CAP_ARM_MTE 205
1133+
#define KVM_CAP_VM_MOVE_ENC_CONTEXT_FROM 206
11331134

11341135
#ifdef KVM_CAP_IRQ_ROUTING
11351136

0 commit comments

Comments
 (0)