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>
2016-07-13 20:18:56 -04:00
# include <linux/init.h>
# include <linux/export.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/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>
2016-12-02 11:07:20 +01:00
# include <asm/nmi.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 ( ) ;
2017-03-28 17:16:53 -07:00
if ( ms_hyperv . hints & HV_X64_DEPRECATING_AEOI_RECOMMENDED )
ack_APIC_irq ( ) ;
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
}
2016-04-15 15:50:32 +02:00
static unsigned char hv_get_nmi_reason ( void )
{
return 0 ;
}
2016-12-02 11:07:20 +01:00
# ifdef CONFIG_X86_LOCAL_APIC
/*
* Prior to WS2016 Debug - VM sends NMIs to all CPUs which makes
* it dificult to process CHANNELMSG_UNLOAD in case of crash . Handle
* unknown NMI on the first CPU which gets it .
*/
static int hv_nmi_unknown ( unsigned int val , struct pt_regs * regs )
{
static atomic_t nmi_cpu = ATOMIC_INIT ( - 1 ) ;
if ( ! unknown_nmi_panic )
return NMI_DONE ;
if ( atomic_cmpxchg ( & nmi_cpu , - 1 , raw_smp_processor_id ( ) ) ! = - 1 )
return NMI_HANDLED ;
return NMI_DONE ;
}
# endif
2017-06-22 18:07:30 +08:00
static unsigned long hv_get_tsc_khz ( void )
{
unsigned long freq ;
rdmsrl ( HV_X64_MSR_TSC_FREQUENCY , freq ) ;
return freq / 1000 ;
}
2010-05-07 16:57:28 -07:00
static void __init ms_hyperv_init_platform ( void )
2010-05-06 12:08:41 -07:00
{
2017-01-19 11:51:47 -07:00
int hv_host_info_eax ;
int hv_host_info_ebx ;
int hv_host_info_ecx ;
int hv_host_info_edx ;
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
2017-01-19 11:51:47 -07:00
/*
* Extract host information .
*/
if ( cpuid_eax ( HVCPUID_VENDOR_MAXFUNCTION ) > = HVCPUID_VERSION ) {
hv_host_info_eax = cpuid_eax ( HVCPUID_VERSION ) ;
hv_host_info_ebx = cpuid_ebx ( HVCPUID_VERSION ) ;
hv_host_info_ecx = cpuid_ecx ( HVCPUID_VERSION ) ;
hv_host_info_edx = cpuid_edx ( HVCPUID_VERSION ) ;
pr_info ( " Hyper-V Host Build:%d-%d.%d-%d-%d.%d \n " ,
hv_host_info_eax , hv_host_info_ebx > > 16 ,
hv_host_info_ebx & 0xFFFF , hv_host_info_ecx ,
hv_host_info_edx > > 24 , hv_host_info_edx & 0xFFFFFF ) ;
}
2017-06-22 18:07:30 +08:00
if ( ms_hyperv . features & HV_X64_ACCESS_FREQUENCY_MSRS & &
ms_hyperv . misc_features & HV_FEATURE_FREQUENCY_MSRS_AVAILABLE ) {
x86_platform . calibrate_tsc = hv_get_tsc_khz ;
x86_platform . calibrate_cpu = hv_get_tsc_khz ;
}
2013-10-10 15:30:24 -07:00
# ifdef CONFIG_X86_LOCAL_APIC
2017-06-22 18:07:29 +08:00
if ( ms_hyperv . features & HV_X64_ACCESS_FREQUENCY_MSRS & &
ms_hyperv . misc_features & HV_FEATURE_FREQUENCY_MSRS_AVAILABLE ) {
2013-09-30 17:28:52 +02:00
/*
* 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
}
2016-12-02 11:07:20 +01:00
register_nmi_handler ( NMI_UNKNOWN , hv_nmi_unknown , NMI_FLAG_FIRST ,
" hv_nmi_unknown " ) ;
2013-10-10 15:30:24 -07:00
# endif
2013-09-30 17:28:52 +02:00
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 ;
2017-01-18 16:45:02 -07:00
# if IS_ENABLED(CONFIG_HYPERV)
/*
* Setup the hook to get control post apic initialization .
*/
x86_platform . apic_post_init = hyperv_init ;
# endif
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 ) ;