Software APIs
otp_ctrl_testutils.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/lib/testing/otp_ctrl_testutils.h"
6
10#include "sw/device/lib/testing/test_framework/check.h"
11
12#define MODULE_ID MAKE_MODULE_ID('o', 'c', 't')
13
14/*
15 * OTP the Direct Access Interface (DAI) operation time-out in micro seconds.
16 *
17 * It is not possible to predict the specific cycle count that a DAI operation
18 * takes, thus arbitrary value of 10ms is used.
19 */
20const uint16_t kOtpDaiTimeoutUs = 10000;
21
22/**
23 * Checks whether the DAI operation has finished.
24 */
25static bool dai_finished(const dif_otp_ctrl_t *otp_ctrl) {
27 dif_result_t res = dif_otp_ctrl_get_status(otp_ctrl, &status);
28 return res == kDifOk &&
29 bitfield_bit32_read(status.codes, kDifOtpCtrlStatusCodeDaiIdle);
30}
31
32status_t otp_ctrl_testutils_dai_access_error_check(
33 const dif_otp_ctrl_t *otp_ctrl, exp_test_result_t exp_result,
34 int32_t address) {
36 TRY(dif_otp_ctrl_get_status(otp_ctrl, &status));
37 if (exp_result == kExpectFailed) {
38 if (!bitfield_bit32_read(status.codes, kDifOtpCtrlStatusCodeDaiError)) {
39 LOG_ERROR("Expected a DAI error for access to 0x%x", address);
40 }
43 LOG_ERROR("Expected access locked error for access to 0x%x", address);
44 }
45 } else {
46 if (bitfield_bit32_read(status.codes, kDifOtpCtrlStatusCodeDaiError)) {
47 LOG_ERROR("No DAI error expected for access to 0x%x", address);
48 }
50 LOG_ERROR("No DAI error code expected for access to 0x%x", address);
51 }
52 }
53 return OK_STATUS();
54}
55
56status_t otp_ctrl_testutils_wait_for_dai(const dif_otp_ctrl_t *otp_ctrl) {
57 TRY(otp_ctrl_testutils_wait_for_dai_pre_hook(otp_ctrl));
58 IBEX_TRY_SPIN_FOR(dai_finished(otp_ctrl), kOtpDaiTimeoutUs);
59 TRY(otp_ctrl_testutils_wait_for_dai_post_hook(otp_ctrl));
60 return OK_STATUS();
61}
62
63status_t otp_ctrl_testutils_lock_partition(const dif_otp_ctrl_t *otp,
65 uint64_t digest) {
66 TRY(dif_otp_ctrl_dai_digest(otp, partition, digest));
67 return otp_ctrl_testutils_wait_for_dai(otp);
68}
69
70status_t otp_ctrl_testutils_dai_read32(const dif_otp_ctrl_t *otp,
72 uint32_t address, uint32_t *result) {
73 TRY(otp_ctrl_testutils_wait_for_dai(otp));
74 TRY(otp_ctrl_testutils_dai_read_pre_hook(otp));
75 TRY(dif_otp_ctrl_dai_read_start(otp, partition, address));
76 TRY(otp_ctrl_testutils_wait_for_dai(otp));
77 TRY(dif_otp_ctrl_dai_read32_end(otp, result));
78 TRY(otp_ctrl_testutils_dai_read_post_hook(otp));
79 return OK_STATUS();
80}
81
82status_t otp_ctrl_testutils_dai_read32_array(const dif_otp_ctrl_t *otp,
84 uint32_t start_address,
85 uint32_t *buffer, size_t len) {
86 uint32_t stop_address = start_address + (len * sizeof(uint32_t));
87 for (uint32_t addr = start_address, i = 0; addr < stop_address;
88 addr += sizeof(uint32_t), ++i) {
89 TRY(otp_ctrl_testutils_wait_for_dai(otp));
90 TRY(otp_ctrl_testutils_dai_read_pre_hook(otp));
91 TRY(dif_otp_ctrl_dai_read_start(otp, partition, addr));
92 TRY(otp_ctrl_testutils_wait_for_dai(otp));
93 TRY(dif_otp_ctrl_dai_read32_end(otp, &buffer[i]));
94 TRY(otp_ctrl_testutils_dai_read_post_hook(otp));
95 }
96 return OK_STATUS();
97}
98
99status_t otp_ctrl_testutils_dai_read64(const dif_otp_ctrl_t *otp,
100 dif_otp_ctrl_partition_t partition,
101 uint32_t address, uint64_t *result) {
102 TRY(otp_ctrl_testutils_wait_for_dai(otp));
103 TRY(otp_ctrl_testutils_dai_read_pre_hook(otp));
104 TRY(dif_otp_ctrl_dai_read_start(otp, partition, address));
105 TRY(otp_ctrl_testutils_wait_for_dai(otp));
106 TRY(dif_otp_ctrl_dai_read64_end(otp, result));
107 TRY(otp_ctrl_testutils_dai_read_post_hook(otp));
108 return OK_STATUS();
109}
110
111status_t otp_ctrl_testutils_dai_read64_array(const dif_otp_ctrl_t *otp,
112 dif_otp_ctrl_partition_t partition,
113 uint32_t start_address,
114 uint64_t *buffer, size_t len) {
115 uint32_t stop_address = start_address + (len * sizeof(uint64_t));
116 for (uint32_t addr = start_address, i = 0; addr < stop_address;
117 addr += sizeof(uint64_t), ++i) {
118 TRY(otp_ctrl_testutils_wait_for_dai(otp));
119 TRY(otp_ctrl_testutils_dai_read_pre_hook(otp));
120 TRY(dif_otp_ctrl_dai_read_start(otp, partition, addr));
121 TRY(otp_ctrl_testutils_wait_for_dai(otp));
122 TRY(dif_otp_ctrl_dai_read64_end(otp, &buffer[i]));
123 TRY(otp_ctrl_testutils_dai_read_post_hook(otp));
124 }
125 return OK_STATUS();
126}
127
128/**
129 * Checks if there were any errors found after executing a DAI write transaction
130 * to the SECRET2 partition.
131 *
132 * @param otp otp_ctrl instance
133 * @return OK_STATUS if there were no errors detected.
134 */
136static status_t otp_ctrl_dai_write_error_check(const dif_otp_ctrl_t *otp) {
138 TRY(dif_otp_ctrl_get_status(otp, &status));
139
140 // TODO: Check for other OTP errors.
141 if (bitfield_bit32_read(status.codes, kDifOtpCtrlStatusCodeDaiIdle) &&
142 !bitfield_bit32_read(status.codes, kDifOtpCtrlStatusCodeDaiError)) {
143 return OK_STATUS();
144 }
145 LOG_ERROR("dai_write_error_check code: 0x%x", status.codes);
146 return INTERNAL();
147}
148
149status_t otp_ctrl_testutils_dai_write32(const dif_otp_ctrl_t *otp,
150 dif_otp_ctrl_partition_t partition,
151 uint32_t start_address,
152 const uint32_t *buffer, size_t len) {
153 // Software partitions don't have scrambling or ECC enabled, so it is possible
154 // to read the value and compare it against the expected value before
155 // performing the write.
156 bool check_before_write = (
157#ifdef OPENTITAN_IS_EARLGREY
158 partition == kDifOtpCtrlPartitionRotCreatorAuthCodesign ||
159 partition == kDifOtpCtrlPartitionRotCreatorAuthState ||
160#endif // OPENTITAN_IS_EARLGREY
162 partition == kDifOtpCtrlPartitionOwnerSwCfg);
163 uint32_t stop_address = start_address + (len * sizeof(uint32_t));
164 for (uint32_t addr = start_address, i = 0; addr < stop_address;
165 addr += sizeof(uint32_t), ++i) {
166 uint32_t read_data;
167 if (check_before_write) {
168 TRY(otp_ctrl_testutils_dai_read32(otp, partition, addr, &read_data));
169 if (read_data == buffer[i]) {
170 continue;
171 }
172 if (read_data != 0) {
173 LOG_ERROR("OTP partition: %d addr[0x%x] got: 0x%08x, expected: 0x%08x",
174 partition, addr, read_data, buffer[i]);
175 return INTERNAL();
176 }
177 }
178
179 TRY(otp_ctrl_testutils_wait_for_dai(otp));
180 TRY(otp_ctrl_testutils_dai_write_pre_hook(otp));
181 TRY(dif_otp_ctrl_dai_program32(otp, partition, addr, buffer[i]));
182 TRY(otp_ctrl_testutils_dai_write_post_hook(otp));
183 TRY(otp_ctrl_testutils_wait_for_dai(otp));
184 TRY(otp_ctrl_testutils_dai_write_pre_error_check_hook(otp));
185 TRY(otp_ctrl_dai_write_error_check(otp));
186 TRY(otp_ctrl_testutils_dai_write_post_error_check_hook(otp));
187
188 TRY(otp_ctrl_testutils_dai_read32(otp, partition, addr, &read_data));
189 if (read_data != buffer[i]) {
190 return INTERNAL();
191 }
192 }
193 return OK_STATUS();
194}
195
196status_t otp_ctrl_testutils_dai_write64(const dif_otp_ctrl_t *otp,
197 dif_otp_ctrl_partition_t partition,
198 uint32_t start_address,
199 const uint64_t *buffer, size_t len) {
200 uint32_t stop_address = start_address + (len * sizeof(uint64_t));
201 for (uint32_t addr = start_address, i = 0; addr < stop_address;
202 addr += sizeof(uint64_t), ++i) {
203 TRY(otp_ctrl_testutils_wait_for_dai(otp));
204 TRY(otp_ctrl_testutils_dai_write_pre_hook(otp));
205 TRY(dif_otp_ctrl_dai_program64(otp, partition, addr, buffer[i]));
206 TRY(otp_ctrl_testutils_dai_write_post_hook(otp));
207 TRY(otp_ctrl_testutils_wait_for_dai(otp));
208 TRY(otp_ctrl_testutils_dai_write_pre_error_check_hook(otp));
209 TRY(otp_ctrl_dai_write_error_check(otp));
210 TRY(otp_ctrl_testutils_dai_write_post_error_check_hook(otp));
211
212 uint64_t read_data;
213 TRY(otp_ctrl_testutils_dai_read64(otp, partition, addr, &read_data));
214 if (read_data != buffer[i]) {
215 return INTERNAL();
216 }
217 }
218 return OK_STATUS();
219}
220
222status_t otp_ctrl_testutils_wait_for_dai_pre_hook(
223 const dif_otp_ctrl_t *otp_ctrl) {
224 return OK_STATUS();
225}
226
228status_t otp_ctrl_testutils_wait_for_dai_post_hook(
229 const dif_otp_ctrl_t *otp_ctrl) {
230 return OK_STATUS();
231}
232
234status_t otp_ctrl_testutils_dai_write_pre_hook(const dif_otp_ctrl_t *otp_ctrl) {
235 return OK_STATUS();
236}
237
239status_t otp_ctrl_testutils_dai_write_post_hook(
240 const dif_otp_ctrl_t *otp_ctrl) {
241 return OK_STATUS();
242}
243
245status_t otp_ctrl_testutils_dai_read_pre_hook(const dif_otp_ctrl_t *otp_ctrl) {
246 return OK_STATUS();
247}
248
250status_t otp_ctrl_testutils_dai_read_post_hook(const dif_otp_ctrl_t *otp_ctrl) {
251 return OK_STATUS();
252}
253
255status_t otp_ctrl_testutils_dai_write_pre_error_check_hook(
256 const dif_otp_ctrl_t *otp_ctrl) {
257 return OK_STATUS();
258}
259
261status_t otp_ctrl_testutils_dai_write_post_error_check_hook(
262 const dif_otp_ctrl_t *otp_ctrl) {
263 return OK_STATUS();
264}