mirror of
https://github.com/Fishwaldo/opensbi.git
synced 2025-03-15 19:31:32 +00:00
The trigger allocation function uses bit shift instead of mask to check the
mapped status of the triggers. This causes index 0 to be return always. As a
result, the older triggers are overwritten.
Use the mask for MAPPED field in state word to check if the trigger is mapped.
Fixes: 97f234f15
("lib: sbi: Introduce the SBI debug triggers extension support")
Signed-off-by: Himanshu Chauhan <hchauhan@ventanamicro.com>
Reviewed-by: Anup Patel <anup@brainfault.org>
728 lines
18 KiB
C
728 lines
18 KiB
C
/*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*
|
|
* Copyright (c) 2023 Ventana Micro Systems, Inc.
|
|
*
|
|
* Author(s):
|
|
* Himanshu Chauhan <hchauhan@ventanamicro.com>
|
|
*/
|
|
|
|
#include <sbi/sbi_ecall_interface.h>
|
|
#include <sbi/sbi_csr_detect.h>
|
|
#include <sbi/sbi_platform.h>
|
|
#include <sbi/sbi_byteorder.h>
|
|
#include <sbi/sbi_console.h>
|
|
#include <sbi/sbi_domain.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 *)(unsigned long)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_MASK(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 (!sbi_hart_has_extension(scratch, SBI_HART_EXT_SDTRIG))
|
|
return SBI_SUCCESS;
|
|
|
|
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();
|
|
|
|
/* Skip probing triggers if already probed */
|
|
if (hart_state->probed)
|
|
goto _probed;
|
|
|
|
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->probed = 1;
|
|
|
|
_probed:
|
|
hart_state->available_trigs = hart_state->total_trigs;
|
|
|
|
return SBI_SUCCESS;
|
|
}
|
|
|
|
int sbi_dbtr_get_total_triggers(void)
|
|
{
|
|
struct sbi_dbtr_hart_triggers_state *hs;
|
|
struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
|
|
|
|
/*
|
|
* This function may be used during ecall registration.
|
|
* By that time the debug trigger module might not be
|
|
* initialized. If the extension is not supported, report
|
|
* number of triggers as 0.
|
|
*/
|
|
if (!sbi_hart_has_extension(scratch, SBI_HART_EXT_SDTRIG))
|
|
return 0;
|
|
|
|
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 (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(unsigned long smode,
|
|
unsigned long trig_idx_base, unsigned long trig_count)
|
|
{
|
|
struct sbi_dbtr_data_msg *xmit;
|
|
struct sbi_dbtr_trigger *trig;
|
|
struct sbi_dbtr_shmem_entry *entry;
|
|
void *shmem_base = NULL;
|
|
struct sbi_dbtr_hart_triggers_state *hs = NULL;
|
|
|
|
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(unsigned long smode,
|
|
unsigned long trig_count, unsigned long *out)
|
|
{
|
|
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 (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(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;
|
|
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 (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;
|
|
}
|