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 18 #include "mshv.h" 19 20 #define HV_GET_REGISTER_BATCH_SIZE \ 21 (HV_HYP_PAGE_SIZE / sizeof(union hv_register_value)) 22 #define HV_SET_REGISTER_BATCH_SIZE \ 23 ((HV_HYP_PAGE_SIZE - sizeof(struct hv_input_set_vp_registers)) \ 24 / sizeof(struct hv_register_assoc)) 25 26 int hv_call_get_vp_registers(u32 vp_index, u64 partition_id, u16 count, 27 union hv_input_vtl input_vtl, 28 struct hv_register_assoc *registers) 29 { 30 struct hv_input_get_vp_registers *input_page; 31 union hv_register_value *output_page; 32 u16 completed = 0; 33 unsigned long remaining = count; 34 int rep_count, i; 35 u64 status = HV_STATUS_SUCCESS; 36 unsigned long flags; 37 38 local_irq_save(flags); 39 40 input_page = *this_cpu_ptr(hyperv_pcpu_input_arg); 41 output_page = *this_cpu_ptr(hyperv_pcpu_output_arg); 42 43 input_page->partition_id = partition_id; 44 input_page->vp_index = vp_index; 45 input_page->input_vtl.as_uint8 = input_vtl.as_uint8; 46 input_page->rsvd_z8 = 0; 47 input_page->rsvd_z16 = 0; 48 49 while (remaining) { 50 rep_count = min(remaining, HV_GET_REGISTER_BATCH_SIZE); 51 for (i = 0; i < rep_count; ++i) 52 input_page->names[i] = registers[i].name; 53 54 status = hv_do_rep_hypercall(HVCALL_GET_VP_REGISTERS, rep_count, 55 0, input_page, output_page); 56 if (!hv_result_success(status)) 57 break; 58 59 completed = hv_repcomp(status); 60 for (i = 0; i < completed; ++i) 61 registers[i].value = output_page[i]; 62 63 registers += completed; 64 remaining -= completed; 65 } 66 local_irq_restore(flags); 67 68 return hv_result_to_errno(status); 69 } 70 EXPORT_SYMBOL_GPL(hv_call_get_vp_registers); 71 72 int hv_call_set_vp_registers(u32 vp_index, u64 partition_id, u16 count, 73 union hv_input_vtl input_vtl, 74 struct hv_register_assoc *registers) 75 { 76 struct hv_input_set_vp_registers *input_page; 77 u16 completed = 0; 78 unsigned long remaining = count; 79 int rep_count; 80 u64 status = HV_STATUS_SUCCESS; 81 unsigned long flags; 82 83 local_irq_save(flags); 84 input_page = *this_cpu_ptr(hyperv_pcpu_input_arg); 85 86 input_page->partition_id = partition_id; 87 input_page->vp_index = vp_index; 88 input_page->input_vtl.as_uint8 = input_vtl.as_uint8; 89 input_page->rsvd_z8 = 0; 90 input_page->rsvd_z16 = 0; 91 92 while (remaining) { 93 rep_count = min(remaining, HV_SET_REGISTER_BATCH_SIZE); 94 memcpy(input_page->elements, registers, 95 sizeof(struct hv_register_assoc) * rep_count); 96 97 status = hv_do_rep_hypercall(HVCALL_SET_VP_REGISTERS, rep_count, 98 0, input_page, NULL); 99 if (!hv_result_success(status)) 100 break; 101 102 completed = hv_repcomp(status); 103 registers += completed; 104 remaining -= completed; 105 } 106 107 local_irq_restore(flags); 108 109 return hv_result_to_errno(status); 110 } 111 EXPORT_SYMBOL_GPL(hv_call_set_vp_registers); 112 113 int hv_call_get_partition_property(u64 partition_id, 114 u64 property_code, 115 u64 *property_value) 116 { 117 u64 status; 118 unsigned long flags; 119 struct hv_input_get_partition_property *input; 120 struct hv_output_get_partition_property *output; 121 122 local_irq_save(flags); 123 input = *this_cpu_ptr(hyperv_pcpu_input_arg); 124 output = *this_cpu_ptr(hyperv_pcpu_output_arg); 125 memset(input, 0, sizeof(*input)); 126 input->partition_id = partition_id; 127 input->property_code = property_code; 128 status = hv_do_hypercall(HVCALL_GET_PARTITION_PROPERTY, input, output); 129 130 if (!hv_result_success(status)) { 131 local_irq_restore(flags); 132 return hv_result_to_errno(status); 133 } 134 *property_value = output->property_value; 135 136 local_irq_restore(flags); 137 138 return 0; 139 } 140 EXPORT_SYMBOL_GPL(hv_call_get_partition_property); 141 142 /* 143 * Handle any pre-processing before going into the guest mode on this cpu, most 144 * notably call schedule(). Must be invoked with both preemption and 145 * interrupts enabled. 146 * 147 * Returns: 0 on success, -errno on error. 148 */ 149 int mshv_do_pre_guest_mode_work(ulong th_flags) 150 { 151 if (th_flags & (_TIF_SIGPENDING | _TIF_NOTIFY_SIGNAL)) 152 return -EINTR; 153 154 if (th_flags & _TIF_NEED_RESCHED) 155 schedule(); 156 157 if (th_flags & _TIF_NOTIFY_RESUME) 158 resume_user_mode_work(NULL); 159 160 return 0; 161 } 162 EXPORT_SYMBOL_GPL(mshv_do_pre_guest_mode_work); 163