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 <linux/version.h> 1362306a36Sopenharmony_ci#include <asm/sbi.h> 1462306a36Sopenharmony_ci#include <asm/kvm_vcpu_sbi.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cistatic int kvm_sbi_ext_base_handler(struct kvm_vcpu *vcpu, struct kvm_run *run, 1762306a36Sopenharmony_ci struct kvm_vcpu_sbi_return *retdata) 1862306a36Sopenharmony_ci{ 1962306a36Sopenharmony_ci struct kvm_cpu_context *cp = &vcpu->arch.guest_context; 2062306a36Sopenharmony_ci const struct kvm_vcpu_sbi_extension *sbi_ext; 2162306a36Sopenharmony_ci unsigned long *out_val = &retdata->out_val; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci switch (cp->a6) { 2462306a36Sopenharmony_ci case SBI_EXT_BASE_GET_SPEC_VERSION: 2562306a36Sopenharmony_ci *out_val = (KVM_SBI_VERSION_MAJOR << 2662306a36Sopenharmony_ci SBI_SPEC_VERSION_MAJOR_SHIFT) | 2762306a36Sopenharmony_ci KVM_SBI_VERSION_MINOR; 2862306a36Sopenharmony_ci break; 2962306a36Sopenharmony_ci case SBI_EXT_BASE_GET_IMP_ID: 3062306a36Sopenharmony_ci *out_val = KVM_SBI_IMPID; 3162306a36Sopenharmony_ci break; 3262306a36Sopenharmony_ci case SBI_EXT_BASE_GET_IMP_VERSION: 3362306a36Sopenharmony_ci *out_val = LINUX_VERSION_CODE; 3462306a36Sopenharmony_ci break; 3562306a36Sopenharmony_ci case SBI_EXT_BASE_PROBE_EXT: 3662306a36Sopenharmony_ci if ((cp->a0 >= SBI_EXT_EXPERIMENTAL_START && 3762306a36Sopenharmony_ci cp->a0 <= SBI_EXT_EXPERIMENTAL_END) || 3862306a36Sopenharmony_ci (cp->a0 >= SBI_EXT_VENDOR_START && 3962306a36Sopenharmony_ci cp->a0 <= SBI_EXT_VENDOR_END)) { 4062306a36Sopenharmony_ci /* 4162306a36Sopenharmony_ci * For experimental/vendor extensions 4262306a36Sopenharmony_ci * forward it to the userspace 4362306a36Sopenharmony_ci */ 4462306a36Sopenharmony_ci kvm_riscv_vcpu_sbi_forward(vcpu, run); 4562306a36Sopenharmony_ci retdata->uexit = true; 4662306a36Sopenharmony_ci } else { 4762306a36Sopenharmony_ci sbi_ext = kvm_vcpu_sbi_find_ext(vcpu, cp->a0); 4862306a36Sopenharmony_ci *out_val = sbi_ext && sbi_ext->probe ? 4962306a36Sopenharmony_ci sbi_ext->probe(vcpu) : !!sbi_ext; 5062306a36Sopenharmony_ci } 5162306a36Sopenharmony_ci break; 5262306a36Sopenharmony_ci case SBI_EXT_BASE_GET_MVENDORID: 5362306a36Sopenharmony_ci *out_val = vcpu->arch.mvendorid; 5462306a36Sopenharmony_ci break; 5562306a36Sopenharmony_ci case SBI_EXT_BASE_GET_MARCHID: 5662306a36Sopenharmony_ci *out_val = vcpu->arch.marchid; 5762306a36Sopenharmony_ci break; 5862306a36Sopenharmony_ci case SBI_EXT_BASE_GET_MIMPID: 5962306a36Sopenharmony_ci *out_val = vcpu->arch.mimpid; 6062306a36Sopenharmony_ci break; 6162306a36Sopenharmony_ci default: 6262306a36Sopenharmony_ci retdata->err_val = SBI_ERR_NOT_SUPPORTED; 6362306a36Sopenharmony_ci break; 6462306a36Sopenharmony_ci } 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci return 0; 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ciconst struct kvm_vcpu_sbi_extension vcpu_sbi_ext_base = { 7062306a36Sopenharmony_ci .extid_start = SBI_EXT_BASE, 7162306a36Sopenharmony_ci .extid_end = SBI_EXT_BASE, 7262306a36Sopenharmony_ci .handler = kvm_sbi_ext_base_handler, 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic int kvm_sbi_ext_forward_handler(struct kvm_vcpu *vcpu, 7662306a36Sopenharmony_ci struct kvm_run *run, 7762306a36Sopenharmony_ci struct kvm_vcpu_sbi_return *retdata) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci /* 8062306a36Sopenharmony_ci * Both SBI experimental and vendor extensions are 8162306a36Sopenharmony_ci * unconditionally forwarded to userspace. 8262306a36Sopenharmony_ci */ 8362306a36Sopenharmony_ci kvm_riscv_vcpu_sbi_forward(vcpu, run); 8462306a36Sopenharmony_ci retdata->uexit = true; 8562306a36Sopenharmony_ci return 0; 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ciconst struct kvm_vcpu_sbi_extension vcpu_sbi_ext_experimental = { 8962306a36Sopenharmony_ci .extid_start = SBI_EXT_EXPERIMENTAL_START, 9062306a36Sopenharmony_ci .extid_end = SBI_EXT_EXPERIMENTAL_END, 9162306a36Sopenharmony_ci .handler = kvm_sbi_ext_forward_handler, 9262306a36Sopenharmony_ci}; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ciconst struct kvm_vcpu_sbi_extension vcpu_sbi_ext_vendor = { 9562306a36Sopenharmony_ci .extid_start = SBI_EXT_VENDOR_START, 9662306a36Sopenharmony_ci .extid_end = SBI_EXT_VENDOR_END, 9762306a36Sopenharmony_ci .handler = kvm_sbi_ext_forward_handler, 9862306a36Sopenharmony_ci}; 99