Software APIs
clkmgr_off_trans_impl.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/tests/clkmgr_off_trans_impl.h"
6 
17 #include "sw/device/lib/dif/dif_rv_core_ibex.h"
19 #include "sw/device/lib/testing/aon_timer_testutils.h"
20 #include "sw/device/lib/testing/rstmgr_testutils.h"
21 #include "sw/device/lib/testing/test_framework/check.h"
23 
24 #include "aes_regs.h"
25 #include "hmac_regs.h"
26 #include "kmac_regs.h"
27 #include "otbn_regs.h"
28 
29 static_assert(kDtAesCount >= 1, "This test requires at least one AES instance");
30 static_assert(kDtAonTimerCount >= 1,
31  "This test requires at least one AON Timer instance");
32 static_assert(kDtClkmgrCount >= 1,
33  "This test requires at least one Clkmgr instance");
34 static_assert(kDtHmacCount >= 1,
35  "This test requires at least one HMAC instance");
36 static_assert(kDtKmacCount >= 1,
37  "This test requires at least one KMAC instance");
38 static_assert(kDtOtbnCount >= 1,
39  "This test requires at least one OTBN instance");
40 static_assert(kDtPwrmgrCount == 1, "this test expects exactly one pwrmgr");
41 static_assert(kDtRstmgrCount >= 1,
42  "This test requires at least one Rstmgr instance");
43 
44 static const dt_aes_t kTestAes = (dt_aes_t)0;
45 static const dt_aon_timer_t kAonTimerDt = (dt_aon_timer_t)0;
46 static const dt_clkmgr_t kClkmgrDt = (dt_clkmgr_t)0;
47 static const dt_hmac_t kTestHmac = (dt_hmac_t)0;
48 static const dt_kmac_t kTestKmac = (dt_kmac_t)0;
49 static const dt_otbn_t kTestOtbn = (dt_otbn_t)0;
50 static const dt_pwrmgr_t kPwrmgrDt = (dt_pwrmgr_t)0;
51 static const dt_rstmgr_t kRstmgrDt = (dt_rstmgr_t)0;
52 
53 /**
54  * Test an access to a transactional unit that has been disabled causes
55  * a hang access, resulting in a watchdog reset. Check the crash dump
56  * data address and current pc after the reset.
57  */
58 OTTF_DEFINE_TEST_CONFIG();
59 
60 static dif_aon_timer_t aon_timer;
61 
62 static dif_aes_t aes;
63 static dif_hmac_t hmac;
64 static dif_kmac_t kmac;
65 static dif_otbn_t otbn;
66 
67 typedef struct clock_error_info {
68  /**
69  * The unit being tested.
70  */
71  const char *name;
72 
73  /**
74  * Hintable clock for this unit.
75  */
77 
78  /**
79  * The memory location that causes the error.
80  * This is the expected value of crash_dump's mdaa.
81  */
82  uint32_t csr_offset;
83 
84  /**
85  * The access function to invoke; this function shall perform a
86  * simple access to the IP block, and is thus expected to yield a
87  * timeout if its clock has been stopped.
88  */
89  void (*crash_function)(void);
91 
92 /**
93  * The functions that cause the errors
94  * are chosen so they perform a CSR read shortly after the function entry,
95  * so crash_dump's mcpc is expected to be past the possible error pc by
96  * no more than about 8 instructions, meaning 8 * 4 bytes.
97  */
98 enum { kPcSpread = 8 * 4 };
99 
100 inline uint32_t addr_as_offset(mmio_region_t base, uint32_t offset) {
101  return (uint32_t)base.base + offset;
102 }
103 
104 /**
105  * Description of each of the IP blocks to which transactions may be
106  * performed.
107  *
108  * Note: presently this code expects only a single instance of each IP,
109  * which means that we may index the array with the IP block type.
110  */
111 static clock_error_info_t info[kTestTransCount];
112 
113 /**
114  * Send CSR access to aes, expecting to timeout.
115  */
116 OT_NOINLINE void aes_csr_access(void) {
117  CHECK_DIF_OK(dif_aes_alert_force(&aes, kDifAesAlertRecovCtrlUpdateErr));
118 }
119 
120 OT_NOINLINE static void hmac_csr_access(void) {
121  dif_hmac_irq_state_snapshot_t snapshot;
122  CHECK_DIF_OK(dif_hmac_irq_get_state(&hmac, &snapshot));
123 }
124 
125 OT_NOINLINE static void kmac_csr_access(void) {
127  CHECK_DIF_OK(dif_kmac_get_status(&kmac, &status));
128 }
129 
130 OT_NOINLINE static void otbn_csr_access(void) {
131  dif_otbn_err_bits_t err_bits;
132  CHECK_DIF_OK(dif_otbn_get_err_bits(&otbn, &err_bits));
133 }
134 
135 /**
136  * Test that disabling a 'hintable' unit's clock causes the unit to become
137  * unresponsive to CSR accesses. Configure a watchdog reset, and if it triggers
138  * the test is successful.
139  */
140 static void test_hintable_clocks_off(const dif_clkmgr_t *clkmgr,
142  void (*crash_fn)(void) = NULL;
143 
144  // Make sure the clock for the unit is on.
145  CHECK_DIF_OK(
147 
148  // Disable the unit, set the aon timer to bite, and issue a CSR read.
149  CHECK_DIF_OK(
151  // Short wait to make sure clocks reacted to hints.
152  busy_spin_micros(5);
153 
154  // Check all units but any on the hinted clock are alive.
155  for (test_trans_block_t block = kTestTransFirst; block < kTestTransCount;
156  ++block) {
157  if (info[block].clock == clock) {
158  LOG_INFO("Hintable clock controls IP block '%s'", info[block].name);
159  crash_fn = info[block].crash_function;
160  } else {
161  // This CSR access should complete successfully.
162  info[block].crash_function();
163  }
164  }
165 
166  // Set the watchdog with some time to run the necessary code before the
167  // access that should hang.
168  uint32_t bite_us = 20;
169  uint32_t bite_cycles = 0;
170  CHECK_STATUS_OK(
171  aon_timer_testutils_get_aon_cycles_32_from_us(bite_us, &bite_cycles));
172  LOG_INFO("Setting bite reset for %u us (%u cycles)", bite_us, bite_cycles);
173  CHECK_STATUS_OK(aon_timer_testutils_watchdog_config(&aon_timer, UINT32_MAX,
174  bite_cycles, false));
175  // This should hang.
176  crash_fn();
177  LOG_ERROR("Access to disabled unit should freeze and cause a reset");
178 }
179 
180 bool execute_off_trans_test(test_trans_block_t block) {
181  dif_clkmgr_t clkmgr;
182  dif_pwrmgr_t pwrmgr;
183  dif_rstmgr_t rstmgr;
184 
185  CHECK_DIF_OK(dif_rstmgr_init_from_dt(kRstmgrDt, &rstmgr));
186  CHECK_DIF_OK(dif_clkmgr_init_from_dt(kClkmgrDt, &clkmgr));
187  CHECK_DIF_OK(dif_pwrmgr_init_from_dt(kPwrmgrDt, &pwrmgr));
188 
189  // Initialize aon timer.
190  CHECK_DIF_OK(dif_aon_timer_init_from_dt(kAonTimerDt, &aon_timer));
191 
192  // Note: presently this code expects only a single instance of each IP,
193  // which means that we may index the `info` array with the IP block type.
194  CHECK(block < ARRAYSIZE(info));
195 
196  // Construct descriptions of each IP block.
197  for (test_trans_block_t trans = kTestTransFirst; trans < kTestTransCount;
198  ++trans) {
199  dt_instance_id_t inst = (dt_instance_id_t)0;
200  switch (trans) {
201  case kTestTransAes:
202  // Initialize aes.
203  CHECK_DIF_OK(dif_aes_init_from_dt(kTestAes, &aes));
204  inst = dt_aes_instance_id(kTestAes);
205  info[trans].name = "aes";
206  info[trans].csr_offset =
207  addr_as_offset(aes.base_addr, AES_ALERT_TEST_REG_OFFSET);
208  info[trans].crash_function = aes_csr_access;
209  break;
210 
211  case kTestTransHmac:
212  // Initialize hmac.
213  CHECK_DIF_OK(dif_hmac_init_from_dt(kTestHmac, &hmac));
214  inst = dt_hmac_instance_id(kTestHmac);
215  info[trans].name = "hmac";
216  info[trans].csr_offset =
217  addr_as_offset(hmac.base_addr, HMAC_INTR_STATE_REG_OFFSET);
218  info[trans].crash_function = hmac_csr_access;
219  break;
220 
221  case kTestTransKmac:
222  // Initialize kmac.
223  CHECK_DIF_OK(dif_kmac_init_from_dt(kTestKmac, &kmac));
224  inst = dt_kmac_instance_id(kTestKmac);
225  info[trans].name = "kmac";
226  info[trans].csr_offset =
227  addr_as_offset(kmac.base_addr, KMAC_STATUS_REG_OFFSET);
228  info[trans].crash_function = kmac_csr_access;
229  break;
230 
231  case kTestTransOtbn:
232  // Initialize otbn.
233  CHECK_DIF_OK(dif_otbn_init_from_dt(kTestOtbn, &otbn));
234  inst = dt_otbn_instance_id(kTestOtbn);
235  info[trans].name = "otbn";
236  info[trans].csr_offset =
237  addr_as_offset(otbn.base_addr, OTBN_ERR_BITS_REG_OFFSET);
238  info[trans].crash_function = otbn_csr_access;
239  break;
240 
241  default:
242  LOG_ERROR("Invalid/unrecognised IP block type (%d)", trans);
243  break;
244  }
245  // Ascertain which of the hintable clocks is driving this IP block.
246  // Note: this code presently expects only a single IP block of each type.
247  CHECK_DIF_OK(
248  dif_clkmgr_find_hintable_clock(&clkmgr, inst, &info[trans].clock));
249  }
250 
251  // This is the hintable clock that we are going to control.
252  dif_clkmgr_hintable_clock_t clock = info[block].clock;
253 
254  // Enable cpu dump capture.
255  CHECK_DIF_OK(dif_rstmgr_cpu_info_set_enabled(&rstmgr, kDifToggleEnabled));
256 
257  if (UNWRAP(rstmgr_testutils_is_reset_info(&rstmgr, kDifRstmgrResetInfoPor))) {
258  // Enable watchdog bite reset.
259  dif_pwrmgr_request_sources_t reset_sources;
260  CHECK_DIF_OK(dif_pwrmgr_find_request_source(
261  &pwrmgr, kDifPwrmgrReqTypeReset, dt_aon_timer_instance_id(kAonTimerDt),
262  kDtAonTimerResetReqAonTimer, &reset_sources));
263  CHECK_DIF_OK(dif_pwrmgr_set_request_sources(
264  &pwrmgr, kDifPwrmgrReqTypeReset, reset_sources, kDifToggleEnabled));
265  CHECK_STATUS_OK(rstmgr_testutils_pre_reset(&rstmgr));
266 
267  test_hintable_clocks_off(&clkmgr, clock);
268 
269  // This should never be reached.
270  LOG_ERROR("This is unreachable since a reset should have been triggered");
271  return false;
272  } else if (UNWRAP(rstmgr_testutils_is_reset_info(
273  &rstmgr, kDifRstmgrResetInfoWatchdog))) {
274  // Verify the cpu crash dump.
275  LOG_INFO("Got an expected watchdog reset when reading for clock %d", clock);
277  // The sizes for dif_rstmgr_cpu_info_dump_read are measured in
278  // units of dif_rstmgr_cpu_info_dump_segment_t.
279  size_t size_read;
280  CHECK_DIF_OK(dif_rstmgr_cpu_info_dump_read(
281  &rstmgr, (dif_rstmgr_cpu_info_dump_segment_t *)&crash_dump,
282  sizeof(crash_dump) / sizeof(dif_rstmgr_cpu_info_dump_segment_t),
283  &size_read));
284  // crash_dump.fault_state.mdaa is the DATA ADDRESS that caused the error.
285  CHECK(crash_dump.fault_state.mdaa == info[block].csr_offset);
286  // The functions that cause the error are chosen so they perform a
287  // CSR read shortly after the function entry, so this expects
288  // crash_dump.fault_state.mcpc to be past the crash_function by no
289  // more than about 8 instructions, meaning 8 * 4 bytes.
290  CHECK(crash_dump.fault_state.mcpc >=
291  (uintptr_t)info[block].crash_function &&
292  crash_dump.fault_state.mcpc <=
293  (uintptr_t)info[block].crash_function + kPcSpread);
294 
295  return true;
296  } else {
298  reset_info = rstmgr_testutils_reason_get();
299  LOG_ERROR("Unexpected reset_info 0x%x", reset_info);
300  }
301  return false;
302 }