162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2019 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_ci#ifndef CONFIG_RISCV_SBI_V01 1662306a36Sopenharmony_cistatic const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_v01 = { 1762306a36Sopenharmony_ci .extid_start = -1UL, 1862306a36Sopenharmony_ci .extid_end = -1UL, 1962306a36Sopenharmony_ci .handler = NULL, 2062306a36Sopenharmony_ci}; 2162306a36Sopenharmony_ci#endif 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#ifndef CONFIG_RISCV_PMU_SBI 2462306a36Sopenharmony_cistatic const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_pmu = { 2562306a36Sopenharmony_ci .extid_start = -1UL, 2662306a36Sopenharmony_ci .extid_end = -1UL, 2762306a36Sopenharmony_ci .handler = NULL, 2862306a36Sopenharmony_ci}; 2962306a36Sopenharmony_ci#endif 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistruct kvm_riscv_sbi_extension_entry { 3262306a36Sopenharmony_ci enum KVM_RISCV_SBI_EXT_ID ext_idx; 3362306a36Sopenharmony_ci const struct kvm_vcpu_sbi_extension *ext_ptr; 3462306a36Sopenharmony_ci}; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic const struct kvm_riscv_sbi_extension_entry sbi_ext[] = { 3762306a36Sopenharmony_ci { 3862306a36Sopenharmony_ci .ext_idx = KVM_RISCV_SBI_EXT_V01, 3962306a36Sopenharmony_ci .ext_ptr = &vcpu_sbi_ext_v01, 4062306a36Sopenharmony_ci }, 4162306a36Sopenharmony_ci { 4262306a36Sopenharmony_ci .ext_idx = KVM_RISCV_SBI_EXT_MAX, /* Can't be disabled */ 4362306a36Sopenharmony_ci .ext_ptr = &vcpu_sbi_ext_base, 4462306a36Sopenharmony_ci }, 4562306a36Sopenharmony_ci { 4662306a36Sopenharmony_ci .ext_idx = KVM_RISCV_SBI_EXT_TIME, 4762306a36Sopenharmony_ci .ext_ptr = &vcpu_sbi_ext_time, 4862306a36Sopenharmony_ci }, 4962306a36Sopenharmony_ci { 5062306a36Sopenharmony_ci .ext_idx = KVM_RISCV_SBI_EXT_IPI, 5162306a36Sopenharmony_ci .ext_ptr = &vcpu_sbi_ext_ipi, 5262306a36Sopenharmony_ci }, 5362306a36Sopenharmony_ci { 5462306a36Sopenharmony_ci .ext_idx = KVM_RISCV_SBI_EXT_RFENCE, 5562306a36Sopenharmony_ci .ext_ptr = &vcpu_sbi_ext_rfence, 5662306a36Sopenharmony_ci }, 5762306a36Sopenharmony_ci { 5862306a36Sopenharmony_ci .ext_idx = KVM_RISCV_SBI_EXT_SRST, 5962306a36Sopenharmony_ci .ext_ptr = &vcpu_sbi_ext_srst, 6062306a36Sopenharmony_ci }, 6162306a36Sopenharmony_ci { 6262306a36Sopenharmony_ci .ext_idx = KVM_RISCV_SBI_EXT_HSM, 6362306a36Sopenharmony_ci .ext_ptr = &vcpu_sbi_ext_hsm, 6462306a36Sopenharmony_ci }, 6562306a36Sopenharmony_ci { 6662306a36Sopenharmony_ci .ext_idx = KVM_RISCV_SBI_EXT_PMU, 6762306a36Sopenharmony_ci .ext_ptr = &vcpu_sbi_ext_pmu, 6862306a36Sopenharmony_ci }, 6962306a36Sopenharmony_ci { 7062306a36Sopenharmony_ci .ext_idx = KVM_RISCV_SBI_EXT_EXPERIMENTAL, 7162306a36Sopenharmony_ci .ext_ptr = &vcpu_sbi_ext_experimental, 7262306a36Sopenharmony_ci }, 7362306a36Sopenharmony_ci { 7462306a36Sopenharmony_ci .ext_idx = KVM_RISCV_SBI_EXT_VENDOR, 7562306a36Sopenharmony_ci .ext_ptr = &vcpu_sbi_ext_vendor, 7662306a36Sopenharmony_ci }, 7762306a36Sopenharmony_ci}; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_civoid kvm_riscv_vcpu_sbi_forward(struct kvm_vcpu *vcpu, struct kvm_run *run) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci struct kvm_cpu_context *cp = &vcpu->arch.guest_context; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci vcpu->arch.sbi_context.return_handled = 0; 8462306a36Sopenharmony_ci vcpu->stat.ecall_exit_stat++; 8562306a36Sopenharmony_ci run->exit_reason = KVM_EXIT_RISCV_SBI; 8662306a36Sopenharmony_ci run->riscv_sbi.extension_id = cp->a7; 8762306a36Sopenharmony_ci run->riscv_sbi.function_id = cp->a6; 8862306a36Sopenharmony_ci run->riscv_sbi.args[0] = cp->a0; 8962306a36Sopenharmony_ci run->riscv_sbi.args[1] = cp->a1; 9062306a36Sopenharmony_ci run->riscv_sbi.args[2] = cp->a2; 9162306a36Sopenharmony_ci run->riscv_sbi.args[3] = cp->a3; 9262306a36Sopenharmony_ci run->riscv_sbi.args[4] = cp->a4; 9362306a36Sopenharmony_ci run->riscv_sbi.args[5] = cp->a5; 9462306a36Sopenharmony_ci run->riscv_sbi.ret[0] = cp->a0; 9562306a36Sopenharmony_ci run->riscv_sbi.ret[1] = cp->a1; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_civoid kvm_riscv_vcpu_sbi_system_reset(struct kvm_vcpu *vcpu, 9962306a36Sopenharmony_ci struct kvm_run *run, 10062306a36Sopenharmony_ci u32 type, u64 reason) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci unsigned long i; 10362306a36Sopenharmony_ci struct kvm_vcpu *tmp; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci kvm_for_each_vcpu(i, tmp, vcpu->kvm) 10662306a36Sopenharmony_ci tmp->arch.power_off = true; 10762306a36Sopenharmony_ci kvm_make_all_cpus_request(vcpu->kvm, KVM_REQ_SLEEP); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci memset(&run->system_event, 0, sizeof(run->system_event)); 11062306a36Sopenharmony_ci run->system_event.type = type; 11162306a36Sopenharmony_ci run->system_event.ndata = 1; 11262306a36Sopenharmony_ci run->system_event.data[0] = reason; 11362306a36Sopenharmony_ci run->exit_reason = KVM_EXIT_SYSTEM_EVENT; 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ciint kvm_riscv_vcpu_sbi_return(struct kvm_vcpu *vcpu, struct kvm_run *run) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci struct kvm_cpu_context *cp = &vcpu->arch.guest_context; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci /* Handle SBI return only once */ 12162306a36Sopenharmony_ci if (vcpu->arch.sbi_context.return_handled) 12262306a36Sopenharmony_ci return 0; 12362306a36Sopenharmony_ci vcpu->arch.sbi_context.return_handled = 1; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci /* Update return values */ 12662306a36Sopenharmony_ci cp->a0 = run->riscv_sbi.ret[0]; 12762306a36Sopenharmony_ci cp->a1 = run->riscv_sbi.ret[1]; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci /* Move to next instruction */ 13062306a36Sopenharmony_ci vcpu->arch.guest_context.sepc += 4; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci return 0; 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic int riscv_vcpu_set_sbi_ext_single(struct kvm_vcpu *vcpu, 13662306a36Sopenharmony_ci unsigned long reg_num, 13762306a36Sopenharmony_ci unsigned long reg_val) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci unsigned long i; 14062306a36Sopenharmony_ci const struct kvm_riscv_sbi_extension_entry *sext = NULL; 14162306a36Sopenharmony_ci struct kvm_vcpu_sbi_context *scontext = &vcpu->arch.sbi_context; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (reg_num >= KVM_RISCV_SBI_EXT_MAX) 14462306a36Sopenharmony_ci return -ENOENT; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci if (reg_val != 1 && reg_val != 0) 14762306a36Sopenharmony_ci return -EINVAL; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(sbi_ext); i++) { 15062306a36Sopenharmony_ci if (sbi_ext[i].ext_idx == reg_num) { 15162306a36Sopenharmony_ci sext = &sbi_ext[i]; 15262306a36Sopenharmony_ci break; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci if (!sext) 15662306a36Sopenharmony_ci return -ENOENT; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci /* 15962306a36Sopenharmony_ci * We can't set the extension status to available here, since it may 16062306a36Sopenharmony_ci * have a probe() function which needs to confirm availability first, 16162306a36Sopenharmony_ci * but it may be too early to call that here. We can set the status to 16262306a36Sopenharmony_ci * unavailable, though. 16362306a36Sopenharmony_ci */ 16462306a36Sopenharmony_ci if (!reg_val) 16562306a36Sopenharmony_ci scontext->ext_status[sext->ext_idx] = 16662306a36Sopenharmony_ci KVM_RISCV_SBI_EXT_UNAVAILABLE; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci return 0; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic int riscv_vcpu_get_sbi_ext_single(struct kvm_vcpu *vcpu, 17262306a36Sopenharmony_ci unsigned long reg_num, 17362306a36Sopenharmony_ci unsigned long *reg_val) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci unsigned long i; 17662306a36Sopenharmony_ci const struct kvm_riscv_sbi_extension_entry *sext = NULL; 17762306a36Sopenharmony_ci struct kvm_vcpu_sbi_context *scontext = &vcpu->arch.sbi_context; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (reg_num >= KVM_RISCV_SBI_EXT_MAX) 18062306a36Sopenharmony_ci return -ENOENT; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(sbi_ext); i++) { 18362306a36Sopenharmony_ci if (sbi_ext[i].ext_idx == reg_num) { 18462306a36Sopenharmony_ci sext = &sbi_ext[i]; 18562306a36Sopenharmony_ci break; 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci if (!sext) 18962306a36Sopenharmony_ci return -ENOENT; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci /* 19262306a36Sopenharmony_ci * If the extension status is still uninitialized, then we should probe 19362306a36Sopenharmony_ci * to determine if it's available, but it may be too early to do that 19462306a36Sopenharmony_ci * here. The best we can do is report that the extension has not been 19562306a36Sopenharmony_ci * disabled, i.e. we return 1 when the extension is available and also 19662306a36Sopenharmony_ci * when it only may be available. 19762306a36Sopenharmony_ci */ 19862306a36Sopenharmony_ci *reg_val = scontext->ext_status[sext->ext_idx] != 19962306a36Sopenharmony_ci KVM_RISCV_SBI_EXT_UNAVAILABLE; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci return 0; 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic int riscv_vcpu_set_sbi_ext_multi(struct kvm_vcpu *vcpu, 20562306a36Sopenharmony_ci unsigned long reg_num, 20662306a36Sopenharmony_ci unsigned long reg_val, bool enable) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci unsigned long i, ext_id; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci if (reg_num > KVM_REG_RISCV_SBI_MULTI_REG_LAST) 21162306a36Sopenharmony_ci return -ENOENT; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci for_each_set_bit(i, ®_val, BITS_PER_LONG) { 21462306a36Sopenharmony_ci ext_id = i + reg_num * BITS_PER_LONG; 21562306a36Sopenharmony_ci if (ext_id >= KVM_RISCV_SBI_EXT_MAX) 21662306a36Sopenharmony_ci break; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci riscv_vcpu_set_sbi_ext_single(vcpu, ext_id, enable); 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci return 0; 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic int riscv_vcpu_get_sbi_ext_multi(struct kvm_vcpu *vcpu, 22562306a36Sopenharmony_ci unsigned long reg_num, 22662306a36Sopenharmony_ci unsigned long *reg_val) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci unsigned long i, ext_id, ext_val; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci if (reg_num > KVM_REG_RISCV_SBI_MULTI_REG_LAST) 23162306a36Sopenharmony_ci return -ENOENT; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci for (i = 0; i < BITS_PER_LONG; i++) { 23462306a36Sopenharmony_ci ext_id = i + reg_num * BITS_PER_LONG; 23562306a36Sopenharmony_ci if (ext_id >= KVM_RISCV_SBI_EXT_MAX) 23662306a36Sopenharmony_ci break; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci ext_val = 0; 23962306a36Sopenharmony_ci riscv_vcpu_get_sbi_ext_single(vcpu, ext_id, &ext_val); 24062306a36Sopenharmony_ci if (ext_val) 24162306a36Sopenharmony_ci *reg_val |= KVM_REG_RISCV_SBI_MULTI_MASK(ext_id); 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci return 0; 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ciint kvm_riscv_vcpu_set_reg_sbi_ext(struct kvm_vcpu *vcpu, 24862306a36Sopenharmony_ci const struct kvm_one_reg *reg) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci unsigned long __user *uaddr = 25162306a36Sopenharmony_ci (unsigned long __user *)(unsigned long)reg->addr; 25262306a36Sopenharmony_ci unsigned long reg_num = reg->id & ~(KVM_REG_ARCH_MASK | 25362306a36Sopenharmony_ci KVM_REG_SIZE_MASK | 25462306a36Sopenharmony_ci KVM_REG_RISCV_SBI_EXT); 25562306a36Sopenharmony_ci unsigned long reg_val, reg_subtype; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci if (KVM_REG_SIZE(reg->id) != sizeof(unsigned long)) 25862306a36Sopenharmony_ci return -EINVAL; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci if (vcpu->arch.ran_atleast_once) 26162306a36Sopenharmony_ci return -EBUSY; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci reg_subtype = reg_num & KVM_REG_RISCV_SUBTYPE_MASK; 26462306a36Sopenharmony_ci reg_num &= ~KVM_REG_RISCV_SUBTYPE_MASK; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci if (copy_from_user(®_val, uaddr, KVM_REG_SIZE(reg->id))) 26762306a36Sopenharmony_ci return -EFAULT; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci switch (reg_subtype) { 27062306a36Sopenharmony_ci case KVM_REG_RISCV_SBI_SINGLE: 27162306a36Sopenharmony_ci return riscv_vcpu_set_sbi_ext_single(vcpu, reg_num, reg_val); 27262306a36Sopenharmony_ci case KVM_REG_RISCV_SBI_MULTI_EN: 27362306a36Sopenharmony_ci return riscv_vcpu_set_sbi_ext_multi(vcpu, reg_num, reg_val, true); 27462306a36Sopenharmony_ci case KVM_REG_RISCV_SBI_MULTI_DIS: 27562306a36Sopenharmony_ci return riscv_vcpu_set_sbi_ext_multi(vcpu, reg_num, reg_val, false); 27662306a36Sopenharmony_ci default: 27762306a36Sopenharmony_ci return -ENOENT; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci return 0; 28162306a36Sopenharmony_ci} 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ciint kvm_riscv_vcpu_get_reg_sbi_ext(struct kvm_vcpu *vcpu, 28462306a36Sopenharmony_ci const struct kvm_one_reg *reg) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci int rc; 28762306a36Sopenharmony_ci unsigned long __user *uaddr = 28862306a36Sopenharmony_ci (unsigned long __user *)(unsigned long)reg->addr; 28962306a36Sopenharmony_ci unsigned long reg_num = reg->id & ~(KVM_REG_ARCH_MASK | 29062306a36Sopenharmony_ci KVM_REG_SIZE_MASK | 29162306a36Sopenharmony_ci KVM_REG_RISCV_SBI_EXT); 29262306a36Sopenharmony_ci unsigned long reg_val, reg_subtype; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci if (KVM_REG_SIZE(reg->id) != sizeof(unsigned long)) 29562306a36Sopenharmony_ci return -EINVAL; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci reg_subtype = reg_num & KVM_REG_RISCV_SUBTYPE_MASK; 29862306a36Sopenharmony_ci reg_num &= ~KVM_REG_RISCV_SUBTYPE_MASK; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci reg_val = 0; 30162306a36Sopenharmony_ci switch (reg_subtype) { 30262306a36Sopenharmony_ci case KVM_REG_RISCV_SBI_SINGLE: 30362306a36Sopenharmony_ci rc = riscv_vcpu_get_sbi_ext_single(vcpu, reg_num, ®_val); 30462306a36Sopenharmony_ci break; 30562306a36Sopenharmony_ci case KVM_REG_RISCV_SBI_MULTI_EN: 30662306a36Sopenharmony_ci case KVM_REG_RISCV_SBI_MULTI_DIS: 30762306a36Sopenharmony_ci rc = riscv_vcpu_get_sbi_ext_multi(vcpu, reg_num, ®_val); 30862306a36Sopenharmony_ci if (!rc && reg_subtype == KVM_REG_RISCV_SBI_MULTI_DIS) 30962306a36Sopenharmony_ci reg_val = ~reg_val; 31062306a36Sopenharmony_ci break; 31162306a36Sopenharmony_ci default: 31262306a36Sopenharmony_ci rc = -ENOENT; 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci if (rc) 31562306a36Sopenharmony_ci return rc; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci if (copy_to_user(uaddr, ®_val, KVM_REG_SIZE(reg->id))) 31862306a36Sopenharmony_ci return -EFAULT; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci return 0; 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ciconst struct kvm_vcpu_sbi_extension *kvm_vcpu_sbi_find_ext( 32462306a36Sopenharmony_ci struct kvm_vcpu *vcpu, unsigned long extid) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci struct kvm_vcpu_sbi_context *scontext = &vcpu->arch.sbi_context; 32762306a36Sopenharmony_ci const struct kvm_riscv_sbi_extension_entry *entry; 32862306a36Sopenharmony_ci const struct kvm_vcpu_sbi_extension *ext; 32962306a36Sopenharmony_ci int i; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(sbi_ext); i++) { 33262306a36Sopenharmony_ci entry = &sbi_ext[i]; 33362306a36Sopenharmony_ci ext = entry->ext_ptr; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci if (ext->extid_start <= extid && ext->extid_end >= extid) { 33662306a36Sopenharmony_ci if (entry->ext_idx >= KVM_RISCV_SBI_EXT_MAX || 33762306a36Sopenharmony_ci scontext->ext_status[entry->ext_idx] == 33862306a36Sopenharmony_ci KVM_RISCV_SBI_EXT_AVAILABLE) 33962306a36Sopenharmony_ci return ext; 34062306a36Sopenharmony_ci if (scontext->ext_status[entry->ext_idx] == 34162306a36Sopenharmony_ci KVM_RISCV_SBI_EXT_UNAVAILABLE) 34262306a36Sopenharmony_ci return NULL; 34362306a36Sopenharmony_ci if (ext->probe && !ext->probe(vcpu)) { 34462306a36Sopenharmony_ci scontext->ext_status[entry->ext_idx] = 34562306a36Sopenharmony_ci KVM_RISCV_SBI_EXT_UNAVAILABLE; 34662306a36Sopenharmony_ci return NULL; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci scontext->ext_status[entry->ext_idx] = 35062306a36Sopenharmony_ci KVM_RISCV_SBI_EXT_AVAILABLE; 35162306a36Sopenharmony_ci return ext; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci return NULL; 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ciint kvm_riscv_vcpu_sbi_ecall(struct kvm_vcpu *vcpu, struct kvm_run *run) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci int ret = 1; 36162306a36Sopenharmony_ci bool next_sepc = true; 36262306a36Sopenharmony_ci struct kvm_cpu_context *cp = &vcpu->arch.guest_context; 36362306a36Sopenharmony_ci const struct kvm_vcpu_sbi_extension *sbi_ext; 36462306a36Sopenharmony_ci struct kvm_cpu_trap utrap = {0}; 36562306a36Sopenharmony_ci struct kvm_vcpu_sbi_return sbi_ret = { 36662306a36Sopenharmony_ci .out_val = 0, 36762306a36Sopenharmony_ci .err_val = 0, 36862306a36Sopenharmony_ci .utrap = &utrap, 36962306a36Sopenharmony_ci }; 37062306a36Sopenharmony_ci bool ext_is_v01 = false; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci sbi_ext = kvm_vcpu_sbi_find_ext(vcpu, cp->a7); 37362306a36Sopenharmony_ci if (sbi_ext && sbi_ext->handler) { 37462306a36Sopenharmony_ci#ifdef CONFIG_RISCV_SBI_V01 37562306a36Sopenharmony_ci if (cp->a7 >= SBI_EXT_0_1_SET_TIMER && 37662306a36Sopenharmony_ci cp->a7 <= SBI_EXT_0_1_SHUTDOWN) 37762306a36Sopenharmony_ci ext_is_v01 = true; 37862306a36Sopenharmony_ci#endif 37962306a36Sopenharmony_ci ret = sbi_ext->handler(vcpu, run, &sbi_ret); 38062306a36Sopenharmony_ci } else { 38162306a36Sopenharmony_ci /* Return error for unsupported SBI calls */ 38262306a36Sopenharmony_ci cp->a0 = SBI_ERR_NOT_SUPPORTED; 38362306a36Sopenharmony_ci goto ecall_done; 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci /* 38762306a36Sopenharmony_ci * When the SBI extension returns a Linux error code, it exits the ioctl 38862306a36Sopenharmony_ci * loop and forwards the error to userspace. 38962306a36Sopenharmony_ci */ 39062306a36Sopenharmony_ci if (ret < 0) { 39162306a36Sopenharmony_ci next_sepc = false; 39262306a36Sopenharmony_ci goto ecall_done; 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci /* Handle special error cases i.e trap, exit or userspace forward */ 39662306a36Sopenharmony_ci if (sbi_ret.utrap->scause) { 39762306a36Sopenharmony_ci /* No need to increment sepc or exit ioctl loop */ 39862306a36Sopenharmony_ci ret = 1; 39962306a36Sopenharmony_ci sbi_ret.utrap->sepc = cp->sepc; 40062306a36Sopenharmony_ci kvm_riscv_vcpu_trap_redirect(vcpu, sbi_ret.utrap); 40162306a36Sopenharmony_ci next_sepc = false; 40262306a36Sopenharmony_ci goto ecall_done; 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci /* Exit ioctl loop or Propagate the error code the guest */ 40662306a36Sopenharmony_ci if (sbi_ret.uexit) { 40762306a36Sopenharmony_ci next_sepc = false; 40862306a36Sopenharmony_ci ret = 0; 40962306a36Sopenharmony_ci } else { 41062306a36Sopenharmony_ci cp->a0 = sbi_ret.err_val; 41162306a36Sopenharmony_ci ret = 1; 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ciecall_done: 41462306a36Sopenharmony_ci if (next_sepc) 41562306a36Sopenharmony_ci cp->sepc += 4; 41662306a36Sopenharmony_ci /* a1 should only be updated when we continue the ioctl loop */ 41762306a36Sopenharmony_ci if (!ext_is_v01 && ret == 1) 41862306a36Sopenharmony_ci cp->a1 = sbi_ret.out_val; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci return ret; 42162306a36Sopenharmony_ci} 422