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