Software APIs
lc_walkthrough_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 
11 #include "sw/device/lib/testing/lc_ctrl_testutils.h"
12 #include "sw/device/lib/testing/otp_ctrl_testutils.h"
13 #include "sw/device/lib/testing/test_framework/check.h"
14 #include "sw/device/lib/testing/test_framework/ottf_test_config.h"
15 
17 
18 #define LC_TOKEN_SIZE 16
19 
20 OTTF_DEFINE_TEST_CONFIG();
21 
22 static dif_lc_ctrl_t lc;
23 static dif_otp_ctrl_t otp;
24 static dif_rstmgr_t rstmgr;
25 
26 /**
27  * Track LC state transition tokens and destination state.
28  *
29  * These variables will be further randomized and overridden by the testbench.
30  */
31 
32 static volatile const uint8_t kDestState;
33 
34 // LC exit token value for LC state transition.
35 static volatile const uint8_t kLcExitToken[LC_TOKEN_SIZE] = {
36  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
37  0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
38 };
39 
40 // LC exit token value in OTP secret0 partition.
41 static volatile const uint8_t kOtpExitToken[LC_TOKEN_SIZE] = {
42  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
43  0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
44 };
45 
46 // LC unlock token value in OTP secret0 partition.
47 static volatile const uint8_t kOtpUnlockToken[LC_TOKEN_SIZE] = {
48  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
49  0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
50 };
51 
52 // LC rma token value for LC state transition.
53 static volatile const uint8_t kLcRmaToken[LC_TOKEN_SIZE] = {
54  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
55  0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
56 };
57 
58 // LC rma token value in OTP secret2 partition.
59 static volatile const uint8_t kOtpRmaToken[LC_TOKEN_SIZE] = {
60  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
61  0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
62 };
63 
64 static void lock_otp_secret0_partition(void) {
65  uint64_t otp_unlock_token_0 = 0;
66  uint64_t otp_unlock_token_1 = 0;
67  for (int i = 0; i < LC_TOKEN_SIZE; i++) {
68  if (i < LC_TOKEN_SIZE / 2) {
69  otp_unlock_token_0 |= (uint64_t)kOtpUnlockToken[i] << (i * 8);
70  } else {
71  otp_unlock_token_1 |= (uint64_t)kOtpUnlockToken[i]
72  << ((i - LC_TOKEN_SIZE / 2) * 8);
73  }
74  }
75 
76  uint64_t otp_exit_token_0 = 0;
77  uint64_t otp_exit_token_1 = 0;
78  for (int i = 0; i < LC_TOKEN_SIZE; i++) {
79  if (i < LC_TOKEN_SIZE / 2) {
80  otp_exit_token_0 |= (uint64_t)kOtpExitToken[i] << (i * 8);
81  } else {
82  otp_exit_token_1 |= (uint64_t)kOtpExitToken[i]
83  << ((i - LC_TOKEN_SIZE / 2) * 8);
84  }
85  }
86 
88  /*address=*/0x0,
89  /*value=*/otp_unlock_token_0));
90  CHECK_STATUS_OK(otp_ctrl_testutils_wait_for_dai(&otp));
92  /*address=*/0x8,
93  /*value=*/otp_unlock_token_1));
94  CHECK_STATUS_OK(otp_ctrl_testutils_wait_for_dai(&otp));
96  /*address=*/0x10,
97  /*value=*/otp_exit_token_0));
98  CHECK_STATUS_OK(otp_ctrl_testutils_wait_for_dai(&otp));
100  /*address=*/0x18,
101  /*value=*/otp_exit_token_1));
102  CHECK_STATUS_OK(otp_ctrl_testutils_wait_for_dai(&otp));
103 
105  /*digest=*/0));
106  CHECK_STATUS_OK(otp_ctrl_testutils_wait_for_dai(&otp));
107 }
108 
109 static void lock_otp_secret2_partition(void) {
110  // Write LC RMA tokens to OTP secret2 partition.
111  uint64_t otp_rma_token_0 = 0;
112  uint64_t otp_rma_token_1 = 0;
113  for (int i = 0; i < LC_TOKEN_SIZE; i++) {
114  if (i < LC_TOKEN_SIZE / 2) {
115  otp_rma_token_0 |= (uint64_t)kOtpRmaToken[i] << (i * 8);
116  } else {
117  otp_rma_token_1 |= (uint64_t)kOtpRmaToken[i]
118  << ((i - LC_TOKEN_SIZE / 2) * 8);
119  }
120  }
122  /*address=*/0x0,
123  /*value=*/otp_rma_token_0));
124  CHECK_STATUS_OK(otp_ctrl_testutils_wait_for_dai(&otp));
126  /*address=*/0x8,
127  /*value=*/otp_rma_token_1));
128  CHECK_STATUS_OK(otp_ctrl_testutils_wait_for_dai(&otp));
129 
131  /*digest=*/0));
132  CHECK_STATUS_OK(otp_ctrl_testutils_wait_for_dai(&otp));
133 }
134 
135 /**
136  * Walkthrough LC state: RAW -> TestUnlock0 -> Destination -> RMA (if
137  * applicable).
138  *
139  * 1). Preload OTP RAW image file.
140  * 2). DV sequence drives JTAG interface to write RawUnlockToken and
141  * transfers LC state to TestUnlock0 state.
142  * 3). In TestUnlock0 state, SW programs OTP secret0 partition to write
143  * ExitToken and TestUnlockToken.
144  * Also programs OTP secret2 partition to write RmaToken.
145  * 4). DV sequence issues reset to lock OTP secret0 partition.
146  * 5). SW requests a LC state transfer to ProdEnd state with the correct
147  * TestLockToken.
148  * 6). DV sequence issues reset and SW check if LC transfers to the destination
149  * state.
150  * 7). If the destination state can be transferred to RMA state, will issue
151  * another LC state transfer to RMA state.
152  *
153  * Note that the destination state is a runtime input from the testbench.
154  */
155 
156 bool test_main(void) {
157  LOG_INFO("Start LC walkthrough %d test.", kDestState);
158 
159  mmio_region_t lc_reg =
161  CHECK_DIF_OK(dif_lc_ctrl_init(lc_reg, &lc));
162 
163  mmio_region_t otp_reg =
165  CHECK_DIF_OK(dif_otp_ctrl_init(otp_reg, &otp));
166 
167  CHECK_DIF_OK(dif_rstmgr_init(
169 
170  LOG_INFO("Read and check LC state and count.");
171  dif_lc_ctrl_state_t curr_state;
172  CHECK_DIF_OK(dif_lc_ctrl_get_state(&lc, &curr_state));
173 
174  if (curr_state == kDifLcCtrlStateTestUnlocked0) {
175  CHECK_STATUS_OK(lc_ctrl_testutils_check_transition_count(&lc, 1));
176  bool secret0_locked;
177  CHECK_DIF_OK(dif_otp_ctrl_is_digest_computed(
178  &otp, kDifOtpCtrlPartitionSecret0, &secret0_locked));
179 
180  if (!secret0_locked) {
181  LOG_INFO("In TestUnlocked0 state. Write and lock OTP secret0 partition.");
182  lock_otp_secret0_partition();
183  LOG_INFO("Written and locked OTP secret0 partition!");
184  CHECK_DIF_OK(dif_rstmgr_software_device_reset(&rstmgr));
186  // Print out LcRmaToken to avoid SW compile error saying kLcRmaToken is
187  // unused in certain state trasition tests.
188  LOG_INFO("LC RMA token start with %08x", kLcRmaToken[0]);
189  // Unreachable
190  return false;
191  } else {
192  LOG_INFO(
193  "In TestUnlocked0 state. Issue LC state transfer to destination "
194  "state.");
195  dif_lc_ctrl_token_t token;
196  for (int i = 0; i < LC_TOKEN_SIZE; i++) {
197  if (kDestState != kDifLcCtrlStateRma) {
198  token.data[i] = kLcExitToken[i];
199  } else {
200  // Transition from TestUnlock to Rma state is unconditional and
201  // requires to write all zero default tokens.
202  token.data[i] = 0;
203  }
204  }
205 
206  CHECK_DIF_OK(dif_lc_ctrl_mutex_try_acquire(&lc));
207 
208  // TODO(lowRISC/opentitan#12775): randomize using external or internal
209  // clock.
210  bool use_ext_clock = false;
211  CHECK_DIF_OK(
212  dif_lc_ctrl_configure(&lc, kDestState, use_ext_clock, &token),
213  "LC transition configuration failed!");
214  CHECK_DIF_OK(dif_lc_ctrl_transition(&lc), "LC transition failed!");
215 
216  LOG_INFO("Waiting for LC transition done and reboot.");
218 
219  // Unreachable.
220  return false;
221  }
222  } else if (curr_state == kDestState) {
223  bool secret2_locked;
224  CHECK_DIF_OK(dif_otp_ctrl_is_digest_computed(
225  &otp, kDifOtpCtrlPartitionSecret2, &secret2_locked));
226 
227  if (!secret2_locked) {
228  LOG_INFO(
229  "In destination state. Check LC count and lock secret2 partition.");
230  CHECK(curr_state == kDestState);
231  CHECK_STATUS_OK(lc_ctrl_testutils_check_transition_count(&lc, 2));
232 
233  lock_otp_secret2_partition();
234  LOG_INFO("Written and locked OTP secret2 partition in next power cycle!");
235  CHECK_DIF_OK(dif_rstmgr_software_device_reset(&rstmgr));
237  return false;
238 
239  } else {
240  if (kDestState == kDifLcCtrlStateRma ||
241  kDestState == kDifLcCtrlStateProdEnd) {
242  CHECK_STATUS_OK(lc_ctrl_testutils_check_transition_count(&lc, 2));
243  LOG_INFO("LC transition done!");
244  return true;
245  } else {
246  // Check LC counts and transit to RMA state.
247  LOG_INFO(
248  "In destination state. Check LC count and transfer to RMA "
249  "state.");
250  CHECK_STATUS_OK(lc_ctrl_testutils_check_transition_count(&lc, 2));
251 
252  // Issue a LC state transfer to destination state.
253  dif_lc_ctrl_token_t token;
254  for (int i = 0; i < LC_TOKEN_SIZE; i++) {
255  token.data[i] = kLcRmaToken[i];
256  }
257 
258  CHECK_DIF_OK(dif_lc_ctrl_mutex_try_acquire(&lc));
259 
260  // TODO: randomize using external or internal clock.
261  bool use_ext_clock = true;
262  CHECK_DIF_OK(dif_lc_ctrl_configure(&lc, kDifLcCtrlStateRma,
263  use_ext_clock, &token),
264  "LC transition configuration failed!");
265  CHECK_DIF_OK(dif_lc_ctrl_transition(&lc), "LC transition failed!");
266 
267  LOG_INFO("Waiting for LC RMA transition done and reboot.");
269 
270  // Unreachable.
271  return false;
272  }
273  }
274  } else {
275  LOG_INFO("In RMA state. Check LC count and exit.");
276  CHECK(curr_state == kDifLcCtrlStateRma);
277  CHECK_STATUS_OK(lc_ctrl_testutils_check_transition_count(&lc, 3));
278  return true;
279  }
280 }