Software APIs
ibex.c
1// Copyright lowRISC contributors (OpenTitan project).
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
5#include "sw/device/silicon_creator/lib/drivers/ibex.h"
6
7#include "hw/top/dt/dt_rv_core_ibex.h"
12
13#include "hw/top/rv_core_ibex_regs.h"
14
15static const dt_rv_core_ibex_t kRvCoreIbexDt = kDtRvCoreIbex;
16
17/**
18 * Base address of the rv_core_ibex registers.
19 *
20 */
21static inline uint32_t rv_core_ibex_base(void) {
22 return dt_rv_core_ibex_reg_block(kRvCoreIbexDt, kDtRvCoreIbexRegBlockCfg);
23}
24
25uint32_t ibex_fpga_version(void) {
26 const uint32_t kBase = rv_core_ibex_base();
27 return abs_mmio_read32(kBase + RV_CORE_IBEX_FPGA_INFO_REG_OFFSET);
28}
29
30size_t ibex_addr_remap_slots(void) { return RV_CORE_IBEX_PARAM_NUM_REGIONS; }
31
32void ibex_addr_remap_set(size_t slot, uint32_t matching_addr,
33 uint32_t remap_addr, size_t size) {
34 HARDENED_CHECK_LT(slot, RV_CORE_IBEX_PARAM_NUM_REGIONS);
35 const uint32_t kBase = rv_core_ibex_base();
36 slot *= sizeof(uint32_t);
37 // Work-around for opentitan#22884: Mask off bits below the alignment size
38 // prior to programming the REMAP_ADDR register.
39 size = size - 1;
40 uint32_t match = (matching_addr & ~size) | size >> 1;
41 remap_addr &= ~size;
42
43 sec_mmio_write32(kBase + RV_CORE_IBEX_IBUS_ADDR_MATCHING_0_REG_OFFSET + slot,
44 match);
45 sec_mmio_write32(kBase + RV_CORE_IBEX_DBUS_ADDR_MATCHING_0_REG_OFFSET + slot,
46 match);
47
48 sec_mmio_write32(kBase + RV_CORE_IBEX_IBUS_REMAP_ADDR_0_REG_OFFSET + slot,
49 remap_addr);
50 sec_mmio_write32(kBase + RV_CORE_IBEX_DBUS_REMAP_ADDR_0_REG_OFFSET + slot,
51 remap_addr);
52
53 sec_mmio_write32(kBase + RV_CORE_IBEX_IBUS_ADDR_EN_0_REG_OFFSET + slot, 1);
54 sec_mmio_write32(kBase + RV_CORE_IBEX_DBUS_ADDR_EN_0_REG_OFFSET + slot, 1);
55 icache_invalidate();
56}
57
58uint32_t ibex_addr_remap_get(size_t slot) {
59 const uint32_t kBase = rv_core_ibex_base();
60 HARDENED_CHECK_LT(slot, RV_CORE_IBEX_PARAM_NUM_REGIONS);
61 slot *= sizeof(uint32_t);
62 if (abs_mmio_read32(kBase + RV_CORE_IBEX_IBUS_ADDR_EN_0_REG_OFFSET + slot)) {
63 return abs_mmio_read32(kBase + RV_CORE_IBEX_IBUS_REMAP_ADDR_0_REG_OFFSET +
64 slot);
65 } else {
66 return 0;
67 }
68}
69
70void ibex_addr_remap_lockdown(size_t slot) {
71 const uint32_t kBase = rv_core_ibex_base();
72 HARDENED_CHECK_LT(slot, RV_CORE_IBEX_PARAM_NUM_REGIONS);
73 slot *= sizeof(uint32_t);
74 sec_mmio_write32(kBase + RV_CORE_IBEX_IBUS_REGWEN_0_REG_OFFSET + slot, 0);
75 sec_mmio_write32(kBase + RV_CORE_IBEX_DBUS_REGWEN_0_REG_OFFSET + slot, 0);
76}
77
78bool ibex_addr_remap_is_enabled(size_t slot) {
79 const uint32_t kBase = rv_core_ibex_base();
80 HARDENED_CHECK_LT(slot, RV_CORE_IBEX_PARAM_NUM_REGIONS);
81 slot *= sizeof(uint32_t);
82
83 uint32_t reg_en_i =
84 sec_mmio_read32(kBase + RV_CORE_IBEX_IBUS_ADDR_EN_0_REG_OFFSET + slot);
85 uint32_t reg_en_i_mask = 1 << RV_CORE_IBEX_DBUS_ADDR_EN_0_EN_0_BIT;
86 uint32_t reg_en_d =
87 sec_mmio_read32(kBase + RV_CORE_IBEX_DBUS_ADDR_EN_0_REG_OFFSET + slot);
88 uint32_t reg_en_d_mask = 1 << RV_CORE_IBEX_IBUS_ADDR_EN_0_EN_0_BIT;
89
90 return (reg_en_i & reg_en_i_mask) && (reg_en_d & reg_en_d_mask);
91}
92
93static bool remap_verify(uint32_t reg_matching, uint32_t reg_remap,
94 uint32_t matching_addr, uint32_t remap_addr,
95 size_t size) {
96 /* Check that matching register is non-zero
97 * (otherwise the NAPOT formula below does not work) */
98 if (reg_matching == 0) {
99 return false;
100 }
101
102 /* decode NAPOT encoding */
103 uint32_t reg_matching_size =
104 1 << bitfield_count_trailing_zeroes32(~reg_matching);
105 uint32_t reg_matching_addr = reg_matching & ~(reg_matching_size - 1);
106
107 /* Check that the matching address is within the remap range */
108 if (matching_addr < reg_matching_addr ||
109 matching_addr - reg_matching_addr >= reg_matching_size) {
110 return false;
111 }
112
113 /* We know that the matching address is withing the remap range, we can now
114 safely check for the size validity */
115 if (size > reg_matching_size - (matching_addr - reg_matching_addr)) {
116 return false;
117 }
118
119 /* Check that remapping offset is identical */
120 if (remap_addr - matching_addr != reg_remap - reg_matching_addr) {
121 return false;
122 }
123
124 return true;
125}
126
127bool ibex_addr_remap_verify(size_t slot, uint32_t matching_addr,
128 uint32_t remap_addr, size_t size) {
129 const uint32_t kBase = rv_core_ibex_base();
130 HARDENED_CHECK_LT(slot, RV_CORE_IBEX_PARAM_NUM_REGIONS);
131 slot *= sizeof(uint32_t);
132
133 /* Check IBUS remapping */
134 uint32_t reg_matching = sec_mmio_read32(
135 kBase + RV_CORE_IBEX_IBUS_ADDR_MATCHING_0_REG_OFFSET + slot);
136 uint32_t reg_remap_addr =
137 sec_mmio_read32(kBase + RV_CORE_IBEX_IBUS_REMAP_ADDR_0_REG_OFFSET + slot);
138 if (!remap_verify(reg_matching, reg_remap_addr, matching_addr, remap_addr,
139 size)) {
140 return false;
141 }
142
143 /* Check DBUS remapping */
144 reg_matching = sec_mmio_read32(
145 kBase + RV_CORE_IBEX_DBUS_ADDR_MATCHING_0_REG_OFFSET + slot);
146 reg_remap_addr =
147 sec_mmio_read32(kBase + RV_CORE_IBEX_DBUS_REMAP_ADDR_0_REG_OFFSET + slot);
148 if (!remap_verify(reg_matching, reg_remap_addr, matching_addr, remap_addr,
149 size)) {
150 return false;
151 }
152
153 return true;
154}
155
156void ibex_enable_nmi(ibex_nmi_source_t nmi_src) {
157 abs_mmio_write32(rv_core_ibex_base() + RV_CORE_IBEX_NMI_ENABLE_REG_OFFSET,
158 nmi_src);
159}
160
161void ibex_clear_nmi(ibex_nmi_source_t nmi_src) {
162 abs_mmio_write32(rv_core_ibex_base() + RV_CORE_IBEX_NMI_STATE_REG_OFFSET,
163 nmi_src);
164}
165
166// `extern` declarations to give the inline functions in the corresponding
167// header a link location.
168extern void ibex_mcycle_zero(void);
169extern uint32_t ibex_mcycle32(void);
170extern uint64_t ibex_mcycle(void);
171extern uint64_t ibex_time_to_cycles(uint64_t time_us);