/* Is Linux running on nested Microsoft Hypervisor */ bool hv_nested; struct ms_hyperv_info ms_hyperv;
/* Used in modules via hv_do_hypercall(): see arch/x86/include/asm/mshyperv.h */ bool hyperv_paravisor_present __ro_after_init;
EXPORT_SYMBOL_GPL(hyperv_paravisor_present);
void hv_remove_vmbus_handler(void)
{ /* We have no way to deallocate the interrupt gate */
vmbus_handler = NULL;
}
/* * Routines to do per-architecture handling of stimer0 * interrupts when in Direct Mode
*/
DEFINE_IDTENTRY_SYSVEC(sysvec_hyperv_stimer0)
{ struct pt_regs *old_regs = set_irq_regs(regs);
inc_irq_stat(hyperv_stimer0_count); if (hv_stimer0_handler)
hv_stimer0_handler();
add_interrupt_randomness(HYPERV_STIMER0_VECTOR);
apic_eoi();
set_irq_regs(old_regs);
}
/* For x86/x64, override weak placeholders in hyperv_timer.c */ void hv_setup_stimer0_handler(void (*handler)(void))
{
hv_stimer0_handler = handler;
}
void hv_remove_stimer0_handler(void)
{ /* We have no way to deallocate the interrupt gate */
hv_stimer0_handler = NULL;
}
#ifdef CONFIG_KEXEC_CORE staticvoid hv_machine_shutdown(void)
{ if (kexec_in_progress && hv_kexec_handler)
hv_kexec_handler();
/* * Call hv_cpu_die() on all the CPUs, otherwise later the hypervisor * corrupts the old VP Assist Pages and can crash the kexec kernel.
*/ if (kexec_in_progress)
cpuhp_remove_state(CPUHP_AP_HYPERV_ONLINE);
/* The function calls stop_other_cpus(). */
native_machine_shutdown();
/* Disable the hypercall page when there is only 1 active CPU. */ if (kexec_in_progress)
hyperv_cleanup();
} #endif/* CONFIG_KEXEC_CORE */
#ifdef CONFIG_CRASH_DUMP staticvoid hv_machine_crash_shutdown(struct pt_regs *regs)
{ if (hv_crash_handler)
hv_crash_handler(regs);
/* The function calls crash_smp_send_stop(). */
native_machine_crash_shutdown(regs);
/* Disable the hypercall page when there is only 1 active CPU. */
hyperv_cleanup();
} #endif/* CONFIG_CRASH_DUMP */
/* * Hyper-V clock counter resets during hibernation. Save and restore clock * offset during suspend/resume, while also considering the time passed * before suspend. This is to make sure that sched_clock using hv tsc page * based clocksource, proceeds from where it left off during suspend and * it shows correct time for the timestamps of kernel messages after resume.
*/ staticvoid save_hv_clock_tsc_state(void)
{
hv_ref_counter_at_suspend = hv_read_reference_counter();
}
staticvoid restore_hv_clock_tsc_state(void)
{ /* * Adjust the offsets used by hv tsc clocksource to * account for the time spent before hibernation. * adjusted value = reference counter (time) at suspend * - reference counter (time) now.
*/
hv_adj_sched_clock_offset(hv_ref_counter_at_suspend - hv_read_reference_counter());
}
/* * Functions to override save_sched_clock_state and restore_sched_clock_state * functions of x86_platform. The Hyper-V clock counter is reset during * suspend-resume and the offset used to measure time needs to be * corrected, post resume.
*/ staticvoid hv_save_sched_clock_state(void)
{
old_save_sched_clock_state();
save_hv_clock_tsc_state();
}
/* HYPERCALL and VP_INDEX MSRs are mandatory for all features. */
eax = cpuid_eax(HYPERV_CPUID_FEATURES); if (!(eax & HV_MSR_HYPERCALL_AVAILABLE)) {
pr_warn("x86/hyperv: HYPERCALL MSR not available.\n"); return 0;
} if (!(eax & HV_MSR_VP_INDEX_AVAILABLE)) {
pr_warn("x86/hyperv: VP_INDEX MSR not available.\n"); return 0;
}
return HYPERV_CPUID_VENDOR_AND_MAX_FUNCTIONS;
}
#ifdef CONFIG_X86_LOCAL_APIC /* * Prior to WS2016 Debug-VM sends NMIs to all CPUs which makes * it difficult to process CHANNELMSG_UNLOAD in case of crash. Handle * unknown NMI on the first CPU which gets it.
*/ staticint hv_nmi_unknown(unsignedint val, struct pt_regs *regs)
{ static atomic_t nmi_cpu = ATOMIC_INIT(-1); unsignedint old_cpu, this_cpu;
#ifdef CONFIG_X86_64
for_each_present_cpu(i) { if (i == 0) continue;
ret = hv_call_add_logical_proc(numa_cpu_node(i), i, cpu_physical_id(i));
BUG_ON(ret);
}
for_each_present_cpu(i) { if (i == 0) continue;
ret = hv_call_create_vp(numa_cpu_node(i), hv_current_partition_id, i, i);
BUG_ON(ret);
} #endif
} #endif
/* * When a fully enlightened TDX VM runs on Hyper-V, the firmware sets the * HW_REDUCED flag: refer to acpi_tb_create_local_fadt(). Consequently ttyS0 * interrupts can't work because request_irq() -> ... -> irq_to_desc() returns * NULL for ttyS0. This happens because mp_config_acpi_legacy_irqs() sees a * nr_legacy_irqs() of 0, so it doesn't initialize the array 'mp_irqs[]', and * later setup_IO_APIC_irqs() -> find_irq_entry() fails to find the legacy irqs * from the array and hence doesn't create the necessary irq description info. * * Clone arch/x86/kernel/acpi/boot.c: acpi_generic_reduced_hw_init() here, * except don't change 'legacy_pic', which keeps its default value * 'default_legacy_pic'. This way, mp_config_acpi_legacy_irqs() sees a non-zero * nr_legacy_irqs() and eventually serial console interrupts works properly.
*/ staticvoid __init reduced_hw_init(void)
{
x86_init.timers.timer_init = x86_init_noop;
x86_init.irqs.pre_vector_init = x86_init_noop;
}
int hv_get_hypervisor_version(union hv_hypervisor_version_info *info)
{ unsignedint hv_max_functions;
hv_max_functions = cpuid_eax(HYPERV_CPUID_VENDOR_AND_MAX_FUNCTIONS); if (hv_max_functions < HYPERV_CPUID_VERSION) {
pr_err("%s: Could not detect Hyper-V version\n", __func__); return -ENODEV;
}
/* A TDX VM must use x2APIC and doesn't use lazy EOI. */
ms_hyperv.hints &= ~HV_X64_APIC_ACCESS_RECOMMENDED;
if (!ms_hyperv.paravisor_present) { /* * Mark the Hyper-V TSC page feature as disabled * in a TDX VM without paravisor so that the * Invariant TSC, which is a better clocksource * anyway, is used instead.
*/
ms_hyperv.features &= ~HV_MSR_REFERENCE_TSC_AVAILABLE;
/* * The Invariant TSC is expected to be available * in a TDX VM without paravisor, but if not, * print a warning message. The slower Hyper-V MSR-based * Ref Counter should end up being the clocksource.
*/ if (!(ms_hyperv.features & HV_ACCESS_TSC_INVARIANT))
pr_warn("Hyper-V: Invariant TSC is unavailable\n");
/* HV_MSR_CRASH_CTL is unsupported. */
ms_hyperv.misc_features &= ~HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE;
#if IS_ENABLED(CONFIG_HYPERV) #ifdefined(CONFIG_KEXEC_CORE)
machine_ops.shutdown = hv_machine_shutdown; #endif #ifdefined(CONFIG_CRASH_DUMP)
machine_ops.crash_shutdown = hv_machine_crash_shutdown; #endif #endif /* * HV_ACCESS_TSC_INVARIANT is always zero for the root partition. Root * partition doesn't need to write to synthetic MSR to enable invariant * TSC feature. It sees what the hardware provides.
*/ if (ms_hyperv.features & HV_ACCESS_TSC_INVARIANT) { /* * Writing to synthetic MSR 0x40000118 updates/changes the * guest visible CPUIDs. Setting bit 0 of this MSR enables * guests to report invariant TSC feature through CPUID * instruction, CPUID 0x800000007/EDX, bit 8. See code in * early_init_intel() where this bit is examined. The * setting of this MSR bit should happen before init_intel() * is called.
*/
wrmsrq(HV_X64_MSR_TSC_INVARIANT_CONTROL, HV_EXPOSE_INVARIANT_TSC);
setup_force_cpu_cap(X86_FEATURE_TSC_RELIABLE);
}
/* * 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;
#if IS_ENABLED(CONFIG_HYPERV) if ((hv_get_isolation_type() == HV_ISOLATION_TYPE_VBS) ||
ms_hyperv.paravisor_present)
hv_vtom_init(); /* * Setup the hook to get control post apic initialization.
*/
x86_platform.apic_post_init = hyperv_init;
hyperv_setup_mmu_ops();
/* Install system interrupt handler for hypervisor callback */
sysvec_install(HYPERVISOR_CALLBACK_VECTOR, sysvec_hyperv_callback);
/* Install system interrupt handler for reenlightenment notifications */ if (ms_hyperv.features & HV_ACCESS_REENLIGHTENMENT) {
sysvec_install(HYPERV_REENLIGHTENMENT_VECTOR, sysvec_hyperv_reenlightenment);
}
/* Install system interrupt handler for stimer0 */ if (ms_hyperv.misc_features & HV_STIMER_DIRECT_MODE_AVAILABLE) {
sysvec_install(HYPERV_STIMER0_VECTOR, sysvec_hyperv_stimer0);
}
/* * Hyper-V doesn't provide irq remapping for IO-APIC. To enable x2apic, * set x2apic destination mode to physical mode when x2apic is available * and Hyper-V IOMMU driver makes sure cpus assigned with IO-APIC irqs * have 8-bit APIC id.
*/ # ifdef CONFIG_X86_X2APIC if (x2apic_supported())
x2apic_phys = 1; # endif
/* Register Hyper-V specific clocksource */
hv_init_clocksource();
x86_setup_ops_for_tsc_pg_clock();
hv_vtl_init_platform(); #endif /* * TSC should be marked as unstable only after Hyper-V * clocksource has been initialized. This ensures that the * stability of the sched_clock is not altered. * * HV_ACCESS_TSC_INVARIANT is always zero for the root partition. No * need to check for it.
*/ if (!hv_root_partition() &&
!(ms_hyperv.features & HV_ACCESS_TSC_INVARIANT))
mark_tsc_unstable("running on Hyper-V");
/* * If ms_hyperv_msi_ext_dest_id() returns true, hyperv_prepare_irq_remapping() * returns -ENODEV and the Hyper-V IOMMU driver is not used; instead, the * generic support of the 15-bit APIC ID is used: see __irq_msi_compose_msg(). * * Note: for a VM on Hyper-V, the I/O-APIC is the only device which * (logically) generates MSIs directly to the system APIC irq domain. * There is no HPET, and PCI MSI/MSI-X interrupts are remapped by the * pci-hyperv host bridge. * * Note: for a Hyper-V root partition, this will always return false. * The hypervisor doesn't expose these HYPERV_CPUID_VIRT_STACK_* cpuids by * default, they are implemented as intercepts by the Windows Hyper-V stack. * Even a nested root partition (L2 root) will not get them because the * nested (L1) hypervisor filters them out.
*/ staticbool __init ms_hyperv_msi_ext_dest_id(void)
{
u32 eax;
eax = cpuid_eax(HYPERV_CPUID_VIRT_STACK_INTERFACE); if (eax != HYPERV_VS_INTERFACE_EAX_SIGNATURE) returnfalse;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.