[PATCH 14/18] dbtr: Decouple dbtr trigger index from hardware trigger number

Nicholas Piggin
2026-03-13T05:21:14Z

Some Sdtrig implementations have heterogeneous triggers, which require
the dbtr trigger index be decoupled from the hardware trigger allocated
to it.

The SBI interface supports this, prepare the implementation by creating
a new sbi_hw_trigger data structure that is mapped by the
sbi_dbtr_trigger with its HW_INDEX field.

The dbtr and hw triggers are still fixed to the same number.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
include/sbi/sbi_dbtr.h | 14 ++++++-
lib/sbi/sbi_dbtr.c | 87 ++++++++++++++++++++++++++++++------------
2 files changed, 76 insertions(+), 25 deletions(-)

diff --git a/include/sbi/sbi_dbtr.h b/include/sbi/sbi_dbtr.h
index b8b5c9ab..c0b9369e 100644
--- a/include/sbi/sbi_dbtr.h
+++ b/include/sbi/sbi_dbtr.h
@@ -48,9 +48,14 @@ struct sbi_dbtr_shmem {
unsigned long phys_hi;
};

+struct sbi_hw_trigger {
+ unsigned long index;
+ bool inuse; /* Allocated for a DBTR trigger */
+ unsigned long type_mask; /* Types supported by this trigger */
+};
+
struct sbi_dbtr_trigger {
unsigned long index;
- unsigned long type_mask;
unsigned long state;
unsigned long tdata1;
unsigned long tdata2;
@@ -69,6 +74,7 @@ struct sbi_dbtr_id_msg {
};

struct sbi_dbtr_hart_triggers_state {
+ struct sbi_hw_trigger hw_triggers[RV_MAX_TRIGGERS];
struct sbi_dbtr_trigger triggers[RV_MAX_TRIGGERS];
struct sbi_dbtr_shmem shmem;
u32 total_trigs;
@@ -89,6 +95,12 @@ struct sbi_dbtr_hart_triggers_state {
& RV_DBTR_BIT_MASK(TS, HW_IDX)); \
}while (0);

+#define GET_TRIG_HW_INDEX(_state) \
+ ({ \
+ (_state & RV_DBTR_BIT_MASK(TS, HW_IDX)) \
+ >> RV_DBTR_BIT(TS, HW_IDX); \
+ })
+
/** SBI shared mem messages layout */
union sbi_dbtr_shmem_entry {
struct sbi_dbtr_data_msg data;
diff --git a/lib/sbi/sbi_dbtr.c b/lib/sbi/sbi_dbtr.c
index 3aea486e..74b114b8 100644
--- a/lib/sbi/sbi_dbtr.c
+++ b/lib/sbi/sbi_dbtr.c
@@ -11,6 +11,7 @@
#include <sbi/sbi_csr_detect.h>
#include <sbi/sbi_platform.h>
#include <sbi/sbi_byteorder.h>
+#include <sbi/sbi_string.h>
#include <sbi/sbi_console.h>
#include <sbi/sbi_domain.h>
#include <sbi/sbi_trap.h>
@@ -43,6 +44,15 @@ static unsigned long hart_state_ptr_offset;
(__trg); \
})

+#define INDEX_TO_HW_TRIGGER(_index) \
+ ({ \
+ struct sbi_hw_trigger *__trg = NULL; \
+ struct sbi_dbtr_hart_triggers_state *__hs = NULL; \
+ __hs = dbtr_get_hart_state_ptr(sbi_scratch_thishart_ptr()); \
+ __trg = &__hs->hw_triggers[_index]; \
+ (__trg); \
+ })
+
#define for_each_trig_entry(_base, _max, _etype, _entry) \
for (int _idx = 0; _entry = ((_etype *)_base + _idx), \
_idx < _max; \
@@ -75,17 +85,6 @@ static inline void *hart_shmem_base(
hs->shmem.phys_hi, hs->shmem.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;
@@ -126,6 +125,16 @@ static inline void sbi_free_trigger(struct sbi_dbtr_trigger *trig)
if (!hart_state)
return;

+ if (trig->state & RV_DBTR_BIT_MASK(TS, MAPPED)) {
+ unsigned long hw_index = GET_TRIG_HW_INDEX(trig->state);
+ struct sbi_hw_trigger *hw_trig = INDEX_TO_HW_TRIGGER(hw_index);
+ if (!hw_trig->inuse)
+ sbi_printf("DBTR error: Hardware trigger was not marked inuse\n");
+ if (hw_index != hw_trig->index)
+ sbi_printf("DBTR error: HW index mismatch\n");
+ hw_trig->inuse = false;
+ }
+
trig->state = 0;
trig->tdata1 = 0;
trig->tdata2 = 0;
@@ -136,10 +145,15 @@ static inline void sbi_free_trigger(struct sbi_dbtr_trigger *trig)

static bool sbi_hw_trigger_probe(int i)
{
+ struct sbi_hw_trigger *hw_trig = INDEX_TO_HW_TRIGGER(i);
struct sbi_trap_info trap = {0};
unsigned long tdata1;
unsigned long val;

+ sbi_memset(hw_trig, 0, sizeof(*hw_trig));
+
+ hw_trig->index = i;
+
csr_write_allowed(CSR_TSELECT, &trap, i);
if (trap.cause)
return false;
@@ -172,12 +186,12 @@ static bool sbi_hw_trigger_probe(int i)
if (type == 0)
return false;

- sbi_trigger_init(INDEX_TO_TRIGGER(i), BIT(type), i);
+ hw_trig->type_mask = BIT(type);
} else {
if (val == 1)
return false;

- sbi_trigger_init(INDEX_TO_TRIGGER(i), val, i);
+ hw_trig->type_mask = val;
}

return true;
@@ -213,6 +227,17 @@ int sbi_dbtr_init(struct sbi_scratch *scratch, bool coldboot)
if (hart_state->probed)
goto _probed;

+ /*
+ * dbtr triggers are what is exposed to the supervisor. hw triggers
+ * are what is selected by tselect. For now these are tied to the
+ * same index.
+ */
+ for (i = 0; i < RV_MAX_TRIGGERS; i++) {
+ struct sbi_dbtr_trigger *trig = INDEX_TO_TRIGGER(i);
+ sbi_memset(trig, 0, sizeof(*trig));
+ trig->index = i;
+ }
+
for (i = 0; i < RV_MAX_TRIGGERS; i++) {
if (sbi_hw_trigger_probe(i))
hart_state->total_trigs++;
@@ -304,6 +329,7 @@ int sbi_dbtr_setup_shmem(const struct sbi_domain *dom, unsigned long smode,
}

static void dbtr_trigger_setup(struct sbi_dbtr_trigger *trig,
+ struct sbi_hw_trigger *hw_trig,
struct sbi_dbtr_data_msg *recv)
{
unsigned long tdata1;
@@ -321,7 +347,9 @@ static void dbtr_trigger_setup(struct sbi_dbtr_trigger *trig,

__set_bit(RV_DBTR_BIT(TS, MAPPED), &trig->state);

- SET_TRIG_HW_INDEX(trig->state, trig->index);
+ SET_TRIG_HW_INDEX(trig->state, hw_trig->index);
+
+ hw_trig->inuse = true;

switch (TDATA1_GET_TYPE(tdata1)) {
case RISCV_DBTR_TRIG_MCONTROL:
@@ -423,7 +451,7 @@ static void dbtr_trigger_enable(struct sbi_dbtr_trigger *trig)
* 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_TSELECT, GET_TRIG_HW_INDEX(state));
csr_write(CSR_TDATA1, 0x0);
csr_write(CSR_TDATA2, trig->tdata2);
csr_write(CSR_TDATA1, trig->tdata1);
@@ -459,7 +487,7 @@ static void dbtr_trigger_disable(struct sbi_dbtr_trigger *trig)
break;
}

- csr_write(CSR_TSELECT, trig->index);
+ csr_write(CSR_TSELECT, GET_TRIG_HW_INDEX(trig->state));
csr_write(CSR_TDATA1, trig->tdata1);
}

@@ -468,7 +496,7 @@ 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_TSELECT, GET_TRIG_HW_INDEX(trig->state));
csr_write(CSR_TDATA1, 0x0);
csr_write(CSR_TDATA2, 0x0);
}
@@ -527,7 +555,7 @@ 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;
+ struct sbi_hw_trigger *hw_trig;
int i;
struct sbi_dbtr_hart_triggers_state *hs;

@@ -541,9 +569,9 @@ int sbi_dbtr_num_trig(unsigned long data, unsigned long *out)
}

for (i = 0; i < hs->total_trigs; i++) {
- trig = INDEX_TO_TRIGGER(i);
+ hw_trig = INDEX_TO_HW_TRIGGER(i);

- if (__test_bit(type, &trig->type_mask))
+ if (__test_bit(type, &hw_trig->type_mask))
total++;
}

@@ -589,7 +617,7 @@ int sbi_dbtr_read_trig(unsigned long smode,

xmit->tstate = cpu_to_lle(trig->state);
if (trig->state & RV_DBTR_BIT_MASK(TS, MAPPED)) {
- csr_write(CSR_TSELECT, trig->index);
+ csr_write(CSR_TSELECT, GET_TRIG_HW_INDEX(trig->state));
trig->tdata1 = csr_read(CSR_TDATA1);
trig->tdata2 = csr_read_allowed(CSR_TDATA2, &trap);
trig->tdata3 = csr_read_allowed(CSR_TDATA3, &trap);
@@ -666,6 +694,7 @@ int sbi_dbtr_install_trig(unsigned long smode,

/* Install triggers */
for_each_trig_entry(shmem_base, trig_count, typeof(*entry), entry) {
+ struct sbi_hw_trigger *hw_trig;
/*
* Since we have already checked if enough triggers are
* available, trigger allocation must succeed.
@@ -675,10 +704,18 @@ int sbi_dbtr_install_trig(unsigned long smode,
recv = (struct sbi_dbtr_data_msg *)(&entry->data);
xmit = (struct sbi_dbtr_id_msg *)(&entry->id);

- dbtr_trigger_setup(trig, recv);
+ hw_trig = INDEX_TO_HW_TRIGGER(trig->index); /* 1:1 trig / HW idx */
+ if (hw_trig->inuse) {
+ *out = hs->available_trigs;
+ sbi_hart_protection_unmap_range((unsigned long)shmem_base,
+ trig_count * sizeof(*entry));
+ sbi_printf("DBTR error: Hardware trigger already inuse\n");
+ return SBI_ERR_FAILED;
+ }
+
+ dbtr_trigger_setup(trig, hw_trig, recv);
dbtr_trigger_enable(trig);
xmit->idx = cpu_to_lle(trig->index);
-
}

sbi_hart_protection_unmap_range((unsigned long)shmem_base,
@@ -771,6 +808,7 @@ int sbi_dbtr_update_trig(unsigned long smode,
unsigned long trig_count, unsigned long *out)
{
struct sbi_dbtr_trigger *trig;
+ struct sbi_hw_trigger *hw_trig;
union sbi_dbtr_shmem_entry *entry;
struct sbi_dbtr_data_msg *recv;
void *shmem_base = NULL;
@@ -835,7 +873,8 @@ int sbi_dbtr_update_trig(unsigned long smode,
/* Update triggers */
for_each_trig_entry(shmem_base, trig_count, typeof(*entry), entry) {
trig = INDEX_TO_TRIGGER(entry->id.idx);
- dbtr_trigger_setup(trig, &entry->data);
+ hw_trig = INDEX_TO_HW_TRIGGER(GET_TRIG_HW_INDEX(trig->state));
+ dbtr_trigger_setup(trig, hw_trig, &entry->data);
dbtr_trigger_enable(trig);