forked from Minki/linux
9df56f19a5
We try to handle the hypervisor compatibility mode by detecting hypervisor through a specific order. This is not robust, since hypervisors may implement each others features. This patch tries to handle this situation by always choosing the last one in the CPUID leaves. This is done by letting .detect() return a priority instead of true/false and just re-using the CPUID leaf where the signature were found as the priority (or 1 if it was found by DMI). Then we can just pick hypervisor who has the highest priority. Other sophisticated detection method could also be implemented on top. Suggested by H. Peter Anvin and Paolo Bonzini. Acked-by: K. Y. Srinivasan <kys@microsoft.com> Cc: Haiyang Zhang <haiyangz@microsoft.com> Cc: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> Cc: Jeremy Fitzhardinge <jeremy@goop.org> Cc: Doug Covelli <dcovelli@vmware.com> Cc: Borislav Petkov <bp@suse.de> Cc: Dan Hecht <dhecht@vmware.com> Cc: Paul Gortmaker <paul.gortmaker@windriver.com> Cc: Marcelo Tosatti <mtosatti@redhat.com> Cc: Gleb Natapov <gleb@redhat.com> Cc: Paolo Bonzini <pbonzini@redhat.com> Cc: Frederic Weisbecker <fweisbec@gmail.com> Signed-off-by: Jason Wang <jasowang@redhat.com> Link: http://lkml.kernel.org/r/1374742475-2485-4-git-send-email-jasowang@redhat.com Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
127 lines
3.0 KiB
C
127 lines
3.0 KiB
C
/*
|
|
* HyperV Detection code.
|
|
*
|
|
* Copyright (C) 2010, Novell, Inc.
|
|
* Author : K. Y. Srinivasan <ksrinivasan@novell.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; version 2 of the License.
|
|
*
|
|
*/
|
|
|
|
#include <linux/types.h>
|
|
#include <linux/time.h>
|
|
#include <linux/clocksource.h>
|
|
#include <linux/module.h>
|
|
#include <linux/hardirq.h>
|
|
#include <linux/interrupt.h>
|
|
#include <asm/processor.h>
|
|
#include <asm/hypervisor.h>
|
|
#include <asm/hyperv.h>
|
|
#include <asm/mshyperv.h>
|
|
#include <asm/desc.h>
|
|
#include <asm/idle.h>
|
|
#include <asm/irq_regs.h>
|
|
|
|
struct ms_hyperv_info ms_hyperv;
|
|
EXPORT_SYMBOL_GPL(ms_hyperv);
|
|
|
|
static uint32_t __init ms_hyperv_platform(void)
|
|
{
|
|
u32 eax;
|
|
u32 hyp_signature[3];
|
|
|
|
if (!boot_cpu_has(X86_FEATURE_HYPERVISOR))
|
|
return 0;
|
|
|
|
cpuid(HYPERV_CPUID_VENDOR_AND_MAX_FUNCTIONS,
|
|
&eax, &hyp_signature[0], &hyp_signature[1], &hyp_signature[2]);
|
|
|
|
if (eax >= HYPERV_CPUID_MIN &&
|
|
eax <= HYPERV_CPUID_MAX &&
|
|
!memcmp("Microsoft Hv", hyp_signature, 12))
|
|
return HYPERV_CPUID_VENDOR_AND_MAX_FUNCTIONS;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static cycle_t read_hv_clock(struct clocksource *arg)
|
|
{
|
|
cycle_t current_tick;
|
|
/*
|
|
* Read the partition counter to get the current tick count. This count
|
|
* is set to 0 when the partition is created and is incremented in
|
|
* 100 nanosecond units.
|
|
*/
|
|
rdmsrl(HV_X64_MSR_TIME_REF_COUNT, current_tick);
|
|
return current_tick;
|
|
}
|
|
|
|
static struct clocksource hyperv_cs = {
|
|
.name = "hyperv_clocksource",
|
|
.rating = 400, /* use this when running on Hyperv*/
|
|
.read = read_hv_clock,
|
|
.mask = CLOCKSOURCE_MASK(64),
|
|
};
|
|
|
|
static void __init ms_hyperv_init_platform(void)
|
|
{
|
|
/*
|
|
* Extract the features and hints
|
|
*/
|
|
ms_hyperv.features = cpuid_eax(HYPERV_CPUID_FEATURES);
|
|
ms_hyperv.hints = cpuid_eax(HYPERV_CPUID_ENLIGHTMENT_INFO);
|
|
|
|
printk(KERN_INFO "HyperV: features 0x%x, hints 0x%x\n",
|
|
ms_hyperv.features, ms_hyperv.hints);
|
|
|
|
if (ms_hyperv.features & HV_X64_MSR_TIME_REF_COUNT_AVAILABLE)
|
|
clocksource_register_hz(&hyperv_cs, NSEC_PER_SEC/100);
|
|
}
|
|
|
|
const __refconst struct hypervisor_x86 x86_hyper_ms_hyperv = {
|
|
.name = "Microsoft HyperV",
|
|
.detect = ms_hyperv_platform,
|
|
.init_platform = ms_hyperv_init_platform,
|
|
};
|
|
EXPORT_SYMBOL(x86_hyper_ms_hyperv);
|
|
|
|
#if IS_ENABLED(CONFIG_HYPERV)
|
|
static int vmbus_irq = -1;
|
|
static irq_handler_t vmbus_isr;
|
|
|
|
void hv_register_vmbus_handler(int irq, irq_handler_t handler)
|
|
{
|
|
/*
|
|
* Setup the IDT for hypervisor callback.
|
|
*/
|
|
alloc_intr_gate(HYPERVISOR_CALLBACK_VECTOR, hyperv_callback_vector);
|
|
|
|
vmbus_irq = irq;
|
|
vmbus_isr = handler;
|
|
}
|
|
|
|
void hyperv_vector_handler(struct pt_regs *regs)
|
|
{
|
|
struct pt_regs *old_regs = set_irq_regs(regs);
|
|
struct irq_desc *desc;
|
|
|
|
irq_enter();
|
|
exit_idle();
|
|
|
|
desc = irq_to_desc(vmbus_irq);
|
|
|
|
if (desc)
|
|
generic_handle_irq_desc(vmbus_irq, desc);
|
|
|
|
irq_exit();
|
|
set_irq_regs(old_regs);
|
|
}
|
|
#else
|
|
void hv_register_vmbus_handler(int irq, irq_handler_t handler)
|
|
{
|
|
}
|
|
#endif
|
|
EXPORT_SYMBOL_GPL(hv_register_vmbus_handler);
|