FILE
mshv_common.c
File preview with syntax highlighting.
linux-6.19.14/drivers/hv/mshv_common.c
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Copyright (c) 2024, Microsoft Corporation. 4 * 5 * This file contains functions that will be called from one or more modules. 6 * If any of these modules are configured to build, this file is built and just 7 * statically linked in. 8 * 9 * Authors: Microsoft Linux virtualization team 10 */ 11 12#include <linux/kernel.h> 13#include <linux/mm.h> 14#include <asm/mshyperv.h> 15#include <linux/resume_user_mode.h> 16#include <linux/export.h> 17#include <linux/acpi.h> 18#include <linux/notifier.h> 19#include <linux/reboot.h> 20 21#include "mshv.h" 22 23#define HV_GET_REGISTER_BATCH_SIZE \ 24 (HV_HYP_PAGE_SIZE / sizeof(union hv_register_value)) 25#define HV_SET_REGISTER_BATCH_SIZE \ 26 ((HV_HYP_PAGE_SIZE - sizeof(struct hv_input_set_vp_registers)) \ 27 / sizeof(struct hv_register_assoc)) 28 29int hv_call_get_vp_registers(u32 vp_index, u64 partition_id, u16 count, 30 union hv_input_vtl input_vtl, 31 struct hv_register_assoc *registers) 32{ 33 struct hv_input_get_vp_registers *input_page; 34 union hv_register_value *output_page; 35 u16 completed = 0; 36 unsigned long remaining = count; 37 int rep_count, i; 38 u64 status = HV_STATUS_SUCCESS; 39 unsigned long flags; 40 41 local_irq_save(flags); 42 43 input_page = *this_cpu_ptr(hyperv_pcpu_input_arg); 44 output_page = *this_cpu_ptr(hyperv_pcpu_output_arg); 45 46 input_page->partition_id = partition_id; 47 input_page->vp_index = vp_index; 48 input_page->input_vtl.as_uint8 = input_vtl.as_uint8; 49 input_page->rsvd_z8 = 0; 50 input_page->rsvd_z16 = 0; 51 52 while (remaining) { 53 rep_count = min(remaining, HV_GET_REGISTER_BATCH_SIZE); 54 for (i = 0; i < rep_count; ++i) 55 input_page->names[i] = registers[i].name; 56 57 status = hv_do_rep_hypercall(HVCALL_GET_VP_REGISTERS, rep_count, 58 0, input_page, output_page); 59 if (!hv_result_success(status)) 60 break; 61 62 completed = hv_repcomp(status); 63 for (i = 0; i < completed; ++i) 64 registers[i].value = output_page[i]; 65 66 registers += completed; 67 remaining -= completed; 68 } 69 local_irq_restore(flags); 70 71 return hv_result_to_errno(status); 72} 73EXPORT_SYMBOL_GPL(hv_call_get_vp_registers); 74 75int hv_call_set_vp_registers(u32 vp_index, u64 partition_id, u16 count, 76 union hv_input_vtl input_vtl, 77 struct hv_register_assoc *registers) 78{ 79 struct hv_input_set_vp_registers *input_page; 80 u16 completed = 0; 81 unsigned long remaining = count; 82 int rep_count; 83 u64 status = HV_STATUS_SUCCESS; 84 unsigned long flags; 85 86 local_irq_save(flags); 87 input_page = *this_cpu_ptr(hyperv_pcpu_input_arg); 88 89 input_page->partition_id = partition_id; 90 input_page->vp_index = vp_index; 91 input_page->input_vtl.as_uint8 = input_vtl.as_uint8; 92 input_page->rsvd_z8 = 0; 93 input_page->rsvd_z16 = 0; 94 95 while (remaining) { 96 rep_count = min(remaining, HV_SET_REGISTER_BATCH_SIZE); 97 memcpy(input_page->elements, registers, 98 sizeof(struct hv_register_assoc) * rep_count); 99 100 status = hv_do_rep_hypercall(HVCALL_SET_VP_REGISTERS, rep_count, 101 0, input_page, NULL); 102 if (!hv_result_success(status)) 103 break; 104 105 completed = hv_repcomp(status); 106 registers += completed; 107 remaining -= completed; 108 } 109 110 local_irq_restore(flags); 111 112 return hv_result_to_errno(status); 113} 114EXPORT_SYMBOL_GPL(hv_call_set_vp_registers); 115 116int hv_call_get_partition_property(u64 partition_id, 117 u64 property_code, 118 u64 *property_value) 119{ 120 u64 status; 121 unsigned long flags; 122 struct hv_input_get_partition_property *input; 123 struct hv_output_get_partition_property *output; 124 125 local_irq_save(flags); 126 input = *this_cpu_ptr(hyperv_pcpu_input_arg); 127 output = *this_cpu_ptr(hyperv_pcpu_output_arg); 128 memset(input, 0, sizeof(*input)); 129 input->partition_id = partition_id; 130 input->property_code = property_code; 131 status = hv_do_hypercall(HVCALL_GET_PARTITION_PROPERTY, input, output); 132 133 if (!hv_result_success(status)) { 134 local_irq_restore(flags); 135 return hv_result_to_errno(status); 136 } 137 *property_value = output->property_value; 138 139 local_irq_restore(flags); 140 141 return 0; 142} 143EXPORT_SYMBOL_GPL(hv_call_get_partition_property); 144 145#ifdef CONFIG_X86 146/* 147 * Corresponding sleep states have to be initialized in order for a subsequent 148 * HVCALL_ENTER_SLEEP_STATE call to succeed. Currently only S5 state as per 149 * ACPI 6.4 chapter 7.4.2 is relevant, while S1, S2 and S3 can be supported. 150 * 151 * In order to pass proper PM values to mshv, ACPI should be initialized and 152 * should support S5 sleep state when this method is invoked. 153 */ 154static int hv_initialize_sleep_states(void) 155{ 156 u64 status; 157 unsigned long flags; 158 struct hv_input_set_system_property *in; 159 acpi_status acpi_status; 160 u8 sleep_type_a, sleep_type_b; 161 162 if (!acpi_sleep_state_supported(ACPI_STATE_S5)) { 163 pr_err("%s: S5 sleep state not supported.\n", __func__); 164 return -ENODEV; 165 } 166 167 acpi_status = acpi_get_sleep_type_data(ACPI_STATE_S5, &sleep_type_a, 168 &sleep_type_b); 169 if (ACPI_FAILURE(acpi_status)) 170 return -ENODEV; 171 172 local_irq_save(flags); 173 in = *this_cpu_ptr(hyperv_pcpu_input_arg); 174 memset(in, 0, sizeof(*in)); 175 176 in->property_id = HV_SYSTEM_PROPERTY_SLEEP_STATE; 177 in->set_sleep_state_info.sleep_state = HV_SLEEP_STATE_S5; 178 in->set_sleep_state_info.pm1a_slp_typ = sleep_type_a; 179 in->set_sleep_state_info.pm1b_slp_typ = sleep_type_b; 180 181 status = hv_do_hypercall(HVCALL_SET_SYSTEM_PROPERTY, in, NULL); 182 local_irq_restore(flags); 183 184 if (!hv_result_success(status)) { 185 hv_status_err(status, "\n"); 186 return hv_result_to_errno(status); 187 } 188 189 return 0; 190} 191 192/* 193 * This notifier initializes sleep states in mshv hypervisor which will be 194 * used during power off. 195 */ 196static int hv_reboot_notifier_handler(struct notifier_block *this, 197 unsigned long code, void *another) 198{ 199 int ret = 0; 200 201 if (code == SYS_HALT || code == SYS_POWER_OFF) 202 ret = hv_initialize_sleep_states(); 203 204 return ret ? NOTIFY_DONE : NOTIFY_OK; 205} 206 207static struct notifier_block hv_reboot_notifier = { 208 .notifier_call = hv_reboot_notifier_handler, 209}; 210 211void hv_sleep_notifiers_register(void) 212{ 213 int ret; 214 215 ret = register_reboot_notifier(&hv_reboot_notifier); 216 if (ret) 217 pr_err("%s: cannot register reboot notifier %d\n", __func__, 218 ret); 219} 220 221/* 222 * Power off the machine by entering S5 sleep state via Hyper-V hypercall. 223 * This call does not return if successful. 224 */ 225void hv_machine_power_off(void) 226{ 227 unsigned long flags; 228 struct hv_input_enter_sleep_state *in; 229 230 local_irq_save(flags); 231 in = *this_cpu_ptr(hyperv_pcpu_input_arg); 232 in->sleep_state = HV_SLEEP_STATE_S5; 233 234 (void)hv_do_hypercall(HVCALL_ENTER_SLEEP_STATE, in, NULL); 235 local_irq_restore(flags); 236 237 /* should never reach here */ 238 BUG(); 239 240} 241#endif
~/
Read-only file preview.