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_pmu.h>
1562306a36Sopenharmony_ci#include <asm/kvm_vcpu_sbi.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistatic int kvm_sbi_ext_time_handler(struct kvm_vcpu *vcpu, struct kvm_run *run,
1862306a36Sopenharmony_ci				    struct kvm_vcpu_sbi_return *retdata)
1962306a36Sopenharmony_ci{
2062306a36Sopenharmony_ci	struct kvm_cpu_context *cp = &vcpu->arch.guest_context;
2162306a36Sopenharmony_ci	u64 next_cycle;
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci	if (cp->a6 != SBI_EXT_TIME_SET_TIMER) {
2462306a36Sopenharmony_ci		retdata->err_val = SBI_ERR_INVALID_PARAM;
2562306a36Sopenharmony_ci		return 0;
2662306a36Sopenharmony_ci	}
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	kvm_riscv_vcpu_pmu_incr_fw(vcpu, SBI_PMU_FW_SET_TIMER);
2962306a36Sopenharmony_ci#if __riscv_xlen == 32
3062306a36Sopenharmony_ci	next_cycle = ((u64)cp->a1 << 32) | (u64)cp->a0;
3162306a36Sopenharmony_ci#else
3262306a36Sopenharmony_ci	next_cycle = (u64)cp->a0;
3362306a36Sopenharmony_ci#endif
3462306a36Sopenharmony_ci	kvm_riscv_vcpu_timer_next_event(vcpu, next_cycle);
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	return 0;
3762306a36Sopenharmony_ci}
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ciconst struct kvm_vcpu_sbi_extension vcpu_sbi_ext_time = {
4062306a36Sopenharmony_ci	.extid_start = SBI_EXT_TIME,
4162306a36Sopenharmony_ci	.extid_end = SBI_EXT_TIME,
4262306a36Sopenharmony_ci	.handler = kvm_sbi_ext_time_handler,
4362306a36Sopenharmony_ci};
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic int kvm_sbi_ext_ipi_handler(struct kvm_vcpu *vcpu, struct kvm_run *run,
4662306a36Sopenharmony_ci				   struct kvm_vcpu_sbi_return *retdata)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	int ret = 0;
4962306a36Sopenharmony_ci	unsigned long i;
5062306a36Sopenharmony_ci	struct kvm_vcpu *tmp;
5162306a36Sopenharmony_ci	struct kvm_cpu_context *cp = &vcpu->arch.guest_context;
5262306a36Sopenharmony_ci	unsigned long hmask = cp->a0;
5362306a36Sopenharmony_ci	unsigned long hbase = cp->a1;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	if (cp->a6 != SBI_EXT_IPI_SEND_IPI) {
5662306a36Sopenharmony_ci		retdata->err_val = SBI_ERR_INVALID_PARAM;
5762306a36Sopenharmony_ci		return 0;
5862306a36Sopenharmony_ci	}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	kvm_riscv_vcpu_pmu_incr_fw(vcpu, SBI_PMU_FW_IPI_SENT);
6162306a36Sopenharmony_ci	kvm_for_each_vcpu(i, tmp, vcpu->kvm) {
6262306a36Sopenharmony_ci		if (hbase != -1UL) {
6362306a36Sopenharmony_ci			if (tmp->vcpu_id < hbase)
6462306a36Sopenharmony_ci				continue;
6562306a36Sopenharmony_ci			if (!(hmask & (1UL << (tmp->vcpu_id - hbase))))
6662306a36Sopenharmony_ci				continue;
6762306a36Sopenharmony_ci		}
6862306a36Sopenharmony_ci		ret = kvm_riscv_vcpu_set_interrupt(tmp, IRQ_VS_SOFT);
6962306a36Sopenharmony_ci		if (ret < 0)
7062306a36Sopenharmony_ci			break;
7162306a36Sopenharmony_ci		kvm_riscv_vcpu_pmu_incr_fw(tmp, SBI_PMU_FW_IPI_RCVD);
7262306a36Sopenharmony_ci	}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	return ret;
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ciconst struct kvm_vcpu_sbi_extension vcpu_sbi_ext_ipi = {
7862306a36Sopenharmony_ci	.extid_start = SBI_EXT_IPI,
7962306a36Sopenharmony_ci	.extid_end = SBI_EXT_IPI,
8062306a36Sopenharmony_ci	.handler = kvm_sbi_ext_ipi_handler,
8162306a36Sopenharmony_ci};
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic int kvm_sbi_ext_rfence_handler(struct kvm_vcpu *vcpu, struct kvm_run *run,
8462306a36Sopenharmony_ci				      struct kvm_vcpu_sbi_return *retdata)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	struct kvm_cpu_context *cp = &vcpu->arch.guest_context;
8762306a36Sopenharmony_ci	unsigned long hmask = cp->a0;
8862306a36Sopenharmony_ci	unsigned long hbase = cp->a1;
8962306a36Sopenharmony_ci	unsigned long funcid = cp->a6;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	switch (funcid) {
9262306a36Sopenharmony_ci	case SBI_EXT_RFENCE_REMOTE_FENCE_I:
9362306a36Sopenharmony_ci		kvm_riscv_fence_i(vcpu->kvm, hbase, hmask);
9462306a36Sopenharmony_ci		kvm_riscv_vcpu_pmu_incr_fw(vcpu, SBI_PMU_FW_FENCE_I_SENT);
9562306a36Sopenharmony_ci		break;
9662306a36Sopenharmony_ci	case SBI_EXT_RFENCE_REMOTE_SFENCE_VMA:
9762306a36Sopenharmony_ci		if (cp->a2 == 0 && cp->a3 == 0)
9862306a36Sopenharmony_ci			kvm_riscv_hfence_vvma_all(vcpu->kvm, hbase, hmask);
9962306a36Sopenharmony_ci		else
10062306a36Sopenharmony_ci			kvm_riscv_hfence_vvma_gva(vcpu->kvm, hbase, hmask,
10162306a36Sopenharmony_ci						  cp->a2, cp->a3, PAGE_SHIFT);
10262306a36Sopenharmony_ci		kvm_riscv_vcpu_pmu_incr_fw(vcpu, SBI_PMU_FW_HFENCE_VVMA_SENT);
10362306a36Sopenharmony_ci		break;
10462306a36Sopenharmony_ci	case SBI_EXT_RFENCE_REMOTE_SFENCE_VMA_ASID:
10562306a36Sopenharmony_ci		if (cp->a2 == 0 && cp->a3 == 0)
10662306a36Sopenharmony_ci			kvm_riscv_hfence_vvma_asid_all(vcpu->kvm,
10762306a36Sopenharmony_ci						       hbase, hmask, cp->a4);
10862306a36Sopenharmony_ci		else
10962306a36Sopenharmony_ci			kvm_riscv_hfence_vvma_asid_gva(vcpu->kvm,
11062306a36Sopenharmony_ci						       hbase, hmask,
11162306a36Sopenharmony_ci						       cp->a2, cp->a3,
11262306a36Sopenharmony_ci						       PAGE_SHIFT, cp->a4);
11362306a36Sopenharmony_ci		kvm_riscv_vcpu_pmu_incr_fw(vcpu, SBI_PMU_FW_HFENCE_VVMA_ASID_SENT);
11462306a36Sopenharmony_ci		break;
11562306a36Sopenharmony_ci	case SBI_EXT_RFENCE_REMOTE_HFENCE_GVMA:
11662306a36Sopenharmony_ci	case SBI_EXT_RFENCE_REMOTE_HFENCE_GVMA_VMID:
11762306a36Sopenharmony_ci	case SBI_EXT_RFENCE_REMOTE_HFENCE_VVMA:
11862306a36Sopenharmony_ci	case SBI_EXT_RFENCE_REMOTE_HFENCE_VVMA_ASID:
11962306a36Sopenharmony_ci		/*
12062306a36Sopenharmony_ci		 * Until nested virtualization is implemented, the
12162306a36Sopenharmony_ci		 * SBI HFENCE calls should be treated as NOPs
12262306a36Sopenharmony_ci		 */
12362306a36Sopenharmony_ci		break;
12462306a36Sopenharmony_ci	default:
12562306a36Sopenharmony_ci		retdata->err_val = SBI_ERR_NOT_SUPPORTED;
12662306a36Sopenharmony_ci	}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	return 0;
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ciconst struct kvm_vcpu_sbi_extension vcpu_sbi_ext_rfence = {
13262306a36Sopenharmony_ci	.extid_start = SBI_EXT_RFENCE,
13362306a36Sopenharmony_ci	.extid_end = SBI_EXT_RFENCE,
13462306a36Sopenharmony_ci	.handler = kvm_sbi_ext_rfence_handler,
13562306a36Sopenharmony_ci};
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic int kvm_sbi_ext_srst_handler(struct kvm_vcpu *vcpu,
13862306a36Sopenharmony_ci				    struct kvm_run *run,
13962306a36Sopenharmony_ci				    struct kvm_vcpu_sbi_return *retdata)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	struct kvm_cpu_context *cp = &vcpu->arch.guest_context;
14262306a36Sopenharmony_ci	unsigned long funcid = cp->a6;
14362306a36Sopenharmony_ci	u32 reason = cp->a1;
14462306a36Sopenharmony_ci	u32 type = cp->a0;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	switch (funcid) {
14762306a36Sopenharmony_ci	case SBI_EXT_SRST_RESET:
14862306a36Sopenharmony_ci		switch (type) {
14962306a36Sopenharmony_ci		case SBI_SRST_RESET_TYPE_SHUTDOWN:
15062306a36Sopenharmony_ci			kvm_riscv_vcpu_sbi_system_reset(vcpu, run,
15162306a36Sopenharmony_ci						KVM_SYSTEM_EVENT_SHUTDOWN,
15262306a36Sopenharmony_ci						reason);
15362306a36Sopenharmony_ci			retdata->uexit = true;
15462306a36Sopenharmony_ci			break;
15562306a36Sopenharmony_ci		case SBI_SRST_RESET_TYPE_COLD_REBOOT:
15662306a36Sopenharmony_ci		case SBI_SRST_RESET_TYPE_WARM_REBOOT:
15762306a36Sopenharmony_ci			kvm_riscv_vcpu_sbi_system_reset(vcpu, run,
15862306a36Sopenharmony_ci						KVM_SYSTEM_EVENT_RESET,
15962306a36Sopenharmony_ci						reason);
16062306a36Sopenharmony_ci			retdata->uexit = true;
16162306a36Sopenharmony_ci			break;
16262306a36Sopenharmony_ci		default:
16362306a36Sopenharmony_ci			retdata->err_val = SBI_ERR_NOT_SUPPORTED;
16462306a36Sopenharmony_ci		}
16562306a36Sopenharmony_ci		break;
16662306a36Sopenharmony_ci	default:
16762306a36Sopenharmony_ci		retdata->err_val = SBI_ERR_NOT_SUPPORTED;
16862306a36Sopenharmony_ci	}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	return 0;
17162306a36Sopenharmony_ci}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ciconst struct kvm_vcpu_sbi_extension vcpu_sbi_ext_srst = {
17462306a36Sopenharmony_ci	.extid_start = SBI_EXT_SRST,
17562306a36Sopenharmony_ci	.extid_end = SBI_EXT_SRST,
17662306a36Sopenharmony_ci	.handler = kvm_sbi_ext_srst_handler,
17762306a36Sopenharmony_ci};
178