[PATCH v2 4/7] lib: sbi: Introduce the SBI debug triggers extension support
Himanshu Chauhan
hchauhan at ventanamicro.com
Sun Jan 7 22:55:22 PST 2024
RISC-V Debug specification includes Sdtrig ISA extension
which describes Trigger Module. Triggers can cause
a breakpoint exception or trace action without execution
of a special instruction. They can be used to implement
hardware breakpoints and watchpoints for native debugging.
The SBI Debut Trigger extension (Draft v6) can be found at:
https://lists.riscv.org/g/tech-debug/topic/99825362#1302
This patch is an initial implementation of SBI Debug
Trigger Extension (Draft v6) in OpenSBI.
The following features are supported:
* mcontrol, mcontrol6 triggers
* Breakpoint and trace actions
NOTE: Chained triggers are not supported
Signed-off-by: Himanshu Chauhan <hchauhan at ventanamicro.com>
---
include/sbi/sbi_dbtr.h | 127 +++++++
lib/sbi/objects.mk | 1 +
lib/sbi/sbi_dbtr.c | 742 +++++++++++++++++++++++++++++++++++++++++
lib/sbi/sbi_init.c | 9 +
4 files changed, 879 insertions(+)
create mode 100644 include/sbi/sbi_dbtr.h
create mode 100644 lib/sbi/sbi_dbtr.c
diff --git a/include/sbi/sbi_dbtr.h b/include/sbi/sbi_dbtr.h
new file mode 100644
index 0000000..20855ae
--- /dev/null
+++ b/include/sbi/sbi_dbtr.h
@@ -0,0 +1,127 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Ventana Micro Systems, Inc.
+ *
+ * Authors:
+ * Himanshu Chauhan <hchauhan at ventanamicro.com>
+ */
+
+#ifndef __SBI_DBTR_H__
+#define __SBI_DBTR_H__
+
+#include <sbi/riscv_dbtr.h>
+
+#include <sbi/sbi_hartmask.h>
+#include <sbi/sbi_scratch.h>
+#include <sbi/sbi_domain.h>
+#include <sbi/sbi_types.h>
+#include <sbi/sbi_byteorder.h>
+
+enum {
+ RV_DBTR_DECLARE_BIT(TS, MAPPED, 0), /* trigger mapped to hw trigger */
+ RV_DBTR_DECLARE_BIT(TS, U, 1),
+ RV_DBTR_DECLARE_BIT(TS, S, 2),
+ RV_DBTR_DECLARE_BIT(TS, VU, 3),
+ RV_DBTR_DECLARE_BIT(TS, VS, 4),
+ RV_DBTR_DECLARE_BIT(TS, HAVE_TRIG, 5), /* H/w dbtr details available */
+ RV_DBTR_DECLARE_BIT(TS, HW_IDX, 8), /* Hardware index of trigger */
+};
+
+enum {
+ RV_DBTR_DECLARE_BIT_MASK(TS, MAPPED, 1),
+ RV_DBTR_DECLARE_BIT_MASK(TS, U, 1),
+ RV_DBTR_DECLARE_BIT_MASK(TS, S, 1),
+ RV_DBTR_DECLARE_BIT_MASK(TS, VU, 1),
+ RV_DBTR_DECLARE_BIT_MASK(TS, VS, 1),
+ RV_DBTR_DECLARE_BIT_MASK(TS, HAVE_TRIG, 1),
+ RV_DBTR_DECLARE_BIT_MASK(TS, HW_IDX, (__riscv_xlen-9)),
+};
+
+#if __riscv_xlen == 64
+#define SBI_DBTR_SHMEM_INVALID_ADDR 0xFFFFFFFFFFFFFFFFUL
+#elif __riscv_xlen == 32
+#define SBI_DBTR_SHMEM_INVALID_ADDR 0xFFFFFFFFUL
+#error "Unexpected __riscv_xlen"
+#endif
+
+struct sbi_dbtr_shmem {
+ unsigned long phys_lo;
+ unsigned long phys_hi;
+};
+
+struct sbi_dbtr_trigger {
+ unsigned long index;
+ unsigned long type_mask;
+ unsigned long state;
+ unsigned long tdata1;
+ unsigned long tdata2;
+ unsigned long tdata3;
+};
+
+struct sbi_dbtr_data_msg {
+ unsigned long tstate;
+ unsigned long tdata1;
+ unsigned long tdata2;
+ unsigned long tdata3;
+};
+
+struct sbi_dbtr_id_msg {
+ unsigned long idx;
+};
+
+struct sbi_dbtr_hart_triggers_state {
+ struct sbi_dbtr_trigger triggers[RV_MAX_TRIGGERS];
+ struct sbi_dbtr_shmem shmem;
+ u32 total_trigs;
+ u32 available_trigs;
+ u32 hartid;
+};
+
+#define TDATA1_GET_TYPE(_t1) \
+ EXTRACT_FIELD(_t1, RV_DBTR_BIT_MASK(TDATA1, TYPE))
+
+/* Set the hardware index of trigger in logical trigger state */
+#define SET_TRIG_HW_INDEX(_state, _idx) \
+ do { \
+ _state &= ~RV_DBTR_BIT_MASK(TS, HW_IDX); \
+ _state |= (((unsigned long)_idx \
+ << RV_DBTR_BIT(TS, HW_IDX)) \
+ & RV_DBTR_BIT_MASK(TS, HW_IDX)); \
+ }while (0);
+
+/** SBI shared mem messages layout */
+struct sbi_dbtr_shmem_entry {
+ struct sbi_dbtr_data_msg data;
+ struct sbi_dbtr_id_msg id;
+};
+
+#define SBI_DBTR_SHMEM_ALIGN_MASK ((__riscv_xlen / 8) - 1)
+
+/** Initialize debug triggers */
+int sbi_dbtr_init(struct sbi_scratch *scratch, bool coldboot);
+
+/** SBI DBTR extension functions */
+int sbi_dbtr_supported(void);
+int sbi_dbtr_setup_shmem(const struct sbi_domain *dom, unsigned long smode,
+ unsigned long shmem_phys_lo,
+ unsigned long shmem_phys_hi);
+int sbi_dbtr_num_trig(unsigned long trig_tdata1, unsigned long *out);
+int sbi_dbtr_read_trig(const struct sbi_domain *dom, unsigned long smode,
+ unsigned long trig_idx_base, unsigned long trig_count);
+int sbi_dbtr_install_trig(const struct sbi_domain *dom, unsigned long smode,
+ unsigned long trig_count, unsigned long *out);
+int sbi_dbtr_uninstall_trig(unsigned long trig_idx_base,
+ unsigned long trig_idx_mask);
+int sbi_dbtr_enable_trig(unsigned long trig_idx_base,
+ unsigned long trig_idx_mask);
+int sbi_dbtr_update_trig(const struct sbi_domain *dom,
+ unsigned long smode,
+ unsigned long trig_idx_base,
+ unsigned long trig_idx_mask);
+int sbi_dbtr_disable_trig(unsigned long trig_idx_base,
+ unsigned long trig_idx_mask);
+
+int sbi_dbtr_get_total_triggers(void);
+
+#endif
diff --git a/lib/sbi/objects.mk b/lib/sbi/objects.mk
index c699187..c7de150 100644
--- a/lib/sbi/objects.mk
+++ b/lib/sbi/objects.mk
@@ -70,6 +70,7 @@ libsbi-objs-y += sbi_irqchip.o
libsbi-objs-y += sbi_misaligned_ldst.o
libsbi-objs-y += sbi_platform.o
libsbi-objs-y += sbi_pmu.o
+libsbi-objs-y += sbi_dbtr.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_dbtr.c b/lib/sbi/sbi_dbtr.c
new file mode 100644
index 0000000..1bd29c1
--- /dev/null
+++ b/lib/sbi/sbi_dbtr.c
@@ -0,0 +1,742 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Ventana Micro Systems, Inc.
+ *
+ * Author(s):
+ * Himanshu Chauhan <hchauhan at ventanamicro.com>
+ */
+
+#include <sbi/sbi_ecall_interface.h>
+#include <sbi/sbi_csr_detect.h>
+#include <sbi/sbi_platform.h>
+#include <sbi/sbi_console.h>
+#include <sbi/sbi_trap.h>
+#include <sbi/sbi_dbtr.h>
+#include <sbi/sbi_heap.h>
+#include <sbi/riscv_encoding.h>
+#include <sbi/riscv_asm.h>
+
+
+/** Offset of pointer to HART's debug triggers info in scratch space */
+static unsigned long hart_state_ptr_offset;
+
+#define dbtr_get_hart_state_ptr(__scratch) \
+ sbi_scratch_read_type((__scratch), void *, hart_state_ptr_offset)
+
+#define dbtr_thishart_state_ptr() \
+ dbtr_get_hart_state_ptr(sbi_scratch_thishart_ptr())
+
+#define dbtr_set_hart_state_ptr(__scratch, __hart_state) \
+ sbi_scratch_write_type((__scratch), void *, hart_state_ptr_offset, \
+ (__hart_state))
+
+#define INDEX_TO_TRIGGER(_index) \
+ ({ \
+ struct sbi_dbtr_trigger *__trg = NULL; \
+ struct sbi_dbtr_hart_triggers_state *__hs = NULL; \
+ __hs = dbtr_get_hart_state_ptr(sbi_scratch_thishart_ptr()); \
+ __trg = &__hs->triggers[_index]; \
+ (__trg); \
+ })
+
+#define for_each_trig_entry(_base, _max, _etype, _entry) \
+ for (int _idx = 0; _entry = ((_etype *)_base + _idx), \
+ _idx < _max; \
+ _idx++, _entry = ((_etype *)_base + _idx))
+
+#if __riscv_xlen == 64
+#define DBTR_SHMEM_MAKE_PHYS(_p_hi, _p_lo) (((u64)(_p_hi) << 32) | (_p_lo))
+#elif __riscv_xlen == 32
+#define DBTR_SHMEM_MAKE_PHYS(_p_hi, _p_lo) (((u64)(_p_hi) << 32) | (_p_lo))
+#else
+#error "Undefined XLEN"
+#endif
+
+static inline int sbi_dbtr_shmem_disabled(void)
+{
+ struct sbi_dbtr_hart_triggers_state *hs = NULL;
+
+ hs = dbtr_get_hart_state_ptr(sbi_scratch_thishart_ptr());
+
+ if (!hs)
+ return 1;
+
+ return (hs->shmem.phys_lo == SBI_DBTR_SHMEM_INVALID_ADDR &&
+ hs->shmem.phys_hi == SBI_DBTR_SHMEM_INVALID_ADDR
+ ? 1 : 0);
+}
+
+static inline void sbi_dbtr_disable_shmem(void)
+{
+ struct sbi_dbtr_hart_triggers_state *hs = NULL;
+
+ hs = dbtr_get_hart_state_ptr(sbi_scratch_thishart_ptr());
+
+ if (!hs)
+ return;
+
+ hs->shmem.phys_lo = SBI_DBTR_SHMEM_INVALID_ADDR;
+ hs->shmem.phys_hi = SBI_DBTR_SHMEM_INVALID_ADDR;
+}
+
+static inline void *hart_shmem_base(void)
+{
+ struct sbi_dbtr_shmem* shmem;
+ unsigned long phys_hi, phys_lo;
+ struct sbi_dbtr_hart_triggers_state *hs = NULL;
+
+ hs = dbtr_get_hart_state_ptr(sbi_scratch_thishart_ptr());
+
+ if (!hs)
+ return NULL;
+
+ shmem = &hs->shmem;
+
+ phys_hi = (shmem->phys_hi == SBI_DBTR_SHMEM_INVALID_ADDR
+ ? shmem->phys_hi : 0);
+ phys_lo = (shmem->phys_lo == SBI_DBTR_SHMEM_INVALID_ADDR
+ ? 0 : shmem->phys_lo);
+
+ return ((void *)DBTR_SHMEM_MAKE_PHYS(phys_hi, phys_lo));
+}
+
+static void sbi_trigger_init(struct sbi_dbtr_trigger *trig,
+ unsigned long type_mask, unsigned long idx)
+{
+ trig->type_mask = type_mask;
+ trig->state = 0;
+ trig->tdata1 = 0;
+ trig->tdata2 = 0;
+ trig->tdata3 = 0;
+ trig->index = idx;
+}
+
+static inline struct sbi_dbtr_trigger *sbi_alloc_trigger(void)
+{
+ int i;
+ struct sbi_dbtr_trigger *f_trig = NULL;
+ struct sbi_dbtr_hart_triggers_state *hart_state;
+
+ hart_state = dbtr_thishart_state_ptr();
+ if (!hart_state)
+ return NULL;
+
+ if (hart_state->available_trigs <= 0)
+ return NULL;
+
+ for (i = 0; i < hart_state->total_trigs; i++) {
+ f_trig = INDEX_TO_TRIGGER(i);
+ if (f_trig->state & RV_DBTR_BIT(TS, MAPPED))
+ continue;
+ hart_state->available_trigs--;
+ break;
+ }
+
+ if (i == hart_state->total_trigs)
+ return NULL;
+
+ __set_bit(RV_DBTR_BIT(TS, MAPPED), &f_trig->state);
+
+ return f_trig;
+}
+
+static inline void sbi_free_trigger(struct sbi_dbtr_trigger *trig)
+{
+ struct sbi_dbtr_hart_triggers_state *hart_state;
+
+ if (trig == NULL)
+ return;
+
+ hart_state = dbtr_thishart_state_ptr();
+ if (!hart_state)
+ return;
+
+ trig->state = 0;
+ trig->tdata1 = 0;
+ trig->tdata2 = 0;
+ trig->tdata3 = 0;
+
+ hart_state->available_trigs++;
+}
+
+int sbi_dbtr_init(struct sbi_scratch *scratch, bool coldboot)
+{
+ struct sbi_trap_info trap = {0};
+ unsigned long tdata1;
+ unsigned long val;
+ int i;
+ struct sbi_dbtr_hart_triggers_state *hart_state = NULL;
+
+ if (coldboot) {
+ hart_state_ptr_offset = sbi_scratch_alloc_type_offset(void *);
+ if (!hart_state_ptr_offset)
+ return SBI_ENOMEM;
+ }
+
+ hart_state = dbtr_get_hart_state_ptr(scratch);
+ if (!hart_state) {
+ hart_state = sbi_zalloc(sizeof(*hart_state));
+ if (!hart_state)
+ return SBI_ENOMEM;
+ hart_state->hartid = current_hartid();
+ dbtr_set_hart_state_ptr(scratch, hart_state);
+ }
+
+ /* disable the shared memory */
+ sbi_dbtr_disable_shmem();
+
+ for (i = 0; i < RV_MAX_TRIGGERS; i++) {
+ csr_write_allowed(CSR_TSELECT, (ulong)&trap, i);
+ if (trap.cause)
+ break;
+
+ val = csr_read_allowed(CSR_TSELECT, (ulong)&trap);
+ if (trap.cause)
+ break;
+
+ /*
+ * Read back tselect and check that it contains the
+ * written value
+ */
+ if (val != i)
+ break;
+
+ val = csr_read_allowed(CSR_TINFO, (ulong)&trap);
+ if (trap.cause) {
+ /*
+ * If reading tinfo caused an exception, the
+ * debugger must read tdata1 to discover the
+ * type.
+ */
+ tdata1 = csr_read_allowed(CSR_TDATA1,
+ (ulong)&trap);
+ if (trap.cause)
+ break;
+
+ if (TDATA1_GET_TYPE(tdata1) == 0)
+ break;
+
+ sbi_trigger_init(INDEX_TO_TRIGGER(i),
+ BIT(TDATA1_GET_TYPE(tdata1)),
+ i);
+ hart_state->total_trigs++;
+ } else {
+ if (val == 1)
+ break;
+
+ sbi_trigger_init(INDEX_TO_TRIGGER(i), val, i);
+ hart_state->total_trigs++;
+ }
+ }
+
+ hart_state->available_trigs = hart_state->total_trigs;
+
+ return 0;
+}
+
+int sbi_dbtr_supported(void)
+{
+ struct sbi_dbtr_hart_triggers_state *hs;
+
+ hs = dbtr_thishart_state_ptr();
+ if (!hs)
+ return 0;
+
+ return !!hs->total_trigs;
+}
+
+int sbi_dbtr_get_total_triggers(void)
+{
+ struct sbi_dbtr_hart_triggers_state *hs;
+
+ hs = dbtr_thishart_state_ptr();
+ if (!hs)
+ return 0;
+
+ return hs->total_trigs;
+}
+
+int sbi_dbtr_setup_shmem(const struct sbi_domain *dom, unsigned long smode,
+ unsigned long shmem_phys_lo,
+ unsigned long shmem_phys_hi)
+{
+ u32 hartid = current_hartid();
+ struct sbi_dbtr_hart_triggers_state *hart_state;
+
+ if (smode != PRV_S) {
+ sbi_dprintf("%s: Non supervisor mode. Access denied\n",
+ __func__);
+ return SBI_ERR_DENIED;
+ }
+
+ if (dom && !sbi_domain_is_assigned_hart(dom, hartid)) {
+ sbi_dprintf("%s: calling hart not assigned to this domain\n",
+ __func__);
+ return SBI_ERR_DENIED;
+ }
+
+ /* call is to disable shared memory */
+ if (shmem_phys_lo == SBI_DBTR_SHMEM_INVALID_ADDR
+ && shmem_phys_hi == SBI_DBTR_SHMEM_INVALID_ADDR) {
+ sbi_dbtr_disable_shmem();
+ return SBI_SUCCESS;
+ }
+
+ /* the shared memory must be disabled on this hart */
+ if (!sbi_dbtr_shmem_disabled())
+ return SBI_ERR_ALREADY_AVAILABLE;
+
+ /* lower physical address must be XLEN/8 bytes aligned */
+ if (shmem_phys_lo & SBI_DBTR_SHMEM_ALIGN_MASK)
+ return SBI_ERR_INVALID_PARAM;
+
+ if (dom && !sbi_domain_check_addr(dom, shmem_phys_lo, smode,
+ SBI_DOMAIN_READ | SBI_DOMAIN_WRITE))
+ return SBI_ERR_INVALID_ADDRESS;
+
+ if (shmem_phys_hi != SBI_DBTR_SHMEM_INVALID_ADDR) {
+ if (dom &&
+ !sbi_domain_check_addr(dom, shmem_phys_hi, smode,
+ SBI_DOMAIN_READ | SBI_DOMAIN_WRITE))
+ return SBI_ERR_INVALID_ADDRESS;
+ }
+
+ hart_state = dbtr_thishart_state_ptr();
+ if (!hart_state)
+ return SBI_ERR_FAILED;
+
+ hart_state->shmem.phys_lo = shmem_phys_lo;
+ hart_state->shmem.phys_hi = shmem_phys_hi;
+
+ return SBI_SUCCESS;
+}
+
+static void dbtr_trigger_setup(struct sbi_dbtr_trigger *trig,
+ struct sbi_dbtr_data_msg *recv)
+{
+ unsigned long tdata1;
+
+ if (!trig)
+ return;
+
+ trig->tdata1 = lle_to_cpu(recv->tdata1);
+ trig->tdata2 = lle_to_cpu(recv->tdata2);
+ trig->tdata3 = lle_to_cpu(recv->tdata3);
+
+ tdata1 = lle_to_cpu(recv->tdata1);
+
+ trig->state = 0;
+
+ __set_bit(RV_DBTR_BIT(TS, MAPPED), &trig->state);
+
+ SET_TRIG_HW_INDEX(trig->state, trig->index);
+
+ switch (TDATA1_GET_TYPE(tdata1)) {
+ case RISCV_DBTR_TRIG_MCONTROL:
+ if (__test_bit(RV_DBTR_BIT(MC, U), &tdata1))
+ __set_bit(RV_DBTR_BIT(TS, U), &trig->state);
+
+ if (__test_bit(RV_DBTR_BIT(MC, S), &tdata1))
+ __set_bit(RV_DBTR_BIT(TS, S), &trig->state);
+ break;
+ case RISCV_DBTR_TRIG_MCONTROL6:
+ if (__test_bit(RV_DBTR_BIT(MC6, U), &tdata1))
+ __set_bit(RV_DBTR_BIT(TS, U), &trig->state);
+
+ if (__test_bit(RV_DBTR_BIT(MC6, S), &tdata1))
+ __set_bit(RV_DBTR_BIT(TS, S), &trig->state);
+
+ if (__test_bit(RV_DBTR_BIT(MC6, VU), &tdata1))
+ __set_bit(RV_DBTR_BIT(TS, VU), &trig->state);
+
+ if (__test_bit(RV_DBTR_BIT(MC6, VS), &tdata1))
+ __set_bit(RV_DBTR_BIT(TS, VS), &trig->state);
+ break;
+ default:
+ sbi_dprintf("%s: Unknown type (tdata1: 0x%lx Type: %ld)\n",
+ __func__, tdata1, TDATA1_GET_TYPE(tdata1));
+ break;
+ }
+}
+
+static inline void update_bit(unsigned long new, int nr, volatile unsigned long *addr)
+{
+ if (new)
+ __set_bit(nr, addr);
+ else
+ __clear_bit(nr, addr);
+}
+
+static void dbtr_trigger_enable(struct sbi_dbtr_trigger *trig)
+{
+ unsigned long state;
+ unsigned long tdata1;
+
+ if (!trig && !(trig->state & RV_DBTR_BIT_MASK(TS, MAPPED)))
+ return;
+
+ state = trig->state;
+ tdata1 = trig->tdata1;
+
+ switch (TDATA1_GET_TYPE(tdata1)) {
+ case RISCV_DBTR_TRIG_MCONTROL:
+ update_bit(state & RV_DBTR_BIT_MASK(TS, U),
+ RV_DBTR_BIT(MC, U), &trig->tdata1);
+ update_bit(state & RV_DBTR_BIT_MASK(TS, S),
+ RV_DBTR_BIT(MC, S), &trig->tdata1);
+ break;
+ case RISCV_DBTR_TRIG_MCONTROL6:
+ update_bit(state & RV_DBTR_BIT_MASK(TS, VU),
+ RV_DBTR_BIT(MC6, VU), &trig->tdata1);
+ update_bit(state & RV_DBTR_BIT_MASK(TS, VS),
+ RV_DBTR_BIT(MC6, VS), &trig->tdata1);
+ update_bit(state & RV_DBTR_BIT_MASK(TS, U),
+ RV_DBTR_BIT(MC6, U), &trig->tdata1);
+ update_bit(state & RV_DBTR_BIT_MASK(TS, S),
+ RV_DBTR_BIT(MC6, S), &trig->tdata1);
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * RISC-V Debug Support v1.0.0 section 5.5:
+ * Debugger cannot simply set a trigger by writing tdata1, then tdata2,
+ * etc. The current value of tdata2 might not be legal with the new
+ * value of tdata1. To help with this situation, it is guaranteed that
+ * writing 0 to tdata1 disables the trigger, and leaves it in a state
+ * where tdata2 and tdata3 can be written with any value that makes
+ * sense for any trigger type supported by this trigger.
+ */
+ csr_write(CSR_TSELECT, trig->index);
+ csr_write(CSR_TDATA1, 0x0);
+ csr_write(CSR_TDATA2, trig->tdata2);
+ csr_write(CSR_TDATA1, trig->tdata1);
+}
+
+static void dbtr_trigger_disable(struct sbi_dbtr_trigger *trig)
+{
+ unsigned long tdata1;
+
+ if (!trig && !(trig->state & RV_DBTR_BIT_MASK(TS, MAPPED)))
+ return;
+
+ tdata1 = trig->tdata1;
+
+ switch (TDATA1_GET_TYPE(tdata1)) {
+ case RISCV_DBTR_TRIG_MCONTROL:
+ __clear_bit(RV_DBTR_BIT(MC, U), &trig->tdata1);
+ __clear_bit(RV_DBTR_BIT(MC, S), &trig->tdata1);
+ break;
+ case RISCV_DBTR_TRIG_MCONTROL6:
+ __clear_bit(RV_DBTR_BIT(MC6, VU), &trig->tdata1);
+ __clear_bit(RV_DBTR_BIT(MC6, VS), &trig->tdata1);
+ __clear_bit(RV_DBTR_BIT(MC6, U), &trig->tdata1);
+ __clear_bit(RV_DBTR_BIT(MC6, S), &trig->tdata1);
+ break;
+ default:
+ break;
+ }
+
+ csr_write(CSR_TSELECT, trig->index);
+ csr_write(CSR_TDATA1, trig->tdata1);
+}
+
+static void dbtr_trigger_clear(struct sbi_dbtr_trigger *trig)
+{
+ if (!trig && !(trig->state & RV_DBTR_BIT_MASK(TS, MAPPED)))
+ return;
+
+ csr_write(CSR_TSELECT, trig->index);
+ csr_write(CSR_TDATA1, 0x0);
+ csr_write(CSR_TDATA2, 0x0);
+}
+
+static int dbtr_trigger_supported(unsigned long type)
+{
+ switch (type) {
+ case RISCV_DBTR_TRIG_MCONTROL:
+ case RISCV_DBTR_TRIG_MCONTROL6:
+ return 1;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int dbtr_trigger_valid(unsigned long type, unsigned long tdata)
+{
+ switch (type) {
+ case RISCV_DBTR_TRIG_MCONTROL:
+ if (!(tdata & RV_DBTR_BIT_MASK(MC, DMODE)) &&
+ !(tdata & RV_DBTR_BIT_MASK(MC, M)))
+ return 1;
+ break;
+ case RISCV_DBTR_TRIG_MCONTROL6:
+ if (!(tdata & RV_DBTR_BIT_MASK(MC6, DMODE)) &&
+ !(tdata & RV_DBTR_BIT_MASK(MC6, M)))
+ return 1;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+int sbi_dbtr_num_trig(unsigned long data, unsigned long *out)
+{
+ unsigned long type = TDATA1_GET_TYPE(data);
+ u32 hartid = current_hartid();
+ unsigned long total = 0;
+ struct sbi_dbtr_trigger *trig;
+ int i;
+ struct sbi_dbtr_hart_triggers_state *hs;
+
+ hs = dbtr_thishart_state_ptr();
+ if (!hs)
+ return SBI_ERR_FAILED;
+
+ if (data == 0) {
+ *out = hs->total_trigs;
+ return SBI_SUCCESS;
+ }
+
+ for (i = 0; i < hs->total_trigs; i++) {
+ trig = INDEX_TO_TRIGGER(i);
+
+ if (__test_bit(type, &trig->type_mask))
+ total++;
+ }
+
+ sbi_dprintf("%s: hart%d: total triggers of type %lu: %lu\n",
+ __func__, hartid, type, total);
+
+ *out = total;
+ return SBI_SUCCESS;
+}
+
+int sbi_dbtr_read_trig(const struct sbi_domain *dom, unsigned long smode,
+ unsigned long trig_idx_base, unsigned long trig_count)
+{
+ struct sbi_dbtr_data_msg *xmit;
+ u32 hartid = current_hartid();
+ struct sbi_dbtr_trigger *trig;
+ struct sbi_dbtr_shmem_entry *entry;
+ void *shmem_base = NULL;
+ struct sbi_dbtr_hart_triggers_state *hs = NULL;
+
+ if (smode != PRV_S)
+ return SBI_ERR_DENIED;
+ if (dom && !sbi_domain_is_assigned_hart(dom, hartid))
+ return SBI_ERR_DENIED;
+
+ hs = dbtr_thishart_state_ptr();
+ if (!hs)
+ return SBI_ERR_FAILED;
+
+ if (trig_idx_base >= hs->total_trigs ||
+ trig_idx_base + trig_count >= hs->total_trigs)
+ return SBI_ERR_INVALID_PARAM;
+
+ if (sbi_dbtr_shmem_disabled())
+ return SBI_ERR_NO_SHMEM;
+
+ shmem_base = hart_shmem_base();
+
+ for_each_trig_entry(shmem_base, trig_count, typeof(*entry), entry) {
+ sbi_hart_map_saddr((unsigned long)entry, sizeof(*entry));
+ xmit = &entry->data;
+ trig = INDEX_TO_TRIGGER((_idx + trig_idx_base));
+ xmit->tstate = cpu_to_lle(trig->state);
+ xmit->tdata1 = cpu_to_lle(trig->tdata1);
+ xmit->tdata2 = cpu_to_lle(trig->tdata2);
+ xmit->tdata3 = cpu_to_lle(trig->tdata3);
+ sbi_hart_unmap_saddr();
+ }
+
+ return SBI_SUCCESS;
+}
+
+int sbi_dbtr_install_trig(const struct sbi_domain *dom, unsigned long smode,
+ unsigned long trig_count, unsigned long *out)
+{
+ u32 hartid = current_hartid();
+ void *shmem_base = NULL;
+ struct sbi_dbtr_shmem_entry *entry;
+ struct sbi_dbtr_data_msg *recv;
+ struct sbi_dbtr_id_msg *xmit;
+ unsigned long ctrl;
+ struct sbi_dbtr_trigger *trig;
+ struct sbi_dbtr_hart_triggers_state *hs = NULL;
+
+ if (smode != PRV_S)
+ return SBI_ERR_DENIED;
+ if (dom && !sbi_domain_is_assigned_hart(dom, hartid))
+ return SBI_ERR_DENIED;
+
+ if (sbi_dbtr_shmem_disabled())
+ return SBI_ERR_NO_SHMEM;
+
+ shmem_base = hart_shmem_base();
+ hs = dbtr_thishart_state_ptr();
+
+ /* Check requested triggers configuration */
+ for_each_trig_entry(shmem_base, trig_count, typeof(*entry), entry) {
+ sbi_hart_map_saddr((unsigned long)entry, sizeof(*entry));
+ recv = (struct sbi_dbtr_data_msg *)(&entry->data);
+ ctrl = recv->tdata1;
+
+ if (!dbtr_trigger_supported(TDATA1_GET_TYPE(ctrl))) {
+ *out = _idx;
+ sbi_hart_unmap_saddr();
+ return SBI_ERR_FAILED;
+ }
+
+ if (!dbtr_trigger_valid(TDATA1_GET_TYPE(ctrl), ctrl)) {
+ *out = _idx;
+ sbi_hart_unmap_saddr();
+ return SBI_ERR_FAILED;
+ }
+ sbi_hart_unmap_saddr();
+ }
+
+ if (hs->available_trigs < trig_count) {
+ *out = hs->available_trigs;
+ return SBI_ERR_FAILED;
+ }
+
+ /* Install triggers */
+ for_each_trig_entry(shmem_base, trig_count, typeof(*entry), entry) {
+ /*
+ * Since we have already checked if enough triggers are
+ * available, trigger allocation must succeed.
+ */
+ trig = sbi_alloc_trigger();
+
+ sbi_hart_map_saddr((unsigned long)entry, sizeof(*entry));
+
+ recv = (struct sbi_dbtr_data_msg *)(&entry->data);
+ xmit = (struct sbi_dbtr_id_msg *)(&entry->id);
+
+ dbtr_trigger_setup(trig, recv);
+ dbtr_trigger_enable(trig);
+ xmit->idx = cpu_to_lle(trig->index);
+ sbi_hart_unmap_saddr();
+ }
+
+ return SBI_SUCCESS;
+}
+
+int sbi_dbtr_uninstall_trig(unsigned long trig_idx_base,
+ unsigned long trig_idx_mask)
+{
+ unsigned long trig_mask = trig_idx_mask << trig_idx_base;
+ unsigned long idx = trig_idx_base;
+ struct sbi_dbtr_trigger *trig;
+ struct sbi_dbtr_hart_triggers_state *hs;
+
+ hs = dbtr_thishart_state_ptr();
+ if (!hs)
+ return SBI_ERR_FAILED;
+
+ for_each_set_bit_from(idx, &trig_mask, hs->total_trigs) {
+ trig = INDEX_TO_TRIGGER(idx);
+ if (!(trig->state & RV_DBTR_BIT_MASK(TS, MAPPED)))
+ return SBI_ERR_INVALID_PARAM;
+
+ dbtr_trigger_clear(trig);
+
+ sbi_free_trigger(trig);
+ }
+
+ return SBI_SUCCESS;
+}
+
+int sbi_dbtr_enable_trig(unsigned long trig_idx_base,
+ unsigned long trig_idx_mask)
+{
+ unsigned long trig_mask = trig_idx_mask << trig_idx_base;
+ unsigned long idx = trig_idx_base;
+ struct sbi_dbtr_trigger *trig;
+ struct sbi_dbtr_hart_triggers_state *hs;
+
+ hs = dbtr_thishart_state_ptr();
+ if (!hs)
+ return SBI_ERR_FAILED;
+
+ for_each_set_bit_from(idx, &trig_mask, hs->total_trigs) {
+ trig = INDEX_TO_TRIGGER(idx);
+ sbi_dprintf("%s: enable trigger %lu\n", __func__, idx);
+ dbtr_trigger_enable(trig);
+ }
+
+ return SBI_SUCCESS;
+}
+
+int sbi_dbtr_update_trig(const struct sbi_domain *dom,
+ unsigned long smode,
+ unsigned long trig_idx_base,
+ unsigned long trig_idx_mask)
+{
+ unsigned long trig_mask = trig_idx_mask << trig_idx_base;
+ unsigned long idx = trig_idx_base;
+ u32 hartid = current_hartid();
+ struct sbi_dbtr_data_msg *recv;
+ unsigned long uidx = 0;
+ struct sbi_dbtr_trigger *trig;
+ struct sbi_dbtr_shmem_entry *entry;
+ void *shmem_base = NULL;
+ struct sbi_dbtr_hart_triggers_state *hs = NULL;
+
+ if (smode != PRV_S)
+ return SBI_ERR_DENIED;
+ if (dom && !sbi_domain_is_assigned_hart(dom, hartid))
+ return SBI_ERR_DENIED;
+
+ if (sbi_dbtr_shmem_disabled())
+ return SBI_ERR_NO_SHMEM;
+
+ shmem_base = hart_shmem_base();
+ hs = dbtr_thishart_state_ptr();
+ if (!hs)
+ return SBI_ERR_FAILED;
+
+ for_each_set_bit_from(idx, &trig_mask, hs->total_trigs) {
+ trig = INDEX_TO_TRIGGER(idx);
+
+ if (!(trig->state & RV_DBTR_BIT_MASK(TS, MAPPED)))
+ return SBI_ERR_INVALID_PARAM;
+
+ entry = (shmem_base + uidx * sizeof(*entry));
+ recv = &entry->data;
+
+ trig->tdata2 = lle_to_cpu(recv->tdata2);
+ dbtr_trigger_enable(trig);
+ uidx++;
+ }
+
+ return SBI_SUCCESS;
+}
+
+int sbi_dbtr_disable_trig(unsigned long trig_idx_base,
+ unsigned long trig_idx_mask)
+{
+ unsigned long trig_mask = trig_idx_mask << trig_idx_base;
+ unsigned long idx = trig_idx_base;
+ struct sbi_dbtr_trigger *trig;
+ struct sbi_dbtr_hart_triggers_state *hs;
+
+ hs = dbtr_thishart_state_ptr();
+ if (!hs)
+ return SBI_ERR_FAILED;
+
+ for_each_set_bit_from(idx, &trig_mask, hs->total_trigs) {
+ trig = INDEX_TO_TRIGGER(idx);
+ dbtr_trigger_disable(trig);
+ }
+
+ return SBI_SUCCESS;
+}
diff --git a/lib/sbi/sbi_init.c b/lib/sbi/sbi_init.c
index 6a98e13..0dcde27 100644
--- a/lib/sbi/sbi_init.c
+++ b/lib/sbi/sbi_init.c
@@ -23,6 +23,7 @@
#include <sbi/sbi_irqchip.h>
#include <sbi/sbi_platform.h>
#include <sbi/sbi_pmu.h>
+#include <sbi/sbi_dbtr.h>
#include <sbi/sbi_system.h>
#include <sbi/sbi_string.h>
#include <sbi/sbi_timer.h>
@@ -322,6 +323,10 @@ static void __noreturn init_coldboot(struct sbi_scratch *scratch, u32 hartid)
sbi_hart_hang();
}
+ rc = sbi_dbtr_init(scratch, true);
+ if (rc)
+ sbi_hart_hang();
+
sbi_boot_print_banner(scratch);
rc = sbi_irqchip_init(scratch, true);
@@ -439,6 +444,10 @@ static void __noreturn init_warm_startup(struct sbi_scratch *scratch,
if (rc)
sbi_hart_hang();
+ rc = sbi_dbtr_init(scratch, false);
+ if (rc)
+ sbi_hart_hang();
+
rc = sbi_irqchip_init(scratch, false);
if (rc)
sbi_hart_hang();
--
2.34.1
More information about the opensbi
mailing list