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 SBI_ERR_INVALID_PARAM; 25 if (!target_vcpu->arch.power_off) 26 return SBI_ERR_ALREADY_AVAILABLE; 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 SBI_ERR_FAILURE; 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 SBI_ERR_INVALID_PARAM; 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 struct kvm_vcpu_sbi_return *retdata) 71 { 72 int ret = 0; 73 struct kvm_cpu_context *cp = &vcpu->arch.guest_context; 74 struct kvm *kvm = vcpu->kvm; 75 unsigned long funcid = cp->a6; 76 77 switch (funcid) { 78 case SBI_EXT_HSM_HART_START: 79 mutex_lock(&kvm->lock); 80 ret = kvm_sbi_hsm_vcpu_start(vcpu); 81 mutex_unlock(&kvm->lock); 82 break; 83 case SBI_EXT_HSM_HART_STOP: 84 ret = kvm_sbi_hsm_vcpu_stop(vcpu); 85 break; 86 case SBI_EXT_HSM_HART_STATUS: 87 ret = kvm_sbi_hsm_vcpu_get_status(vcpu); 88 if (ret >= 0) { 89 retdata->out_val = ret; 90 retdata->err_val = 0; 91 } 92 return 0; 93 case SBI_EXT_HSM_HART_SUSPEND: 94 switch (cp->a0) { 95 case SBI_HSM_SUSPEND_RET_DEFAULT: 96 kvm_riscv_vcpu_wfi(vcpu); 97 break; 98 case SBI_HSM_SUSPEND_NON_RET_DEFAULT: 99 ret = SBI_ERR_NOT_SUPPORTED; 100 break; 101 default: 102 ret = SBI_ERR_INVALID_PARAM; 103 } 104 break; 105 default: 106 ret = SBI_ERR_NOT_SUPPORTED; 107 } 108 109 retdata->err_val = ret; 110 111 return 0; 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