2010-05-06 12:08:41 -07:00
/*
* 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
2010-05-07 16:55:41 -07:00
* the Free Software Foundation ; version 2 of the License .
2010-05-06 12:08:41 -07:00
*
*/
# include <linux/types.h>
2011-09-07 15:25:10 -07:00
# include <linux/time.h>
# include <linux/clocksource.h>
2010-05-09 22:46:54 -07:00
# include <linux/module.h>
2013-02-03 17:22:39 -08:00
# include <linux/hardirq.h>
2013-09-30 17:28:52 +02:00
# include <linux/efi.h>
2013-02-03 17:22:39 -08:00
# include <linux/interrupt.h>
2014-02-23 21:40:22 +00:00
# include <linux/irq.h>
2015-08-01 16:08:07 -07:00
# include <linux/kexec.h>
2010-05-06 12:08:41 -07:00
# include <asm/processor.h>
2010-05-07 16:57:28 -07:00
# include <asm/hypervisor.h>
2010-05-06 12:08:41 -07:00
# include <asm/hyperv.h>
# include <asm/mshyperv.h>
2013-02-03 17:22:39 -08:00
# include <asm/desc.h>
# include <asm/idle.h>
# include <asm/irq_regs.h>
2013-09-30 17:28:52 +02:00
# include <asm/i8259.h>
2013-10-11 16:07:31 -07:00
# include <asm/apic.h>
2014-02-28 11:30:29 +08:00
# include <asm/timer.h>
2015-08-01 16:08:07 -07:00
# include <asm/reboot.h>
2010-05-06 12:08:41 -07:00
2010-05-07 16:57:28 -07:00
struct ms_hyperv_info ms_hyperv ;
2010-06-28 08:48:55 -06:00
EXPORT_SYMBOL_GPL ( ms_hyperv ) ;
2010-05-06 12:08:41 -07:00
2014-03-04 23:39:58 +01:00
# if IS_ENABLED(CONFIG_HYPERV)
2014-03-05 13:42:14 +01:00
static void ( * vmbus_handler ) ( void ) ;
2015-09-23 12:02:57 +02:00
static void ( * hv_kexec_handler ) ( void ) ;
static void ( * hv_crash_handler ) ( struct pt_regs * regs ) ;
2014-02-23 21:40:22 +00:00
void hyperv_vector_handler ( struct pt_regs * regs )
{
struct pt_regs * old_regs = set_irq_regs ( regs ) ;
2015-05-15 15:48:25 +02:00
entering_irq ( ) ;
2014-02-23 21:40:22 +00:00
inc_irq_stat ( irq_hv_callback_count ) ;
if ( vmbus_handler )
vmbus_handler ( ) ;
2015-05-15 15:48:25 +02:00
exiting_irq ( ) ;
2014-02-23 21:40:22 +00:00
set_irq_regs ( old_regs ) ;
}
2014-03-05 13:42:14 +01:00
void hv_setup_vmbus_irq ( void ( * handler ) ( void ) )
2014-02-23 21:40:22 +00:00
{
vmbus_handler = handler ;
/*
* Setup the IDT for hypervisor callback . Prevent reallocation
* at module reload .
*/
if ( ! test_bit ( HYPERVISOR_CALLBACK_VECTOR , used_vectors ) )
alloc_intr_gate ( HYPERVISOR_CALLBACK_VECTOR ,
hyperv_callback_vector ) ;
}
2014-03-05 13:42:14 +01:00
void hv_remove_vmbus_irq ( void )
2014-02-23 21:40:22 +00:00
{
/* We have no way to deallocate the interrupt gate */
vmbus_handler = NULL ;
}
EXPORT_SYMBOL_GPL ( hv_setup_vmbus_irq ) ;
EXPORT_SYMBOL_GPL ( hv_remove_vmbus_irq ) ;
2015-08-01 16:08:07 -07:00
void hv_setup_kexec_handler ( void ( * handler ) ( void ) )
{
hv_kexec_handler = handler ;
}
EXPORT_SYMBOL_GPL ( hv_setup_kexec_handler ) ;
void hv_remove_kexec_handler ( void )
{
hv_kexec_handler = NULL ;
}
EXPORT_SYMBOL_GPL ( hv_remove_kexec_handler ) ;
2015-08-01 16:08:09 -07:00
void hv_setup_crash_handler ( void ( * handler ) ( struct pt_regs * regs ) )
{
hv_crash_handler = handler ;
}
EXPORT_SYMBOL_GPL ( hv_setup_crash_handler ) ;
void hv_remove_crash_handler ( void )
{
hv_crash_handler = NULL ;
}
EXPORT_SYMBOL_GPL ( hv_remove_crash_handler ) ;
2014-02-23 21:40:22 +00:00
2015-09-23 12:02:57 +02:00
# ifdef CONFIG_KEXEC_CORE
2015-08-01 16:08:07 -07:00
static void hv_machine_shutdown ( void )
{
if ( kexec_in_progress & & hv_kexec_handler )
hv_kexec_handler ( ) ;
native_machine_shutdown ( ) ;
}
2015-08-01 16:08:09 -07:00
static void hv_machine_crash_shutdown ( struct pt_regs * regs )
{
if ( hv_crash_handler )
hv_crash_handler ( regs ) ;
native_machine_crash_shutdown ( regs ) ;
}
2015-09-23 12:02:57 +02:00
# endif /* CONFIG_KEXEC_CORE */
# endif /* CONFIG_HYPERV */
2015-08-01 16:08:09 -07:00
2013-07-25 16:54:35 +08:00
static uint32_t __init ms_hyperv_platform ( void )
2010-05-06 12:08:41 -07:00
{
2010-05-07 16:57:28 -07:00
u32 eax ;
u32 hyp_signature [ 3 ] ;
2010-05-06 12:08:41 -07:00
2010-05-07 16:57:28 -07:00
if ( ! boot_cpu_has ( X86_FEATURE_HYPERVISOR ) )
2013-07-25 16:54:35 +08:00
return 0 ;
2010-05-06 12:08:41 -07:00
2010-05-07 16:57:28 -07:00
cpuid ( HYPERV_CPUID_VENDOR_AND_MAX_FUNCTIONS ,
& eax , & hyp_signature [ 0 ] , & hyp_signature [ 1 ] , & hyp_signature [ 2 ] ) ;
2010-05-06 12:08:41 -07:00
2013-07-25 16:54:35 +08:00
if ( eax > = HYPERV_CPUID_MIN & &
eax < = HYPERV_CPUID_MAX & &
! memcmp ( " Microsoft Hv " , hyp_signature , 12 ) )
return HYPERV_CPUID_VENDOR_AND_MAX_FUNCTIONS ;
return 0 ;
2010-05-06 12:08:41 -07:00
}
2011-09-07 15:25:10 -07:00
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 ) ,
2015-01-12 16:26:02 -08:00
. flags = CLOCK_SOURCE_IS_CONTINUOUS ,
2011-09-07 15:25:10 -07:00
} ;
2016-04-15 15:50:32 +02:00
static unsigned char hv_get_nmi_reason ( void )
{
return 0 ;
}
2010-05-07 16:57:28 -07:00
static void __init ms_hyperv_init_platform ( void )
2010-05-06 12:08:41 -07:00
{
/*
2010-05-07 16:57:28 -07:00
* Extract the features and hints
2010-05-06 12:08:41 -07:00
*/
2010-05-07 16:57:28 -07:00
ms_hyperv . features = cpuid_eax ( HYPERV_CPUID_FEATURES ) ;
2015-08-01 16:08:20 -07:00
ms_hyperv . misc_features = cpuid_edx ( HYPERV_CPUID_FEATURES ) ;
2010-05-07 16:57:28 -07:00
ms_hyperv . hints = cpuid_eax ( HYPERV_CPUID_ENLIGHTMENT_INFO ) ;
2010-05-06 12:08:41 -07:00
2016-02-02 11:45:02 +08:00
pr_info ( " HyperV: features 0x%x, hints 0x%x \n " ,
ms_hyperv . features , ms_hyperv . hints ) ;
2011-09-07 15:25:10 -07:00
2013-10-10 15:30:24 -07:00
# ifdef CONFIG_X86_LOCAL_APIC
2013-09-30 17:28:52 +02:00
if ( ms_hyperv . features & HV_X64_MSR_APIC_FREQUENCY_AVAILABLE ) {
/*
* Get the APIC frequency .
*/
2013-11-06 10:00:05 -08:00
u64 hv_lapic_frequency ;
2013-09-30 17:28:52 +02:00
rdmsrl ( HV_X64_MSR_APIC_FREQUENCY , hv_lapic_frequency ) ;
hv_lapic_frequency = div_u64 ( hv_lapic_frequency , HZ ) ;
lapic_timer_frequency = hv_lapic_frequency ;
2016-02-02 11:45:02 +08:00
pr_info ( " HyperV: LAPIC Timer Frequency: %#x \n " ,
lapic_timer_frequency ) ;
2013-09-30 17:28:52 +02:00
}
2013-10-10 15:30:24 -07:00
# endif
2013-09-30 17:28:52 +02:00
2013-02-03 17:22:37 -08:00
if ( ms_hyperv . features & HV_X64_MSR_TIME_REF_COUNT_AVAILABLE )
clocksource_register_hz ( & hyperv_cs , NSEC_PER_SEC / 100 ) ;
2014-02-28 11:30:29 +08:00
# ifdef CONFIG_X86_IO_APIC
no_timer_check = 1 ;
# endif
2015-09-23 12:02:57 +02:00
# if IS_ENABLED(CONFIG_HYPERV) && defined(CONFIG_KEXEC_CORE)
2015-08-01 16:08:07 -07:00
machine_ops . shutdown = hv_machine_shutdown ;
2015-08-01 16:08:09 -07:00
machine_ops . crash_shutdown = hv_machine_crash_shutdown ;
2015-09-23 12:02:57 +02:00
# endif
2015-08-19 09:54:24 -07:00
mark_tsc_unstable ( " running on Hyper-V " ) ;
2016-04-15 15:50:32 +02:00
/*
* Generation 2 instances don ' t support reading the NMI status from
* 0x61 port .
*/
if ( efi_enabled ( EFI_BOOT ) )
x86_platform . get_nmi_reason = hv_get_nmi_reason ;
2010-05-06 12:08:41 -07:00
}
2010-05-07 16:57:28 -07:00
const __refconst struct hypervisor_x86 x86_hyper_ms_hyperv = {
. name = " Microsoft HyperV " ,
. detect = ms_hyperv_platform ,
. init_platform = ms_hyperv_init_platform ,
} ;
2010-05-09 01:10:34 -07:00
EXPORT_SYMBOL ( x86_hyper_ms_hyperv ) ;