74112400df
In case of programmable counters configured to count inst/cycles we often end-up with counter not incrementing at all from kernel's perspective. For example: - Kernel configures hpm3 to count instructions and sets hpmcounter to -10000 and all modes except U mode are inhibited. - In QEMU we configure a timer to expire after ~10000 instructions. - Problem is, it's often the case that kernel might not even schedule Umode task and we hit the timer callback in QEMU. - In the timer callback we inject the interrupt into kernel, kernel runs the handler and reads hpmcounter3 value. - Given QEMU maintains individual counters to count for each privilege mode, and given umode never ran, the umode counter didn't increment and QEMU returns same value as was programmed by the kernel when starting the counter. - Kernel checks for overflow using previous and current value of the counter and reprograms the counter given there wasn't an overflow as per the counter value. (Which itself is a problem. We have QEMU telling kernel that counter3 overflowed but the counter value returned by QEMU doesn't seem to reflect that.). This change makes sure that timer is reprogrammed from the handler if the counter didn't overflow based on the counter value. Second, this change makes sure that whenever the counter is read, it's value is updated to reflect the latest count. Signed-off-by: Rajnesh Kanwal <rkanwal@rivosinc.com> Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Message-ID: <20240711-smcntrpmf_v7-v8-11-b7c38ae7b263@rivosinc.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
43 lines
1.7 KiB
C
43 lines
1.7 KiB
C
/*
|
|
* RISC-V PMU header file.
|
|
*
|
|
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms and conditions of the GNU General Public License,
|
|
* version 2 or later, as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with
|
|
* this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#ifndef RISCV_PMU_H
|
|
#define RISCV_PMU_H
|
|
|
|
#include "cpu.h"
|
|
#include "qapi/error.h"
|
|
|
|
bool riscv_pmu_ctr_monitor_instructions(CPURISCVState *env,
|
|
uint32_t target_ctr);
|
|
bool riscv_pmu_ctr_monitor_cycles(CPURISCVState *env,
|
|
uint32_t target_ctr);
|
|
void riscv_pmu_timer_cb(void *priv);
|
|
void riscv_pmu_init(RISCVCPU *cpu, Error **errp);
|
|
int riscv_pmu_update_event_map(CPURISCVState *env, uint64_t value,
|
|
uint32_t ctr_idx);
|
|
int riscv_pmu_incr_ctr(RISCVCPU *cpu, enum riscv_pmu_event_idx event_idx);
|
|
void riscv_pmu_generate_fdt_node(void *fdt, uint32_t cmask, char *pmu_name);
|
|
int riscv_pmu_setup_timer(CPURISCVState *env, uint64_t value,
|
|
uint32_t ctr_idx);
|
|
void riscv_pmu_update_fixed_ctrs(CPURISCVState *env, target_ulong newpriv,
|
|
bool new_virt);
|
|
RISCVException riscv_pmu_read_ctr(CPURISCVState *env, target_ulong *val,
|
|
bool upper_half, uint32_t ctr_idx);
|
|
|
|
#endif /* RISCV_PMU_H */
|