diff mbox series

[v4,14/14] kvm: Add support for userspace MSR filtering and handling of MSR_KVM_MIGRATION_CONTROL.

Message ID 67935c3fd5f29a2ba9d67a91255276d3b9ccc99b.1628076205.git.ashish.kalra@amd.com
State New
Headers show
Series Add SEV guest live migration support | expand

Commit Message

Ashish Kalra Aug. 4, 2021, noon UTC
From: Ashish Kalra <ashish.kalra@amd.com>

Add support for userspace MSR filtering using KVM_X86_SET_MSR_FILTER
ioctl and handling of MSRs in userspace. Currently this is only used
for SEV guests which use MSR_KVM_MIGRATION_CONTROL to indicate if the
guest is enabled and ready for migration.

KVM arch code calls into SEV guest specific code to delete the
SEV migrate blocker which has been setup at SEV_LAUNCH_FINISH.

Signed-off-by: Ashish Kalra <ashish.kalra@amd.com>
---
 include/sysemu/sev.h  |  1 +
 target/i386/kvm/kvm.c | 61 +++++++++++++++++++++++++++++++++++++++++++
 target/i386/sev.c     |  6 +++++
 3 files changed, 68 insertions(+)

Comments

Wang, Wei W Sept. 10, 2021, 7:56 a.m. UTC | #1
On Wednesday, August 4, 2021 8:00 PM, Ashish Kalra wrote:
> +/*
> + * Currently this exit is only used by SEV guests for
> + * MSR_KVM_MIGRATION_CONTROL to indicate if the guest
> + * is ready for migration.
> + */
> +static int kvm_handle_x86_msr(X86CPU *cpu, struct kvm_run *run) {
> +    static uint64_t msr_kvm_migration_control;
> +
> +    if (run->msr.index != MSR_KVM_MIGRATION_CONTROL) {
> +        run->msr.error = -EINVAL;
> +        return -1;
> +    }
> +
> +    switch (run->exit_reason) {
> +    case KVM_EXIT_X86_RDMSR:
> +        run->msr.error = 0;
> +        run->msr.data = msr_kvm_migration_control;
> +        break;
> +    case KVM_EXIT_X86_WRMSR:
> +        msr_kvm_migration_control = run->msr.data;
> +        if (run->msr.data == KVM_MIGRATION_READY) {
> +            sev_del_migrate_blocker();

It seems this is enabled/disabled by the guest, which means that the guest can always refuse to be migrated?

Best,
Wei
Ashish Kalra Sept. 10, 2021, 9:14 a.m. UTC | #2
On Fri, Sep 10, 2021 at 07:56:36AM +0000, Wang, Wei W wrote:
> On Wednesday, August 4, 2021 8:00 PM, Ashish Kalra wrote:
> > +/*
> > + * Currently this exit is only used by SEV guests for
> > + * MSR_KVM_MIGRATION_CONTROL to indicate if the guest
> > + * is ready for migration.
> > + */
> > +static int kvm_handle_x86_msr(X86CPU *cpu, struct kvm_run *run) {
> > +    static uint64_t msr_kvm_migration_control;
> > +
> > +    if (run->msr.index != MSR_KVM_MIGRATION_CONTROL) {
> > +        run->msr.error = -EINVAL;
> > +        return -1;
> > +    }
> > +
> > +    switch (run->exit_reason) {
> > +    case KVM_EXIT_X86_RDMSR:
> > +        run->msr.error = 0;
> > +        run->msr.data = msr_kvm_migration_control;
> > +        break;
> > +    case KVM_EXIT_X86_WRMSR:
> > +        msr_kvm_migration_control = run->msr.data;
> > +        if (run->msr.data == KVM_MIGRATION_READY) {
> > +            sev_del_migrate_blocker();
> 
> It seems this is enabled/disabled by the guest, which means that the guest can always refuse to be migrated?
> 

Yes.

Are there any specific concerns/issues with that ?

Thanks,
Ashish
Wang, Wei W Sept. 10, 2021, 9:36 a.m. UTC | #3
On Friday, September 10, 2021 5:14 PM, Ashish Kalra wrote:
> > It seems this is enabled/disabled by the guest, which means that the guest
> can always refuse to be migrated?
> >
> 
> Yes.
> 
> Are there any specific concerns/issues with that ?

It's kind of wired if everybody rejects to migrate from the guest
but they ticked the option "agree to be migrated" when they buy the guest
(e.g. at a discounted price).

In general, I think the migration decision should be made by the management s/w, not the migratable guest.

Best,
Wei
diff mbox series

Patch

diff --git a/include/sysemu/sev.h b/include/sysemu/sev.h
index 023e694ac4..d04890113c 100644
--- a/include/sysemu/sev.h
+++ b/include/sysemu/sev.h
@@ -38,5 +38,6 @@  int sev_add_shared_regions_list(unsigned long gfn_start, unsigned long gfn_end);
 int sev_save_outgoing_shared_regions_list(QEMUFile *f);
 int sev_load_incoming_shared_regions_list(QEMUFile *f);
 bool sev_is_gfn_in_unshared_region(unsigned long gfn);
+void sev_del_migrate_blocker(void);
 
 #endif
diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c
index 303722e06f..785b8fae6b 100644
--- a/target/i386/kvm/kvm.c
+++ b/target/i386/kvm/kvm.c
@@ -2240,6 +2240,19 @@  static void register_smram_listener(Notifier *n, void *unused)
                                  &smram_address_space, 1);
 }
 
+static __u64 bitmap;
+struct kvm_msr_filter msr_filter_allow = {
+    .flags = KVM_MSR_FILTER_DEFAULT_ALLOW,
+    .ranges = {
+        {
+            .flags = KVM_MSR_FILTER_READ | KVM_MSR_FILTER_WRITE,
+            .nmsrs = 1,
+            .base = MSR_KVM_MIGRATION_CONTROL,
+            .bitmap = (uint8_t *)&bitmap,
+        }
+    }
+};
+
 int kvm_arch_init(MachineState *ms, KVMState *s)
 {
     uint64_t identity_base = 0xfffbc000;
@@ -2298,6 +2311,21 @@  int kvm_arch_init(MachineState *ms, KVMState *s)
         }
     }
 
+    ret = kvm_check_extension(s, KVM_CAP_X86_USER_SPACE_MSR) ?
+          kvm_check_extension(s, KVM_CAP_X86_MSR_FILTER) :
+          -ENOTSUP;
+    if (ret > 0) {
+        ret = kvm_vm_enable_cap(s, KVM_CAP_X86_USER_SPACE_MSR,
+                                0, KVM_MSR_EXIT_REASON_FILTER);
+        if (ret == 0) {
+            ret = kvm_vm_ioctl(s, KVM_X86_SET_MSR_FILTER, &msr_filter_allow);
+            if (ret < 0) {
+                error_report("kvm: KVM_X86_SET_MSR_FILTER failed : %s",
+                             strerror(-ret));
+            }
+        }
+    }
+
     ret = kvm_get_supported_msrs(s);
     if (ret < 0) {
         return ret;
@@ -4472,6 +4500,35 @@  static int kvm_handle_exit_hypercall(X86CPU *cpu, struct kvm_run *run)
     return 0;
 }
 
+/*
+ * Currently this exit is only used by SEV guests for
+ * MSR_KVM_MIGRATION_CONTROL to indicate if the guest
+ * is ready for migration.
+ */
+static int kvm_handle_x86_msr(X86CPU *cpu, struct kvm_run *run)
+{
+    static uint64_t msr_kvm_migration_control;
+
+    if (run->msr.index != MSR_KVM_MIGRATION_CONTROL) {
+        run->msr.error = -EINVAL;
+        return -1;
+    }
+
+    switch (run->exit_reason) {
+    case KVM_EXIT_X86_RDMSR:
+        run->msr.error = 0;
+        run->msr.data = msr_kvm_migration_control;
+        break;
+    case KVM_EXIT_X86_WRMSR:
+        msr_kvm_migration_control = run->msr.data;
+        if (run->msr.data == KVM_MIGRATION_READY) {
+            sev_del_migrate_blocker();
+        }
+        run->msr.error = 0;
+    }
+    return 0;
+}
+
 int kvm_arch_insert_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp)
 {
     static const uint8_t int3 = 0xcc;
@@ -4736,6 +4793,10 @@  int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
     case KVM_EXIT_HYPERCALL:
         ret = kvm_handle_exit_hypercall(cpu, run);
         break;
+    case KVM_EXIT_X86_RDMSR:
+    case KVM_EXIT_X86_WRMSR:
+        ret = kvm_handle_x86_msr(cpu, run);
+        break;
     default:
         fprintf(stderr, "KVM: unknown exit reason %d\n", run->exit_reason);
         ret = -1;
diff --git a/target/i386/sev.c b/target/i386/sev.c
index d22f2ef6dc..58f74db0e3 100644
--- a/target/i386/sev.c
+++ b/target/i386/sev.c
@@ -791,6 +791,12 @@  sev_launch_finish(SevGuestState *sev)
     }
 }
 
+void
+sev_del_migrate_blocker(void)
+{
+    migrate_del_blocker(sev_mig_blocker);
+}
+
 static int
 sev_receive_finish(SevGuestState *s)
 {