Software APIs
rv_core_ibex_address_translation_test.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 "dt/dt_rv_core_ibex.h" // Generated
9 #include "sw/device/lib/dif/dif_rv_core_ibex.h"
12 #include "sw/device/lib/testing/test_framework/check.h"
13 #include "sw/device/lib/testing/test_framework/ottf_isrs.h"
14 #include "sw/device/lib/testing/test_framework/ottf_macros.h"
16 #include "sw/device/silicon_creator/lib/epmp_state.h"
17 
18 #define TEST_STR "Hello there, WHaT 1S Y0Ur N@ME?"
19 #define EXPECTED_RESULT_MAKE_LOWER_CASE "hello there, what 1s y0ur n@me?"
20 #define EXPECTED_RESULT_GET_NAME "My name is Titan, Open Titan"
21 
22 OTTF_DEFINE_TEST_CONFIG();
23 
24 // A function which takes a string as its only argument.
25 typedef void (*str_fn_t)(char *);
26 
27 /**
28  * A toy function that replaces the content of a given string with "My name is
29  * Titan, Open Titan". If the char buffer given is too small, it fills the
30  * buffer as far as is possible.
31  *
32  * @param input The string to have it's content replaced.
33  */
34 extern void get_name(char *input);
35 
36 /**
37  * A toy function that takes an ASCII string and converts every capital letter
38  * into a lowercase letter.
39  *
40  * @param input The string to be converted to lowercase.
41  */
42 extern void make_lower_case(char *input);
43 
44 /**
45  * Function that will be mapped to. Produces illegal instruction exception if
46  * unmapped.
47  */
48 extern void remapped_function(char *input);
49 
50 enum {
51  // Alignment of the functions in asm.
52  kRemapAlignment = 256,
53 };
54 
55 // Short-hand arrays. (Allow slots to be simply indexed.)
56 const dif_rv_core_ibex_addr_translation_slot_t kSlots[] = {
57  kDifRvCoreIbexAddrTranslationSlot_0, kDifRvCoreIbexAddrTranslationSlot_1};
58 
59 // Translation descriptions to use.
60 // Junk bits (0xDEADBEEF) are added to the `remap_addr` fields to ensure they
61 // are ignored by the translation mechanism.
62 static const dif_rv_core_ibex_addr_translation_mapping_t kMakeLowerCaseMapping =
63  {
64  .matching_addr = (uintptr_t)remapped_function,
65  .remap_addr = (uintptr_t)make_lower_case +
66  (uintptr_t)(0xDEADBEEF & (kRemapAlignment - 1)),
67  .size = kRemapAlignment,
68 };
69 static const dif_rv_core_ibex_addr_translation_mapping_t kGetNameMapping = {
70  .matching_addr = (uintptr_t)remapped_function,
71  .remap_addr =
72  (uintptr_t)get_name + (uintptr_t)(0xDEADBEEF & (kRemapAlignment - 1)),
73  .size = kRemapAlignment,
74 };
75 
76 // Stores whether an access fault exception has fired.
77 static volatile bool illegal_instr_fault = false;
78 
79 /**
80  * Overrides the default OTTF exception handler.
81  *
82  * This exception handler only processes the faults that are relevant to this
83  * test. It falls into an infinite `wait_for_interrupt` routine (by calling
84  * `abort()`) for the rest.
85  *
86  * The controlled fault originates in unmapped virtual memory. Normally the
87  * return address would be calculated relative to the trapped instruction.
88  * However, due to the virtual memory not being mapped to physical memory, this
89  * approach would not work.
90  *
91  * Instead the control flow needs to be returned to the caller. In other words,
92  * test_main -> unmapped vitual memory -> exception_handler -> test_main.
93  *
94  * Before the jump into the exception handler, the register set is saved on
95  * stack by the OTTF exception handler entry subroutine, which means that the
96  * return address can be loaded from there. See comments below for more details.
97  */
98 void ottf_exception_handler(uint32_t *exc_info) {
99  // The frame address is the address of the stack location that holds the
100  // `mepc`, since the OTTF exception handler entry code saves the `mepc` to
101  // the top of the stack before transferring control flow to the exception
102  // handler function (which is overridden here). See the `handler_exception`
103  // subroutine in `sw/device/lib/testing/testing/ottf_isrs.S` for more details.
104  uintptr_t mepc_stack_addr = (uintptr_t)OT_FRAME_ADDR();
105 
106  // The return address of the function that holds the trapping instruction is
107  // the second top-most value placed on the stack by the OTTF exception handler
108  // entry code. We grab this off the stack so that we can use it to overwrite
109  // the `mepc` value stored on the stack, so that the `ottf_isr_exit`
110  // subroutine (in `sw/device/lib/testing/test_framework/ottf_isrs.S`) will
111  // restore control flow to the `test_main` function as described
112  // above.
113  uintptr_t ret_addr = *(uintptr_t *)(mepc_stack_addr + OTTF_WORD_SIZE);
114 
115  uint32_t mcause = ibex_mcause_read();
116  ibex_exc_t exception = mcause & kIbexExcMax;
117 
118  switch (exception) {
119  case kIbexExcIllegalInstrFault:
120  LOG_INFO("Illegal instruction fault handler");
121  illegal_instr_fault = true;
122  *(uintptr_t *)mepc_stack_addr = ret_addr;
123  break;
124  default:
125  LOG_FATAL("Unexpected exception id = 0x%x", exception);
126  CHECK(false);
127  }
128 }
129 
130 /**
131  * Configures the given address translation mapping in the given slot and bus.
132  *
133  * @param ibex_core A handle to the ibex core.
134  * @param slot The slot index to be used for mapping.
135  * @param bus The bus to be used for mapping.
136  * @param mapping A description of the mapping.
137  */
138 void map_to_slot(dif_rv_core_ibex_t *ibex_core, size_t slot,
139  dif_rv_core_ibex_addr_translation_bus_t bus,
141  CHECK_DIF_OK(dif_rv_core_ibex_configure_addr_translation(
142  ibex_core, kSlots[slot], bus, mapping));
143 }
144 
145 /**
146  * Enables address translation for the given slot and bus.
147  *
148  * @param ibex_core A handle to the ibex core.
149  * @param slot The slot index to be used enabled.
150  * @param bus The bus to be enabled.
151  */
152 void enable_slot(dif_rv_core_ibex_t *ibex_core, size_t slot,
153  dif_rv_core_ibex_addr_translation_bus_t bus) {
154  CHECK_DIF_OK(
155  dif_rv_core_ibex_enable_addr_translation(ibex_core, kSlots[slot], bus));
156 }
157 
158 /**
159  * Disables address translation for the given slot and bus.
160  *
161  * @param ibex_core A handle to the ibex core.
162  * @param slot The slot index to be used enabled.
163  * @param bus The bus to be enabled.
164  */
165 void disable_slot(dif_rv_core_ibex_t *ibex_core, size_t slot,
166  dif_rv_core_ibex_addr_translation_bus_t bus) {
167  CHECK_DIF_OK(
168  dif_rv_core_ibex_disable_addr_translation(ibex_core, kSlots[slot], bus));
169 }
170 
171 void check_ibus_map(dif_rv_core_ibex_t *ibex_core) {
172  // Check the functions are expected before doing the mapping test.
173  char test_str[] = TEST_STR;
174  make_lower_case(test_str);
175  CHECK_STR_EQ(test_str, EXPECTED_RESULT_MAKE_LOWER_CASE);
176 
177  get_name(test_str);
178  CHECK_STR_EQ(test_str, EXPECTED_RESULT_GET_NAME);
179 
180  // Map virtual address space to make_lower_case() using slot 1.
181  map_to_slot(ibex_core, 1, kDifRvCoreIbexAddrTranslationIBus,
182  kMakeLowerCaseMapping);
183 
184  // Enable address translation slot 1.
185  enable_slot(ibex_core, 1, kDifRvCoreIbexAddrTranslationIBus);
186 
187  // Reset test string content.
188  memcpy(test_str, TEST_STR, sizeof(test_str));
189 
190  // Run make_lower_case() from virtual memory and check the result.
191  remapped_function(test_str);
192  CHECK_STR_EQ(test_str, EXPECTED_RESULT_MAKE_LOWER_CASE);
193 
194  // Remap virtual address space to get_name() using slot 1.
195  map_to_slot(ibex_core, 1, kDifRvCoreIbexAddrTranslationIBus, kGetNameMapping);
196 
197  // Run get_name() from virtual memory and check the result.
198  remapped_function(test_str);
199  CHECK_STR_EQ(test_str, EXPECTED_RESULT_GET_NAME);
200 
201  /////////////////////////////////////////////////////////////////////////////
202  // Check slot 0 has higher priority than slot 1.
203  /////////////////////////////////////////////////////////////////////////////
204  //
205  // Map virtual address space to make_lower_case() but using slot 0.
206  map_to_slot(ibex_core, 0, kDifRvCoreIbexAddrTranslationIBus,
207  kMakeLowerCaseMapping);
208 
209  // Enable address translation slot 0.
210  enable_slot(ibex_core, 0, kDifRvCoreIbexAddrTranslationIBus);
211 
212  // Reset test string content.
213  memcpy(test_str, TEST_STR, sizeof(test_str));
214 
215  // Run get_name() from virtual memory and check the result.
216  remapped_function(test_str);
217  CHECK_STR_EQ(test_str, EXPECTED_RESULT_MAKE_LOWER_CASE);
218 
219  /////////////////////////////////////////////////////////////////////////////
220  // Check address translation no longer occurs after being disabled.
221  /////////////////////////////////////////////////////////////////////////////
222  //
223  // Disable all address translation.
224  for (size_t slot_i = 0; slot_i < 2; ++slot_i) {
225  disable_slot(ibex_core, slot_i, kDifRvCoreIbexAddrTranslationIBus);
226  }
227 
228  // Ensure there hasn't already been an access fault.
229  CHECK(!illegal_instr_fault);
230 
231  // Try to run the remap address as a function.
232  remapped_function(test_str);
233 
234  // Ensure the exception has fired.
235  CHECK(illegal_instr_fault);
236 }
237 
238 void check_dbus_map(dif_rv_core_ibex_t *ibex_core) {
239  CHECK_ARRAYS_NE((uint8_t *)make_lower_case, (uint8_t *)get_name,
240  kRemapAlignment,
241  "make_lower_case and get_name are the same!");
242  CHECK_ARRAYS_NE((uint8_t *)make_lower_case, (uint8_t *)remapped_function,
243  kRemapAlignment,
244  "make_lower_case and remapped_function are the same!");
245  CHECK_ARRAYS_NE((uint8_t *)get_name, (uint8_t *)remapped_function,
246  kRemapAlignment,
247  "get_name and remapped_function are the same!");
248 
249  // Map virtual address space to make_lower_case() using slot 1.
250  map_to_slot(ibex_core, 1, kDifRvCoreIbexAddrTranslationDBus,
251  kMakeLowerCaseMapping);
252 
253  // Enable address translation slot 1.
254  enable_slot(ibex_core, 1, kDifRvCoreIbexAddrTranslationDBus);
255 
256  CHECK_ARRAYS_EQ((uint8_t *)make_lower_case, (uint8_t *)remapped_function,
257  kRemapAlignment,
258  "remapped_function is not mapped to make_lower_case!");
259 
260  // Remap virtual address space to get_name() using slot 1.
261  map_to_slot(ibex_core, 1, kDifRvCoreIbexAddrTranslationDBus, kGetNameMapping);
262 
263  CHECK_ARRAYS_EQ((uint8_t *)get_name, (uint8_t *)remapped_function,
264  kRemapAlignment,
265  "remapped_function is not mapped to get_name!");
266 
267  /////////////////////////////////////////////////////////////////////////////
268  // Check slot 0 has higher priority than slot 1.
269  /////////////////////////////////////////////////////////////////////////////
270  //
271  // Map virtual address space to make_lower_case() but using slot 0.
272  map_to_slot(ibex_core, 0, kDifRvCoreIbexAddrTranslationDBus,
273  kMakeLowerCaseMapping);
274 
275  // Enable address translation slot 0.
276  enable_slot(ibex_core, 0, kDifRvCoreIbexAddrTranslationDBus);
277 
278  CHECK_ARRAYS_EQ((uint8_t *)make_lower_case, (uint8_t *)remapped_function,
279  kRemapAlignment,
280  "remapped_function is not mapped to make_lower_case!");
281 
282  /////////////////////////////////////////////////////////////////////////////
283  // Check address translation no longer occurs after being disabled.
284  /////////////////////////////////////////////////////////////////////////////
285  //
286  // Disable all address translation.
287  for (size_t slot_i = 0; slot_i < 2; ++slot_i) {
288  disable_slot(ibex_core, slot_i, kDifRvCoreIbexAddrTranslationDBus);
289  }
290 
291  CHECK_ARRAYS_NE((uint8_t *)make_lower_case, (uint8_t *)remapped_function,
292  kRemapAlignment,
293  "make_lower_case and remapped_function are the same!");
294  CHECK_ARRAYS_NE((uint8_t *)get_name, (uint8_t *)remapped_function,
295  kRemapAlignment,
296  "make_lower_case and remapped_function are the same!");
297 }
298 
299 bool test_main(void) {
300  // Get ibex core handle.
301  dif_rv_core_ibex_t ibex_core;
302  dt_rv_core_ibex_t kRvCoreIbexDt = (dt_rv_core_ibex_t)0;
303  static_assert(kDtRvCoreIbexCount >= 1,
304  "This test requires at least one Ibex core");
305  CHECK_DIF_OK(dif_rv_core_ibex_init_from_dt(kRvCoreIbexDt, &ibex_core));
306 
307  check_ibus_map(&ibex_core);
308  check_dbus_map(&ibex_core);
309 
310  return true;
311 }