1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (c) 2021 Western Digital Corporation or its affiliates. 4 * 5 * Authors: 6 * Atish Patra <atish.patra@wdc.com> 7 */ 8 9 #include <linux/errno.h> 10 #include <linux/err.h> 11 #include <linux/kvm_host.h> 12 #include <asm/csr.h> 13 #include <asm/sbi.h> 14 #include <asm/kvm_vcpu_sbi.h> 15 16 static int kvm_sbi_hsm_vcpu_start(struct kvm_vcpu *vcpu) 17 { 18 struct kvm_cpu_context *reset_cntx; 19 struct kvm_cpu_context *cp = &vcpu->arch.guest_context; 20 struct kvm_vcpu *target_vcpu; 21 unsigned long target_vcpuid = cp->a0; 22 23 target_vcpu = kvm_get_vcpu_by_id(vcpu->kvm, target_vcpuid); 24 if (!target_vcpu) 25 return -EINVAL; 26 if (!target_vcpu->arch.power_off) 27 return -EALREADY; 28 29 reset_cntx = &target_vcpu->arch.guest_reset_context; 30 /* start address */ 31 reset_cntx->sepc = cp->a1; 32 /* target vcpu id to start */ 33 reset_cntx->a0 = target_vcpuid; 34 /* private data passed from kernel */ 35 reset_cntx->a1 = cp->a2; 36 kvm_make_request(KVM_REQ_VCPU_RESET, target_vcpu); 37 38 kvm_riscv_vcpu_power_on(target_vcpu); 39 40 return 0; 41 } 42 43 static int kvm_sbi_hsm_vcpu_stop(struct kvm_vcpu *vcpu) 44 { 45 if (vcpu->arch.power_off) 46 return -EINVAL; 47 48 kvm_riscv_vcpu_power_off(vcpu); 49 50 return 0; 51 } 52 53 static int kvm_sbi_hsm_vcpu_get_status(struct kvm_vcpu *vcpu) 54 { 55 struct kvm_cpu_context *cp = &vcpu->arch.guest_context; 56 unsigned long target_vcpuid = cp->a0; 57 struct kvm_vcpu *target_vcpu; 58 59 target_vcpu = kvm_get_vcpu_by_id(vcpu->kvm, target_vcpuid); 60 if (!target_vcpu) 61 return -EINVAL; 62 if (!target_vcpu->arch.power_off) 63 return SBI_HSM_STATE_STARTED; 64 else if (vcpu->stat.generic.blocking) 65 return SBI_HSM_STATE_SUSPENDED; 66 else 67 return SBI_HSM_STATE_STOPPED; 68 } 69 70 static int kvm_sbi_ext_hsm_handler(struct kvm_vcpu *vcpu, struct kvm_run *run, 71 unsigned long *out_val, 72 struct kvm_cpu_trap *utrap, 73 bool *exit) 74 { 75 int ret = 0; 76 struct kvm_cpu_context *cp = &vcpu->arch.guest_context; 77 struct kvm *kvm = vcpu->kvm; 78 unsigned long funcid = cp->a6; 79 80 switch (funcid) { 81 case SBI_EXT_HSM_HART_START: 82 mutex_lock(&kvm->lock); 83 ret = kvm_sbi_hsm_vcpu_start(vcpu); 84 mutex_unlock(&kvm->lock); 85 break; 86 case SBI_EXT_HSM_HART_STOP: 87 ret = kvm_sbi_hsm_vcpu_stop(vcpu); 88 break; 89 case SBI_EXT_HSM_HART_STATUS: 90 ret = kvm_sbi_hsm_vcpu_get_status(vcpu); 91 if (ret >= 0) { 92 *out_val = ret; 93 ret = 0; 94 } 95 break; 96 case SBI_EXT_HSM_HART_SUSPEND: 97 switch (cp->a0) { 98 case SBI_HSM_SUSPEND_RET_DEFAULT: 99 kvm_riscv_vcpu_wfi(vcpu); 100 break; 101 case SBI_HSM_SUSPEND_NON_RET_DEFAULT: 102 ret = -EOPNOTSUPP; 103 break; 104 default: 105 ret = -EINVAL; 106 } 107 break; 108 default: 109 ret = -EOPNOTSUPP; 110 } 111 112 return ret; 113 } 114 115 const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_hsm = { 116 .extid_start = SBI_EXT_HSM, 117 .extid_end = SBI_EXT_HSM, 118 .handler = kvm_sbi_ext_hsm_handler, 119 }; 120