162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2023 Rivos Inc 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Authors: 662306a36Sopenharmony_ci * Atish Patra <atishp@rivosinc.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/csr.h> 1362306a36Sopenharmony_ci#include <asm/sbi.h> 1462306a36Sopenharmony_ci#include <asm/kvm_vcpu_sbi.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cistatic int kvm_sbi_ext_pmu_handler(struct kvm_vcpu *vcpu, struct kvm_run *run, 1762306a36Sopenharmony_ci struct kvm_vcpu_sbi_return *retdata) 1862306a36Sopenharmony_ci{ 1962306a36Sopenharmony_ci int ret = 0; 2062306a36Sopenharmony_ci struct kvm_cpu_context *cp = &vcpu->arch.guest_context; 2162306a36Sopenharmony_ci struct kvm_pmu *kvpmu = vcpu_to_pmu(vcpu); 2262306a36Sopenharmony_ci unsigned long funcid = cp->a6; 2362306a36Sopenharmony_ci u64 temp; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci if (!kvpmu->init_done) { 2662306a36Sopenharmony_ci retdata->err_val = SBI_ERR_NOT_SUPPORTED; 2762306a36Sopenharmony_ci return 0; 2862306a36Sopenharmony_ci } 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci switch (funcid) { 3162306a36Sopenharmony_ci case SBI_EXT_PMU_NUM_COUNTERS: 3262306a36Sopenharmony_ci ret = kvm_riscv_vcpu_pmu_num_ctrs(vcpu, retdata); 3362306a36Sopenharmony_ci break; 3462306a36Sopenharmony_ci case SBI_EXT_PMU_COUNTER_GET_INFO: 3562306a36Sopenharmony_ci ret = kvm_riscv_vcpu_pmu_ctr_info(vcpu, cp->a0, retdata); 3662306a36Sopenharmony_ci break; 3762306a36Sopenharmony_ci case SBI_EXT_PMU_COUNTER_CFG_MATCH: 3862306a36Sopenharmony_ci#if defined(CONFIG_32BIT) 3962306a36Sopenharmony_ci temp = ((uint64_t)cp->a5 << 32) | cp->a4; 4062306a36Sopenharmony_ci#else 4162306a36Sopenharmony_ci temp = cp->a4; 4262306a36Sopenharmony_ci#endif 4362306a36Sopenharmony_ci /* 4462306a36Sopenharmony_ci * This can fail if perf core framework fails to create an event. 4562306a36Sopenharmony_ci * Forward the error to userspace because it's an error which 4662306a36Sopenharmony_ci * happened within the host kernel. The other option would be 4762306a36Sopenharmony_ci * to convert to an SBI error and forward to the guest. 4862306a36Sopenharmony_ci */ 4962306a36Sopenharmony_ci ret = kvm_riscv_vcpu_pmu_ctr_cfg_match(vcpu, cp->a0, cp->a1, 5062306a36Sopenharmony_ci cp->a2, cp->a3, temp, retdata); 5162306a36Sopenharmony_ci break; 5262306a36Sopenharmony_ci case SBI_EXT_PMU_COUNTER_START: 5362306a36Sopenharmony_ci#if defined(CONFIG_32BIT) 5462306a36Sopenharmony_ci temp = ((uint64_t)cp->a4 << 32) | cp->a3; 5562306a36Sopenharmony_ci#else 5662306a36Sopenharmony_ci temp = cp->a3; 5762306a36Sopenharmony_ci#endif 5862306a36Sopenharmony_ci ret = kvm_riscv_vcpu_pmu_ctr_start(vcpu, cp->a0, cp->a1, cp->a2, 5962306a36Sopenharmony_ci temp, retdata); 6062306a36Sopenharmony_ci break; 6162306a36Sopenharmony_ci case SBI_EXT_PMU_COUNTER_STOP: 6262306a36Sopenharmony_ci ret = kvm_riscv_vcpu_pmu_ctr_stop(vcpu, cp->a0, cp->a1, cp->a2, retdata); 6362306a36Sopenharmony_ci break; 6462306a36Sopenharmony_ci case SBI_EXT_PMU_COUNTER_FW_READ: 6562306a36Sopenharmony_ci ret = kvm_riscv_vcpu_pmu_ctr_read(vcpu, cp->a0, retdata); 6662306a36Sopenharmony_ci break; 6762306a36Sopenharmony_ci default: 6862306a36Sopenharmony_ci retdata->err_val = SBI_ERR_NOT_SUPPORTED; 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci return ret; 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic unsigned long kvm_sbi_ext_pmu_probe(struct kvm_vcpu *vcpu) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci struct kvm_pmu *kvpmu = vcpu_to_pmu(vcpu); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci return kvpmu->init_done; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ciconst struct kvm_vcpu_sbi_extension vcpu_sbi_ext_pmu = { 8262306a36Sopenharmony_ci .extid_start = SBI_EXT_PMU, 8362306a36Sopenharmony_ci .extid_end = SBI_EXT_PMU, 8462306a36Sopenharmony_ci .handler = kvm_sbi_ext_pmu_handler, 8562306a36Sopenharmony_ci .probe = kvm_sbi_ext_pmu_probe, 8662306a36Sopenharmony_ci}; 87