Pavona 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 }
41 // causes[kOtpPartitionCount] is reserved for the DAI error.
42 if (status.causes[kOtpPartitionCount] != kDifOtpCtrlErrorLockedAccess) {
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 }
49 // causes[kOtpPartitionCount] is reserved for the DAI error.
50 if (status.causes[kOtpPartitionCount] != kDifOtpCtrlErrorOk) {
51 LOG_ERROR("No DAI error code expected for access to 0x%x", address);
52 }
53 }
54 return OK_STATUS();
55}
56
57status_t otp_ctrl_testutils_wait_for_dai(const dif_otp_ctrl_t *otp_ctrl) {
58 TRY(otp_ctrl_testutils_wait_for_dai_pre_hook(otp_ctrl));
59 IBEX_TRY_SPIN_FOR(dai_finished(otp_ctrl), kOtpDaiTimeoutUs);
60 TRY(otp_ctrl_testutils_wait_for_dai_post_hook(otp_ctrl));
61 return OK_STATUS();
62}
63
64status_t otp_ctrl_testutils_lock_partition(const dif_otp_ctrl_t *otp,
65 otp_partition_t partition,
66 uint64_t digest) {
67 TRY(dif_otp_ctrl_dai_digest(otp, partition, digest));
68 return otp_ctrl_testutils_wait_for_dai(otp);
69}
70
71status_t otp_ctrl_testutils_dai_read32(const dif_otp_ctrl_t *otp,
72 otp_partition_t partition,
73 uint32_t address, uint32_t *result) {
74 TRY(otp_ctrl_testutils_wait_for_dai(otp));
75 TRY(otp_ctrl_testutils_dai_read_pre_hook(otp));
76 TRY(dif_otp_ctrl_dai_read_start(otp, partition, address));
77 TRY(otp_ctrl_testutils_wait_for_dai(otp));
78 TRY(dif_otp_ctrl_dai_read32_end(otp, result));
79 TRY(otp_ctrl_testutils_dai_read_post_hook(otp));
80 return OK_STATUS();
81}
82
83status_t otp_ctrl_testutils_dai_read32_array(const dif_otp_ctrl_t *otp,
84 otp_partition_t partition,
85 uint32_t start_address,
86 uint32_t *buffer, size_t len) {
87 uint32_t stop_address = start_address + (len * sizeof(uint32_t));
88 for (uint32_t addr = start_address, i = 0; addr < stop_address;
89 addr += sizeof(uint32_t), ++i) {
90 TRY(otp_ctrl_testutils_wait_for_dai(otp));
91 TRY(otp_ctrl_testutils_dai_read_pre_hook(otp));
92 TRY(dif_otp_ctrl_dai_read_start(otp, partition, addr));
93 TRY(otp_ctrl_testutils_wait_for_dai(otp));
94 TRY(dif_otp_ctrl_dai_read32_end(otp, &buffer[i]));
95 TRY(otp_ctrl_testutils_dai_read_post_hook(otp));
96 }
97 return OK_STATUS();
98}
99
100status_t otp_ctrl_testutils_dai_read64(const dif_otp_ctrl_t *otp,
101 otp_partition_t partition,
102 uint32_t address, uint64_t *result) {
103 TRY(otp_ctrl_testutils_wait_for_dai(otp));
104 TRY(otp_ctrl_testutils_dai_read_pre_hook(otp));
105 TRY(dif_otp_ctrl_dai_read_start(otp, partition, address));
106 TRY(otp_ctrl_testutils_wait_for_dai(otp));
107 TRY(dif_otp_ctrl_dai_read64_end(otp, result));
108 TRY(otp_ctrl_testutils_dai_read_post_hook(otp));
109 return OK_STATUS();
110}
111
112status_t otp_ctrl_testutils_dai_read64_array(const dif_otp_ctrl_t *otp,
113 otp_partition_t partition,
114 uint32_t start_address,
115 uint64_t *buffer, size_t len) {
116 uint32_t stop_address = start_address + (len * sizeof(uint64_t));
117 for (uint32_t addr = start_address, i = 0; addr < stop_address;
118 addr += sizeof(uint64_t), ++i) {
119 TRY(otp_ctrl_testutils_wait_for_dai(otp));
120 TRY(otp_ctrl_testutils_dai_read_pre_hook(otp));
121 TRY(dif_otp_ctrl_dai_read_start(otp, partition, addr));
122 TRY(otp_ctrl_testutils_wait_for_dai(otp));
123 TRY(dif_otp_ctrl_dai_read64_end(otp, &buffer[i]));
124 TRY(otp_ctrl_testutils_dai_read_post_hook(otp));
125 }
126 return OK_STATUS();
127}
128
129/**
130 * Checks if there were any errors found after executing a DAI write transaction
131 * to the SECRET2 partition.
132 *
133 * @param otp otp_ctrl instance
134 * @return OK_STATUS if there were no errors detected.
135 */
137static status_t otp_ctrl_dai_write_error_check(const dif_otp_ctrl_t *otp) {
139 TRY(dif_otp_ctrl_get_status(otp, &status));
140
141 // TODO: Check for other OTP errors.
142 if (bitfield_bit32_read(status.codes, kDifOtpCtrlStatusCodeDaiIdle) &&
143 !bitfield_bit32_read(status.codes, kDifOtpCtrlStatusCodeDaiError)) {
144 return OK_STATUS();
145 }
146 LOG_ERROR("dai_write_error_check code: 0x%x", status.codes);
147 return INTERNAL();
148}
149
150status_t otp_ctrl_testutils_dai_write32(const dif_otp_ctrl_t *otp,
151 otp_partition_t partition,
152 uint32_t start_address,
153 const uint32_t *buffer, size_t len) {
154 // Software partitions don't have scrambling or ECC enabled, so it is possible
155 // to read the value and compare it against the expected value before
156 // performing the write.
157 bool check_before_write = (
158#ifdef OPENTITAN_IS_EGRET
159 partition == kOtpPartitionRotCreatorAuthCodesign ||
160 partition == kOtpPartitionRotCreatorAuthState ||
161#endif // OPENTITAN_IS_EGRET
162 partition == kOtpPartitionCreatorSwCfg ||
163 partition == kOtpPartitionOwnerSwCfg);
164 uint32_t stop_address = start_address + (len * sizeof(uint32_t));
165 for (uint32_t addr = start_address, i = 0; addr < stop_address;
166 addr += sizeof(uint32_t), ++i) {
167 uint32_t read_data;
168 if (check_before_write) {
169 TRY(otp_ctrl_testutils_dai_read32(otp, partition, addr, &read_data));
170 if (read_data == buffer[i]) {
171 continue;
172 }
173 if (read_data != 0) {
174 LOG_ERROR("OTP partition: %d addr[0x%x] got: 0x%08x, expected: 0x%08x",
175 partition, addr, read_data, buffer[i]);
176 return INTERNAL();
177 }
178 }
179
180 TRY(otp_ctrl_testutils_wait_for_dai(otp));
181 TRY(otp_ctrl_testutils_dai_write_pre_hook(otp));
182 TRY(dif_otp_ctrl_dai_program32(otp, partition, addr, buffer[i]));
183 TRY(otp_ctrl_testutils_dai_write_post_hook(otp));
184 TRY(otp_ctrl_testutils_wait_for_dai(otp));
185 TRY(otp_ctrl_testutils_dai_write_pre_error_check_hook(otp));
186 TRY(otp_ctrl_dai_write_error_check(otp));
187 TRY(otp_ctrl_testutils_dai_write_post_error_check_hook(otp));
188
189 TRY(otp_ctrl_testutils_dai_read32(otp, partition, addr, &read_data));
190 if (read_data != buffer[i]) {
191 return INTERNAL();
192 }
193 }
194 return OK_STATUS();
195}
196
197status_t otp_ctrl_testutils_dai_write64(const dif_otp_ctrl_t *otp,
198 otp_partition_t partition,
199 uint32_t start_address,
200 const uint64_t *buffer, size_t len) {
201 uint32_t stop_address = start_address + (len * sizeof(uint64_t));
202 for (uint32_t addr = start_address, i = 0; addr < stop_address;
203 addr += sizeof(uint64_t), ++i) {
204 TRY(otp_ctrl_testutils_wait_for_dai(otp));
205 TRY(otp_ctrl_testutils_dai_write_pre_hook(otp));
206 TRY(dif_otp_ctrl_dai_program64(otp, partition, addr, buffer[i]));
207 TRY(otp_ctrl_testutils_dai_write_post_hook(otp));
208 TRY(otp_ctrl_testutils_wait_for_dai(otp));
209 TRY(otp_ctrl_testutils_dai_write_pre_error_check_hook(otp));
210 TRY(otp_ctrl_dai_write_error_check(otp));
211 TRY(otp_ctrl_testutils_dai_write_post_error_check_hook(otp));
212
213 uint64_t read_data;
214 TRY(otp_ctrl_testutils_dai_read64(otp, partition, addr, &read_data));
215 if (read_data != buffer[i]) {
216 return INTERNAL();
217 }
218 }
219 return OK_STATUS();
220}
221
223status_t otp_ctrl_testutils_wait_for_dai_pre_hook(
224 const dif_otp_ctrl_t *otp_ctrl) {
225 return OK_STATUS();
226}
227
229status_t otp_ctrl_testutils_wait_for_dai_post_hook(
230 const dif_otp_ctrl_t *otp_ctrl) {
231 return OK_STATUS();
232}
233
235status_t otp_ctrl_testutils_dai_write_pre_hook(const dif_otp_ctrl_t *otp_ctrl) {
236 return OK_STATUS();
237}
238
240status_t otp_ctrl_testutils_dai_write_post_hook(
241 const dif_otp_ctrl_t *otp_ctrl) {
242 return OK_STATUS();
243}
244
246status_t otp_ctrl_testutils_dai_read_pre_hook(const dif_otp_ctrl_t *otp_ctrl) {
247 return OK_STATUS();
248}
249
251status_t otp_ctrl_testutils_dai_read_post_hook(const dif_otp_ctrl_t *otp_ctrl) {
252 return OK_STATUS();
253}
254
256status_t otp_ctrl_testutils_dai_write_pre_error_check_hook(
257 const dif_otp_ctrl_t *otp_ctrl) {
258 return OK_STATUS();
259}
260
262status_t otp_ctrl_testutils_dai_write_post_error_check_hook(
263 const dif_otp_ctrl_t *otp_ctrl) {
264 return OK_STATUS();
265}