2017-01-18 16:45:02 -07:00
/*
* X86 specific Hyper - V initialization code .
*
* Copyright ( C ) 2016 , Microsoft , Inc .
*
* Author : K . Y . Srinivasan < kys @ microsoft . com >
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE , GOOD TITLE or
* NON INFRINGEMENT . See the GNU General Public License for more
* details .
*
*/
# include <linux/types.h>
2018-01-24 14:23:33 +01:00
# include <asm/apic.h>
# include <asm/desc.h>
2017-01-18 16:45:02 -07:00
# include <asm/hypervisor.h>
2018-03-20 15:02:05 +01:00
# include <asm/hyperv-tlfs.h>
2017-01-18 16:45:02 -07:00
# include <asm/mshyperv.h>
# include <linux/version.h>
# include <linux/vmalloc.h>
# include <linux/mm.h>
2017-01-19 11:51:46 -07:00
# include <linux/clockchips.h>
2017-03-04 18:27:11 -07:00
# include <linux/hyperv.h>
2017-08-02 18:09:18 +02:00
# include <linux/slab.h>
# include <linux/cpuhotplug.h>
2017-01-19 11:51:46 -07:00
2017-03-03 14:21:40 +01:00
# ifdef CONFIG_HYPERV_TSCPAGE
2017-01-19 11:51:46 -07:00
static struct ms_hyperv_tsc_page * tsc_pg ;
2017-03-03 14:21:40 +01:00
struct ms_hyperv_tsc_page * hv_get_tsc_page ( void )
{
return tsc_pg ;
}
2018-01-24 14:23:32 +01:00
EXPORT_SYMBOL_GPL ( hv_get_tsc_page ) ;
2017-03-03 14:21:40 +01:00
2017-01-19 11:51:46 -07:00
static u64 read_hv_clock_tsc ( struct clocksource * arg )
{
2017-03-03 14:21:41 +01:00
u64 current_tick = hv_read_tsc_page ( tsc_pg ) ;
2017-01-19 11:51:46 -07:00
2017-03-03 14:21:41 +01:00
if ( current_tick = = U64_MAX )
rdmsrl ( HV_X64_MSR_TIME_REF_COUNT , current_tick ) ;
2017-01-19 11:51:46 -07:00
return current_tick ;
}
static struct clocksource hyperv_cs_tsc = {
. name = " hyperv_clocksource_tsc_page " ,
. rating = 400 ,
. read = read_hv_clock_tsc ,
. mask = CLOCKSOURCE_MASK ( 64 ) ,
. flags = CLOCK_SOURCE_IS_CONTINUOUS ,
} ;
# endif
static u64 read_hv_clock_msr ( struct clocksource * arg )
{
u64 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_msr = {
. name = " hyperv_clocksource_msr " ,
. rating = 400 ,
. read = read_hv_clock_msr ,
. mask = CLOCKSOURCE_MASK ( 64 ) ,
. flags = CLOCK_SOURCE_IS_CONTINUOUS ,
} ;
2017-01-18 16:45:02 -07:00
2017-08-02 18:09:14 +02:00
void * hv_hypercall_pg ;
EXPORT_SYMBOL_GPL ( hv_hypercall_pg ) ;
2017-02-04 09:57:13 -07:00
struct clocksource * hyperv_cs ;
EXPORT_SYMBOL_GPL ( hyperv_cs ) ;
2017-08-02 18:09:18 +02:00
u32 * hv_vp_index ;
EXPORT_SYMBOL_GPL ( hv_vp_index ) ;
2018-03-20 15:02:08 +01:00
struct hv_vp_assist_page * * hv_vp_assist_page ;
EXPORT_SYMBOL_GPL ( hv_vp_assist_page ) ;
2018-05-16 14:53:31 -07:00
void __percpu * * hyperv_pcpu_input_arg ;
EXPORT_SYMBOL_GPL ( hyperv_pcpu_input_arg ) ;
2017-10-06 17:48:54 +02:00
u32 hv_max_vp_index ;
2017-08-02 18:09:18 +02:00
static int hv_cpu_init ( unsigned int cpu )
{
u64 msr_vp_index ;
2018-03-20 15:02:08 +01:00
struct hv_vp_assist_page * * hvp = & hv_vp_assist_page [ smp_processor_id ( ) ] ;
2018-05-16 14:53:31 -07:00
void * * input_arg ;
input_arg = ( void * * ) this_cpu_ptr ( hyperv_pcpu_input_arg ) ;
* input_arg = page_address ( alloc_page ( GFP_KERNEL ) ) ;
2017-08-02 18:09:18 +02:00
hv_get_vp_index ( msr_vp_index ) ;
hv_vp_index [ smp_processor_id ( ) ] = msr_vp_index ;
2017-10-06 17:48:54 +02:00
if ( msr_vp_index > hv_max_vp_index )
hv_max_vp_index = msr_vp_index ;
2018-03-20 15:02:08 +01:00
if ( ! hv_vp_assist_page )
return 0 ;
if ( ! * hvp )
* hvp = __vmalloc ( PAGE_SIZE , GFP_KERNEL , PAGE_KERNEL ) ;
if ( * hvp ) {
u64 val ;
val = vmalloc_to_pfn ( * hvp ) ;
val = ( val < < HV_X64_MSR_VP_ASSIST_PAGE_ADDRESS_SHIFT ) |
HV_X64_MSR_VP_ASSIST_PAGE_ENABLE ;
wrmsrl ( HV_X64_MSR_VP_ASSIST_PAGE , val ) ;
}
2017-08-02 18:09:18 +02:00
return 0 ;
}
2018-01-24 14:23:33 +01:00
static void ( * hv_reenlightenment_cb ) ( void ) ;
static void hv_reenlightenment_notify ( struct work_struct * dummy )
{
struct hv_tsc_emulation_status emu_status ;
rdmsrl ( HV_X64_MSR_TSC_EMULATION_STATUS , * ( u64 * ) & emu_status ) ;
/* Don't issue the callback if TSC accesses are not emulated */
if ( hv_reenlightenment_cb & & emu_status . inprogress )
hv_reenlightenment_cb ( ) ;
}
static DECLARE_DELAYED_WORK ( hv_reenlightenment_work , hv_reenlightenment_notify ) ;
void hyperv_stop_tsc_emulation ( void )
{
u64 freq ;
struct hv_tsc_emulation_status emu_status ;
rdmsrl ( HV_X64_MSR_TSC_EMULATION_STATUS , * ( u64 * ) & emu_status ) ;
emu_status . inprogress = 0 ;
wrmsrl ( HV_X64_MSR_TSC_EMULATION_STATUS , * ( u64 * ) & emu_status ) ;
rdmsrl ( HV_X64_MSR_TSC_FREQUENCY , freq ) ;
tsc_khz = div64_u64 ( freq , 1000 ) ;
}
EXPORT_SYMBOL_GPL ( hyperv_stop_tsc_emulation ) ;
static inline bool hv_reenlightenment_available ( void )
{
/*
* Check for required features and priviliges to make TSC frequency
* change notifications work .
*/
return ms_hyperv . features & HV_X64_ACCESS_FREQUENCY_MSRS & &
ms_hyperv . misc_features & HV_FEATURE_FREQUENCY_MSRS_AVAILABLE & &
ms_hyperv . features & HV_X64_ACCESS_REENLIGHTENMENT ;
}
__visible void __irq_entry hyperv_reenlightenment_intr ( struct pt_regs * regs )
{
entering_ack_irq ( ) ;
2018-01-24 14:23:35 +01:00
inc_irq_stat ( irq_hv_reenlightenment_count ) ;
2018-01-24 14:23:33 +01:00
schedule_delayed_work ( & hv_reenlightenment_work , HZ / 10 ) ;
exiting_irq ( ) ;
}
void set_hv_tscchange_cb ( void ( * cb ) ( void ) )
{
struct hv_reenlightenment_control re_ctrl = {
. vector = HYPERV_REENLIGHTENMENT_VECTOR ,
. enabled = 1 ,
. target_vp = hv_vp_index [ smp_processor_id ( ) ]
} ;
struct hv_tsc_emulation_control emu_ctrl = { . enabled = 1 } ;
if ( ! hv_reenlightenment_available ( ) ) {
pr_warn ( " Hyper-V: reenlightenment support is unavailable \n " ) ;
return ;
}
hv_reenlightenment_cb = cb ;
/* Make sure callback is registered before we write to MSRs */
wmb ( ) ;
wrmsrl ( HV_X64_MSR_REENLIGHTENMENT_CONTROL , * ( ( u64 * ) & re_ctrl ) ) ;
wrmsrl ( HV_X64_MSR_TSC_EMULATION_CONTROL , * ( ( u64 * ) & emu_ctrl ) ) ;
}
EXPORT_SYMBOL_GPL ( set_hv_tscchange_cb ) ;
void clear_hv_tscchange_cb ( void )
{
struct hv_reenlightenment_control re_ctrl ;
if ( ! hv_reenlightenment_available ( ) )
return ;
rdmsrl ( HV_X64_MSR_REENLIGHTENMENT_CONTROL , * ( u64 * ) & re_ctrl ) ;
re_ctrl . enabled = 0 ;
wrmsrl ( HV_X64_MSR_REENLIGHTENMENT_CONTROL , * ( u64 * ) & re_ctrl ) ;
hv_reenlightenment_cb = NULL ;
}
EXPORT_SYMBOL_GPL ( clear_hv_tscchange_cb ) ;
2018-01-24 14:23:34 +01:00
static int hv_cpu_die ( unsigned int cpu )
{
struct hv_reenlightenment_control re_ctrl ;
unsigned int new_cpu ;
2018-05-16 14:53:31 -07:00
unsigned long flags ;
void * * input_arg ;
void * input_pg = NULL ;
local_irq_save ( flags ) ;
input_arg = ( void * * ) this_cpu_ptr ( hyperv_pcpu_input_arg ) ;
input_pg = * input_arg ;
* input_arg = NULL ;
local_irq_restore ( flags ) ;
free_page ( ( unsigned long ) input_pg ) ;
2018-01-24 14:23:34 +01:00
2018-03-20 15:02:08 +01:00
if ( hv_vp_assist_page & & hv_vp_assist_page [ cpu ] )
wrmsrl ( HV_X64_MSR_VP_ASSIST_PAGE , 0 ) ;
2018-01-24 14:23:34 +01:00
if ( hv_reenlightenment_cb = = NULL )
return 0 ;
rdmsrl ( HV_X64_MSR_REENLIGHTENMENT_CONTROL , * ( ( u64 * ) & re_ctrl ) ) ;
if ( re_ctrl . target_vp = = hv_vp_index [ cpu ] ) {
/* Reassign to some other online CPU */
new_cpu = cpumask_any_but ( cpu_online_mask , cpu ) ;
re_ctrl . target_vp = hv_vp_index [ new_cpu ] ;
wrmsrl ( HV_X64_MSR_REENLIGHTENMENT_CONTROL , * ( ( u64 * ) & re_ctrl ) ) ;
}
return 0 ;
}
2017-01-18 16:45:02 -07:00
/*
* This function is to be invoked early in the boot sequence after the
* hypervisor has been detected .
*
* 1. Setup the hypercall page .
2017-01-19 11:51:46 -07:00
* 2. Register Hyper - V specific clocksource .
2018-05-16 14:53:30 -07:00
* 3. Setup Hyper - V specific APIC entry points .
2017-01-18 16:45:02 -07:00
*/
2018-05-16 14:53:30 -07:00
void __init hyperv_init ( void )
2017-01-18 16:45:02 -07:00
{
2018-01-24 14:23:31 +01:00
u64 guest_id , required_msrs ;
2017-01-18 16:45:02 -07:00
union hv_x64_msr_hypercall_contents hypercall_msr ;
2018-07-03 16:01:55 -07:00
int cpuhp , i ;
2017-01-18 16:45:02 -07:00
2017-11-09 14:27:36 +01:00
if ( x86_hyper_type ! = X86_HYPER_MS_HYPERV )
2017-01-18 16:45:02 -07:00
return ;
2018-01-24 14:23:31 +01:00
/* Absolutely required MSRs */
required_msrs = HV_X64_MSR_HYPERCALL_AVAILABLE |
HV_X64_MSR_VP_INDEX_AVAILABLE ;
if ( ( ms_hyperv . features & required_msrs ) ! = required_msrs )
return ;
2018-05-16 14:53:31 -07:00
/*
* Allocate the per - CPU state for the hypercall input arg .
* If this allocation fails , we will not be able to setup
* ( per - CPU ) hypercall input page and thus this failure is
* fatal on Hyper - V .
*/
hyperv_pcpu_input_arg = alloc_percpu ( void * ) ;
BUG_ON ( hyperv_pcpu_input_arg = = NULL ) ;
2017-08-02 18:09:18 +02:00
/* Allocate percpu VP index */
hv_vp_index = kmalloc_array ( num_possible_cpus ( ) , sizeof ( * hv_vp_index ) ,
GFP_KERNEL ) ;
if ( ! hv_vp_index )
return ;
2018-07-03 16:01:55 -07:00
for ( i = 0 ; i < num_possible_cpus ( ) ; i + + )
hv_vp_index [ i ] = VP_INVAL ;
2018-03-20 15:02:08 +01:00
hv_vp_assist_page = kcalloc ( num_possible_cpus ( ) ,
sizeof ( * hv_vp_assist_page ) , GFP_KERNEL ) ;
if ( ! hv_vp_assist_page ) {
ms_hyperv . hints & = ~ HV_X64_ENLIGHTENED_VMCS_RECOMMENDED ;
2017-08-02 18:09:18 +02:00
goto free_vp_index ;
2018-03-20 15:02:08 +01:00
}
cpuhp = cpuhp_setup_state ( CPUHP_AP_ONLINE_DYN , " x86/hyperv_init:online " ,
hv_cpu_init , hv_cpu_die ) ;
if ( cpuhp < 0 )
goto free_vp_assist_page ;
2017-08-02 18:09:18 +02:00
2017-01-18 16:45:02 -07:00
/*
* Setup the hypercall page and enable hypercalls .
* 1. Register the guest ID
* 2. Enable the hypercall and register the hypercall page
*/
guest_id = generate_guest_id ( 0 , LINUX_VERSION_CODE , 0 ) ;
wrmsrl ( HV_X64_MSR_GUEST_OS_ID , guest_id ) ;
2017-08-02 18:09:14 +02:00
hv_hypercall_pg = __vmalloc ( PAGE_SIZE , GFP_KERNEL , PAGE_KERNEL_RX ) ;
if ( hv_hypercall_pg = = NULL ) {
2017-01-18 16:45:02 -07:00
wrmsrl ( HV_X64_MSR_GUEST_OS_ID , 0 ) ;
2018-03-20 15:02:08 +01:00
goto remove_cpuhp_state ;
2017-01-18 16:45:02 -07:00
}
rdmsrl ( HV_X64_MSR_HYPERCALL , hypercall_msr . as_uint64 ) ;
hypercall_msr . enable = 1 ;
2017-08-02 18:09:14 +02:00
hypercall_msr . guest_physical_address = vmalloc_to_pfn ( hv_hypercall_pg ) ;
2017-01-18 16:45:02 -07:00
wrmsrl ( HV_X64_MSR_HYPERCALL , hypercall_msr . as_uint64 ) ;
2017-01-19 11:51:46 -07:00
2018-05-16 14:53:30 -07:00
hv_apic_init ( ) ;
2017-01-19 11:51:46 -07:00
/*
* Register Hyper - V specific clocksource .
*/
2017-03-03 14:21:40 +01:00
# ifdef CONFIG_HYPERV_TSCPAGE
2018-06-05 13:37:54 -07:00
if ( ms_hyperv . features & HV_MSR_REFERENCE_TSC_AVAILABLE ) {
2017-01-19 11:51:46 -07:00
union hv_x64_msr_hypercall_contents tsc_msr ;
tsc_pg = __vmalloc ( PAGE_SIZE , GFP_KERNEL , PAGE_KERNEL ) ;
2017-02-04 09:57:13 -07:00
if ( ! tsc_pg )
goto register_msr_cs ;
hyperv_cs = & hyperv_cs_tsc ;
2017-01-19 11:51:46 -07:00
rdmsrl ( HV_X64_MSR_REFERENCE_TSC , tsc_msr . as_uint64 ) ;
tsc_msr . enable = 1 ;
tsc_msr . guest_physical_address = vmalloc_to_pfn ( tsc_pg ) ;
wrmsrl ( HV_X64_MSR_REFERENCE_TSC , tsc_msr . as_uint64 ) ;
2017-03-03 14:21:42 +01:00
hyperv_cs_tsc . archdata . vclock_mode = VCLOCK_HVCLOCK ;
2017-01-19 11:51:46 -07:00
clocksource_register_hz ( & hyperv_cs_tsc , NSEC_PER_SEC / 100 ) ;
return ;
}
2017-02-14 22:17:17 +01:00
register_msr_cs :
2017-01-19 11:51:46 -07:00
# endif
/*
* For 32 bit guests just use the MSR based mechanism for reading
* the partition counter .
*/
2017-02-04 09:57:13 -07:00
hyperv_cs = & hyperv_cs_msr ;
2018-06-05 13:37:54 -07:00
if ( ms_hyperv . features & HV_MSR_TIME_REF_COUNT_AVAILABLE )
2017-01-19 11:51:46 -07:00
clocksource_register_hz ( & hyperv_cs_msr , NSEC_PER_SEC / 100 ) ;
2017-08-02 18:09:18 +02:00
return ;
2018-03-20 15:02:08 +01:00
remove_cpuhp_state :
cpuhp_remove_state ( cpuhp ) ;
free_vp_assist_page :
kfree ( hv_vp_assist_page ) ;
hv_vp_assist_page = NULL ;
2017-08-02 18:09:18 +02:00
free_vp_index :
kfree ( hv_vp_index ) ;
hv_vp_index = NULL ;
2017-01-18 16:45:02 -07:00
}
2017-01-18 16:45:03 -07:00
2017-01-28 12:37:14 -07:00
/*
* This routine is called before kexec / kdump , it does the required cleanup .
*/
void hyperv_cleanup ( void )
{
union hv_x64_msr_hypercall_contents hypercall_msr ;
/* Reset our OS id */
wrmsrl ( HV_X64_MSR_GUEST_OS_ID , 0 ) ;
/* Reset the hypercall page */
hypercall_msr . as_uint64 = 0 ;
wrmsrl ( HV_X64_MSR_HYPERCALL , hypercall_msr . as_uint64 ) ;
2017-01-28 12:37:15 -07:00
/* Reset the TSC page */
hypercall_msr . as_uint64 = 0 ;
wrmsrl ( HV_X64_MSR_REFERENCE_TSC , hypercall_msr . as_uint64 ) ;
2017-01-28 12:37:14 -07:00
}
EXPORT_SYMBOL_GPL ( hyperv_cleanup ) ;
2017-10-29 11:33:41 -07:00
void hyperv_report_panic ( struct pt_regs * regs , long err )
2017-01-19 11:51:48 -07:00
{
static bool panic_reported ;
2017-10-29 11:33:41 -07:00
u64 guest_id ;
2017-01-19 11:51:48 -07:00
/*
* We prefer to report panic on ' die ' chain as we have proper
* registers to report , but if we miss it ( e . g . on BUG ( ) ) we need
* to report it on ' panic ' .
*/
if ( panic_reported )
return ;
panic_reported = true ;
2017-10-29 11:33:41 -07:00
rdmsrl ( HV_X64_MSR_GUEST_OS_ID , guest_id ) ;
wrmsrl ( HV_X64_MSR_CRASH_P0 , err ) ;
wrmsrl ( HV_X64_MSR_CRASH_P1 , guest_id ) ;
wrmsrl ( HV_X64_MSR_CRASH_P2 , regs - > ip ) ;
wrmsrl ( HV_X64_MSR_CRASH_P3 , regs - > ax ) ;
wrmsrl ( HV_X64_MSR_CRASH_P4 , regs - > sp ) ;
2017-01-19 11:51:48 -07:00
/*
* Let Hyper - V know there is crash data available
*/
wrmsrl ( HV_X64_MSR_CRASH_CTL , HV_CRASH_CTL_CRASH_NOTIFY ) ;
}
EXPORT_SYMBOL_GPL ( hyperv_report_panic ) ;
2017-01-19 11:51:49 -07:00
2018-07-08 02:56:51 +00:00
/**
* hyperv_report_panic_msg - report panic message to Hyper - V
* @ pa : physical address of the panic page containing the message
* @ size : size of the message in the page
*/
void hyperv_report_panic_msg ( phys_addr_t pa , size_t size )
{
/*
* P3 to contain the physical address of the panic page & P4 to
* contain the size of the panic data in that page . Rest of the
* registers are no - op when the NOTIFY_MSG flag is set .
*/
wrmsrl ( HV_X64_MSR_CRASH_P0 , 0 ) ;
wrmsrl ( HV_X64_MSR_CRASH_P1 , 0 ) ;
wrmsrl ( HV_X64_MSR_CRASH_P2 , 0 ) ;
wrmsrl ( HV_X64_MSR_CRASH_P3 , pa ) ;
wrmsrl ( HV_X64_MSR_CRASH_P4 , size ) ;
/*
* Let Hyper - V know there is crash data available along with
* the panic message .
*/
wrmsrl ( HV_X64_MSR_CRASH_CTL ,
( HV_CRASH_CTL_CRASH_NOTIFY | HV_CRASH_CTL_CRASH_NOTIFY_MSG ) ) ;
}
EXPORT_SYMBOL_GPL ( hyperv_report_panic_msg ) ;
2017-12-22 11:19:02 -07:00
bool hv_is_hyperv_initialized ( void )
2017-01-19 11:51:49 -07:00
{
union hv_x64_msr_hypercall_contents hypercall_msr ;
2017-12-22 11:19:02 -07:00
/*
* Ensure that we ' re really on Hyper - V , and not a KVM or Xen
* emulation of Hyper - V
*/
if ( x86_hyper_type ! = X86_HYPER_MS_HYPERV )
return false ;
/*
* Verify that earlier initialization succeeded by checking
* that the hypercall page is setup
*/
2017-01-19 11:51:49 -07:00
hypercall_msr . as_uint64 = 0 ;
rdmsrl ( HV_X64_MSR_HYPERCALL , hypercall_msr . as_uint64 ) ;
2017-12-22 11:19:02 -07:00
return hypercall_msr . enable ;
2017-01-19 11:51:49 -07:00
}
2017-12-22 11:19:02 -07:00
EXPORT_SYMBOL_GPL ( hv_is_hyperv_initialized ) ;