162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * X86 specific Hyper-V initialization code.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2016, Microsoft, Inc.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author : K. Y. Srinivasan <kys@microsoft.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#define pr_fmt(fmt)  "Hyper-V: " fmt
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/efi.h>
1362306a36Sopenharmony_ci#include <linux/types.h>
1462306a36Sopenharmony_ci#include <linux/bitfield.h>
1562306a36Sopenharmony_ci#include <linux/io.h>
1662306a36Sopenharmony_ci#include <asm/apic.h>
1762306a36Sopenharmony_ci#include <asm/desc.h>
1862306a36Sopenharmony_ci#include <asm/e820/api.h>
1962306a36Sopenharmony_ci#include <asm/sev.h>
2062306a36Sopenharmony_ci#include <asm/ibt.h>
2162306a36Sopenharmony_ci#include <asm/hypervisor.h>
2262306a36Sopenharmony_ci#include <asm/hyperv-tlfs.h>
2362306a36Sopenharmony_ci#include <asm/mshyperv.h>
2462306a36Sopenharmony_ci#include <asm/idtentry.h>
2562306a36Sopenharmony_ci#include <asm/set_memory.h>
2662306a36Sopenharmony_ci#include <linux/kexec.h>
2762306a36Sopenharmony_ci#include <linux/version.h>
2862306a36Sopenharmony_ci#include <linux/vmalloc.h>
2962306a36Sopenharmony_ci#include <linux/mm.h>
3062306a36Sopenharmony_ci#include <linux/hyperv.h>
3162306a36Sopenharmony_ci#include <linux/slab.h>
3262306a36Sopenharmony_ci#include <linux/kernel.h>
3362306a36Sopenharmony_ci#include <linux/cpuhotplug.h>
3462306a36Sopenharmony_ci#include <linux/syscore_ops.h>
3562306a36Sopenharmony_ci#include <clocksource/hyperv_timer.h>
3662306a36Sopenharmony_ci#include <linux/highmem.h>
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ciint hyperv_init_cpuhp;
3962306a36Sopenharmony_ciu64 hv_current_partition_id = ~0ull;
4062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hv_current_partition_id);
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_civoid *hv_hypercall_pg;
4362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hv_hypercall_pg);
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ciunion hv_ghcb * __percpu *hv_ghcb_pg;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci/* Storage to save the hypercall page temporarily for hibernation */
4862306a36Sopenharmony_cistatic void *hv_hypercall_pg_saved;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistruct hv_vp_assist_page **hv_vp_assist_page;
5162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hv_vp_assist_page);
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic int hyperv_init_ghcb(void)
5462306a36Sopenharmony_ci{
5562306a36Sopenharmony_ci	u64 ghcb_gpa;
5662306a36Sopenharmony_ci	void *ghcb_va;
5762306a36Sopenharmony_ci	void **ghcb_base;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	if (!ms_hyperv.paravisor_present || !hv_isolation_type_snp())
6062306a36Sopenharmony_ci		return 0;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	if (!hv_ghcb_pg)
6362306a36Sopenharmony_ci		return -EINVAL;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	/*
6662306a36Sopenharmony_ci	 * GHCB page is allocated by paravisor. The address
6762306a36Sopenharmony_ci	 * returned by MSR_AMD64_SEV_ES_GHCB is above shared
6862306a36Sopenharmony_ci	 * memory boundary and map it here.
6962306a36Sopenharmony_ci	 */
7062306a36Sopenharmony_ci	rdmsrl(MSR_AMD64_SEV_ES_GHCB, ghcb_gpa);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	/* Mask out vTOM bit. ioremap_cache() maps decrypted */
7362306a36Sopenharmony_ci	ghcb_gpa &= ~ms_hyperv.shared_gpa_boundary;
7462306a36Sopenharmony_ci	ghcb_va = (void *)ioremap_cache(ghcb_gpa, HV_HYP_PAGE_SIZE);
7562306a36Sopenharmony_ci	if (!ghcb_va)
7662306a36Sopenharmony_ci		return -ENOMEM;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	ghcb_base = (void **)this_cpu_ptr(hv_ghcb_pg);
7962306a36Sopenharmony_ci	*ghcb_base = ghcb_va;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	return 0;
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic int hv_cpu_init(unsigned int cpu)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	union hv_vp_assist_msr_contents msr = { 0 };
8762306a36Sopenharmony_ci	struct hv_vp_assist_page **hvp;
8862306a36Sopenharmony_ci	int ret;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	ret = hv_common_cpu_init(cpu);
9162306a36Sopenharmony_ci	if (ret)
9262306a36Sopenharmony_ci		return ret;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	if (!hv_vp_assist_page)
9562306a36Sopenharmony_ci		return 0;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	hvp = &hv_vp_assist_page[cpu];
9862306a36Sopenharmony_ci	if (hv_root_partition) {
9962306a36Sopenharmony_ci		/*
10062306a36Sopenharmony_ci		 * For root partition we get the hypervisor provided VP assist
10162306a36Sopenharmony_ci		 * page, instead of allocating a new page.
10262306a36Sopenharmony_ci		 */
10362306a36Sopenharmony_ci		rdmsrl(HV_X64_MSR_VP_ASSIST_PAGE, msr.as_uint64);
10462306a36Sopenharmony_ci		*hvp = memremap(msr.pfn << HV_X64_MSR_VP_ASSIST_PAGE_ADDRESS_SHIFT,
10562306a36Sopenharmony_ci				PAGE_SIZE, MEMREMAP_WB);
10662306a36Sopenharmony_ci	} else {
10762306a36Sopenharmony_ci		/*
10862306a36Sopenharmony_ci		 * The VP assist page is an "overlay" page (see Hyper-V TLFS's
10962306a36Sopenharmony_ci		 * Section 5.2.1 "GPA Overlay Pages"). Here it must be zeroed
11062306a36Sopenharmony_ci		 * out to make sure we always write the EOI MSR in
11162306a36Sopenharmony_ci		 * hv_apic_eoi_write() *after* the EOI optimization is disabled
11262306a36Sopenharmony_ci		 * in hv_cpu_die(), otherwise a CPU may not be stopped in the
11362306a36Sopenharmony_ci		 * case of CPU offlining and the VM will hang.
11462306a36Sopenharmony_ci		 */
11562306a36Sopenharmony_ci		if (!*hvp) {
11662306a36Sopenharmony_ci			*hvp = __vmalloc(PAGE_SIZE, GFP_KERNEL | __GFP_ZERO);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci			/*
11962306a36Sopenharmony_ci			 * Hyper-V should never specify a VM that is a Confidential
12062306a36Sopenharmony_ci			 * VM and also running in the root partition. Root partition
12162306a36Sopenharmony_ci			 * is blocked to run in Confidential VM. So only decrypt assist
12262306a36Sopenharmony_ci			 * page in non-root partition here.
12362306a36Sopenharmony_ci			 */
12462306a36Sopenharmony_ci			if (*hvp && !ms_hyperv.paravisor_present && hv_isolation_type_snp()) {
12562306a36Sopenharmony_ci				WARN_ON_ONCE(set_memory_decrypted((unsigned long)(*hvp), 1));
12662306a36Sopenharmony_ci				memset(*hvp, 0, PAGE_SIZE);
12762306a36Sopenharmony_ci			}
12862306a36Sopenharmony_ci		}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci		if (*hvp)
13162306a36Sopenharmony_ci			msr.pfn = vmalloc_to_pfn(*hvp);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	}
13462306a36Sopenharmony_ci	if (!WARN_ON(!(*hvp))) {
13562306a36Sopenharmony_ci		msr.enable = 1;
13662306a36Sopenharmony_ci		wrmsrl(HV_X64_MSR_VP_ASSIST_PAGE, msr.as_uint64);
13762306a36Sopenharmony_ci	}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	return hyperv_init_ghcb();
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic void (*hv_reenlightenment_cb)(void);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistatic void hv_reenlightenment_notify(struct work_struct *dummy)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	struct hv_tsc_emulation_status emu_status;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	rdmsrl(HV_X64_MSR_TSC_EMULATION_STATUS, *(u64 *)&emu_status);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	/* Don't issue the callback if TSC accesses are not emulated */
15162306a36Sopenharmony_ci	if (hv_reenlightenment_cb && emu_status.inprogress)
15262306a36Sopenharmony_ci		hv_reenlightenment_cb();
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_cistatic DECLARE_DELAYED_WORK(hv_reenlightenment_work, hv_reenlightenment_notify);
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_civoid hyperv_stop_tsc_emulation(void)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	u64 freq;
15962306a36Sopenharmony_ci	struct hv_tsc_emulation_status emu_status;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	rdmsrl(HV_X64_MSR_TSC_EMULATION_STATUS, *(u64 *)&emu_status);
16262306a36Sopenharmony_ci	emu_status.inprogress = 0;
16362306a36Sopenharmony_ci	wrmsrl(HV_X64_MSR_TSC_EMULATION_STATUS, *(u64 *)&emu_status);
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	rdmsrl(HV_X64_MSR_TSC_FREQUENCY, freq);
16662306a36Sopenharmony_ci	tsc_khz = div64_u64(freq, 1000);
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hyperv_stop_tsc_emulation);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_cistatic inline bool hv_reenlightenment_available(void)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	/*
17362306a36Sopenharmony_ci	 * Check for required features and privileges to make TSC frequency
17462306a36Sopenharmony_ci	 * change notifications work.
17562306a36Sopenharmony_ci	 */
17662306a36Sopenharmony_ci	return ms_hyperv.features & HV_ACCESS_FREQUENCY_MSRS &&
17762306a36Sopenharmony_ci		ms_hyperv.misc_features & HV_FEATURE_FREQUENCY_MSRS_AVAILABLE &&
17862306a36Sopenharmony_ci		ms_hyperv.features & HV_ACCESS_REENLIGHTENMENT;
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ciDEFINE_IDTENTRY_SYSVEC(sysvec_hyperv_reenlightenment)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	apic_eoi();
18462306a36Sopenharmony_ci	inc_irq_stat(irq_hv_reenlightenment_count);
18562306a36Sopenharmony_ci	schedule_delayed_work(&hv_reenlightenment_work, HZ/10);
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_civoid set_hv_tscchange_cb(void (*cb)(void))
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	struct hv_reenlightenment_control re_ctrl = {
19162306a36Sopenharmony_ci		.vector = HYPERV_REENLIGHTENMENT_VECTOR,
19262306a36Sopenharmony_ci		.enabled = 1,
19362306a36Sopenharmony_ci	};
19462306a36Sopenharmony_ci	struct hv_tsc_emulation_control emu_ctrl = {.enabled = 1};
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	if (!hv_reenlightenment_available()) {
19762306a36Sopenharmony_ci		pr_warn("reenlightenment support is unavailable\n");
19862306a36Sopenharmony_ci		return;
19962306a36Sopenharmony_ci	}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	if (!hv_vp_index)
20262306a36Sopenharmony_ci		return;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	hv_reenlightenment_cb = cb;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	/* Make sure callback is registered before we write to MSRs */
20762306a36Sopenharmony_ci	wmb();
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	re_ctrl.target_vp = hv_vp_index[get_cpu()];
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	wrmsrl(HV_X64_MSR_REENLIGHTENMENT_CONTROL, *((u64 *)&re_ctrl));
21262306a36Sopenharmony_ci	wrmsrl(HV_X64_MSR_TSC_EMULATION_CONTROL, *((u64 *)&emu_ctrl));
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	put_cpu();
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(set_hv_tscchange_cb);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_civoid clear_hv_tscchange_cb(void)
21962306a36Sopenharmony_ci{
22062306a36Sopenharmony_ci	struct hv_reenlightenment_control re_ctrl;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	if (!hv_reenlightenment_available())
22362306a36Sopenharmony_ci		return;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	rdmsrl(HV_X64_MSR_REENLIGHTENMENT_CONTROL, *(u64 *)&re_ctrl);
22662306a36Sopenharmony_ci	re_ctrl.enabled = 0;
22762306a36Sopenharmony_ci	wrmsrl(HV_X64_MSR_REENLIGHTENMENT_CONTROL, *(u64 *)&re_ctrl);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	hv_reenlightenment_cb = NULL;
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(clear_hv_tscchange_cb);
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_cistatic int hv_cpu_die(unsigned int cpu)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	struct hv_reenlightenment_control re_ctrl;
23662306a36Sopenharmony_ci	unsigned int new_cpu;
23762306a36Sopenharmony_ci	void **ghcb_va;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	if (hv_ghcb_pg) {
24062306a36Sopenharmony_ci		ghcb_va = (void **)this_cpu_ptr(hv_ghcb_pg);
24162306a36Sopenharmony_ci		if (*ghcb_va)
24262306a36Sopenharmony_ci			iounmap(*ghcb_va);
24362306a36Sopenharmony_ci		*ghcb_va = NULL;
24462306a36Sopenharmony_ci	}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	hv_common_cpu_die(cpu);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	if (hv_vp_assist_page && hv_vp_assist_page[cpu]) {
24962306a36Sopenharmony_ci		union hv_vp_assist_msr_contents msr = { 0 };
25062306a36Sopenharmony_ci		if (hv_root_partition) {
25162306a36Sopenharmony_ci			/*
25262306a36Sopenharmony_ci			 * For root partition the VP assist page is mapped to
25362306a36Sopenharmony_ci			 * hypervisor provided page, and thus we unmap the
25462306a36Sopenharmony_ci			 * page here and nullify it, so that in future we have
25562306a36Sopenharmony_ci			 * correct page address mapped in hv_cpu_init.
25662306a36Sopenharmony_ci			 */
25762306a36Sopenharmony_ci			memunmap(hv_vp_assist_page[cpu]);
25862306a36Sopenharmony_ci			hv_vp_assist_page[cpu] = NULL;
25962306a36Sopenharmony_ci			rdmsrl(HV_X64_MSR_VP_ASSIST_PAGE, msr.as_uint64);
26062306a36Sopenharmony_ci			msr.enable = 0;
26162306a36Sopenharmony_ci		}
26262306a36Sopenharmony_ci		wrmsrl(HV_X64_MSR_VP_ASSIST_PAGE, msr.as_uint64);
26362306a36Sopenharmony_ci	}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	if (hv_reenlightenment_cb == NULL)
26662306a36Sopenharmony_ci		return 0;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	rdmsrl(HV_X64_MSR_REENLIGHTENMENT_CONTROL, *((u64 *)&re_ctrl));
26962306a36Sopenharmony_ci	if (re_ctrl.target_vp == hv_vp_index[cpu]) {
27062306a36Sopenharmony_ci		/*
27162306a36Sopenharmony_ci		 * Reassign reenlightenment notifications to some other online
27262306a36Sopenharmony_ci		 * CPU or just disable the feature if there are no online CPUs
27362306a36Sopenharmony_ci		 * left (happens on hibernation).
27462306a36Sopenharmony_ci		 */
27562306a36Sopenharmony_ci		new_cpu = cpumask_any_but(cpu_online_mask, cpu);
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci		if (new_cpu < nr_cpu_ids)
27862306a36Sopenharmony_ci			re_ctrl.target_vp = hv_vp_index[new_cpu];
27962306a36Sopenharmony_ci		else
28062306a36Sopenharmony_ci			re_ctrl.enabled = 0;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci		wrmsrl(HV_X64_MSR_REENLIGHTENMENT_CONTROL, *((u64 *)&re_ctrl));
28362306a36Sopenharmony_ci	}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	return 0;
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cistatic int __init hv_pci_init(void)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	bool gen2vm = efi_enabled(EFI_BOOT);
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	/*
29362306a36Sopenharmony_ci	 * A Generation-2 VM doesn't support legacy PCI/PCIe, so both
29462306a36Sopenharmony_ci	 * raw_pci_ops and raw_pci_ext_ops are NULL, and pci_subsys_init() ->
29562306a36Sopenharmony_ci	 * pcibios_init() doesn't call pcibios_resource_survey() ->
29662306a36Sopenharmony_ci	 * e820__reserve_resources_late(); as a result, any emulated persistent
29762306a36Sopenharmony_ci	 * memory of E820_TYPE_PRAM (12) via the kernel parameter
29862306a36Sopenharmony_ci	 * memmap=nn[KMG]!ss is not added into iomem_resource and hence can't be
29962306a36Sopenharmony_ci	 * detected by register_e820_pmem(). Fix this by directly calling
30062306a36Sopenharmony_ci	 * e820__reserve_resources_late() here: e820__reserve_resources_late()
30162306a36Sopenharmony_ci	 * depends on e820__reserve_resources(), which has been called earlier
30262306a36Sopenharmony_ci	 * from setup_arch(). Note: e820__reserve_resources_late() also adds
30362306a36Sopenharmony_ci	 * any memory of E820_TYPE_PMEM (7) into iomem_resource, and
30462306a36Sopenharmony_ci	 * acpi_nfit_register_region() -> acpi_nfit_insert_resource() ->
30562306a36Sopenharmony_ci	 * region_intersects() returns REGION_INTERSECTS, so the memory of
30662306a36Sopenharmony_ci	 * E820_TYPE_PMEM won't get added twice.
30762306a36Sopenharmony_ci	 *
30862306a36Sopenharmony_ci	 * We return 0 here so that pci_arch_init() won't print the warning:
30962306a36Sopenharmony_ci	 * "PCI: Fatal: No config space access function found"
31062306a36Sopenharmony_ci	 */
31162306a36Sopenharmony_ci	if (gen2vm) {
31262306a36Sopenharmony_ci		e820__reserve_resources_late();
31362306a36Sopenharmony_ci		return 0;
31462306a36Sopenharmony_ci	}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	/* For Generation-1 VM, we'll proceed in pci_arch_init().  */
31762306a36Sopenharmony_ci	return 1;
31862306a36Sopenharmony_ci}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_cistatic int hv_suspend(void)
32162306a36Sopenharmony_ci{
32262306a36Sopenharmony_ci	union hv_x64_msr_hypercall_contents hypercall_msr;
32362306a36Sopenharmony_ci	int ret;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	if (hv_root_partition)
32662306a36Sopenharmony_ci		return -EPERM;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	/*
32962306a36Sopenharmony_ci	 * Reset the hypercall page as it is going to be invalidated
33062306a36Sopenharmony_ci	 * across hibernation. Setting hv_hypercall_pg to NULL ensures
33162306a36Sopenharmony_ci	 * that any subsequent hypercall operation fails safely instead of
33262306a36Sopenharmony_ci	 * crashing due to an access of an invalid page. The hypercall page
33362306a36Sopenharmony_ci	 * pointer is restored on resume.
33462306a36Sopenharmony_ci	 */
33562306a36Sopenharmony_ci	hv_hypercall_pg_saved = hv_hypercall_pg;
33662306a36Sopenharmony_ci	hv_hypercall_pg = NULL;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	/* Disable the hypercall page in the hypervisor */
33962306a36Sopenharmony_ci	rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
34062306a36Sopenharmony_ci	hypercall_msr.enable = 0;
34162306a36Sopenharmony_ci	wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	ret = hv_cpu_die(0);
34462306a36Sopenharmony_ci	return ret;
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_cistatic void hv_resume(void)
34862306a36Sopenharmony_ci{
34962306a36Sopenharmony_ci	union hv_x64_msr_hypercall_contents hypercall_msr;
35062306a36Sopenharmony_ci	int ret;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	ret = hv_cpu_init(0);
35362306a36Sopenharmony_ci	WARN_ON(ret);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	/* Re-enable the hypercall page */
35662306a36Sopenharmony_ci	rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
35762306a36Sopenharmony_ci	hypercall_msr.enable = 1;
35862306a36Sopenharmony_ci	hypercall_msr.guest_physical_address =
35962306a36Sopenharmony_ci		vmalloc_to_pfn(hv_hypercall_pg_saved);
36062306a36Sopenharmony_ci	wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	hv_hypercall_pg = hv_hypercall_pg_saved;
36362306a36Sopenharmony_ci	hv_hypercall_pg_saved = NULL;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	/*
36662306a36Sopenharmony_ci	 * Reenlightenment notifications are disabled by hv_cpu_die(0),
36762306a36Sopenharmony_ci	 * reenable them here if hv_reenlightenment_cb was previously set.
36862306a36Sopenharmony_ci	 */
36962306a36Sopenharmony_ci	if (hv_reenlightenment_cb)
37062306a36Sopenharmony_ci		set_hv_tscchange_cb(hv_reenlightenment_cb);
37162306a36Sopenharmony_ci}
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci/* Note: when the ops are called, only CPU0 is online and IRQs are disabled. */
37462306a36Sopenharmony_cistatic struct syscore_ops hv_syscore_ops = {
37562306a36Sopenharmony_ci	.suspend	= hv_suspend,
37662306a36Sopenharmony_ci	.resume		= hv_resume,
37762306a36Sopenharmony_ci};
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_cistatic void (* __initdata old_setup_percpu_clockev)(void);
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_cistatic void __init hv_stimer_setup_percpu_clockev(void)
38262306a36Sopenharmony_ci{
38362306a36Sopenharmony_ci	/*
38462306a36Sopenharmony_ci	 * Ignore any errors in setting up stimer clockevents
38562306a36Sopenharmony_ci	 * as we can run with the LAPIC timer as a fallback.
38662306a36Sopenharmony_ci	 */
38762306a36Sopenharmony_ci	(void)hv_stimer_alloc(false);
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	/*
39062306a36Sopenharmony_ci	 * Still register the LAPIC timer, because the direct-mode STIMER is
39162306a36Sopenharmony_ci	 * not supported by old versions of Hyper-V. This also allows users
39262306a36Sopenharmony_ci	 * to switch to LAPIC timer via /sys, if they want to.
39362306a36Sopenharmony_ci	 */
39462306a36Sopenharmony_ci	if (old_setup_percpu_clockev)
39562306a36Sopenharmony_ci		old_setup_percpu_clockev();
39662306a36Sopenharmony_ci}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_cistatic void __init hv_get_partition_id(void)
39962306a36Sopenharmony_ci{
40062306a36Sopenharmony_ci	struct hv_get_partition_id *output_page;
40162306a36Sopenharmony_ci	u64 status;
40262306a36Sopenharmony_ci	unsigned long flags;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	local_irq_save(flags);
40562306a36Sopenharmony_ci	output_page = *this_cpu_ptr(hyperv_pcpu_output_arg);
40662306a36Sopenharmony_ci	status = hv_do_hypercall(HVCALL_GET_PARTITION_ID, NULL, output_page);
40762306a36Sopenharmony_ci	if (!hv_result_success(status)) {
40862306a36Sopenharmony_ci		/* No point in proceeding if this failed */
40962306a36Sopenharmony_ci		pr_err("Failed to get partition ID: %lld\n", status);
41062306a36Sopenharmony_ci		BUG();
41162306a36Sopenharmony_ci	}
41262306a36Sopenharmony_ci	hv_current_partition_id = output_page->partition_id;
41362306a36Sopenharmony_ci	local_irq_restore(flags);
41462306a36Sopenharmony_ci}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_HYPERV_VTL_MODE)
41762306a36Sopenharmony_cistatic u8 __init get_vtl(void)
41862306a36Sopenharmony_ci{
41962306a36Sopenharmony_ci	u64 control = HV_HYPERCALL_REP_COMP_1 | HVCALL_GET_VP_REGISTERS;
42062306a36Sopenharmony_ci	struct hv_get_vp_registers_input *input;
42162306a36Sopenharmony_ci	struct hv_get_vp_registers_output *output;
42262306a36Sopenharmony_ci	unsigned long flags;
42362306a36Sopenharmony_ci	u64 ret;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	local_irq_save(flags);
42662306a36Sopenharmony_ci	input = *this_cpu_ptr(hyperv_pcpu_input_arg);
42762306a36Sopenharmony_ci	output = (struct hv_get_vp_registers_output *)input;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	memset(input, 0, struct_size(input, element, 1));
43062306a36Sopenharmony_ci	input->header.partitionid = HV_PARTITION_ID_SELF;
43162306a36Sopenharmony_ci	input->header.vpindex = HV_VP_INDEX_SELF;
43262306a36Sopenharmony_ci	input->header.inputvtl = 0;
43362306a36Sopenharmony_ci	input->element[0].name0 = HV_X64_REGISTER_VSM_VP_STATUS;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	ret = hv_do_hypercall(control, input, output);
43662306a36Sopenharmony_ci	if (hv_result_success(ret)) {
43762306a36Sopenharmony_ci		ret = output->as64.low & HV_X64_VTL_MASK;
43862306a36Sopenharmony_ci	} else {
43962306a36Sopenharmony_ci		pr_err("Failed to get VTL(error: %lld) exiting...\n", ret);
44062306a36Sopenharmony_ci		BUG();
44162306a36Sopenharmony_ci	}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	local_irq_restore(flags);
44462306a36Sopenharmony_ci	return ret;
44562306a36Sopenharmony_ci}
44662306a36Sopenharmony_ci#else
44762306a36Sopenharmony_cistatic inline u8 get_vtl(void) { return 0; }
44862306a36Sopenharmony_ci#endif
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci/*
45162306a36Sopenharmony_ci * This function is to be invoked early in the boot sequence after the
45262306a36Sopenharmony_ci * hypervisor has been detected.
45362306a36Sopenharmony_ci *
45462306a36Sopenharmony_ci * 1. Setup the hypercall page.
45562306a36Sopenharmony_ci * 2. Register Hyper-V specific clocksource.
45662306a36Sopenharmony_ci * 3. Setup Hyper-V specific APIC entry points.
45762306a36Sopenharmony_ci */
45862306a36Sopenharmony_civoid __init hyperv_init(void)
45962306a36Sopenharmony_ci{
46062306a36Sopenharmony_ci	u64 guest_id;
46162306a36Sopenharmony_ci	union hv_x64_msr_hypercall_contents hypercall_msr;
46262306a36Sopenharmony_ci	int cpuhp;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	if (x86_hyper_type != X86_HYPER_MS_HYPERV)
46562306a36Sopenharmony_ci		return;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	if (hv_common_init())
46862306a36Sopenharmony_ci		return;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	/*
47162306a36Sopenharmony_ci	 * The VP assist page is useless to a TDX guest: the only use we
47262306a36Sopenharmony_ci	 * would have for it is lazy EOI, which can not be used with TDX.
47362306a36Sopenharmony_ci	 */
47462306a36Sopenharmony_ci	if (hv_isolation_type_tdx())
47562306a36Sopenharmony_ci		hv_vp_assist_page = NULL;
47662306a36Sopenharmony_ci	else
47762306a36Sopenharmony_ci		hv_vp_assist_page = kcalloc(num_possible_cpus(),
47862306a36Sopenharmony_ci					    sizeof(*hv_vp_assist_page),
47962306a36Sopenharmony_ci					    GFP_KERNEL);
48062306a36Sopenharmony_ci	if (!hv_vp_assist_page) {
48162306a36Sopenharmony_ci		ms_hyperv.hints &= ~HV_X64_ENLIGHTENED_VMCS_RECOMMENDED;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci		if (!hv_isolation_type_tdx())
48462306a36Sopenharmony_ci			goto common_free;
48562306a36Sopenharmony_ci	}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	if (ms_hyperv.paravisor_present && hv_isolation_type_snp()) {
48862306a36Sopenharmony_ci		/* Negotiate GHCB Version. */
48962306a36Sopenharmony_ci		if (!hv_ghcb_negotiate_protocol())
49062306a36Sopenharmony_ci			hv_ghcb_terminate(SEV_TERM_SET_GEN,
49162306a36Sopenharmony_ci					  GHCB_SEV_ES_PROT_UNSUPPORTED);
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci		hv_ghcb_pg = alloc_percpu(union hv_ghcb *);
49462306a36Sopenharmony_ci		if (!hv_ghcb_pg)
49562306a36Sopenharmony_ci			goto free_vp_assist_page;
49662306a36Sopenharmony_ci	}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	cpuhp = cpuhp_setup_state(CPUHP_AP_HYPERV_ONLINE, "x86/hyperv_init:online",
49962306a36Sopenharmony_ci				  hv_cpu_init, hv_cpu_die);
50062306a36Sopenharmony_ci	if (cpuhp < 0)
50162306a36Sopenharmony_ci		goto free_ghcb_page;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	/*
50462306a36Sopenharmony_ci	 * Setup the hypercall page and enable hypercalls.
50562306a36Sopenharmony_ci	 * 1. Register the guest ID
50662306a36Sopenharmony_ci	 * 2. Enable the hypercall and register the hypercall page
50762306a36Sopenharmony_ci	 *
50862306a36Sopenharmony_ci	 * A TDX VM with no paravisor only uses TDX GHCI rather than hv_hypercall_pg:
50962306a36Sopenharmony_ci	 * when the hypercall input is a page, such a VM must pass a decrypted
51062306a36Sopenharmony_ci	 * page to Hyper-V, e.g. hv_post_message() uses the per-CPU page
51162306a36Sopenharmony_ci	 * hyperv_pcpu_input_arg, which is decrypted if no paravisor is present.
51262306a36Sopenharmony_ci	 *
51362306a36Sopenharmony_ci	 * A TDX VM with the paravisor uses hv_hypercall_pg for most hypercalls,
51462306a36Sopenharmony_ci	 * which are handled by the paravisor and the VM must use an encrypted
51562306a36Sopenharmony_ci	 * input page: in such a VM, the hyperv_pcpu_input_arg is encrypted and
51662306a36Sopenharmony_ci	 * used in the hypercalls, e.g. see hv_mark_gpa_visibility() and
51762306a36Sopenharmony_ci	 * hv_arch_irq_unmask(). Such a VM uses TDX GHCI for two hypercalls:
51862306a36Sopenharmony_ci	 * 1. HVCALL_SIGNAL_EVENT: see vmbus_set_event() and _hv_do_fast_hypercall8().
51962306a36Sopenharmony_ci	 * 2. HVCALL_POST_MESSAGE: the input page must be a decrypted page, i.e.
52062306a36Sopenharmony_ci	 * hv_post_message() in such a VM can't use the encrypted hyperv_pcpu_input_arg;
52162306a36Sopenharmony_ci	 * instead, hv_post_message() uses the post_msg_page, which is decrypted
52262306a36Sopenharmony_ci	 * in such a VM and is only used in such a VM.
52362306a36Sopenharmony_ci	 */
52462306a36Sopenharmony_ci	guest_id = hv_generate_guest_id(LINUX_VERSION_CODE);
52562306a36Sopenharmony_ci	wrmsrl(HV_X64_MSR_GUEST_OS_ID, guest_id);
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	/* With the paravisor, the VM must also write the ID via GHCB/GHCI */
52862306a36Sopenharmony_ci	hv_ivm_msr_write(HV_X64_MSR_GUEST_OS_ID, guest_id);
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	/* A TDX VM with no paravisor only uses TDX GHCI rather than hv_hypercall_pg */
53162306a36Sopenharmony_ci	if (hv_isolation_type_tdx() && !ms_hyperv.paravisor_present)
53262306a36Sopenharmony_ci		goto skip_hypercall_pg_init;
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	hv_hypercall_pg = __vmalloc_node_range(PAGE_SIZE, 1, VMALLOC_START,
53562306a36Sopenharmony_ci			VMALLOC_END, GFP_KERNEL, PAGE_KERNEL_ROX,
53662306a36Sopenharmony_ci			VM_FLUSH_RESET_PERMS, NUMA_NO_NODE,
53762306a36Sopenharmony_ci			__builtin_return_address(0));
53862306a36Sopenharmony_ci	if (hv_hypercall_pg == NULL)
53962306a36Sopenharmony_ci		goto clean_guest_os_id;
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
54262306a36Sopenharmony_ci	hypercall_msr.enable = 1;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	if (hv_root_partition) {
54562306a36Sopenharmony_ci		struct page *pg;
54662306a36Sopenharmony_ci		void *src;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci		/*
54962306a36Sopenharmony_ci		 * For the root partition, the hypervisor will set up its
55062306a36Sopenharmony_ci		 * hypercall page. The hypervisor guarantees it will not show
55162306a36Sopenharmony_ci		 * up in the root's address space. The root can't change the
55262306a36Sopenharmony_ci		 * location of the hypercall page.
55362306a36Sopenharmony_ci		 *
55462306a36Sopenharmony_ci		 * Order is important here. We must enable the hypercall page
55562306a36Sopenharmony_ci		 * so it is populated with code, then copy the code to an
55662306a36Sopenharmony_ci		 * executable page.
55762306a36Sopenharmony_ci		 */
55862306a36Sopenharmony_ci		wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci		pg = vmalloc_to_page(hv_hypercall_pg);
56162306a36Sopenharmony_ci		src = memremap(hypercall_msr.guest_physical_address << PAGE_SHIFT, PAGE_SIZE,
56262306a36Sopenharmony_ci				MEMREMAP_WB);
56362306a36Sopenharmony_ci		BUG_ON(!src);
56462306a36Sopenharmony_ci		memcpy_to_page(pg, 0, src, HV_HYP_PAGE_SIZE);
56562306a36Sopenharmony_ci		memunmap(src);
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci		hv_remap_tsc_clocksource();
56862306a36Sopenharmony_ci	} else {
56962306a36Sopenharmony_ci		hypercall_msr.guest_physical_address = vmalloc_to_pfn(hv_hypercall_pg);
57062306a36Sopenharmony_ci		wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
57162306a36Sopenharmony_ci	}
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ciskip_hypercall_pg_init:
57462306a36Sopenharmony_ci	/*
57562306a36Sopenharmony_ci	 * Some versions of Hyper-V that provide IBT in guest VMs have a bug
57662306a36Sopenharmony_ci	 * in that there's no ENDBR64 instruction at the entry to the
57762306a36Sopenharmony_ci	 * hypercall page. Because hypercalls are invoked via an indirect call
57862306a36Sopenharmony_ci	 * to the hypercall page, all hypercall attempts fail when IBT is
57962306a36Sopenharmony_ci	 * enabled, and Linux panics. For such buggy versions, disable IBT.
58062306a36Sopenharmony_ci	 *
58162306a36Sopenharmony_ci	 * Fixed versions of Hyper-V always provide ENDBR64 on the hypercall
58262306a36Sopenharmony_ci	 * page, so if future Linux kernel versions enable IBT for 32-bit
58362306a36Sopenharmony_ci	 * builds, additional hypercall page hackery will be required here
58462306a36Sopenharmony_ci	 * to provide an ENDBR32.
58562306a36Sopenharmony_ci	 */
58662306a36Sopenharmony_ci#ifdef CONFIG_X86_KERNEL_IBT
58762306a36Sopenharmony_ci	if (cpu_feature_enabled(X86_FEATURE_IBT) &&
58862306a36Sopenharmony_ci	    *(u32 *)hv_hypercall_pg != gen_endbr()) {
58962306a36Sopenharmony_ci		setup_clear_cpu_cap(X86_FEATURE_IBT);
59062306a36Sopenharmony_ci		pr_warn("Disabling IBT because of Hyper-V bug\n");
59162306a36Sopenharmony_ci	}
59262306a36Sopenharmony_ci#endif
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	/*
59562306a36Sopenharmony_ci	 * hyperv_init() is called before LAPIC is initialized: see
59662306a36Sopenharmony_ci	 * apic_intr_mode_init() -> x86_platform.apic_post_init() and
59762306a36Sopenharmony_ci	 * apic_bsp_setup() -> setup_local_APIC(). The direct-mode STIMER
59862306a36Sopenharmony_ci	 * depends on LAPIC, so hv_stimer_alloc() should be called from
59962306a36Sopenharmony_ci	 * x86_init.timers.setup_percpu_clockev.
60062306a36Sopenharmony_ci	 */
60162306a36Sopenharmony_ci	old_setup_percpu_clockev = x86_init.timers.setup_percpu_clockev;
60262306a36Sopenharmony_ci	x86_init.timers.setup_percpu_clockev = hv_stimer_setup_percpu_clockev;
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	hv_apic_init();
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	x86_init.pci.arch_init = hv_pci_init;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	register_syscore_ops(&hv_syscore_ops);
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	hyperv_init_cpuhp = cpuhp;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	if (cpuid_ebx(HYPERV_CPUID_FEATURES) & HV_ACCESS_PARTITION_ID)
61362306a36Sopenharmony_ci		hv_get_partition_id();
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	BUG_ON(hv_root_partition && hv_current_partition_id == ~0ull);
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci#ifdef CONFIG_PCI_MSI
61862306a36Sopenharmony_ci	/*
61962306a36Sopenharmony_ci	 * If we're running as root, we want to create our own PCI MSI domain.
62062306a36Sopenharmony_ci	 * We can't set this in hv_pci_init because that would be too late.
62162306a36Sopenharmony_ci	 */
62262306a36Sopenharmony_ci	if (hv_root_partition)
62362306a36Sopenharmony_ci		x86_init.irqs.create_pci_msi_domain = hv_create_pci_msi_domain;
62462306a36Sopenharmony_ci#endif
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	/* Query the VMs extended capability once, so that it can be cached. */
62762306a36Sopenharmony_ci	hv_query_ext_cap(0);
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	/* Find the VTL */
63062306a36Sopenharmony_ci	ms_hyperv.vtl = get_vtl();
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	if (ms_hyperv.vtl > 0) /* non default VTL */
63362306a36Sopenharmony_ci		hv_vtl_early_init();
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	return;
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ciclean_guest_os_id:
63862306a36Sopenharmony_ci	wrmsrl(HV_X64_MSR_GUEST_OS_ID, 0);
63962306a36Sopenharmony_ci	hv_ivm_msr_write(HV_X64_MSR_GUEST_OS_ID, 0);
64062306a36Sopenharmony_ci	cpuhp_remove_state(cpuhp);
64162306a36Sopenharmony_cifree_ghcb_page:
64262306a36Sopenharmony_ci	free_percpu(hv_ghcb_pg);
64362306a36Sopenharmony_cifree_vp_assist_page:
64462306a36Sopenharmony_ci	kfree(hv_vp_assist_page);
64562306a36Sopenharmony_ci	hv_vp_assist_page = NULL;
64662306a36Sopenharmony_cicommon_free:
64762306a36Sopenharmony_ci	hv_common_free();
64862306a36Sopenharmony_ci}
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci/*
65162306a36Sopenharmony_ci * This routine is called before kexec/kdump, it does the required cleanup.
65262306a36Sopenharmony_ci */
65362306a36Sopenharmony_civoid hyperv_cleanup(void)
65462306a36Sopenharmony_ci{
65562306a36Sopenharmony_ci	union hv_x64_msr_hypercall_contents hypercall_msr;
65662306a36Sopenharmony_ci	union hv_reference_tsc_msr tsc_msr;
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	/* Reset our OS id */
65962306a36Sopenharmony_ci	wrmsrl(HV_X64_MSR_GUEST_OS_ID, 0);
66062306a36Sopenharmony_ci	hv_ivm_msr_write(HV_X64_MSR_GUEST_OS_ID, 0);
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	/*
66362306a36Sopenharmony_ci	 * Reset hypercall page reference before reset the page,
66462306a36Sopenharmony_ci	 * let hypercall operations fail safely rather than
66562306a36Sopenharmony_ci	 * panic the kernel for using invalid hypercall page
66662306a36Sopenharmony_ci	 */
66762306a36Sopenharmony_ci	hv_hypercall_pg = NULL;
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	/* Reset the hypercall page */
67062306a36Sopenharmony_ci	hypercall_msr.as_uint64 = hv_get_register(HV_X64_MSR_HYPERCALL);
67162306a36Sopenharmony_ci	hypercall_msr.enable = 0;
67262306a36Sopenharmony_ci	hv_set_register(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	/* Reset the TSC page */
67562306a36Sopenharmony_ci	tsc_msr.as_uint64 = hv_get_register(HV_X64_MSR_REFERENCE_TSC);
67662306a36Sopenharmony_ci	tsc_msr.enable = 0;
67762306a36Sopenharmony_ci	hv_set_register(HV_X64_MSR_REFERENCE_TSC, tsc_msr.as_uint64);
67862306a36Sopenharmony_ci}
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_civoid hyperv_report_panic(struct pt_regs *regs, long err, bool in_die)
68162306a36Sopenharmony_ci{
68262306a36Sopenharmony_ci	static bool panic_reported;
68362306a36Sopenharmony_ci	u64 guest_id;
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	if (in_die && !panic_on_oops)
68662306a36Sopenharmony_ci		return;
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	/*
68962306a36Sopenharmony_ci	 * We prefer to report panic on 'die' chain as we have proper
69062306a36Sopenharmony_ci	 * registers to report, but if we miss it (e.g. on BUG()) we need
69162306a36Sopenharmony_ci	 * to report it on 'panic'.
69262306a36Sopenharmony_ci	 */
69362306a36Sopenharmony_ci	if (panic_reported)
69462306a36Sopenharmony_ci		return;
69562306a36Sopenharmony_ci	panic_reported = true;
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	rdmsrl(HV_X64_MSR_GUEST_OS_ID, guest_id);
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	wrmsrl(HV_X64_MSR_CRASH_P0, err);
70062306a36Sopenharmony_ci	wrmsrl(HV_X64_MSR_CRASH_P1, guest_id);
70162306a36Sopenharmony_ci	wrmsrl(HV_X64_MSR_CRASH_P2, regs->ip);
70262306a36Sopenharmony_ci	wrmsrl(HV_X64_MSR_CRASH_P3, regs->ax);
70362306a36Sopenharmony_ci	wrmsrl(HV_X64_MSR_CRASH_P4, regs->sp);
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	/*
70662306a36Sopenharmony_ci	 * Let Hyper-V know there is crash data available
70762306a36Sopenharmony_ci	 */
70862306a36Sopenharmony_ci	wrmsrl(HV_X64_MSR_CRASH_CTL, HV_CRASH_CTL_CRASH_NOTIFY);
70962306a36Sopenharmony_ci}
71062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hyperv_report_panic);
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_cibool hv_is_hyperv_initialized(void)
71362306a36Sopenharmony_ci{
71462306a36Sopenharmony_ci	union hv_x64_msr_hypercall_contents hypercall_msr;
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	/*
71762306a36Sopenharmony_ci	 * Ensure that we're really on Hyper-V, and not a KVM or Xen
71862306a36Sopenharmony_ci	 * emulation of Hyper-V
71962306a36Sopenharmony_ci	 */
72062306a36Sopenharmony_ci	if (x86_hyper_type != X86_HYPER_MS_HYPERV)
72162306a36Sopenharmony_ci		return false;
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	/* A TDX VM with no paravisor uses TDX GHCI call rather than hv_hypercall_pg */
72462306a36Sopenharmony_ci	if (hv_isolation_type_tdx() && !ms_hyperv.paravisor_present)
72562306a36Sopenharmony_ci		return true;
72662306a36Sopenharmony_ci	/*
72762306a36Sopenharmony_ci	 * Verify that earlier initialization succeeded by checking
72862306a36Sopenharmony_ci	 * that the hypercall page is setup
72962306a36Sopenharmony_ci	 */
73062306a36Sopenharmony_ci	hypercall_msr.as_uint64 = 0;
73162306a36Sopenharmony_ci	rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	return hypercall_msr.enable;
73462306a36Sopenharmony_ci}
73562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hv_is_hyperv_initialized);
736