diff options
author | Thomas Gleixner <tglx@linutronix.de> | 2021-09-11 01:38:47 +0300 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2021-09-11 01:38:47 +0300 |
commit | c2f4954c2d3fc4f77b46c67585e17a58df4ba8e4 (patch) | |
tree | 533a2077028e02a851e51ad509a0aa3a9107999f /arch/arm64/hyperv/hv_core.c | |
parent | 4b92d4add5f6dcf21275185c997d6ecb800054cd (diff) | |
parent | 926de8c4326c14fcf35f1de142019043597a4fac (diff) | |
download | linux-c2f4954c2d3fc4f77b46c67585e17a58df4ba8e4.tar.xz |
Merge branch 'linus' into smp/urgent
Ensure that all usage sites of get/put_online_cpus() except for the
struggler in drivers/thermal are gone. So the last user and the deprecated
inlines can be removed.
Diffstat (limited to 'arch/arm64/hyperv/hv_core.c')
-rw-r--r-- | arch/arm64/hyperv/hv_core.c | 181 |
1 files changed, 181 insertions, 0 deletions
diff --git a/arch/arm64/hyperv/hv_core.c b/arch/arm64/hyperv/hv_core.c new file mode 100644 index 000000000000..b54c34793701 --- /dev/null +++ b/arch/arm64/hyperv/hv_core.c @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Low level utility routines for interacting with Hyper-V. + * + * Copyright (C) 2021, Microsoft, Inc. + * + * Author : Michael Kelley <mikelley@microsoft.com> + */ + +#include <linux/types.h> +#include <linux/export.h> +#include <linux/mm.h> +#include <linux/hyperv.h> +#include <linux/arm-smccc.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) +{ + struct arm_smccc_res res; + u64 input_address; + u64 output_address; + + 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) +{ + struct arm_smccc_res res; + u64 control; + + 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(!hv_result_success(res.a0)); +} +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_output *result) +{ + struct arm_smccc_1_2_regs args; + struct arm_smccc_1_2_regs res; + + args.a0 = HV_FUNC_ID; + args.a1 = HVCALL_GET_VP_REGISTERS | HV_HYPERCALL_FAST_BIT | + HV_HYPERCALL_REP_COMP_1; + args.a2 = HV_PARTITION_ID_SELF; + args.a3 = HV_VP_INDEX_SELF; + args.a4 = msr; + + /* + * Use the SMCCC 1.2 interface because the results are in registers + * beyond X0-X3. + */ + arm_smccc_1_2_hvc(&args, &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(!hv_result_success(res.a0)); + + result->as64.low = res.a6; + result->as64.high = res.a7; +} +EXPORT_SYMBOL_GPL(hv_get_vpreg_128); + +u64 hv_get_vpreg(u32 msr) +{ + struct hv_get_vp_registers_output output; + + hv_get_vpreg_128(msr, &output); + + return output.as64.low; +} +EXPORT_SYMBOL_GPL(hv_get_vpreg); + +/* + * hyperv_report_panic - report a panic to Hyper-V. This function uses + * the older version of the Hyper-V interface that admittedly doesn't + * pass enough information to be useful beyond just recording the + * occurrence of a panic. The parallel hv_kmsg_dump() uses the + * new interface that allows reporting 4 Kbytes of data, which is much + * more useful. Hyper-V on ARM64 always supports the newer interface, but + * we retain support for the older version because the sysadmin is allowed + * to disable the newer version via sysctl in case of information security + * concerns about the more verbose version. + */ +void hyperv_report_panic(struct pt_regs *regs, long err, bool in_die) +{ + static bool panic_reported; + u64 guest_id; + + /* Don't report a panic to Hyper-V if we're not going to panic */ + if (in_die && !panic_on_oops) + return; + + /* + * We prefer to report panic on 'die' chain as we have proper + * registers to report, but if we miss it (e.g. on BUG()) we need + * to report it on 'panic'. + * + * Calling code in the 'die' and 'panic' paths ensures that only + * one CPU is running this code, so no atomicity is needed. + */ + if (panic_reported) + return; + panic_reported = true; + + guest_id = hv_get_vpreg(HV_REGISTER_GUEST_OSID); + + /* + * Hyper-V provides the ability to store only 5 values. + * Pick the passed in error value, the guest_id, the PC, + * and the SP. + */ + hv_set_vpreg(HV_REGISTER_CRASH_P0, err); + hv_set_vpreg(HV_REGISTER_CRASH_P1, guest_id); + hv_set_vpreg(HV_REGISTER_CRASH_P2, regs->pc); + hv_set_vpreg(HV_REGISTER_CRASH_P3, regs->sp); + hv_set_vpreg(HV_REGISTER_CRASH_P4, 0); + + /* + * Let Hyper-V know there is crash data available + */ + hv_set_vpreg(HV_REGISTER_CRASH_CTL, HV_CRASH_CTL_CRASH_NOTIFY); +} +EXPORT_SYMBOL_GPL(hyperv_report_panic); |