xref: /kernel/linux/linux-6.6/arch/riscv/kvm/vcpu_sbi.c (revision 62306a36)
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, &reg_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(&reg_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, &reg_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, &reg_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, &reg_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