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
 ------------------------
 
                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;
+}
 
        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:
                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;
        }