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_timer.h>
1462306a36Sopenharmony_ci#include <asm/kvm_vcpu_sbi.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_cistatic int kvm_sbi_ext_v01_handler(struct kvm_vcpu *vcpu, struct kvm_run *run,
1762306a36Sopenharmony_ci				   struct kvm_vcpu_sbi_return *retdata)
1862306a36Sopenharmony_ci{
1962306a36Sopenharmony_ci	ulong hmask;
2062306a36Sopenharmony_ci	int i, ret = 0;
2162306a36Sopenharmony_ci	u64 next_cycle;
2262306a36Sopenharmony_ci	struct kvm_vcpu *rvcpu;
2362306a36Sopenharmony_ci	struct kvm *kvm = vcpu->kvm;
2462306a36Sopenharmony_ci	struct kvm_cpu_context *cp = &vcpu->arch.guest_context;
2562306a36Sopenharmony_ci	struct kvm_cpu_trap *utrap = retdata->utrap;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	switch (cp->a7) {
2862306a36Sopenharmony_ci	case SBI_EXT_0_1_CONSOLE_GETCHAR:
2962306a36Sopenharmony_ci	case SBI_EXT_0_1_CONSOLE_PUTCHAR:
3062306a36Sopenharmony_ci		/*
3162306a36Sopenharmony_ci		 * The CONSOLE_GETCHAR/CONSOLE_PUTCHAR SBI calls cannot be
3262306a36Sopenharmony_ci		 * handled in kernel so we forward these to user-space
3362306a36Sopenharmony_ci		 */
3462306a36Sopenharmony_ci		kvm_riscv_vcpu_sbi_forward(vcpu, run);
3562306a36Sopenharmony_ci		retdata->uexit = true;
3662306a36Sopenharmony_ci		break;
3762306a36Sopenharmony_ci	case SBI_EXT_0_1_SET_TIMER:
3862306a36Sopenharmony_ci#if __riscv_xlen == 32
3962306a36Sopenharmony_ci		next_cycle = ((u64)cp->a1 << 32) | (u64)cp->a0;
4062306a36Sopenharmony_ci#else
4162306a36Sopenharmony_ci		next_cycle = (u64)cp->a0;
4262306a36Sopenharmony_ci#endif
4362306a36Sopenharmony_ci		ret = kvm_riscv_vcpu_timer_next_event(vcpu, next_cycle);
4462306a36Sopenharmony_ci		break;
4562306a36Sopenharmony_ci	case SBI_EXT_0_1_CLEAR_IPI:
4662306a36Sopenharmony_ci		ret = kvm_riscv_vcpu_unset_interrupt(vcpu, IRQ_VS_SOFT);
4762306a36Sopenharmony_ci		break;
4862306a36Sopenharmony_ci	case SBI_EXT_0_1_SEND_IPI:
4962306a36Sopenharmony_ci		if (cp->a0)
5062306a36Sopenharmony_ci			hmask = kvm_riscv_vcpu_unpriv_read(vcpu, false, cp->a0, utrap);
5162306a36Sopenharmony_ci		else
5262306a36Sopenharmony_ci			hmask = (1UL << atomic_read(&kvm->online_vcpus)) - 1;
5362306a36Sopenharmony_ci		if (utrap->scause)
5462306a36Sopenharmony_ci			break;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci		for_each_set_bit(i, &hmask, BITS_PER_LONG) {
5762306a36Sopenharmony_ci			rvcpu = kvm_get_vcpu_by_id(vcpu->kvm, i);
5862306a36Sopenharmony_ci			ret = kvm_riscv_vcpu_set_interrupt(rvcpu, IRQ_VS_SOFT);
5962306a36Sopenharmony_ci			if (ret < 0)
6062306a36Sopenharmony_ci				break;
6162306a36Sopenharmony_ci		}
6262306a36Sopenharmony_ci		break;
6362306a36Sopenharmony_ci	case SBI_EXT_0_1_SHUTDOWN:
6462306a36Sopenharmony_ci		kvm_riscv_vcpu_sbi_system_reset(vcpu, run,
6562306a36Sopenharmony_ci						KVM_SYSTEM_EVENT_SHUTDOWN, 0);
6662306a36Sopenharmony_ci		retdata->uexit = true;
6762306a36Sopenharmony_ci		break;
6862306a36Sopenharmony_ci	case SBI_EXT_0_1_REMOTE_FENCE_I:
6962306a36Sopenharmony_ci	case SBI_EXT_0_1_REMOTE_SFENCE_VMA:
7062306a36Sopenharmony_ci	case SBI_EXT_0_1_REMOTE_SFENCE_VMA_ASID:
7162306a36Sopenharmony_ci		if (cp->a0)
7262306a36Sopenharmony_ci			hmask = kvm_riscv_vcpu_unpriv_read(vcpu, false, cp->a0, utrap);
7362306a36Sopenharmony_ci		else
7462306a36Sopenharmony_ci			hmask = (1UL << atomic_read(&kvm->online_vcpus)) - 1;
7562306a36Sopenharmony_ci		if (utrap->scause)
7662306a36Sopenharmony_ci			break;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci		if (cp->a7 == SBI_EXT_0_1_REMOTE_FENCE_I)
7962306a36Sopenharmony_ci			kvm_riscv_fence_i(vcpu->kvm, 0, hmask);
8062306a36Sopenharmony_ci		else if (cp->a7 == SBI_EXT_0_1_REMOTE_SFENCE_VMA) {
8162306a36Sopenharmony_ci			if (cp->a1 == 0 && cp->a2 == 0)
8262306a36Sopenharmony_ci				kvm_riscv_hfence_vvma_all(vcpu->kvm,
8362306a36Sopenharmony_ci							  0, hmask);
8462306a36Sopenharmony_ci			else
8562306a36Sopenharmony_ci				kvm_riscv_hfence_vvma_gva(vcpu->kvm,
8662306a36Sopenharmony_ci							  0, hmask,
8762306a36Sopenharmony_ci							  cp->a1, cp->a2,
8862306a36Sopenharmony_ci							  PAGE_SHIFT);
8962306a36Sopenharmony_ci		} else {
9062306a36Sopenharmony_ci			if (cp->a1 == 0 && cp->a2 == 0)
9162306a36Sopenharmony_ci				kvm_riscv_hfence_vvma_asid_all(vcpu->kvm,
9262306a36Sopenharmony_ci							       0, hmask,
9362306a36Sopenharmony_ci							       cp->a3);
9462306a36Sopenharmony_ci			else
9562306a36Sopenharmony_ci				kvm_riscv_hfence_vvma_asid_gva(vcpu->kvm,
9662306a36Sopenharmony_ci							       0, hmask,
9762306a36Sopenharmony_ci							       cp->a1, cp->a2,
9862306a36Sopenharmony_ci							       PAGE_SHIFT,
9962306a36Sopenharmony_ci							       cp->a3);
10062306a36Sopenharmony_ci		}
10162306a36Sopenharmony_ci		break;
10262306a36Sopenharmony_ci	default:
10362306a36Sopenharmony_ci		retdata->err_val = SBI_ERR_NOT_SUPPORTED;
10462306a36Sopenharmony_ci		break;
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	return ret;
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ciconst struct kvm_vcpu_sbi_extension vcpu_sbi_ext_v01 = {
11162306a36Sopenharmony_ci	.extid_start = SBI_EXT_0_1_SET_TIMER,
11262306a36Sopenharmony_ci	.extid_end = SBI_EXT_0_1_SHUTDOWN,
11362306a36Sopenharmony_ci	.handler = kvm_sbi_ext_v01_handler,
11462306a36Sopenharmony_ci};
115