From 0c304b661965d81ba2194b54a0c71f4c2e5cab16 Mon Sep 17 00:00:00 2001 From: Atish Patra Date: Mon, 8 Nov 2021 10:53:02 -0800 Subject: [PATCH] lib: sbi: Allow programmable counters to monitor cycle/instret events A platform may use programmable counters for cycle/instret events. The priv spec allows that provided that cycle/instret also report those events in addition to the programmable counters. We should allow that functionality in OpenSBI. Reviewed-by: Anup Patel Reviewed-by: Bin Meng Signed-off-by: Atish Patra --- include/sbi/sbi_pmu.h | 1 + lib/sbi/sbi_pmu.c | 60 +++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 45 insertions(+), 16 deletions(-) diff --git a/include/sbi/sbi_pmu.h b/include/sbi/sbi_pmu.h index b3010cc..f018556 100644 --- a/include/sbi/sbi_pmu.h +++ b/include/sbi/sbi_pmu.h @@ -26,6 +26,7 @@ #define SBI_PMU_FW_CTR_MAX 16 #define SBI_PMU_HW_CTR_MAX 32 #define SBI_PMU_CTR_MAX (SBI_PMU_HW_CTR_MAX + SBI_PMU_FW_CTR_MAX) +#define SBI_PMU_FIXED_CTR_MASK 0x07 /** Initialize PMU */ int sbi_pmu_init(struct sbi_scratch *scratch, bool cold_boot); diff --git a/lib/sbi/sbi_pmu.c b/lib/sbi/sbi_pmu.c index e2373ff..818b875 100644 --- a/lib/sbi/sbi_pmu.c +++ b/lib/sbi/sbi_pmu.c @@ -175,9 +175,7 @@ static int pmu_add_hw_event_map(u32 eidx_start, u32 eidx_end, u32 cmap, struct sbi_pmu_hw_event *event = &hw_event_map[num_hw_events]; /* The first two counters are reserved by priv spec */ - if ((eidx_start == SBI_PMU_HW_CPU_CYCLES && cmap != 0x1) || - (eidx_start == SBI_PMU_HW_INSTRUCTIONS && cmap != 0x4) || - (eidx_start > SBI_PMU_HW_INSTRUCTIONS && (cmap & 0x07))) + if (eidx_start > SBI_PMU_HW_INSTRUCTIONS && (cmap & SBI_PMU_FIXED_CTR_MASK)) return SBI_EDENIED; if (num_hw_events >= SBI_PMU_HW_EVENT_MAX - 1) { @@ -188,8 +186,6 @@ static int pmu_add_hw_event_map(u32 eidx_start, u32 eidx_end, u32 cmap, event->start_idx = eidx_start; event->end_idx = eidx_end; - event->counters = cmap; - event->select = select; /* Sanity check */ for (i = 0; i < num_hw_events; i++) { @@ -199,11 +195,19 @@ static int pmu_add_hw_event_map(u32 eidx_start, u32 eidx_end, u32 cmap, else is_overlap = pmu_event_range_overlap(&hw_event_map[i], event); if (is_overlap) - return SBI_EINVALID_ADDR; + goto reset_event; } + + event->counters = cmap; + event->select = select; num_hw_events++; return 0; + +reset_event: + event->start_idx = 0; + event->end_idx = 0; + return SBI_EINVAL; } /** @@ -437,23 +441,37 @@ static int pmu_update_hw_mhpmevent(struct sbi_pmu_hw_event *hw_evt, int ctr_idx, return 0; } +static int pmu_ctr_find_fixed_fw(unsigned long evt_idx_code) +{ + /* Non-programmables counters are enabled always. No need to do lookup */ + if (evt_idx_code == SBI_PMU_HW_CPU_CYCLES) + return 0; + else if (evt_idx_code == SBI_PMU_HW_INSTRUCTIONS) + return 2; + else + return SBI_EINVAL; +} + static int pmu_ctr_find_hw(unsigned long cbase, unsigned long cmask, unsigned long flags, unsigned long event_idx, uint64_t data) { unsigned long ctr_mask; - int i, ret = 0, ctr_idx = SBI_ENOTSUPP; + int i, ret = 0, fixed_ctr, ctr_idx = SBI_ENOTSUPP; struct sbi_pmu_hw_event *temp; unsigned long mctr_inhbt = csr_read(CSR_MCOUNTINHIBIT); - int evt_idx_code = get_cidx_code(event_idx); + struct sbi_scratch *scratch = sbi_scratch_thishart_ptr(); if (cbase > num_hw_ctrs) return SBI_EINVAL; - /* Non-programmables counters are enabled always. No need to do lookup */ - if (evt_idx_code == SBI_PMU_HW_CPU_CYCLES) - return 0; - else if (evt_idx_code == SBI_PMU_HW_INSTRUCTIONS) - return 2; + /** + * If Sscof is present try to find the programmable counter for + * cycle/instret as well. + */ + fixed_ctr = pmu_ctr_find_fixed_fw(event_idx); + if (fixed_ctr >= 0 && + !sbi_hart_has_feature(scratch, SBI_HART_HAS_SSCOFPMF)) + return fixed_ctr; for (i = 0; i < num_hw_events; i++) { temp = &hw_event_map[i]; @@ -465,7 +483,9 @@ static int pmu_ctr_find_hw(unsigned long cbase, unsigned long cmask, unsigned lo if ((event_idx == SBI_PMU_EVENT_RAW_IDX) && temp->select != data) continue; - ctr_mask = temp->counters & (cmask << cbase); + /* Fixed counters should not be part of the search */ + ctr_mask = temp->counters & (cmask << cbase) & + (~SBI_PMU_FIXED_CTR_MASK); for_each_set_bit_from(cbase, &ctr_mask, SBI_PMU_HW_CTR_MAX) { if (__test_bit(cbase, &mctr_inhbt)) { ctr_idx = cbase; @@ -474,8 +494,16 @@ static int pmu_ctr_find_hw(unsigned long cbase, unsigned long cmask, unsigned lo } } - if (ctr_idx == SBI_ENOTSUPP) - return SBI_EFAIL; + if (ctr_idx == SBI_ENOTSUPP) { + /** + * We can't find any programmable counters for cycle/instret. + * Return the fixed counter as they are mandatory anyways. + */ + if (fixed_ctr >= 0) + return fixed_ctr; + else + return SBI_EFAIL; + } ret = pmu_update_hw_mhpmevent(temp, ctr_idx, flags, event_idx, data); -- 2.7.4