hyperv: Identify Hyper-V features and recommends properly

Features bits will be used to detect devices, e.g. timers, which
do not have corresponding event channels.

Submitted by:	Jun Su <junsu microsoft com>
Reviewed by:	sephe, Dexuan Cui <decui microsoft com>
Rearranged by:	sephe
MFC after:	1 week
Sponsored by:	Microsoft OSTC
This commit is contained in:
sephe 2016-04-11 03:28:17 +00:00
parent 43950de88e
commit 297e2d2f52
2 changed files with 98 additions and 2 deletions

View File

@ -33,6 +33,7 @@
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/pcpu.h>
#include <sys/timetc.h>
@ -50,6 +51,9 @@ __FBSDID("$FreeBSD$");
static u_int hv_get_timecount(struct timecounter *tc);
u_int hyperv_features;
u_int hyperv_recommends;
/**
* Globals
*/
@ -393,3 +397,88 @@ void hv_vmbus_synic_cleanup(void *arg)
wrmsr(HV_X64_MSR_SIEFP, siefp.as_uint64_t);
}
static bool
hyperv_identify(void)
{
u_int regs[4];
unsigned int maxLeaf;
unsigned int op;
if (vm_guest != VM_GUEST_HV)
return (false);
op = HV_CPU_ID_FUNCTION_HV_VENDOR_AND_MAX_FUNCTION;
do_cpuid(op, regs);
maxLeaf = regs[0];
if (maxLeaf < HV_CPU_ID_FUNCTION_MS_HV_IMPLEMENTATION_LIMITS)
return (false);
op = HV_CPU_ID_FUNCTION_HV_INTERFACE;
do_cpuid(op, regs);
if (regs[0] != 0x31237648 /* HV#1 */)
return (false);
op = HV_CPU_ID_FUNCTION_MS_HV_FEATURES;
do_cpuid(op, regs);
if ((regs[0] & HV_FEATURE_MSR_HYPERCALL) == 0) {
/*
* Hyper-V w/o Hypercall is impossible; someone
* is faking Hyper-V.
*/
return (false);
}
hyperv_features = regs[0];
op = HV_CPU_ID_FUNCTION_MS_HV_VERSION;
do_cpuid(op, regs);
printf("Hyper-V Version: %d.%d.%d [SP%d]\n",
regs[1] >> 16, regs[1] & 0xffff, regs[0], regs[2]);
printf(" Features: 0x%b\n", hyperv_features,
"\020"
"\001VPRUNTIME"
"\002TMREFCNT"
"\003SYNCIC"
"\004SYNCTM"
"\005APIC"
"\006HYERCALL"
"\007VPINDEX"
"\010RESET"
"\011STATS"
"\012REFTSC"
"\013IDLE"
"\014TMFREQ"
"\015DEBUG");
op = HV_CPU_ID_FUNCTION_MS_HV_ENLIGHTENMENT_INFORMATION;
do_cpuid(op, regs);
hyperv_recommends = regs[0];
if (bootverbose)
printf(" Recommends: %08x %08x\n", regs[0], regs[1]);
op = HV_CPU_ID_FUNCTION_MS_HV_IMPLEMENTATION_LIMITS;
do_cpuid(op, regs);
if (bootverbose) {
printf(" Limits: Vcpu:%d Lcpu:%d Int:%d\n",
regs[0], regs[1], regs[2]);
}
if (maxLeaf >= HV_CPU_ID_FUNCTION_MS_HV_HARDWARE_FEATURE) {
op = HV_CPU_ID_FUNCTION_MS_HV_HARDWARE_FEATURE;
do_cpuid(op, regs);
if (bootverbose) {
printf(" HW Features: %08x AMD: %08x\n",
regs[0], regs[3]);
}
}
return (true);
}
static void
hyperv_init(void *dummy __unused)
{
if (!hyperv_identify())
return;
}
SYSINIT(hyperv_initialize, SI_SUB_HYPERVISOR, SI_ORDER_FIRST, hyperv_init, NULL);

View File

@ -471,10 +471,17 @@ typedef enum {
HV_CPU_ID_FUNCTION_MS_HV_VERSION = 0x40000002,
HV_CPU_ID_FUNCTION_MS_HV_FEATURES = 0x40000003,
HV_CPU_ID_FUNCTION_MS_HV_ENLIGHTENMENT_INFORMATION = 0x40000004,
HV_CPU_ID_FUNCTION_MS_HV_IMPLEMENTATION_LIMITS = 0x40000005
HV_CPU_ID_FUNCTION_MS_HV_IMPLEMENTATION_LIMITS = 0x40000005,
HV_CPU_ID_FUNCTION_MS_HV_HARDWARE_FEATURE = 0x40000006
} hv_vmbus_cpuid_function;
#define HV_FEATURE_MSR_TIME_REFCNT (1 << 1)
#define HV_FEATURE_MSR_SYNCIC (1 << 2)
#define HV_FEATURE_MSR_STIMER (1 << 3)
#define HV_FEATURE_MSR_APIC (1 << 4)
#define HV_FEATURE_MSR_HYPERCALL (1 << 5)
#define HV_FEATURE_MSR_GUEST_IDLE (1 << 10)
/*
* Define the format of the SIMP register
*/