Software APIs
sram_ctrl_execution_main_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 <stdbool.h>
6 #include <stdint.h>
7 
10 #include "sw/device/lib/base/multibits.h"
14 #include "sw/device/lib/testing/lc_ctrl_testutils.h"
15 #include "sw/device/lib/testing/otp_ctrl_testutils.h"
16 #include "sw/device/lib/testing/sram_ctrl_testutils.h"
17 #include "sw/device/lib/testing/test_framework/check.h"
18 #include "sw/device/lib/testing/test_framework/ottf_isrs.h"
19 #include "sw/device/lib/testing/test_framework/ottf_macros.h"
21 #include "sw/device/lib/testing/test_framework/status.h"
22 
24 #include "otp_ctrl_regs.h" // Generated
25 
26 OTTF_DEFINE_TEST_CONFIG();
27 
28 static dif_sram_ctrl_t sram_ctrl;
29 
30 /**
31  * This flag is used to verify that the execution from SRAM was successful.
32  * Declared as volatile, because it is referenced in the fault handler, as well
33  * as the main test flow.
34  */
35 static volatile bool exception_observed;
36 
37 /**
38  * Main SRAM start and end addresses (inclusive).
39  */
40 static const uint32_t kRamStartAddr = TOP_EARLGREY_SRAM_CTRL_MAIN_RAM_BASE_ADDR;
41 static const uint32_t kRamEndAddr = TOP_EARLGREY_SRAM_CTRL_MAIN_RAM_BASE_ADDR +
43  1;
44 
45 /**
46  * OTP HW partition relative IFETCH offset in bytes.
47  */
48 static const uint32_t kOtpIfetchHwRelativeOffset =
49  OTP_CTRL_PARAM_EN_SRAM_IFETCH_OFFSET - OTP_CTRL_PARAM_HW_CFG1_OFFSET;
50 
51 /**
52  * Executes the return instruction from MAIN SRAM.
53  *
54  * This function will return on success, or cause an exception if the
55  * execution is disabled.
56  */
58 OT_SECTION(".data")
59 void execute_code_in_sram(void) { asm volatile("jalr zero, 0(ra)"); }
60 
61 static bool otp_ifetch_enabled(void) {
62  dif_otp_ctrl_t otp;
63  CHECK_DIF_OK(dif_otp_ctrl_init(
65 
66  dif_otp_ctrl_config_t config = {
67  .check_timeout = 100000,
68  .integrity_period_mask = 0x3ffff,
69  .consistency_period_mask = 0x3ffffff,
70  };
71  CHECK_DIF_OK(dif_otp_ctrl_configure(&otp, config));
72 
74  kOtpIfetchHwRelativeOffset));
75 
76  CHECK_STATUS_OK(otp_ctrl_testutils_wait_for_dai(&otp));
77 
78  uint32_t value;
79  CHECK_DIF_OK(dif_otp_ctrl_dai_read32_end(&otp, &value));
80 
81  return bitfield_field32_read(
82  value, (bitfield_field32_t){.mask = 0xff, .index = 0}) ==
83  kMultiBitBool8True;
84 }
85 
86 /**
87  * Overrides the default OTTF exception handler.
88  *
89  * This exception handler only processes the faults that are relevant to this
90  * test. It falls into an infinite `wait_for_interrupt` routine (by calling
91  * `abort()`) for the rest.
92  *
93  * The controlled fault originates in the retention SRAM, which means that
94  * normally the return address would be calculated relative to the trapped
95  * instruction. However, due to execution from retention SRAM being permanently
96  * disabled, this approach would not work.
97  *
98  * Instead the control flow needs to be returned to the caller. In other words,
99  * sram_execution_test -> retention_sram -> exception_handler
100  * -> sram_execution_test.
101  *
102  * Before the jump into the exception handler, the register set is saved on
103  * stack by the OTTF exception handler entry subroutine, which means that the
104  * return address can be loaded from there. See comments below for more details.
105  */
106 void ottf_exception_handler(uint32_t *exc_info) {
107  // The frame address is the address of the stack location that holds the
108  // `mepc`, since the OTTF exception handler entry code saves the `mepc` to
109  // the top of the stack before transferring control flow to the exception
110  // handler function (which is overridden here). See the `handler_exception`
111  // subroutine in `sw/device/lib/testing/testing/ottf_isrs.S` for more details.
112  uintptr_t mepc_stack_addr = (uintptr_t)OT_FRAME_ADDR();
113 
114  // The return address of the function that holds the trapping instruction is
115  // the second top-most value placed on the stack by the OTTF exception handler
116  // entry code. We grab this off the stack so that we can use it to overwrite
117  // the `mepc` value stored on the stack, so that the `ottf_isr_exit`
118  // subroutine (in `sw/device/lib/testing/test_framework/ottf_isrs.S`) will
119  // restore control flow to the `sram_execution_test` function as described
120  // above.
121  uintptr_t ret_addr = *(uintptr_t *)(mepc_stack_addr + OTTF_WORD_SIZE);
122 
123  LOG_INFO("Handling exception: mepc = %p, (trapped) return address = %p",
124  ibex_mepc_read(), ret_addr);
125 
126  uint32_t mcause = ibex_mcause_read();
127  ibex_exc_t exception = mcause & kIbexExcMax;
128 
129  switch (exception) {
130  case kIbexExcInstrAccessFault:
131  LOG_INFO("Instruction access fault handler");
132  exception_observed = true;
133  *(uintptr_t *)mepc_stack_addr = ret_addr;
134  break;
135  case kIbexExcIllegalInstrFault:
136  LOG_INFO("Illegal instruction fault handler");
137  exception_observed = true;
138  *(uintptr_t *)mepc_stack_addr = ret_addr;
139  break;
140  default:
141  LOG_FATAL("Unexpected exception id = 0x%x", exception);
142  abort();
143  }
144 }
145 
146 /**
147  * Sets the sram_ctrl exec CSR to both states and attempts to execute
148  * the code in SRAM. Checks whether an exception was observed for each
149  * case in line with the expected result based on LC_STATE and
150  * OTP IFETCH.
151  */
152 void do_execute_test(bool debug_func, bool ifetch_en) {
153  bool csr_enabled_exception_expected;
154  bool csr_disabled_exception_expected;
155 
156  if (debug_func && !ifetch_en) {
157  csr_enabled_exception_expected = false;
158  csr_disabled_exception_expected = false;
159  } else if (debug_func && ifetch_en) {
160  csr_enabled_exception_expected = false;
161  csr_disabled_exception_expected = true;
162  } else if (!debug_func && !ifetch_en) {
163  csr_enabled_exception_expected = true;
164  csr_disabled_exception_expected = true;
165  } else {
166  csr_enabled_exception_expected = false;
167  csr_disabled_exception_expected = true;
168  }
169 
170  CHECK_DIF_OK(dif_sram_ctrl_exec_set_enabled(&sram_ctrl, kDifToggleEnabled));
171  exception_observed = false;
173  execute_code_in_sram();
174  CHECK(exception_observed == csr_enabled_exception_expected,
175  "Expected exception not observed whilst executing from SRAM!");
176  CHECK_DIF_OK(dif_sram_ctrl_exec_set_enabled(&sram_ctrl, kDifToggleDisabled));
177  exception_observed = false;
179  execute_code_in_sram();
180  CHECK(exception_observed == csr_disabled_exception_expected,
181  "Expected exception not observed whilst executing from SRAM!");
182 }
183 
184 /**
185  * Performs the tests.
186  *
187  * When chip is in one of the lifecycle states where debug functions are
188  * enabled, execution from SRAM is enabled if the EN_SRAM_IFETCH
189  * (OTP) is disabled. When EN_SRAM_IFETCH (OTP) is enabled, EXEC CSR
190  * determines whether the execution from SRAM is enabled.
191  */
192 bool test_main(void) {
193  uintptr_t func_address = (uintptr_t)execute_code_in_sram;
194  CHECK(func_address >= kRamStartAddr && func_address <= kRamEndAddr,
195  "Test code resides outside of the Main SRAM: function address = %x",
196  func_address);
197 
198  CHECK_DIF_OK(dif_sram_ctrl_init(
200  &sram_ctrl));
201 
202  bool locked;
203  CHECK_DIF_OK(
204  dif_sram_ctrl_is_locked(&sram_ctrl, kDifSramCtrlLockExec, &locked));
205  CHECK(!locked, "Execution is disabled and locked, cannot perform the test");
206 
207  dif_lc_ctrl_t lc;
208  CHECK_DIF_OK(dif_lc_ctrl_init(
210 
211  bool debug_func = false;
212  CHECK_STATUS_OK(lc_ctrl_testutils_debug_func_enabled(&lc, &debug_func));
213  // For the current configuration (set by the testbench)
214  // check that execution exceptions are as expected.
215  do_execute_test(debug_func, otp_ifetch_enabled());
216 
217  // When the test is complete flag WFI status so
218  // the testbench can reset and progress to the next
219  // combination of LC_STATE and OTP IFETCH.
220  test_status_set(kTestStatusInWfi);
222 
223  return true;
224 }