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"
27 #include "kmac_regs.h"
28 #include "otbn_regs.h"
29 
30 /**
31  * Test an access to a transactional unit that has been disabled causes
32  * a hang access, resulting in a watchdog reset. Check the crash dump
33  * data address and current pc after the reset.
34  */
35 OTTF_DEFINE_TEST_CONFIG();
36 
37 static dif_aon_timer_t aon_timer;
38 
39 static dif_aes_t aes;
40 static dif_hmac_t hmac;
41 static dif_kmac_t kmac;
42 static dif_otbn_t otbn;
43 
44 typedef struct clock_error_info {
45  /**
46  * The unit being tested.
47  */
48  const char *name;
49 
50  /**
51  * The memory location that causes the error.
52  * This is the expected value of crash_dump's mdaa.
53  */
54  uint32_t csr_offset;
55 
56  /**
57  * The address of the function causing the error. The functions
58  * that cause the error are chosen so they perform a CSR read
59  * shortly after the function entry, so crash_dump's mcpc is
60  * expected to be past the possible error pc by no more than about 8
61  * instructions, meaning 8 * 4 bytes.
62  */
63  uint32_t crash_function;
65 
66 enum { kPcSpread = 8 * 4 };
67 
68 inline uint32_t addr_as_offset(mmio_region_t base, uint32_t offset) {
69  return (uint32_t)base.base + offset;
70 }
71 
72 /**
73  * Send CSR access to aes, expecting to timeout.
74  */
75 OT_NOINLINE void aes_csr_access(void) {
76  CHECK_DIF_OK(dif_aes_alert_force(&aes, kDifAesAlertRecovCtrlUpdateErr));
77 }
78 
79 OT_NOINLINE static void hmac_csr_access(void) {
80  dif_hmac_irq_state_snapshot_t snapshot;
81  CHECK_DIF_OK(dif_hmac_irq_get_state(&hmac, &snapshot));
82 }
83 
84 OT_NOINLINE static void kmac_csr_access(void) {
86  CHECK_DIF_OK(dif_kmac_get_status(&kmac, &status));
87 }
88 
89 OT_NOINLINE static void otbn_csr_access(void) {
90  dif_otbn_err_bits_t err_bits;
91  CHECK_DIF_OK(dif_otbn_get_err_bits(&otbn, &err_bits));
92 }
93 
94 static void trans_csr_access(dif_clkmgr_hintable_clock_t trans) {
95  switch (trans) {
97  aes_csr_access();
98  break;
100  hmac_csr_access();
101  break;
103  kmac_csr_access();
104  break;
106  otbn_csr_access();
107  break;
108  default:
109  LOG_ERROR("Invalid hintable clock (%d)", trans);
110  break;
111  }
112 }
113 
114 /**
115  * Test that disabling a 'hintable' unit's clock causes the unit to become
116  * unresponsive to CSR accesses. Configure a watchdog reset, and if it triggers
117  * the test is successful.
118  */
119 static void test_hintable_clocks_off(const dif_clkmgr_t *clkmgr,
121  // Make sure the clock for the unit is on.
122  CHECK_DIF_OK(
124 
125  // Disable the unit, set the aon timer to bite, and issue a CSR read.
126  CHECK_DIF_OK(
128  // Short wait to make sure clocks reacted to hints.
129  busy_spin_micros(5);
130 
131  // Check all units but the hinted one are alive.
132  for (dif_clkmgr_hintable_clock_t other = 0;
133  other <= kTopEarlgreyHintableClocksLast; ++other) {
134  if (other != clock) {
135  trans_csr_access(other);
136  }
137  }
138 
139  // Set the watchdog with some time to run the necessary code before the
140  // access that should hang.
141  uint32_t bite_us = 20;
142  uint32_t bite_cycles = 0;
143  CHECK_STATUS_OK(
144  aon_timer_testutils_get_aon_cycles_32_from_us(bite_us, &bite_cycles));
145  LOG_INFO("Setting bite reset for %u us (%u cycles)", bite_us, bite_cycles);
146  CHECK_STATUS_OK(aon_timer_testutils_watchdog_config(&aon_timer, UINT32_MAX,
147  bite_cycles, false));
148  // This should hang.
149  trans_csr_access(clock);
150  LOG_ERROR("Access to disabled unit should freeze and cause a reset");
151 }
152 
153 bool execute_off_trans_test(dif_clkmgr_hintable_clock_t clock) {
154  dif_clkmgr_t clkmgr;
155  dif_pwrmgr_t pwrmgr;
156  dif_rstmgr_t rstmgr;
157 
158  CHECK_DIF_OK(dif_rstmgr_init(
160 
161  CHECK_DIF_OK(dif_clkmgr_init(
163 
164  CHECK_DIF_OK(dif_pwrmgr_init(
166 
167  // Initialize aon timer.
168  CHECK_DIF_OK(dif_aon_timer_init(
170 
171  // Initialize aes.
172  CHECK_DIF_OK(
174 
175  // Initialize hmac.
176  CHECK_DIF_OK(
177  dif_hmac_init(mmio_region_from_addr(TOP_EARLGREY_HMAC_BASE_ADDR), &hmac));
178 
179  // Initialize kmac.
180  CHECK_DIF_OK(
181  dif_kmac_init(mmio_region_from_addr(TOP_EARLGREY_KMAC_BASE_ADDR), &kmac));
182 
183  // Initialize otbn.
184  CHECK_DIF_OK(
185  dif_otbn_init(mmio_region_from_addr(TOP_EARLGREY_OTBN_BASE_ADDR), &otbn));
186 
187  // Initialize the expected error data address and execution address.
188  clock_error_info_t clock_error_info[kTopEarlgreyHintableClocksLast + 1] = {
189  {"aes", addr_as_offset(aes.base_addr, AES_ALERT_TEST_REG_OFFSET),
190  (uint32_t)&aes_csr_access},
191  {"hmac", addr_as_offset(hmac.base_addr, HMAC_INTR_STATE_REG_OFFSET),
192  (uint32_t)&hmac_csr_access},
193  {"kmac", addr_as_offset(kmac.base_addr, KMAC_STATUS_REG_OFFSET),
194  (uint32_t)&kmac_csr_access},
195  {"otbn", addr_as_offset(otbn.base_addr, OTBN_ERR_BITS_REG_OFFSET),
196  (uint32_t)&otbn_csr_access}};
197 
198  // Enable cpu dump capture.
199  CHECK_DIF_OK(dif_rstmgr_cpu_info_set_enabled(&rstmgr, kDifToggleEnabled));
200 
201  if (UNWRAP(rstmgr_testutils_is_reset_info(&rstmgr, kDifRstmgrResetInfoPor))) {
202  // Enable watchdog bite reset.
204  kDifPwrmgrResetRequestSourceTwo,
206  CHECK_STATUS_OK(rstmgr_testutils_pre_reset(&rstmgr));
207 
208  test_hintable_clocks_off(&clkmgr, clock);
209 
210  // This should never be reached.
211  LOG_ERROR("This is unreachable since a reset should have been triggered");
212  return false;
213  } else if (UNWRAP(rstmgr_testutils_is_reset_info(
214  &rstmgr, kDifRstmgrResetInfoWatchdog))) {
215  // Verify the cpu crash dump.
216  LOG_INFO("Got an expected watchdog reset when reading for clock %d", clock);
218  // The sizes for dif_rstmgr_cpu_info_dump_read are measured in
219  // units of dif_rstmgr_cpu_info_dump_segment_t.
220  size_t size_read;
221  CHECK_DIF_OK(dif_rstmgr_cpu_info_dump_read(
222  &rstmgr, (dif_rstmgr_cpu_info_dump_segment_t *)&crash_dump,
223  sizeof(crash_dump) / sizeof(dif_rstmgr_cpu_info_dump_segment_t),
224  &size_read));
225  // crash_dump.fault_state.mdaa is the DATA ADDRESS that caused the error.
226  CHECK(crash_dump.fault_state.mdaa == clock_error_info[clock].csr_offset);
227  // The functions that cause the error are chosen so they perform a
228  // CSR read shortly after the function entry, so this expects
229  // crash_dump.fault_state.mcpc to be past the crash_function by no
230  // more than about 8 instructions, meaning 8 * 4 bytes.
231  CHECK(crash_dump.fault_state.mcpc >=
233  crash_dump.fault_state.mcpc <=
234  clock_error_info[clock].crash_function + kPcSpread);
235 
236  return true;
237  } else {
239  reset_info = rstmgr_testutils_reason_get();
240  LOG_ERROR("Unexpected reset_info 0x%x", reset_info);
241  }
242  return false;
243 }