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 100us is used.
19 */
20const uint16_t kOtpDaiTimeoutUs = 5000;
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 IBEX_TRY_SPIN_FOR(dai_finished(otp_ctrl), kOtpDaiTimeoutUs);
58 return OK_STATUS();
59}
60
61status_t otp_ctrl_testutils_lock_partition(const dif_otp_ctrl_t *otp,
63 uint64_t digest) {
64 TRY(dif_otp_ctrl_dai_digest(otp, partition, digest));
65 return otp_ctrl_testutils_wait_for_dai(otp);
66}
67
68status_t otp_ctrl_testutils_dai_read32(const dif_otp_ctrl_t *otp,
70 uint32_t address, uint32_t *result) {
71 TRY(otp_ctrl_testutils_wait_for_dai(otp));
72 TRY(dif_otp_ctrl_dai_read_start(otp, partition, address));
73 TRY(otp_ctrl_testutils_wait_for_dai(otp));
74 TRY(dif_otp_ctrl_dai_read32_end(otp, result));
75 return OK_STATUS();
76}
77
78status_t otp_ctrl_testutils_dai_read32_array(const dif_otp_ctrl_t *otp,
80 uint32_t start_address,
81 uint32_t *buffer, size_t len) {
82 uint32_t stop_address = start_address + (len * sizeof(uint32_t));
83 for (uint32_t addr = start_address, i = 0; addr < stop_address;
84 addr += sizeof(uint32_t), ++i) {
85 TRY(otp_ctrl_testutils_wait_for_dai(otp));
86 TRY(dif_otp_ctrl_dai_read_start(otp, partition, addr));
87 TRY(otp_ctrl_testutils_wait_for_dai(otp));
88 TRY(dif_otp_ctrl_dai_read32_end(otp, &buffer[i]));
89 }
90 return OK_STATUS();
91}
92
93status_t otp_ctrl_testutils_dai_read64(const dif_otp_ctrl_t *otp,
95 uint32_t address, uint64_t *result) {
96 TRY(otp_ctrl_testutils_wait_for_dai(otp));
97 TRY(dif_otp_ctrl_dai_read_start(otp, partition, address));
98 TRY(otp_ctrl_testutils_wait_for_dai(otp));
99 TRY(dif_otp_ctrl_dai_read64_end(otp, result));
100 return OK_STATUS();
101}
102
103status_t otp_ctrl_testutils_dai_read64_array(const dif_otp_ctrl_t *otp,
104 dif_otp_ctrl_partition_t partition,
105 uint32_t start_address,
106 uint64_t *buffer, size_t len) {
107 uint32_t stop_address = start_address + (len * sizeof(uint64_t));
108 for (uint32_t addr = start_address, i = 0; addr < stop_address;
109 addr += sizeof(uint64_t), ++i) {
110 TRY(otp_ctrl_testutils_wait_for_dai(otp));
111 TRY(dif_otp_ctrl_dai_read_start(otp, partition, addr));
112 TRY(otp_ctrl_testutils_wait_for_dai(otp));
113 TRY(dif_otp_ctrl_dai_read64_end(otp, &buffer[i]));
114 }
115 return OK_STATUS();
116}
117
118/**
119 * Checks if there were any errors found after executing a DAI write transaction
120 * to the SECRET2 partition.
121 *
122 * @param otp otp_ctrl instance
123 * @return OK_STATUS if there were no errors detected.
124 */
126static status_t otp_ctrl_dai_write_error_check(const dif_otp_ctrl_t *otp) {
128 TRY(dif_otp_ctrl_get_status(otp, &status));
129
130 // TODO: Check for other OTP errors.
131 if (bitfield_bit32_read(status.codes, kDifOtpCtrlStatusCodeDaiIdle) &&
132 !bitfield_bit32_read(status.codes, kDifOtpCtrlStatusCodeDaiError)) {
133 return OK_STATUS();
134 }
135 LOG_ERROR("dai_write_error_check code: 0x%x", status.codes);
136 return INTERNAL();
137}
138
139status_t otp_ctrl_testutils_dai_write32(const dif_otp_ctrl_t *otp,
140 dif_otp_ctrl_partition_t partition,
141 uint32_t start_address,
142 const uint32_t *buffer, size_t len) {
143 // Software partitions don't have scrambling or ECC enabled, so it is possible
144 // to read the value and compare it against the expected value before
145 // performing the write.
146 bool check_before_write = (
147#ifdef OPENTITAN_IS_EARLGREY
148 partition == kDifOtpCtrlPartitionRotCreatorAuthCodesign ||
149 partition == kDifOtpCtrlPartitionRotCreatorAuthState ||
150#endif // OPENTITAN_IS_EARLGREY
152 partition == kDifOtpCtrlPartitionOwnerSwCfg);
153 uint32_t stop_address = start_address + (len * sizeof(uint32_t));
154 for (uint32_t addr = start_address, i = 0; addr < stop_address;
155 addr += sizeof(uint32_t), ++i) {
156 uint32_t read_data;
157 if (check_before_write) {
158 TRY(otp_ctrl_testutils_dai_read32(otp, partition, addr, &read_data));
159 if (read_data == buffer[i]) {
160 continue;
161 }
162 if (read_data != 0) {
163 LOG_ERROR("OTP partition: %d addr[0x%x] got: 0x%08x, expected: 0x%08x",
164 partition, addr, read_data, buffer[i]);
165 return INTERNAL();
166 }
167 }
168
169 TRY(otp_ctrl_testutils_wait_for_dai(otp));
170 TRY(dif_otp_ctrl_dai_program32(otp, partition, addr, buffer[i]));
171 TRY(otp_ctrl_testutils_wait_for_dai(otp));
172 TRY(otp_ctrl_dai_write_error_check(otp));
173
174 TRY(otp_ctrl_testutils_dai_read32(otp, partition, addr, &read_data));
175 if (read_data != buffer[i]) {
176 return INTERNAL();
177 }
178 }
179 return OK_STATUS();
180}
181
182status_t otp_ctrl_testutils_dai_write64(const dif_otp_ctrl_t *otp,
183 dif_otp_ctrl_partition_t partition,
184 uint32_t start_address,
185 const uint64_t *buffer, size_t len) {
186 uint32_t stop_address = start_address + (len * sizeof(uint64_t));
187 for (uint32_t addr = start_address, i = 0; addr < stop_address;
188 addr += sizeof(uint64_t), ++i) {
189 TRY(otp_ctrl_testutils_wait_for_dai(otp));
190 TRY(dif_otp_ctrl_dai_program64(otp, partition, addr, buffer[i]));
191 TRY(otp_ctrl_testutils_wait_for_dai(otp));
192 TRY(otp_ctrl_dai_write_error_check(otp));
193
194 uint64_t read_data;
195 TRY(otp_ctrl_testutils_dai_read64(otp, partition, addr, &read_data));
196 if (read_data != buffer[i]) {
197 return INTERNAL();
198 }
199 }
200 return OK_STATUS();
201}