Software APIs
dif_rv_core_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/lib/dif/dif_rv_core_ibex.h"
6
7#include <stdbool.h>
8#include <stddef.h>
9#include <stdint.h>
10
14
15// #include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
16#include "rv_core_ibex_regs.h"
17
18/**
19 * Error code constants of `dif_rv_core_ibex_error_status_t` are masks for the
20 * bits of RV_CORE_IBEX_ERR_STATUS_REG register.
21 */
22static_assert(kDifRvCoreIbexErrorStatusRegisterTransmissionIntegrity ==
23 1 << RV_CORE_IBEX_ERR_STATUS_REG_INTG_ERR_BIT,
24 "Layout of RV_CORE_IBEX_ERR_STATUS_REG register changed.");
25static_assert(kDifRvCoreIbexErrorStatusFatalResponseIntegrity ==
26 1 << RV_CORE_IBEX_ERR_STATUS_FATAL_INTG_ERR_BIT,
27 "Layout of RV_CORE_IBEX_ERR_STATUS_REG register changed.");
28static_assert(kDifRvCoreIbexErrorStatusFatalInternalError ==
29 1 << RV_CORE_IBEX_ERR_STATUS_FATAL_CORE_ERR_BIT,
30 "Layout of RV_CORE_IBEX_ERR_STATUS_REG register changed.");
31static_assert(kDifRvCoreIbexErrorStatusRecoverableInternal ==
32 1 << RV_CORE_IBEX_ERR_STATUS_RECOV_CORE_ERR_BIT,
33 "Layout of RV_CORE_IBEX_ERR_STATUS_REG register changed.");
34
36 uint32_t matching;
37 uint32_t remap;
38 uint32_t en;
39 uint32_t lock;
40} ibex_addr_translation_regs_t;
41
42static const ibex_addr_translation_regs_t kRegsMap
43 [kDifRvCoreIbexAddrTranslationSlotCount]
44 [kDifRvCoreIbexAddrTranslationSlotCount] = {
45 [kDifRvCoreIbexAddrTranslationSlot_0]
46 [kDifRvCoreIbexAddrTranslationDBus] =
47 {
48 .matching = RV_CORE_IBEX_DBUS_ADDR_MATCHING_0_REG_OFFSET,
49 .remap = RV_CORE_IBEX_DBUS_REMAP_ADDR_0_REG_OFFSET,
50 .en = RV_CORE_IBEX_DBUS_ADDR_EN_0_REG_OFFSET,
51 .lock = RV_CORE_IBEX_DBUS_REGWEN_0_REG_OFFSET,
52 },
53 [kDifRvCoreIbexAddrTranslationSlot_0]
54 [kDifRvCoreIbexAddrTranslationIBus] =
55 {
56 .matching = RV_CORE_IBEX_IBUS_ADDR_MATCHING_0_REG_OFFSET,
57 .remap = RV_CORE_IBEX_IBUS_REMAP_ADDR_0_REG_OFFSET,
58 .en = RV_CORE_IBEX_IBUS_ADDR_EN_0_REG_OFFSET,
59 .lock = RV_CORE_IBEX_IBUS_REGWEN_0_REG_OFFSET,
60 },
61 [kDifRvCoreIbexAddrTranslationSlot_1]
62 [kDifRvCoreIbexAddrTranslationDBus] =
63 {
64 .matching = RV_CORE_IBEX_DBUS_ADDR_MATCHING_1_REG_OFFSET,
65 .remap = RV_CORE_IBEX_DBUS_REMAP_ADDR_1_REG_OFFSET,
66 .en = RV_CORE_IBEX_DBUS_ADDR_EN_1_REG_OFFSET,
67 .lock = RV_CORE_IBEX_DBUS_REGWEN_1_REG_OFFSET,
68 },
69 [kDifRvCoreIbexAddrTranslationSlot_1]
70 [kDifRvCoreIbexAddrTranslationIBus] =
71 {
72 .matching = RV_CORE_IBEX_IBUS_ADDR_MATCHING_1_REG_OFFSET,
73 .remap = RV_CORE_IBEX_IBUS_REMAP_ADDR_1_REG_OFFSET,
74 .en = RV_CORE_IBEX_IBUS_ADDR_EN_1_REG_OFFSET,
75 .lock = RV_CORE_IBEX_IBUS_REGWEN_1_REG_OFFSET,
76 },
77};
78
79/**
80 * Convert the region address and size into a natural power of two address.
81 *
82 * @param addr Region start address.
83 * @param size region size.
84 * @return Napot address
85 */
86static uint32_t to_napot(uint32_t addr, size_t size) {
87 return addr | (size - 1) >> 1;
88}
89
90/**
91 * Split a natural power of two address into a start address and size.
92 *
93 * @param napot Address formated in NAPOT.
94 * @param size Pointer to receive the region size.
95 * @return The region start address.
96 */
97static uint32_t from_napot(uint32_t napot, size_t *size) {
98 for (size_t i = 1; i < sizeof(uint32_t) * 8; ++i) {
99 uint32_t ref = (1u << i) - 1;
100 if ((napot & ref) == ref >> 1) {
101 *size = 1u << i;
102 break;
103 }
104 }
105 return napot & ~((*size - 1) >> 1);
106}
107
108dif_result_t dif_rv_core_ibex_configure_addr_translation(
109 const dif_rv_core_ibex_t *rv_core_ibex,
110 dif_rv_core_ibex_addr_translation_slot_t slot,
111 dif_rv_core_ibex_addr_translation_bus_t bus,
112 dif_rv_core_ibex_addr_translation_mapping_t addr_map) {
113 if (rv_core_ibex == NULL || slot >= kDifRvCoreIbexAddrTranslationSlotCount ||
114 bus >= kDifRvCoreIbexAddrTranslationBusCount ||
115 !bitfield_is_power_of_two32(addr_map.size)) {
116 return kDifBadArg;
117 }
118
119 const ibex_addr_translation_regs_t regs = kRegsMap[slot][bus];
120
121 if (mmio_region_read32(rv_core_ibex->base_addr, (ptrdiff_t)regs.lock) == 0) {
122 return kDifLocked;
123 }
124
125 uint32_t mask = to_napot(addr_map.matching_addr, addr_map.size);
126 mmio_region_write32(rv_core_ibex->base_addr, (ptrdiff_t)regs.matching, mask);
127 mmio_region_write32(rv_core_ibex->base_addr, (ptrdiff_t)regs.remap,
128 addr_map.remap_addr);
129 icache_invalidate();
130 return kDifOk;
131}
132
133dif_result_t dif_rv_core_ibex_enable_addr_translation(
134 const dif_rv_core_ibex_t *rv_core_ibex,
135 dif_rv_core_ibex_addr_translation_slot_t slot,
136 dif_rv_core_ibex_addr_translation_bus_t bus) {
137 if (rv_core_ibex == NULL || slot >= kDifRvCoreIbexAddrTranslationSlotCount ||
138 bus >= kDifRvCoreIbexAddrTranslationBusCount) {
139 return kDifBadArg;
140 }
141 const ibex_addr_translation_regs_t regs = kRegsMap[slot][bus];
142 if (mmio_region_read32(rv_core_ibex->base_addr, (ptrdiff_t)regs.lock) == 0) {
143 return kDifLocked;
144 }
145 mmio_region_write32(rv_core_ibex->base_addr, (ptrdiff_t)regs.en, 1);
146 icache_invalidate();
147 return kDifOk;
148}
149
150dif_result_t dif_rv_core_ibex_disable_addr_translation(
151 const dif_rv_core_ibex_t *rv_core_ibex,
152 dif_rv_core_ibex_addr_translation_slot_t slot,
153 dif_rv_core_ibex_addr_translation_bus_t bus) {
154 if (rv_core_ibex == NULL || slot >= kDifRvCoreIbexAddrTranslationSlotCount ||
155 bus >= kDifRvCoreIbexAddrTranslationBusCount) {
156 return kDifBadArg;
157 }
158 const ibex_addr_translation_regs_t regs = kRegsMap[slot][bus];
159 if (mmio_region_read32(rv_core_ibex->base_addr, (ptrdiff_t)regs.lock) == 0) {
160 return kDifLocked;
161 }
162 mmio_region_write32(rv_core_ibex->base_addr, (ptrdiff_t)regs.en, 0);
163 icache_invalidate();
164 return kDifOk;
165}
166
167dif_result_t dif_rv_core_ibex_read_addr_translation(
168 const dif_rv_core_ibex_t *rv_core_ibex,
169 dif_rv_core_ibex_addr_translation_slot_t slot,
170 dif_rv_core_ibex_addr_translation_bus_t bus,
171 dif_rv_core_ibex_addr_translation_mapping_t *addr_map) {
172 if (rv_core_ibex == NULL || addr_map == NULL ||
173 slot >= kDifRvCoreIbexAddrTranslationSlotCount ||
174 bus >= kDifRvCoreIbexAddrTranslationBusCount) {
175 return kDifBadArg;
176 }
177
178 const ibex_addr_translation_regs_t regs = kRegsMap[slot][bus];
179
180 const uint32_t reg =
181 mmio_region_read32(rv_core_ibex->base_addr, (ptrdiff_t)regs.matching);
182 addr_map->matching_addr = from_napot(reg, &addr_map->size);
183
184 addr_map->remap_addr =
185 mmio_region_read32(rv_core_ibex->base_addr, (ptrdiff_t)regs.remap);
186
187 return kDifOk;
188}
189
190dif_result_t dif_rv_core_ibex_lock_addr_translation(
191 const dif_rv_core_ibex_t *rv_core_ibex,
192 dif_rv_core_ibex_addr_translation_slot_t slot,
193 dif_rv_core_ibex_addr_translation_bus_t bus) {
194 if (rv_core_ibex == NULL || slot >= kDifRvCoreIbexAddrTranslationSlotCount ||
195 bus >= kDifRvCoreIbexAddrTranslationBusCount) {
196 return kDifBadArg;
197 }
198 const ibex_addr_translation_regs_t regs = kRegsMap[slot][bus];
199
200 // Only locks in case it is not locked already.
201 if (mmio_region_read32(rv_core_ibex->base_addr, (ptrdiff_t)regs.lock) == 1) {
202 mmio_region_write32(rv_core_ibex->base_addr, (ptrdiff_t)regs.lock, 0);
203 }
204
205 return kDifOk;
206}
207
208dif_result_t dif_rv_core_ibex_get_error_status(
209 const dif_rv_core_ibex_t *rv_core_ibex,
210 dif_rv_core_ibex_error_status_t *error_status) {
211 if (rv_core_ibex == NULL || error_status == NULL) {
212 return kDifBadArg;
213 }
214
215 *error_status = mmio_region_read32(rv_core_ibex->base_addr,
216 RV_CORE_IBEX_ERR_STATUS_REG_OFFSET);
217
218 return kDifOk;
219}
220
221dif_result_t dif_rv_core_ibex_clear_error_status(
222 const dif_rv_core_ibex_t *rv_core_ibex,
223 dif_rv_core_ibex_error_status_t error_status) {
224 if (rv_core_ibex == NULL ||
225 (error_status & ~(uint32_t)kDifRvCoreIbexErrorStatusAll) != 0) {
226 return kDifBadArg;
227 }
228
229 mmio_region_write32(rv_core_ibex->base_addr,
230 RV_CORE_IBEX_ERR_STATUS_REG_OFFSET, error_status);
231
232 return kDifOk;
233}
234
235dif_result_t dif_rv_core_ibex_enable_nmi(const dif_rv_core_ibex_t *rv_core_ibex,
236 dif_rv_core_ibex_nmi_source_t nmi) {
237 if (rv_core_ibex == NULL || nmi & ~(uint32_t)kDifRvCoreIbexNmiSourceAll) {
238 return kDifBadArg;
239 }
240
241 uint32_t reg = 0;
242 reg = bitfield_bit32_write(
243 reg, RV_CORE_IBEX_NMI_ENABLE_ALERT_EN_BIT,
244 (nmi & kDifRvCoreIbexNmiSourceAlert) == kDifRvCoreIbexNmiSourceAlert);
245 reg = bitfield_bit32_write(
246 reg, RV_CORE_IBEX_NMI_ENABLE_WDOG_EN_BIT,
247 (nmi & kDifRvCoreIbexNmiSourceWdog) == kDifRvCoreIbexNmiSourceWdog);
248
249 mmio_region_write32(rv_core_ibex->base_addr,
250 RV_CORE_IBEX_NMI_ENABLE_REG_OFFSET, reg);
251
252 return kDifOk;
253}
254
255dif_result_t dif_rv_core_ibex_get_nmi_state(
256 const dif_rv_core_ibex_t *rv_core_ibex,
257 dif_rv_core_ibex_nmi_state_t *nmi_state) {
258 if (rv_core_ibex == NULL || nmi_state == NULL) {
259 return kDifBadArg;
260 }
261
262 *nmi_state = (dif_rv_core_ibex_nmi_state_t){0};
263
264 uint32_t reg = mmio_region_read32(rv_core_ibex->base_addr,
265 RV_CORE_IBEX_NMI_ENABLE_REG_OFFSET);
266 nmi_state->alert_enabled =
267 bitfield_bit32_read(reg, RV_CORE_IBEX_NMI_ENABLE_ALERT_EN_BIT);
268 nmi_state->wdog_enabled =
269 bitfield_bit32_read(reg, RV_CORE_IBEX_NMI_ENABLE_WDOG_EN_BIT);
270
271 reg = mmio_region_read32(rv_core_ibex->base_addr,
272 RV_CORE_IBEX_NMI_STATE_REG_OFFSET);
273 nmi_state->alert_raised =
274 bitfield_bit32_read(reg, RV_CORE_IBEX_NMI_STATE_ALERT_BIT);
275 nmi_state->wdog_barked =
276 bitfield_bit32_read(reg, RV_CORE_IBEX_NMI_STATE_WDOG_BIT);
277
278 return kDifOk;
279}
280
281dif_result_t dif_rv_core_ibex_clear_nmi_state(
282 const dif_rv_core_ibex_t *rv_core_ibex, dif_rv_core_ibex_nmi_source_t nmi) {
283 if (rv_core_ibex == NULL || nmi & ~(uint32_t)kDifRvCoreIbexNmiSourceAll) {
284 return kDifBadArg;
285 }
286
287 uint32_t reg = 0;
288 reg = bitfield_bit32_write(
289 reg, RV_CORE_IBEX_NMI_STATE_ALERT_BIT,
290 (nmi & kDifRvCoreIbexNmiSourceAlert) == kDifRvCoreIbexNmiSourceAlert);
291 reg = bitfield_bit32_write(
292 reg, RV_CORE_IBEX_NMI_STATE_WDOG_BIT,
293 (nmi & kDifRvCoreIbexNmiSourceWdog) == kDifRvCoreIbexNmiSourceWdog);
294
295 mmio_region_write32(rv_core_ibex->base_addr,
296 RV_CORE_IBEX_NMI_STATE_REG_OFFSET, reg);
297 return kDifOk;
298}
299
300dif_result_t dif_rv_core_ibex_get_rnd_status(
301 const dif_rv_core_ibex_t *rv_core_ibex,
302 dif_rv_core_ibex_rnd_status_t *status) {
303 if (rv_core_ibex == NULL || status == NULL) {
304 return kDifBadArg;
305 }
306
307 *status = mmio_region_read32(rv_core_ibex->base_addr,
308 RV_CORE_IBEX_RND_STATUS_REG_OFFSET);
309 return kDifOk;
310}
311
312dif_result_t dif_rv_core_ibex_read_rnd_data(
313 const dif_rv_core_ibex_t *rv_core_ibex, uint32_t *data) {
314 if (rv_core_ibex == NULL || data == NULL) {
315 return kDifBadArg;
316 }
317
318 *data = mmio_region_read32(rv_core_ibex->base_addr,
319 RV_CORE_IBEX_RND_DATA_REG_OFFSET);
320 return kDifOk;
321}
322
323dif_result_t dif_rv_core_ibex_read_fpga_info(
324 const dif_rv_core_ibex_t *rv_core_ibex,
325 dif_rv_core_ibex_fpga_info_t *info) {
326 if (rv_core_ibex == NULL || info == NULL) {
327 return kDifBadArg;
328 }
329
330 *info = mmio_region_read32(rv_core_ibex->base_addr,
331 RV_CORE_IBEX_FPGA_INFO_REG_OFFSET);
332 return kDifOk;
333}
334
335dif_result_t dif_rv_core_ibex_get_sw_recov_err_alert(
336 const dif_rv_core_ibex_t *rv_core_ibex, bool *enabled) {
337 if (rv_core_ibex == NULL || enabled == NULL) {
338 return kDifBadArg;
339 }
340 uint32_t reg = mmio_region_read32(rv_core_ibex->base_addr,
341 RV_CORE_IBEX_SW_RECOV_ERR_REG_OFFSET);
342 *enabled = reg != kMultiBitBool4False;
343 return kDifOk;
344}
345
346dif_result_t dif_rv_core_ibex_trigger_sw_recov_err_alert(
347 const dif_rv_core_ibex_t *rv_core_ibex) {
348 if (rv_core_ibex == NULL) {
349 return kDifBadArg;
350 }
351
352 uint32_t reg = 0;
353 reg = bitfield_field32_write(reg, RV_CORE_IBEX_SW_RECOV_ERR_VAL_FIELD,
354 kMultiBitBool4True);
355
356 mmio_region_write32(rv_core_ibex->base_addr,
357 RV_CORE_IBEX_SW_RECOV_ERR_REG_OFFSET, reg);
358 return kDifOk;
359}
360
361dif_result_t dif_rv_core_ibex_get_sw_fatal_err_alert(
362 const dif_rv_core_ibex_t *rv_core_ibex, bool *enabled) {
363 if (rv_core_ibex == NULL || enabled == NULL) {
364 return kDifBadArg;
365 }
366 uint32_t reg = mmio_region_read32(rv_core_ibex->base_addr,
367 RV_CORE_IBEX_SW_FATAL_ERR_REG_OFFSET);
368 *enabled = reg != kMultiBitBool4False;
369 return kDifOk;
370}
371
372dif_result_t dif_rv_core_ibex_trigger_sw_fatal_err_alert(
373 const dif_rv_core_ibex_t *rv_core_ibex) {
374 if (rv_core_ibex == NULL) {
375 return kDifBadArg;
376 }
377
378 uint32_t reg = 0;
379 reg = bitfield_field32_write(reg, RV_CORE_IBEX_SW_FATAL_ERR_VAL_FIELD,
380 kMultiBitBool4True);
381
382 mmio_region_write32(rv_core_ibex->base_addr,
383 RV_CORE_IBEX_SW_FATAL_ERR_REG_OFFSET, reg);
384 return kDifOk;
385}
386
387enum {
388 kIbexCrashDumpBytesCount = sizeof(dif_rv_core_ibex_crash_dump_info_t),
389 kIbexCrashDumpWordsCount = kIbexCrashDumpBytesCount / sizeof(uint32_t),
390 kIbexCrashDumpStateBytesCount = sizeof(dif_rv_core_ibex_crash_dump_state_t),
391 kIbexCrashDumpStateWordsCount =
392 kIbexCrashDumpStateBytesCount / sizeof(uint32_t),
393 kIbexCrashDumpPreviousStateBytesCount =
394 sizeof(dif_rv_core_ibex_previous_crash_dump_state_t),
395 kIbexCrashDumpPreviousStateWordsCount =
396 kIbexCrashDumpPreviousStateBytesCount / sizeof(uint32_t),
397};
398
399dif_result_t dif_rv_core_ibex_parse_crash_dump(
400 const dif_rv_core_ibex_t *rv_core_ibex, uint32_t *cpu_info,
401 uint32_t cpu_info_len,
402 dif_rv_core_ibex_crash_dump_info_t *crash_dump_info) {
403 if (rv_core_ibex == NULL || cpu_info == NULL || crash_dump_info == NULL ||
404 cpu_info_len < kIbexCrashDumpWordsCount) {
405 return kDifBadArg;
406 }
407
408 memcpy(crash_dump_info, cpu_info, kIbexCrashDumpBytesCount - 1);
409 crash_dump_info->double_fault =
410 dif_bool_to_toggle(cpu_info[kIbexCrashDumpWordsCount - 1] == 1);
411
412 return kDifOk;
413}