Commit 0408f16b authored by Stanislav Kinsburskiy's avatar Stanislav Kinsburskiy Committed by Wei Liu

clocksource: hyper-v: Add TSC page support for root partition

Microsoft Hypervisor root partition has to map the TSC page specified
by the hypervisor, instead of providing the page to the hypervisor like
it's done in the guest partitions.

However, it's too early to map the page when the clock is initialized, so, the
actual mapping is happening later.
Signed-off-by: default avatarStanislav Kinsburskiy <stanislav.kinsburskiy@gmail.com>
CC: "K. Y. Srinivasan" <kys@microsoft.com>
CC: Haiyang Zhang <haiyangz@microsoft.com>
CC: Wei Liu <wei.liu@kernel.org>
CC: Dexuan Cui <decui@microsoft.com>
CC: Thomas Gleixner <tglx@linutronix.de>
CC: Ingo Molnar <mingo@redhat.com>
CC: Borislav Petkov <bp@alien8.de>
CC: Dave Hansen <dave.hansen@linux.intel.com>
CC: x86@kernel.org
CC: "H. Peter Anvin" <hpa@zytor.com>
CC: Daniel Lezcano <daniel.lezcano@linaro.org>
CC: linux-hyperv@vger.kernel.org
CC: linux-kernel@vger.kernel.org
Reviewed-by: default avatarMichael Kelley <mikelley@microsoft.com>
Reviewed-by: default avatarAnirudh Rayabharam <anrayabh@linux.microsoft.com>
Link: https://lore.kernel.org/r/166759443644.385891.15921594265843430260.stgit@skinsburskii-cloud-desktop.internal.cloudapp.netSigned-off-by: default avatarWei Liu <wei.liu@kernel.org>
parent 364adc45
......@@ -462,6 +462,8 @@ void __init hyperv_init(void)
BUG_ON(!src);
memcpy_to_page(pg, 0, src, HV_HYP_PAGE_SIZE);
memunmap(src);
hv_remap_tsc_clocksource();
} else {
hypercall_msr.guest_physical_address = vmalloc_to_pfn(hv_hypercall_pg);
wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
......
......@@ -509,9 +509,6 @@ static bool __init hv_init_tsc_clocksource(void)
if (!(ms_hyperv.features & HV_MSR_REFERENCE_TSC_AVAILABLE))
return false;
if (hv_root_partition)
return false;
/*
* If Hyper-V offers TSC_INVARIANT, then the virtualized TSC correctly
* handles frequency and offset changes due to live migration,
......@@ -529,16 +526,28 @@ static bool __init hv_init_tsc_clocksource(void)
}
hv_read_reference_counter = read_hv_clock_tsc;
tsc_pfn = HVPFN_DOWN(virt_to_phys(tsc_page));
/*
* The Hyper-V TLFS specifies to preserve the value of reserved
* bits in registers. So read the existing value, preserve the
* low order 12 bits, and add in the guest physical address
* (which already has at least the low 12 bits set to zero since
* it is page aligned). Also set the "enable" bit, which is bit 0.
* TSC page mapping works differently in root compared to guest.
* - In guest partition the guest PFN has to be passed to the
* hypervisor.
* - In root partition it's other way around: it has to map the PFN
* provided by the hypervisor.
* But it can't be mapped right here as it's too early and MMU isn't
* ready yet. So, we only set the enable bit here and will remap the
* page later in hv_remap_tsc_clocksource().
*
* It worth mentioning, that TSC clocksource read function
* (read_hv_clock_tsc) has a MSR-based fallback mechanism, used when
* TSC page is zeroed (which is the case until the PFN is remapped) and
* thus TSC clocksource will work even without the real TSC page
* mapped.
*/
tsc_msr.as_uint64 = hv_get_register(HV_REGISTER_REFERENCE_TSC);
if (hv_root_partition)
tsc_pfn = tsc_msr.pfn;
else
tsc_pfn = HVPFN_DOWN(virt_to_phys(tsc_page));
tsc_msr.enable = 1;
tsc_msr.pfn = tsc_pfn;
hv_set_register(HV_REGISTER_REFERENCE_TSC, tsc_msr.as_uint64);
......@@ -573,3 +582,20 @@ void __init hv_init_clocksource(void)
hv_sched_clock_offset = hv_read_reference_counter();
hv_setup_sched_clock(read_hv_sched_clock_msr);
}
void __init hv_remap_tsc_clocksource(void)
{
if (!(ms_hyperv.features & HV_MSR_REFERENCE_TSC_AVAILABLE))
return;
if (!hv_root_partition) {
WARN(1, "%s: attempt to remap TSC page in guest partition\n",
__func__);
return;
}
tsc_page = memremap(tsc_pfn << HV_HYP_PAGE_SHIFT, sizeof(tsc_pg),
MEMREMAP_WB);
if (!tsc_page)
pr_err("Failed to remap Hyper-V TSC page.\n");
}
......@@ -31,6 +31,7 @@ extern void hv_stimer_global_cleanup(void);
extern void hv_stimer0_isr(void);
extern void hv_init_clocksource(void);
extern void hv_remap_tsc_clocksource(void);
extern unsigned long hv_get_tsc_pfn(void);
extern struct ms_hyperv_tsc_page *hv_get_tsc_page(void);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment