.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * HyperV Detection code. |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright (C) 2010, Novell, Inc. |
---|
5 | 6 | * Author : K. Y. Srinivasan <ksrinivasan@novell.com> |
---|
6 | | - * |
---|
7 | | - * This program is free software; you can redistribute it and/or modify |
---|
8 | | - * it under the terms of the GNU General Public License as published by |
---|
9 | | - * the Free Software Foundation; version 2 of the License. |
---|
10 | | - * |
---|
11 | 7 | */ |
---|
12 | 8 | |
---|
13 | 9 | #include <linux/types.h> |
---|
.. | .. |
---|
21 | 17 | #include <linux/irq.h> |
---|
22 | 18 | #include <linux/kexec.h> |
---|
23 | 19 | #include <linux/i8253.h> |
---|
| 20 | +#include <linux/random.h> |
---|
24 | 21 | #include <asm/processor.h> |
---|
25 | 22 | #include <asm/hypervisor.h> |
---|
26 | 23 | #include <asm/hyperv-tlfs.h> |
---|
27 | 24 | #include <asm/mshyperv.h> |
---|
28 | 25 | #include <asm/desc.h> |
---|
| 26 | +#include <asm/idtentry.h> |
---|
29 | 27 | #include <asm/irq_regs.h> |
---|
30 | 28 | #include <asm/i8259.h> |
---|
31 | 29 | #include <asm/apic.h> |
---|
32 | 30 | #include <asm/timer.h> |
---|
33 | 31 | #include <asm/reboot.h> |
---|
34 | 32 | #include <asm/nmi.h> |
---|
| 33 | +#include <clocksource/hyperv_timer.h> |
---|
35 | 34 | |
---|
36 | 35 | struct ms_hyperv_info ms_hyperv; |
---|
37 | 36 | EXPORT_SYMBOL_GPL(ms_hyperv); |
---|
.. | .. |
---|
42 | 41 | static void (*hv_kexec_handler)(void); |
---|
43 | 42 | static void (*hv_crash_handler)(struct pt_regs *regs); |
---|
44 | 43 | |
---|
45 | | -__visible void __irq_entry hyperv_vector_handler(struct pt_regs *regs) |
---|
| 44 | +DEFINE_IDTENTRY_SYSVEC(sysvec_hyperv_callback) |
---|
46 | 45 | { |
---|
47 | 46 | struct pt_regs *old_regs = set_irq_regs(regs); |
---|
48 | 47 | |
---|
49 | | - entering_irq(); |
---|
50 | 48 | inc_irq_stat(irq_hv_callback_count); |
---|
51 | 49 | if (vmbus_handler) |
---|
52 | 50 | vmbus_handler(); |
---|
.. | .. |
---|
54 | 52 | if (ms_hyperv.hints & HV_DEPRECATING_AEOI_RECOMMENDED) |
---|
55 | 53 | ack_APIC_irq(); |
---|
56 | 54 | |
---|
57 | | - exiting_irq(); |
---|
58 | 55 | set_irq_regs(old_regs); |
---|
59 | 56 | } |
---|
60 | 57 | |
---|
61 | | -void hv_setup_vmbus_irq(void (*handler)(void)) |
---|
| 58 | +int hv_setup_vmbus_irq(int irq, void (*handler)(void)) |
---|
62 | 59 | { |
---|
| 60 | + /* |
---|
| 61 | + * The 'irq' argument is ignored on x86/x64 because a hard-coded |
---|
| 62 | + * interrupt vector is used for Hyper-V interrupts. |
---|
| 63 | + */ |
---|
63 | 64 | vmbus_handler = handler; |
---|
| 65 | + return 0; |
---|
64 | 66 | } |
---|
65 | 67 | |
---|
66 | 68 | void hv_remove_vmbus_irq(void) |
---|
.. | .. |
---|
75 | 77 | * Routines to do per-architecture handling of stimer0 |
---|
76 | 78 | * interrupts when in Direct Mode |
---|
77 | 79 | */ |
---|
78 | | - |
---|
79 | | -__visible void __irq_entry hv_stimer0_vector_handler(struct pt_regs *regs) |
---|
| 80 | +DEFINE_IDTENTRY_SYSVEC(sysvec_hyperv_stimer0) |
---|
80 | 81 | { |
---|
81 | 82 | struct pt_regs *old_regs = set_irq_regs(regs); |
---|
82 | 83 | |
---|
83 | | - entering_irq(); |
---|
84 | 84 | inc_irq_stat(hyperv_stimer0_count); |
---|
85 | 85 | if (hv_stimer0_handler) |
---|
86 | 86 | hv_stimer0_handler(); |
---|
| 87 | + add_interrupt_randomness(HYPERV_STIMER0_VECTOR); |
---|
87 | 88 | ack_APIC_irq(); |
---|
88 | 89 | |
---|
89 | | - exiting_irq(); |
---|
90 | 90 | set_irq_regs(old_regs); |
---|
91 | 91 | } |
---|
92 | 92 | |
---|
93 | 93 | int hv_setup_stimer0_irq(int *irq, int *vector, void (*handler)(void)) |
---|
94 | 94 | { |
---|
95 | 95 | *vector = HYPERV_STIMER0_VECTOR; |
---|
96 | | - *irq = 0; /* Unused on x86/x64 */ |
---|
| 96 | + *irq = -1; /* Unused on x86/x64 */ |
---|
97 | 97 | hv_stimer0_handler = handler; |
---|
98 | 98 | return 0; |
---|
99 | 99 | } |
---|
.. | .. |
---|
135 | 135 | { |
---|
136 | 136 | if (kexec_in_progress && hv_kexec_handler) |
---|
137 | 137 | hv_kexec_handler(); |
---|
| 138 | + |
---|
| 139 | + /* |
---|
| 140 | + * Call hv_cpu_die() on all the CPUs, otherwise later the hypervisor |
---|
| 141 | + * corrupts the old VP Assist Pages and can crash the kexec kernel. |
---|
| 142 | + */ |
---|
| 143 | + if (kexec_in_progress && hyperv_init_cpuhp > 0) |
---|
| 144 | + cpuhp_remove_state(hyperv_init_cpuhp); |
---|
| 145 | + |
---|
| 146 | + /* The function calls stop_other_cpus(). */ |
---|
138 | 147 | native_machine_shutdown(); |
---|
| 148 | + |
---|
| 149 | + /* Disable the hypercall page when there is only 1 active CPU. */ |
---|
| 150 | + if (kexec_in_progress) |
---|
| 151 | + hyperv_cleanup(); |
---|
139 | 152 | } |
---|
140 | 153 | |
---|
141 | 154 | static void hv_machine_crash_shutdown(struct pt_regs *regs) |
---|
142 | 155 | { |
---|
143 | 156 | if (hv_crash_handler) |
---|
144 | 157 | hv_crash_handler(regs); |
---|
| 158 | + |
---|
| 159 | + /* The function calls crash_smp_send_stop(). */ |
---|
145 | 160 | native_machine_crash_shutdown(regs); |
---|
| 161 | + |
---|
| 162 | + /* Disable the hypercall page when there is only 1 active CPU. */ |
---|
| 163 | + hyperv_cleanup(); |
---|
146 | 164 | } |
---|
147 | 165 | #endif /* CONFIG_KEXEC_CORE */ |
---|
148 | 166 | #endif /* CONFIG_HYPERV */ |
---|
.. | .. |
---|
200 | 218 | return freq / 1000; |
---|
201 | 219 | } |
---|
202 | 220 | |
---|
| 221 | +#if defined(CONFIG_SMP) && IS_ENABLED(CONFIG_HYPERV) |
---|
| 222 | +static void __init hv_smp_prepare_boot_cpu(void) |
---|
| 223 | +{ |
---|
| 224 | + native_smp_prepare_boot_cpu(); |
---|
| 225 | +#if defined(CONFIG_X86_64) && defined(CONFIG_PARAVIRT_SPINLOCKS) |
---|
| 226 | + hv_init_spinlocks(); |
---|
| 227 | +#endif |
---|
| 228 | +} |
---|
| 229 | +#endif |
---|
| 230 | + |
---|
203 | 231 | static void __init ms_hyperv_init_platform(void) |
---|
204 | 232 | { |
---|
205 | 233 | int hv_host_info_eax; |
---|
206 | 234 | int hv_host_info_ebx; |
---|
207 | 235 | int hv_host_info_ecx; |
---|
208 | 236 | int hv_host_info_edx; |
---|
| 237 | + |
---|
| 238 | +#ifdef CONFIG_PARAVIRT |
---|
| 239 | + pv_info.name = "Hyper-V"; |
---|
| 240 | +#endif |
---|
209 | 241 | |
---|
210 | 242 | /* |
---|
211 | 243 | * Extract the features and hints |
---|
.. | .. |
---|
239 | 271 | hv_host_info_edx >> 24, hv_host_info_edx & 0xFFFFFF); |
---|
240 | 272 | } |
---|
241 | 273 | |
---|
242 | | - if (ms_hyperv.features & HV_X64_ACCESS_FREQUENCY_MSRS && |
---|
| 274 | + if (ms_hyperv.features & HV_ACCESS_FREQUENCY_MSRS && |
---|
243 | 275 | ms_hyperv.misc_features & HV_FEATURE_FREQUENCY_MSRS_AVAILABLE) { |
---|
244 | 276 | x86_platform.calibrate_tsc = hv_get_tsc_khz; |
---|
245 | 277 | x86_platform.calibrate_cpu = hv_get_tsc_khz; |
---|
.. | .. |
---|
261 | 293 | crash_kexec_post_notifiers = true; |
---|
262 | 294 | |
---|
263 | 295 | #ifdef CONFIG_X86_LOCAL_APIC |
---|
264 | | - if (ms_hyperv.features & HV_X64_ACCESS_FREQUENCY_MSRS && |
---|
| 296 | + if (ms_hyperv.features & HV_ACCESS_FREQUENCY_MSRS && |
---|
265 | 297 | ms_hyperv.misc_features & HV_FEATURE_FREQUENCY_MSRS_AVAILABLE) { |
---|
266 | 298 | /* |
---|
267 | 299 | * Get the APIC frequency. |
---|
.. | .. |
---|
270 | 302 | |
---|
271 | 303 | rdmsrl(HV_X64_MSR_APIC_FREQUENCY, hv_lapic_frequency); |
---|
272 | 304 | hv_lapic_frequency = div_u64(hv_lapic_frequency, HZ); |
---|
273 | | - lapic_timer_frequency = hv_lapic_frequency; |
---|
| 305 | + lapic_timer_period = hv_lapic_frequency; |
---|
274 | 306 | pr_info("Hyper-V: LAPIC Timer Frequency: %#x\n", |
---|
275 | | - lapic_timer_frequency); |
---|
| 307 | + lapic_timer_period); |
---|
276 | 308 | } |
---|
277 | 309 | |
---|
278 | 310 | register_nmi_handler(NMI_UNKNOWN, hv_nmi_unknown, NMI_FLAG_FIRST, |
---|
.. | .. |
---|
287 | 319 | machine_ops.shutdown = hv_machine_shutdown; |
---|
288 | 320 | machine_ops.crash_shutdown = hv_machine_crash_shutdown; |
---|
289 | 321 | #endif |
---|
290 | | - mark_tsc_unstable("running on Hyper-V"); |
---|
| 322 | + if (ms_hyperv.features & HV_ACCESS_TSC_INVARIANT) { |
---|
| 323 | + wrmsrl(HV_X64_MSR_TSC_INVARIANT_CONTROL, 0x1); |
---|
| 324 | + setup_force_cpu_cap(X86_FEATURE_TSC_RELIABLE); |
---|
| 325 | + } |
---|
291 | 326 | |
---|
292 | 327 | /* |
---|
293 | 328 | * Generation 2 instances don't support reading the NMI status from |
---|
.. | .. |
---|
313 | 348 | x86_platform.apic_post_init = hyperv_init; |
---|
314 | 349 | hyperv_setup_mmu_ops(); |
---|
315 | 350 | /* Setup the IDT for hypervisor callback */ |
---|
316 | | - alloc_intr_gate(HYPERVISOR_CALLBACK_VECTOR, hyperv_callback_vector); |
---|
| 351 | + alloc_intr_gate(HYPERVISOR_CALLBACK_VECTOR, asm_sysvec_hyperv_callback); |
---|
317 | 352 | |
---|
318 | 353 | /* Setup the IDT for reenlightenment notifications */ |
---|
319 | | - if (ms_hyperv.features & HV_X64_ACCESS_REENLIGHTENMENT) |
---|
| 354 | + if (ms_hyperv.features & HV_ACCESS_REENLIGHTENMENT) { |
---|
320 | 355 | alloc_intr_gate(HYPERV_REENLIGHTENMENT_VECTOR, |
---|
321 | | - hyperv_reenlightenment_vector); |
---|
| 356 | + asm_sysvec_hyperv_reenlightenment); |
---|
| 357 | + } |
---|
322 | 358 | |
---|
323 | 359 | /* Setup the IDT for stimer0 */ |
---|
324 | | - if (ms_hyperv.misc_features & HV_STIMER_DIRECT_MODE_AVAILABLE) |
---|
| 360 | + if (ms_hyperv.misc_features & HV_STIMER_DIRECT_MODE_AVAILABLE) { |
---|
325 | 361 | alloc_intr_gate(HYPERV_STIMER0_VECTOR, |
---|
326 | | - hv_stimer0_callback_vector); |
---|
| 362 | + asm_sysvec_hyperv_stimer0); |
---|
| 363 | + } |
---|
| 364 | + |
---|
| 365 | +# ifdef CONFIG_SMP |
---|
| 366 | + smp_ops.smp_prepare_boot_cpu = hv_smp_prepare_boot_cpu; |
---|
| 367 | +# endif |
---|
| 368 | + |
---|
| 369 | + /* |
---|
| 370 | + * Hyper-V doesn't provide irq remapping for IO-APIC. To enable x2apic, |
---|
| 371 | + * set x2apic destination mode to physcial mode when x2apic is available |
---|
| 372 | + * and Hyper-V IOMMU driver makes sure cpus assigned with IO-APIC irqs |
---|
| 373 | + * have 8-bit APIC id. |
---|
| 374 | + */ |
---|
| 375 | +# ifdef CONFIG_X86_X2APIC |
---|
| 376 | + if (x2apic_supported()) |
---|
| 377 | + x2apic_phys = 1; |
---|
| 378 | +# endif |
---|
| 379 | + |
---|
| 380 | + /* Register Hyper-V specific clocksource */ |
---|
| 381 | + hv_init_clocksource(); |
---|
327 | 382 | #endif |
---|
| 383 | + /* |
---|
| 384 | + * TSC should be marked as unstable only after Hyper-V |
---|
| 385 | + * clocksource has been initialized. This ensures that the |
---|
| 386 | + * stability of the sched_clock is not altered. |
---|
| 387 | + */ |
---|
| 388 | + if (!(ms_hyperv.features & HV_ACCESS_TSC_INVARIANT)) |
---|
| 389 | + mark_tsc_unstable("running on Hyper-V"); |
---|
328 | 390 | } |
---|
329 | 391 | |
---|
330 | 392 | const __initconst struct hypervisor_x86 x86_hyper_ms_hyperv = { |
---|