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