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