1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright (C) 2020-2022 Loongson Technology Corporation Limited 4 */ 5 6#include <linux/kernel.h> 7#include <linux/kvm_host.h> 8#include <linux/sched/stat.h> 9#include <asm/kvm_para.h> 10#include <asm/paravirt.h> 11#include "intc/ls3a_ipi.h" 12 13int kvm_virt_ipi(struct kvm_vcpu *vcpu) 14{ 15 int ret = 0; 16 u64 ipi_bitmap; 17 unsigned int min, action, cpu; 18 19 ipi_bitmap = vcpu->arch.gprs[REG_A1]; 20 min = vcpu->arch.gprs[REG_A2]; 21 action = vcpu->arch.gprs[REG_A3]; 22 23 if (ipi_bitmap) { 24 cpu = find_first_bit((void *)&ipi_bitmap, BITS_PER_LONG); 25 while (cpu < BITS_PER_LONG) { 26 kvm_helper_send_ipi(vcpu, cpu + min, action); 27 cpu = find_next_bit((void *)&ipi_bitmap, BITS_PER_LONG, cpu + 1); 28 } 29 } 30 31 return ret; 32} 33 34int kvm_save_notify(struct kvm_vcpu *vcpu) 35{ 36 unsigned long num, id, data; 37 struct gfn_to_pfn_cache *cache = &vcpu->arch.st.cache; 38 39 int ret = 0; 40 41 num = vcpu->arch.gprs[REG_A0]; 42 id = vcpu->arch.gprs[REG_A1]; 43 data = vcpu->arch.gprs[REG_A2]; 44 45 switch (id) { 46 case KVM_FEATURE_STEAL_TIME: 47 if (!sched_info_on()) 48 break; 49 50 if (vcpu->arch.st.guest_addr && (data == 0)) 51 kvm_release_pfn(cache->pfn, cache->dirty, cache); 52 53 vcpu->arch.st.guest_addr = data; 54 kvm_debug("cpu :%d addr:%lx\n", vcpu->vcpu_id, data); 55 vcpu->arch.st.last_steal = current->sched_info.run_delay; 56 kvm_make_request(KVM_REQ_RECORD_STEAL, vcpu); 57 break; 58 default: 59 break; 60 }; 61 62 return ret; 63}; 64 65static int _kvm_pv_feature(struct kvm_vcpu *vcpu) 66{ 67 int feature = vcpu->arch.gprs[REG_A1]; 68 int ret = KVM_RET_NOT_SUPPORTED; 69 switch (feature) { 70 case KVM_FEATURE_STEAL_TIME: 71 if (sched_info_on()) 72 ret = KVM_RET_SUC; 73 break; 74 case KVM_FEATURE_MULTI_IPI: 75 ret = KVM_RET_SUC; 76 break; 77 case KVM_FEATURE_PARAVIRT_SPINLOCK: 78#ifdef CONFIG_PARAVIRT_SPINLOCKS 79 ret = KVM_RET_SUC; 80#endif 81 break; 82 default: 83 break; 84 } 85 return ret; 86} 87 88static int kvm_pv_kick_cpu(struct kvm_vcpu *vcpu) 89{ 90 int cpu = vcpu->arch.gprs[REG_A1]; 91 struct kvm_vcpu *dst_vcpu = vcpu->kvm->vcpus[cpu]; 92 int ret = 0; 93 94 dst_vcpu->arch.pv.pv_unhalted = true; 95 kvm_make_request(KVM_REQ_EVENT, dst_vcpu); 96 kvm_vcpu_kick(dst_vcpu); 97 98 return ret; 99} 100 101/* 102 * hypcall emulation always return to guest, Caller should check retval. 103 */ 104int _kvm_handle_pv_hcall(struct kvm_vcpu *vcpu) 105{ 106 unsigned long func = vcpu->arch.gprs[REG_A0]; 107 int hyp_ret = KVM_RET_NOT_SUPPORTED; 108 109 switch (func) { 110 case KVM_HC_FUNC_FEATURE: 111 hyp_ret = _kvm_pv_feature(vcpu); 112 break; 113 case KVM_HC_FUNC_NOTIFY: 114 hyp_ret = kvm_save_notify(vcpu); 115 break; 116 case KVM_HC_FUNC_IPI: 117 hyp_ret = kvm_virt_ipi(vcpu); 118 break; 119 case KVM_HC_KICK_CPU: 120 hyp_ret = kvm_pv_kick_cpu(vcpu); 121 break; 122 default: 123 kvm_info("[%#lx] hvc func:%#lx unsupported\n", vcpu->arch.pc, func); 124 break; 125 }; 126 127 vcpu->arch.gprs[REG_A0] = hyp_ret; 128 129 return RESUME_GUEST; 130} 131