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