x86/kvm/hyper-v: Introduce KVM_GET_SUPPORTED_HV_CPUID
authorVitaly Kuznetsov <vkuznets@redhat.com>
Mon, 10 Dec 2018 17:21:56 +0000 (18:21 +0100)
committerPaolo Bonzini <pbonzini@redhat.com>
Fri, 14 Dec 2018 16:59:54 +0000 (17:59 +0100)
With every new Hyper-V Enlightenment we implement we're forced to add a
KVM_CAP_HYPERV_* capability. While this approach works it is fairly
inconvenient: the majority of the enlightenments we do have corresponding
CPUID feature bit(s) and userspace has to know this anyways to be able to
expose the feature to the guest.

Add KVM_GET_SUPPORTED_HV_CPUID ioctl (backed by KVM_CAP_HYPERV_CPUID, "one
cap to rule them all!") returning all Hyper-V CPUID feature leaves.

Using the existing KVM_GET_SUPPORTED_CPUID doesn't seem to be possible:
Hyper-V CPUID feature leaves intersect with KVM's (e.g. 0x40000000,
0x40000001) and we would probably confuse userspace in case we decide to
return these twice.

KVM_CAP_HYPERV_CPUID's number is interim: we're intended to drop
KVM_CAP_HYPERV_STIMER_DIRECT and use its number instead.

Suggested-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Documentation/virtual/kvm/api.txt
arch/x86/kvm/hyperv.c
arch/x86/kvm/hyperv.h
arch/x86/kvm/x86.c
include/uapi/linux/kvm.h

index f2c345f7b6307d21a44b8663cc0ddbf2de8a1497..356156f5c52d299b13481ccc516f15388c5797aa 100644 (file)
@@ -3800,6 +3800,62 @@ is enabled; for more information, see the description of the capability.
 However, it can always be used as long as KVM_CHECK_EXTENSION confirms
 that KVM_CAP_MANUAL_DIRTY_LOG_PROTECT is present.
 
+4.118 KVM_GET_SUPPORTED_HV_CPUID
+
+Capability: KVM_CAP_HYPERV_CPUID
+Architectures: x86
+Type: vcpu ioctl
+Parameters: struct kvm_cpuid2 (in/out)
+Returns: 0 on success, -1 on error
+
+struct kvm_cpuid2 {
+       __u32 nent;
+       __u32 padding;
+       struct kvm_cpuid_entry2 entries[0];
+};
+
+struct kvm_cpuid_entry2 {
+       __u32 function;
+       __u32 index;
+       __u32 flags;
+       __u32 eax;
+       __u32 ebx;
+       __u32 ecx;
+       __u32 edx;
+       __u32 padding[3];
+};
+
+This ioctl returns x86 cpuid features leaves related to Hyper-V emulation in
+KVM.  Userspace can use the information returned by this ioctl to construct
+cpuid information presented to guests consuming Hyper-V enlightenments (e.g.
+Windows or Hyper-V guests).
+
+CPUID feature leaves returned by this ioctl are defined by Hyper-V Top Level
+Functional Specification (TLFS). These leaves can't be obtained with
+KVM_GET_SUPPORTED_CPUID ioctl because some of them intersect with KVM feature
+leaves (0x40000000, 0x40000001).
+
+Currently, the following list of CPUID leaves are returned:
+ HYPERV_CPUID_VENDOR_AND_MAX_FUNCTIONS
+ HYPERV_CPUID_INTERFACE
+ HYPERV_CPUID_VERSION
+ HYPERV_CPUID_FEATURES
+ HYPERV_CPUID_ENLIGHTMENT_INFO
+ HYPERV_CPUID_IMPLEMENT_LIMITS
+ HYPERV_CPUID_NESTED_FEATURES
+
+HYPERV_CPUID_NESTED_FEATURES leaf is only exposed when Enlightened VMCS was
+enabled on the corresponding vCPU (KVM_CAP_HYPERV_ENLIGHTENED_VMCS).
+
+Userspace invokes KVM_GET_SUPPORTED_CPUID by passing a kvm_cpuid2 structure
+with the 'nent' field indicating the number of entries in the variable-size
+array 'entries'.  If the number of entries is too low to describe all Hyper-V
+feature leaves, an error (E2BIG) is returned. If the number is more or equal
+to the number of Hyper-V feature leaves, the 'nent' field is adjusted to the
+number of valid entries in the 'entries' array, which is then filled.
+
+'index' and 'flags' fields in 'struct kvm_cpuid_entry2' are currently reserved,
+userspace should not expect to get any particular value there.
 
 5. The kvm_run structure
 ------------------------
index a9b5d6c4e3eb85eae822b7c5c2f92dc114e0780b..e3c076020d4e8d43e975cf89dc5f4265fd390401 100644 (file)
@@ -1752,3 +1752,124 @@ int kvm_vm_ioctl_hv_eventfd(struct kvm *kvm, struct kvm_hyperv_eventfd *args)
                return kvm_hv_eventfd_deassign(kvm, args->conn_id);
        return kvm_hv_eventfd_assign(kvm, args->conn_id, args->fd);
 }
+
+int kvm_vcpu_ioctl_get_hv_cpuid(struct kvm_vcpu *vcpu, struct kvm_cpuid2 *cpuid,
+                               struct kvm_cpuid_entry2 __user *entries)
+{
+       uint16_t evmcs_ver = kvm_x86_ops->nested_get_evmcs_version(vcpu);
+       struct kvm_cpuid_entry2 cpuid_entries[] = {
+               { .function = HYPERV_CPUID_VENDOR_AND_MAX_FUNCTIONS },
+               { .function = HYPERV_CPUID_INTERFACE },
+               { .function = HYPERV_CPUID_VERSION },
+               { .function = HYPERV_CPUID_FEATURES },
+               { .function = HYPERV_CPUID_ENLIGHTMENT_INFO },
+               { .function = HYPERV_CPUID_IMPLEMENT_LIMITS },
+               { .function = HYPERV_CPUID_NESTED_FEATURES },
+       };
+       int i, nent = ARRAY_SIZE(cpuid_entries);
+
+       /* Skip NESTED_FEATURES if eVMCS is not supported */
+       if (!evmcs_ver)
+               --nent;
+
+       if (cpuid->nent < nent)
+               return -E2BIG;
+
+       if (cpuid->nent > nent)
+               cpuid->nent = nent;
+
+       for (i = 0; i < nent; i++) {
+               struct kvm_cpuid_entry2 *ent = &cpuid_entries[i];
+               u32 signature[3];
+
+               switch (ent->function) {
+               case HYPERV_CPUID_VENDOR_AND_MAX_FUNCTIONS:
+                       memcpy(signature, "Linux KVM Hv", 12);
+
+                       ent->eax = HYPERV_CPUID_NESTED_FEATURES;
+                       ent->ebx = signature[0];
+                       ent->ecx = signature[1];
+                       ent->edx = signature[2];
+                       break;
+
+               case HYPERV_CPUID_INTERFACE:
+                       memcpy(signature, "Hv#1\0\0\0\0\0\0\0\0", 12);
+                       ent->eax = signature[0];
+                       break;
+
+               case HYPERV_CPUID_VERSION:
+                       /*
+                        * We implement some Hyper-V 2016 functions so let's use
+                        * this version.
+                        */
+                       ent->eax = 0x00003839;
+                       ent->ebx = 0x000A0000;
+                       break;
+
+               case HYPERV_CPUID_FEATURES:
+                       ent->eax |= HV_X64_MSR_VP_RUNTIME_AVAILABLE;
+                       ent->eax |= HV_MSR_TIME_REF_COUNT_AVAILABLE;
+                       ent->eax |= HV_X64_MSR_SYNIC_AVAILABLE;
+                       ent->eax |= HV_MSR_SYNTIMER_AVAILABLE;
+                       ent->eax |= HV_X64_MSR_APIC_ACCESS_AVAILABLE;
+                       ent->eax |= HV_X64_MSR_HYPERCALL_AVAILABLE;
+                       ent->eax |= HV_X64_MSR_VP_INDEX_AVAILABLE;
+                       ent->eax |= HV_X64_MSR_RESET_AVAILABLE;
+                       ent->eax |= HV_MSR_REFERENCE_TSC_AVAILABLE;
+                       ent->eax |= HV_X64_MSR_GUEST_IDLE_AVAILABLE;
+                       ent->eax |= HV_X64_ACCESS_FREQUENCY_MSRS;
+                       ent->eax |= HV_X64_ACCESS_REENLIGHTENMENT;
+
+                       ent->ebx |= HV_X64_POST_MESSAGES;
+                       ent->ebx |= HV_X64_SIGNAL_EVENTS;
+
+                       ent->edx |= HV_FEATURE_FREQUENCY_MSRS_AVAILABLE;
+                       ent->edx |= HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE;
+                       ent->edx |= HV_STIMER_DIRECT_MODE_AVAILABLE;
+
+                       break;
+
+               case HYPERV_CPUID_ENLIGHTMENT_INFO:
+                       ent->eax |= HV_X64_REMOTE_TLB_FLUSH_RECOMMENDED;
+                       ent->eax |= HV_X64_APIC_ACCESS_RECOMMENDED;
+                       ent->eax |= HV_X64_SYSTEM_RESET_RECOMMENDED;
+                       ent->eax |= HV_X64_RELAXED_TIMING_RECOMMENDED;
+                       ent->eax |= HV_X64_CLUSTER_IPI_RECOMMENDED;
+                       ent->eax |= HV_X64_EX_PROCESSOR_MASKS_RECOMMENDED;
+                       ent->eax |= HV_X64_ENLIGHTENED_VMCS_RECOMMENDED;
+
+                       /*
+                        * Default number of spinlock retry attempts, matches
+                        * HyperV 2016.
+                        */
+                       ent->ebx = 0x00000FFF;
+
+                       break;
+
+               case HYPERV_CPUID_IMPLEMENT_LIMITS:
+                       /* Maximum number of virtual processors */
+                       ent->eax = KVM_MAX_VCPUS;
+                       /*
+                        * Maximum number of logical processors, matches
+                        * HyperV 2016.
+                        */
+                       ent->ebx = 64;
+
+                       break;
+
+               case HYPERV_CPUID_NESTED_FEATURES:
+                       ent->eax = evmcs_ver;
+
+                       break;
+
+               default:
+                       break;
+               }
+       }
+
+       if (copy_to_user(entries, cpuid_entries,
+                        nent * sizeof(struct kvm_cpuid_entry2)))
+               return -EFAULT;
+
+       return 0;
+}
index 9c21c34798993331e12d74c33116450b69dc85c3..fd7cf13a21448ff23e957d8ef38282d139ea36b5 100644 (file)
@@ -97,5 +97,7 @@ void kvm_hv_setup_tsc_page(struct kvm *kvm,
 void kvm_hv_init_vm(struct kvm *kvm);
 void kvm_hv_destroy_vm(struct kvm *kvm);
 int kvm_vm_ioctl_hv_eventfd(struct kvm *kvm, struct kvm_hyperv_eventfd *args);
+int kvm_vcpu_ioctl_get_hv_cpuid(struct kvm_vcpu *vcpu, struct kvm_cpuid2 *cpuid,
+                               struct kvm_cpuid_entry2 __user *entries);
 
 #endif
index 54ef79421c6b56ee915bb029df1aa2ab08476af6..18b7817af2bca68b6d4fc58ca9a45e4968df6cab 100644 (file)
@@ -2997,6 +2997,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
        case KVM_CAP_HYPERV_TLBFLUSH:
        case KVM_CAP_HYPERV_SEND_IPI:
        case KVM_CAP_HYPERV_ENLIGHTENED_VMCS:
+       case KVM_CAP_HYPERV_CPUID:
        case KVM_CAP_PCI_SEGMENT:
        case KVM_CAP_DEBUGREGS:
        case KVM_CAP_X86_ROBUST_SINGLESTEP:
@@ -4191,6 +4192,25 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
                r = kvm_x86_ops->set_nested_state(vcpu, user_kvm_nested_state, &kvm_state);
                break;
        }
+       case KVM_GET_SUPPORTED_HV_CPUID: {
+               struct kvm_cpuid2 __user *cpuid_arg = argp;
+               struct kvm_cpuid2 cpuid;
+
+               r = -EFAULT;
+               if (copy_from_user(&cpuid, cpuid_arg, sizeof(cpuid)))
+                       goto out;
+
+               r = kvm_vcpu_ioctl_get_hv_cpuid(vcpu, &cpuid,
+                                               cpuid_arg->entries);
+               if (r)
+                       goto out;
+
+               r = -EFAULT;
+               if (copy_to_user(cpuid_arg, &cpuid, sizeof(cpuid)))
+                       goto out;
+               r = 0;
+               break;
+       }
        default:
                r = -EINVAL;
        }
index 9fe35f1ac938f577248b9cf0bf3304f4d038b614..6d4ea4b6c92206ac5843c258f5c1f56987d79f0e 100644 (file)
@@ -987,6 +987,7 @@ struct kvm_ppc_resize_hpt {
 #define KVM_CAP_EXCEPTION_PAYLOAD 164
 #define KVM_CAP_ARM_VM_IPA_SIZE 165
 #define KVM_CAP_MANUAL_DIRTY_LOG_PROTECT 166
+#define KVM_CAP_HYPERV_CPUID 167
 
 #ifdef KVM_CAP_IRQ_ROUTING
 
@@ -1436,6 +1437,9 @@ struct kvm_enc_region {
 /* Available with KVM_CAP_MANUAL_DIRTY_LOG_PROTECT */
 #define KVM_CLEAR_DIRTY_LOG          _IOWR(KVMIO, 0xc0, struct kvm_clear_dirty_log)
 
+/* Available with KVM_CAP_HYPERV_CPUID */
+#define KVM_GET_SUPPORTED_HV_CPUID _IOWR(KVMIO, 0xc1, struct kvm_cpuid2)
+
 /* Secure Encrypted Virtualization command */
 enum sev_cmd_id {
        /* Guest initialization commands */