[PATCH v2 10/15] lib: sbi: Add PMU support

Xiang W wxjstz at 126.com
Sun Jun 20 04:59:09 PDT 2021


在 2021-06-18星期五的 17:08 -0700,Atish Patra写道:
> On Tue, Jun 8, 2021 at 4:59 AM Xiang W <wxjstz at 126.com> wrote:
> > 
> > 在 2021-05-26星期三的 17:30 -0700,Atish Patra写道:
> > > RISC-V SBI v0.3 specification defined a PMU extension to
> > > configure/start/stop
> > > the hardware/firmware pmu events.
> > > 
> > > Implement PMU support in OpenSBI library. The implementation is
> > > agnostic of
> > > event to counter mapping & mhpmevent value configuration. That
> > > means,
> > > it
> > > expects platform hooks will be used to set up the mapping and
> > > provide
> > > the mhpmevent value at runtime.
> > > 
> > > Signed-off-by: Atish Patra <atish.patra at wdc.com>
> > > ---
> > >  include/sbi/sbi_ecall_interface.h | 138 ++++++-
> > >  include/sbi/sbi_error.h           |   2 +
> > >  include/sbi/sbi_pmu.h             |  73 ++++
> > >  lib/sbi/objects.mk                |   1 +
> > >  lib/sbi/sbi_init.c                |   9 +
> > >  lib/sbi/sbi_pmu.c                 | 600
> > > ++++++++++++++++++++++++++++++
> > >  6 files changed, 822 insertions(+), 1 deletion(-)
> > >  create mode 100644 include/sbi/sbi_pmu.h
> > >  create mode 100644 lib/sbi/sbi_pmu.c
> > > 
> > > diff --git a/include/sbi/sbi_ecall_interface.h
> > > b/include/sbi/sbi_ecall_interface.h
> > > index 559a33e7ced0..8fc723bc212c 100644
> > > --- a/include/sbi/sbi_ecall_interface.h
> > > +++ b/include/sbi/sbi_ecall_interface.h
> > > @@ -28,6 +28,7 @@
> > >  #define SBI_EXT_RFENCE                         0x52464E43
> > >  #define SBI_EXT_HSM                            0x48534D
> > >  #define SBI_EXT_SRST                           0x53525354
> > > +#define SBI_EXT_PMU                            0x504D55
> > > 
> > >  /* SBI function IDs for BASE extension*/
> > >  #define SBI_EXT_BASE_GET_SPEC_VERSION          0x0
> > > @@ -91,6 +92,139 @@
> > >  #define SBI_SRST_RESET_REASON_NONE     0x0
> > >  #define SBI_SRST_RESET_REASON_SYSFAIL  0x1
> > > 
> > > +/* SBI function IDs for PMU extension */
> > > +#define SBI_EXT_PMU_NUM_COUNTERS       0x0
> > > +#define SBI_EXT_PMU_COUNTER_GET_INFO   0x1
> > > +#define SBI_EXT_PMU_COUNTER_CFG_MATCH  0x2
> > > +#define SBI_EXT_PMU_COUNTER_START      0x3
> > > +#define SBI_EXT_PMU_COUNTER_STOP       0x4
> > > +#define SBI_EXT_PMU_COUNTER_FW_READ    0x5
> > > +
> > > +/** General pmu event codes specified in SBI PMU extension */
> > > +enum sbi_pmu_hw_generic_events_t {
> > > +       SBI_PMU_HW_NO_EVENT                     = 0,
> > > +       SBI_PMU_HW_CPU_CYCLES                   = 1,
> > > +       SBI_PMU_HW_INSTRUCTIONS                 = 2,
> > > +       SBI_PMU_HW_CACHE_REFERENCES             = 3,
> > > +       SBI_PMU_HW_CACHE_MISSES                 = 4,
> > > +       SBI_PMU_HW_BRANCH_INSTRUCTIONS          = 5,
> > > +       SBI_PMU_HW_BRANCH_MISSES                = 6,
> > > +       SBI_PMU_HW_BUS_CYCLES                   = 7,
> > > +       SBI_PMU_HW_STALLED_CYCLES_FRONTEND      = 8,
> > > +       SBI_PMU_HW_STALLED_CYCLES_BACKEND       = 9,
> > > +       SBI_PMU_HW_REF_CPU_CYCLES               = 10,
> > > +
> > > +       SBI_PMU_HW_GENERAL_MAX,
> > > +};
> > > +
> > > +/**
> > > + * Generalized hardware cache events:
> > > + *
> > > + *       { L1-D, L1-I, LLC, ITLB, DTLB, BPU, NODE } x
> > > + *       { read, write, prefetch } x
> > > + *       { accesses, misses }
> > > + */
> > > +enum sbi_pmu_hw_cache_id {
> > > +       SBI_PMU_HW_CACHE_L1D            = 0,
> > > +       SBI_PMU_HW_CACHE_L1I            = 1,
> > > +       SBI_PMU_HW_CACHE_LL             = 2,
> > > +       SBI_PMU_HW_CACHE_DTLB           = 3,
> > > +       SBI_PMU_HW_CACHE_ITLB           = 4,
> > > +       SBI_PMU_HW_CACHE_BPU            = 5,
> > > +       SBI_PMU_HW_CACHE_NODE           = 6,
> > > +
> > > +       SBI_PMU_HW_CACHE_MAX,
> > > +};
> > > +
> > > +enum sbi_pmu_hw_cache_op_id {
> > > +       SBI_PMU_HW_CACHE_OP_READ        = 0,
> > > +       SBI_PMU_HW_CACHE_OP_WRITE       = 1,
> > > +       SBI_PMU_HW_CACHE_OP_PREFETCH    = 2,
> > > +
> > > +       SBI_PMU_HW_CACHE_OP_MAX,
> > > +};
> > > +
> > > +enum sbi_pmu_hw_cache_op_result_id {
> > > +       SBI_PMU_HW_CACHE_RESULT_ACCESS  = 0,
> > > +       SBI_PMU_HW_CACHE_RESULT_MISS    = 1,
> > > +
> > > +       SBI_PMU_HW_CACHE_RESULT_MAX,
> > > +};
> > > +
> > > +/**
> > > + * Special "firmware" events provided by the OpenSBI, even if
> > > the
> > > hardware
> > > + * does not support performance events. These events are encoded
> > > as
> > > a raw
> > > + * event type in Linux kernel perf framework.
> > > + */
> > > +enum sbi_pmu_fw_event_code_id {
> > > +       SBI_PMU_FW_MISALIGNED_LOAD      = 0,
> > > +       SBI_PMU_FW_MISALIGNED_STORE     = 1,
> > > +       SBI_PMU_FW_ACCESS_LOAD          = 2,
> > > +       SBI_PMU_FW_ACCESS_STORE         = 3,
> > > +       SBI_PMU_FW_ILLEGAL_INSN         = 4,
> > > +       SBI_PMU_FW_SET_TIMER            = 5,
> > > +       SBI_PMU_FW_IPI_SENT             = 6,
> > > +       SBI_PMU_FW_IPI_RECVD            = 7,
> > > +       SBI_PMU_FW_FENCE_I_SENT         = 8,
> > > +       SBI_PMU_FW_FENCE_I_RECVD        = 9,
> > > +       SBI_PMU_FW_SFENCE_VMA_SENT      = 10,
> > > +       SBI_PMU_FW_SFENCE_VMA_RCVD      = 11,
> > > +       SBI_PMU_FW_SFENCE_VMA_ASID_SENT = 12,
> > > +       SBI_PMU_FW_SFENCE_VMA_ASID_RCVD = 13,
> > > +
> > > +       SBI_PMU_FW_HFENCE_GVMA_SENT     = 14,
> > > +       SBI_PMU_FW_HFENCE_GVMA_RCVD     = 15,
> > > +       SBI_PMU_FW_HFENCE_GVMA_VMID_SENT = 16,
> > > +       SBI_PMU_FW_HFENCE_GVMA_VMID_RCVD = 17,
> > > +
> > > +       SBI_PMU_FW_HFENCE_VVMA_SENT     = 18,
> > > +       SBI_PMU_FW_HFENCE_VVMA_RCVD     = 19,
> > > +       SBI_PMU_FW_HFENCE_VVMA_ASID_SENT = 20,
> > > +       SBI_PMU_FW_HFENCE_VVMA_ASID_RCVD = 21,
> > > +       SBI_PMU_FW_MAX,
> > > +};
> > > +
> > > +/** SBI PMU event idx type */
> > > +enum sbi_pmu_event_type_id {
> > > +       SBI_PMU_EVENT_TYPE_HW                           = 0x0,
> > > +       SBI_PMU_EVENT_TYPE_HW_CACHE                     = 0x1,
> > > +       SBI_PMU_EVENT_TYPE_HW_RAW                       = 0x2,
> > > +       SBI_PMU_EVENT_TYPE_FW                           = 0xf,
> > > +       SBI_PMU_EVENT_TYPE_MAX,
> > > +};
> > > +
> > > +/** SBI PMU counter type */
> > > +enum sbi_pmu_ctr_type {
> > > +       SBI_PMU_CTR_TYPE_HW = 0,
> > > +       SBI_PMU_CTR_TYPE_FW,
> > > +};
> > > +
> > > +/* Helper macros to decode event idx */
> > > +#define SBI_PMU_EVENT_IDX_OFFSET 20
> > > +#define SBI_PMU_EVENT_IDX_MASK 0xFFFFF
> > > +#define SBI_PMU_EVENT_IDX_CODE_MASK 0xFFFF
> > > +#define SBI_PMU_EVENT_IDX_TYPE_MASK 0xF0000
> > > +#define SBI_PMU_EVENT_RAW_IDX 0x20000
> > > +
> > > +#define SBI_PMU_EVENT_IDX_INVALID 0xFFFFFFFF
> > > +
> > > +/* Flags defined for config matching function */
> > > +#define SBI_PMU_CFG_FLAG_SKIP_MATCH    (1 << 0)
> > > +#define SBI_PMU_CFG_FLAG_CLEAR_VALUE   (1 << 1)
> > > +#define SBI_PMU_CFG_FLAG_AUTO_START    (1 << 2)
> > > +#define SBI_PMU_CFG_FLAG_SET_MINH      (1 << 3)
> > > +#define SBI_PMU_CFG_FLAG_SET_SINH      (1 << 4)
> > > +#define SBI_PMU_CFG_FLAG_SET_UINH      (1 << 5)
> > > +#define SBI_PMU_CFG_FLAG_SET_VSINH     (1 << 6)
> > > +#define SBI_PMU_CFG_FLAG_SET_VUINH     (1 << 7)
> > > +
> > > +/* Flags defined for counter start function */
> > > +#define SBI_PMU_START_FLAG_SET_INIT_VALUE (1 << 0)
> > > +
> > > +/* Flags defined for counter stop function */
> > > +#define SBI_PMU_STOP_FLAG_RESET (1 << 0)
> > > +
> > > +/* SBI base specification related macros */
> > >  #define SBI_SPEC_VERSION_MAJOR_OFFSET          24
> > >  #define SBI_SPEC_VERSION_MAJOR_MASK            0x7f
> > >  #define SBI_SPEC_VERSION_MINOR_MASK            0xffffff
> > > @@ -107,8 +241,10 @@
> > >  #define SBI_ERR_DENIED                         -4
> > >  #define SBI_ERR_INVALID_ADDRESS                        -5
> > >  #define SBI_ERR_ALREADY_AVAILABLE              -6
> > > +#define SBI_ERR_ALREADY_STARTED                        -7
> > > +#define SBI_ERR_ALREADY_STOPPED                        -8
> > > 
> > > -#define
> > > SBI_LAST_ERR                           SBI_ERR_ALREADY_AVAILABLE
> > > +#define
> > > SBI_LAST_ERR                           SBI_ERR_ALREADY_STOPPED
> > > 
> > >  /* clang-format on */
> > > 
> > > diff --git a/include/sbi/sbi_error.h b/include/sbi/sbi_error.h
> > > index 3655d122006b..dd65e14b6fcd 100644
> > > --- a/include/sbi/sbi_error.h
> > > +++ b/include/sbi/sbi_error.h
> > > @@ -21,6 +21,8 @@
> > >  #define SBI_EDENIED            SBI_ERR_DENIED
> > >  #define SBI_EINVALID_ADDR      SBI_ERR_INVALID_ADDRESS
> > >  #define SBI_EALREADY           SBI_ERR_ALREADY_AVAILABLE
> > > +#define SBI_EALREADY_STARTED   SBI_ERR_ALREADY_STARTED
> > > +#define SBI_EALREADY_STOPPED   SBI_ERR_ALREADY_STOPPED
> > > 
> > >  #define SBI_ENODEV             -1000
> > >  #define SBI_ENOSYS             -1001
> > > diff --git a/include/sbi/sbi_pmu.h b/include/sbi/sbi_pmu.h
> > > new file mode 100644
> > > index 000000000000..b3010cc5c1ce
> > > --- /dev/null
> > > +++ b/include/sbi/sbi_pmu.h
> > > @@ -0,0 +1,73 @@
> > > +/*
> > > + * SPDX-License-Identifier: BSD-2-Clause
> > > + *
> > > + * Copyright (c) 2021 Western Digital Corporation or its
> > > affiliates.
> > > + *
> > > + * Authors:
> > > + *   Atish Patra <atish.patra at wdc.com>
> > > + */
> > > +
> > > +#ifndef __SBI_PMU_H__
> > > +#define __SBI_PMU_H__
> > > +
> > > +#include <sbi/sbi_types.h>
> > > +#include <sbi/sbi_hartmask.h>
> > > +#include <sbi/sbi_scratch.h>
> > > +#include <sbi/sbi_ecall_interface.h>
> > > +
> > > +/* Event related macros */
> > > +/* Maximum number of hardware events that can mapped by OpenSBI
> > > */
> > > +#define SBI_PMU_HW_EVENT_MAX 64
> > > +
> > > +/* Maximum number of firmware events that can mapped by OpenSBI
> > > */
> > > +#define SBI_PMU_FW_EVENT_MAX 32
> > > +
> > > +/* Counter related macros */
> > > +#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)
> > > +
> > > +/** Initialize PMU */
> > > +int sbi_pmu_init(struct sbi_scratch *scratch, bool cold_boot);
> > > +
> > > +/** Reset PMU during hart exit */
> > > +void sbi_pmu_exit(struct sbi_scratch *scratch);
> > > +
> > > +/**
> > > + * Add the hardware event to counter mapping information. This
> > > should be called
> > > + * from the platform code to update the mapping table.
> > > + * @param eidx_start Start of the event idx range for supported
> > > counters
> > > + * @param eidx_end   End of the event idx range for supported
> > > counters
> > > + * @param cmap       A bitmap representing counters supporting
> > > the
> > > event range
> > > + * @return 0 on success, error otherwise.
> > > + */
> > > +int sbi_pmu_add_hw_event_counter_map(u32 eidx_start, u32
> > > eidx_end,
> > > u32 cmap);
> > > +
> > > +/**
> > > + * Add the raw hardware event selector and supported counter
> > > information. This
> > > + * should be called from the platform code to update the mapping
> > > table.
> > > + * @param info  a pointer to the hardware event info
> > > + * @return 0 on success, error otherwise.
> > > + */
> > > +
> > > +int sbi_pmu_add_raw_event_counter_map(uint64_t select, u32
> > > cmap);
> > > +
> > > +int sbi_pmu_ctr_read(uint32_t cidx, unsigned long *cval);
> > > +
> > > +int sbi_pmu_ctr_stop(unsigned long cidx_base, unsigned long
> > > cidx_mask,
> > > +                    unsigned long flag);
> > > +
> > > +int sbi_pmu_ctr_start(unsigned long cidx_base, unsigned long
> > > cidx_mask,
> > > +                     unsigned long flags, uint64_t ival);
> > > +
> > > +int sbi_pmu_ctr_get_info(uint32_t cidx, unsigned long
> > > *ctr_info);
> > > +
> > > +unsigned long sbi_pmu_num_ctr(void);
> > > +
> > > +int sbi_pmu_ctr_cfg_match(unsigned long cidx_base, unsigned long
> > > cidx_mask,
> > > +                         unsigned long flags, unsigned long
> > > event_idx,
> > > +                         uint64_t event_data);
> > > +
> > > +int sbi_pmu_ctr_incr_fw(enum sbi_pmu_fw_event_code_id fw_id);
> > > +
> > > +#endif
> > > diff --git a/lib/sbi/objects.mk b/lib/sbi/objects.mk
> > > index 6f2c06f5b501..d9068b707854 100644
> > > --- a/lib/sbi/objects.mk
> > > +++ b/lib/sbi/objects.mk
> > > @@ -33,6 +33,7 @@ libsbi-objs-y += sbi_init.o
> > >  libsbi-objs-y += sbi_ipi.o
> > >  libsbi-objs-y += sbi_misaligned_ldst.o
> > >  libsbi-objs-y += sbi_platform.o
> > > +libsbi-objs-y += sbi_pmu.o
> > >  libsbi-objs-y += sbi_scratch.o
> > >  libsbi-objs-y += sbi_string.o
> > >  libsbi-objs-y += sbi_system.o
> > > diff --git a/lib/sbi/sbi_init.c b/lib/sbi/sbi_init.c
> > > index 65af5705f484..143b98f93c02 100644
> > > --- a/lib/sbi/sbi_init.c
> > > +++ b/lib/sbi/sbi_init.c
> > > @@ -19,6 +19,7 @@
> > >  #include <sbi/sbi_hsm.h>
> > >  #include <sbi/sbi_ipi.h>
> > >  #include <sbi/sbi_platform.h>
> > > +#include <sbi/sbi_pmu.h>
> > >  #include <sbi/sbi_system.h>
> > >  #include <sbi/sbi_string.h>
> > >  #include <sbi/sbi_timer.h>
> > > @@ -252,6 +253,8 @@ static void __noreturn init_coldboot(struct
> > > sbi_scratch *scratch, u32 hartid)
> > >         if (rc)
> > >                 sbi_hart_hang();
> > > 
> > > +       sbi_pmu_init(scratch, TRUE);
> > > +
> > >         sbi_boot_print_banner(scratch);
> > > 
> > >         rc = sbi_platform_irqchip_init(plat, TRUE);
> > > @@ -353,6 +356,8 @@ static void init_warm_startup(struct
> > > sbi_scratch
> > > *scratch, u32 hartid)
> > >         if (rc)
> > >                 sbi_hart_hang();
> > > 
> > > +       sbi_pmu_init(scratch, FALSE);
> > > +
> > >         rc = sbi_platform_irqchip_init(plat, FALSE);
> > >         if (rc)
> > >                 sbi_hart_hang();
> > > @@ -393,6 +398,8 @@ static void init_warm_resume(struct
> > > sbi_scratch
> > > *scratch)
> > >         if (rc)
> > >                 sbi_hart_hang();
> > > 
> > > +       sbi_pmu_init(scratch, FALSE);
> > > +
> > >         rc = sbi_hart_pmp_configure(scratch);
> > >         if (rc)
> > >                 sbi_hart_hang();
> > > @@ -516,6 +523,8 @@ void __noreturn sbi_exit(struct sbi_scratch
> > > *scratch)
> > > 
> > >         sbi_platform_early_exit(plat);
> > > 
> > > +       sbi_pmu_exit(scratch);
> > > +
> > >         sbi_timer_exit(scratch);
> > > 
> > >         sbi_ipi_exit(scratch);
> > > diff --git a/lib/sbi/sbi_pmu.c b/lib/sbi/sbi_pmu.c
> > > new file mode 100644
> > > index 000000000000..ed519dfcc860
> > > --- /dev/null
> > > +++ b/lib/sbi/sbi_pmu.c
> > > @@ -0,0 +1,600 @@
> > > +/*
> > > + * SPDX-License-Identifier: BSD-2-Clause
> > > + *
> > > + * Copyright (c) 2021 Western Digital Corporation or its
> > > affiliates.
> > > + *
> > > + * Authors:
> > > + *   Atish Patra <atish.patra at wdc.com>
> > > + */
> > > +
> > > +#include <sbi/riscv_asm.h>
> > > +#include <sbi/sbi_bitops.h>
> > > +#include <sbi/sbi_console.h>
> > > +#include <sbi/sbi_hart.h>
> > > +#include <sbi/sbi_platform.h>
> > > +#include <sbi/sbi_pmu.h>
> > > +#include <sbi/sbi_scratch.h>
> > > +#include <sbi/sbi_string.h>
> > > +
> > > +/** Information about hardware counters */
> > > +struct sbi_pmu_hw_event {
> > > +       unsigned long counters;
> > > +       unsigned long start_idx;
> > > +       unsigned long end_idx;
> > > +       /* Event selector value used only for raw events */
> > > +       uint64_t select;
> > > +};
> > > +
> > > +/** Representation of a firmware event */
> > > +struct sbi_pmu_fw_event {
> > > +
> > > +       /* Event associated with the particular counter */
> > > +       unsigned long event_idx;
> > > +
> > > +       /* Current value of the counter */
> > > +       unsigned long curr_count;
> > > +
> > > +       /* A flag indicating pmu event monitoring is started */
> > > +       bool bStarted;
> > > +};
> > > +
> > > +/* Information about PMU counters as per SBI specification */
> > > +union sbi_pmu_ctr_info {
> > > +       unsigned long value;
> > > +       struct {
> > > +               unsigned long csr:12;
> > > +               unsigned long width:6;
> > > +#if __riscv_xlen == 32
> > > +               unsigned long reserved:13;
> > > +#else
> > > +               unsigned long reserved:45;
> > > +#endif
> > > +               unsigned long type:1;
> > > +       };
> > > +};
> > > +
> > > +/* Mapping between event range and possible counters  */
> > > +static struct sbi_pmu_hw_event
> > > hw_event_map[SBI_PMU_HW_EVENT_MAX] =
> > > {0};
> > > +
> > > +/* counter to enabled event mapping */
> > > +static uint32_t
> > > active_events[SBI_HARTMASK_MAX_BITS][SBI_PMU_HW_CTR_MAX +
> > > SBI_PMU_FW_CTR_MAX];
> > > +
> > > +/* Contains all the information about firmwares events */
> > > +static struct sbi_pmu_fw_event
> > > fw_event_map[SBI_HARTMASK_MAX_BITS][SBI_PMU_FW_EVENT_MAX] = {0};
> > > +
> > > +/* Maximum number of hardware events available */
> > > +static uint32_t num_hw_events;
> > > +/* Maximum number of hardware counters available */
> > > +static uint32_t num_hw_ctrs;
> > > +
> > > +/* Maximum number of counters available */
> > > +static uint32_t total_ctrs;
> > > +
> > > +/* Helper macros to retrieve event idx and code type */
> > > +#define get_cidx_type(x) ((x & SBI_PMU_EVENT_IDX_TYPE_MASK) >>
> > > 16)
> > > +#define get_cidx_code(x) (x & SBI_PMU_EVENT_IDX_CODE_MASK)
> > > +
> > > +/**
> > > + * Perform a sanity check on event & counter mappings with event
> > > range overlap check
> > > + * @param evtA Pointer to the existing hw event structure
> > > + * @param evtB Pointer to the new hw event structure
> > > + *
> > > + * Return FALSE if the range doesn't overlap, TRUE otherwise
> > > + */
> > > +static bool pmu_event_range_overlap(struct sbi_pmu_hw_event
> > > *evtA,
> > > +                                   struct sbi_pmu_hw_event
> > > *evtB)
> > > +{
> > > +       /* check if the range of events overlap with a previous
> > > entry
> > > */
> > > +       if (((evtA->end_idx < evtB->start_idx) && (evtA->end_idx
> > > <
> > > evtB->end_idx)) ||
> > > +          ((evtA->start_idx > evtB->start_idx) && (evtA-
> > > >start_idx >
> > > evtB->end_idx)))
> > > +               return FALSE;
> > > +       return TRUE;
> > > +}
> > > +
> > > +static bool pmu_event_select_overlap(struct sbi_pmu_hw_event
> > > *evt,
> > > +                                    uint64_t select_val)
> > > +{
> > > +
> > > +       if (evt->select == select_val)
> > > +               return TRUE;
> > > +
> > > +       return FALSE;
> > > +}
> > > +
> > > +static int pmu_ctr_validate(uint32_t cidx, uint32_t
> > > *event_idx_code)
> > > +{
> > > +       uint32_t event_idx_val;
> > > +       uint32_t event_idx_type;
> > > +       u32 hartid = current_hartid();
> > > +
> > > +       event_idx_val = active_events[hartid][cidx];
> > > +
> > > +       if (cidx >= total_ctrs || (event_idx_val ==
> > > SBI_PMU_EVENT_IDX_INVALID))
> > > +               return SBI_EINVAL;
> > > +
> > > +       event_idx_type = get_cidx_type(event_idx_val);
> > > +       if (event_idx_type >= SBI_PMU_EVENT_TYPE_MAX)
> > > +               return SBI_EINVAL;
> > > +
> > > +       *event_idx_code = get_cidx_code(event_idx_val);
> > > +
> > > +       return event_idx_type;
> > > +}
> > > +
> > > +static int pmu_ctr_read_fw(uint32_t cidx, unsigned long *cval,
> > > +                              uint32_t fw_evt_code)
> > > +{
> > > +       u32 hartid = current_hartid();
> > > +       struct sbi_pmu_fw_event fevent;
> > > +
> > > +       fevent = fw_event_map[hartid][fw_evt_code];
> > > +       *cval = fevent.curr_count;
> > > +
> > > +       return 0;
> > > +}
> > > +
> > > +/* Add a hardware counter read for completeness for future
> > > purpose
> > > */
> > > +static int pmu_ctr_read_hw(uint32_t cidx, uint64_t *cval)
> > > +{
> > > +#if __riscv_xlen == 32
> > > +       uint32_t temp, temph = 0;
> > > +
> > > +       temp = csr_read_num(CSR_MCYCLE + cidx);
> > > +       temph = csr_read_num(CSR_MCYCLEH + cidx);
> > > +       *cval = ((uint64_t)temph << 32) | temp;
> > > +#else
> > > +       *cval = csr_read_num(CSR_MCYCLE + cidx);
> > > +#endif
> > > +
> > > +       return 0;
> > > +}
> > > +
> > > +int sbi_pmu_ctr_read(uint32_t cidx, unsigned long *cval)
> > > +{
> > > +       int event_idx_type;
> > > +       uint32_t event_code;
> > > +       uint64_t cval64;
> > > +
> > > +       event_idx_type = pmu_ctr_validate(cidx, &event_code);
> > > +       if (event_idx_type < 0)
> > > +               return SBI_EINVAL;
> > > +       else if (event_idx_type == SBI_PMU_EVENT_TYPE_FW)
> > > +               pmu_ctr_read_fw(cidx, cval, event_code);
> > > +       else
> > > +               pmu_ctr_read_hw(cidx, &cval64);
> > > +
> > > +       return 0;
> > > +}
> > > +
> > > +static int pmu_add_hw_event_map(u32 eidx_start, u32 eidx_end,
> > > u32
> > > cmap,
> > > +                               uint64_t select)
> > > +{
> > > +       int i = 0;
> > > +       bool is_overlap;
> > > +       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 <
> > > 0x08))
> > > +               return SBI_EDENIED;
> > > +
> > > +       if (num_hw_events >= SBI_PMU_HW_EVENT_MAX - 1) {
> > > +               sbi_printf("Can not handle more than %d perf
> > > events\n",
> > > +                           SBI_PMU_HW_EVENT_MAX);
> > > +               return SBI_EFAIL;
> > > +       }
> > > +
> > > +       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++) {
> > > +               if (eidx_start == SBI_PMU_EVENT_RAW_IDX)
> > > +               /* All raw events have same event idx. Just do
> > > sanity
> > > check on select */
> > > +                       is_overlap =
> > > pmu_event_select_overlap(&hw_event_map[i], select);
> > > +               else
> > > +                       is_overlap =
> > > pmu_event_range_overlap(&hw_event_map[i], event);
> > > +               if (is_overlap)
> > > +                       return SBI_EINVALID_ADDR;
> > > +       }
> > > +       num_hw_events++;
> > > +
> > > +       return 0;
> > > +}
> > > +
> > > +/**
> > > + * Logical counter ids are assigned to hardware counters are
> > > assigned consecutively.
> > > + * E.g. counter0 must count MCYCLE where counter2 must count
> > > minstret. Similarly,
> > > + * counterX will mhpmcounterX.
> > > + */
> > > +int sbi_pmu_add_hw_event_counter_map(u32 eidx_start, u32
> > > eidx_end,
> > > u32 cmap)
> > > +{
> > > +       if ((eidx_start > eidx_end) || eidx_start ==
> > > SBI_PMU_EVENT_RAW_IDX ||
> > > +            eidx_end == SBI_PMU_EVENT_RAW_IDX)
> > > +               return SBI_EINVAL;
> > > +
> > > +       return pmu_add_hw_event_map(eidx_start, eidx_end, cmap,
> > > 0);
> > > +}
> > > +
> > > +int sbi_pmu_add_raw_event_counter_map(uint64_t select, u32 cmap)
> > > +{
> > > +       return pmu_add_hw_event_map(SBI_PMU_EVENT_RAW_IDX,
> > > +                                   SBI_PMU_EVENT_RAW_IDX, cmap,
> > > select);
> > > +}
> > > +
> > > +static void pmu_ctr_write_hw(uint32_t cidx, uint64_t ival)
> > > +{
> > > +#if __riscv_xlen == 32
> > > +       csr_write_num(CSR_MCYCLE + cidx, ival & 0xFFFF);
> > > +       csr_write_num(CSR_MCYCLEH + cidx, ival >> BITS_PER_LONG);
> > 
> > The above code is recommended to be modified to:
> >         csr_write_num(CSR_MCYCLE + cidx, 0);
> >         csr_write_num(CSR_MCYCLEH + cidx, ival >> BITS_PER_LONG);
> >         csr_write_num(CSR_MCYCLE + cidx, ival & 0xFFFF);
> > 
> 
> why ?
Assuming that the counter changes quickly, write the low bit first and
then the high bit. The status may have changed when the high bit is
written. For example, if you want to write 0x00000001ffffffff, the
result after writing may be 0x0000000100000000. If the high bit is
written first, the state of the low bit is uncertain, and a carry may
occur before the low bit is written. For example, if you want to write
0x00000001ffffffff, the result may be 0x00000002ffffffff after writing.
> 
> > Regards,
> > Xiang W
> > > +#else
> > > +       csr_write_num(CSR_MCYCLE + cidx, ival);
> > > +#endif
> > > +}
> > > +
> > > +static int pmu_ctr_start_hw(uint32_t cidx, uint64_t ival, bool
> > > ival_update)
> > > +{
> > > +       unsigned long mctr_en = csr_read(CSR_MCOUNTEREN);
> > > +       unsigned long mctr_inhbt = csr_read(CSR_MCOUNTINHIBIT);
> > > +
> > > +       if (cidx > num_hw_ctrs)
> > > +               return SBI_EINVAL;
> > > +
> > > +       if (__test_bit(cidx, &mctr_en) && !__test_bit(cidx,
> > > &mctr_inhbt))
> > > +               return SBI_EALREADY_STARTED;
> > > +
> > > +       __set_bit(cidx, &mctr_en);
> > > +       __clear_bit(cidx, &mctr_inhbt);
> > > +
> > > +       if (ival_update)
> > > +               pmu_ctr_write_hw(cidx, ival);
> > > +
> > > +       csr_write(CSR_MCOUNTEREN, mctr_en);
> > > +       csr_write(CSR_MCOUNTINHIBIT, mctr_inhbt);
> > > +
> > > +       return 0;
> > > +}
> > > +
> > > +static int pmu_ctr_start_fw(uint32_t cidx, uint32_t fw_evt_code,
> > > +                           uint64_t ival, bool ival_update)
> > > +{
> > > +       u32 hartid = current_hartid();
> > > +       struct sbi_pmu_fw_event *fevent;
> > > +
> > > +       fevent = &fw_event_map[hartid][fw_evt_code];
> > > +       if (ival_update)
> > > +               fevent->curr_count = ival;
> > > +       fevent->bStarted = TRUE;
> > > +
> > > +       return 0;
> > > +}
> > > +
> > > +int sbi_pmu_ctr_start(unsigned long cbase, unsigned long cmask,
> > > +                     unsigned long flags, uint64_t ival)
> > > +{
> > > +       int event_idx_type;
> > > +       uint32_t event_code;
> > > +       unsigned long ctr_mask = cmask << cbase;
> > > +       int ret = SBI_EINVAL;
> > > +       bool bUpdate = FALSE;
> > > +
> > > +       if (cbase >= total_ctrs)
> > > +               return ret;
> > > +
> > > +       if (flags & SBI_PMU_START_FLAG_SET_INIT_VALUE)
> > > +               bUpdate = TRUE;
> > > +
> > > +       for_each_set_bit_from(cbase, &ctr_mask, total_ctrs) {
> > > +               event_idx_type = pmu_ctr_validate(cbase,
> > > &event_code);
> > > +               if (event_idx_type < 0)
> > > +                       return SBI_EINVAL;
> > > +               else if (event_idx_type == SBI_PMU_EVENT_TYPE_FW)
> > > +                       ret = pmu_ctr_start_fw(cbase, event_code,
> > > ival, bUpdate);
> > > +               else
> > > +                       ret = pmu_ctr_start_hw(cbase, ival,
> > > bUpdate);
> > > +       }
> > > +
> > > +       return ret;
> > > +}
> > > +
> > > +static int pmu_ctr_stop_hw(uint32_t cidx)
> > > +{
> > > +       unsigned long mctr_en = csr_read(CSR_MCOUNTEREN);
> > > +       unsigned long mctr_inhbt = csr_read(CSR_MCOUNTINHIBIT);
> > > +
> > > +       if (__test_bit(cidx, &mctr_en) && !__test_bit(cidx,
> > > &mctr_inhbt)) {
> > > +               __set_bit(cidx, &mctr_inhbt);
> > > +               __clear_bit(cidx, &mctr_en);
> > > +               csr_write(CSR_MCOUNTEREN, mctr_en);
> > > +               csr_write(CSR_MCOUNTINHIBIT, mctr_inhbt);
> > > +               return 0;
> > > +       } else
> > > +               return SBI_EALREADY_STOPPED;
> > > +}
> > > +
> > > +static int pmu_ctr_stop_fw(uint32_t cidx, uint32_t fw_evt_code)
> > > +{
> > > +       u32 hartid = current_hartid();
> > > +
> > > +       fw_event_map[hartid][fw_evt_code].bStarted = FALSE;
> > > +
> > > +       return 0;
> > > +}
> > > +
> > > +int sbi_pmu_ctr_stop(unsigned long cbase, unsigned long cmask,
> > > +                    unsigned long flag)
> > > +{
> > > +       u32 hartid = current_hartid();
> > > +       int ret = SBI_EINVAL;
> > > +       int event_idx_type;
> > > +       uint32_t event_code;
> > > +       unsigned long ctr_mask = cmask << cbase;
> > > +
> > > +       if (cbase >= total_ctrs)
> > > +               return SBI_EINVAL;
> > > +
> > > +       for_each_set_bit_from(cbase, &ctr_mask, total_ctrs) {
> > > +               event_idx_type = pmu_ctr_validate(cbase,
> > > &event_code);
> > > +               if (event_idx_type < 0)
> > > +                       return SBI_EINVAL;
> > > +
> > > +               else if (event_idx_type == SBI_PMU_EVENT_TYPE_FW)
> > > +                       ret = pmu_ctr_stop_fw(cbase, event_code);
> > > +               else
> > > +                       ret = pmu_ctr_stop_hw(cbase);
> > > +
> > > +               if (!ret && (flag & SBI_PMU_STOP_FLAG_RESET))
> > > +                       active_events[hartid][cbase] =
> > > SBI_PMU_EVENT_IDX_INVALID;
> > > +       }
> > > +
> > > +       return ret;
> > > +}
> > > +
> > > +static int pmu_update_hw_mhpmevent(struct sbi_pmu_hw_event
> > > *hw_evt,
> > > int ctr_idx,
> > > +                                   unsigned long eindex,
> > > uint64_t
> > > data)
> > > +{
> > > +       struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
> > > +       const struct sbi_platform *plat =
> > > sbi_platform_ptr(scratch);
> > > +       uint64_t mhpmevent_val;
> > > +
> > > +       /* Get the final mhpmevent value to be written from
> > > platform
> > > */
> > > +       mhpmevent_val = sbi_platform_pmu_xlate_to_mhpmevent(plat,
> > > eindex, data);
> > > +
> > > +       if (!mhpmevent_val || ctr_idx < 3 || ctr_idx >=
> > > SBI_PMU_HW_CTR_MAX)
> > > +               return SBI_EFAIL;
> > > +
> > > +       /* TODO: The upper 8 bits of mhpmevent is reserved by
> > > sscofpmf extension.
> > > +        * Update those bits based on the flags received from
> > > supervisor.
> > > +        * The OVF bit also should be cleared here in case it was
> > > not
> > > cleared
> > > +        * during event stop.
> > > +        */
> > > +       csr_write_num(CSR_MCOUNTINHIBIT + ctr_idx,
> > > mhpmevent_val);
> > > +
> > > +       return 0;
> > > +}
> > > +
> > > +static int pmu_ctr_find_hw(unsigned long cbase, unsigned long
> > > cmask,
> > > +                          unsigned long event_idx, uint64_t
> > > data)
> > > +{
> > > +       unsigned long ctr_mask;
> > > +       int i, ret = 0, ctr_idx = SBI_ENOTSUPP;
> > > +       struct sbi_pmu_hw_event *temp;
> > > +       unsigned long mctr_en = csr_read(CSR_MCOUNTEREN);
> > > +       unsigned long mctr_inhbt = csr_read(CSR_MCOUNTINHIBIT);
> > > +       int evt_idx_code = get_cidx_code(event_idx);
> > > +
> > > +       if (cbase > num_hw_ctrs)
> > > +               return SBI_EINVAL;
> > > +
> > > +       for (i = 0; i < num_hw_events; i++) {
> > > +               temp = &hw_event_map[i];
> > > +               if ((temp->start_idx > event_idx && event_idx <
> > > temp-
> > > > end_idx) ||
> > > +                   (temp->start_idx < event_idx && event_idx >
> > > temp-
> > > > end_idx))
> > > +                       continue;
> > > +
> > > +               /* For raw events, event data is used as the
> > > select
> > > value */
> > > +               if ((event_idx == SBI_PMU_EVENT_RAW_IDX) && temp-
> > > > select != data)
> > > +                       continue;
> > > +
> > > +               ctr_mask = temp->counters & (cmask << cbase);
> > > +               for_each_set_bit_from(cbase, &ctr_mask,
> > > SBI_PMU_HW_CTR_MAX) {
> > > +                       if (!__test_bit(cbase, &mctr_en) &&
> > > +                           __test_bit(cbase, &mctr_inhbt)) {
> > > +                               ctr_idx = cbase;
> > > +                               break;
> > > +                       }
> > > +               }
> > > +       }
> > > +
> > > +       if (ctr_idx == SBI_ENOTSUPP)
> > > +               return SBI_EFAIL;
> > > +
> > > +       /* No need to update the  event selectors for fixed
> > > events */
> > > +       if (evt_idx_code != SBI_PMU_HW_CPU_CYCLES &&
> > > +           evt_idx_code != SBI_PMU_HW_INSTRUCTIONS)
> > > +               ret = pmu_update_hw_mhpmevent(temp, ctr_idx,
> > > event_idx, data);
> > > +
> > > +       if (!ret)
> > > +               ret = ctr_idx;
> > > +
> > > +       return ret;
> > > +}
> > > +
> > > +
> > > +/**
> > > + * Any firmware counter can map to any firmware event.
> > > + * Thus, select the first available fw counter after sanity
> > > + * check.
> > > + */
> > > +static int pmu_ctr_find_fw(unsigned long cbase, unsigned long
> > > cmask,
> > > u32 hartid)
> > > +{
> > > +       int i = 0;
> > > +       int fw_base;
> > > +       unsigned long ctr_mask = cmask << cbase;
> > > +
> > > +       if (cbase <= num_hw_ctrs)
> > > +               fw_base = num_hw_ctrs + 1;
> > > +       else
> > > +               fw_base = cbase;
> > > +
> > > +       for (i = fw_base; i < total_ctrs; i++)
> > > +               if ((active_events[hartid][i] ==
> > > SBI_PMU_EVENT_IDX_INVALID) &&
> > > +                   ((1UL << i) & ctr_mask))
> > > +                       return i;
> > > +
> > > +       return SBI_ENOTSUPP;
> > > +}
> > > +
> > > +int sbi_pmu_ctr_cfg_match(unsigned long cidx_base, unsigned long
> > > cidx_mask,
> > > +                         unsigned long flags, unsigned long
> > > event_idx,
> > > +                         uint64_t event_data)
> > > +{
> > > +       int ctr_idx = SBI_ENOTSUPP;
> > > +       u32 hartid = current_hartid();
> > > +       int event_type = get_cidx_type(event_idx);
> > > +       struct sbi_pmu_fw_event *fevent;
> > > +       uint32_t fw_evt_code;
> > > +       unsigned long tmp = cidx_mask << cidx_base;
> > > +
> > > +       /* Do a basic sanity check of counter base & mask */
> > > +       if (cidx_base >= total_ctrs || __fls(tmp) >= total_ctrs
> > > ||
> > > +           event_type >= SBI_PMU_EVENT_TYPE_MAX)
> > > +               return SBI_EINVAL;
> > > +
> > > +       if (flags & SBI_PMU_CFG_FLAG_SKIP_MATCH) {
> > > +               /* The caller wants to skip the match because it
> > > already knows the
> > > +                * counter idx for the given event. Verify that
> > > the
> > > counter idx
> > > +                * is still valid.
> > > +                */
> > > +               if (active_events[hartid][cidx_base] ==
> > > SBI_PMU_EVENT_IDX_INVALID)
> > > +                       return SBI_EINVAL;
> > > +               ctr_idx = cidx_base;
> > > +               goto skip_match;
> > > +       }
> > > +
> > > +       if (event_type == SBI_PMU_EVENT_TYPE_FW) {
> > > +               /* Any firmware counter can be used track any
> > > firmware event */
> > > +               ctr_idx = pmu_ctr_find_fw(cidx_base, cidx_mask,
> > > hartid);
> > > +       } else {
> > > +               ctr_idx = pmu_ctr_find_hw(cidx_base, cidx_mask,
> > > event_idx, event_data);
> > > +       }
> > > +
> > > +       if (ctr_idx < 0)
> > > +               return SBI_ENOTSUPP;
> > > +
> > > +       active_events[hartid][ctr_idx] = event_idx;
> > > +skip_match:
> > > +       if (event_type == SBI_PMU_EVENT_TYPE_HW) {
> > > +               if (flags & SBI_PMU_CFG_FLAG_CLEAR_VALUE)
> > > +                       pmu_ctr_write_hw(ctr_idx, 0);
> > > +               if (flags & SBI_PMU_CFG_FLAG_AUTO_START)
> > > +                       pmu_ctr_start_hw(ctr_idx, 0, false);
> > > +       } else if (event_type == SBI_PMU_EVENT_TYPE_FW) {
> > > +               fw_evt_code = get_cidx_code(event_idx);
> > > +               fevent = &fw_event_map[hartid][fw_evt_code];
> > > +               if (flags & SBI_PMU_CFG_FLAG_CLEAR_VALUE)
> > > +                       fevent->curr_count = 0;
> > > +               if (flags & SBI_PMU_CFG_FLAG_AUTO_START)
> > > +                       fevent->bStarted = TRUE;
> > > +       }
> > > +
> > > +       return ctr_idx;
> > > +}
> > > +
> > > +inline int sbi_pmu_ctr_incr_fw(enum sbi_pmu_fw_event_code_id
> > > fw_id)
> > > +{
> > > +       u32 hartid = current_hartid();
> > > +       struct sbi_pmu_fw_event *fevent;
> > > +
> > > +       if (unlikely(fw_id >= SBI_PMU_FW_MAX))
> > > +               return SBI_EINVAL;
> > > +
> > > +       fevent = &fw_event_map[hartid][fw_id];
> > > +
> > > +       /* PMU counters will be only enabled during performance
> > > debugging */
> > > +       if (unlikely(fevent->bStarted))
> > > +               fevent->curr_count++;
> > > +
> > > +       return 0;
> > > +}
> > > +
> > > +unsigned long sbi_pmu_num_ctr(void)
> > > +{
> > > +       return (num_hw_ctrs + SBI_PMU_FW_CTR_MAX);
> > > +}
> > > +
> > > +int sbi_pmu_ctr_get_info(uint32_t cidx, unsigned long *ctr_info)
> > > +{
> > > +       union sbi_pmu_ctr_info cinfo = {0};
> > > +       struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
> > > +
> > > +       /* Sanity check. Counter1 is not mapped at all */
> > > +       if (cidx >= total_ctrs || cidx == 1)
> > > +               return SBI_EINVAL;
> > > +
> > > +       /* We have 31 HW counters with 31 being the last
> > > index(MHPMCOUNTER31) */
> > > +       if (cidx <= num_hw_ctrs) {
> > > +               cinfo.type = SBI_PMU_CTR_TYPE_HW;
> > > +               cinfo.csr = CSR_CYCLE + cidx;
> > > +               /* mcycle & minstret are always 64 bit */
> > > +               if (cidx == 0 || cidx == 2)
> > > +                       cinfo.width = 63;
> > > +               else
> > > +                       cinfo.width =
> > > sbi_hart_mhpm_bits(scratch);
> > > +       } else {
> > > +               /* it's a firmware counter */
> > > +               cinfo.type = SBI_PMU_CTR_TYPE_FW;
> > > +               /* Firmware counters are XLEN bits wide */
> > > +               cinfo.width = BITS_PER_LONG - 1;
> > > +       }
> > > +
> > > +       *ctr_info = cinfo.value;
> > > +
> > > +       return 0;
> > > +}
> > > +
> > > +static void pmu_reset_event_map(u32 hartid)
> > > +{
> > > +       int j;
> > > +
> > > +       /* Initialize the counter to event mapping table */
> > > +       for (j = 0; j < total_ctrs; j++)
> > > +               active_events[hartid][j] =
> > > SBI_PMU_EVENT_IDX_INVALID;
> > > +       for (j = 0; j < SBI_PMU_FW_CTR_MAX; j++)
> > > +               sbi_memset(&fw_event_map[hartid][j], 0,
> > > +                          sizeof(struct sbi_pmu_fw_event));
> > > +}
> > > +
> > > +void sbi_pmu_exit(struct sbi_scratch *scratch)
> > > +{
> > > +       u32 hartid = current_hartid();
> > > +
> > > +       csr_write(CSR_MCOUNTINHIBIT, -1);
> > > +       csr_write(CSR_MCOUNTEREN, 2);
> > > +       pmu_reset_event_map(hartid);
> > > +}
> > > +
> > > +int sbi_pmu_init(struct sbi_scratch *scratch, bool cold_boot)
> > > +{
> > > +       const struct sbi_platform *plat;
> > > +
> > > +       if (!sbi_hart_has_feature(scratch,
> > > SBI_HART_HAS_MCOUNTINHIBIT))
> > > +               return SBI_ENOTSUPP;
> > > +
> > > +       if (cold_boot) {
> > > +               plat = sbi_platform_ptr(scratch);
> > > +               /* Initialize hw pmu events */
> > > +               sbi_platform_pmu_init(plat);
> > > +
> > > +               /* mcycle & minstret is available always */
> > > +               num_hw_ctrs = sbi_hart_mhpm_count(scratch) + 2;
> > > +               total_ctrs = num_hw_ctrs + SBI_PMU_FW_CTR_MAX;
> > > +       }
> > > +
> > > +       pmu_reset_event_map(current_hartid());
> > > +
> > > +       return 0;
> > > +}
> > > --
> > > 2.25.1
> > > 
> > > 
> > 
> > 
> > 
> > --
> > opensbi mailing list
> > opensbi at lists.infradead.org
> > http://lists.infradead.org/mailman/listinfo/opensbi
> 
> 
> 





More information about the opensbi mailing list