This series enables Linux guests running on Hyper-V on ARM64
hardware. New ARM64-specific code in arch/arm64/hyperv initializes
Hyper-V, including its interrupts and hypercall mechanism.
Existing architecture independent drivers for Hyper-V's VMbus and
synthetic devices just work when built for ARM64. Hyper-V code is
built and included in the image and modules only if CONFIG_HYPERV
is enabled.
The six patches are organized as follows:
1) Add definitions and functions for making Hyper-V hypercalls
and getting/setting virtual processor registers provided by
Hyper-V
2) Add architecture specific definitions needed by the
architecture independent Hyper-V clocksource driver in
drivers/clocksource/hyperv_timer.c. Update the clocksource
driver to be initialized on ARM64.
3) Add functions needed by the arch independent VMbus driver
for reporting a panic to Hyper-V and as stubs for the kexec
and crash handlers.
4) Add Hyper-V initialization code and utility functions that
report Hyper-v status.
5) Export screen_info so it may be used by the Hyper-V frame buffer
driver built as a module. It is already exported for x86,
powerpc, and alpha architectures.
6) Make CONFIG_HYPERV selectable on ARM64 in addition to x86/x64.
Hyper-V on ARM64 runs with a 4 Kbyte page size, but allows guests
with 4K/16K/64K page size. Linux guests with this ARM64 enablement
code work with all three supported ARM64 page sizes.
The Hyper-V vPCI driver at drivers/pci/host/pci-hyperv.c has
x86/x64-specific code and is not being built for ARM64. Fixing
this driver to enable vPCI devices on ARM64 will be done later.
In a few cases, terminology from the x86/x64 world has been carried
over into the ARM64 code ("MSR", "TSC"). Hyper-V still uses the
x86/x64 terminology and has not replaced it with something more
generic, so the code uses the Hyper-V terminology. This will be
fixed when Hyper-V updates the usage in the TLFS.
This patch set is based on the 5.11.0 code tree, plus this patch series
https://lore.kernel.org/lkml/[email protected]/
that refactors the boundary between arch independent and arch
dependent code for Hyper-V.
Changes in v8:
* Removed a fair amount of code based on refactoring the boundary between
arch independent and arch dependent code for Hyper-V, per comments
from Arnd Bergmann. The removed code was either duplicated on
the x86 side, or has been folded into architecture independent
code as not really being architecture dependent.
* Added config dependency on !CONFIG_CPU_BIG_ENDIAN [Arnd Bergmann]
* Reworked the approach to Hyper-V initialization. The functionality
is the same, but is now structured like the Xen code with an early
init function called in setup_arch() and an early initcall to
finish the initialization. [Arnd Bergmann]
Changes in v7:
* Separately upstreamed split of hyperv-tlfs.h into arch dependent
and independent versions. In this patch set, update the ARM64
hyperv-tlfs.h to include architecture independent definitions.
This approach eliminates a lot of lines of otherwise duplicated
code on the ARM64 side.
* Break ARM64 mshyperv.h into smaller pieces. Have an initial
baseline, and add code along with patches for a particular
functional area. [Marc Zyngier]
* In mshyperv.h, use static inline functions instead of #defines
where possible. [Arnd Bergmann]
* Use VMbus INTID obtained from ACPI DSDT instead of hardcoding.
The STIMER INTID is still hardcoded because it is needed
before Linux has initialized the ACPI subsystem, so it can't
be obtained from the DSDT. Wedging it into the GTDT seems
dubious, so was not done. [Marc Zyngier]
* Update Hyper-V page size allocation functions to use
alloc_page() if PAGE_SIZE == HV_HYP_PAGE_SIZE [Arnd
Bergmann]
* Various other minor changes based on feedback and to rebase
to latest linux-next [Marc Zyngier and Arnd Bergmann]
Changes in v6:
* Use SMCCC hypercall interface instead of direct invocation
of HVC instruction and the Hyper-V hypercall interface
[Marc Zyngier]
* Reimplemented functions to alloc/free Hyper-V size pages
using kmalloc/kfree since kmalloc now guarantees alignment of
power of 2 size allocations [Marc Zyngier]
* Export screen_info in arm64 architecture so it can be used
by the Hyper-V buffer driver built as a module
* Renamed source file arch/arm64/hyperv/hv_init.c to hv_core.c
to better reflect its content
* Fixed the bit position of certain feature flags presented by
Hyper-V to the guest. The bit positions on ARM64 don't match
the position on x86 like originally thought.
* Minor fixups to rebase to 5.6-rc5 linux-next
Changes in v5:
* Minor fixups to rebase to 5.4-rc1 linux-next
Changes in v4:
* Moved clock-related code into an architecture independent
Hyper-V clocksource driver that is already upstream. Clock
related code is removed from this patch set except for the
ARM64 specific interrupt handler. [Marc Zyngier]
* Separately upstreamed the split of mshyperv.h into arch independent
and arch dependent portions. The arch independent portion has been
removed from this patch set.
* Divided patch #2 of the series into multiple smaller patches
[Marc Zyngier]
* Changed a dozen or so smaller things based on feedback
[Marc Zyngier, Will Deacon]
* Added functions to alloc/free Hyper-V size pages for use by
drivers for Hyper-V synthetic devices when updated to not assume
guest page size and Hyper-v page size are the same
Changes in v3:
* Added initialization of hv_vp_index array like was recently
added on x86 branch [KY Srinivasan]
* Changed Hyper-V ARM64 register symbols to be all uppercase
instead of mixed case [KY Srinivasan]
* Separated mshyperv.h into two files, one architecture
independent and one architecture dependent. After this code
is upstream, will make changes to the x86 code to use the
architecture independent file and remove duplication. And
once we have a multi-architecture Hyper-V TLFS, will do a
separate patch to split hyperv-tlfs.h in the same way.
[KY Srinivasan]
* Minor tweaks to rebase to latest linux-next code
Changes in v2:
* Removed patch to implement slow_virt_to_phys() on ARM64.
Use of slow_virt_to_phys() in arch independent Hyper-V
drivers has been eliminated by commit 6ba34171bcbd
("Drivers: hv: vmbus: Remove use of slow_virt_to_phys()")
* Minor tweaks to rebase to latest linux-next code
Michael Kelley (6):
arm64: hyperv: Add Hyper-V hypercall and register access utilities
arm64: hyperv: Add Hyper-V clocksource/clockevent support
arm64: hyperv: Add kexec and panic handlers
arm64: hyperv: Initialize hypervisor on boot
arm64: efi: Export screen_info
Drivers: hv: Enable Hyper-V code to be built on ARM64
MAINTAINERS | 3 +
arch/arm64/Kbuild | 1 +
arch/arm64/hyperv/Makefile | 2 +
arch/arm64/hyperv/hv_core.c | 220 +++++++++++++++++++++++++++++++++++
arch/arm64/hyperv/mshyperv.c | 194 ++++++++++++++++++++++++++++++
arch/arm64/include/asm/hyperv-tlfs.h | 69 +++++++++++
arch/arm64/include/asm/mshyperv.h | 73 ++++++++++++
arch/arm64/kernel/efi.c | 1 +
arch/arm64/kernel/setup.c | 4 +
drivers/clocksource/hyperv_timer.c | 14 +++
drivers/hv/Kconfig | 3 +-
11 files changed, 583 insertions(+), 1 deletion(-)
create mode 100644 arch/arm64/hyperv/Makefile
create mode 100644 arch/arm64/hyperv/hv_core.c
create mode 100644 arch/arm64/hyperv/mshyperv.c
create mode 100644 arch/arm64/include/asm/hyperv-tlfs.h
create mode 100644 arch/arm64/include/asm/mshyperv.h
--
1.8.3.1
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 <[email protected]>
---
arch/arm64/hyperv/mshyperv.c | 140 ++++++++++++++++++++++++++++++++++++++
arch/arm64/include/asm/mshyperv.h | 6 ++
arch/arm64/kernel/setup.c | 4 ++
3 files changed, 150 insertions(+)
diff --git a/arch/arm64/hyperv/mshyperv.c b/arch/arm64/hyperv/mshyperv.c
index d202b4c..c72bc66 100644
--- a/arch/arm64/hyperv/mshyperv.c
+++ b/arch/arm64/hyperv/mshyperv.c
@@ -14,6 +14,146 @@
#include <linux/types.h>
#include <linux/export.h>
#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/acpi.h>
+#include <linux/version.h>
+#include <linux/log2.h>
+#include <asm/mshyperv.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);
+
+/*
+ * As hypercall input and output, align to a power of 2 to ensure they
+ * don't cross a page boundary. Initialize the first element of the
+ * variable size array in the input to ensure enough space is allocated.
+ */
+static struct hv_get_vp_registers_input input __initdata __aligned(
+ roundup_pow_of_two(sizeof(struct hv_get_vp_registers_input) +
+ sizeof(struct input))) = {.element[0].name0 = 1};
+static struct hv_get_vp_registers_output result __initdata __aligned(
+ roundup_pow_of_two(sizeof(struct hv_get_vp_registers_output)));
+
+void __init hyperv_early_init(void)
+{
+ u32 a, b, c, d;
+ u64 guest_id;
+
+ /*
+ * 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;
+
+ /* 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_FEATURES, &input, &result);
+ ms_hyperv.features = result.as32.a;
+ ms_hyperv.misc_features = result.as32.c;
+
+ __hv_get_vpreg_128(HV_REGISTER_ENLIGHTENMENTS, &input, &result);
+ ms_hyperv.hints = result.as32.a;
+
+ pr_info("Hyper-V: Features 0x%x, hints 0x%x, misc 0x%x\n",
+ ms_hyperv.features, ms_hyperv.hints, ms_hyperv.misc_features);
+
+ /*
+ * If Hyper-V has crash notifications, set crash_kexec_post_notifiers
+ * so that we will report the panic to Hyper-V before running kdump.
+ */
+ if (ms_hyperv.misc_features & HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE)
+ crash_kexec_post_notifiers = true;
+
+ /* Get information about the Hyper-V host version */
+ __hv_get_vpreg_128(HV_REGISTER_HYPERVISOR_VERSION, &input, &result);
+ a = result.as32.a;
+ b = result.as32.b;
+ c = result.as32.c;
+ d = result.as32.d;
+ pr_info("Hyper-V: Host Build %d.%d.%d.%d-%d-%d\n",
+ b >> 16, b & 0xFFFF, a, d & 0xFFFFFF, c, d >> 24);
+
+ hyperv_initialized = true;
+}
+
+static u64 hypercall_output __initdata;
+
+static int __init hyperv_init(void)
+{
+ struct hv_get_vpindex_from_apicid_input *input;
+ u64 status;
+ int i;
+
+ /*
+ * Hypercall inputs must not cross a page boundary, so allocate
+ * power of 2 size, which will be aligned to that size.
+ */
+ input = kzalloc(roundup_pow_of_two(sizeof(input->header) +
+ sizeof(input->element[0])), GFP_KERNEL);
+ if (!input)
+ return -ENOMEM;
+
+ /* Allocate and initialize percpu VP index array */
+ hv_max_vp_index = num_possible_cpus();
+ hv_vp_index = kmalloc_array(hv_max_vp_index, sizeof(*hv_vp_index),
+ GFP_KERNEL);
+ if (!hv_vp_index) {
+ kfree(input);
+ return -ENOMEM;
+ }
+
+ input->header.partitionid = HV_PARTITION_ID_SELF;
+ for (i = 0; i < hv_max_vp_index; i++) {
+ input->element[0].mpidr = cpu_logical_map(i);
+ status = hv_do_hypercall(HVCALL_VPINDEX_FROM_APICID |
+ HV_HYPERCALL_REP_COMP_1, input,
+ &hypercall_output);
+ if ((status & HV_HYPERCALL_RESULT_MASK) == HV_STATUS_SUCCESS)
+ hv_vp_index[i] = hypercall_output;
+ else {
+ pr_warn("Hyper-V: No VP index for CPU %d MPIDR %llx status %llx\n",
+ i, cpu_logical_map(i), status);
+ hv_vp_index[i] = VP_INVAL;
+ }
+ }
+
+ kfree(input);
+ return 0;
+}
+
+early_initcall(hyperv_init);
+
+/* This routine is called before kexec/kdump. It does required cleanup. */
+void hyperv_cleanup(void)
+{
+ hv_set_vpreg(HV_REGISTER_GUEST_OSID, 0);
+
+}
+EXPORT_SYMBOL_GPL(hyperv_cleanup);
+
+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);
/*
* The VMbus handler functions are no-ops on ARM64 because
diff --git a/arch/arm64/include/asm/mshyperv.h b/arch/arm64/include/asm/mshyperv.h
index d6ff2ee..55a61a8 100644
--- a/arch/arm64/include/asm/mshyperv.h
+++ b/arch/arm64/include/asm/mshyperv.h
@@ -23,6 +23,12 @@
#include <asm/hyperv-tlfs.h>
#include <clocksource/arm_arch_timer.h>
+#if IS_ENABLED(CONFIG_HYPERV)
+void __init hyperv_early_init(void);
+#else
+static inline void hyperv_early_init(void) {};
+#endif
+
/*
* Declare calls to get and set Hyper-V VP register values on ARM64, which
* requires a hypercall.
diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c
index c18aacd..1df8285 100644
--- a/arch/arm64/kernel/setup.c
+++ b/arch/arm64/kernel/setup.c
@@ -49,6 +49,7 @@
#include <asm/traps.h>
#include <asm/efi.h>
#include <asm/xen/hypervisor.h>
+#include <asm/mshyperv.h>
#include <asm/mmu_context.h>
static int num_standard_resources;
@@ -340,6 +341,9 @@ void __init __no_sanitize_address setup_arch(char **cmdline_p)
if (acpi_disabled)
unflatten_device_tree();
+ /* Do after acpi_boot_table_init() so local FADT is available */
+ hyperv_early_init();
+
bootmem_init();
kasan_init();
--
1.8.3.1
Add architecture specific definitions and functions needed
by the architecture independent Hyper-V clocksource driver.
Update the Hyper-V clocksource driver to be initialized
on ARM64.
Signed-off-by: Michael Kelley <[email protected]>
---
arch/arm64/include/asm/mshyperv.h | 12 ++++++++++++
drivers/clocksource/hyperv_timer.c | 14 ++++++++++++++
2 files changed, 26 insertions(+)
diff --git a/arch/arm64/include/asm/mshyperv.h b/arch/arm64/include/asm/mshyperv.h
index 44ee012..d6ff2ee 100644
--- a/arch/arm64/include/asm/mshyperv.h
+++ b/arch/arm64/include/asm/mshyperv.h
@@ -21,6 +21,7 @@
#include <linux/types.h>
#include <linux/arm-smccc.h>
#include <asm/hyperv-tlfs.h>
+#include <clocksource/arm_arch_timer.h>
/*
* Declare calls to get and set Hyper-V VP register values on ARM64, which
@@ -42,6 +43,17 @@ static inline u64 hv_get_register(unsigned int reg)
return hv_get_vpreg(reg);
}
+/* Define the interrupt ID used by STIMER0 Direct Mode interrupts. This
+ * value can't come from ACPI tables because it is needed before the
+ * Linux ACPI subsystem is initialized.
+ */
+#define HYPERV_STIMER0_VECTOR 31
+
+static inline u64 hv_get_raw_timer(void)
+{
+ return arch_timer_read_counter();
+}
+
/* SMCCC hypercall parameters */
#define HV_SMCCC_FUNC_NUMBER 1
#define HV_FUNC_ID ARM_SMCCC_CALL_VAL( \
diff --git a/drivers/clocksource/hyperv_timer.c b/drivers/clocksource/hyperv_timer.c
index c553b8c..f8bb5df 100644
--- a/drivers/clocksource/hyperv_timer.c
+++ b/drivers/clocksource/hyperv_timer.c
@@ -567,3 +567,17 @@ void __init hv_init_clocksource(void)
hv_setup_sched_clock(read_hv_sched_clock_msr);
}
EXPORT_SYMBOL_GPL(hv_init_clocksource);
+
+/* Initialize everything on ARM64 */
+static int __init hyperv_timer_init(struct acpi_table_header *table)
+{
+ if (!hv_is_hyperv_initialized())
+ return -EINVAL;
+
+ hv_init_clocksource();
+ if (hv_stimer_alloc(true))
+ return -EINVAL;
+
+ return 0;
+}
+TIMER_ACPI_DECLARE(hyperv, ACPI_SIG_GTDT, hyperv_timer_init);
--
1.8.3.1
hyperv-tlfs.h defines Hyper-V interfaces from the Hyper-V Top Level
Functional Spec (TLFS), and #includes the architecture-independent
part of hyperv-tlfs.h in include/asm-generic. The published TLFS
is distinctly oriented to x86/x64, so the ARM64-specific
hyperv-tlfs.h includes information for ARM64 that is not yet formally
published. The TLFS is available here:
docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/reference/tlfs
mshyperv.h defines Linux-specific structures and routines for
interacting with Hyper-V on ARM64, and #includes the architecture-
independent part of mshyperv.h in include/asm-generic.
Use these definitions to provide utility functions to make
Hyper-V hypercalls and to get and set Hyper-V provided
registers associated with a virtual processor.
Signed-off-by: Michael Kelley <[email protected]>
---
MAINTAINERS | 3 +
arch/arm64/Kbuild | 1 +
arch/arm64/hyperv/Makefile | 2 +
arch/arm64/hyperv/hv_core.c | 167 +++++++++++++++++++++++++++++++++++
arch/arm64/include/asm/hyperv-tlfs.h | 69 +++++++++++++++
arch/arm64/include/asm/mshyperv.h | 55 ++++++++++++
6 files changed, 297 insertions(+)
create mode 100644 arch/arm64/hyperv/Makefile
create mode 100644 arch/arm64/hyperv/hv_core.c
create mode 100644 arch/arm64/include/asm/hyperv-tlfs.h
create mode 100644 arch/arm64/include/asm/mshyperv.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 546aa66..0a22f3a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8235,6 +8235,9 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/hyperv/linux.git
F: Documentation/ABI/stable/sysfs-bus-vmbus
F: Documentation/ABI/testing/debugfs-hyperv
F: Documentation/networking/device_drivers/ethernet/microsoft/netvsc.rst
+F: arch/arm64/hyperv
+F: arch/arm64/include/asm/hyperv-tlfs.h
+F: arch/arm64/include/asm/mshyperv.h
F: arch/x86/hyperv
F: arch/x86/include/asm/hyperv-tlfs.h
F: arch/x86/include/asm/mshyperv.h
diff --git a/arch/arm64/Kbuild b/arch/arm64/Kbuild
index d646582..7a37608 100644
--- a/arch/arm64/Kbuild
+++ b/arch/arm64/Kbuild
@@ -3,4 +3,5 @@ obj-y += kernel/ mm/
obj-$(CONFIG_NET) += net/
obj-$(CONFIG_KVM) += kvm/
obj-$(CONFIG_XEN) += xen/
+obj-$(subst m,y,$(CONFIG_HYPERV)) += hyperv/
obj-$(CONFIG_CRYPTO) += crypto/
diff --git a/arch/arm64/hyperv/Makefile b/arch/arm64/hyperv/Makefile
new file mode 100644
index 0000000..1697d30
--- /dev/null
+++ b/arch/arm64/hyperv/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-y := hv_core.o
diff --git a/arch/arm64/hyperv/hv_core.c b/arch/arm64/hyperv/hv_core.c
new file mode 100644
index 0000000..9a37124
--- /dev/null
+++ b/arch/arm64/hyperv/hv_core.c
@@ -0,0 +1,167 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Low level utility routines for interacting with Hyper-V.
+ *
+ * Copyright (C) 2021, Microsoft, Inc.
+ *
+ * Author : Michael Kelley <[email protected]>
+ */
+
+
+#include <linux/types.h>
+#include <linux/log2.h>
+#include <linux/export.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/hyperv.h>
+#include <linux/arm-smccc.h>
+#include <linux/vmalloc.h>
+#include <linux/module.h>
+#include <asm-generic/bug.h>
+#include <asm/hyperv-tlfs.h>
+#include <asm/mshyperv.h>
+
+/*
+ * hv_do_hypercall- Invoke the specified hypercall
+ */
+u64 hv_do_hypercall(u64 control, void *input, void *output)
+{
+ u64 input_address;
+ u64 output_address;
+ struct arm_smccc_res res;
+
+ input_address = input ? virt_to_phys(input) : 0;
+ output_address = output ? virt_to_phys(output) : 0;
+
+ arm_smccc_1_1_hvc(HV_FUNC_ID, control,
+ input_address, output_address, &res);
+ return res.a0;
+}
+EXPORT_SYMBOL_GPL(hv_do_hypercall);
+
+/*
+ * hv_do_fast_hypercall8 -- Invoke the specified hypercall
+ * with arguments in registers instead of physical memory.
+ * Avoids the overhead of virt_to_phys for simple hypercalls.
+ */
+
+u64 hv_do_fast_hypercall8(u16 code, u64 input)
+{
+ u64 control;
+ struct arm_smccc_res res;
+
+ control = (u64)code | HV_HYPERCALL_FAST_BIT;
+
+ arm_smccc_1_1_hvc(HV_FUNC_ID, control, input, &res);
+ return res.a0;
+}
+EXPORT_SYMBOL_GPL(hv_do_fast_hypercall8);
+
+
+/*
+ * Set a single VP register to a 64-bit value.
+ */
+void hv_set_vpreg(u32 msr, u64 value)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_hvc(
+ HV_FUNC_ID,
+ HVCALL_SET_VP_REGISTERS | HV_HYPERCALL_FAST_BIT |
+ HV_HYPERCALL_REP_COMP_1,
+ HV_PARTITION_ID_SELF,
+ HV_VP_INDEX_SELF,
+ msr,
+ 0,
+ value,
+ 0,
+ &res);
+
+ /*
+ * Something is fundamentally broken in the hypervisor if
+ * setting a VP register fails. There's really no way to
+ * continue as a guest VM, so panic.
+ */
+ BUG_ON((res.a0 & HV_HYPERCALL_RESULT_MASK) != HV_STATUS_SUCCESS);
+}
+EXPORT_SYMBOL_GPL(hv_set_vpreg);
+
+/*
+ * Get the value of a single VP register. One version
+ * returns just 64 bits and another returns the full 128 bits.
+ * The two versions are separate to avoid complicating the
+ * calling sequence for the more frequently used 64 bit version.
+ */
+
+void __hv_get_vpreg_128(u32 msr,
+ struct hv_get_vp_registers_input *input,
+ struct hv_get_vp_registers_output *res)
+{
+ u64 status;
+
+ input->header.partitionid = HV_PARTITION_ID_SELF;
+ input->header.vpindex = HV_VP_INDEX_SELF;
+ input->header.inputvtl = 0;
+ input->element[0].name0 = msr;
+ input->element[0].name1 = 0;
+
+
+ status = hv_do_hypercall(
+ HVCALL_GET_VP_REGISTERS | HV_HYPERCALL_REP_COMP_1,
+ input, res);
+
+ /*
+ * Something is fundamentally broken in the hypervisor if
+ * getting a VP register fails. There's really no way to
+ * continue as a guest VM, so panic.
+ */
+ BUG_ON((status & HV_HYPERCALL_RESULT_MASK) != HV_STATUS_SUCCESS);
+}
+
+u64 hv_get_vpreg(u32 msr)
+{
+ struct hv_get_vp_registers_input *input;
+ struct hv_get_vp_registers_output *output;
+ u64 result;
+
+ /*
+ * Allocate a power of 2 size so alignment to that size is
+ * guaranteed, since the hypercall input and output areas
+ * must not cross a page boundary.
+ */
+ input = kzalloc(roundup_pow_of_two(sizeof(input->header) +
+ sizeof(input->element[0])), GFP_ATOMIC);
+ output = kmalloc(roundup_pow_of_two(sizeof(*output)), GFP_ATOMIC);
+
+ __hv_get_vpreg_128(msr, input, output);
+
+ result = output->as64.low;
+ kfree(input);
+ kfree(output);
+ return result;
+}
+EXPORT_SYMBOL_GPL(hv_get_vpreg);
+
+void hv_get_vpreg_128(u32 msr, struct hv_get_vp_registers_output *res)
+{
+ struct hv_get_vp_registers_input *input;
+ struct hv_get_vp_registers_output *output;
+
+ /*
+ * Allocate a power of 2 size so alignment to that size is
+ * guaranteed, since the hypercall input and output areas
+ * must not cross a page boundary.
+ */
+ input = kzalloc(roundup_pow_of_two(sizeof(input->header) +
+ sizeof(input->element[0])), GFP_ATOMIC);
+ output = kmalloc(roundup_pow_of_two(sizeof(*output)), GFP_ATOMIC);
+
+ __hv_get_vpreg_128(msr, input, output);
+
+ res->as64.low = output->as64.low;
+ res->as64.high = output->as64.high;
+ kfree(input);
+ kfree(output);
+}
+EXPORT_SYMBOL_GPL(hv_get_vpreg_128);
diff --git a/arch/arm64/include/asm/hyperv-tlfs.h b/arch/arm64/include/asm/hyperv-tlfs.h
new file mode 100644
index 0000000..4d964a7
--- /dev/null
+++ b/arch/arm64/include/asm/hyperv-tlfs.h
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * This file contains definitions from the Hyper-V Hypervisor Top-Level
+ * Functional Specification (TLFS):
+ * https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/reference/tlfs
+ *
+ * Copyright (C) 2021, Microsoft, Inc.
+ *
+ * Author : Michael Kelley <[email protected]>
+ */
+
+#ifndef _ASM_HYPERV_TLFS_H
+#define _ASM_HYPERV_TLFS_H
+
+#include <linux/types.h>
+
+/*
+ * All data structures defined in the TLFS that are shared between Hyper-V
+ * and a guest VM use Little Endian byte ordering. This matches the default
+ * byte ordering of Linux running on ARM64, so no special handling is required.
+ */
+
+/*
+ * These Hyper-V registers provide information equivalent to the CPUID
+ * instruction on x86/x64.
+ */
+#define HV_REGISTER_HYPERVISOR_VERSION 0x00000100 /*CPUID 0x40000002 */
+#define HV_REGISTER_FEATURES 0x00000200 /*CPUID 0x40000003 */
+#define HV_REGISTER_ENLIGHTENMENTS 0x00000201 /*CPUID 0x40000004 */
+
+/*
+ * Group C Features. See the asm-generic version of hyperv-tlfs.h
+ * for a description of Feature Groups.
+ */
+
+/* Crash MSRs available */
+#define HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE BIT(8)
+
+/* STIMER direct mode is available */
+#define HV_STIMER_DIRECT_MODE_AVAILABLE BIT(13)
+
+/*
+ * Synthetic register definitions equivalent to MSRs on x86/x64
+ */
+#define HV_REGISTER_CRASH_P0 0x00000210
+#define HV_REGISTER_CRASH_P1 0x00000211
+#define HV_REGISTER_CRASH_P2 0x00000212
+#define HV_REGISTER_CRASH_P3 0x00000213
+#define HV_REGISTER_CRASH_P4 0x00000214
+#define HV_REGISTER_CRASH_CTL 0x00000215
+
+#define HV_REGISTER_GUEST_OSID 0x00090002
+#define HV_REGISTER_VP_INDEX 0x00090003
+#define HV_REGISTER_TIME_REF_COUNT 0x00090004
+#define HV_REGISTER_REFERENCE_TSC 0x00090017
+
+#define HV_REGISTER_SINT0 0x000A0000
+#define HV_REGISTER_SCONTROL 0x000A0010
+#define HV_REGISTER_SIEFP 0x000A0012
+#define HV_REGISTER_SIMP 0x000A0013
+#define HV_REGISTER_EOM 0x000A0014
+
+#define HV_REGISTER_STIMER0_CONFIG 0x000B0000
+#define HV_REGISTER_STIMER0_COUNT 0x000B0001
+
+#include <asm-generic/hyperv-tlfs.h>
+
+#endif
diff --git a/arch/arm64/include/asm/mshyperv.h b/arch/arm64/include/asm/mshyperv.h
new file mode 100644
index 0000000..44ee012
--- /dev/null
+++ b/arch/arm64/include/asm/mshyperv.h
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * Linux-specific definitions for managing interactions with Microsoft's
+ * Hyper-V hypervisor. The definitions in this file are specific to
+ * the ARM64 architecture. See include/asm-generic/mshyperv.h for
+ * definitions are that architecture independent.
+ *
+ * Definitions that are specified in the Hyper-V Top Level Functional
+ * Spec (TLFS) should not go in this file, but should instead go in
+ * hyperv-tlfs.h.
+ *
+ * Copyright (C) 2021, Microsoft, Inc.
+ *
+ * Author : Michael Kelley <[email protected]>
+ */
+
+#ifndef _ASM_MSHYPERV_H
+#define _ASM_MSHYPERV_H
+
+#include <linux/types.h>
+#include <linux/arm-smccc.h>
+#include <asm/hyperv-tlfs.h>
+
+/*
+ * Declare calls to get and set Hyper-V VP register values on ARM64, which
+ * requires a hypercall.
+ */
+
+extern void hv_set_vpreg(u32 reg, u64 value);
+extern u64 hv_get_vpreg(u32 reg);
+extern void __hv_get_vpreg_128(u32 reg, struct hv_get_vp_registers_input *input,
+ struct hv_get_vp_registers_output *result);
+
+static inline void hv_set_register(unsigned int reg, u64 value)
+{
+ hv_set_vpreg(reg, value);
+}
+
+static inline u64 hv_get_register(unsigned int reg)
+{
+ return hv_get_vpreg(reg);
+}
+
+/* SMCCC hypercall parameters */
+#define HV_SMCCC_FUNC_NUMBER 1
+#define HV_FUNC_ID ARM_SMCCC_CALL_VAL( \
+ ARM_SMCCC_STD_CALL, \
+ ARM_SMCCC_SMC_64, \
+ ARM_SMCCC_OWNER_VENDOR_HYP, \
+ HV_SMCCC_FUNC_NUMBER)
+
+#include <asm-generic/mshyperv.h>
+
+#endif
--
1.8.3.1
The Hyper-V frame buffer driver may be built as a module, and
it needs access to screen_info. So export screen_info.
Signed-off-by: Michael Kelley <[email protected]>
---
arch/arm64/kernel/efi.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c
index fa02efb..e1be6c4 100644
--- a/arch/arm64/kernel/efi.c
+++ b/arch/arm64/kernel/efi.c
@@ -55,6 +55,7 @@ static __init pteval_t create_mapping_protection(efi_memory_desc_t *md)
/* we will fill this structure from the stub, so don't put it in .bss */
struct screen_info screen_info __section(".data");
+EXPORT_SYMBOL(screen_info);
int __init efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md)
{
--
1.8.3.1
Update drivers/hv/Kconfig so CONFIG_HYPERV can be selected on
ARM64, causing the Hyper-V specific code to be built.
Signed-off-by: Michael Kelley <[email protected]>
---
drivers/hv/Kconfig | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/hv/Kconfig b/drivers/hv/Kconfig
index 79e5356..d492682 100644
--- a/drivers/hv/Kconfig
+++ b/drivers/hv/Kconfig
@@ -4,7 +4,8 @@ menu "Microsoft Hyper-V guest support"
config HYPERV
tristate "Microsoft Hyper-V client drivers"
- depends on X86 && ACPI && X86_LOCAL_APIC && HYPERVISOR_GUEST
+ depends on ACPI && ((X86 && X86_LOCAL_APIC && HYPERVISOR_GUEST) \
+ || (ARM64 && !CPU_BIG_ENDIAN))
select PARAVIRT
select X86_HV_CALLBACK_VECTOR
help
--
1.8.3.1
Hi Michael,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on arm64/for-next/core]
[also build test WARNING on tip/timers/core efi/next linus/master v5.11 next-20210218]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/Michael-Kelley/Enable-Linux-guests-on-Hyper-V-on-ARM64/20210219-072336
base: https://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git for-next/core
config: i386-randconfig-a003-20210218 (attached as .config)
compiler: gcc-9 (Debian 9.3.0-15) 9.3.0
reproduce (this is a W=1 build):
# https://github.com/0day-ci/linux/commit/a8eb25332c441e0965c0ecdfb1a86b507e3465e1
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Michael-Kelley/Enable-Linux-guests-on-Hyper-V-on-ARM64/20210219-072336
git checkout a8eb25332c441e0965c0ecdfb1a86b507e3465e1
# save the attached .config to linux build tree
make W=1 ARCH=i386
If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <[email protected]>
All warnings (new ones prefixed by >>):
>> drivers/clocksource/hyperv_timer.c:478:44: warning: 'struct acpi_table_header' declared inside parameter list will not be visible outside of this definition or declaration
478 | static int __init hyperv_timer_init(struct acpi_table_header *table)
| ^~~~~~~~~~~~~~~~~
drivers/clocksource/hyperv_timer.c: In function 'hyperv_timer_init':
drivers/clocksource/hyperv_timer.c:484:6: error: too many arguments to function 'hv_stimer_alloc'
484 | if (hv_stimer_alloc(true))
| ^~~~~~~~~~~~~~~
drivers/clocksource/hyperv_timer.c:173:5: note: declared here
173 | int hv_stimer_alloc(void)
| ^~~~~~~~~~~~~~~
In file included from include/linux/clockchips.h:14,
from drivers/clocksource/hyperv_timer.c:16:
drivers/clocksource/hyperv_timer.c: At top level:
include/linux/clocksource.h:283:50: error: expected ')' before numeric constant
283 | ACPI_DECLARE_PROBE_ENTRY(timer, name, table_id, 0, NULL, 0, fn)
| ^
drivers/clocksource/hyperv_timer.c:489:1: note: in expansion of macro 'TIMER_ACPI_DECLARE'
489 | TIMER_ACPI_DECLARE(hyperv, ACPI_SIG_GTDT, hyperv_timer_init);
| ^~~~~~~~~~~~~~~~~~
drivers/clocksource/hyperv_timer.c:478:19: warning: 'hyperv_timer_init' defined but not used [-Wunused-function]
478 | static int __init hyperv_timer_init(struct acpi_table_header *table)
| ^~~~~~~~~~~~~~~~~
vim +478 drivers/clocksource/hyperv_timer.c
476
477 /* Initialize everything on ARM64 */
> 478 static int __init hyperv_timer_init(struct acpi_table_header *table)
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/[email protected]
Hi Michael,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on arm64/for-next/core]
[also build test ERROR on tip/timers/core efi/next linus/master v5.11 next-20210218]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/Michael-Kelley/Enable-Linux-guests-on-Hyper-V-on-ARM64/20210219-072336
base: https://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git for-next/core
config: i386-randconfig-a003-20210218 (attached as .config)
compiler: gcc-9 (Debian 9.3.0-15) 9.3.0
reproduce (this is a W=1 build):
# https://github.com/0day-ci/linux/commit/a8eb25332c441e0965c0ecdfb1a86b507e3465e1
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Michael-Kelley/Enable-Linux-guests-on-Hyper-V-on-ARM64/20210219-072336
git checkout a8eb25332c441e0965c0ecdfb1a86b507e3465e1
# save the attached .config to linux build tree
make W=1 ARCH=i386
If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <[email protected]>
All errors (new ones prefixed by >>):
drivers/clocksource/hyperv_timer.c:478:44: warning: 'struct acpi_table_header' declared inside parameter list will not be visible outside of this definition or declaration
478 | static int __init hyperv_timer_init(struct acpi_table_header *table)
| ^~~~~~~~~~~~~~~~~
drivers/clocksource/hyperv_timer.c: In function 'hyperv_timer_init':
>> drivers/clocksource/hyperv_timer.c:484:6: error: too many arguments to function 'hv_stimer_alloc'
484 | if (hv_stimer_alloc(true))
| ^~~~~~~~~~~~~~~
drivers/clocksource/hyperv_timer.c:173:5: note: declared here
173 | int hv_stimer_alloc(void)
| ^~~~~~~~~~~~~~~
In file included from include/linux/clockchips.h:14,
from drivers/clocksource/hyperv_timer.c:16:
drivers/clocksource/hyperv_timer.c: At top level:
>> include/linux/clocksource.h:283:50: error: expected ')' before numeric constant
283 | ACPI_DECLARE_PROBE_ENTRY(timer, name, table_id, 0, NULL, 0, fn)
| ^
drivers/clocksource/hyperv_timer.c:489:1: note: in expansion of macro 'TIMER_ACPI_DECLARE'
489 | TIMER_ACPI_DECLARE(hyperv, ACPI_SIG_GTDT, hyperv_timer_init);
| ^~~~~~~~~~~~~~~~~~
drivers/clocksource/hyperv_timer.c:478:19: warning: 'hyperv_timer_init' defined but not used [-Wunused-function]
478 | static int __init hyperv_timer_init(struct acpi_table_header *table)
| ^~~~~~~~~~~~~~~~~
vim +/hv_stimer_alloc +484 drivers/clocksource/hyperv_timer.c
476
477 /* Initialize everything on ARM64 */
478 static int __init hyperv_timer_init(struct acpi_table_header *table)
479 {
480 if (!hv_is_hyperv_initialized())
481 return -EINVAL;
482
483 hv_init_clocksource();
> 484 if (hv_stimer_alloc(true))
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/[email protected]
On Thu, Feb 18, 2021 at 03:16:29PM -0800, Michael Kelley wrote:
> hyperv-tlfs.h defines Hyper-V interfaces from the Hyper-V Top Level
> Functional Spec (TLFS), and #includes the architecture-independent
> part of hyperv-tlfs.h in include/asm-generic. The published TLFS
> is distinctly oriented to x86/x64, so the ARM64-specific
> hyperv-tlfs.h includes information for ARM64 that is not yet formally
> published. The TLFS is available here:
>
> docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/reference/tlfs
>
> mshyperv.h defines Linux-specific structures and routines for
> interacting with Hyper-V on ARM64, and #includes the architecture-
> independent part of mshyperv.h in include/asm-generic.
>
> Use these definitions to provide utility functions to make
> Hyper-V hypercalls and to get and set Hyper-V provided
> registers associated with a virtual processor.
>
> Signed-off-by: Michael Kelley <[email protected]>
Reviewed-by: Wei Liu <[email protected]>
On Thu, Feb 18, 2021 at 03:16:29PM -0800, Michael Kelley wrote:
[...]
> +
> +/*
> + * Get the value of a single VP register. One version
> + * returns just 64 bits and another returns the full 128 bits.
> + * The two versions are separate to avoid complicating the
> + * calling sequence for the more frequently used 64 bit version.
> + */
> +
> +void __hv_get_vpreg_128(u32 msr,
> + struct hv_get_vp_registers_input *input,
> + struct hv_get_vp_registers_output *res)
> +{
> + u64 status;
> +
> + input->header.partitionid = HV_PARTITION_ID_SELF;
> + input->header.vpindex = HV_VP_INDEX_SELF;
> + input->header.inputvtl = 0;
> + input->element[0].name0 = msr;
> + input->element[0].name1 = 0;
> +
> +
> + status = hv_do_hypercall(
> + HVCALL_GET_VP_REGISTERS | HV_HYPERCALL_REP_COMP_1,
> + input, res);
> +
> + /*
> + * Something is fundamentally broken in the hypervisor if
> + * getting a VP register fails. There's really no way to
> + * continue as a guest VM, so panic.
> + */
> + BUG_ON((status & HV_HYPERCALL_RESULT_MASK) != HV_STATUS_SUCCESS);
> +}
> +
> +u64 hv_get_vpreg(u32 msr)
> +{
> + struct hv_get_vp_registers_input *input;
> + struct hv_get_vp_registers_output *output;
> + u64 result;
> +
> + /*
> + * Allocate a power of 2 size so alignment to that size is
> + * guaranteed, since the hypercall input and output areas
> + * must not cross a page boundary.
> + */
> + input = kzalloc(roundup_pow_of_two(sizeof(input->header) +
> + sizeof(input->element[0])), GFP_ATOMIC);
> + output = kmalloc(roundup_pow_of_two(sizeof(*output)), GFP_ATOMIC);
> +
Do we need to BUG_ON(!input || !output)? Or we expect the page fault
(for input being NULL) or the failure of hypercall (for output being
NULL) to tell us the allocation failed?
Hmm.. think a bit more on this, maybe we'd better retry the allocation
if it failed. Because say we are under memory pressusre, and only have
memory enough for doing one hvcall, and one thread allocates that memory
but gets preempted by another thread trying to do another hvcall:
<thread 1>
hv_get_vpreg():
input = kzalloc(...);
output = kmalloc(...);
<preempted and switch to thread 2>
hv_get_vpreg():
intput = kzalloc(...); // allocation fails, but actually if
// we wait for thread 1 to finish its
// hvcall, we can get enough memory.
, in this case, if thread 2 retried, it might get the enough memory,
therefore there is no need to BUG_ON() on allocation failure. That said,
I don't think this is likely to happen, and there may be better
solutions for this, so maybe we can keep it as it is (assuming that
memory allocation for hvcall never fails) and improve later.
Regards,
Boqun
> + __hv_get_vpreg_128(msr, input, output);
> +
> + result = output->as64.low;
> + kfree(input);
> + kfree(output);
> + return result;
> +}
> +EXPORT_SYMBOL_GPL(hv_get_vpreg);
> +
> +void hv_get_vpreg_128(u32 msr, struct hv_get_vp_registers_output *res)
> +{
> + struct hv_get_vp_registers_input *input;
> + struct hv_get_vp_registers_output *output;
> +
> + /*
> + * Allocate a power of 2 size so alignment to that size is
> + * guaranteed, since the hypercall input and output areas
> + * must not cross a page boundary.
> + */
> + input = kzalloc(roundup_pow_of_two(sizeof(input->header) +
> + sizeof(input->element[0])), GFP_ATOMIC);
> + output = kmalloc(roundup_pow_of_two(sizeof(*output)), GFP_ATOMIC);
> +
> + __hv_get_vpreg_128(msr, input, output);
> +
> + res->as64.low = output->as64.low;
> + res->as64.high = output->as64.high;
> + kfree(input);
> + kfree(output);
> +}
[...]
> > +
> > + /*
> > + * Allocate a power of 2 size so alignment to that size is
> > + * guaranteed, since the hypercall input and output areas
> > + * must not cross a page boundary.
> > + */
> > + input = kzalloc(roundup_pow_of_two(sizeof(input->header) +
> > + sizeof(input->element[0])), GFP_ATOMIC);
> > + output = kmalloc(roundup_pow_of_two(sizeof(*output)), GFP_ATOMIC);
> > +
> Check for null from these malloc routines? Here and in other places.
Between, is there a plan to setup a percpu input/output page for hypercall
input/output on ARM (like we do for x64)? I didn't check the specific size requirement
for this particular call, but, that generally will remove the need for these
allocations.
> Update drivers/hv/Kconfig so CONFIG_HYPERV can be selected on
> ARM64, causing the Hyper-V specific code to be built.
>
> Signed-off-by: Michael Kelley <[email protected]>
> ---
> drivers/hv/Kconfig | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/hv/Kconfig b/drivers/hv/Kconfig
> index 79e5356..d492682 100644
> --- a/drivers/hv/Kconfig
> +++ b/drivers/hv/Kconfig
> @@ -4,7 +4,8 @@ menu "Microsoft Hyper-V guest support"
>
> config HYPERV
> tristate "Microsoft Hyper-V client drivers"
> - depends on X86 && ACPI && X86_LOCAL_APIC && HYPERVISOR_GUEST
> + depends on ACPI && ((X86 && X86_LOCAL_APIC && HYPERVISOR_GUEST) \
> + || (ARM64 && !CPU_BIG_ENDIAN))
> select PARAVIRT
> select X86_HV_CALLBACK_VECTOR
> help
> --
> 1.8.3.1
Reviewed-by: Sunil Muthuswamy <[email protected]>
> +/* Define the interrupt ID used by STIMER0 Direct Mode interrupts. This
> + * value can't come from ACPI tables because it is needed before the
> + * Linux ACPI subsystem is initialized.
> + */
> +#define HYPERV_STIMER0_VECTOR 31
nit: On x64, this is defined in HEX
> +
> + return 0;
> +}
> +TIMER_ACPI_DECLARE(hyperv, ACPI_SIG_GTDT, hyperv_timer_init);
> --
> 1.8.3.1
Reviewed-by: Sunil Muthuswamy <[email protected]>
> +}
> +EXPORT_SYMBOL_GPL(hv_do_fast_hypercall8);
> +
> +
nit: Extra line, here and few other places
> +u64 hv_get_vpreg(u32 msr)
> +{
> + struct hv_get_vp_registers_input *input;
> + struct hv_get_vp_registers_output *output;
> + u64 result;
It seems like the code is using both spaces and tabs to align variable names. Can
we stick to one or the other, preferably spaces.
> +
> + /*
> + * Allocate a power of 2 size so alignment to that size is
> + * guaranteed, since the hypercall input and output areas
> + * must not cross a page boundary.
> + */
> + input = kzalloc(roundup_pow_of_two(sizeof(input->header) +
> + sizeof(input->element[0])), GFP_ATOMIC);
> + output = kmalloc(roundup_pow_of_two(sizeof(*output)), GFP_ATOMIC);
> +
Check for null from these malloc routines? Here and in other places.
> + __hv_get_vpreg_128(msr, input, output);
+ * Linux-specific definitions for managing interactions with Microsoft's
+ * Hyper-V hypervisor. The definitions in this file are specific to
+ * the ARM64 architecture. See include/asm-generic/mshyperv.h for
nit: Two space before 'See'. Here and in couple of other places.
> +static u64 hypercall_output __initdata;
> +
> +static int __init hyperv_init(void)
> +{
> + struct hv_get_vpindex_from_apicid_input *input;
> + u64 status;
> + int i;
nit: both, tabs & spaces are being used to indent variable names. Can we stick
to one?
> +
> + /*
> + * Hypercall inputs must not cross a page boundary, so allocate
> + * power of 2 size, which will be aligned to that size.
> + */
> + input = kzalloc(roundup_pow_of_two(sizeof(input->header) +
> + sizeof(input->element[0])), GFP_KERNEL);
> + if (!input)
> + return -ENOMEM;
Similar to the comment on the other patch, can this be done through a
per-cpu hypercall input page?
> + if ((status & HV_HYPERCALL_RESULT_MASK) == HV_STATUS_SUCCESS)
> + hv_vp_index[i] = hypercall_output;
> + else {
CNF suggests using braces even for single line 'if' statements if the 'else' is a multi-line
statement
> + pr_warn("Hyper-V: No VP index for CPU %d MPIDR %llx status %llx\n",
> + i, cpu_logical_map(i), status);
> + hv_vp_index[i] = VP_INVAL;
> + }
> +bool hv_is_hyperv_initialized(void)
> +{
> + return hyperv_initialized;
> +}
> +EXPORT_SYMBOL_GPL(hv_is_hyperv_initialized);
This to me seems like more of an indication that whether the hypervisor is Hyper-V or
something else, rather than if necessary Hyper-V pieces have been initialized. If that is
the case, suggest renaming. Maybe just call it 'hv_is_hyperv'?
From: Boqun Feng <[email protected]> Sent: Tuesday, February 23, 2021 6:37 PM
>
> On Thu, Feb 18, 2021 at 03:16:29PM -0800, Michael Kelley wrote:
> [...]
> > +
> > +/*
> > + * Get the value of a single VP register. One version
> > + * returns just 64 bits and another returns the full 128 bits.
> > + * The two versions are separate to avoid complicating the
> > + * calling sequence for the more frequently used 64 bit version.
> > + */
> > +
> > +void __hv_get_vpreg_128(u32 msr,
> > + struct hv_get_vp_registers_input *input,
> > + struct hv_get_vp_registers_output *res)
> > +{
> > + u64 status;
> > +
> > + input->header.partitionid = HV_PARTITION_ID_SELF;
> > + input->header.vpindex = HV_VP_INDEX_SELF;
> > + input->header.inputvtl = 0;
> > + input->element[0].name0 = msr;
> > + input->element[0].name1 = 0;
> > +
> > +
> > + status = hv_do_hypercall(
> > + HVCALL_GET_VP_REGISTERS | HV_HYPERCALL_REP_COMP_1,
> > + input, res);
> > +
> > + /*
> > + * Something is fundamentally broken in the hypervisor if
> > + * getting a VP register fails. There's really no way to
> > + * continue as a guest VM, so panic.
> > + */
> > + BUG_ON((status & HV_HYPERCALL_RESULT_MASK) != HV_STATUS_SUCCESS);
> > +}
> > +
> > +u64 hv_get_vpreg(u32 msr)
> > +{
> > + struct hv_get_vp_registers_input *input;
> > + struct hv_get_vp_registers_output *output;
> > + u64 result;
> > +
> > + /*
> > + * Allocate a power of 2 size so alignment to that size is
> > + * guaranteed, since the hypercall input and output areas
> > + * must not cross a page boundary.
> > + */
> > + input = kzalloc(roundup_pow_of_two(sizeof(input->header) +
> > + sizeof(input->element[0])), GFP_ATOMIC);
> > + output = kmalloc(roundup_pow_of_two(sizeof(*output)), GFP_ATOMIC);
> > +
>
> Do we need to BUG_ON(!input || !output)? Or we expect the page fault
> (for input being NULL) or the failure of hypercall (for output being
> NULL) to tell us the allocation failed?
>
> Hmm.. think a bit more on this, maybe we'd better retry the allocation
> if it failed. Because say we are under memory pressusre, and only have
> memory enough for doing one hvcall, and one thread allocates that memory
> but gets preempted by another thread trying to do another hvcall:
>
> <thread 1>
> hv_get_vpreg():
> input = kzalloc(...);
> output = kmalloc(...);
> <preempted and switch to thread 2>
> hv_get_vpreg():
> intput = kzalloc(...); // allocation fails, but actually if
> // we wait for thread 1 to finish its
> // hvcall, we can get enough memory.
>
> , in this case, if thread 2 retried, it might get the enough memory,
> therefore there is no need to BUG_ON() on allocation failure. That said,
> I don't think this is likely to happen, and there may be better
> solutions for this, so maybe we can keep it as it is (assuming that
> memory allocation for hvcall never fails) and improve later.
>
> Regards,
> Boqun
Having to do these memory allocations in order to make a
hypercall results in a lot of messiness. I've just gone back to
try again at doing hv_get_vpreg() and hv_get_vpreg_128()
as "fast" hypercalls that pass inputs (and outputs) in registers
like hv_set_vpreg(). I have it working now, with some tweaks
to arm_smccc_1_1_hvc() to allow outputs in a wider range
of registers than just X0 thru X3. This wider range of registers
is allowed by the SMCCC version 1.2 and 1.3 specs, so hopefully
is acceptable. I'll send out a new version using this "fast"
hypercall approach that completely avoids all these memory
allocation problems.
Michael
>
> > + __hv_get_vpreg_128(msr, input, output);
> > +
> > + result = output->as64.low;
> > + kfree(input);
> > + kfree(output);
> > + return result;
> > +}
> > +EXPORT_SYMBOL_GPL(hv_get_vpreg);
> > +
> > +void hv_get_vpreg_128(u32 msr, struct hv_get_vp_registers_output *res)
> > +{
> > + struct hv_get_vp_registers_input *input;
> > + struct hv_get_vp_registers_output *output;
> > +
> > + /*
> > + * Allocate a power of 2 size so alignment to that size is
> > + * guaranteed, since the hypercall input and output areas
> > + * must not cross a page boundary.
> > + */
> > + input = kzalloc(roundup_pow_of_two(sizeof(input->header) +
> > + sizeof(input->element[0])), GFP_ATOMIC);
> > + output = kmalloc(roundup_pow_of_two(sizeof(*output)), GFP_ATOMIC);
> > +
> > + __hv_get_vpreg_128(msr, input, output);
> > +
> > + res->as64.low = output->as64.low;
> > + res->as64.high = output->as64.high;
> > + kfree(input);
> > + kfree(output);
> > +}
> [...]
From: Sunil Muthuswamy <[email protected]> Sent: Tuesday, March 2, 2021 4:35 PM
>
> > +}
> > +EXPORT_SYMBOL_GPL(hv_do_fast_hypercall8);
> > +
> > +
> nit: Extra line, here and few other places
I'll try to clean these up.
>
> > +u64 hv_get_vpreg(u32 msr)
> > +{
> > + struct hv_get_vp_registers_input *input;
> > + struct hv_get_vp_registers_output *output;
> > + u64 result;
> It seems like the code is using both spaces and tabs to align variable names. Can
> we stick to one or the other, preferably spaces.
I think the general preference is to use tabs for alignment, not
spaces, so I'll try to clean these up in the next version.
>
> > +
> > + /*
> > + * Allocate a power of 2 size so alignment to that size is
> > + * guaranteed, since the hypercall input and output areas
> > + * must not cross a page boundary.
> > + */
> > + input = kzalloc(roundup_pow_of_two(sizeof(input->header) +
> > + sizeof(input->element[0])), GFP_ATOMIC);
> > + output = kmalloc(roundup_pow_of_two(sizeof(*output)), GFP_ATOMIC);
> > +
> Check for null from these malloc routines? Here and in other places.
See my comment back to Boqun Feng on the same point.
>
> > + __hv_get_vpreg_128(msr, input, output);
>
> + * Linux-specific definitions for managing interactions with Microsoft's
> + * Hyper-V hypervisor. The definitions in this file are specific to
> + * the ARM64 architecture. See include/asm-generic/mshyperv.h for
> nit: Two space before 'See'. Here and in couple of other places.
The debate rages in the broader world of writing about using one
space vs. two spaces. :-) I'm still old school on this point, and use two
spaces out of decades of habit, particularly with monospaced fonts,
though I'm probably not 100% consistent.
Michael
From: Sunil Muthuswamy <[email protected]> Sent: Wednesday, March 3, 2021 10:28 AM
>
> > > +
> > > + /*
> > > + * Allocate a power of 2 size so alignment to that size is
> > > + * guaranteed, since the hypercall input and output areas
> > > + * must not cross a page boundary.
> > > + */
> > > + input = kzalloc(roundup_pow_of_two(sizeof(input->header) +
> > > + sizeof(input->element[0])), GFP_ATOMIC);
> > > + output = kmalloc(roundup_pow_of_two(sizeof(*output)), GFP_ATOMIC);
> > > +
> > Check for null from these malloc routines? Here and in other places.
>
> Between, is there a plan to setup a percpu input/output page for hypercall
> input/output on ARM (like we do for x64)? I didn't check the specific size requirement
> for this particular call, but, that generally will remove the need for these
> allocations.
I'm not planning to add percpu input/output pages in this patch set. As noted
in another reply, we can eliminate these memory allocations entirely by using
"fast" hypercalls. At that point, there would be no usage of percpu input/output
pages in this patch set. If the need arises in the context of future work, we can
add them at that time.
Michael
From: Sunil Muthuswamy <[email protected]> Sent: Wednesday, March 3, 2021 12:21 PM
>
> > +static u64 hypercall_output __initdata;
> > +
> > +static int __init hyperv_init(void)
> > +{
> > + struct hv_get_vpindex_from_apicid_input *input;
> > + u64 status;
> > + int i;
> nit: both, tabs & spaces are being used to indent variable names. Can we stick
> to one?
>
Agreed. Will make this consistent in the next version.
> > +
> > + /*
> > + * Hypercall inputs must not cross a page boundary, so allocate
> > + * power of 2 size, which will be aligned to that size.
> > + */
> > + input = kzalloc(roundup_pow_of_two(sizeof(input->header) +
> > + sizeof(input->element[0])), GFP_KERNEL);
> > + if (!input)
> > + return -ENOMEM;
> Similar to the comment on the other patch, can this be done through a
> per-cpu hypercall input page?
Per comments on other previous patch, these allocations can be eliminated
and the problem avoided entirely.
>
> > + if ((status & HV_HYPERCALL_RESULT_MASK) == HV_STATUS_SUCCESS)
> > + hv_vp_index[i] = hypercall_output;
> > + else {
> CNF suggests using braces even for single line 'if' statements if the 'else' is a multi-line
> statement
Will do.
>
> > + pr_warn("Hyper-V: No VP index for CPU %d MPIDR %llx status
> %llx\n",
> > + i, cpu_logical_map(i), status);
> > + hv_vp_index[i] = VP_INVAL;
> > + }
>
> > +bool hv_is_hyperv_initialized(void)
> > +{
> > + return hyperv_initialized;
> > +}
> > +EXPORT_SYMBOL_GPL(hv_is_hyperv_initialized);
>
> This to me seems like more of an indication that whether the hypervisor is Hyper-V or
> something else, rather than if necessary Hyper-V pieces have been initialized. If that is
> the case, suggest renaming. Maybe just call it 'hv_is_hyperv'?
This function also exists on the x86/x64 side and is used in architecture independent
code. It's meaning is somewhere between "is Hyper-V" and "is Hyper-V initialized".
I'm not sure there's much value in changing the name everywhere.
Michael