diff --git a/lib/sbi/sbi_dbtr.c b/lib/sbi/sbi_dbtr.c index 8bcb4312..01047969 100644 --- a/lib/sbi/sbi_dbtr.c +++ b/lib/sbi/sbi_dbtr.c @@ -34,6 +34,25 @@ static unsigned long hart_state_ptr_offset; sbi_scratch_write_type((__scratch), void *, hart_state_ptr_offset, \ (__hart_state)) +#define tdata_read_safe(__csr) \ + ({ \ + struct sbi_trap_info __trap = {0}; \ + csr_read_allowed((__csr), &__trap); \ + }) + +#define tdata_write_safe(__csr, __value) \ + ({ \ + struct sbi_trap_info __trap = {0}; \ + csr_write_allowed((__csr), &__trap, (__value)); \ + }) + +#define tdata_implemented(__csr) \ + ({ \ + struct sbi_trap_info __trap = {0}; \ + csr_read_allowed((__csr), &__trap); \ + !__trap.cause; \ + }) + #define INDEX_TO_TRIGGER(_index) \ ({ \ struct sbi_dbtr_trigger *__trg = NULL; \ @@ -418,7 +437,8 @@ static void dbtr_trigger_enable(struct sbi_dbtr_trigger *trig) */ csr_write(CSR_TSELECT, trig->index); csr_write(CSR_TDATA1, 0x0); - csr_write(CSR_TDATA2, trig->tdata2); + tdata_write_safe(CSR_TDATA2, trig->tdata2); + tdata_write_safe(CSR_TDATA3, trig->tdata3); csr_write(CSR_TDATA1, trig->tdata1); } @@ -463,7 +483,8 @@ static void dbtr_trigger_clear(struct sbi_dbtr_trigger *trig) csr_write(CSR_TSELECT, trig->index); csr_write(CSR_TDATA1, 0x0); - csr_write(CSR_TDATA2, 0x0); + tdata_write_safe(CSR_TDATA2, 0x0); + tdata_write_safe(CSR_TDATA3, 0x0); } static int dbtr_trigger_supported(unsigned long type) @@ -566,8 +587,8 @@ int sbi_dbtr_read_trig(unsigned long smode, trig = INDEX_TO_TRIGGER((_idx + trig_idx_base)); csr_write(CSR_TSELECT, trig->index); trig->tdata1 = csr_read(CSR_TDATA1); - trig->tdata2 = csr_read(CSR_TDATA2); - trig->tdata3 = csr_read(CSR_TDATA3); + trig->tdata2 = tdata_read_safe(CSR_TDATA2); + trig->tdata3 = tdata_read_safe(CSR_TDATA3); xmit->tstate = cpu_to_lle(trig->state); xmit->tdata1 = cpu_to_lle(trig->tdata1); xmit->tdata2 = cpu_to_lle(trig->tdata2); @@ -589,6 +610,7 @@ int sbi_dbtr_install_trig(unsigned long smode, unsigned long ctrl; struct sbi_dbtr_trigger *trig; struct sbi_dbtr_hart_triggers_state *hs = NULL; + bool tdata2_impl, tdata3_impl; hs = dbtr_thishart_state_ptr(); if (!hs) @@ -601,6 +623,15 @@ int sbi_dbtr_install_trig(unsigned long smode, sbi_hart_protection_map_range((unsigned long)shmem_base, trig_count * sizeof(*entry)); + /* + * SBI v3.0 sec 19.4 requires SBI_ERR_NOT_SUPPORTED when a trigger + * programs a non-zero value into an unimplemented optional CSR. Only + * the "whole CSR unimplemented" case is caught; WARL bits tied off + * inside an otherwise-implemented CSR are not. + */ + tdata2_impl = tdata_implemented(CSR_TDATA2); + tdata3_impl = tdata_implemented(CSR_TDATA3); + /* Check requested triggers configuration */ for_each_trig_entry(shmem_base, trig_count, typeof(*entry), entry) { recv = (struct sbi_dbtr_data_msg *)(&entry->data); @@ -619,6 +650,14 @@ int sbi_dbtr_install_trig(unsigned long smode, trig_count * sizeof(*entry)); return SBI_ERR_FAILED; } + + if ((recv->tdata2 && !tdata2_impl) || + (recv->tdata3 && !tdata3_impl)) { + *out = _idx; + sbi_hart_protection_unmap_range((unsigned long)shmem_base, + trig_count * sizeof(*entry)); + return SBI_ERR_NOT_SUPPORTED; + } } if (hs->available_trigs < trig_count) { @@ -705,6 +744,7 @@ int sbi_dbtr_update_trig(unsigned long smode, union sbi_dbtr_shmem_entry *entry; void *shmem_base = NULL; struct sbi_dbtr_hart_triggers_state *hs = NULL; + bool tdata2_impl, tdata3_impl; hs = dbtr_thishart_state_ptr(); if (!hs) @@ -718,6 +758,15 @@ int sbi_dbtr_update_trig(unsigned long smode, if (trig_count >= hs->total_trigs) return SBI_ERR_BAD_RANGE; + /* + * SBI v3.0 sec 19.5 requires SBI_ERR_NOT_SUPPORTED when a trigger + * programs a non-zero value into an unimplemented optional CSR. Only + * the "whole CSR unimplemented" case is caught; WARL bits tied off + * inside an otherwise-implemented CSR are not. + */ + tdata2_impl = tdata_implemented(CSR_TDATA2); + tdata3_impl = tdata_implemented(CSR_TDATA3); + for_each_trig_entry(shmem_base, trig_count, typeof(*entry), entry) { sbi_hart_protection_map_range((unsigned long)entry, sizeof(*entry)); trig_idx = entry->id.idx; @@ -734,6 +783,12 @@ int sbi_dbtr_update_trig(unsigned long smode, return SBI_ERR_FAILED; } + if ((entry->data.tdata2 && !tdata2_impl) || + (entry->data.tdata3 && !tdata3_impl)) { + sbi_hart_protection_unmap_range((unsigned long)entry, sizeof(*entry)); + return SBI_ERR_NOT_SUPPORTED; + } + dbtr_trigger_setup(trig, &entry->data); sbi_hart_protection_unmap_range((unsigned long)entry, sizeof(*entry)); dbtr_trigger_enable(trig);