diff mbox series

[RFC,v2,04/22] i386/kvm: handle Xen HVM cpuid leaves

Message ID 20221209095612.689243-5-dwmw2@infradead.org
State New
Headers show
Series Xen HVM support under KVM | expand

Commit Message

David Woodhouse Dec. 9, 2022, 9:55 a.m. UTC
From: Joao Martins <joao.m.martins@oracle.com>

Introduce support for emulating CPUID for Xen HVM guests. It doesn't make
sense to advertise the KVM leaves to a Xen guest, so do it unconditionally
when the xen-version machine property is set.

Signed-off-by: Joao Martins <joao.m.martins@oracle.com>
[dwmw2: Obtain xen_version from machine property, make it automatic]
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
---
 target/i386/cpu.c     |  1 +
 target/i386/cpu.h     |  2 ++
 target/i386/kvm/kvm.c | 72 +++++++++++++++++++++++++++++++++++++++++--
 target/i386/xen.h     |  8 +++++
 4 files changed, 80 insertions(+), 3 deletions(-)

Comments

Paul Durrant Dec. 12, 2022, 1:13 p.m. UTC | #1
On 09/12/2022 09:55, David Woodhouse wrote:
> From: Joao Martins <joao.m.martins@oracle.com>
> 
> Introduce support for emulating CPUID for Xen HVM guests. It doesn't make
> sense to advertise the KVM leaves to a Xen guest, so do it unconditionally
> when the xen-version machine property is set.
> 
> Signed-off-by: Joao Martins <joao.m.martins@oracle.com>
> [dwmw2: Obtain xen_version from machine property, make it automatic]
> Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
[snip]
> -    if (cpu->expose_kvm) {
> +    xen_version = kvm_arch_xen_version(MACHINE(qdev_get_machine()));
> +    if (xen_version) {
> +#ifdef CONFIG_XEN_EMU
> +        struct kvm_cpuid_entry2 *xen_max_leaf;
> +
> +        memcpy(signature, "XenVMMXenVMM", 12);
> +
> +        xen_max_leaf = c = &cpuid_data.entries[cpuid_i++];
> +        c->function = kvm_base + XEN_CPUID_SIGNATURE;
> +        c->eax = kvm_base + XEN_CPUID_TIME;
> +        c->ebx = signature[0];
> +        c->ecx = signature[1];
> +        c->edx = signature[2];
> +
> +        c = &cpuid_data.entries[cpuid_i++];
> +        c->function = kvm_base + XEN_CPUID_VENDOR;
> +        c->eax = xen_version;
> +        c->ebx = 0;
> +        c->ecx = 0;
> +        c->edx = 0;
> +
> +        c = &cpuid_data.entries[cpuid_i++];
> +        c->function = kvm_base + XEN_CPUID_HVM_MSR;
> +        /* Number of hypercall-transfer pages */
> +        c->eax = 1;
> +        /* Hypercall MSR base address */
> +        c->ebx = XEN_HYPERCALL_MSR;
> +        c->ecx = 0;
> +        c->edx = 0;
> +
> +        c = &cpuid_data.entries[cpuid_i++];
> +        c->function = kvm_base + XEN_CPUID_TIME;
> +        c->eax = ((!!tsc_is_stable_and_known(env) << 1) |
> +            (!!(env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_RDTSCP) << 2));
> +        /* default=0 (emulate if necessary) */
> +        c->ebx = 0;
> +        /* guest tsc frequency */
> +        c->ecx = env->user_tsc_khz;
> +        /* guest tsc incarnation (migration count) */
> +        c->edx = 0;
> +
> +        c = &cpuid_data.entries[cpuid_i++];
> +        c->function = kvm_base + XEN_CPUID_HVM;
> +        xen_max_leaf->eax = kvm_base + XEN_CPUID_HVM;
> +        if (xen_version >= XEN_VERSION(4,5)) {
> +            c->function = kvm_base + XEN_CPUID_HVM;
> +
> +            if (cpu->xen_vapic) {
> +                c->eax |= XEN_HVM_CPUID_APIC_ACCESS_VIRT;
> +                c->eax |= XEN_HVM_CPUID_X2APIC_VIRT;
> +            }
> +
> +            c->eax |= XEN_HVM_CPUID_IOMMU_MAPPINGS;
> +
> +            if (xen_version >= XEN_VERSION(4,6)) {
> +                c->eax |= XEN_HVM_CPUID_VCPU_ID_PRESENT;
> +                c->ebx = cs->cpu_index;
> +            }
> +        }
> +
> +        kvm_base += 0x100;

Ok, this tells me that we are intending to handle Hyper-V enlightenments 
being simultaneously enabled... in which case that MSR above needs to 
move, along with the cpuid leaves. It should be 0x40000200 in this case.

   Paul
David Woodhouse Dec. 13, 2022, 9:47 a.m. UTC | #2
On Mon, 2022-12-12 at 13:13 +0000, Paul Durrant wrote:
> Ok, this tells me that we are intending to handle Hyper-V enlightenments 
> being simultaneously enabled... in which case that MSR above needs to 
> move, along with the cpuid leaves. It should be 0x40000200 in this case.

Ah, yes. That gets entertaining because hyperv_enabled() doesn't work
at the time kvm_arch_init() runs, because kvm_hyperv_expand_features()
hasn't run yet.

But kvm_arch_init() is idempotent so for now I'll just call it *again*
from kvm_arch_init_vcpu() to move the MSR in the hyperv case.
diff mbox series

Patch

diff --git a/target/i386/cpu.c b/target/i386/cpu.c
index 22b681ca37..50aa95f134 100644
--- a/target/i386/cpu.c
+++ b/target/i386/cpu.c
@@ -7069,6 +7069,7 @@  static Property x86_cpu_properties[] = {
      * own cache information (see x86_cpu_load_def()).
      */
     DEFINE_PROP_BOOL("legacy-cache", X86CPU, legacy_cache, true),
+    DEFINE_PROP_BOOL("xen-vapic", X86CPU, xen_vapic, false),
 
     /*
      * From "Requirements for Implementing the Microsoft
diff --git a/target/i386/cpu.h b/target/i386/cpu.h
index d4bc19577a..c6c57baed5 100644
--- a/target/i386/cpu.h
+++ b/target/i386/cpu.h
@@ -1964,6 +1964,8 @@  struct ArchCPU {
     int32_t thread_id;
 
     int32_t hv_max_vps;
+
+    bool xen_vapic;
 };
 
 
diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c
index 0a2069b117..0d3eddf9de 100644
--- a/target/i386/kvm/kvm.c
+++ b/target/i386/kvm/kvm.c
@@ -22,6 +22,7 @@ 
 
 #include <linux/kvm.h>
 #include "standard-headers/asm-x86/kvm_para.h"
+#include "standard-headers/xen/arch-x86/cpuid.h"
 
 #include "cpu.h"
 #include "host-cpu.h"
@@ -1752,14 +1753,13 @@  int kvm_arch_init_vcpu(CPUState *cs)
     X86CPU *cpu = X86_CPU(cs);
     CPUX86State *env = &cpu->env;
     uint32_t limit, i, j, cpuid_i;
-    uint32_t unused;
+    uint32_t unused, xen_version = 0;
     struct kvm_cpuid_entry2 *c;
     uint32_t signature[3];
     int kvm_base = KVM_CPUID_SIGNATURE;
     int max_nested_state_len;
     int r;
     Error *local_err = NULL;
-
     memset(&cpuid_data, 0, sizeof(cpuid_data));
 
     cpuid_i = 0;
@@ -1811,7 +1811,73 @@  int kvm_arch_init_vcpu(CPUState *cs)
         has_msr_hv_hypercall = true;
     }
 
-    if (cpu->expose_kvm) {
+    xen_version = kvm_arch_xen_version(MACHINE(qdev_get_machine()));
+    if (xen_version) {
+#ifdef CONFIG_XEN_EMU
+        struct kvm_cpuid_entry2 *xen_max_leaf;
+
+        memcpy(signature, "XenVMMXenVMM", 12);
+
+        xen_max_leaf = c = &cpuid_data.entries[cpuid_i++];
+        c->function = kvm_base + XEN_CPUID_SIGNATURE;
+        c->eax = kvm_base + XEN_CPUID_TIME;
+        c->ebx = signature[0];
+        c->ecx = signature[1];
+        c->edx = signature[2];
+
+        c = &cpuid_data.entries[cpuid_i++];
+        c->function = kvm_base + XEN_CPUID_VENDOR;
+        c->eax = xen_version;
+        c->ebx = 0;
+        c->ecx = 0;
+        c->edx = 0;
+
+        c = &cpuid_data.entries[cpuid_i++];
+        c->function = kvm_base + XEN_CPUID_HVM_MSR;
+        /* Number of hypercall-transfer pages */
+        c->eax = 1;
+        /* Hypercall MSR base address */
+        c->ebx = XEN_HYPERCALL_MSR;
+        c->ecx = 0;
+        c->edx = 0;
+
+        c = &cpuid_data.entries[cpuid_i++];
+        c->function = kvm_base + XEN_CPUID_TIME;
+        c->eax = ((!!tsc_is_stable_and_known(env) << 1) |
+            (!!(env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_RDTSCP) << 2));
+        /* default=0 (emulate if necessary) */
+        c->ebx = 0;
+        /* guest tsc frequency */
+        c->ecx = env->user_tsc_khz;
+        /* guest tsc incarnation (migration count) */
+        c->edx = 0;
+
+        c = &cpuid_data.entries[cpuid_i++];
+        c->function = kvm_base + XEN_CPUID_HVM;
+        xen_max_leaf->eax = kvm_base + XEN_CPUID_HVM;
+        if (xen_version >= XEN_VERSION(4,5)) {
+            c->function = kvm_base + XEN_CPUID_HVM;
+
+            if (cpu->xen_vapic) {
+                c->eax |= XEN_HVM_CPUID_APIC_ACCESS_VIRT;
+                c->eax |= XEN_HVM_CPUID_X2APIC_VIRT;
+            }
+
+            c->eax |= XEN_HVM_CPUID_IOMMU_MAPPINGS;
+
+            if (xen_version >= XEN_VERSION(4,6)) {
+                c->eax |= XEN_HVM_CPUID_VCPU_ID_PRESENT;
+                c->ebx = cs->cpu_index;
+            }
+        }
+
+        kvm_base += 0x100;
+#else /* CONFIG_XEN_EMU */
+        /* This should never happen as kvm_arch_init() would have died first. */
+        fprintf(stderr, "Cannot enable Xen CPUID without Xen support\n");
+        abort();
+#endif
+    } else if (cpu->expose_kvm) {
         memcpy(signature, "KVMKVMKVM\0\0\0", 12);
         c = &cpuid_data.entries[cpuid_i++];
         c->function = KVM_CPUID_SIGNATURE | kvm_base;
diff --git a/target/i386/xen.h b/target/i386/xen.h
index 6c4f3b7822..ae880c47bc 100644
--- a/target/i386/xen.h
+++ b/target/i386/xen.h
@@ -14,6 +14,14 @@ 
 
 #define XEN_HYPERCALL_MSR 0x40000000
 
+#define XEN_CPUID_SIGNATURE        0
+#define XEN_CPUID_VENDOR           1
+#define XEN_CPUID_HVM_MSR          2
+#define XEN_CPUID_TIME             3
+#define XEN_CPUID_HVM              4
+
+#define XEN_VERSION(maj, min) ((maj) << 16 | (min))
+
 int kvm_xen_init(KVMState *s, uint32_t xen_version);
 
 #endif /* QEMU_I386_XEN_H */