Software APIs
rnd_functest.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 
8 #include "sw/device/lib/base/crc32.h"
11 #include "sw/device/lib/testing/entropy_testutils.h"
12 #include "sw/device/lib/testing/rand_testutils.h"
13 #include "sw/device/lib/testing/test_framework/check.h"
15 #include "sw/device/silicon_creator/lib/drivers/otp.h"
16 #include "sw/device/silicon_creator/lib/drivers/rnd.h"
17 #include "sw/device/silicon_creator/lib/error.h"
18 
20 #include "otp_ctrl_regs.h"
21 
22 OTTF_DEFINE_TEST_CONFIG();
23 
24 static dif_entropy_src_t entropy_src;
25 
26 // Returns `rnd_threshold` if the target `test_id` supports a low configuration
27 // threshold, otherwise it returns 0.
28 static uint32_t random_low_threshold(dif_entropy_src_test_t test_id,
29  uint32_t rnd_threshold) {
30  uint32_t result;
31  switch (test_id) {
35  result = 0;
36  break;
40  result = rnd_threshold;
41  break;
42  default:
43  CHECK(false, "Unexpected dif_entropy_src_test_t %d", test_id);
44  return 0;
45  }
46  return result;
47 }
48 
49 // Configures entropy_src with random but harmless health test thresholds. The
50 // entropy complex is enabled in continuous mode to generate the random values,
51 // and then re-enabled to run with the configured health thresholds.
52 static void configure_random_health_checks(void) {
53  // Generate random values using entropy complex in continuous mode.
54  CHECK_STATUS_OK(entropy_testutils_auto_mode_init());
55  dif_entropy_src_health_test_config_t configs[kDifEntropySrcTestNumVariants];
56  for (uint32_t test_id = kDifEntropySrcTestRepetitionCount;
57  test_id < kDifEntropySrcTestNumVariants; ++test_id) {
58  // Randomize threshold values without aiming to disrrupt the default
59  // entropy_src health test behavior.
60  uint32_t meaningless_threshold =
61  rand_testutils_gen32_range(/*min=*/0, /*max=*/16);
62  configs[test_id] = (dif_entropy_src_health_test_config_t){
64  .high_threshold = 0xFFFFFF00 ^ meaningless_threshold,
65  .low_threshold = random_low_threshold(test_id, meaningless_threshold),
66  };
67  }
68 
69  // Configure health values after disabling the entropy complex.
70  CHECK_STATUS_OK(entropy_testutils_stop_all());
71  for (size_t i = 0; i < ARRAYSIZE(configs); ++i) {
72  CHECK_DIF_OK(
73  dif_entropy_src_health_test_configure(&entropy_src, configs[i]));
74  }
75  CHECK_STATUS_OK(entropy_testutils_auto_mode_init());
76 }
77 
78 // Configures the entropy source health check and alert thresholds with the
79 // configuration stored in OTP.
80 static void configure_health_checks_from_otp(void) {
81  CHECK_STATUS_OK(entropy_testutils_stop_all());
84  {
86  .high_threshold = otp_read32(
87  OTP_CTRL_PARAM_CREATOR_SW_CFG_RNG_REPCNT_THRESHOLDS_OFFSET),
88  .low_threshold = 0,
89  },
91  {
93  .high_threshold = otp_read32(
94  OTP_CTRL_PARAM_CREATOR_SW_CFG_RNG_REPCNTS_THRESHOLDS_OFFSET),
95  .low_threshold = 0,
96  },
98  {
100  .high_threshold = otp_read32(
101  OTP_CTRL_PARAM_CREATOR_SW_CFG_RNG_ADAPTP_HI_THRESHOLDS_OFFSET),
102  .low_threshold = otp_read32(
103  OTP_CTRL_PARAM_CREATOR_SW_CFG_RNG_ADAPTP_LO_THRESHOLDS_OFFSET),
104  },
106  {
107  .test_type = kDifEntropySrcTestBucket,
108  .high_threshold = otp_read32(
109  OTP_CTRL_PARAM_CREATOR_SW_CFG_RNG_BUCKET_THRESHOLDS_OFFSET),
110  .low_threshold = 0,
111  },
113  {
114  .test_type = kDifEntropySrcTestMarkov,
115  .high_threshold = otp_read32(
116  OTP_CTRL_PARAM_CREATOR_SW_CFG_RNG_MARKOV_HI_THRESHOLDS_OFFSET),
117  .low_threshold = otp_read32(
118  OTP_CTRL_PARAM_CREATOR_SW_CFG_RNG_MARKOV_LO_THRESHOLDS_OFFSET),
119  },
121  {
122  .test_type = kDifEntropySrcTestMailbox,
123  .high_threshold = otp_read32(
124  OTP_CTRL_PARAM_CREATOR_SW_CFG_RNG_EXTHT_HI_THRESHOLDS_OFFSET),
125  .low_threshold = otp_read32(
126  OTP_CTRL_PARAM_CREATOR_SW_CFG_RNG_EXTHT_LO_THRESHOLDS_OFFSET),
127  },
128  };
129  for (size_t i = 0; i < ARRAYSIZE(configs); ++i) {
130  CHECK_DIF_OK(
131  dif_entropy_src_health_test_configure(&entropy_src, configs[i]));
132  }
133 
134  // The entropy_src DIF expects the alert threshold to be set at configuration
135  // time. We skip enabling csrng and edn0/1 in this case.
137  .fips_enable = true,
138  .fips_flag = true,
139  .rng_fips = true,
140  .route_to_firmware = false,
141  .bypass_conditioner = false,
142  .single_bit_mode = kDifEntropySrcSingleBitModeDisabled,
143  .health_test_window_size = 0x0200,
144  .alert_threshold = (uint16_t)otp_read32(
145  OTP_CTRL_PARAM_CREATOR_SW_CFG_RNG_ALERT_THRESHOLD_OFFSET),
146  };
147  CHECK_DIF_OK(dif_entropy_src_configure(&entropy_src, entropy_src_config,
149 }
150 
151 // Checks that the CRC value stored in OTP is consistent with what ROM is
152 // expecting.
153 rom_error_t test_otp_crc_check(void) {
154  uint32_t ctx;
155  crc32_init(&ctx);
156  uint32_t otp_in_order_offsets[] = {
157  OTP_CTRL_PARAM_CREATOR_SW_CFG_RNG_REPCNT_THRESHOLDS_OFFSET,
158  OTP_CTRL_PARAM_CREATOR_SW_CFG_RNG_REPCNTS_THRESHOLDS_OFFSET,
159  OTP_CTRL_PARAM_CREATOR_SW_CFG_RNG_ADAPTP_HI_THRESHOLDS_OFFSET,
160  OTP_CTRL_PARAM_CREATOR_SW_CFG_RNG_ADAPTP_LO_THRESHOLDS_OFFSET,
161  OTP_CTRL_PARAM_CREATOR_SW_CFG_RNG_BUCKET_THRESHOLDS_OFFSET,
162  OTP_CTRL_PARAM_CREATOR_SW_CFG_RNG_MARKOV_HI_THRESHOLDS_OFFSET,
163  OTP_CTRL_PARAM_CREATOR_SW_CFG_RNG_MARKOV_LO_THRESHOLDS_OFFSET,
164  OTP_CTRL_PARAM_CREATOR_SW_CFG_RNG_EXTHT_HI_THRESHOLDS_OFFSET,
165  OTP_CTRL_PARAM_CREATOR_SW_CFG_RNG_EXTHT_LO_THRESHOLDS_OFFSET,
166  OTP_CTRL_PARAM_CREATOR_SW_CFG_RNG_ALERT_THRESHOLD_OFFSET,
167  };
168  for (size_t i = 0; i < ARRAYSIZE(otp_in_order_offsets); ++i) {
169  crc32_add32(&ctx, otp_read32(otp_in_order_offsets[i]));
170  }
171  uint32_t crc = crc32_finish(&ctx);
172  uint32_t expected = crc ^ kErrorOk;
173  uint32_t got =
174  otp_read32(OTP_CTRL_PARAM_CREATOR_SW_CFG_RNG_HEALTH_CONFIG_DIGEST_OFFSET);
175  CHECK(expected == got, "Unexpected OTP CRC value. expected: 0x%x, got: 0x%x",
176  expected, got);
177 
178  // If calculated properly, this will return kErrorOk.
179  return crc ^ got;
180 }
181 
182 rom_error_t test_rnd_health_config_check(void) {
183  // The default OTP image uses the default reset configuration. We test that
184  // first here because the health tests configuration cannot be reset by
185  // toggling the entropy_src enable field. The
186  // `configure_random_health_checks()` function called later reduces the health
187  // test window sizes.
188 
189  // Other lc states must match the expected CRC stored in OTP.
190  lifecycle_state_t lc_expect_check[] = {
191  kLcStateDev,
192  kLcStateProd,
193  kLcStateProdEnd,
194  kLcStateRma,
195  };
196  configure_health_checks_from_otp();
197  for (size_t i = 0; i < ARRAYSIZE(lc_expect_check); ++i) {
198  rom_error_t res = rnd_health_config_check(lc_expect_check[i]);
199  CHECK(res == kErrorOk, "Lifecycle: %d, error: %d", lc_expect_check[i], res);
200  }
201 
202  // Don't care lc states should always return `kErrorOk` regardless of what
203  // values are stored in the entropy_src.
204  lifecycle_state_t lc_expect_check_skip[] = {
205  kLcStateTest,
206  };
207  configure_random_health_checks();
208  for (size_t i = 0; i < ARRAYSIZE(lc_expect_check_skip); ++i) {
209  CHECK(rnd_health_config_check(lc_expect_check_skip[i]) == kErrorOk);
210  }
211 
212  // Configure entropy source one last time to leave the device in a good end
213  // state.
214  CHECK_STATUS_OK(entropy_testutils_auto_mode_init());
215  return kErrorOk;
216 }
217 
218 rom_error_t test_rnd(void) {
219  const size_t kTestNumIter = 5;
220  uint32_t prev = rnd_uint32();
221  uint32_t error_cnt = 0;
222  for (size_t i = 0; i < kTestNumIter; ++i) {
223  uint32_t got = rnd_uint32();
224  if (got == prev) {
225  LOG_ERROR("Unexpected duplicate random number: 0x%x.", got);
226  error_cnt += 1;
227  }
228  prev = got;
229  }
230  if (error_cnt > 0) {
231  LOG_ERROR("rnd_test failed, expected: 0 errors, got: %d.", error_cnt);
232  return kErrorUnknown;
233  }
234  return kErrorOk;
235 }
236 
237 bool test_main(void) {
238  CHECK_DIF_OK(dif_entropy_src_init(
240 
241  status_t result = OK_STATUS();
242  EXECUTE_TEST(result, test_rnd);
243  EXECUTE_TEST(result, test_otp_crc_check);
244  EXECUTE_TEST(result, test_rnd_health_config_check);
245  return status_ok(result);
246 }