Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 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_OS_ID); /* * 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_GUEST_CRASH_P0, err); hv_set_vpreg(HV_REGISTER_GUEST_CRASH_P1, guest_id); hv_set_vpreg(HV_REGISTER_GUEST_CRASH_P2, regs->pc); hv_set_vpreg(HV_REGISTER_GUEST_CRASH_P3, regs->sp); hv_set_vpreg(HV_REGISTER_GUEST_CRASH_P4, 0); /* * Let Hyper-V know there is crash data available */ hv_set_vpreg(HV_REGISTER_GUEST_CRASH_CTL, HV_CRASH_CTL_CRASH_NOTIFY); } EXPORT_SYMBOL_GPL(hyperv_report_panic); |