162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2021 Western Digital Corporation or its affiliates. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Authors: 662306a36Sopenharmony_ci * Atish Patra <atish.patra@wdc.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/errno.h> 1062306a36Sopenharmony_ci#include <linux/err.h> 1162306a36Sopenharmony_ci#include <linux/kvm_host.h> 1262306a36Sopenharmony_ci#include <asm/sbi.h> 1362306a36Sopenharmony_ci#include <asm/kvm_vcpu_sbi.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_cistatic int kvm_sbi_hsm_vcpu_start(struct kvm_vcpu *vcpu) 1662306a36Sopenharmony_ci{ 1762306a36Sopenharmony_ci struct kvm_cpu_context *reset_cntx; 1862306a36Sopenharmony_ci struct kvm_cpu_context *cp = &vcpu->arch.guest_context; 1962306a36Sopenharmony_ci struct kvm_vcpu *target_vcpu; 2062306a36Sopenharmony_ci unsigned long target_vcpuid = cp->a0; 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci target_vcpu = kvm_get_vcpu_by_id(vcpu->kvm, target_vcpuid); 2362306a36Sopenharmony_ci if (!target_vcpu) 2462306a36Sopenharmony_ci return SBI_ERR_INVALID_PARAM; 2562306a36Sopenharmony_ci if (!target_vcpu->arch.power_off) 2662306a36Sopenharmony_ci return SBI_ERR_ALREADY_AVAILABLE; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci reset_cntx = &target_vcpu->arch.guest_reset_context; 2962306a36Sopenharmony_ci /* start address */ 3062306a36Sopenharmony_ci reset_cntx->sepc = cp->a1; 3162306a36Sopenharmony_ci /* target vcpu id to start */ 3262306a36Sopenharmony_ci reset_cntx->a0 = target_vcpuid; 3362306a36Sopenharmony_ci /* private data passed from kernel */ 3462306a36Sopenharmony_ci reset_cntx->a1 = cp->a2; 3562306a36Sopenharmony_ci kvm_make_request(KVM_REQ_VCPU_RESET, target_vcpu); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci kvm_riscv_vcpu_power_on(target_vcpu); 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci return 0; 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic int kvm_sbi_hsm_vcpu_stop(struct kvm_vcpu *vcpu) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci if (vcpu->arch.power_off) 4562306a36Sopenharmony_ci return SBI_ERR_FAILURE; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci kvm_riscv_vcpu_power_off(vcpu); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci return 0; 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic int kvm_sbi_hsm_vcpu_get_status(struct kvm_vcpu *vcpu) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci struct kvm_cpu_context *cp = &vcpu->arch.guest_context; 5562306a36Sopenharmony_ci unsigned long target_vcpuid = cp->a0; 5662306a36Sopenharmony_ci struct kvm_vcpu *target_vcpu; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci target_vcpu = kvm_get_vcpu_by_id(vcpu->kvm, target_vcpuid); 5962306a36Sopenharmony_ci if (!target_vcpu) 6062306a36Sopenharmony_ci return SBI_ERR_INVALID_PARAM; 6162306a36Sopenharmony_ci if (!target_vcpu->arch.power_off) 6262306a36Sopenharmony_ci return SBI_HSM_STATE_STARTED; 6362306a36Sopenharmony_ci else if (vcpu->stat.generic.blocking) 6462306a36Sopenharmony_ci return SBI_HSM_STATE_SUSPENDED; 6562306a36Sopenharmony_ci else 6662306a36Sopenharmony_ci return SBI_HSM_STATE_STOPPED; 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic int kvm_sbi_ext_hsm_handler(struct kvm_vcpu *vcpu, struct kvm_run *run, 7062306a36Sopenharmony_ci struct kvm_vcpu_sbi_return *retdata) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci int ret = 0; 7362306a36Sopenharmony_ci struct kvm_cpu_context *cp = &vcpu->arch.guest_context; 7462306a36Sopenharmony_ci struct kvm *kvm = vcpu->kvm; 7562306a36Sopenharmony_ci unsigned long funcid = cp->a6; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci switch (funcid) { 7862306a36Sopenharmony_ci case SBI_EXT_HSM_HART_START: 7962306a36Sopenharmony_ci mutex_lock(&kvm->lock); 8062306a36Sopenharmony_ci ret = kvm_sbi_hsm_vcpu_start(vcpu); 8162306a36Sopenharmony_ci mutex_unlock(&kvm->lock); 8262306a36Sopenharmony_ci break; 8362306a36Sopenharmony_ci case SBI_EXT_HSM_HART_STOP: 8462306a36Sopenharmony_ci ret = kvm_sbi_hsm_vcpu_stop(vcpu); 8562306a36Sopenharmony_ci break; 8662306a36Sopenharmony_ci case SBI_EXT_HSM_HART_STATUS: 8762306a36Sopenharmony_ci ret = kvm_sbi_hsm_vcpu_get_status(vcpu); 8862306a36Sopenharmony_ci if (ret >= 0) { 8962306a36Sopenharmony_ci retdata->out_val = ret; 9062306a36Sopenharmony_ci retdata->err_val = 0; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci return 0; 9362306a36Sopenharmony_ci case SBI_EXT_HSM_HART_SUSPEND: 9462306a36Sopenharmony_ci switch (cp->a0) { 9562306a36Sopenharmony_ci case SBI_HSM_SUSPEND_RET_DEFAULT: 9662306a36Sopenharmony_ci kvm_riscv_vcpu_wfi(vcpu); 9762306a36Sopenharmony_ci break; 9862306a36Sopenharmony_ci case SBI_HSM_SUSPEND_NON_RET_DEFAULT: 9962306a36Sopenharmony_ci ret = SBI_ERR_NOT_SUPPORTED; 10062306a36Sopenharmony_ci break; 10162306a36Sopenharmony_ci default: 10262306a36Sopenharmony_ci ret = SBI_ERR_INVALID_PARAM; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci break; 10562306a36Sopenharmony_ci default: 10662306a36Sopenharmony_ci ret = SBI_ERR_NOT_SUPPORTED; 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci retdata->err_val = ret; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci return 0; 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ciconst struct kvm_vcpu_sbi_extension vcpu_sbi_ext_hsm = { 11562306a36Sopenharmony_ci .extid_start = SBI_EXT_HSM, 11662306a36Sopenharmony_ci .extid_end = SBI_EXT_HSM, 11762306a36Sopenharmony_ci .handler = kvm_sbi_ext_hsm_handler, 11862306a36Sopenharmony_ci}; 119