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 15static 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 42static 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 52static 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 69static 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 114const 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