// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2023 Rivos Inc * * Authors: * Atish Patra */ #include #include #include #include #include #include static int kvm_sbi_ext_pmu_handler(struct kvm_vcpu *vcpu, struct kvm_run *run, struct kvm_vcpu_sbi_return *retdata) { int ret = 0; struct kvm_cpu_context *cp = &vcpu->arch.guest_context; struct kvm_pmu *kvpmu = vcpu_to_pmu(vcpu); unsigned long funcid = cp->a6; u64 temp; if (!kvpmu->init_done) { retdata->err_val = SBI_ERR_NOT_SUPPORTED; return 0; } switch (funcid) { case SBI_EXT_PMU_NUM_COUNTERS: ret = kvm_riscv_vcpu_pmu_num_ctrs(vcpu, retdata); break; case SBI_EXT_PMU_COUNTER_GET_INFO: ret = kvm_riscv_vcpu_pmu_ctr_info(vcpu, cp->a0, retdata); break; case SBI_EXT_PMU_COUNTER_CFG_MATCH: #if defined(CONFIG_32BIT) temp = ((uint64_t)cp->a5 << 32) | cp->a4; #else temp = cp->a4; #endif /* * This can fail if perf core framework fails to create an event. * Forward the error to userspace because it's an error which * happened within the host kernel. The other option would be * to convert to an SBI error and forward to the guest. */ ret = kvm_riscv_vcpu_pmu_ctr_cfg_match(vcpu, cp->a0, cp->a1, cp->a2, cp->a3, temp, retdata); break; case SBI_EXT_PMU_COUNTER_START: #if defined(CONFIG_32BIT) temp = ((uint64_t)cp->a4 << 32) | cp->a3; #else temp = cp->a3; #endif ret = kvm_riscv_vcpu_pmu_ctr_start(vcpu, cp->a0, cp->a1, cp->a2, temp, retdata); break; case SBI_EXT_PMU_COUNTER_STOP: ret = kvm_riscv_vcpu_pmu_ctr_stop(vcpu, cp->a0, cp->a1, cp->a2, retdata); break; case SBI_EXT_PMU_COUNTER_FW_READ: ret = kvm_riscv_vcpu_pmu_ctr_read(vcpu, cp->a0, retdata); break; default: retdata->err_val = SBI_ERR_NOT_SUPPORTED; } return ret; } static unsigned long kvm_sbi_ext_pmu_probe(struct kvm_vcpu *vcpu) { struct kvm_pmu *kvpmu = vcpu_to_pmu(vcpu); return kvpmu->init_done; } const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_pmu = { .extid_start = SBI_EXT_PMU, .extid_end = SBI_EXT_PMU, .handler = kvm_sbi_ext_pmu_handler, .probe = kvm_sbi_ext_pmu_probe, };