Message ID | 1584200119-18594-8-git-send-email-mikelley@microsoft.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | Subject: Enable Linux guests on Hyper-V on ARM64 | expand |
On Sat, Mar 14, 2020 at 4:36 PM Michael Kelley <mikelley@microsoft.com> wrote: > > Add ARM64-specific code to initialize the Hyper-V > hypervisor when booting as a guest VM. Provide functions > and data structures indicating hypervisor status that > are needed by VMbus driver. > > This code is built only when CONFIG_HYPERV is enabled. > > Signed-off-by: Michael Kelley <mikelley@microsoft.com> > --- > arch/arm64/hyperv/hv_core.c | 156 ++++++++++++++++++++++++++++++++++++++++++++ As you are effectively adding a new clocksource driver here, please move the code to drivers/clocksource and send the patch to the respective maintainers (added to Cc here), splitting it out from the rest of the patch. You should also describe why your platform doesn't just use the normal architected timer interface. > +TIMER_ACPI_DECLARE(hyperv, ACPI_SIG_GTDT, hyperv_init); This looks like it registers a driver for the same device as the normal arch timer. Won't that clash? Arnd
From: Arnd Bergmann <arnd@arndb.de> Sent: Monday, March 16, 2020 1:30 AM > > On Sat, Mar 14, 2020 at 4:36 PM Michael Kelley <mikelley@microsoft.com> wrote: > > > > Add ARM64-specific code to initialize the Hyper-V > > hypervisor when booting as a guest VM. Provide functions > > and data structures indicating hypervisor status that > > are needed by VMbus driver. > > > > This code is built only when CONFIG_HYPERV is enabled. > > > > Signed-off-by: Michael Kelley <mikelley@microsoft.com> > > --- > > arch/arm64/hyperv/hv_core.c | 156 > ++++++++++++++++++++++++++++++++++++++++++++ > > As you are effectively adding a new clocksource driver here, please move the > code to drivers/clocksource and send the patch to the respective maintainers > (added to Cc here), splitting it out from the rest of the patch. > > You should also describe why your platform doesn't just use the normal > architected timer interface. > > > +TIMER_ACPI_DECLARE(hyperv, ACPI_SIG_GTDT, hyperv_init); > > This looks like it registers a driver for the same device as the normal > arch timer. Won't that clash? > > Arnd There is a Hyper-V clocksource driver in drivers/clocksource/hyperv_timer.c. It is architecture independent and works for both x86 and ARM64. The requirement here is really for a place to hang the general Hyper-V initialization code. On the x86 side, there's infrastructure already in place to do hypervisor initialization, but nothing corresponding on the ARM64 side. The TIMER_ACPI_DECLARE hook is admittedly a temporary approach, and I'm happy to hear if someone has a better way to handle this. FWIW, Hyper-V doesn't currently virtualize the ARM arch counter/timer for guest VMs. The Hyper-V synthetic counter/timer in the Hyper-V clocksource driver is used on both ARM64 and x86. But this Hyper-V init code doesn't actually touch the GTDT device, so it won't interfere with the ARM arch counter/timer when a future Hyper-V version does virtualize it. Michael
On Wed, Mar 18, 2020 at 1:18 AM Michael Kelley <mikelley@microsoft.com> wrote: > > From: Arnd Bergmann <arnd@arndb.de> Sent: Monday, March 16, 2020 1:30 AM > > > > As you are effectively adding a new clocksource driver here, please move the > > code to drivers/clocksource and send the patch to the respective maintainers > > (added to Cc here), splitting it out from the rest of the patch. > > > > You should also describe why your platform doesn't just use the normal > > architected timer interface. > > > > > +TIMER_ACPI_DECLARE(hyperv, ACPI_SIG_GTDT, hyperv_init); > > > > This looks like it registers a driver for the same device as the normal > > arch timer. Won't that clash? > > There is a Hyper-V clocksource driver in drivers/clocksource/hyperv_timer.c. > It is architecture independent and works for both x86 and ARM64. > > The requirement here is really for a place to hang the general Hyper-V > initialization code. On the x86 side, there's infrastructure already in place > to do hypervisor initialization, but nothing corresponding on the ARM64 side. > The TIMER_ACPI_DECLARE hook is admittedly a temporary approach, and I'm > happy to hear if someone has a better way to handle this. > > FWIW, Hyper-V doesn't currently virtualize the ARM arch counter/timer for > guest VMs. The Hyper-V synthetic counter/timer in the Hyper-V clocksource > driver is used on both ARM64 and x86. But this Hyper-V init code doesn't actually > touch the GTDT device, so it won't interfere with the ARM arch counter/timer > when a future Hyper-V version does virtualize it. I don't have a good idea to solve it, just a few more thoughts: - if your platform does not actually provide the generic timer, then the ACPI tables should not list one either. Instead, create a separate description for your custom timer, and have that added to the ACPI spec. - To treat the timer more like a normal driver, better have the TIMER_ACPI_DECLARE() function live only in the driver itself, and use an early initcall (arch_initcall, subsys_initcall, etc) it initialize the rest as late as you can. - Some of the other code added to arch/arm64/ might be able to live in drivers/virt/hyperv in order to be shared between x86 and arm64. (No idea how much of it there is). Arnd
diff --git a/arch/arm64/hyperv/hv_core.c b/arch/arm64/hyperv/hv_core.c index 8d6de9f..b496f59 100644 --- a/arch/arm64/hyperv/hv_core.c +++ b/arch/arm64/hyperv/hv_core.c @@ -13,14 +13,47 @@ #include <linux/types.h> #include <linux/version.h> #include <linux/export.h> +#include <linux/vmalloc.h> #include <linux/mm.h> +#include <linux/acpi.h> +#include <linux/module.h> #include <linux/slab.h> #include <linux/hyperv.h> #include <linux/arm-smccc.h> #include <linux/string.h> +#include <linux/cpuhotplug.h> +#include <linux/psci.h> +#include <linux/sched_clock.h> #include <asm-generic/bug.h> #include <asm/hyperv-tlfs.h> #include <asm/mshyperv.h> +#include <asm/sysreg.h> +#include <clocksource/hyperv_timer.h> + +static bool hyperv_initialized; + +struct ms_hyperv_info ms_hyperv __ro_after_init; +EXPORT_SYMBOL_GPL(ms_hyperv); + +u32 *hv_vp_index; +EXPORT_SYMBOL_GPL(hv_vp_index); + +u32 hv_max_vp_index; +EXPORT_SYMBOL_GPL(hv_max_vp_index); + +static int hv_cpu_init(unsigned int cpu) +{ + u64 msr_vp_index; + + hv_get_vp_index(msr_vp_index); + + hv_vp_index[smp_processor_id()] = msr_vp_index; + + if (msr_vp_index > hv_max_vp_index) + hv_max_vp_index = msr_vp_index; + + return 0; +} /* @@ -57,6 +90,117 @@ void hv_free_hyperv_page(unsigned long addr) /* + * This function is invoked via the ACPI clocksource probe mechanism. We + * don't actually use any values from the ACPI GTDT table, but we set up + * the Hyper-V synthetic clocksource and do other initialization for + * interacting with Hyper-V the first time. Using early_initcall to invoke + * this function is too late because interrupts are already enabled at that + * point, and hv_init_clocksource() must run before interrupts are enabled. + * + * 1. Setup the guest ID. + * 2. Get features and hints info from Hyper-V + * 3. Setup per-cpu VP indices. + * 4. Initialize the Hyper-V clocksource. + */ + +static int __init hyperv_init(struct acpi_table_header *table) +{ + struct hv_get_vp_register_output result; + u32 a, b, c, d; + u64 guest_id; + int i, cpuhp; + + /* + * If we're in a VM on Hyper-V, the ACPI hypervisor_id field will + * have the string "MsHyperV". + */ + if (strncmp((char *)&acpi_gbl_FADT.hypervisor_id, "MsHyperV", 8)) + return -EINVAL; + + /* Setup the guest ID */ + guest_id = generate_guest_id(0, LINUX_VERSION_CODE, 0); + hv_set_vpreg(HV_REGISTER_GUEST_OSID, guest_id); + + /* Get the features and hints from Hyper-V */ + hv_get_vpreg_128(HV_REGISTER_PRIVILEGES_AND_FEATURES, &result); + ms_hyperv.features = lower_32_bits(result.registervaluelow); + ms_hyperv.misc_features = lower_32_bits(result.registervaluehigh); + + hv_get_vpreg_128(HV_REGISTER_FEATURES, &result); + ms_hyperv.hints = lower_32_bits(result.registervaluelow); + + pr_info("Hyper-V: Features 0x%x, misc features 0x%x, hints 0x%x\n", + ms_hyperv.features, ms_hyperv.misc_features, ms_hyperv.hints); + + /* + * Hyper-V on ARM64 doesn't support AutoEOI. Add the hint + * that tells architecture independent code not to use this + * feature. + */ + ms_hyperv.hints |= HV_DEPRECATING_AEOI_RECOMMENDED; + + /* Get information about the Hyper-V host version */ + hv_get_vpreg_128(HV_REGISTER_HYPERVISOR_VERSION, &result); + a = lower_32_bits(result.registervaluelow); + b = upper_32_bits(result.registervaluelow); + c = lower_32_bits(result.registervaluehigh); + d = upper_32_bits(result.registervaluehigh); + pr_info("Hyper-V: Host Build %d.%d.%d.%d-%d-%d\n", + b >> 16, b & 0xFFFF, a, d & 0xFFFFFF, c, d >> 24); + + /* Allocate and initialize percpu VP index array */ + hv_vp_index = kmalloc_array(num_possible_cpus(), sizeof(*hv_vp_index), + GFP_KERNEL); + if (!hv_vp_index) + return -ENOMEM; + + for (i = 0; i < num_possible_cpus(); i++) + hv_vp_index[i] = VP_INVAL; + + cpuhp = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, + "arm64/hyperv_init:online", hv_cpu_init, NULL); + if (cpuhp < 0) + goto free_vp_index; + + hv_init_clocksource(); + if (hv_stimer_alloc()) + goto remove_cpuhp_state; + + hyperv_initialized = true; + return 0; + +remove_cpuhp_state: + cpuhp_remove_state(cpuhp); +free_vp_index: + kfree(hv_vp_index); + hv_vp_index = NULL; + return -EINVAL; +} +TIMER_ACPI_DECLARE(hyperv, ACPI_SIG_GTDT, hyperv_init); + + +/* + * Called from hv_init_clocksource() to do ARM64 + * specific initialization of the sched clock + */ +void __init hv_setup_sched_clock(void *sched_clock) +{ + sched_clock_register(sched_clock, 64, HV_CLOCK_HZ); +} + +/* + * This routine is called before kexec/kdump, it does the required cleanup. + */ +void hyperv_cleanup(void) +{ + /* Reset our OS id */ + hv_set_vpreg(HV_REGISTER_GUEST_OSID, 0); + +} +EXPORT_SYMBOL_GPL(hyperv_cleanup); + + +/* * hv_do_hypercall- Invoke the specified hypercall */ u64 hv_do_hypercall(u64 control, void *input, void *output) @@ -260,3 +404,15 @@ void hyperv_report_panic_msg(phys_addr_t pa, size_t size) (HV_CRASH_CTL_CRASH_NOTIFY | HV_CRASH_CTL_CRASH_NOTIFY_MSG)); } EXPORT_SYMBOL_GPL(hyperv_report_panic_msg); + +bool hv_is_hyperv_initialized(void) +{ + return hyperv_initialized; +} +EXPORT_SYMBOL_GPL(hv_is_hyperv_initialized); + +bool hv_is_hibernation_supported(void) +{ + return false; +} +EXPORT_SYMBOL_GPL(hv_is_hibernation_supported);
Add ARM64-specific code to initialize the Hyper-V hypervisor when booting as a guest VM. Provide functions and data structures indicating hypervisor status that are needed by VMbus driver. This code is built only when CONFIG_HYPERV is enabled. Signed-off-by: Michael Kelley <mikelley@microsoft.com> --- arch/arm64/hyperv/hv_core.c | 156 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+)