Message ID | 20220303235440.638790-9-atishp@rivosinc.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | Improve PMU support | expand |
On Fri, Mar 4, 2022 at 8:06 AM Atish Patra <atishp@rivosinc.com> wrote: > The Sscofpmf ('Ss' for Privileged arch and Supervisor-level extensions, > and 'cofpmf' for Count OverFlow and Privilege Mode Filtering) > extension allows the perf to handle overflow interrupts and filtering > support. This patch provides a framework for programmable > counters to leverage the extension. As the extension doesn't have any > provision for the overflow bit for fixed counters, the fixed events > can also be monitoring using programmable counters. The underlying > counters for cycle and instruction counters are always running. Thus, > a separate timer device is programmed to handle the overflow. > > Signed-off-by: Atish Patra <atish.patra@wdc.com> > Signed-off-by: Atish Patra <atishp@rivosinc.com> > --- > target/riscv/cpu.c | 9 ++ > target/riscv/cpu.h | 25 +++ > target/riscv/cpu_bits.h | 55 +++++++ > target/riscv/csr.c | 157 ++++++++++++++++-- > target/riscv/pmu.c | 346 +++++++++++++++++++++++++++++++++++++++- > target/riscv/pmu.h | 7 + > 6 files changed, 588 insertions(+), 11 deletions(-) > > diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c > index 2d2b0b04fbc8..e13d2f81a05c 100644 > --- a/target/riscv/cpu.c > +++ b/target/riscv/cpu.c > @@ -22,6 +22,7 @@ > #include "qemu/ctype.h" > #include "qemu/log.h" > #include "cpu.h" > +#include "pmu.h" > #include "internals.h" > #include "exec/exec-all.h" > #include "qapi/error.h" > @@ -689,6 +690,13 @@ static void riscv_cpu_realize(DeviceState *dev, Error > **errp) > set_misa(env, env->misa_mxl, ext); > } > > + if (cpu->cfg.pmu_num) { > + if (!riscv_pmu_init(cpu, cpu->cfg.pmu_num) && > cpu->cfg.ext_sscofpmf) { > + cpu->pmu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, > + riscv_pmu_timer_cb, cpu); > + } > + } > + > riscv_cpu_register_gdb_regs_for_features(cs); > > qemu_init_vcpu(cs); > @@ -780,6 +788,7 @@ static Property riscv_cpu_properties[] = { > DEFINE_PROP_BOOL("v", RISCVCPU, cfg.ext_v, false), > DEFINE_PROP_BOOL("h", RISCVCPU, cfg.ext_h, true), > DEFINE_PROP_UINT8("pmu-num", RISCVCPU, cfg.pmu_num, 16), > + DEFINE_PROP_BOOL("sscofpmf", RISCVCPU, cfg.ext_sscofpmf, false), > DEFINE_PROP_BOOL("Zifencei", RISCVCPU, cfg.ext_ifencei, true), > DEFINE_PROP_BOOL("Zicsr", RISCVCPU, cfg.ext_icsr, true), > DEFINE_PROP_BOOL("Zfh", RISCVCPU, cfg.ext_zfh, false), > diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h > index b49adcccd85d..eaa5a73897eb 100644 > --- a/target/riscv/cpu.h > +++ b/target/riscv/cpu.h > @@ -129,6 +129,8 @@ typedef struct PMUCTRState { > /* Snapshort value of a counter in RV32 */ > target_ulong mhpmcounterh_prev; > bool started; > + /* Value beyond UINT32_MAX/UINT64_MAX before overflow interrupt > trigger */ > + target_ulong irq_overflow_left; > } PMUCTRState; > > struct CPURISCVState { > @@ -281,6 +283,9 @@ struct CPURISCVState { > /* PMU event selector configured values. First three are unused*/ > target_ulong mhpmevent_val[RV_MAX_MHPMEVENTS]; > > + /* PMU event selector configured values for RV32*/ > + target_ulong mhpmeventh_val[RV_MAX_MHPMEVENTS]; > + > target_ulong sscratch; > target_ulong mscratch; > > @@ -400,6 +405,7 @@ struct RISCVCPUConfig { > bool ext_zhinxmin; > bool ext_zve32f; > bool ext_zve64f; > + bool ext_sscofpmf; > > /* Vendor-specific custom extensions */ > bool ext_XVentanaCondOps; > @@ -438,6 +444,12 @@ struct RISCVCPU { > > /* Configuration Settings */ > RISCVCPUConfig cfg; > + > + QEMUTimer *pmu_timer; > + /* A bitmask of Available programmable counters */ > + uint32_t pmu_avail_ctrs; > + /* Mapping of events to counters */ > + GHashTable *pmu_event_ctr_map; > }; > > static inline int riscv_has_ext(CPURISCVState *env, target_ulong ext) > @@ -697,6 +709,19 @@ enum { > CSR_TABLE_SIZE = 0x1000 > }; > > +/** > + * The event id are encoded based on the encoding specified in the > + * SBI specification v0.3 > + */ > + > +enum riscv_pmu_event_idx { > + RISCV_PMU_EVENT_HW_CPU_CYCLES = 0x01, > + RISCV_PMU_EVENT_HW_INSTRUCTIONS = 0x02, > + RISCV_PMU_EVENT_CACHE_DTLB_READ_MISS = 0x10019, > + RISCV_PMU_EVENT_CACHE_DTLB_WRITE_MISS = 0x1001B, > + RISCV_PMU_EVENT_CACHE_ITLB_PREFETCH_MISS = 0x10021, > +}; > + > /* CSR function table */ > extern riscv_csr_operations csr_ops[CSR_TABLE_SIZE]; > > diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h > index 48b39e6d52a7..da78e2704081 100644 > --- a/target/riscv/cpu_bits.h > +++ b/target/riscv/cpu_bits.h > @@ -400,6 +400,37 @@ > #define CSR_MHPMEVENT29 0x33d > #define CSR_MHPMEVENT30 0x33e > #define CSR_MHPMEVENT31 0x33f > + > +#define CSR_MHPMEVENT3H 0x723 > +#define CSR_MHPMEVENT4H 0x724 > +#define CSR_MHPMEVENT5H 0x725 > +#define CSR_MHPMEVENT6H 0x726 > +#define CSR_MHPMEVENT7H 0x727 > +#define CSR_MHPMEVENT8H 0x728 > +#define CSR_MHPMEVENT9H 0x729 > +#define CSR_MHPMEVENT10H 0x72a > +#define CSR_MHPMEVENT11H 0x72b > +#define CSR_MHPMEVENT12H 0x72c > +#define CSR_MHPMEVENT13H 0x72d > +#define CSR_MHPMEVENT14H 0x72e > +#define CSR_MHPMEVENT15H 0x72f > +#define CSR_MHPMEVENT16H 0x730 > +#define CSR_MHPMEVENT17H 0x731 > +#define CSR_MHPMEVENT18H 0x732 > +#define CSR_MHPMEVENT19H 0x733 > +#define CSR_MHPMEVENT20H 0x734 > +#define CSR_MHPMEVENT21H 0x735 > +#define CSR_MHPMEVENT22H 0x736 > +#define CSR_MHPMEVENT23H 0x737 > +#define CSR_MHPMEVENT24H 0x738 > +#define CSR_MHPMEVENT25H 0x739 > +#define CSR_MHPMEVENT26H 0x73a > +#define CSR_MHPMEVENT27H 0x73b > +#define CSR_MHPMEVENT28H 0x73c > +#define CSR_MHPMEVENT29H 0x73d > +#define CSR_MHPMEVENT30H 0x73e > +#define CSR_MHPMEVENT31H 0x73f > + > #define CSR_MHPMCOUNTER3H 0xb83 > #define CSR_MHPMCOUNTER4H 0xb84 > #define CSR_MHPMCOUNTER5H 0xb85 > @@ -461,6 +492,7 @@ > #define CSR_VSMTE 0x2c0 > #define CSR_VSPMMASK 0x2c1 > #define CSR_VSPMBASE 0x2c2 > +#define CSR_SCOUNTOVF 0xda0 > > /* mstatus CSR bits */ > #define MSTATUS_UIE 0x00000001 > @@ -635,6 +667,7 @@ typedef enum RISCVException { > #define IRQ_VS_EXT 10 > #define IRQ_M_EXT 11 > #define IRQ_S_GEXT 12 > +#define IRQ_PMU_OVF 13 > #define IRQ_LOCAL_MAX 16 > #define IRQ_LOCAL_GUEST_MAX (TARGET_LONG_BITS - 1) > > @@ -652,11 +685,13 @@ typedef enum RISCVException { > #define MIP_VSEIP (1 << IRQ_VS_EXT) > #define MIP_MEIP (1 << IRQ_M_EXT) > #define MIP_SGEIP (1 << IRQ_S_GEXT) > +#define MIP_LCOFIP (1 << IRQ_PMU_OVF) > > /* sip masks */ > #define SIP_SSIP MIP_SSIP > #define SIP_STIP MIP_STIP > #define SIP_SEIP MIP_SEIP > +#define SIP_LCOFIP MIP_LCOFIP > > /* MIE masks */ > #define MIE_SEIE (1 << IRQ_S_EXT) > @@ -804,4 +839,24 @@ typedef enum RISCVException { > #define HVICTL_VALID_MASK \ > (HVICTL_VTI | HVICTL_IID | HVICTL_IPRIOM | HVICTL_IPRIO) > > +/* PMU related bits */ > +#define MIE_LCOFIE (1 << IRQ_PMU_OVF) > + > +#define MHPMEVENT_BIT_OF BIT(63) > Hi Atish, I think it's better to use BIT_ULL() for value >= 32. BIT() is defined to: (1UL << (nr)), which might cause the overflow issue on some platforms (e.g. Windows). Regards, Frank Chang +#define MHPMEVENTH_BIT_OF BIT(31) > +#define MHPMEVENT_BIT_MINH BIT(62) > +#define MHPMEVENTH_BIT_MINH BIT(30) > +#define MHPMEVENT_BIT_SINH BIT(61) > +#define MHPMEVENTH_BIT_SINH BIT(29) > +#define MHPMEVENT_BIT_UINH BIT(60) > +#define MHPMEVENTH_BIT_UINH BIT(28) > +#define MHPMEVENT_BIT_VSINH BIT(59) > +#define MHPMEVENTH_BIT_VSINH BIT(27) > +#define MHPMEVENT_BIT_VUINH BIT(58) > +#define MHPMEVENTH_BIT_VUINH BIT(26) > + > +#define MHPMEVENT_SSCOF_MASK _ULL(0xFFFF000000000000) > +#define MHPMEVENT_IDX_MASK 0xFFFFF > +#define MHPMEVENT_SSCOF_RESVD 16 > + > #endif > diff --git a/target/riscv/csr.c b/target/riscv/csr.c > index 3286e1e44455..cfcc1a0882d9 100644 > --- a/target/riscv/csr.c > +++ b/target/riscv/csr.c > @@ -96,13 +96,26 @@ static RISCVException mctr32(CPURISCVState *env, int > csrno) > return mctr(env, csrno); > } > > +static RISCVException sscofpmf(CPURISCVState *env, int csrno) > +{ > + #if !defined(CONFIG_USER_ONLY) > + CPUState *cs = env_cpu(env); > + RISCVCPU *cpu = RISCV_CPU(cs); > + > + if (!cpu->cfg.ext_sscofpmf) { > + return RISCV_EXCP_ILLEGAL_INST; > + } > +#endif > + return RISCV_EXCP_NONE; > +} > + > static RISCVException ctr(CPURISCVState *env, int csrno) > { > #if !defined(CONFIG_USER_ONLY) > CPUState *cs = env_cpu(env); > RISCVCPU *cpu = RISCV_CPU(cs); > int ctr_index; > - int base_csrno = CSR_HPMCOUNTER3; > + int base_csrno = CSR_CYCLE; > bool rv32 = riscv_cpu_mxl(env) == MXL_RV32 ? true : false; > > if (rv32 && csrno >= CSR_CYCLEH) { > @@ -111,11 +124,18 @@ static RISCVException ctr(CPURISCVState *env, int > csrno) > } > ctr_index = csrno - base_csrno; > > - if (!cpu->cfg.pmu_num || ctr_index >= (cpu->cfg.pmu_num)) { > + if ((csrno >= CSR_CYCLE && csrno <= CSR_INSTRET) || > + (csrno >= CSR_CYCLEH && csrno <= CSR_INSTRETH)) { > + goto skip_ext_pmu_check; > + } > + > + if ((!cpu->cfg.pmu_num || !(cpu->pmu_avail_ctrs & BIT(ctr_index)))) { > /* No counter is enabled in PMU or the counter is out of range */ > return RISCV_EXCP_ILLEGAL_INST; > } > > +skip_ext_pmu_check: > + > if (env->priv == PRV_S) { > switch (csrno) { > case CSR_CYCLE: > @@ -134,7 +154,6 @@ static RISCVException ctr(CPURISCVState *env, int > csrno) > } > break; > case CSR_HPMCOUNTER3...CSR_HPMCOUNTER31: > - ctr_index = csrno - CSR_CYCLE; > if (!get_field(env->mcounteren, 1 << ctr_index)) { > return RISCV_EXCP_ILLEGAL_INST; > } > @@ -158,7 +177,6 @@ static RISCVException ctr(CPURISCVState *env, int > csrno) > } > break; > case CSR_HPMCOUNTER3H...CSR_HPMCOUNTER31H: > - ctr_index = csrno - CSR_CYCLEH; > if (!get_field(env->mcounteren, 1 << ctr_index)) { > return RISCV_EXCP_ILLEGAL_INST; > } > @@ -188,7 +206,6 @@ static RISCVException ctr(CPURISCVState *env, int > csrno) > } > break; > case CSR_HPMCOUNTER3...CSR_HPMCOUNTER31: > - ctr_index = csrno - CSR_CYCLE; > if (!get_field(env->hcounteren, 1 << ctr_index) && > get_field(env->mcounteren, 1 << ctr_index)) { > return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; > @@ -216,7 +233,6 @@ static RISCVException ctr(CPURISCVState *env, int > csrno) > } > break; > case CSR_HPMCOUNTER3H...CSR_HPMCOUNTER31H: > - ctr_index = csrno - CSR_CYCLEH; > if (!get_field(env->hcounteren, 1 << ctr_index) && > get_field(env->mcounteren, 1 << ctr_index)) { > return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; > @@ -585,11 +601,36 @@ static int write_mhpmevent(CPURISCVState *env, int > csrno, target_ulong val) > { > int evt_index = csrno - CSR_MCOUNTINHIBIT; > > + if (riscv_cpu_mxl(env) != MXL_RV32) { > + riscv_pmu_update_event_map(env, val, evt_index); > + } > env->mhpmevent_val[evt_index] = val; > > return RISCV_EXCP_NONE; > } > > +static int read_mhpmeventh(CPURISCVState *env, int csrno, target_ulong > *val) > +{ > + int evt_index = csrno - CSR_MHPMEVENT3H + 3; > + > + *val = env->mhpmevent_val[evt_index]; > + > + return RISCV_EXCP_NONE; > +} > + > +static int write_mhpmeventh(CPURISCVState *env, int csrno, target_ulong > val) > +{ > + int evt_index = csrno - CSR_MHPMEVENT3H + 3; > + uint64_t mhpmevth_val = val; > + uint64_t mhpmevt_val = env->mhpmevent_val[evt_index]; > + > + mhpmevt_val = mhpmevt_val | (mhpmevth_val << 32); > + riscv_pmu_update_event_map(env, mhpmevt_val, evt_index); > + env->mhpmeventh_val[evt_index] = val; > + > + return RISCV_EXCP_NONE; > +} > + > static int write_mhpmcounter(CPURISCVState *env, int csrno, target_ulong > val) > { > int ctr_idx = csrno - CSR_MCYCLE; > @@ -599,7 +640,10 @@ static int write_mhpmcounter(CPURISCVState *env, int > csrno, target_ulong val) > if (riscv_pmu_ctr_monitor_cycles(env, ctr_idx) || > riscv_pmu_ctr_monitor_instructions(env, ctr_idx)) { > counter->mhpmcounter_prev = get_ticks(false); > - } else { > + if (ctr_idx > 2) { > + riscv_pmu_setup_timer(env, val, ctr_idx); > + } > + } else { > /* Other counters can keep incrementing from the given value */ > counter->mhpmcounter_prev = val; > } > @@ -611,11 +655,17 @@ static int write_mhpmcounterh(CPURISCVState *env, > int csrno, target_ulong val) > { > int ctr_idx = csrno - CSR_MCYCLEH; > PMUCTRState *counter = &env->pmu_ctrs[ctr_idx]; > + uint64_t mhpmctr_val = counter->mhpmcounter_val; > + uint64_t mhpmctrh_val = val; > > counter->mhpmcounterh_val = val; > + mhpmctr_val = mhpmctr_val | (mhpmctrh_val << 32); > if (riscv_pmu_ctr_monitor_cycles(env, ctr_idx) || > riscv_pmu_ctr_monitor_instructions(env, ctr_idx)) { > counter->mhpmcounterh_prev = get_ticks(true); > + if (ctr_idx > 2) { > + riscv_pmu_setup_timer(env, mhpmctr_val, ctr_idx); > + } > } else { > counter->mhpmcounterh_prev = val; > } > @@ -688,6 +738,32 @@ static int read_hpmcounterh(CPURISCVState *env, int > csrno, target_ulong *val) > return riscv_pmu_read_ctr(env, val, true, ctr_index); > } > > +static int read_scountovf(CPURISCVState *env, int csrno, target_ulong > *val) > +{ > + int mhpmevt_start = CSR_MHPMEVENT3 - CSR_MCOUNTINHIBIT; > + int i; > + *val = 0; > + target_ulong *mhpm_evt_val; > + uint64_t of_bit_mask; > + > + if (riscv_cpu_mxl(env) == MXL_RV32) { > + mhpm_evt_val = env->mhpmeventh_val; > + of_bit_mask = MHPMEVENTH_BIT_OF; > + } else { > + mhpm_evt_val = env->mhpmevent_val; > + of_bit_mask = MHPMEVENT_BIT_OF; > + } > + > + for (i = mhpmevt_start; i < RV_MAX_MHPMEVENTS; i++) { > + if ((get_field(env->mcounteren, BIT(i))) && > + (mhpm_evt_val[i] & of_bit_mask)) { > + *val |= BIT(i); > + } > + } > + > + return RISCV_EXCP_NONE; > +} > + > #if defined(CONFIG_USER_ONLY) > static RISCVException read_time(CPURISCVState *env, int csrno, > target_ulong *val) > @@ -734,7 +810,8 @@ static RISCVException read_timeh(CPURISCVState *env, > int csrno, > /* Machine constants */ > > #define M_MODE_INTERRUPTS ((uint64_t)(MIP_MSIP | MIP_MTIP | MIP_MEIP)) > -#define S_MODE_INTERRUPTS ((uint64_t)(MIP_SSIP | MIP_STIP | MIP_SEIP)) > +#define S_MODE_INTERRUPTS ((uint64_t)(MIP_SSIP | MIP_STIP | MIP_SEIP | \ > + MIP_LCOFIP)) > #define VS_MODE_INTERRUPTS ((uint64_t)(MIP_VSSIP | MIP_VSTIP | MIP_VSEIP)) > #define HS_MODE_INTERRUPTS ((uint64_t)(MIP_SGEIP | VS_MODE_INTERRUPTS)) > > @@ -775,7 +852,8 @@ static const target_ulong vs_delegable_excps = > DELEGABLE_EXCPS & > static const target_ulong sstatus_v1_10_mask = SSTATUS_SIE | SSTATUS_SPIE > | > SSTATUS_UIE | SSTATUS_UPIE | SSTATUS_SPP | SSTATUS_FS | SSTATUS_XS | > SSTATUS_SUM | SSTATUS_MXR | SSTATUS_VS; > -static const target_ulong sip_writable_mask = SIP_SSIP | MIP_USIP | > MIP_UEIP; > +static const target_ulong sip_writable_mask = SIP_SSIP | MIP_USIP | > MIP_UEIP | > + SIP_LCOFIP; > static const target_ulong hip_writable_mask = MIP_VSSIP; > static const target_ulong hvip_writable_mask = MIP_VSSIP | MIP_VSTIP | > MIP_VSEIP; > static const target_ulong vsip_writable_mask = MIP_VSSIP; > @@ -3814,6 +3892,65 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { > [CSR_MHPMEVENT31] = { "mhpmevent31", any, read_mhpmevent, > write_mhpmevent }, > > + [CSR_MHPMEVENT3H] = { "mhpmevent3h", sscofpmf, read_mhpmeventh, > + write_mhpmeventh}, > + [CSR_MHPMEVENT4H] = { "mhpmevent4h", sscofpmf, read_mhpmeventh, > + write_mhpmeventh}, > + [CSR_MHPMEVENT5H] = { "mhpmevent5h", sscofpmf, read_mhpmeventh, > + write_mhpmeventh}, > + [CSR_MHPMEVENT6H] = { "mhpmevent6h", sscofpmf, read_mhpmeventh, > + write_mhpmeventh}, > + [CSR_MHPMEVENT7H] = { "mhpmevent7h", sscofpmf, read_mhpmeventh, > + write_mhpmeventh}, > + [CSR_MHPMEVENT8H] = { "mhpmevent8h", sscofpmf, read_mhpmeventh, > + write_mhpmeventh}, > + [CSR_MHPMEVENT9H] = { "mhpmevent9h", sscofpmf, read_mhpmeventh, > + write_mhpmeventh}, > + [CSR_MHPMEVENT10H] = { "mhpmevent10h", sscofpmf, > read_mhpmeventh, > + write_mhpmeventh}, > + [CSR_MHPMEVENT11H] = { "mhpmevent11h", sscofpmf, > read_mhpmeventh, > + write_mhpmeventh}, > + [CSR_MHPMEVENT12H] = { "mhpmevent12h", sscofpmf, > read_mhpmeventh, > + write_mhpmeventh}, > + [CSR_MHPMEVENT13H] = { "mhpmevent13h", sscofpmf, > read_mhpmeventh, > + write_mhpmeventh}, > + [CSR_MHPMEVENT14H] = { "mhpmevent14h", sscofpmf, > read_mhpmeventh, > + write_mhpmeventh}, > + [CSR_MHPMEVENT15H] = { "mhpmevent15h", sscofpmf, > read_mhpmeventh, > + write_mhpmeventh}, > + [CSR_MHPMEVENT16H] = { "mhpmevent16h", sscofpmf, > read_mhpmeventh, > + write_mhpmeventh}, > + [CSR_MHPMEVENT17H] = { "mhpmevent17h", sscofpmf, > read_mhpmeventh, > + write_mhpmeventh}, > + [CSR_MHPMEVENT18H] = { "mhpmevent18h", sscofpmf, > read_mhpmeventh, > + write_mhpmeventh}, > + [CSR_MHPMEVENT19H] = { "mhpmevent19h", sscofpmf, > read_mhpmeventh, > + write_mhpmeventh}, > + [CSR_MHPMEVENT20H] = { "mhpmevent20h", sscofpmf, > read_mhpmeventh, > + write_mhpmeventh}, > + [CSR_MHPMEVENT21H] = { "mhpmevent21h", sscofpmf, > read_mhpmeventh, > + write_mhpmeventh}, > + [CSR_MHPMEVENT22H] = { "mhpmevent22h", sscofpmf, > read_mhpmeventh, > + write_mhpmeventh}, > + [CSR_MHPMEVENT23H] = { "mhpmevent23h", sscofpmf, > read_mhpmeventh, > + write_mhpmeventh}, > + [CSR_MHPMEVENT24H] = { "mhpmevent24h", sscofpmf, > read_mhpmeventh, > + write_mhpmeventh}, > + [CSR_MHPMEVENT25H] = { "mhpmevent25h", sscofpmf, > read_mhpmeventh, > + write_mhpmeventh}, > + [CSR_MHPMEVENT26H] = { "mhpmevent26h", sscofpmf, > read_mhpmeventh, > + write_mhpmeventh}, > + [CSR_MHPMEVENT27H] = { "mhpmevent27h", sscofpmf, > read_mhpmeventh, > + write_mhpmeventh}, > + [CSR_MHPMEVENT28H] = { "mhpmevent28h", sscofpmf, > read_mhpmeventh, > + write_mhpmeventh}, > + [CSR_MHPMEVENT29H] = { "mhpmevent29h", sscofpmf, > read_mhpmeventh, > + write_mhpmeventh}, > + [CSR_MHPMEVENT30H] = { "mhpmevent30h", sscofpmf, > read_mhpmeventh, > + write_mhpmeventh}, > + [CSR_MHPMEVENT31H] = { "mhpmevent31h", sscofpmf, > read_mhpmeventh, > + write_mhpmeventh}, > + > [CSR_HPMCOUNTER3H] = { "hpmcounter3h", ctr32, read_hpmcounterh }, > [CSR_HPMCOUNTER4H] = { "hpmcounter4h", ctr32, read_hpmcounterh }, > [CSR_HPMCOUNTER5H] = { "hpmcounter5h", ctr32, read_hpmcounterh }, > @@ -3902,5 +4039,7 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { > write_mhpmcounterh > }, > [CSR_MHPMCOUNTER31H] = { "mhpmcounter31h", mctr32, read_hpmcounterh, > write_mhpmcounterh > }, > + [CSR_SCOUNTOVF] = { "scountovf", sscofpmf, read_scountovf }, > + > #endif /* !CONFIG_USER_ONLY */ > }; > diff --git a/target/riscv/pmu.c b/target/riscv/pmu.c > index 000fe8da45ef..5b212d2eb630 100644 > --- a/target/riscv/pmu.c > +++ b/target/riscv/pmu.c > @@ -20,13 +20,355 @@ > #include "cpu.h" > #include "pmu.h" > > +#define RISCV_TIMEBASE_FREQ 1000000000 /* 1Ghz */ > +#define MAKE_32BIT_MASK(shift, length) \ > + (((uint32_t)(~0UL) >> (32 - (length))) << (shift)) > + > +static bool riscv_pmu_counter_valid(RISCVCPU *cpu, uint32_t ctr_idx) > +{ > + if (ctr_idx < 3 || ctr_idx >= RV_MAX_MHPMCOUNTERS || > + !(cpu->pmu_avail_ctrs & BIT(ctr_idx))) { > + return false; > + } else { > + return true; > + } > +} > + > +static bool riscv_pmu_counter_enabled(RISCVCPU *cpu, uint32_t ctr_idx) > +{ > + CPURISCVState *env = &cpu->env; > + > + if (!riscv_pmu_counter_valid(cpu, ctr_idx) || > + !get_field(env->mcounteren, BIT(ctr_idx))) { > + return false; > + } else { > + return true; > + } > +} > + > +static int riscv_pmu_incr_ctr_rv32(RISCVCPU *cpu, uint32_t ctr_idx) > +{ > + CPURISCVState *env = &cpu->env; > + target_ulong max_val = UINT32_MAX; > + PMUCTRState *counter = &env->pmu_ctrs[ctr_idx]; > + > + /* Privilege mode filtering */ > + if ((env->priv == PRV_M && > + (env->mhpmeventh_val[ctr_idx] & MHPMEVENTH_BIT_MINH)) || > + (env->priv == PRV_S && > + (env->mhpmeventh_val[ctr_idx] & MHPMEVENTH_BIT_SINH)) || > + (env->priv == PRV_U && > + (env->mhpmeventh_val[ctr_idx] & MHPMEVENTH_BIT_UINH))) { > + return 0; > + } > + > + /* Handle the overflow scenario */ > + if (counter->mhpmcounter_val == max_val) { > + if (counter->mhpmcounterh_val == max_val) { > + counter->mhpmcounter_val = 0; > + counter->mhpmcounterh_val = 0; > + /* Generate interrupt only if OF bit is clear */ > + if (!(env->mhpmeventh_val[ctr_idx] & MHPMEVENTH_BIT_OF)) { > + env->mhpmeventh_val[ctr_idx] |= MHPMEVENTH_BIT_OF; > + riscv_cpu_update_mip(cpu, MIP_LCOFIP, BOOL_TO_MASK(1)); > + } > + } else { > + counter->mhpmcounterh_val++; > + } > + } else { > + counter->mhpmcounter_val++; > + } > + > + return 0; > +} > + > +static int riscv_pmu_incr_ctr_rv64(RISCVCPU *cpu, uint32_t ctr_idx) > +{ > + CPURISCVState *env = &cpu->env; > + PMUCTRState *counter = &env->pmu_ctrs[ctr_idx]; > + uint64_t max_val = UINT64_MAX; > + > + /* Privilege mode filtering */ > + if ((env->priv == PRV_M && > + (env->mhpmevent_val[ctr_idx] & MHPMEVENT_BIT_MINH)) || > + (env->priv == PRV_S && > + (env->mhpmevent_val[ctr_idx] & MHPMEVENT_BIT_SINH)) || > + (env->priv == PRV_U && > + (env->mhpmevent_val[ctr_idx] & MHPMEVENT_BIT_UINH))) { > + return 0; > + } > + > + /* Handle the overflow scenario */ > + if (counter->mhpmcounter_val == max_val) { > + counter->mhpmcounter_val = 0; > + /* Generate interrupt only if OF bit is clear */ > + if (!(env->mhpmevent_val[ctr_idx] & MHPMEVENT_BIT_OF)) { > + env->mhpmevent_val[ctr_idx] |= MHPMEVENT_BIT_OF; > + riscv_cpu_update_mip(cpu, MIP_LCOFIP, BOOL_TO_MASK(1)); > + } > + } else { > + counter->mhpmcounter_val++; > + } > + return 0; > +} > + > +int riscv_pmu_incr_ctr(RISCVCPU *cpu, enum riscv_pmu_event_idx event_idx) > +{ > + uint32_t ctr_idx; > + int ret; > + CPURISCVState *env = &cpu->env; > + gpointer value; > + > + value = g_hash_table_lookup(cpu->pmu_event_ctr_map, > + GUINT_TO_POINTER(event_idx)); > + if (!value) { > + return -1; > + } > + > + ctr_idx = GPOINTER_TO_UINT(value); > + if (!riscv_pmu_counter_enabled(cpu, ctr_idx) || > + get_field(env->mcountinhibit, BIT(ctr_idx))) { > + return -1; > + } > + > + if (riscv_cpu_mxl(env) == MXL_RV32) { > + ret = riscv_pmu_incr_ctr_rv32(cpu, ctr_idx); > + } else { > + ret = riscv_pmu_incr_ctr_rv64(cpu, ctr_idx); > + } > + > + return ret; > +} > + > bool riscv_pmu_ctr_monitor_instructions(CPURISCVState *env, > uint32_t target_ctr) > { > - return (target_ctr == 0) ? true : false; > + RISCVCPU *cpu; > + uint32_t event_idx; > + uint32_t ctr_idx; > + > + /* Fixed instret counter */ > + if (target_ctr == 2) { > + return true; > + } > + > + cpu = RISCV_CPU(env_cpu(env)); > + event_idx = RISCV_PMU_EVENT_HW_INSTRUCTIONS; > + ctr_idx = GPOINTER_TO_UINT(g_hash_table_lookup(cpu->pmu_event_ctr_map, > + GUINT_TO_POINTER(event_idx))); > + if (!ctr_idx) { > + return false; > + } > + > + return target_ctr == ctr_idx ? true : false; > } > > bool riscv_pmu_ctr_monitor_cycles(CPURISCVState *env, uint32_t target_ctr) > { > - return (target_ctr == 2) ? true : false; > + RISCVCPU *cpu; > + uint32_t event_idx; > + uint32_t ctr_idx; > + > + /* Fixed mcycle counter */ > + if (target_ctr == 0) { > + return true; > + } > + > + cpu = RISCV_CPU(env_cpu(env)); > + event_idx = RISCV_PMU_EVENT_HW_CPU_CYCLES; > + ctr_idx = GPOINTER_TO_UINT(g_hash_table_lookup(cpu->pmu_event_ctr_map, > + GUINT_TO_POINTER(event_idx))); > + > + /* Counter zero is not used for event_ctr_map */ > + if (!ctr_idx) { > + return false; > + } > + > + return (target_ctr == ctr_idx) ? true : false; > +} > + > +static gboolean pmu_remove_event_map(gpointer key, gpointer value, > + gpointer udata) > +{ > + return (GPOINTER_TO_UINT(value) == GPOINTER_TO_UINT(udata)) ? true : > false; > +} > + > +static int64_t pmu_icount_ticks_to_ns(int64_t value) > +{ > + int64_t ret = 0; > + > + if (icount_enabled()) { > + ret = icount_to_ns(value); > + } else { > + ret = (NANOSECONDS_PER_SECOND / RISCV_TIMEBASE_FREQ) * value; > + } > + > + return ret; > +} > + > +int riscv_pmu_update_event_map(CPURISCVState *env, uint64_t value, > + uint32_t ctr_idx) > +{ > + uint32_t event_idx; > + RISCVCPU *cpu = RISCV_CPU(env_cpu(env)); > + > + if (!riscv_pmu_counter_valid(cpu, ctr_idx)) { > + return -1; > + } > + > + /** > + * Expected mhpmevent value is zero for reset case. Remove the current > + * mapping. > + */ > + if (!value) { > + g_hash_table_foreach_remove(cpu->pmu_event_ctr_map, > + pmu_remove_event_map, > + GUINT_TO_POINTER(ctr_idx)); > + return 0; > + } > + > + event_idx = value & MHPMEVENT_IDX_MASK; > + if (g_hash_table_lookup(cpu->pmu_event_ctr_map, > + GUINT_TO_POINTER(event_idx))) { > + return 0; > + } > + > + switch (event_idx) { > + case RISCV_PMU_EVENT_HW_CPU_CYCLES: > + case RISCV_PMU_EVENT_HW_INSTRUCTIONS: > + case RISCV_PMU_EVENT_CACHE_DTLB_READ_MISS: > + case RISCV_PMU_EVENT_CACHE_DTLB_WRITE_MISS: > + case RISCV_PMU_EVENT_CACHE_ITLB_PREFETCH_MISS: > + break; > + default: > + /* We don't support any raw events right now */ > + return -1; > + } > + g_hash_table_insert(cpu->pmu_event_ctr_map, > GUINT_TO_POINTER(event_idx), > + GUINT_TO_POINTER(ctr_idx)); > + > + return 0; > +} > + > +static void pmu_timer_trigger_irq(RISCVCPU *cpu, > + enum riscv_pmu_event_idx evt_idx) > +{ > + uint32_t ctr_idx; > + CPURISCVState *env = &cpu->env; > + PMUCTRState *counter; > + target_ulong *mhpmevent_val; > + uint64_t of_bit_mask; > + int64_t irq_trigger_at; > + > + if (evt_idx != RISCV_PMU_EVENT_HW_CPU_CYCLES && > + evt_idx != RISCV_PMU_EVENT_HW_INSTRUCTIONS) { > + return; > + } > + > + ctr_idx = GPOINTER_TO_UINT(g_hash_table_lookup(cpu->pmu_event_ctr_map, > + GUINT_TO_POINTER(evt_idx))); > + if (!riscv_pmu_counter_enabled(cpu, ctr_idx)) { > + return; > + } > + > + if (riscv_cpu_mxl(env) == MXL_RV32) { > + mhpmevent_val = &env->mhpmeventh_val[ctr_idx]; > + of_bit_mask = MHPMEVENTH_BIT_OF; > + } else { > + mhpmevent_val = &env->mhpmevent_val[ctr_idx]; > + of_bit_mask = MHPMEVENT_BIT_OF; > + } > + > + counter = &env->pmu_ctrs[ctr_idx]; > + if (counter->irq_overflow_left > 0) { > + irq_trigger_at = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + > + counter->irq_overflow_left; > + timer_mod_anticipate_ns(cpu->pmu_timer, irq_trigger_at); > + counter->irq_overflow_left = 0; > + return; > + } > + > + if (cpu->pmu_avail_ctrs & BIT(ctr_idx)) { > + /* Generate interrupt only if OF bit is clear */ > + if (!(*mhpmevent_val & of_bit_mask)) { > + *mhpmevent_val |= of_bit_mask; > + riscv_cpu_update_mip(cpu, MIP_LCOFIP, BOOL_TO_MASK(1)); > + } > + } > +} > + > +/* Timer callback for instret and cycle counter overflow */ > +void riscv_pmu_timer_cb(void *priv) > +{ > + RISCVCPU *cpu = priv; > + > + /* Timer event was triggered only for these events */ > + pmu_timer_trigger_irq(cpu, RISCV_PMU_EVENT_HW_CPU_CYCLES); > + pmu_timer_trigger_irq(cpu, RISCV_PMU_EVENT_HW_INSTRUCTIONS); > +} > + > +int riscv_pmu_setup_timer(CPURISCVState *env, uint64_t value, uint32_t > ctr_idx) > +{ > + uint64_t overflow_delta, overflow_at; > + int64_t overflow_ns, overflow_left = 0; > + RISCVCPU *cpu = RISCV_CPU(env_cpu(env)); > + PMUCTRState *counter = &env->pmu_ctrs[ctr_idx]; > + > + if (!riscv_pmu_counter_valid(cpu, ctr_idx) || !cpu->cfg.ext_sscofpmf) > { > + return -1; > + } > + > + if (value) { > + overflow_delta = UINT64_MAX - value + 1; > + } else { > + overflow_delta = UINT64_MAX - value; > + } > + > + /** > + * QEMU supports only int64_t timers while RISC-V counters are > uint64_t. > + * Compute the leftover and save it so that it can be reprogrammed > again > + * when timer expires. > + */ > + if (overflow_delta > INT64_MAX) { > + overflow_left = overflow_delta - INT64_MAX; > + } > + > + if (riscv_pmu_ctr_monitor_cycles(env, ctr_idx) || > + riscv_pmu_ctr_monitor_instructions(env, ctr_idx)) { > + overflow_ns = pmu_icount_ticks_to_ns((int64_t)overflow_delta); > + overflow_left = pmu_icount_ticks_to_ns(overflow_left) ; > + } else { > + return -1; > + } > + overflow_at = (uint64_t)qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + > overflow_ns; > + > + if (overflow_at > INT64_MAX) { > + overflow_left += overflow_at - INT64_MAX; > + counter->irq_overflow_left = overflow_left; > + overflow_at = INT64_MAX; > + } > + timer_mod_anticipate_ns(cpu->pmu_timer, overflow_at); > + > + return 0; > +} > + > + > +int riscv_pmu_init(RISCVCPU *cpu, int num_counters) > +{ > + if (num_counters > (RV_MAX_MHPMCOUNTERS - 3)) { > + return -1; > + } > + > + cpu->pmu_event_ctr_map = g_hash_table_new(g_direct_hash, > g_direct_equal); > + if (!cpu->pmu_event_ctr_map) { > + /* PMU support can not be enabled */ > + qemu_log_mask(LOG_UNIMP, "PMU events can't be supported\n"); > + cpu->cfg.pmu_num = 0; > + return -1; > + } > + > + /* Create a bitmask of available programmable counters */ > + cpu->pmu_avail_ctrs = MAKE_32BIT_MASK(3, num_counters); > + > + return 0; > } > diff --git a/target/riscv/pmu.h b/target/riscv/pmu.h > index 58a5bc3a4089..036653627f78 100644 > --- a/target/riscv/pmu.h > +++ b/target/riscv/pmu.h > @@ -26,3 +26,10 @@ 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); > +int riscv_pmu_init(RISCVCPU *cpu, int num_counters); > +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); > +int riscv_pmu_setup_timer(CPURISCVState *env, uint64_t value, > + uint32_t ctr_idx); > -- > 2.30.2 > > >
On Thu, Mar 17, 2022 at 12:38 AM Frank Chang <frank.chang@sifive.com> wrote: > > On Fri, Mar 4, 2022 at 8:06 AM Atish Patra <atishp@rivosinc.com> wrote: >> >> The Sscofpmf ('Ss' for Privileged arch and Supervisor-level extensions, >> and 'cofpmf' for Count OverFlow and Privilege Mode Filtering) >> extension allows the perf to handle overflow interrupts and filtering >> support. This patch provides a framework for programmable >> counters to leverage the extension. As the extension doesn't have any >> provision for the overflow bit for fixed counters, the fixed events >> can also be monitoring using programmable counters. The underlying >> counters for cycle and instruction counters are always running. Thus, >> a separate timer device is programmed to handle the overflow. >> >> Signed-off-by: Atish Patra <atish.patra@wdc.com> >> Signed-off-by: Atish Patra <atishp@rivosinc.com> >> --- >> target/riscv/cpu.c | 9 ++ >> target/riscv/cpu.h | 25 +++ >> target/riscv/cpu_bits.h | 55 +++++++ >> target/riscv/csr.c | 157 ++++++++++++++++-- >> target/riscv/pmu.c | 346 +++++++++++++++++++++++++++++++++++++++- >> target/riscv/pmu.h | 7 + >> 6 files changed, 588 insertions(+), 11 deletions(-) >> >> diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c >> index 2d2b0b04fbc8..e13d2f81a05c 100644 >> --- a/target/riscv/cpu.c >> +++ b/target/riscv/cpu.c >> @@ -22,6 +22,7 @@ >> #include "qemu/ctype.h" >> #include "qemu/log.h" >> #include "cpu.h" >> +#include "pmu.h" >> #include "internals.h" >> #include "exec/exec-all.h" >> #include "qapi/error.h" >> @@ -689,6 +690,13 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) >> set_misa(env, env->misa_mxl, ext); >> } >> >> + if (cpu->cfg.pmu_num) { >> + if (!riscv_pmu_init(cpu, cpu->cfg.pmu_num) && cpu->cfg.ext_sscofpmf) { >> + cpu->pmu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, >> + riscv_pmu_timer_cb, cpu); >> + } >> + } >> + >> riscv_cpu_register_gdb_regs_for_features(cs); >> >> qemu_init_vcpu(cs); >> @@ -780,6 +788,7 @@ static Property riscv_cpu_properties[] = { >> DEFINE_PROP_BOOL("v", RISCVCPU, cfg.ext_v, false), >> DEFINE_PROP_BOOL("h", RISCVCPU, cfg.ext_h, true), >> DEFINE_PROP_UINT8("pmu-num", RISCVCPU, cfg.pmu_num, 16), >> + DEFINE_PROP_BOOL("sscofpmf", RISCVCPU, cfg.ext_sscofpmf, false), >> DEFINE_PROP_BOOL("Zifencei", RISCVCPU, cfg.ext_ifencei, true), >> DEFINE_PROP_BOOL("Zicsr", RISCVCPU, cfg.ext_icsr, true), >> DEFINE_PROP_BOOL("Zfh", RISCVCPU, cfg.ext_zfh, false), >> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h >> index b49adcccd85d..eaa5a73897eb 100644 >> --- a/target/riscv/cpu.h >> +++ b/target/riscv/cpu.h >> @@ -129,6 +129,8 @@ typedef struct PMUCTRState { >> /* Snapshort value of a counter in RV32 */ >> target_ulong mhpmcounterh_prev; >> bool started; >> + /* Value beyond UINT32_MAX/UINT64_MAX before overflow interrupt trigger */ >> + target_ulong irq_overflow_left; >> } PMUCTRState; >> >> struct CPURISCVState { >> @@ -281,6 +283,9 @@ struct CPURISCVState { >> /* PMU event selector configured values. First three are unused*/ >> target_ulong mhpmevent_val[RV_MAX_MHPMEVENTS]; >> >> + /* PMU event selector configured values for RV32*/ >> + target_ulong mhpmeventh_val[RV_MAX_MHPMEVENTS]; >> + >> target_ulong sscratch; >> target_ulong mscratch; >> >> @@ -400,6 +405,7 @@ struct RISCVCPUConfig { >> bool ext_zhinxmin; >> bool ext_zve32f; >> bool ext_zve64f; >> + bool ext_sscofpmf; >> >> /* Vendor-specific custom extensions */ >> bool ext_XVentanaCondOps; >> @@ -438,6 +444,12 @@ struct RISCVCPU { >> >> /* Configuration Settings */ >> RISCVCPUConfig cfg; >> + >> + QEMUTimer *pmu_timer; >> + /* A bitmask of Available programmable counters */ >> + uint32_t pmu_avail_ctrs; >> + /* Mapping of events to counters */ >> + GHashTable *pmu_event_ctr_map; >> }; >> >> static inline int riscv_has_ext(CPURISCVState *env, target_ulong ext) >> @@ -697,6 +709,19 @@ enum { >> CSR_TABLE_SIZE = 0x1000 >> }; >> >> +/** >> + * The event id are encoded based on the encoding specified in the >> + * SBI specification v0.3 >> + */ >> + >> +enum riscv_pmu_event_idx { >> + RISCV_PMU_EVENT_HW_CPU_CYCLES = 0x01, >> + RISCV_PMU_EVENT_HW_INSTRUCTIONS = 0x02, >> + RISCV_PMU_EVENT_CACHE_DTLB_READ_MISS = 0x10019, >> + RISCV_PMU_EVENT_CACHE_DTLB_WRITE_MISS = 0x1001B, >> + RISCV_PMU_EVENT_CACHE_ITLB_PREFETCH_MISS = 0x10021, >> +}; >> + >> /* CSR function table */ >> extern riscv_csr_operations csr_ops[CSR_TABLE_SIZE]; >> >> diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h >> index 48b39e6d52a7..da78e2704081 100644 >> --- a/target/riscv/cpu_bits.h >> +++ b/target/riscv/cpu_bits.h >> @@ -400,6 +400,37 @@ >> #define CSR_MHPMEVENT29 0x33d >> #define CSR_MHPMEVENT30 0x33e >> #define CSR_MHPMEVENT31 0x33f >> + >> +#define CSR_MHPMEVENT3H 0x723 >> +#define CSR_MHPMEVENT4H 0x724 >> +#define CSR_MHPMEVENT5H 0x725 >> +#define CSR_MHPMEVENT6H 0x726 >> +#define CSR_MHPMEVENT7H 0x727 >> +#define CSR_MHPMEVENT8H 0x728 >> +#define CSR_MHPMEVENT9H 0x729 >> +#define CSR_MHPMEVENT10H 0x72a >> +#define CSR_MHPMEVENT11H 0x72b >> +#define CSR_MHPMEVENT12H 0x72c >> +#define CSR_MHPMEVENT13H 0x72d >> +#define CSR_MHPMEVENT14H 0x72e >> +#define CSR_MHPMEVENT15H 0x72f >> +#define CSR_MHPMEVENT16H 0x730 >> +#define CSR_MHPMEVENT17H 0x731 >> +#define CSR_MHPMEVENT18H 0x732 >> +#define CSR_MHPMEVENT19H 0x733 >> +#define CSR_MHPMEVENT20H 0x734 >> +#define CSR_MHPMEVENT21H 0x735 >> +#define CSR_MHPMEVENT22H 0x736 >> +#define CSR_MHPMEVENT23H 0x737 >> +#define CSR_MHPMEVENT24H 0x738 >> +#define CSR_MHPMEVENT25H 0x739 >> +#define CSR_MHPMEVENT26H 0x73a >> +#define CSR_MHPMEVENT27H 0x73b >> +#define CSR_MHPMEVENT28H 0x73c >> +#define CSR_MHPMEVENT29H 0x73d >> +#define CSR_MHPMEVENT30H 0x73e >> +#define CSR_MHPMEVENT31H 0x73f >> + >> #define CSR_MHPMCOUNTER3H 0xb83 >> #define CSR_MHPMCOUNTER4H 0xb84 >> #define CSR_MHPMCOUNTER5H 0xb85 >> @@ -461,6 +492,7 @@ >> #define CSR_VSMTE 0x2c0 >> #define CSR_VSPMMASK 0x2c1 >> #define CSR_VSPMBASE 0x2c2 >> +#define CSR_SCOUNTOVF 0xda0 >> >> /* mstatus CSR bits */ >> #define MSTATUS_UIE 0x00000001 >> @@ -635,6 +667,7 @@ typedef enum RISCVException { >> #define IRQ_VS_EXT 10 >> #define IRQ_M_EXT 11 >> #define IRQ_S_GEXT 12 >> +#define IRQ_PMU_OVF 13 >> #define IRQ_LOCAL_MAX 16 >> #define IRQ_LOCAL_GUEST_MAX (TARGET_LONG_BITS - 1) >> >> @@ -652,11 +685,13 @@ typedef enum RISCVException { >> #define MIP_VSEIP (1 << IRQ_VS_EXT) >> #define MIP_MEIP (1 << IRQ_M_EXT) >> #define MIP_SGEIP (1 << IRQ_S_GEXT) >> +#define MIP_LCOFIP (1 << IRQ_PMU_OVF) >> >> /* sip masks */ >> #define SIP_SSIP MIP_SSIP >> #define SIP_STIP MIP_STIP >> #define SIP_SEIP MIP_SEIP >> +#define SIP_LCOFIP MIP_LCOFIP >> >> /* MIE masks */ >> #define MIE_SEIE (1 << IRQ_S_EXT) >> @@ -804,4 +839,24 @@ typedef enum RISCVException { >> #define HVICTL_VALID_MASK \ >> (HVICTL_VTI | HVICTL_IID | HVICTL_IPRIOM | HVICTL_IPRIO) >> >> +/* PMU related bits */ >> +#define MIE_LCOFIE (1 << IRQ_PMU_OVF) >> + >> +#define MHPMEVENT_BIT_OF BIT(63) > > > Hi Atish, > > I think it's better to use BIT_ULL() for value >= 32. > BIT() is defined to: (1UL << (nr)), > which might cause the overflow issue on some platforms (e.g. Windows). > Sure. Fixed it. > Regards, > Frank Chang > >> +#define MHPMEVENTH_BIT_OF BIT(31) >> +#define MHPMEVENT_BIT_MINH BIT(62) >> +#define MHPMEVENTH_BIT_MINH BIT(30) >> +#define MHPMEVENT_BIT_SINH BIT(61) >> +#define MHPMEVENTH_BIT_SINH BIT(29) >> +#define MHPMEVENT_BIT_UINH BIT(60) >> +#define MHPMEVENTH_BIT_UINH BIT(28) >> +#define MHPMEVENT_BIT_VSINH BIT(59) >> +#define MHPMEVENTH_BIT_VSINH BIT(27) >> +#define MHPMEVENT_BIT_VUINH BIT(58) >> +#define MHPMEVENTH_BIT_VUINH BIT(26) >> + >> +#define MHPMEVENT_SSCOF_MASK _ULL(0xFFFF000000000000) >> +#define MHPMEVENT_IDX_MASK 0xFFFFF >> +#define MHPMEVENT_SSCOF_RESVD 16 >> + >> #endif >> diff --git a/target/riscv/csr.c b/target/riscv/csr.c >> index 3286e1e44455..cfcc1a0882d9 100644 >> --- a/target/riscv/csr.c >> +++ b/target/riscv/csr.c >> @@ -96,13 +96,26 @@ static RISCVException mctr32(CPURISCVState *env, int csrno) >> return mctr(env, csrno); >> } >> >> +static RISCVException sscofpmf(CPURISCVState *env, int csrno) >> +{ >> + #if !defined(CONFIG_USER_ONLY) >> + CPUState *cs = env_cpu(env); >> + RISCVCPU *cpu = RISCV_CPU(cs); >> + >> + if (!cpu->cfg.ext_sscofpmf) { >> + return RISCV_EXCP_ILLEGAL_INST; >> + } >> +#endif >> + return RISCV_EXCP_NONE; >> +} >> + >> static RISCVException ctr(CPURISCVState *env, int csrno) >> { >> #if !defined(CONFIG_USER_ONLY) >> CPUState *cs = env_cpu(env); >> RISCVCPU *cpu = RISCV_CPU(cs); >> int ctr_index; >> - int base_csrno = CSR_HPMCOUNTER3; >> + int base_csrno = CSR_CYCLE; >> bool rv32 = riscv_cpu_mxl(env) == MXL_RV32 ? true : false; >> >> if (rv32 && csrno >= CSR_CYCLEH) { >> @@ -111,11 +124,18 @@ static RISCVException ctr(CPURISCVState *env, int csrno) >> } >> ctr_index = csrno - base_csrno; >> >> - if (!cpu->cfg.pmu_num || ctr_index >= (cpu->cfg.pmu_num)) { >> + if ((csrno >= CSR_CYCLE && csrno <= CSR_INSTRET) || >> + (csrno >= CSR_CYCLEH && csrno <= CSR_INSTRETH)) { >> + goto skip_ext_pmu_check; >> + } >> + >> + if ((!cpu->cfg.pmu_num || !(cpu->pmu_avail_ctrs & BIT(ctr_index)))) { >> /* No counter is enabled in PMU or the counter is out of range */ >> return RISCV_EXCP_ILLEGAL_INST; >> } >> >> +skip_ext_pmu_check: >> + >> if (env->priv == PRV_S) { >> switch (csrno) { >> case CSR_CYCLE: >> @@ -134,7 +154,6 @@ static RISCVException ctr(CPURISCVState *env, int csrno) >> } >> break; >> case CSR_HPMCOUNTER3...CSR_HPMCOUNTER31: >> - ctr_index = csrno - CSR_CYCLE; >> if (!get_field(env->mcounteren, 1 << ctr_index)) { >> return RISCV_EXCP_ILLEGAL_INST; >> } >> @@ -158,7 +177,6 @@ static RISCVException ctr(CPURISCVState *env, int csrno) >> } >> break; >> case CSR_HPMCOUNTER3H...CSR_HPMCOUNTER31H: >> - ctr_index = csrno - CSR_CYCLEH; >> if (!get_field(env->mcounteren, 1 << ctr_index)) { >> return RISCV_EXCP_ILLEGAL_INST; >> } >> @@ -188,7 +206,6 @@ static RISCVException ctr(CPURISCVState *env, int csrno) >> } >> break; >> case CSR_HPMCOUNTER3...CSR_HPMCOUNTER31: >> - ctr_index = csrno - CSR_CYCLE; >> if (!get_field(env->hcounteren, 1 << ctr_index) && >> get_field(env->mcounteren, 1 << ctr_index)) { >> return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; >> @@ -216,7 +233,6 @@ static RISCVException ctr(CPURISCVState *env, int csrno) >> } >> break; >> case CSR_HPMCOUNTER3H...CSR_HPMCOUNTER31H: >> - ctr_index = csrno - CSR_CYCLEH; >> if (!get_field(env->hcounteren, 1 << ctr_index) && >> get_field(env->mcounteren, 1 << ctr_index)) { >> return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; >> @@ -585,11 +601,36 @@ static int write_mhpmevent(CPURISCVState *env, int csrno, target_ulong val) >> { >> int evt_index = csrno - CSR_MCOUNTINHIBIT; >> >> + if (riscv_cpu_mxl(env) != MXL_RV32) { >> + riscv_pmu_update_event_map(env, val, evt_index); >> + } >> env->mhpmevent_val[evt_index] = val; >> >> return RISCV_EXCP_NONE; >> } >> >> +static int read_mhpmeventh(CPURISCVState *env, int csrno, target_ulong *val) >> +{ >> + int evt_index = csrno - CSR_MHPMEVENT3H + 3; >> + >> + *val = env->mhpmevent_val[evt_index]; >> + >> + return RISCV_EXCP_NONE; >> +} >> + >> +static int write_mhpmeventh(CPURISCVState *env, int csrno, target_ulong val) >> +{ >> + int evt_index = csrno - CSR_MHPMEVENT3H + 3; >> + uint64_t mhpmevth_val = val; >> + uint64_t mhpmevt_val = env->mhpmevent_val[evt_index]; >> + >> + mhpmevt_val = mhpmevt_val | (mhpmevth_val << 32); >> + riscv_pmu_update_event_map(env, mhpmevt_val, evt_index); >> + env->mhpmeventh_val[evt_index] = val; >> + >> + return RISCV_EXCP_NONE; >> +} >> + >> static int write_mhpmcounter(CPURISCVState *env, int csrno, target_ulong val) >> { >> int ctr_idx = csrno - CSR_MCYCLE; >> @@ -599,7 +640,10 @@ static int write_mhpmcounter(CPURISCVState *env, int csrno, target_ulong val) >> if (riscv_pmu_ctr_monitor_cycles(env, ctr_idx) || >> riscv_pmu_ctr_monitor_instructions(env, ctr_idx)) { >> counter->mhpmcounter_prev = get_ticks(false); >> - } else { >> + if (ctr_idx > 2) { >> + riscv_pmu_setup_timer(env, val, ctr_idx); >> + } >> + } else { >> /* Other counters can keep incrementing from the given value */ >> counter->mhpmcounter_prev = val; >> } >> @@ -611,11 +655,17 @@ static int write_mhpmcounterh(CPURISCVState *env, int csrno, target_ulong val) >> { >> int ctr_idx = csrno - CSR_MCYCLEH; >> PMUCTRState *counter = &env->pmu_ctrs[ctr_idx]; >> + uint64_t mhpmctr_val = counter->mhpmcounter_val; >> + uint64_t mhpmctrh_val = val; >> >> counter->mhpmcounterh_val = val; >> + mhpmctr_val = mhpmctr_val | (mhpmctrh_val << 32); >> if (riscv_pmu_ctr_monitor_cycles(env, ctr_idx) || >> riscv_pmu_ctr_monitor_instructions(env, ctr_idx)) { >> counter->mhpmcounterh_prev = get_ticks(true); >> + if (ctr_idx > 2) { >> + riscv_pmu_setup_timer(env, mhpmctr_val, ctr_idx); >> + } >> } else { >> counter->mhpmcounterh_prev = val; >> } >> @@ -688,6 +738,32 @@ static int read_hpmcounterh(CPURISCVState *env, int csrno, target_ulong *val) >> return riscv_pmu_read_ctr(env, val, true, ctr_index); >> } >> >> +static int read_scountovf(CPURISCVState *env, int csrno, target_ulong *val) >> +{ >> + int mhpmevt_start = CSR_MHPMEVENT3 - CSR_MCOUNTINHIBIT; >> + int i; >> + *val = 0; >> + target_ulong *mhpm_evt_val; >> + uint64_t of_bit_mask; >> + >> + if (riscv_cpu_mxl(env) == MXL_RV32) { >> + mhpm_evt_val = env->mhpmeventh_val; >> + of_bit_mask = MHPMEVENTH_BIT_OF; >> + } else { >> + mhpm_evt_val = env->mhpmevent_val; >> + of_bit_mask = MHPMEVENT_BIT_OF; >> + } >> + >> + for (i = mhpmevt_start; i < RV_MAX_MHPMEVENTS; i++) { >> + if ((get_field(env->mcounteren, BIT(i))) && >> + (mhpm_evt_val[i] & of_bit_mask)) { >> + *val |= BIT(i); >> + } >> + } >> + >> + return RISCV_EXCP_NONE; >> +} >> + >> #if defined(CONFIG_USER_ONLY) >> static RISCVException read_time(CPURISCVState *env, int csrno, >> target_ulong *val) >> @@ -734,7 +810,8 @@ static RISCVException read_timeh(CPURISCVState *env, int csrno, >> /* Machine constants */ >> >> #define M_MODE_INTERRUPTS ((uint64_t)(MIP_MSIP | MIP_MTIP | MIP_MEIP)) >> -#define S_MODE_INTERRUPTS ((uint64_t)(MIP_SSIP | MIP_STIP | MIP_SEIP)) >> +#define S_MODE_INTERRUPTS ((uint64_t)(MIP_SSIP | MIP_STIP | MIP_SEIP | \ >> + MIP_LCOFIP)) >> #define VS_MODE_INTERRUPTS ((uint64_t)(MIP_VSSIP | MIP_VSTIP | MIP_VSEIP)) >> #define HS_MODE_INTERRUPTS ((uint64_t)(MIP_SGEIP | VS_MODE_INTERRUPTS)) >> >> @@ -775,7 +852,8 @@ static const target_ulong vs_delegable_excps = DELEGABLE_EXCPS & >> static const target_ulong sstatus_v1_10_mask = SSTATUS_SIE | SSTATUS_SPIE | >> SSTATUS_UIE | SSTATUS_UPIE | SSTATUS_SPP | SSTATUS_FS | SSTATUS_XS | >> SSTATUS_SUM | SSTATUS_MXR | SSTATUS_VS; >> -static const target_ulong sip_writable_mask = SIP_SSIP | MIP_USIP | MIP_UEIP; >> +static const target_ulong sip_writable_mask = SIP_SSIP | MIP_USIP | MIP_UEIP | >> + SIP_LCOFIP; >> static const target_ulong hip_writable_mask = MIP_VSSIP; >> static const target_ulong hvip_writable_mask = MIP_VSSIP | MIP_VSTIP | MIP_VSEIP; >> static const target_ulong vsip_writable_mask = MIP_VSSIP; >> @@ -3814,6 +3892,65 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { >> [CSR_MHPMEVENT31] = { "mhpmevent31", any, read_mhpmevent, >> write_mhpmevent }, >> >> + [CSR_MHPMEVENT3H] = { "mhpmevent3h", sscofpmf, read_mhpmeventh, >> + write_mhpmeventh}, >> + [CSR_MHPMEVENT4H] = { "mhpmevent4h", sscofpmf, read_mhpmeventh, >> + write_mhpmeventh}, >> + [CSR_MHPMEVENT5H] = { "mhpmevent5h", sscofpmf, read_mhpmeventh, >> + write_mhpmeventh}, >> + [CSR_MHPMEVENT6H] = { "mhpmevent6h", sscofpmf, read_mhpmeventh, >> + write_mhpmeventh}, >> + [CSR_MHPMEVENT7H] = { "mhpmevent7h", sscofpmf, read_mhpmeventh, >> + write_mhpmeventh}, >> + [CSR_MHPMEVENT8H] = { "mhpmevent8h", sscofpmf, read_mhpmeventh, >> + write_mhpmeventh}, >> + [CSR_MHPMEVENT9H] = { "mhpmevent9h", sscofpmf, read_mhpmeventh, >> + write_mhpmeventh}, >> + [CSR_MHPMEVENT10H] = { "mhpmevent10h", sscofpmf, read_mhpmeventh, >> + write_mhpmeventh}, >> + [CSR_MHPMEVENT11H] = { "mhpmevent11h", sscofpmf, read_mhpmeventh, >> + write_mhpmeventh}, >> + [CSR_MHPMEVENT12H] = { "mhpmevent12h", sscofpmf, read_mhpmeventh, >> + write_mhpmeventh}, >> + [CSR_MHPMEVENT13H] = { "mhpmevent13h", sscofpmf, read_mhpmeventh, >> + write_mhpmeventh}, >> + [CSR_MHPMEVENT14H] = { "mhpmevent14h", sscofpmf, read_mhpmeventh, >> + write_mhpmeventh}, >> + [CSR_MHPMEVENT15H] = { "mhpmevent15h", sscofpmf, read_mhpmeventh, >> + write_mhpmeventh}, >> + [CSR_MHPMEVENT16H] = { "mhpmevent16h", sscofpmf, read_mhpmeventh, >> + write_mhpmeventh}, >> + [CSR_MHPMEVENT17H] = { "mhpmevent17h", sscofpmf, read_mhpmeventh, >> + write_mhpmeventh}, >> + [CSR_MHPMEVENT18H] = { "mhpmevent18h", sscofpmf, read_mhpmeventh, >> + write_mhpmeventh}, >> + [CSR_MHPMEVENT19H] = { "mhpmevent19h", sscofpmf, read_mhpmeventh, >> + write_mhpmeventh}, >> + [CSR_MHPMEVENT20H] = { "mhpmevent20h", sscofpmf, read_mhpmeventh, >> + write_mhpmeventh}, >> + [CSR_MHPMEVENT21H] = { "mhpmevent21h", sscofpmf, read_mhpmeventh, >> + write_mhpmeventh}, >> + [CSR_MHPMEVENT22H] = { "mhpmevent22h", sscofpmf, read_mhpmeventh, >> + write_mhpmeventh}, >> + [CSR_MHPMEVENT23H] = { "mhpmevent23h", sscofpmf, read_mhpmeventh, >> + write_mhpmeventh}, >> + [CSR_MHPMEVENT24H] = { "mhpmevent24h", sscofpmf, read_mhpmeventh, >> + write_mhpmeventh}, >> + [CSR_MHPMEVENT25H] = { "mhpmevent25h", sscofpmf, read_mhpmeventh, >> + write_mhpmeventh}, >> + [CSR_MHPMEVENT26H] = { "mhpmevent26h", sscofpmf, read_mhpmeventh, >> + write_mhpmeventh}, >> + [CSR_MHPMEVENT27H] = { "mhpmevent27h", sscofpmf, read_mhpmeventh, >> + write_mhpmeventh}, >> + [CSR_MHPMEVENT28H] = { "mhpmevent28h", sscofpmf, read_mhpmeventh, >> + write_mhpmeventh}, >> + [CSR_MHPMEVENT29H] = { "mhpmevent29h", sscofpmf, read_mhpmeventh, >> + write_mhpmeventh}, >> + [CSR_MHPMEVENT30H] = { "mhpmevent30h", sscofpmf, read_mhpmeventh, >> + write_mhpmeventh}, >> + [CSR_MHPMEVENT31H] = { "mhpmevent31h", sscofpmf, read_mhpmeventh, >> + write_mhpmeventh}, >> + >> [CSR_HPMCOUNTER3H] = { "hpmcounter3h", ctr32, read_hpmcounterh }, >> [CSR_HPMCOUNTER4H] = { "hpmcounter4h", ctr32, read_hpmcounterh }, >> [CSR_HPMCOUNTER5H] = { "hpmcounter5h", ctr32, read_hpmcounterh }, >> @@ -3902,5 +4039,7 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { >> write_mhpmcounterh }, >> [CSR_MHPMCOUNTER31H] = { "mhpmcounter31h", mctr32, read_hpmcounterh, >> write_mhpmcounterh }, >> + [CSR_SCOUNTOVF] = { "scountovf", sscofpmf, read_scountovf }, >> + >> #endif /* !CONFIG_USER_ONLY */ >> }; >> diff --git a/target/riscv/pmu.c b/target/riscv/pmu.c >> index 000fe8da45ef..5b212d2eb630 100644 >> --- a/target/riscv/pmu.c >> +++ b/target/riscv/pmu.c >> @@ -20,13 +20,355 @@ >> #include "cpu.h" >> #include "pmu.h" >> >> +#define RISCV_TIMEBASE_FREQ 1000000000 /* 1Ghz */ >> +#define MAKE_32BIT_MASK(shift, length) \ >> + (((uint32_t)(~0UL) >> (32 - (length))) << (shift)) >> + >> +static bool riscv_pmu_counter_valid(RISCVCPU *cpu, uint32_t ctr_idx) >> +{ >> + if (ctr_idx < 3 || ctr_idx >= RV_MAX_MHPMCOUNTERS || >> + !(cpu->pmu_avail_ctrs & BIT(ctr_idx))) { >> + return false; >> + } else { >> + return true; >> + } >> +} >> + >> +static bool riscv_pmu_counter_enabled(RISCVCPU *cpu, uint32_t ctr_idx) >> +{ >> + CPURISCVState *env = &cpu->env; >> + >> + if (!riscv_pmu_counter_valid(cpu, ctr_idx) || >> + !get_field(env->mcounteren, BIT(ctr_idx))) { >> + return false; >> + } else { >> + return true; >> + } >> +} >> + >> +static int riscv_pmu_incr_ctr_rv32(RISCVCPU *cpu, uint32_t ctr_idx) >> +{ >> + CPURISCVState *env = &cpu->env; >> + target_ulong max_val = UINT32_MAX; >> + PMUCTRState *counter = &env->pmu_ctrs[ctr_idx]; >> + >> + /* Privilege mode filtering */ >> + if ((env->priv == PRV_M && >> + (env->mhpmeventh_val[ctr_idx] & MHPMEVENTH_BIT_MINH)) || >> + (env->priv == PRV_S && >> + (env->mhpmeventh_val[ctr_idx] & MHPMEVENTH_BIT_SINH)) || >> + (env->priv == PRV_U && >> + (env->mhpmeventh_val[ctr_idx] & MHPMEVENTH_BIT_UINH))) { >> + return 0; >> + } >> + >> + /* Handle the overflow scenario */ >> + if (counter->mhpmcounter_val == max_val) { >> + if (counter->mhpmcounterh_val == max_val) { >> + counter->mhpmcounter_val = 0; >> + counter->mhpmcounterh_val = 0; >> + /* Generate interrupt only if OF bit is clear */ >> + if (!(env->mhpmeventh_val[ctr_idx] & MHPMEVENTH_BIT_OF)) { >> + env->mhpmeventh_val[ctr_idx] |= MHPMEVENTH_BIT_OF; >> + riscv_cpu_update_mip(cpu, MIP_LCOFIP, BOOL_TO_MASK(1)); >> + } >> + } else { >> + counter->mhpmcounterh_val++; >> + } >> + } else { >> + counter->mhpmcounter_val++; >> + } >> + >> + return 0; >> +} >> + >> +static int riscv_pmu_incr_ctr_rv64(RISCVCPU *cpu, uint32_t ctr_idx) >> +{ >> + CPURISCVState *env = &cpu->env; >> + PMUCTRState *counter = &env->pmu_ctrs[ctr_idx]; >> + uint64_t max_val = UINT64_MAX; >> + >> + /* Privilege mode filtering */ >> + if ((env->priv == PRV_M && >> + (env->mhpmevent_val[ctr_idx] & MHPMEVENT_BIT_MINH)) || >> + (env->priv == PRV_S && >> + (env->mhpmevent_val[ctr_idx] & MHPMEVENT_BIT_SINH)) || >> + (env->priv == PRV_U && >> + (env->mhpmevent_val[ctr_idx] & MHPMEVENT_BIT_UINH))) { >> + return 0; >> + } >> + >> + /* Handle the overflow scenario */ >> + if (counter->mhpmcounter_val == max_val) { >> + counter->mhpmcounter_val = 0; >> + /* Generate interrupt only if OF bit is clear */ >> + if (!(env->mhpmevent_val[ctr_idx] & MHPMEVENT_BIT_OF)) { >> + env->mhpmevent_val[ctr_idx] |= MHPMEVENT_BIT_OF; >> + riscv_cpu_update_mip(cpu, MIP_LCOFIP, BOOL_TO_MASK(1)); >> + } >> + } else { >> + counter->mhpmcounter_val++; >> + } >> + return 0; >> +} >> + >> +int riscv_pmu_incr_ctr(RISCVCPU *cpu, enum riscv_pmu_event_idx event_idx) >> +{ >> + uint32_t ctr_idx; >> + int ret; >> + CPURISCVState *env = &cpu->env; >> + gpointer value; >> + >> + value = g_hash_table_lookup(cpu->pmu_event_ctr_map, >> + GUINT_TO_POINTER(event_idx)); >> + if (!value) { >> + return -1; >> + } >> + >> + ctr_idx = GPOINTER_TO_UINT(value); >> + if (!riscv_pmu_counter_enabled(cpu, ctr_idx) || >> + get_field(env->mcountinhibit, BIT(ctr_idx))) { >> + return -1; >> + } >> + >> + if (riscv_cpu_mxl(env) == MXL_RV32) { >> + ret = riscv_pmu_incr_ctr_rv32(cpu, ctr_idx); >> + } else { >> + ret = riscv_pmu_incr_ctr_rv64(cpu, ctr_idx); >> + } >> + >> + return ret; >> +} >> + >> bool riscv_pmu_ctr_monitor_instructions(CPURISCVState *env, >> uint32_t target_ctr) >> { >> - return (target_ctr == 0) ? true : false; >> + RISCVCPU *cpu; >> + uint32_t event_idx; >> + uint32_t ctr_idx; >> + >> + /* Fixed instret counter */ >> + if (target_ctr == 2) { >> + return true; >> + } >> + >> + cpu = RISCV_CPU(env_cpu(env)); >> + event_idx = RISCV_PMU_EVENT_HW_INSTRUCTIONS; >> + ctr_idx = GPOINTER_TO_UINT(g_hash_table_lookup(cpu->pmu_event_ctr_map, >> + GUINT_TO_POINTER(event_idx))); >> + if (!ctr_idx) { >> + return false; >> + } >> + >> + return target_ctr == ctr_idx ? true : false; >> } >> >> bool riscv_pmu_ctr_monitor_cycles(CPURISCVState *env, uint32_t target_ctr) >> { >> - return (target_ctr == 2) ? true : false; >> + RISCVCPU *cpu; >> + uint32_t event_idx; >> + uint32_t ctr_idx; >> + >> + /* Fixed mcycle counter */ >> + if (target_ctr == 0) { >> + return true; >> + } >> + >> + cpu = RISCV_CPU(env_cpu(env)); >> + event_idx = RISCV_PMU_EVENT_HW_CPU_CYCLES; >> + ctr_idx = GPOINTER_TO_UINT(g_hash_table_lookup(cpu->pmu_event_ctr_map, >> + GUINT_TO_POINTER(event_idx))); >> + >> + /* Counter zero is not used for event_ctr_map */ >> + if (!ctr_idx) { >> + return false; >> + } >> + >> + return (target_ctr == ctr_idx) ? true : false; >> +} >> + >> +static gboolean pmu_remove_event_map(gpointer key, gpointer value, >> + gpointer udata) >> +{ >> + return (GPOINTER_TO_UINT(value) == GPOINTER_TO_UINT(udata)) ? true : false; >> +} >> + >> +static int64_t pmu_icount_ticks_to_ns(int64_t value) >> +{ >> + int64_t ret = 0; >> + >> + if (icount_enabled()) { >> + ret = icount_to_ns(value); >> + } else { >> + ret = (NANOSECONDS_PER_SECOND / RISCV_TIMEBASE_FREQ) * value; >> + } >> + >> + return ret; >> +} >> + >> +int riscv_pmu_update_event_map(CPURISCVState *env, uint64_t value, >> + uint32_t ctr_idx) >> +{ >> + uint32_t event_idx; >> + RISCVCPU *cpu = RISCV_CPU(env_cpu(env)); >> + >> + if (!riscv_pmu_counter_valid(cpu, ctr_idx)) { >> + return -1; >> + } >> + >> + /** >> + * Expected mhpmevent value is zero for reset case. Remove the current >> + * mapping. >> + */ >> + if (!value) { >> + g_hash_table_foreach_remove(cpu->pmu_event_ctr_map, >> + pmu_remove_event_map, >> + GUINT_TO_POINTER(ctr_idx)); >> + return 0; >> + } >> + >> + event_idx = value & MHPMEVENT_IDX_MASK; >> + if (g_hash_table_lookup(cpu->pmu_event_ctr_map, >> + GUINT_TO_POINTER(event_idx))) { >> + return 0; >> + } >> + >> + switch (event_idx) { >> + case RISCV_PMU_EVENT_HW_CPU_CYCLES: >> + case RISCV_PMU_EVENT_HW_INSTRUCTIONS: >> + case RISCV_PMU_EVENT_CACHE_DTLB_READ_MISS: >> + case RISCV_PMU_EVENT_CACHE_DTLB_WRITE_MISS: >> + case RISCV_PMU_EVENT_CACHE_ITLB_PREFETCH_MISS: >> + break; >> + default: >> + /* We don't support any raw events right now */ >> + return -1; >> + } >> + g_hash_table_insert(cpu->pmu_event_ctr_map, GUINT_TO_POINTER(event_idx), >> + GUINT_TO_POINTER(ctr_idx)); >> + >> + return 0; >> +} >> + >> +static void pmu_timer_trigger_irq(RISCVCPU *cpu, >> + enum riscv_pmu_event_idx evt_idx) >> +{ >> + uint32_t ctr_idx; >> + CPURISCVState *env = &cpu->env; >> + PMUCTRState *counter; >> + target_ulong *mhpmevent_val; >> + uint64_t of_bit_mask; >> + int64_t irq_trigger_at; >> + >> + if (evt_idx != RISCV_PMU_EVENT_HW_CPU_CYCLES && >> + evt_idx != RISCV_PMU_EVENT_HW_INSTRUCTIONS) { >> + return; >> + } >> + >> + ctr_idx = GPOINTER_TO_UINT(g_hash_table_lookup(cpu->pmu_event_ctr_map, >> + GUINT_TO_POINTER(evt_idx))); >> + if (!riscv_pmu_counter_enabled(cpu, ctr_idx)) { >> + return; >> + } >> + >> + if (riscv_cpu_mxl(env) == MXL_RV32) { >> + mhpmevent_val = &env->mhpmeventh_val[ctr_idx]; >> + of_bit_mask = MHPMEVENTH_BIT_OF; >> + } else { >> + mhpmevent_val = &env->mhpmevent_val[ctr_idx]; >> + of_bit_mask = MHPMEVENT_BIT_OF; >> + } >> + >> + counter = &env->pmu_ctrs[ctr_idx]; >> + if (counter->irq_overflow_left > 0) { >> + irq_trigger_at = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + >> + counter->irq_overflow_left; >> + timer_mod_anticipate_ns(cpu->pmu_timer, irq_trigger_at); >> + counter->irq_overflow_left = 0; >> + return; >> + } >> + >> + if (cpu->pmu_avail_ctrs & BIT(ctr_idx)) { >> + /* Generate interrupt only if OF bit is clear */ >> + if (!(*mhpmevent_val & of_bit_mask)) { >> + *mhpmevent_val |= of_bit_mask; >> + riscv_cpu_update_mip(cpu, MIP_LCOFIP, BOOL_TO_MASK(1)); >> + } >> + } >> +} >> + >> +/* Timer callback for instret and cycle counter overflow */ >> +void riscv_pmu_timer_cb(void *priv) >> +{ >> + RISCVCPU *cpu = priv; >> + >> + /* Timer event was triggered only for these events */ >> + pmu_timer_trigger_irq(cpu, RISCV_PMU_EVENT_HW_CPU_CYCLES); >> + pmu_timer_trigger_irq(cpu, RISCV_PMU_EVENT_HW_INSTRUCTIONS); >> +} >> + >> +int riscv_pmu_setup_timer(CPURISCVState *env, uint64_t value, uint32_t ctr_idx) >> +{ >> + uint64_t overflow_delta, overflow_at; >> + int64_t overflow_ns, overflow_left = 0; >> + RISCVCPU *cpu = RISCV_CPU(env_cpu(env)); >> + PMUCTRState *counter = &env->pmu_ctrs[ctr_idx]; >> + >> + if (!riscv_pmu_counter_valid(cpu, ctr_idx) || !cpu->cfg.ext_sscofpmf) { >> + return -1; >> + } >> + >> + if (value) { >> + overflow_delta = UINT64_MAX - value + 1; >> + } else { >> + overflow_delta = UINT64_MAX - value; >> + } >> + >> + /** >> + * QEMU supports only int64_t timers while RISC-V counters are uint64_t. >> + * Compute the leftover and save it so that it can be reprogrammed again >> + * when timer expires. >> + */ >> + if (overflow_delta > INT64_MAX) { >> + overflow_left = overflow_delta - INT64_MAX; >> + } >> + >> + if (riscv_pmu_ctr_monitor_cycles(env, ctr_idx) || >> + riscv_pmu_ctr_monitor_instructions(env, ctr_idx)) { >> + overflow_ns = pmu_icount_ticks_to_ns((int64_t)overflow_delta); >> + overflow_left = pmu_icount_ticks_to_ns(overflow_left) ; >> + } else { >> + return -1; >> + } >> + overflow_at = (uint64_t)qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + overflow_ns; >> + >> + if (overflow_at > INT64_MAX) { >> + overflow_left += overflow_at - INT64_MAX; >> + counter->irq_overflow_left = overflow_left; >> + overflow_at = INT64_MAX; >> + } >> + timer_mod_anticipate_ns(cpu->pmu_timer, overflow_at); >> + >> + return 0; >> +} >> + >> + >> +int riscv_pmu_init(RISCVCPU *cpu, int num_counters) >> +{ >> + if (num_counters > (RV_MAX_MHPMCOUNTERS - 3)) { >> + return -1; >> + } >> + >> + cpu->pmu_event_ctr_map = g_hash_table_new(g_direct_hash, g_direct_equal); >> + if (!cpu->pmu_event_ctr_map) { >> + /* PMU support can not be enabled */ >> + qemu_log_mask(LOG_UNIMP, "PMU events can't be supported\n"); >> + cpu->cfg.pmu_num = 0; >> + return -1; >> + } >> + >> + /* Create a bitmask of available programmable counters */ >> + cpu->pmu_avail_ctrs = MAKE_32BIT_MASK(3, num_counters); >> + >> + return 0; >> } >> diff --git a/target/riscv/pmu.h b/target/riscv/pmu.h >> index 58a5bc3a4089..036653627f78 100644 >> --- a/target/riscv/pmu.h >> +++ b/target/riscv/pmu.h >> @@ -26,3 +26,10 @@ 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); >> +int riscv_pmu_init(RISCVCPU *cpu, int num_counters); >> +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); >> +int riscv_pmu_setup_timer(CPURISCVState *env, uint64_t value, >> + uint32_t ctr_idx); >> -- >> 2.30.2 >> >>
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 2d2b0b04fbc8..e13d2f81a05c 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -22,6 +22,7 @@ #include "qemu/ctype.h" #include "qemu/log.h" #include "cpu.h" +#include "pmu.h" #include "internals.h" #include "exec/exec-all.h" #include "qapi/error.h" @@ -689,6 +690,13 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) set_misa(env, env->misa_mxl, ext); } + if (cpu->cfg.pmu_num) { + if (!riscv_pmu_init(cpu, cpu->cfg.pmu_num) && cpu->cfg.ext_sscofpmf) { + cpu->pmu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + riscv_pmu_timer_cb, cpu); + } + } + riscv_cpu_register_gdb_regs_for_features(cs); qemu_init_vcpu(cs); @@ -780,6 +788,7 @@ static Property riscv_cpu_properties[] = { DEFINE_PROP_BOOL("v", RISCVCPU, cfg.ext_v, false), DEFINE_PROP_BOOL("h", RISCVCPU, cfg.ext_h, true), DEFINE_PROP_UINT8("pmu-num", RISCVCPU, cfg.pmu_num, 16), + DEFINE_PROP_BOOL("sscofpmf", RISCVCPU, cfg.ext_sscofpmf, false), DEFINE_PROP_BOOL("Zifencei", RISCVCPU, cfg.ext_ifencei, true), DEFINE_PROP_BOOL("Zicsr", RISCVCPU, cfg.ext_icsr, true), DEFINE_PROP_BOOL("Zfh", RISCVCPU, cfg.ext_zfh, false), diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index b49adcccd85d..eaa5a73897eb 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -129,6 +129,8 @@ typedef struct PMUCTRState { /* Snapshort value of a counter in RV32 */ target_ulong mhpmcounterh_prev; bool started; + /* Value beyond UINT32_MAX/UINT64_MAX before overflow interrupt trigger */ + target_ulong irq_overflow_left; } PMUCTRState; struct CPURISCVState { @@ -281,6 +283,9 @@ struct CPURISCVState { /* PMU event selector configured values. First three are unused*/ target_ulong mhpmevent_val[RV_MAX_MHPMEVENTS]; + /* PMU event selector configured values for RV32*/ + target_ulong mhpmeventh_val[RV_MAX_MHPMEVENTS]; + target_ulong sscratch; target_ulong mscratch; @@ -400,6 +405,7 @@ struct RISCVCPUConfig { bool ext_zhinxmin; bool ext_zve32f; bool ext_zve64f; + bool ext_sscofpmf; /* Vendor-specific custom extensions */ bool ext_XVentanaCondOps; @@ -438,6 +444,12 @@ struct RISCVCPU { /* Configuration Settings */ RISCVCPUConfig cfg; + + QEMUTimer *pmu_timer; + /* A bitmask of Available programmable counters */ + uint32_t pmu_avail_ctrs; + /* Mapping of events to counters */ + GHashTable *pmu_event_ctr_map; }; static inline int riscv_has_ext(CPURISCVState *env, target_ulong ext) @@ -697,6 +709,19 @@ enum { CSR_TABLE_SIZE = 0x1000 }; +/** + * The event id are encoded based on the encoding specified in the + * SBI specification v0.3 + */ + +enum riscv_pmu_event_idx { + RISCV_PMU_EVENT_HW_CPU_CYCLES = 0x01, + RISCV_PMU_EVENT_HW_INSTRUCTIONS = 0x02, + RISCV_PMU_EVENT_CACHE_DTLB_READ_MISS = 0x10019, + RISCV_PMU_EVENT_CACHE_DTLB_WRITE_MISS = 0x1001B, + RISCV_PMU_EVENT_CACHE_ITLB_PREFETCH_MISS = 0x10021, +}; + /* CSR function table */ extern riscv_csr_operations csr_ops[CSR_TABLE_SIZE]; diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index 48b39e6d52a7..da78e2704081 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -400,6 +400,37 @@ #define CSR_MHPMEVENT29 0x33d #define CSR_MHPMEVENT30 0x33e #define CSR_MHPMEVENT31 0x33f + +#define CSR_MHPMEVENT3H 0x723 +#define CSR_MHPMEVENT4H 0x724 +#define CSR_MHPMEVENT5H 0x725 +#define CSR_MHPMEVENT6H 0x726 +#define CSR_MHPMEVENT7H 0x727 +#define CSR_MHPMEVENT8H 0x728 +#define CSR_MHPMEVENT9H 0x729 +#define CSR_MHPMEVENT10H 0x72a +#define CSR_MHPMEVENT11H 0x72b +#define CSR_MHPMEVENT12H 0x72c +#define CSR_MHPMEVENT13H 0x72d +#define CSR_MHPMEVENT14H 0x72e +#define CSR_MHPMEVENT15H 0x72f +#define CSR_MHPMEVENT16H 0x730 +#define CSR_MHPMEVENT17H 0x731 +#define CSR_MHPMEVENT18H 0x732 +#define CSR_MHPMEVENT19H 0x733 +#define CSR_MHPMEVENT20H 0x734 +#define CSR_MHPMEVENT21H 0x735 +#define CSR_MHPMEVENT22H 0x736 +#define CSR_MHPMEVENT23H 0x737 +#define CSR_MHPMEVENT24H 0x738 +#define CSR_MHPMEVENT25H 0x739 +#define CSR_MHPMEVENT26H 0x73a +#define CSR_MHPMEVENT27H 0x73b +#define CSR_MHPMEVENT28H 0x73c +#define CSR_MHPMEVENT29H 0x73d +#define CSR_MHPMEVENT30H 0x73e +#define CSR_MHPMEVENT31H 0x73f + #define CSR_MHPMCOUNTER3H 0xb83 #define CSR_MHPMCOUNTER4H 0xb84 #define CSR_MHPMCOUNTER5H 0xb85 @@ -461,6 +492,7 @@ #define CSR_VSMTE 0x2c0 #define CSR_VSPMMASK 0x2c1 #define CSR_VSPMBASE 0x2c2 +#define CSR_SCOUNTOVF 0xda0 /* mstatus CSR bits */ #define MSTATUS_UIE 0x00000001 @@ -635,6 +667,7 @@ typedef enum RISCVException { #define IRQ_VS_EXT 10 #define IRQ_M_EXT 11 #define IRQ_S_GEXT 12 +#define IRQ_PMU_OVF 13 #define IRQ_LOCAL_MAX 16 #define IRQ_LOCAL_GUEST_MAX (TARGET_LONG_BITS - 1) @@ -652,11 +685,13 @@ typedef enum RISCVException { #define MIP_VSEIP (1 << IRQ_VS_EXT) #define MIP_MEIP (1 << IRQ_M_EXT) #define MIP_SGEIP (1 << IRQ_S_GEXT) +#define MIP_LCOFIP (1 << IRQ_PMU_OVF) /* sip masks */ #define SIP_SSIP MIP_SSIP #define SIP_STIP MIP_STIP #define SIP_SEIP MIP_SEIP +#define SIP_LCOFIP MIP_LCOFIP /* MIE masks */ #define MIE_SEIE (1 << IRQ_S_EXT) @@ -804,4 +839,24 @@ typedef enum RISCVException { #define HVICTL_VALID_MASK \ (HVICTL_VTI | HVICTL_IID | HVICTL_IPRIOM | HVICTL_IPRIO) +/* PMU related bits */ +#define MIE_LCOFIE (1 << IRQ_PMU_OVF) + +#define MHPMEVENT_BIT_OF BIT(63) +#define MHPMEVENTH_BIT_OF BIT(31) +#define MHPMEVENT_BIT_MINH BIT(62) +#define MHPMEVENTH_BIT_MINH BIT(30) +#define MHPMEVENT_BIT_SINH BIT(61) +#define MHPMEVENTH_BIT_SINH BIT(29) +#define MHPMEVENT_BIT_UINH BIT(60) +#define MHPMEVENTH_BIT_UINH BIT(28) +#define MHPMEVENT_BIT_VSINH BIT(59) +#define MHPMEVENTH_BIT_VSINH BIT(27) +#define MHPMEVENT_BIT_VUINH BIT(58) +#define MHPMEVENTH_BIT_VUINH BIT(26) + +#define MHPMEVENT_SSCOF_MASK _ULL(0xFFFF000000000000) +#define MHPMEVENT_IDX_MASK 0xFFFFF +#define MHPMEVENT_SSCOF_RESVD 16 + #endif diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 3286e1e44455..cfcc1a0882d9 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -96,13 +96,26 @@ static RISCVException mctr32(CPURISCVState *env, int csrno) return mctr(env, csrno); } +static RISCVException sscofpmf(CPURISCVState *env, int csrno) +{ + #if !defined(CONFIG_USER_ONLY) + CPUState *cs = env_cpu(env); + RISCVCPU *cpu = RISCV_CPU(cs); + + if (!cpu->cfg.ext_sscofpmf) { + return RISCV_EXCP_ILLEGAL_INST; + } +#endif + return RISCV_EXCP_NONE; +} + static RISCVException ctr(CPURISCVState *env, int csrno) { #if !defined(CONFIG_USER_ONLY) CPUState *cs = env_cpu(env); RISCVCPU *cpu = RISCV_CPU(cs); int ctr_index; - int base_csrno = CSR_HPMCOUNTER3; + int base_csrno = CSR_CYCLE; bool rv32 = riscv_cpu_mxl(env) == MXL_RV32 ? true : false; if (rv32 && csrno >= CSR_CYCLEH) { @@ -111,11 +124,18 @@ static RISCVException ctr(CPURISCVState *env, int csrno) } ctr_index = csrno - base_csrno; - if (!cpu->cfg.pmu_num || ctr_index >= (cpu->cfg.pmu_num)) { + if ((csrno >= CSR_CYCLE && csrno <= CSR_INSTRET) || + (csrno >= CSR_CYCLEH && csrno <= CSR_INSTRETH)) { + goto skip_ext_pmu_check; + } + + if ((!cpu->cfg.pmu_num || !(cpu->pmu_avail_ctrs & BIT(ctr_index)))) { /* No counter is enabled in PMU or the counter is out of range */ return RISCV_EXCP_ILLEGAL_INST; } +skip_ext_pmu_check: + if (env->priv == PRV_S) { switch (csrno) { case CSR_CYCLE: @@ -134,7 +154,6 @@ static RISCVException ctr(CPURISCVState *env, int csrno) } break; case CSR_HPMCOUNTER3...CSR_HPMCOUNTER31: - ctr_index = csrno - CSR_CYCLE; if (!get_field(env->mcounteren, 1 << ctr_index)) { return RISCV_EXCP_ILLEGAL_INST; } @@ -158,7 +177,6 @@ static RISCVException ctr(CPURISCVState *env, int csrno) } break; case CSR_HPMCOUNTER3H...CSR_HPMCOUNTER31H: - ctr_index = csrno - CSR_CYCLEH; if (!get_field(env->mcounteren, 1 << ctr_index)) { return RISCV_EXCP_ILLEGAL_INST; } @@ -188,7 +206,6 @@ static RISCVException ctr(CPURISCVState *env, int csrno) } break; case CSR_HPMCOUNTER3...CSR_HPMCOUNTER31: - ctr_index = csrno - CSR_CYCLE; if (!get_field(env->hcounteren, 1 << ctr_index) && get_field(env->mcounteren, 1 << ctr_index)) { return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; @@ -216,7 +233,6 @@ static RISCVException ctr(CPURISCVState *env, int csrno) } break; case CSR_HPMCOUNTER3H...CSR_HPMCOUNTER31H: - ctr_index = csrno - CSR_CYCLEH; if (!get_field(env->hcounteren, 1 << ctr_index) && get_field(env->mcounteren, 1 << ctr_index)) { return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; @@ -585,11 +601,36 @@ static int write_mhpmevent(CPURISCVState *env, int csrno, target_ulong val) { int evt_index = csrno - CSR_MCOUNTINHIBIT; + if (riscv_cpu_mxl(env) != MXL_RV32) { + riscv_pmu_update_event_map(env, val, evt_index); + } env->mhpmevent_val[evt_index] = val; return RISCV_EXCP_NONE; } +static int read_mhpmeventh(CPURISCVState *env, int csrno, target_ulong *val) +{ + int evt_index = csrno - CSR_MHPMEVENT3H + 3; + + *val = env->mhpmevent_val[evt_index]; + + return RISCV_EXCP_NONE; +} + +static int write_mhpmeventh(CPURISCVState *env, int csrno, target_ulong val) +{ + int evt_index = csrno - CSR_MHPMEVENT3H + 3; + uint64_t mhpmevth_val = val; + uint64_t mhpmevt_val = env->mhpmevent_val[evt_index]; + + mhpmevt_val = mhpmevt_val | (mhpmevth_val << 32); + riscv_pmu_update_event_map(env, mhpmevt_val, evt_index); + env->mhpmeventh_val[evt_index] = val; + + return RISCV_EXCP_NONE; +} + static int write_mhpmcounter(CPURISCVState *env, int csrno, target_ulong val) { int ctr_idx = csrno - CSR_MCYCLE; @@ -599,7 +640,10 @@ static int write_mhpmcounter(CPURISCVState *env, int csrno, target_ulong val) if (riscv_pmu_ctr_monitor_cycles(env, ctr_idx) || riscv_pmu_ctr_monitor_instructions(env, ctr_idx)) { counter->mhpmcounter_prev = get_ticks(false); - } else { + if (ctr_idx > 2) { + riscv_pmu_setup_timer(env, val, ctr_idx); + } + } else { /* Other counters can keep incrementing from the given value */ counter->mhpmcounter_prev = val; } @@ -611,11 +655,17 @@ static int write_mhpmcounterh(CPURISCVState *env, int csrno, target_ulong val) { int ctr_idx = csrno - CSR_MCYCLEH; PMUCTRState *counter = &env->pmu_ctrs[ctr_idx]; + uint64_t mhpmctr_val = counter->mhpmcounter_val; + uint64_t mhpmctrh_val = val; counter->mhpmcounterh_val = val; + mhpmctr_val = mhpmctr_val | (mhpmctrh_val << 32); if (riscv_pmu_ctr_monitor_cycles(env, ctr_idx) || riscv_pmu_ctr_monitor_instructions(env, ctr_idx)) { counter->mhpmcounterh_prev = get_ticks(true); + if (ctr_idx > 2) { + riscv_pmu_setup_timer(env, mhpmctr_val, ctr_idx); + } } else { counter->mhpmcounterh_prev = val; } @@ -688,6 +738,32 @@ static int read_hpmcounterh(CPURISCVState *env, int csrno, target_ulong *val) return riscv_pmu_read_ctr(env, val, true, ctr_index); } +static int read_scountovf(CPURISCVState *env, int csrno, target_ulong *val) +{ + int mhpmevt_start = CSR_MHPMEVENT3 - CSR_MCOUNTINHIBIT; + int i; + *val = 0; + target_ulong *mhpm_evt_val; + uint64_t of_bit_mask; + + if (riscv_cpu_mxl(env) == MXL_RV32) { + mhpm_evt_val = env->mhpmeventh_val; + of_bit_mask = MHPMEVENTH_BIT_OF; + } else { + mhpm_evt_val = env->mhpmevent_val; + of_bit_mask = MHPMEVENT_BIT_OF; + } + + for (i = mhpmevt_start; i < RV_MAX_MHPMEVENTS; i++) { + if ((get_field(env->mcounteren, BIT(i))) && + (mhpm_evt_val[i] & of_bit_mask)) { + *val |= BIT(i); + } + } + + return RISCV_EXCP_NONE; +} + #if defined(CONFIG_USER_ONLY) static RISCVException read_time(CPURISCVState *env, int csrno, target_ulong *val) @@ -734,7 +810,8 @@ static RISCVException read_timeh(CPURISCVState *env, int csrno, /* Machine constants */ #define M_MODE_INTERRUPTS ((uint64_t)(MIP_MSIP | MIP_MTIP | MIP_MEIP)) -#define S_MODE_INTERRUPTS ((uint64_t)(MIP_SSIP | MIP_STIP | MIP_SEIP)) +#define S_MODE_INTERRUPTS ((uint64_t)(MIP_SSIP | MIP_STIP | MIP_SEIP | \ + MIP_LCOFIP)) #define VS_MODE_INTERRUPTS ((uint64_t)(MIP_VSSIP | MIP_VSTIP | MIP_VSEIP)) #define HS_MODE_INTERRUPTS ((uint64_t)(MIP_SGEIP | VS_MODE_INTERRUPTS)) @@ -775,7 +852,8 @@ static const target_ulong vs_delegable_excps = DELEGABLE_EXCPS & static const target_ulong sstatus_v1_10_mask = SSTATUS_SIE | SSTATUS_SPIE | SSTATUS_UIE | SSTATUS_UPIE | SSTATUS_SPP | SSTATUS_FS | SSTATUS_XS | SSTATUS_SUM | SSTATUS_MXR | SSTATUS_VS; -static const target_ulong sip_writable_mask = SIP_SSIP | MIP_USIP | MIP_UEIP; +static const target_ulong sip_writable_mask = SIP_SSIP | MIP_USIP | MIP_UEIP | + SIP_LCOFIP; static const target_ulong hip_writable_mask = MIP_VSSIP; static const target_ulong hvip_writable_mask = MIP_VSSIP | MIP_VSTIP | MIP_VSEIP; static const target_ulong vsip_writable_mask = MIP_VSSIP; @@ -3814,6 +3892,65 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_MHPMEVENT31] = { "mhpmevent31", any, read_mhpmevent, write_mhpmevent }, + [CSR_MHPMEVENT3H] = { "mhpmevent3h", sscofpmf, read_mhpmeventh, + write_mhpmeventh}, + [CSR_MHPMEVENT4H] = { "mhpmevent4h", sscofpmf, read_mhpmeventh, + write_mhpmeventh}, + [CSR_MHPMEVENT5H] = { "mhpmevent5h", sscofpmf, read_mhpmeventh, + write_mhpmeventh}, + [CSR_MHPMEVENT6H] = { "mhpmevent6h", sscofpmf, read_mhpmeventh, + write_mhpmeventh}, + [CSR_MHPMEVENT7H] = { "mhpmevent7h", sscofpmf, read_mhpmeventh, + write_mhpmeventh}, + [CSR_MHPMEVENT8H] = { "mhpmevent8h", sscofpmf, read_mhpmeventh, + write_mhpmeventh}, + [CSR_MHPMEVENT9H] = { "mhpmevent9h", sscofpmf, read_mhpmeventh, + write_mhpmeventh}, + [CSR_MHPMEVENT10H] = { "mhpmevent10h", sscofpmf, read_mhpmeventh, + write_mhpmeventh}, + [CSR_MHPMEVENT11H] = { "mhpmevent11h", sscofpmf, read_mhpmeventh, + write_mhpmeventh}, + [CSR_MHPMEVENT12H] = { "mhpmevent12h", sscofpmf, read_mhpmeventh, + write_mhpmeventh}, + [CSR_MHPMEVENT13H] = { "mhpmevent13h", sscofpmf, read_mhpmeventh, + write_mhpmeventh}, + [CSR_MHPMEVENT14H] = { "mhpmevent14h", sscofpmf, read_mhpmeventh, + write_mhpmeventh}, + [CSR_MHPMEVENT15H] = { "mhpmevent15h", sscofpmf, read_mhpmeventh, + write_mhpmeventh}, + [CSR_MHPMEVENT16H] = { "mhpmevent16h", sscofpmf, read_mhpmeventh, + write_mhpmeventh}, + [CSR_MHPMEVENT17H] = { "mhpmevent17h", sscofpmf, read_mhpmeventh, + write_mhpmeventh}, + [CSR_MHPMEVENT18H] = { "mhpmevent18h", sscofpmf, read_mhpmeventh, + write_mhpmeventh}, + [CSR_MHPMEVENT19H] = { "mhpmevent19h", sscofpmf, read_mhpmeventh, + write_mhpmeventh}, + [CSR_MHPMEVENT20H] = { "mhpmevent20h", sscofpmf, read_mhpmeventh, + write_mhpmeventh}, + [CSR_MHPMEVENT21H] = { "mhpmevent21h", sscofpmf, read_mhpmeventh, + write_mhpmeventh}, + [CSR_MHPMEVENT22H] = { "mhpmevent22h", sscofpmf, read_mhpmeventh, + write_mhpmeventh}, + [CSR_MHPMEVENT23H] = { "mhpmevent23h", sscofpmf, read_mhpmeventh, + write_mhpmeventh}, + [CSR_MHPMEVENT24H] = { "mhpmevent24h", sscofpmf, read_mhpmeventh, + write_mhpmeventh}, + [CSR_MHPMEVENT25H] = { "mhpmevent25h", sscofpmf, read_mhpmeventh, + write_mhpmeventh}, + [CSR_MHPMEVENT26H] = { "mhpmevent26h", sscofpmf, read_mhpmeventh, + write_mhpmeventh}, + [CSR_MHPMEVENT27H] = { "mhpmevent27h", sscofpmf, read_mhpmeventh, + write_mhpmeventh}, + [CSR_MHPMEVENT28H] = { "mhpmevent28h", sscofpmf, read_mhpmeventh, + write_mhpmeventh}, + [CSR_MHPMEVENT29H] = { "mhpmevent29h", sscofpmf, read_mhpmeventh, + write_mhpmeventh}, + [CSR_MHPMEVENT30H] = { "mhpmevent30h", sscofpmf, read_mhpmeventh, + write_mhpmeventh}, + [CSR_MHPMEVENT31H] = { "mhpmevent31h", sscofpmf, read_mhpmeventh, + write_mhpmeventh}, + [CSR_HPMCOUNTER3H] = { "hpmcounter3h", ctr32, read_hpmcounterh }, [CSR_HPMCOUNTER4H] = { "hpmcounter4h", ctr32, read_hpmcounterh }, [CSR_HPMCOUNTER5H] = { "hpmcounter5h", ctr32, read_hpmcounterh }, @@ -3902,5 +4039,7 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { write_mhpmcounterh }, [CSR_MHPMCOUNTER31H] = { "mhpmcounter31h", mctr32, read_hpmcounterh, write_mhpmcounterh }, + [CSR_SCOUNTOVF] = { "scountovf", sscofpmf, read_scountovf }, + #endif /* !CONFIG_USER_ONLY */ }; diff --git a/target/riscv/pmu.c b/target/riscv/pmu.c index 000fe8da45ef..5b212d2eb630 100644 --- a/target/riscv/pmu.c +++ b/target/riscv/pmu.c @@ -20,13 +20,355 @@ #include "cpu.h" #include "pmu.h" +#define RISCV_TIMEBASE_FREQ 1000000000 /* 1Ghz */ +#define MAKE_32BIT_MASK(shift, length) \ + (((uint32_t)(~0UL) >> (32 - (length))) << (shift)) + +static bool riscv_pmu_counter_valid(RISCVCPU *cpu, uint32_t ctr_idx) +{ + if (ctr_idx < 3 || ctr_idx >= RV_MAX_MHPMCOUNTERS || + !(cpu->pmu_avail_ctrs & BIT(ctr_idx))) { + return false; + } else { + return true; + } +} + +static bool riscv_pmu_counter_enabled(RISCVCPU *cpu, uint32_t ctr_idx) +{ + CPURISCVState *env = &cpu->env; + + if (!riscv_pmu_counter_valid(cpu, ctr_idx) || + !get_field(env->mcounteren, BIT(ctr_idx))) { + return false; + } else { + return true; + } +} + +static int riscv_pmu_incr_ctr_rv32(RISCVCPU *cpu, uint32_t ctr_idx) +{ + CPURISCVState *env = &cpu->env; + target_ulong max_val = UINT32_MAX; + PMUCTRState *counter = &env->pmu_ctrs[ctr_idx]; + + /* Privilege mode filtering */ + if ((env->priv == PRV_M && + (env->mhpmeventh_val[ctr_idx] & MHPMEVENTH_BIT_MINH)) || + (env->priv == PRV_S && + (env->mhpmeventh_val[ctr_idx] & MHPMEVENTH_BIT_SINH)) || + (env->priv == PRV_U && + (env->mhpmeventh_val[ctr_idx] & MHPMEVENTH_BIT_UINH))) { + return 0; + } + + /* Handle the overflow scenario */ + if (counter->mhpmcounter_val == max_val) { + if (counter->mhpmcounterh_val == max_val) { + counter->mhpmcounter_val = 0; + counter->mhpmcounterh_val = 0; + /* Generate interrupt only if OF bit is clear */ + if (!(env->mhpmeventh_val[ctr_idx] & MHPMEVENTH_BIT_OF)) { + env->mhpmeventh_val[ctr_idx] |= MHPMEVENTH_BIT_OF; + riscv_cpu_update_mip(cpu, MIP_LCOFIP, BOOL_TO_MASK(1)); + } + } else { + counter->mhpmcounterh_val++; + } + } else { + counter->mhpmcounter_val++; + } + + return 0; +} + +static int riscv_pmu_incr_ctr_rv64(RISCVCPU *cpu, uint32_t ctr_idx) +{ + CPURISCVState *env = &cpu->env; + PMUCTRState *counter = &env->pmu_ctrs[ctr_idx]; + uint64_t max_val = UINT64_MAX; + + /* Privilege mode filtering */ + if ((env->priv == PRV_M && + (env->mhpmevent_val[ctr_idx] & MHPMEVENT_BIT_MINH)) || + (env->priv == PRV_S && + (env->mhpmevent_val[ctr_idx] & MHPMEVENT_BIT_SINH)) || + (env->priv == PRV_U && + (env->mhpmevent_val[ctr_idx] & MHPMEVENT_BIT_UINH))) { + return 0; + } + + /* Handle the overflow scenario */ + if (counter->mhpmcounter_val == max_val) { + counter->mhpmcounter_val = 0; + /* Generate interrupt only if OF bit is clear */ + if (!(env->mhpmevent_val[ctr_idx] & MHPMEVENT_BIT_OF)) { + env->mhpmevent_val[ctr_idx] |= MHPMEVENT_BIT_OF; + riscv_cpu_update_mip(cpu, MIP_LCOFIP, BOOL_TO_MASK(1)); + } + } else { + counter->mhpmcounter_val++; + } + return 0; +} + +int riscv_pmu_incr_ctr(RISCVCPU *cpu, enum riscv_pmu_event_idx event_idx) +{ + uint32_t ctr_idx; + int ret; + CPURISCVState *env = &cpu->env; + gpointer value; + + value = g_hash_table_lookup(cpu->pmu_event_ctr_map, + GUINT_TO_POINTER(event_idx)); + if (!value) { + return -1; + } + + ctr_idx = GPOINTER_TO_UINT(value); + if (!riscv_pmu_counter_enabled(cpu, ctr_idx) || + get_field(env->mcountinhibit, BIT(ctr_idx))) { + return -1; + } + + if (riscv_cpu_mxl(env) == MXL_RV32) { + ret = riscv_pmu_incr_ctr_rv32(cpu, ctr_idx); + } else { + ret = riscv_pmu_incr_ctr_rv64(cpu, ctr_idx); + } + + return ret; +} + bool riscv_pmu_ctr_monitor_instructions(CPURISCVState *env, uint32_t target_ctr) { - return (target_ctr == 0) ? true : false; + RISCVCPU *cpu; + uint32_t event_idx; + uint32_t ctr_idx; + + /* Fixed instret counter */ + if (target_ctr == 2) { + return true; + } + + cpu = RISCV_CPU(env_cpu(env)); + event_idx = RISCV_PMU_EVENT_HW_INSTRUCTIONS; + ctr_idx = GPOINTER_TO_UINT(g_hash_table_lookup(cpu->pmu_event_ctr_map, + GUINT_TO_POINTER(event_idx))); + if (!ctr_idx) { + return false; + } + + return target_ctr == ctr_idx ? true : false; } bool riscv_pmu_ctr_monitor_cycles(CPURISCVState *env, uint32_t target_ctr) { - return (target_ctr == 2) ? true : false; + RISCVCPU *cpu; + uint32_t event_idx; + uint32_t ctr_idx; + + /* Fixed mcycle counter */ + if (target_ctr == 0) { + return true; + } + + cpu = RISCV_CPU(env_cpu(env)); + event_idx = RISCV_PMU_EVENT_HW_CPU_CYCLES; + ctr_idx = GPOINTER_TO_UINT(g_hash_table_lookup(cpu->pmu_event_ctr_map, + GUINT_TO_POINTER(event_idx))); + + /* Counter zero is not used for event_ctr_map */ + if (!ctr_idx) { + return false; + } + + return (target_ctr == ctr_idx) ? true : false; +} + +static gboolean pmu_remove_event_map(gpointer key, gpointer value, + gpointer udata) +{ + return (GPOINTER_TO_UINT(value) == GPOINTER_TO_UINT(udata)) ? true : false; +} + +static int64_t pmu_icount_ticks_to_ns(int64_t value) +{ + int64_t ret = 0; + + if (icount_enabled()) { + ret = icount_to_ns(value); + } else { + ret = (NANOSECONDS_PER_SECOND / RISCV_TIMEBASE_FREQ) * value; + } + + return ret; +} + +int riscv_pmu_update_event_map(CPURISCVState *env, uint64_t value, + uint32_t ctr_idx) +{ + uint32_t event_idx; + RISCVCPU *cpu = RISCV_CPU(env_cpu(env)); + + if (!riscv_pmu_counter_valid(cpu, ctr_idx)) { + return -1; + } + + /** + * Expected mhpmevent value is zero for reset case. Remove the current + * mapping. + */ + if (!value) { + g_hash_table_foreach_remove(cpu->pmu_event_ctr_map, + pmu_remove_event_map, + GUINT_TO_POINTER(ctr_idx)); + return 0; + } + + event_idx = value & MHPMEVENT_IDX_MASK; + if (g_hash_table_lookup(cpu->pmu_event_ctr_map, + GUINT_TO_POINTER(event_idx))) { + return 0; + } + + switch (event_idx) { + case RISCV_PMU_EVENT_HW_CPU_CYCLES: + case RISCV_PMU_EVENT_HW_INSTRUCTIONS: + case RISCV_PMU_EVENT_CACHE_DTLB_READ_MISS: + case RISCV_PMU_EVENT_CACHE_DTLB_WRITE_MISS: + case RISCV_PMU_EVENT_CACHE_ITLB_PREFETCH_MISS: + break; + default: + /* We don't support any raw events right now */ + return -1; + } + g_hash_table_insert(cpu->pmu_event_ctr_map, GUINT_TO_POINTER(event_idx), + GUINT_TO_POINTER(ctr_idx)); + + return 0; +} + +static void pmu_timer_trigger_irq(RISCVCPU *cpu, + enum riscv_pmu_event_idx evt_idx) +{ + uint32_t ctr_idx; + CPURISCVState *env = &cpu->env; + PMUCTRState *counter; + target_ulong *mhpmevent_val; + uint64_t of_bit_mask; + int64_t irq_trigger_at; + + if (evt_idx != RISCV_PMU_EVENT_HW_CPU_CYCLES && + evt_idx != RISCV_PMU_EVENT_HW_INSTRUCTIONS) { + return; + } + + ctr_idx = GPOINTER_TO_UINT(g_hash_table_lookup(cpu->pmu_event_ctr_map, + GUINT_TO_POINTER(evt_idx))); + if (!riscv_pmu_counter_enabled(cpu, ctr_idx)) { + return; + } + + if (riscv_cpu_mxl(env) == MXL_RV32) { + mhpmevent_val = &env->mhpmeventh_val[ctr_idx]; + of_bit_mask = MHPMEVENTH_BIT_OF; + } else { + mhpmevent_val = &env->mhpmevent_val[ctr_idx]; + of_bit_mask = MHPMEVENT_BIT_OF; + } + + counter = &env->pmu_ctrs[ctr_idx]; + if (counter->irq_overflow_left > 0) { + irq_trigger_at = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + counter->irq_overflow_left; + timer_mod_anticipate_ns(cpu->pmu_timer, irq_trigger_at); + counter->irq_overflow_left = 0; + return; + } + + if (cpu->pmu_avail_ctrs & BIT(ctr_idx)) { + /* Generate interrupt only if OF bit is clear */ + if (!(*mhpmevent_val & of_bit_mask)) { + *mhpmevent_val |= of_bit_mask; + riscv_cpu_update_mip(cpu, MIP_LCOFIP, BOOL_TO_MASK(1)); + } + } +} + +/* Timer callback for instret and cycle counter overflow */ +void riscv_pmu_timer_cb(void *priv) +{ + RISCVCPU *cpu = priv; + + /* Timer event was triggered only for these events */ + pmu_timer_trigger_irq(cpu, RISCV_PMU_EVENT_HW_CPU_CYCLES); + pmu_timer_trigger_irq(cpu, RISCV_PMU_EVENT_HW_INSTRUCTIONS); +} + +int riscv_pmu_setup_timer(CPURISCVState *env, uint64_t value, uint32_t ctr_idx) +{ + uint64_t overflow_delta, overflow_at; + int64_t overflow_ns, overflow_left = 0; + RISCVCPU *cpu = RISCV_CPU(env_cpu(env)); + PMUCTRState *counter = &env->pmu_ctrs[ctr_idx]; + + if (!riscv_pmu_counter_valid(cpu, ctr_idx) || !cpu->cfg.ext_sscofpmf) { + return -1; + } + + if (value) { + overflow_delta = UINT64_MAX - value + 1; + } else { + overflow_delta = UINT64_MAX - value; + } + + /** + * QEMU supports only int64_t timers while RISC-V counters are uint64_t. + * Compute the leftover and save it so that it can be reprogrammed again + * when timer expires. + */ + if (overflow_delta > INT64_MAX) { + overflow_left = overflow_delta - INT64_MAX; + } + + if (riscv_pmu_ctr_monitor_cycles(env, ctr_idx) || + riscv_pmu_ctr_monitor_instructions(env, ctr_idx)) { + overflow_ns = pmu_icount_ticks_to_ns((int64_t)overflow_delta); + overflow_left = pmu_icount_ticks_to_ns(overflow_left) ; + } else { + return -1; + } + overflow_at = (uint64_t)qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + overflow_ns; + + if (overflow_at > INT64_MAX) { + overflow_left += overflow_at - INT64_MAX; + counter->irq_overflow_left = overflow_left; + overflow_at = INT64_MAX; + } + timer_mod_anticipate_ns(cpu->pmu_timer, overflow_at); + + return 0; +} + + +int riscv_pmu_init(RISCVCPU *cpu, int num_counters) +{ + if (num_counters > (RV_MAX_MHPMCOUNTERS - 3)) { + return -1; + } + + cpu->pmu_event_ctr_map = g_hash_table_new(g_direct_hash, g_direct_equal); + if (!cpu->pmu_event_ctr_map) { + /* PMU support can not be enabled */ + qemu_log_mask(LOG_UNIMP, "PMU events can't be supported\n"); + cpu->cfg.pmu_num = 0; + return -1; + } + + /* Create a bitmask of available programmable counters */ + cpu->pmu_avail_ctrs = MAKE_32BIT_MASK(3, num_counters); + + return 0; } diff --git a/target/riscv/pmu.h b/target/riscv/pmu.h index 58a5bc3a4089..036653627f78 100644 --- a/target/riscv/pmu.h +++ b/target/riscv/pmu.h @@ -26,3 +26,10 @@ 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); +int riscv_pmu_init(RISCVCPU *cpu, int num_counters); +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); +int riscv_pmu_setup_timer(CPURISCVState *env, uint64_t value, + uint32_t ctr_idx);