diff mbox series

[5/6] kvm/i386: Add support for user space MSR filtering

Message ID 4c7ecaab0295e8420ee03baf37c7722e01bb81ce.1621885749.git.sidcha@amazon.de (mailing list archive)
State New, archived
Headers show
Series Handle hypercall code overlay page in userspace | expand

Commit Message

Siddharth Chandrasekaran May 24, 2021, 8:01 p.m. UTC
Check and enable user space MSR filtering capability and handle new exit
reason KVM_EXIT_X86_WRMSR. This will be used in a follow up patch to
implement hyper-v overlay pages.

Signed-off-by: Siddharth Chandrasekaran <sidcha@amazon.de>
---
 target/i386/kvm/kvm.c | 72 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 72 insertions(+)

Comments

Alexander Graf June 8, 2021, 8:48 a.m. UTC | #1
On 24.05.21 22:01, Siddharth Chandrasekaran wrote:
> Check and enable user space MSR filtering capability and handle new exit
> reason KVM_EXIT_X86_WRMSR. This will be used in a follow up patch to
> implement hyper-v overlay pages.
> 
> Signed-off-by: Siddharth Chandrasekaran <sidcha@amazon.de>

This patch will break bisection, because we're no longer handling the 
writes in kernel space after this, but we also don't have user space 
handling available yet, right? It might be better to move all logic in 
this patch that sets up the filter for Hyper-V MSRs into the next one.

> ---
>   target/i386/kvm/kvm.c | 72 +++++++++++++++++++++++++++++++++++++++++++
>   1 file changed, 72 insertions(+)
> 
> diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c
> index 362f04ab3f..3591f8cecc 100644
> --- a/target/i386/kvm/kvm.c
> +++ b/target/i386/kvm/kvm.c
> @@ -117,6 +117,8 @@ static bool has_msr_ucode_rev;
>   static bool has_msr_vmx_procbased_ctls2;
>   static bool has_msr_perf_capabs;
>   static bool has_msr_pkrs;
> +static bool has_msr_filtering;
> +static bool msr_filters_active;
>   
>   static uint32_t has_architectural_pmu_version;
>   static uint32_t num_architectural_pmu_gp_counters;
> @@ -2138,6 +2140,57 @@ static void register_smram_listener(Notifier *n, void *unused)
>                                    &smram_address_space, 1);
>   }
>   
> +static void kvm_set_msr_filter_range(struct kvm_msr_filter_range *range, uint32_t flags,
> +                                     uint32_t base, uint32_t nmsrs, ...)
> +{
> +    int i, filter_to_userspace;
> +    va_list ap;
> +
> +    range->flags = flags;
> +    range->nmsrs = nmsrs;
> +    range->base = base;
> +
> +    va_start(ap, nmsrs);
> +    for (i = 0; i < nmsrs; i++) {
> +        filter_to_userspace = va_arg(ap, int);
> +        if (!filter_to_userspace) {
> +            range->bitmap[i / 8] = 1 << (i % 8);
> +        }
> +    }
> +    va_end(ap);
> +}
> +
> +static int kvm_set_msr_filters(KVMState *s)
> +{
> +    int r, nmsrs, nfilt = 0, bitmap_pos = 0;
> +    struct kvm_msr_filter filter = { };
> +    struct kvm_msr_filter_range *range;
> +    uint8_t bitmap_buf[KVM_MSR_FILTER_MAX_RANGES * 8] = {0};
> +
> +    filter.flags = KVM_MSR_FILTER_DEFAULT_ALLOW;
> +
> +    if (has_hyperv) {
> +        /* Hyper-V overlay page MSRs */

I think you want to extend this comment and indicate in a human readable 
form that you set the filter on WRMSR to trap HV_X64_MSR_GUEST_OS_ID and 
HV_X64_MSR_HYPERCALL into user space here.

> +        nmsrs = 2;
> +        range = &filter.ranges[nfilt++];
> +        range->bitmap = &bitmap_buf[bitmap_pos];
> +        kvm_set_msr_filter_range(range, KVM_MSR_FILTER_WRITE,
> +                                 HV_X64_MSR_GUEST_OS_ID, nmsrs,
> +                                 true, /* HV_X64_MSR_GUEST_OS_ID */
> +                                 true  /* HV_X64_MSR_HYPERCALL */);
> +        bitmap_pos += ROUND_UP(nmsrs, 8) / 8;
> +        assert(bitmap_pos < sizeof(bitmap_buf));
> +    }
> +
> +    r = kvm_vm_ioctl(s, KVM_X86_SET_MSR_FILTER, &filter);
> +    if (r != 0) {
> +        error_report("kvm: failed to set MSR filters");
> +        return -1;
> +    }
> +
> +    return 0;
> +}
> +
>   int kvm_arch_init(MachineState *ms, KVMState *s)
>   {
>       uint64_t identity_base = 0xfffbc000;
> @@ -2269,6 +2322,17 @@ int kvm_arch_init(MachineState *ms, KVMState *s)
>           }
>       }
>   
> +    has_msr_filtering = kvm_check_extension(s, KVM_CAP_X86_USER_SPACE_MSR) &&
> +                        kvm_check_extension(s, KVM_CAP_X86_MSR_FILTER);
> +    if (has_msr_filtering) {
> +        ret = kvm_vm_enable_cap(s, KVM_CAP_X86_USER_SPACE_MSR, 0,
> +                                KVM_MSR_EXIT_REASON_FILTER);
> +        if (ret == 0) {
> +            ret = kvm_set_msr_filters(s);
> +            msr_filters_active = (ret == 0);
> +        }
> +    }
> +
>       return 0;
>   }
>   
> @@ -4542,6 +4606,11 @@ static bool host_supports_vmx(void)
>       return ecx & CPUID_EXT_VMX;
>   }
>   
> +static int kvm_handle_wrmsr(X86CPU *cpu, struct kvm_run *run)
> +{
> +    return 0;

The default handler should always set run->msr.error = 1 to mimic the 
existing behavior.

> +}
> +
>   #define VMX_INVALID_GUEST_STATE 0x80000021
>   
>   int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
> @@ -4600,6 +4669,9 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
>           ioapic_eoi_broadcast(run->eoi.vector);
>           ret = 0;
>           break;
> +    case KVM_EXIT_X86_WRMSR:
> +        ret = kvm_handle_wrmsr(cpu, run);

Please provide a default RDMSR handler as well here.


Alex

> +        break;
>       default:
>           fprintf(stderr, "KVM: unknown exit reason %d\n", run->exit_reason);
>           ret = -1;
> 



Amazon Development Center Germany GmbH
Krausenstr. 38
10117 Berlin
Geschaeftsfuehrung: Christian Schlaeger, Jonathan Weiss
Eingetragen am Amtsgericht Charlottenburg unter HRB 149173 B
Sitz: Berlin
Ust-ID: DE 289 237 879
Siddharth Chandrasekaran June 8, 2021, 10:53 a.m. UTC | #2
On Tue, Jun 08, 2021 at 10:48:53AM +0200, Alexander Graf wrote:
> On 24.05.21 22:01, Siddharth Chandrasekaran wrote:
> > Check and enable user space MSR filtering capability and handle new exit
> > reason KVM_EXIT_X86_WRMSR. This will be used in a follow up patch to
> > implement hyper-v overlay pages.
> > 
> > Signed-off-by: Siddharth Chandrasekaran <sidcha@amazon.de>
> 
> This patch will break bisection, because we're no longer handling the writes
> in kernel space after this, but we also don't have user space handling
> available yet, right? It might be better to move all logic in this patch
> that sets up the filter for Hyper-V MSRs into the next one.

Yes, that's correct. I'll just bounce back all reads/writes to KVM. That
should maintain the existing behaviour.

> > ---
> >   target/i386/kvm/kvm.c | 72 +++++++++++++++++++++++++++++++++++++++++++
> >   1 file changed, 72 insertions(+)
> > 
> > diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c
> > index 362f04ab3f..3591f8cecc 100644
> > --- a/target/i386/kvm/kvm.c
> > +++ b/target/i386/kvm/kvm.c
> > @@ -117,6 +117,8 @@ static bool has_msr_ucode_rev;
> >   static bool has_msr_vmx_procbased_ctls2;
> >   static bool has_msr_perf_capabs;
> >   static bool has_msr_pkrs;
> > +static bool has_msr_filtering;
> > +static bool msr_filters_active;
> >   static uint32_t has_architectural_pmu_version;
> >   static uint32_t num_architectural_pmu_gp_counters;
> > @@ -2138,6 +2140,57 @@ static void register_smram_listener(Notifier *n, void *unused)
> >                                    &smram_address_space, 1);
> >   }
> > +static void kvm_set_msr_filter_range(struct kvm_msr_filter_range *range, uint32_t flags,
> > +                                     uint32_t base, uint32_t nmsrs, ...)
> > +{
> > +    int i, filter_to_userspace;
> > +    va_list ap;
> > +
> > +    range->flags = flags;
> > +    range->nmsrs = nmsrs;
> > +    range->base = base;
> > +
> > +    va_start(ap, nmsrs);
> > +    for (i = 0; i < nmsrs; i++) {
> > +        filter_to_userspace = va_arg(ap, int);
> > +        if (!filter_to_userspace) {
> > +            range->bitmap[i / 8] = 1 << (i % 8);
> > +        }
> > +    }
> > +    va_end(ap);
> > +}
> > +
> > +static int kvm_set_msr_filters(KVMState *s)
> > +{
> > +    int r, nmsrs, nfilt = 0, bitmap_pos = 0;
> > +    struct kvm_msr_filter filter = { };
> > +    struct kvm_msr_filter_range *range;
> > +    uint8_t bitmap_buf[KVM_MSR_FILTER_MAX_RANGES * 8] = {0};
> > +
> > +    filter.flags = KVM_MSR_FILTER_DEFAULT_ALLOW;
> > +
> > +    if (has_hyperv) {
> > +        /* Hyper-V overlay page MSRs */
> 
> I think you want to extend this comment and indicate in a human readable
> form that you set the filter on WRMSR to trap HV_X64_MSR_GUEST_OS_ID and
> HV_X64_MSR_HYPERCALL into user space here.

Sure.

> > +        nmsrs = 2;
> > +        range = &filter.ranges[nfilt++];
> > +        range->bitmap = &bitmap_buf[bitmap_pos];
> > +        kvm_set_msr_filter_range(range, KVM_MSR_FILTER_WRITE,
> > +                                 HV_X64_MSR_GUEST_OS_ID, nmsrs,
> > +                                 true, /* HV_X64_MSR_GUEST_OS_ID */
> > +                                 true  /* HV_X64_MSR_HYPERCALL */);
> > +        bitmap_pos += ROUND_UP(nmsrs, 8) / 8;
> > +        assert(bitmap_pos < sizeof(bitmap_buf));
> > +    }
> > +
> > +    r = kvm_vm_ioctl(s, KVM_X86_SET_MSR_FILTER, &filter);
> > +    if (r != 0) {
> > +        error_report("kvm: failed to set MSR filters");
> > +        return -1;
> > +    }
> > +
> > +    return 0;
> > +}
> > +
> >   int kvm_arch_init(MachineState *ms, KVMState *s)
> >   {
> >       uint64_t identity_base = 0xfffbc000;
> > @@ -2269,6 +2322,17 @@ int kvm_arch_init(MachineState *ms, KVMState *s)
> >           }
> >       }
> > +    has_msr_filtering = kvm_check_extension(s, KVM_CAP_X86_USER_SPACE_MSR) &&
> > +                        kvm_check_extension(s, KVM_CAP_X86_MSR_FILTER);
> > +    if (has_msr_filtering) {
> > +        ret = kvm_vm_enable_cap(s, KVM_CAP_X86_USER_SPACE_MSR, 0,
> > +                                KVM_MSR_EXIT_REASON_FILTER);
> > +        if (ret == 0) {
> > +            ret = kvm_set_msr_filters(s);
> > +            msr_filters_active = (ret == 0);
> > +        }
> > +    }
> > +
> >       return 0;
> >   }
> > @@ -4542,6 +4606,11 @@ static bool host_supports_vmx(void)
> >       return ecx & CPUID_EXT_VMX;
> >   }
> > +static int kvm_handle_wrmsr(X86CPU *cpu, struct kvm_run *run)
> > +{
> > +    return 0;
> 
> The default handler should always set run->msr.error = 1 to mimic the
> existing behavior.

Will do, thanks.

> > +}
> > +
> >   #define VMX_INVALID_GUEST_STATE 0x80000021
> >   int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
> > @@ -4600,6 +4669,9 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
> >           ioapic_eoi_broadcast(run->eoi.vector);
> >           ret = 0;
> >           break;
> > +    case KVM_EXIT_X86_WRMSR:
> > +        ret = kvm_handle_wrmsr(cpu, run);
> 
> Please provide a default RDMSR handler as well here.

Ack.

~ Sid.



Amazon Development Center Germany GmbH
Krausenstr. 38
10117 Berlin
Geschaeftsfuehrung: Christian Schlaeger, Jonathan Weiss
Eingetragen am Amtsgericht Charlottenburg unter HRB 149173 B
Sitz: Berlin
Ust-ID: DE 289 237 879
Siddharth Chandrasekaran June 25, 2021, 10:35 a.m. UTC | #3
On Tue, Jun 08, 2021 at 12:53:17PM +0200, Siddharth Chandrasekaran wrote:
> On Tue, Jun 08, 2021 at 10:48:53AM +0200, Alexander Graf wrote:
> > On 24.05.21 22:01, Siddharth Chandrasekaran wrote:
> > > Check and enable user space MSR filtering capability and handle new exit
> > > reason KVM_EXIT_X86_WRMSR. This will be used in a follow up patch to
> > > implement hyper-v overlay pages.
> > > 
> > > Signed-off-by: Siddharth Chandrasekaran <sidcha@amazon.de>
> > 
> > This patch will break bisection, because we're no longer handling the writes
> > in kernel space after this, but we also don't have user space handling
> > available yet, right? It might be better to move all logic in this patch
> > that sets up the filter for Hyper-V MSRs into the next one.
> 
> Yes, that's correct. I'll just bounce back all reads/writes to KVM. That
> should maintain the existing behaviour.

Okay, bouncing back to KVM was a bad idea :). Moved filters to next
patch as suggested.

~ Sid.



Amazon Development Center Germany GmbH
Krausenstr. 38
10117 Berlin
Geschaeftsfuehrung: Christian Schlaeger, Jonathan Weiss
Eingetragen am Amtsgericht Charlottenburg unter HRB 149173 B
Sitz: Berlin
Ust-ID: DE 289 237 879
diff mbox series

Patch

diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c
index 362f04ab3f..3591f8cecc 100644
--- a/target/i386/kvm/kvm.c
+++ b/target/i386/kvm/kvm.c
@@ -117,6 +117,8 @@  static bool has_msr_ucode_rev;
 static bool has_msr_vmx_procbased_ctls2;
 static bool has_msr_perf_capabs;
 static bool has_msr_pkrs;
+static bool has_msr_filtering;
+static bool msr_filters_active;
 
 static uint32_t has_architectural_pmu_version;
 static uint32_t num_architectural_pmu_gp_counters;
@@ -2138,6 +2140,57 @@  static void register_smram_listener(Notifier *n, void *unused)
                                  &smram_address_space, 1);
 }
 
+static void kvm_set_msr_filter_range(struct kvm_msr_filter_range *range, uint32_t flags,
+                                     uint32_t base, uint32_t nmsrs, ...)
+{
+    int i, filter_to_userspace;
+    va_list ap;
+
+    range->flags = flags;
+    range->nmsrs = nmsrs;
+    range->base = base;
+
+    va_start(ap, nmsrs);
+    for (i = 0; i < nmsrs; i++) {
+        filter_to_userspace = va_arg(ap, int);
+        if (!filter_to_userspace) {
+            range->bitmap[i / 8] = 1 << (i % 8);
+        }
+    }
+    va_end(ap);
+}
+
+static int kvm_set_msr_filters(KVMState *s)
+{
+    int r, nmsrs, nfilt = 0, bitmap_pos = 0;
+    struct kvm_msr_filter filter = { };
+    struct kvm_msr_filter_range *range;
+    uint8_t bitmap_buf[KVM_MSR_FILTER_MAX_RANGES * 8] = {0};
+
+    filter.flags = KVM_MSR_FILTER_DEFAULT_ALLOW;
+
+    if (has_hyperv) {
+        /* Hyper-V overlay page MSRs */
+        nmsrs = 2;
+        range = &filter.ranges[nfilt++];
+        range->bitmap = &bitmap_buf[bitmap_pos];
+        kvm_set_msr_filter_range(range, KVM_MSR_FILTER_WRITE,
+                                 HV_X64_MSR_GUEST_OS_ID, nmsrs,
+                                 true, /* HV_X64_MSR_GUEST_OS_ID */
+                                 true  /* HV_X64_MSR_HYPERCALL */);
+        bitmap_pos += ROUND_UP(nmsrs, 8) / 8;
+        assert(bitmap_pos < sizeof(bitmap_buf));
+    }
+
+    r = kvm_vm_ioctl(s, KVM_X86_SET_MSR_FILTER, &filter);
+    if (r != 0) {
+        error_report("kvm: failed to set MSR filters");
+        return -1;
+    }
+
+    return 0;
+}
+
 int kvm_arch_init(MachineState *ms, KVMState *s)
 {
     uint64_t identity_base = 0xfffbc000;
@@ -2269,6 +2322,17 @@  int kvm_arch_init(MachineState *ms, KVMState *s)
         }
     }
 
+    has_msr_filtering = kvm_check_extension(s, KVM_CAP_X86_USER_SPACE_MSR) &&
+                        kvm_check_extension(s, KVM_CAP_X86_MSR_FILTER);
+    if (has_msr_filtering) {
+        ret = kvm_vm_enable_cap(s, KVM_CAP_X86_USER_SPACE_MSR, 0,
+                                KVM_MSR_EXIT_REASON_FILTER);
+        if (ret == 0) {
+            ret = kvm_set_msr_filters(s);
+            msr_filters_active = (ret == 0);
+        }
+    }
+
     return 0;
 }
 
@@ -4542,6 +4606,11 @@  static bool host_supports_vmx(void)
     return ecx & CPUID_EXT_VMX;
 }
 
+static int kvm_handle_wrmsr(X86CPU *cpu, struct kvm_run *run)
+{
+    return 0;
+}
+
 #define VMX_INVALID_GUEST_STATE 0x80000021
 
 int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
@@ -4600,6 +4669,9 @@  int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
         ioapic_eoi_broadcast(run->eoi.vector);
         ret = 0;
         break;
+    case KVM_EXIT_X86_WRMSR:
+        ret = kvm_handle_wrmsr(cpu, run);
+        break;
     default:
         fprintf(stderr, "KVM: unknown exit reason %d\n", run->exit_reason);
         ret = -1;