LCOV - code coverage report
Current view: top level - arch/x86/kernel/cpu - mshyperv.c (source / functions) Hit Total Coverage
Test: combined.info Lines: 6 65 9.2 %
Date: 2022-04-01 14:58:12 Functions: 1 6 16.7 %
Branches: 2 26 7.7 %

           Branch data     Line data    Source code
       1                 :            : // SPDX-License-Identifier: GPL-2.0-only
       2                 :            : /*
       3                 :            :  * HyperV  Detection code.
       4                 :            :  *
       5                 :            :  * Copyright (C) 2010, Novell, Inc.
       6                 :            :  * Author : K. Y. Srinivasan <ksrinivasan@novell.com>
       7                 :            :  */
       8                 :            : 
       9                 :            : #include <linux/types.h>
      10                 :            : #include <linux/time.h>
      11                 :            : #include <linux/clocksource.h>
      12                 :            : #include <linux/init.h>
      13                 :            : #include <linux/export.h>
      14                 :            : #include <linux/hardirq.h>
      15                 :            : #include <linux/efi.h>
      16                 :            : #include <linux/interrupt.h>
      17                 :            : #include <linux/irq.h>
      18                 :            : #include <linux/kexec.h>
      19                 :            : #include <linux/i8253.h>
      20                 :            : #include <linux/random.h>
      21                 :            : #include <asm/processor.h>
      22                 :            : #include <asm/hypervisor.h>
      23                 :            : #include <asm/hyperv-tlfs.h>
      24                 :            : #include <asm/mshyperv.h>
      25                 :            : #include <asm/desc.h>
      26                 :            : #include <asm/irq_regs.h>
      27                 :            : #include <asm/i8259.h>
      28                 :            : #include <asm/apic.h>
      29                 :            : #include <asm/timer.h>
      30                 :            : #include <asm/reboot.h>
      31                 :            : #include <asm/nmi.h>
      32                 :            : #include <clocksource/hyperv_timer.h>
      33                 :            : 
      34                 :            : struct ms_hyperv_info ms_hyperv;
      35                 :            : EXPORT_SYMBOL_GPL(ms_hyperv);
      36                 :            : 
      37                 :            : #if IS_ENABLED(CONFIG_HYPERV)
      38                 :            : static void (*vmbus_handler)(void);
      39                 :            : static void (*hv_stimer0_handler)(void);
      40                 :            : static void (*hv_kexec_handler)(void);
      41                 :            : static void (*hv_crash_handler)(struct pt_regs *regs);
      42                 :            : 
      43                 :            : __visible void __irq_entry hyperv_vector_handler(struct pt_regs *regs)
      44                 :            : {
      45                 :            :         struct pt_regs *old_regs = set_irq_regs(regs);
      46                 :            : 
      47                 :            :         entering_irq();
      48                 :            :         inc_irq_stat(irq_hv_callback_count);
      49                 :            :         if (vmbus_handler)
      50                 :            :                 vmbus_handler();
      51                 :            : 
      52                 :            :         if (ms_hyperv.hints & HV_DEPRECATING_AEOI_RECOMMENDED)
      53                 :            :                 ack_APIC_irq();
      54                 :            : 
      55                 :            :         exiting_irq();
      56                 :            :         set_irq_regs(old_regs);
      57                 :            : }
      58                 :            : 
      59                 :            : void hv_setup_vmbus_irq(void (*handler)(void))
      60                 :            : {
      61                 :            :         vmbus_handler = handler;
      62                 :            : }
      63                 :            : 
      64                 :            : void hv_remove_vmbus_irq(void)
      65                 :            : {
      66                 :            :         /* We have no way to deallocate the interrupt gate */
      67                 :            :         vmbus_handler = NULL;
      68                 :            : }
      69                 :            : EXPORT_SYMBOL_GPL(hv_setup_vmbus_irq);
      70                 :            : EXPORT_SYMBOL_GPL(hv_remove_vmbus_irq);
      71                 :            : 
      72                 :            : /*
      73                 :            :  * Routines to do per-architecture handling of stimer0
      74                 :            :  * interrupts when in Direct Mode
      75                 :            :  */
      76                 :            : 
      77                 :            : __visible void __irq_entry hv_stimer0_vector_handler(struct pt_regs *regs)
      78                 :            : {
      79                 :            :         struct pt_regs *old_regs = set_irq_regs(regs);
      80                 :            : 
      81                 :            :         entering_irq();
      82                 :            :         inc_irq_stat(hyperv_stimer0_count);
      83                 :            :         if (hv_stimer0_handler)
      84                 :            :                 hv_stimer0_handler();
      85                 :            :         add_interrupt_randomness(HYPERV_STIMER0_VECTOR, 0);
      86                 :            :         ack_APIC_irq();
      87                 :            : 
      88                 :            :         exiting_irq();
      89                 :            :         set_irq_regs(old_regs);
      90                 :            : }
      91                 :            : 
      92                 :            : int hv_setup_stimer0_irq(int *irq, int *vector, void (*handler)(void))
      93                 :            : {
      94                 :            :         *vector = HYPERV_STIMER0_VECTOR;
      95                 :            :         *irq = -1;   /* Unused on x86/x64 */
      96                 :            :         hv_stimer0_handler = handler;
      97                 :            :         return 0;
      98                 :            : }
      99                 :            : EXPORT_SYMBOL_GPL(hv_setup_stimer0_irq);
     100                 :            : 
     101                 :            : void hv_remove_stimer0_irq(int irq)
     102                 :            : {
     103                 :            :         /* We have no way to deallocate the interrupt gate */
     104                 :            :         hv_stimer0_handler = NULL;
     105                 :            : }
     106                 :            : EXPORT_SYMBOL_GPL(hv_remove_stimer0_irq);
     107                 :            : 
     108                 :            : void hv_setup_kexec_handler(void (*handler)(void))
     109                 :            : {
     110                 :            :         hv_kexec_handler = handler;
     111                 :            : }
     112                 :            : EXPORT_SYMBOL_GPL(hv_setup_kexec_handler);
     113                 :            : 
     114                 :            : void hv_remove_kexec_handler(void)
     115                 :            : {
     116                 :            :         hv_kexec_handler = NULL;
     117                 :            : }
     118                 :            : EXPORT_SYMBOL_GPL(hv_remove_kexec_handler);
     119                 :            : 
     120                 :            : void hv_setup_crash_handler(void (*handler)(struct pt_regs *regs))
     121                 :            : {
     122                 :            :         hv_crash_handler = handler;
     123                 :            : }
     124                 :            : EXPORT_SYMBOL_GPL(hv_setup_crash_handler);
     125                 :            : 
     126                 :            : void hv_remove_crash_handler(void)
     127                 :            : {
     128                 :            :         hv_crash_handler = NULL;
     129                 :            : }
     130                 :            : EXPORT_SYMBOL_GPL(hv_remove_crash_handler);
     131                 :            : 
     132                 :            : #ifdef CONFIG_KEXEC_CORE
     133                 :            : static void hv_machine_shutdown(void)
     134                 :            : {
     135                 :            :         if (kexec_in_progress && hv_kexec_handler)
     136                 :            :                 hv_kexec_handler();
     137                 :            :         native_machine_shutdown();
     138                 :            : }
     139                 :            : 
     140                 :            : static void hv_machine_crash_shutdown(struct pt_regs *regs)
     141                 :            : {
     142                 :            :         if (hv_crash_handler)
     143                 :            :                 hv_crash_handler(regs);
     144                 :            :         native_machine_crash_shutdown(regs);
     145                 :            : }
     146                 :            : #endif /* CONFIG_KEXEC_CORE */
     147                 :            : #endif /* CONFIG_HYPERV */
     148                 :            : 
     149                 :          3 : static uint32_t  __init ms_hyperv_platform(void)
     150                 :            : {
     151                 :          3 :         u32 eax;
     152                 :          3 :         u32 hyp_signature[3];
     153                 :            : 
     154         [ +  - ]:          3 :         if (!boot_cpu_has(X86_FEATURE_HYPERVISOR))
     155                 :            :                 return 0;
     156                 :            : 
     157                 :          3 :         cpuid(HYPERV_CPUID_VENDOR_AND_MAX_FUNCTIONS,
     158                 :            :               &eax, &hyp_signature[0], &hyp_signature[1], &hyp_signature[2]);
     159                 :            : 
     160         [ -  + ]:          3 :         if (eax >= HYPERV_CPUID_MIN &&
     161                 :          0 :             eax <= HYPERV_CPUID_MAX &&
     162         [ #  # ]:          0 :             !memcmp("Microsoft Hv", hyp_signature, 12))
     163                 :          0 :                 return HYPERV_CPUID_VENDOR_AND_MAX_FUNCTIONS;
     164                 :            : 
     165                 :            :         return 0;
     166                 :            : }
     167                 :            : 
     168                 :          0 : static unsigned char hv_get_nmi_reason(void)
     169                 :            : {
     170                 :          0 :         return 0;
     171                 :            : }
     172                 :            : 
     173                 :            : #ifdef CONFIG_X86_LOCAL_APIC
     174                 :            : /*
     175                 :            :  * Prior to WS2016 Debug-VM sends NMIs to all CPUs which makes
     176                 :            :  * it dificult to process CHANNELMSG_UNLOAD in case of crash. Handle
     177                 :            :  * unknown NMI on the first CPU which gets it.
     178                 :            :  */
     179                 :          0 : static int hv_nmi_unknown(unsigned int val, struct pt_regs *regs)
     180                 :            : {
     181                 :          0 :         static atomic_t nmi_cpu = ATOMIC_INIT(-1);
     182                 :            : 
     183         [ #  # ]:          0 :         if (!unknown_nmi_panic)
     184                 :            :                 return NMI_DONE;
     185                 :            : 
     186         [ #  # ]:          0 :         if (atomic_cmpxchg(&nmi_cpu, -1, raw_smp_processor_id()) != -1)
     187                 :          0 :                 return NMI_HANDLED;
     188                 :            : 
     189                 :            :         return NMI_DONE;
     190                 :            : }
     191                 :            : #endif
     192                 :            : 
     193                 :          0 : static unsigned long hv_get_tsc_khz(void)
     194                 :            : {
     195                 :          0 :         unsigned long freq;
     196                 :            : 
     197                 :          0 :         rdmsrl(HV_X64_MSR_TSC_FREQUENCY, freq);
     198                 :            : 
     199                 :          0 :         return freq / 1000;
     200                 :            : }
     201                 :            : 
     202                 :            : #if defined(CONFIG_SMP) && IS_ENABLED(CONFIG_HYPERV)
     203                 :            : static void __init hv_smp_prepare_boot_cpu(void)
     204                 :            : {
     205                 :            :         native_smp_prepare_boot_cpu();
     206                 :            : #if defined(CONFIG_X86_64) && defined(CONFIG_PARAVIRT_SPINLOCKS)
     207                 :            :         hv_init_spinlocks();
     208                 :            : #endif
     209                 :            : }
     210                 :            : #endif
     211                 :            : 
     212                 :          0 : static void __init ms_hyperv_init_platform(void)
     213                 :            : {
     214                 :          0 :         int hv_host_info_eax;
     215                 :          0 :         int hv_host_info_ebx;
     216                 :          0 :         int hv_host_info_ecx;
     217                 :          0 :         int hv_host_info_edx;
     218                 :            : 
     219                 :            : #ifdef CONFIG_PARAVIRT
     220                 :          0 :         pv_info.name = "Hyper-V";
     221                 :            : #endif
     222                 :            : 
     223                 :            :         /*
     224                 :            :          * Extract the features and hints
     225                 :            :          */
     226                 :          0 :         ms_hyperv.features = cpuid_eax(HYPERV_CPUID_FEATURES);
     227                 :          0 :         ms_hyperv.misc_features = cpuid_edx(HYPERV_CPUID_FEATURES);
     228                 :          0 :         ms_hyperv.hints    = cpuid_eax(HYPERV_CPUID_ENLIGHTMENT_INFO);
     229                 :            : 
     230                 :          0 :         pr_info("Hyper-V: features 0x%x, hints 0x%x\n",
     231                 :            :                 ms_hyperv.features, ms_hyperv.hints);
     232                 :            : 
     233                 :          0 :         ms_hyperv.max_vp_index = cpuid_eax(HYPERV_CPUID_IMPLEMENT_LIMITS);
     234                 :          0 :         ms_hyperv.max_lp_index = cpuid_ebx(HYPERV_CPUID_IMPLEMENT_LIMITS);
     235                 :            : 
     236                 :          0 :         pr_debug("Hyper-V: max %u virtual processors, %u logical processors\n",
     237                 :            :                  ms_hyperv.max_vp_index, ms_hyperv.max_lp_index);
     238                 :            : 
     239                 :            :         /*
     240                 :            :          * Extract host information.
     241                 :            :          */
     242         [ #  # ]:          0 :         if (cpuid_eax(HYPERV_CPUID_VENDOR_AND_MAX_FUNCTIONS) >=
     243                 :            :             HYPERV_CPUID_VERSION) {
     244                 :          0 :                 hv_host_info_eax = cpuid_eax(HYPERV_CPUID_VERSION);
     245                 :          0 :                 hv_host_info_ebx = cpuid_ebx(HYPERV_CPUID_VERSION);
     246                 :          0 :                 hv_host_info_ecx = cpuid_ecx(HYPERV_CPUID_VERSION);
     247                 :          0 :                 hv_host_info_edx = cpuid_edx(HYPERV_CPUID_VERSION);
     248                 :            : 
     249                 :          0 :                 pr_info("Hyper-V Host Build:%d-%d.%d-%d-%d.%d\n",
     250                 :            :                         hv_host_info_eax, hv_host_info_ebx >> 16,
     251                 :            :                         hv_host_info_ebx & 0xFFFF, hv_host_info_ecx,
     252                 :            :                         hv_host_info_edx >> 24, hv_host_info_edx & 0xFFFFFF);
     253                 :            :         }
     254                 :            : 
     255         [ #  # ]:          0 :         if (ms_hyperv.features & HV_X64_ACCESS_FREQUENCY_MSRS &&
     256         [ #  # ]:          0 :             ms_hyperv.misc_features & HV_FEATURE_FREQUENCY_MSRS_AVAILABLE) {
     257                 :          0 :                 x86_platform.calibrate_tsc = hv_get_tsc_khz;
     258                 :          0 :                 x86_platform.calibrate_cpu = hv_get_tsc_khz;
     259                 :            :         }
     260                 :            : 
     261         [ #  # ]:          0 :         if (ms_hyperv.hints & HV_X64_ENLIGHTENED_VMCS_RECOMMENDED) {
     262                 :          0 :                 ms_hyperv.nested_features =
     263                 :            :                         cpuid_eax(HYPERV_CPUID_NESTED_FEATURES);
     264                 :            :         }
     265                 :            : 
     266                 :            : #ifdef CONFIG_X86_LOCAL_APIC
     267         [ #  # ]:          0 :         if (ms_hyperv.features & HV_X64_ACCESS_FREQUENCY_MSRS &&
     268         [ #  # ]:          0 :             ms_hyperv.misc_features & HV_FEATURE_FREQUENCY_MSRS_AVAILABLE) {
     269                 :            :                 /*
     270                 :            :                  * Get the APIC frequency.
     271                 :            :                  */
     272                 :          0 :                 u64     hv_lapic_frequency;
     273                 :            : 
     274                 :          0 :                 rdmsrl(HV_X64_MSR_APIC_FREQUENCY, hv_lapic_frequency);
     275                 :          0 :                 hv_lapic_frequency = div_u64(hv_lapic_frequency, HZ);
     276                 :          0 :                 lapic_timer_period = hv_lapic_frequency;
     277                 :          0 :                 pr_info("Hyper-V: LAPIC Timer Frequency: %#x\n",
     278                 :            :                         lapic_timer_period);
     279                 :            :         }
     280                 :            : 
     281                 :          0 :         register_nmi_handler(NMI_UNKNOWN, hv_nmi_unknown, NMI_FLAG_FIRST,
     282                 :            :                              "hv_nmi_unknown");
     283                 :            : #endif
     284                 :            : 
     285                 :            : #ifdef CONFIG_X86_IO_APIC
     286                 :          0 :         no_timer_check = 1;
     287                 :            : #endif
     288                 :            : 
     289                 :            : #if IS_ENABLED(CONFIG_HYPERV) && defined(CONFIG_KEXEC_CORE)
     290                 :            :         machine_ops.shutdown = hv_machine_shutdown;
     291                 :            :         machine_ops.crash_shutdown = hv_machine_crash_shutdown;
     292                 :            : #endif
     293         [ #  # ]:          0 :         if (ms_hyperv.features & HV_X64_ACCESS_TSC_INVARIANT) {
     294                 :          0 :                 wrmsrl(HV_X64_MSR_TSC_INVARIANT_CONTROL, 0x1);
     295                 :          0 :                 setup_force_cpu_cap(X86_FEATURE_TSC_RELIABLE);
     296                 :            :         } else {
     297                 :          0 :                 mark_tsc_unstable("running on Hyper-V");
     298                 :            :         }
     299                 :            : 
     300                 :            :         /*
     301                 :            :          * Generation 2 instances don't support reading the NMI status from
     302                 :            :          * 0x61 port.
     303                 :            :          */
     304         [ #  # ]:          0 :         if (efi_enabled(EFI_BOOT))
     305                 :          0 :                 x86_platform.get_nmi_reason = hv_get_nmi_reason;
     306                 :            : 
     307                 :            :         /*
     308                 :            :          * Hyper-V VMs have a PIT emulation quirk such that zeroing the
     309                 :            :          * counter register during PIT shutdown restarts the PIT. So it
     310                 :            :          * continues to interrupt @18.2 HZ. Setting i8253_clear_counter
     311                 :            :          * to false tells pit_shutdown() not to zero the counter so that
     312                 :            :          * the PIT really is shutdown. Generation 2 VMs don't have a PIT,
     313                 :            :          * and setting this value has no effect.
     314                 :            :          */
     315                 :          0 :         i8253_clear_counter_on_shutdown = false;
     316                 :            : 
     317                 :            : #if IS_ENABLED(CONFIG_HYPERV)
     318                 :            :         /*
     319                 :            :          * Setup the hook to get control post apic initialization.
     320                 :            :          */
     321                 :            :         x86_platform.apic_post_init = hyperv_init;
     322                 :            :         hyperv_setup_mmu_ops();
     323                 :            :         /* Setup the IDT for hypervisor callback */
     324                 :            :         alloc_intr_gate(HYPERVISOR_CALLBACK_VECTOR, hyperv_callback_vector);
     325                 :            : 
     326                 :            :         /* Setup the IDT for reenlightenment notifications */
     327                 :            :         if (ms_hyperv.features & HV_X64_ACCESS_REENLIGHTENMENT)
     328                 :            :                 alloc_intr_gate(HYPERV_REENLIGHTENMENT_VECTOR,
     329                 :            :                                 hyperv_reenlightenment_vector);
     330                 :            : 
     331                 :            :         /* Setup the IDT for stimer0 */
     332                 :            :         if (ms_hyperv.misc_features & HV_STIMER_DIRECT_MODE_AVAILABLE)
     333                 :            :                 alloc_intr_gate(HYPERV_STIMER0_VECTOR,
     334                 :            :                                 hv_stimer0_callback_vector);
     335                 :            : 
     336                 :            : # ifdef CONFIG_SMP
     337                 :            :         smp_ops.smp_prepare_boot_cpu = hv_smp_prepare_boot_cpu;
     338                 :            : # endif
     339                 :            : 
     340                 :            :         /*
     341                 :            :          * Hyper-V doesn't provide irq remapping for IO-APIC. To enable x2apic,
     342                 :            :          * set x2apic destination mode to physcial mode when x2apic is available
     343                 :            :          * and Hyper-V IOMMU driver makes sure cpus assigned with IO-APIC irqs
     344                 :            :          * have 8-bit APIC id.
     345                 :            :          */
     346                 :            : # ifdef CONFIG_X86_X2APIC
     347                 :            :         if (x2apic_supported())
     348                 :            :                 x2apic_phys = 1;
     349                 :            : # endif
     350                 :            : 
     351                 :            :         /* Register Hyper-V specific clocksource */
     352                 :            :         hv_init_clocksource();
     353                 :            : #endif
     354                 :          0 : }
     355                 :            : 
     356                 :          0 : void hv_setup_sched_clock(void *sched_clock)
     357                 :            : {
     358                 :            : #ifdef CONFIG_PARAVIRT
     359                 :          0 :         pv_ops.time.sched_clock = sched_clock;
     360                 :            : #endif
     361                 :          0 : }
     362                 :            : 
     363                 :            : const __initconst struct hypervisor_x86 x86_hyper_ms_hyperv = {
     364                 :            :         .name                   = "Microsoft Hyper-V",
     365                 :            :         .detect                 = ms_hyperv_platform,
     366                 :            :         .type                   = X86_HYPER_MS_HYPERV,
     367                 :            :         .init.init_platform     = ms_hyperv_init_platform,
     368                 :            : };

Generated by: LCOV version 1.14