Software APIs
dif_otp_ctrl.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
5
6#include <stddef.h>
7
11
12#include "otp_ctrl_regs.h" // Generated.
13
14/**
15 * Checks if integrity/consistency-check-related operations are locked.
16 *
17 * This is a convenience function to avoid superfluous error-checking in all the
18 * functions that can be locked out by this register.
19 *
20 * @param check_config True to check the config regwen. False to check the
21 * trigger regwen.
22 */
23static bool checks_are_locked(const dif_otp_ctrl_t *otp, bool check_config) {
24 ptrdiff_t reg_offset = check_config
25 ? OTP_CTRL_CHECK_REGWEN_REG_OFFSET
26 : OTP_CTRL_CHECK_TRIGGER_REGWEN_REG_OFFSET;
27 size_t regwen_bit =
28 check_config ? OTP_CTRL_CHECK_REGWEN_CHECK_REGWEN_BIT
29 : OTP_CTRL_CHECK_TRIGGER_REGWEN_CHECK_TRIGGER_REGWEN_BIT;
30 uint32_t locked = mmio_region_read32(otp->base_addr, reg_offset);
31 return !bitfield_bit32_read(locked, regwen_bit);
32}
33
34dif_result_t dif_otp_ctrl_configure(const dif_otp_ctrl_t *otp,
35 dif_otp_ctrl_config_t config) {
36 if (otp == NULL) {
37 return kDifBadArg;
38 }
39 if (checks_are_locked(otp, /*check_config=*/true)) {
40 return kDifLocked;
41 }
42
43 mmio_region_write32(otp->base_addr, OTP_CTRL_CHECK_TIMEOUT_REG_OFFSET,
44 config.check_timeout);
45 mmio_region_write32(otp->base_addr,
46 OTP_CTRL_INTEGRITY_CHECK_PERIOD_REG_OFFSET,
48 mmio_region_write32(otp->base_addr,
49 OTP_CTRL_CONSISTENCY_CHECK_PERIOD_REG_OFFSET,
51
52 return kDifOk;
53}
54
55dif_result_t dif_otp_ctrl_check_integrity(const dif_otp_ctrl_t *otp) {
56 if (otp == NULL) {
57 return kDifBadArg;
58 }
59 if (checks_are_locked(otp, /*check_config=*/false)) {
60 return kDifLocked;
61 }
62
63 uint32_t reg =
64 bitfield_bit32_write(0, OTP_CTRL_CHECK_TRIGGER_INTEGRITY_BIT, true);
65 mmio_region_write32(otp->base_addr, OTP_CTRL_CHECK_TRIGGER_REG_OFFSET, reg);
66
67 return kDifOk;
68}
69
70dif_result_t dif_otp_ctrl_check_consistency(const dif_otp_ctrl_t *otp) {
71 if (otp == NULL) {
72 return kDifBadArg;
73 }
74 if (checks_are_locked(otp, /*check_config=*/false)) {
75 return kDifLocked;
76 }
77
78 uint32_t reg =
79 bitfield_bit32_write(0, OTP_CTRL_CHECK_TRIGGER_CONSISTENCY_BIT, true);
80 mmio_region_write32(otp->base_addr, OTP_CTRL_CHECK_TRIGGER_REG_OFFSET, reg);
81
82 return kDifOk;
83}
84
85dif_result_t dif_otp_ctrl_dai_lock(const dif_otp_ctrl_t *otp) {
86 if (otp == NULL) {
87 return kDifBadArg;
88 }
89
90 uint32_t reg = bitfield_bit32_write(
91 0, OTP_CTRL_DIRECT_ACCESS_REGWEN_DIRECT_ACCESS_REGWEN_BIT, false);
92 mmio_region_write32(otp->base_addr, OTP_CTRL_DIRECT_ACCESS_REGWEN_REG_OFFSET,
93 reg);
94
95 return kDifOk;
96}
97
98dif_result_t dif_otp_ctrl_dai_is_locked(const dif_otp_ctrl_t *otp,
99 bool *is_locked) {
100 if (otp == NULL || is_locked == NULL) {
101 return kDifBadArg;
102 }
103
104 uint32_t reg = mmio_region_read32(otp->base_addr,
105 OTP_CTRL_DIRECT_ACCESS_REGWEN_REG_OFFSET);
106 *is_locked = !bitfield_bit32_read(
107 reg, OTP_CTRL_DIRECT_ACCESS_REGWEN_DIRECT_ACCESS_REGWEN_BIT);
108
109 return kDifOk;
110}
111
112dif_result_t dif_otp_ctrl_lock_config(const dif_otp_ctrl_t *otp) {
113 if (otp == NULL) {
114 return kDifBadArg;
115 }
116
117 uint32_t reg =
118 bitfield_bit32_write(0, OTP_CTRL_CHECK_REGWEN_CHECK_REGWEN_BIT, false);
119 mmio_region_write32(otp->base_addr, OTP_CTRL_CHECK_REGWEN_REG_OFFSET, reg);
120
121 return kDifOk;
122}
123
124dif_result_t dif_otp_ctrl_config_is_locked(const dif_otp_ctrl_t *otp,
125 bool *is_locked) {
126 if (otp == NULL || is_locked == NULL) {
127 return kDifBadArg;
128 }
129
130 *is_locked = checks_are_locked(otp, /*check_config=*/true);
131 return kDifOk;
132}
133
134dif_result_t dif_otp_ctrl_lock_check_trigger(const dif_otp_ctrl_t *otp) {
135 if (otp == NULL) {
136 return kDifBadArg;
137 }
138
139 uint32_t reg = bitfield_bit32_write(
140 0, OTP_CTRL_CHECK_TRIGGER_REGWEN_CHECK_TRIGGER_REGWEN_BIT, false);
141 mmio_region_write32(otp->base_addr, OTP_CTRL_CHECK_TRIGGER_REGWEN_REG_OFFSET,
142 reg);
143
144 return kDifOk;
145}
146
147dif_result_t dif_otp_ctrl_check_trigger_is_locked(const dif_otp_ctrl_t *otp,
148 bool *is_locked) {
149 if (otp == NULL || is_locked == NULL) {
150 return kDifBadArg;
151 }
152
153 *is_locked = checks_are_locked(otp, /*check_config=*/false);
154 return kDifOk;
155}
156
157static bool sw_read_lock_reg_offset(dif_otp_ctrl_partition_t partition,
158 ptrdiff_t *reg_offset,
159 bitfield_bit32_index_t *index) {
160 switch (partition) {
162 *reg_offset = OTP_CTRL_VENDOR_TEST_READ_LOCK_REG_OFFSET;
163 *index = OTP_CTRL_VENDOR_TEST_READ_LOCK_VENDOR_TEST_READ_LOCK_BIT;
164 break;
166 *reg_offset = OTP_CTRL_CREATOR_SW_CFG_READ_LOCK_REG_OFFSET;
167 *index = OTP_CTRL_CREATOR_SW_CFG_READ_LOCK_CREATOR_SW_CFG_READ_LOCK_BIT;
168 break;
170 *reg_offset = OTP_CTRL_OWNER_SW_CFG_READ_LOCK_REG_OFFSET;
171 *index = OTP_CTRL_OWNER_SW_CFG_READ_LOCK_OWNER_SW_CFG_READ_LOCK_BIT;
172 break;
173#if defined(OPENTITAN_IS_EARLGREY)
174 case kDifOtpCtrlPartitionRotCreatorAuthCodesign:
175 *reg_offset = OTP_CTRL_ROT_CREATOR_AUTH_CODESIGN_READ_LOCK_REG_OFFSET;
176 *index =
177 OTP_CTRL_ROT_CREATOR_AUTH_CODESIGN_READ_LOCK_ROT_CREATOR_AUTH_CODESIGN_READ_LOCK_BIT;
178 break;
179 case kDifOtpCtrlPartitionRotCreatorAuthState:
180 *reg_offset = OTP_CTRL_ROT_CREATOR_AUTH_STATE_READ_LOCK_REG_OFFSET;
181 *index =
182 OTP_CTRL_ROT_CREATOR_AUTH_STATE_READ_LOCK_ROT_CREATOR_AUTH_STATE_READ_LOCK_BIT;
183 break;
184#elif defined(OPENTITAN_IS_DARJEELING)
185 case kDifOtpCtrlPartitionOwnershipSlotState:
186 *reg_offset = OTP_CTRL_OWNERSHIP_SLOT_STATE_READ_LOCK_REG_OFFSET;
187 *index =
188 OTP_CTRL_OWNERSHIP_SLOT_STATE_READ_LOCK_OWNERSHIP_SLOT_STATE_READ_LOCK_BIT;
189 break;
190 case kDifOtpCtrlPartitionRotCreatorAuth:
191 *reg_offset = OTP_CTRL_ROT_CREATOR_AUTH_READ_LOCK_REG_OFFSET;
192 *index =
193 OTP_CTRL_ROT_CREATOR_AUTH_READ_LOCK_ROT_CREATOR_AUTH_READ_LOCK_BIT;
194 break;
195 case kDifOtpCtrlPartitionRotOwnerAuthSlot0:
196 *reg_offset = OTP_CTRL_ROT_OWNER_AUTH_SLOT0_READ_LOCK_REG_OFFSET;
197 *index =
198 OTP_CTRL_ROT_OWNER_AUTH_SLOT0_READ_LOCK_ROT_OWNER_AUTH_SLOT0_READ_LOCK_BIT;
199 break;
200 case kDifOtpCtrlPartitionRotOwnerAuthSlot1:
201 *reg_offset = OTP_CTRL_ROT_OWNER_AUTH_SLOT1_READ_LOCK_REG_OFFSET;
202 *index =
203 OTP_CTRL_ROT_OWNER_AUTH_SLOT1_READ_LOCK_ROT_OWNER_AUTH_SLOT1_READ_LOCK_BIT;
204 break;
205 case kDifOtpCtrlPartitionPlatIntegAuthSlot0:
206 *reg_offset = OTP_CTRL_PLAT_INTEG_AUTH_SLOT0_READ_LOCK_REG_OFFSET;
207 *index =
208 OTP_CTRL_PLAT_INTEG_AUTH_SLOT0_READ_LOCK_PLAT_INTEG_AUTH_SLOT0_READ_LOCK_BIT;
209 break;
210 case kDifOtpCtrlPartitionPlatIntegAuthSlot1:
211 *reg_offset = OTP_CTRL_PLAT_INTEG_AUTH_SLOT1_READ_LOCK_REG_OFFSET;
212 *index =
213 OTP_CTRL_PLAT_INTEG_AUTH_SLOT1_READ_LOCK_PLAT_INTEG_AUTH_SLOT1_READ_LOCK_BIT;
214 break;
215 case kDifOtpCtrlPartitionPlatOwnerAuthSlot0:
216 *reg_offset = OTP_CTRL_PLAT_OWNER_AUTH_SLOT0_READ_LOCK_REG_OFFSET;
217 *index =
218 OTP_CTRL_PLAT_OWNER_AUTH_SLOT0_READ_LOCK_PLAT_OWNER_AUTH_SLOT0_READ_LOCK_BIT;
219 break;
220 case kDifOtpCtrlPartitionPlatOwnerAuthSlot1:
221 *reg_offset = OTP_CTRL_PLAT_OWNER_AUTH_SLOT1_READ_LOCK_REG_OFFSET;
222 *index =
223 OTP_CTRL_PLAT_OWNER_AUTH_SLOT1_READ_LOCK_PLAT_OWNER_AUTH_SLOT1_READ_LOCK_BIT;
224 break;
225 case kDifOtpCtrlPartitionPlatOwnerAuthSlot2:
226 *reg_offset = OTP_CTRL_PLAT_OWNER_AUTH_SLOT2_READ_LOCK_REG_OFFSET;
227 *index =
228 OTP_CTRL_PLAT_OWNER_AUTH_SLOT2_READ_LOCK_PLAT_OWNER_AUTH_SLOT2_READ_LOCK_BIT;
229 break;
230 case kDifOtpCtrlPartitionPlatOwnerAuthSlot3:
231 *reg_offset = OTP_CTRL_PLAT_OWNER_AUTH_SLOT3_READ_LOCK_REG_OFFSET;
232 *index =
233 OTP_CTRL_PLAT_OWNER_AUTH_SLOT3_READ_LOCK_PLAT_OWNER_AUTH_SLOT3_READ_LOCK_BIT;
234 break;
235 case kDifOtpCtrlPartitionExtNvm:
236 *reg_offset = OTP_CTRL_EXT_NVM_READ_LOCK_REG_OFFSET;
237 *index = OTP_CTRL_EXT_NVM_READ_LOCK_EXT_NVM_READ_LOCK_BIT;
238 break;
239 case kDifOtpCtrlPartitionRomPatch:
240 *reg_offset = OTP_CTRL_ROM_PATCH_READ_LOCK_REG_OFFSET;
241 *index = OTP_CTRL_ROM_PATCH_READ_LOCK_ROM_PATCH_READ_LOCK_BIT;
242 break;
243#else
244#error "dif_otp_ctrl does not support this top"
245#endif
246 default:
247 return false;
248 }
249 return true;
250}
251
252dif_result_t dif_otp_ctrl_lock_reading(const dif_otp_ctrl_t *otp,
253 dif_otp_ctrl_partition_t partition) {
254 if (otp == NULL) {
255 return kDifBadArg;
256 }
257
258 ptrdiff_t offset;
260 if (!sw_read_lock_reg_offset(partition, &offset, &index)) {
261 return kDifBadArg;
262 }
263
264 uint32_t reg = bitfield_bit32_write(0, index, false);
265 mmio_region_write32(otp->base_addr, offset, reg);
266
267 return kDifOk;
268}
269
270dif_result_t dif_otp_ctrl_reading_is_locked(const dif_otp_ctrl_t *otp,
271 dif_otp_ctrl_partition_t partition,
272 bool *is_locked) {
273 if (otp == NULL || is_locked == NULL) {
274 return kDifBadArg;
275 }
276
277 ptrdiff_t offset;
279 if (!sw_read_lock_reg_offset(partition, &offset, &index)) {
280 return kDifBadArg;
281 }
282
283 uint32_t reg = mmio_region_read32(otp->base_addr, offset);
284 *is_locked = !bitfield_bit32_read(reg, index);
285 return kDifOk;
286}
287
288dif_result_t dif_otp_ctrl_get_status(const dif_otp_ctrl_t *otp,
290 if (otp == NULL || status == NULL) {
291 return kDifBadArg;
292 }
293
294 static const bitfield_bit32_index_t kIndices[] = {
296 OTP_CTRL_STATUS_VENDOR_TEST_ERROR_BIT,
298 OTP_CTRL_STATUS_CREATOR_SW_CFG_ERROR_BIT,
300 OTP_CTRL_STATUS_OWNER_SW_CFG_ERROR_BIT,
301#if defined(OPENTITAN_IS_EARLGREY)
302 [kDifOtpCtrlStatusCodeRotCreatorAuthCodesignError] =
303 OTP_CTRL_STATUS_ROT_CREATOR_AUTH_CODESIGN_ERROR_BIT,
304 [kDifOtpCtrlStatusCodeRotCreatorAuthStateError] =
305 OTP_CTRL_STATUS_ROT_CREATOR_AUTH_STATE_ERROR_BIT,
306#elif defined(OPENTITAN_IS_DARJEELING)
307 [kDifOtpCtrlStatusCodeOwnershipSlotStateError] =
308 OTP_CTRL_STATUS_OWNERSHIP_SLOT_STATE_ERROR_BIT,
309 [kDifOtpCtrlStatusCodeRotCreatorAuthError] =
310 OTP_CTRL_STATUS_ROT_CREATOR_AUTH_ERROR_BIT,
311 [kDifOtpCtrlStatusCodeRotOwnerAuthSlot0Error] =
312 OTP_CTRL_STATUS_ROT_OWNER_AUTH_SLOT0_ERROR_BIT,
313 [kDifOtpCtrlStatusCodeRotOwnerAuthSlot1Error] =
314 OTP_CTRL_STATUS_ROT_OWNER_AUTH_SLOT1_ERROR_BIT,
315 [kDifOtpCtrlStatusCodePlatIntegAuthSlot0Error] =
316 OTP_CTRL_STATUS_PLAT_INTEG_AUTH_SLOT0_ERROR_BIT,
317 [kDifOtpCtrlStatusCodePlatIntegAuthSlot1Error] =
318 OTP_CTRL_STATUS_PLAT_INTEG_AUTH_SLOT1_ERROR_BIT,
319 [kDifOtpCtrlStatusCodePlatOwnerAuthSlot0Error] =
320 OTP_CTRL_STATUS_PLAT_OWNER_AUTH_SLOT0_ERROR_BIT,
321 [kDifOtpCtrlStatusCodePlatOwnerAuthSlot1Error] =
322 OTP_CTRL_STATUS_PLAT_OWNER_AUTH_SLOT1_ERROR_BIT,
323 [kDifOtpCtrlStatusCodePlatOwnerAuthSlot2Error] =
324 OTP_CTRL_STATUS_PLAT_OWNER_AUTH_SLOT2_ERROR_BIT,
325 [kDifOtpCtrlStatusCodePlatOwnerAuthSlot3Error] =
326 OTP_CTRL_STATUS_PLAT_OWNER_AUTH_SLOT3_ERROR_BIT,
327 [kDifOtpCtrlStatusCodeExtNvmError] = OTP_CTRL_STATUS_EXT_NVM_ERROR_BIT,
328 [kDifOtpCtrlStatusCodeRomPatchError] = OTP_CTRL_STATUS_ROM_PATCH_ERROR_BIT,
329#else
330#error "dif_otp_ctrl does not support this top"
331#endif
332 [kDifOtpCtrlStatusCodeHwCfg0Error] = OTP_CTRL_STATUS_HW_CFG0_ERROR_BIT,
333 [kDifOtpCtrlStatusCodeHwCfg1Error] = OTP_CTRL_STATUS_HW_CFG1_ERROR_BIT,
334 [kDifOtpCtrlStatusCodeSecret0Error] = OTP_CTRL_STATUS_SECRET0_ERROR_BIT,
335 [kDifOtpCtrlStatusCodeSecret1Error] = OTP_CTRL_STATUS_SECRET1_ERROR_BIT,
336 [kDifOtpCtrlStatusCodeSecret2Error] = OTP_CTRL_STATUS_SECRET2_ERROR_BIT,
337#if defined(OPENTITAN_IS_DARJEELING)
338 [kDifOtpCtrlStatusCodeSecret3Error] = OTP_CTRL_STATUS_SECRET3_ERROR_BIT,
339#elif defined(OPENTITAN_IS_EARLGREY)
340// Earlgrey only has 3 secret partitions.
341#else
342#error "dif_otp_ctrl does not support this top"
343#endif
345 OTP_CTRL_STATUS_LIFE_CYCLE_ERROR_BIT,
346 [kDifOtpCtrlStatusCodeDaiError] = OTP_CTRL_STATUS_DAI_ERROR_BIT,
347 [kDifOtpCtrlStatusCodeLciError] = OTP_CTRL_STATUS_LCI_ERROR_BIT,
348 [kDifOtpCtrlStatusCodeTimeoutError] = OTP_CTRL_STATUS_TIMEOUT_ERROR_BIT,
349 [kDifOtpCtrlStatusCodeLfsrError] = OTP_CTRL_STATUS_LFSR_FSM_ERROR_BIT,
351 OTP_CTRL_STATUS_SCRAMBLING_FSM_ERROR_BIT,
352 [kDifOtpCtrlStatusCodeKdfError] = OTP_CTRL_STATUS_KEY_DERIV_FSM_ERROR_BIT,
353 [kDifOtpCtrlStatusCodeBusIntegError] = OTP_CTRL_STATUS_BUS_INTEG_ERROR_BIT,
354 [kDifOtpCtrlStatusCodeDaiIdle] = OTP_CTRL_STATUS_DAI_IDLE_BIT,
355 [kDifOtpCtrlStatusCodeCheckPending] = OTP_CTRL_STATUS_CHECK_PENDING_BIT,
356 };
357
358 status->codes = 0;
359 uint32_t status_code =
360 mmio_region_read32(otp->base_addr, OTP_CTRL_STATUS_REG_OFFSET);
361 for (int i = 0; i < ARRAYSIZE(kIndices); ++i) {
362 // If the error is not present at all, we clear its cause bit if relevant,
363 // and bail immediately.
364 if (!bitfield_bit32_read(status_code, kIndices[i])) {
366 status->causes[i] = kDifOtpCtrlErrorOk;
367 }
368 continue;
369 }
370
371 status->codes =
372 bitfield_bit32_write(status->codes, (bitfield_bit32_index_t)i, true);
373
375 bitfield_field32_t field;
376 field = (bitfield_field32_t){
377 .mask = OTP_CTRL_ERR_CODE_0_ERR_CODE_0_MASK,
378 .index = OTP_CTRL_ERR_CODE_0_ERR_CODE_0_OFFSET,
379 };
380
381 ptrdiff_t address =
382 OTP_CTRL_ERR_CODE_0_REG_OFFSET + i * (ptrdiff_t)sizeof(uint32_t);
383 uint32_t error_code = mmio_region_read32(otp->base_addr, address);
384
386 switch (bitfield_field32_read(error_code, field)) {
387 case OTP_CTRL_ERR_CODE_0_ERR_CODE_0_VALUE_NO_ERROR:
388 err = kDifOtpCtrlErrorOk;
389 break;
390 case OTP_CTRL_ERR_CODE_0_ERR_CODE_0_VALUE_MACRO_ERROR:
392 break;
393 case OTP_CTRL_ERR_CODE_0_ERR_CODE_0_VALUE_MACRO_ECC_CORR_ERROR:
395 break;
396 case OTP_CTRL_ERR_CODE_0_ERR_CODE_0_VALUE_MACRO_ECC_UNCORR_ERROR:
398 break;
399 case OTP_CTRL_ERR_CODE_0_ERR_CODE_0_VALUE_MACRO_WRITE_BLANK_ERROR:
401 break;
402 case OTP_CTRL_ERR_CODE_0_ERR_CODE_0_VALUE_ACCESS_ERROR:
404 break;
405 case OTP_CTRL_ERR_CODE_0_ERR_CODE_0_VALUE_CHECK_FAIL_ERROR:
407 break;
408 case OTP_CTRL_ERR_CODE_0_ERR_CODE_0_VALUE_FSM_STATE_ERROR:
410 break;
411 default:
412 return kDifError;
413 }
414 status->causes[i] = err;
415 }
416 }
417
418 return kDifOk;
419}
420
421typedef struct partition_info {
422 /**
423 * The absolute OTP address at which this partition starts.
424 */
425 uint32_t start_addr;
426 /**
427 * The length of this partition, in bytes, including the digest.
428 *
429 * If the partition has a digest, it is expected to be at address
430 * `start_addr + len - sizeof(uint64_t)`.
431 */
432 uint32_t len;
433 /**
434 * The alignment mask for this partition.
435 *
436 * A valid address for this partition must be such that
437 * `addr & align_mask == 0`.
438 */
439 uint32_t align_mask;
440
441 /**
442 * Whether this is a software-managed partition with a software-managed
443 * digest.
444 */
446
447 /**
448 * Whether this partition has a digest field.
449 */
451
452 /**
453 * Whether this partition is the lifecycle partition.
454 */
456} partition_info_t;
457
458// This is generates too many lines with different formatting variants, so
459// We opt to just disable formatting.
460// clang-format off
461static const partition_info_t kPartitions[] = {
463 .start_addr = OTP_CTRL_PARAM_VENDOR_TEST_OFFSET,
464 .len = OTP_CTRL_PARAM_VENDOR_TEST_SIZE,
465 .align_mask = 0x3,
466 .is_software = true,
467 .has_digest = true,
468 .is_lifecycle = false},
470 .start_addr = OTP_CTRL_PARAM_CREATOR_SW_CFG_OFFSET,
471 .len = OTP_CTRL_PARAM_CREATOR_SW_CFG_SIZE,
472 .align_mask = 0x3,
473 .is_software = true,
474 .has_digest = true,
475 .is_lifecycle = false},
477 .start_addr = OTP_CTRL_PARAM_OWNER_SW_CFG_OFFSET,
478 .len = OTP_CTRL_PARAM_OWNER_SW_CFG_SIZE,
479 .align_mask = 0x3,
480 .is_software = true,
481 .has_digest = true,
482 .is_lifecycle = false},
483#if defined(OPENTITAN_IS_EARLGREY)
484 [kDifOtpCtrlPartitionRotCreatorAuthCodesign] = {
485 .start_addr = OTP_CTRL_PARAM_ROT_CREATOR_AUTH_CODESIGN_OFFSET,
486 .len = OTP_CTRL_PARAM_ROT_CREATOR_AUTH_CODESIGN_SIZE,
487 .align_mask = 0x3,
488 .is_software = true,
489 .has_digest = true,
490 .is_lifecycle = false},
491 [kDifOtpCtrlPartitionRotCreatorAuthState] = {
492 .start_addr = OTP_CTRL_PARAM_ROT_CREATOR_AUTH_STATE_OFFSET,
493 .len = OTP_CTRL_PARAM_ROT_CREATOR_AUTH_STATE_SIZE,
494 .align_mask = 0x3,
495 .is_software = true,
496 .has_digest = true,
497 .is_lifecycle = false},
498#elif defined(OPENTITAN_IS_DARJEELING)
499 [kDifOtpCtrlPartitionOwnershipSlotState] = {
500 .start_addr = OTP_CTRL_PARAM_OWNERSHIP_SLOT_STATE_OFFSET,
501 .len = OTP_CTRL_PARAM_OWNERSHIP_SLOT_STATE_SIZE,
502 .align_mask = 0x3,
503 .is_software = true,
504 .has_digest = false,
505 .is_lifecycle = false},
506 [kDifOtpCtrlPartitionRotCreatorAuth] = {
507 .start_addr = OTP_CTRL_PARAM_ROT_CREATOR_AUTH_OFFSET,
508 .len = OTP_CTRL_PARAM_ROT_CREATOR_AUTH_SIZE,
509 .align_mask = 0x3,
510 .is_software = true,
511 .has_digest = true,
512 .is_lifecycle = false},
513 [kDifOtpCtrlPartitionRotOwnerAuthSlot0] = {
514 .start_addr = OTP_CTRL_PARAM_ROT_OWNER_AUTH_SLOT0_OFFSET,
515 .len = OTP_CTRL_PARAM_ROT_OWNER_AUTH_SLOT0_SIZE,
516 .align_mask = 0x3,
517 .is_software = true,
518 .has_digest = true,
519 .is_lifecycle = false},
520 [kDifOtpCtrlPartitionRotOwnerAuthSlot1] = {
521 .start_addr = OTP_CTRL_PARAM_ROT_OWNER_AUTH_SLOT1_OFFSET,
522 .len = OTP_CTRL_PARAM_ROT_OWNER_AUTH_SLOT1_SIZE,
523 .align_mask = 0x3,
524 .is_software = true,
525 .has_digest = true,
526 .is_lifecycle = false},
527 [kDifOtpCtrlPartitionPlatIntegAuthSlot0] = {
528 .start_addr = OTP_CTRL_PARAM_PLAT_INTEG_AUTH_SLOT0_OFFSET,
529 .len = OTP_CTRL_PARAM_PLAT_INTEG_AUTH_SLOT0_SIZE,
530 .align_mask = 0x3,
531 .is_software = true,
532 .has_digest = true,
533 .is_lifecycle = false},
534 [kDifOtpCtrlPartitionPlatIntegAuthSlot1] = {
535 .start_addr = OTP_CTRL_PARAM_PLAT_INTEG_AUTH_SLOT1_OFFSET,
536 .len = OTP_CTRL_PARAM_PLAT_INTEG_AUTH_SLOT1_SIZE,
537 .align_mask = 0x3,
538 .is_software = true,
539 .has_digest = true,
540 .is_lifecycle = false},
541 [kDifOtpCtrlPartitionPlatOwnerAuthSlot0] = {
542 .start_addr = OTP_CTRL_PARAM_PLAT_OWNER_AUTH_SLOT0_OFFSET,
543 .len = OTP_CTRL_PARAM_PLAT_OWNER_AUTH_SLOT0_SIZE,
544 .align_mask = 0x3,
545 .is_software = true,
546 .has_digest = true,
547 .is_lifecycle = false},
548 [kDifOtpCtrlPartitionPlatOwnerAuthSlot1] = {
549 .start_addr = OTP_CTRL_PARAM_PLAT_OWNER_AUTH_SLOT1_OFFSET,
550 .len = OTP_CTRL_PARAM_PLAT_OWNER_AUTH_SLOT1_SIZE,
551 .align_mask = 0x3,
552 .is_software = true,
553 .has_digest = true,
554 .is_lifecycle = false},
555 [kDifOtpCtrlPartitionPlatOwnerAuthSlot2] = {
556 .start_addr = OTP_CTRL_PARAM_PLAT_OWNER_AUTH_SLOT2_OFFSET,
557 .len = OTP_CTRL_PARAM_PLAT_OWNER_AUTH_SLOT2_SIZE,
558 .align_mask = 0x3,
559 .is_software = true,
560 .has_digest = true,
561 .is_lifecycle = false},
562 [kDifOtpCtrlPartitionPlatOwnerAuthSlot3] = {
563 .start_addr = OTP_CTRL_PARAM_PLAT_OWNER_AUTH_SLOT3_OFFSET,
564 .len = OTP_CTRL_PARAM_PLAT_OWNER_AUTH_SLOT3_SIZE,
565 .align_mask = 0x3,
566 .is_software = true,
567 .has_digest = true,
568 .is_lifecycle = false},
569 [kDifOtpCtrlPartitionExtNvm] = {
570 .start_addr = OTP_CTRL_PARAM_EXT_NVM_OFFSET,
571 .len = OTP_CTRL_PARAM_EXT_NVM_SIZE,
572 .align_mask = 0x3,
573 .is_software = true,
574 .has_digest = false,
575 .is_lifecycle = false},
576 [kDifOtpCtrlPartitionRomPatch] = {
577 .start_addr = OTP_CTRL_PARAM_ROM_PATCH_OFFSET,
578 .len = OTP_CTRL_PARAM_ROM_PATCH_SIZE,
579 .align_mask = 0x3,
580 .is_software = true,
581 .has_digest = true,
582 .is_lifecycle = false},
583#else
584#error "dif_otp_ctrl does not support this top"
585#endif
587 .start_addr = OTP_CTRL_PARAM_HW_CFG0_OFFSET,
588 .len = OTP_CTRL_PARAM_HW_CFG0_SIZE,
589 .align_mask = 0x3,
590 .is_software = false,
591 .has_digest = true,
592 .is_lifecycle = false},
594 .start_addr = OTP_CTRL_PARAM_HW_CFG1_OFFSET,
595 .len = OTP_CTRL_PARAM_HW_CFG1_SIZE,
596 .align_mask = 0x3,
597 .is_software = false,
598 .has_digest = true,
599 .is_lifecycle = false},
601 .start_addr = OTP_CTRL_PARAM_SECRET0_OFFSET,
602 .len = OTP_CTRL_PARAM_SECRET0_SIZE,
603 .align_mask = 0x7,
604 .is_software = false,
605 .has_digest = true,
606 .is_lifecycle = false},
608 .start_addr = OTP_CTRL_PARAM_SECRET1_OFFSET,
609 .len = OTP_CTRL_PARAM_SECRET1_SIZE,
610 .align_mask = 0x7,
611 .is_software = false,
612 .has_digest = true,
613 .is_lifecycle = false},
615 .start_addr = OTP_CTRL_PARAM_SECRET2_OFFSET,
616 .len = OTP_CTRL_PARAM_SECRET2_SIZE,
617 .align_mask = 0x7,
618 .is_software = false,
619 .has_digest = true,
620 .is_lifecycle = false},
621#if defined(OPENTITAN_IS_DARJEELING)
622 [kDifOtpCtrlPartitionSecret3] = {
623 .start_addr = OTP_CTRL_PARAM_SECRET3_OFFSET,
624 .len = OTP_CTRL_PARAM_SECRET3_SIZE,
625 .align_mask = 0x7,
626 .is_software = false,
627 .has_digest = true,
628 .is_lifecycle = false},
629#elif defined(OPENTITAN_IS_EARLGREY)
630// Earlgrey only has 3 secret partitions.
631#else
632#error "dif_otp_ctrl does not support this top"
633#endif
635 .start_addr = OTP_CTRL_PARAM_LIFE_CYCLE_OFFSET,
636 .len = OTP_CTRL_PARAM_LIFE_CYCLE_SIZE,
637 .align_mask = 0x3,
638 .is_software = false,
639 .has_digest = false,
640 .is_lifecycle = true},
641};
642// clang-format on
643
644dif_result_t dif_otp_ctrl_relative_address(dif_otp_ctrl_partition_t partition,
645 uint32_t abs_address,
646 uint32_t *relative_address) {
647 *relative_address = 0;
648
649 if (partition >= ARRAYSIZE(kPartitions)) {
650 return kDifBadArg;
651 }
652
653 if ((abs_address & kPartitions[partition].align_mask) != 0) {
654 return kDifUnaligned;
655 }
656
657 if (abs_address < kPartitions[partition].start_addr) {
658 return kDifOutOfRange;
659 }
660
661 *relative_address = abs_address - kPartitions[partition].start_addr;
662 if (*relative_address >= kPartitions[partition].len) {
663 *relative_address = 0;
664 return kDifOutOfRange;
665 }
666
667 return kDifOk;
668}
669
670dif_result_t dif_otp_ctrl_dai_read_start(const dif_otp_ctrl_t *otp,
671 dif_otp_ctrl_partition_t partition,
672 uint32_t address) {
673 if (otp == NULL || partition >= ARRAYSIZE(kPartitions)) {
674 return kDifBadArg;
675 }
676
677 if ((address & kPartitions[partition].align_mask) != 0) {
678 return kDifUnaligned;
679 }
680
681 if (address >= kPartitions[partition].len) {
682 return kDifOutOfRange;
683 }
684
685 address += kPartitions[partition].start_addr;
686 mmio_region_write32(otp->base_addr, OTP_CTRL_DIRECT_ACCESS_ADDRESS_REG_OFFSET,
687 address);
688
689 uint32_t cmd =
690 bitfield_bit32_write(0, OTP_CTRL_DIRECT_ACCESS_CMD_RD_BIT, true);
691 mmio_region_write32(otp->base_addr, OTP_CTRL_DIRECT_ACCESS_CMD_REG_OFFSET,
692 cmd);
693
694 return kDifOk;
695}
696
697dif_result_t dif_otp_ctrl_dai_read32_end(const dif_otp_ctrl_t *otp,
698 uint32_t *value) {
699 if (otp == NULL || value == NULL) {
700 return kDifBadArg;
701 }
702
703 *value = mmio_region_read32(otp->base_addr,
704 OTP_CTRL_DIRECT_ACCESS_RDATA_0_REG_OFFSET);
705 return kDifOk;
706}
707
708dif_result_t dif_otp_ctrl_dai_read64_end(const dif_otp_ctrl_t *otp,
709 uint64_t *value) {
710 if (otp == NULL || value == NULL) {
711 return kDifBadArg;
712 }
713
714 *value = mmio_region_read32(otp->base_addr,
715 OTP_CTRL_DIRECT_ACCESS_RDATA_1_REG_OFFSET);
716 *value <<= 32;
717 *value |= mmio_region_read32(otp->base_addr,
718 OTP_CTRL_DIRECT_ACCESS_RDATA_0_REG_OFFSET);
719 return kDifOk;
720}
721
722dif_result_t dif_otp_ctrl_dai_program32(const dif_otp_ctrl_t *otp,
723 dif_otp_ctrl_partition_t partition,
724 uint32_t address, uint32_t value) {
725 if (otp == NULL || partition >= ARRAYSIZE(kPartitions)) {
726 return kDifBadArg;
727 }
728
729 // Ensure that we are writing to a 32-bit-access partition by checking that
730 // the alignment mask is 0b11.
731 //
732 // Note furthermore that the LC partition is *not* writeable, so we eject
733 // here.
734 if (kPartitions[partition].align_mask != 0x3 ||
735 kPartitions[partition].is_lifecycle) {
736 return kDifError;
737 }
738
739 if ((address & kPartitions[partition].align_mask) != 0) {
740 return kDifUnaligned;
741 }
742
743 // NOTE: The bounds check is tightened here, since we disallow writing the
744 // digest directly. If the partition does not have a digest, no tightening is
745 // needed.
746 size_t digest_size = kPartitions[partition].has_digest * sizeof(uint64_t);
747 if (address >= kPartitions[partition].len - digest_size) {
748 return kDifOutOfRange;
749 }
750
751 address += kPartitions[partition].start_addr;
752 mmio_region_write32(otp->base_addr, OTP_CTRL_DIRECT_ACCESS_ADDRESS_REG_OFFSET,
753 address);
754
755 mmio_region_write32(otp->base_addr, OTP_CTRL_DIRECT_ACCESS_WDATA_0_REG_OFFSET,
756 value);
757
758 uint32_t cmd =
759 bitfield_bit32_write(0, OTP_CTRL_DIRECT_ACCESS_CMD_WR_BIT, true);
760 mmio_region_write32(otp->base_addr, OTP_CTRL_DIRECT_ACCESS_CMD_REG_OFFSET,
761 cmd);
762
763 return kDifOk;
764}
765
766dif_result_t dif_otp_ctrl_dai_program64(const dif_otp_ctrl_t *otp,
767 dif_otp_ctrl_partition_t partition,
768 uint32_t address, uint64_t value) {
769 if (otp == NULL || partition >= ARRAYSIZE(kPartitions)) {
770 return kDifBadArg;
771 }
772
773 // Ensure that we are writing to a 64-bit-access partition by checking that
774 // the alignment mask is 0b111.
775 if (kPartitions[partition].align_mask != 0x7) {
776 return kDifError;
777 }
778
779 if ((address & kPartitions[partition].align_mask) != 0) {
780 return kDifUnaligned;
781 }
782
783 // NOTE: The bounds check is tightened here, since we disallow writing the
784 // digest directly.
785 size_t digest_size = sizeof(uint64_t);
786 if (address >= kPartitions[partition].len - digest_size) {
787 return kDifOutOfRange;
788 }
789
790 address += kPartitions[partition].start_addr;
791 mmio_region_write32(otp->base_addr, OTP_CTRL_DIRECT_ACCESS_ADDRESS_REG_OFFSET,
792 address);
793
794 mmio_region_write32(otp->base_addr, OTP_CTRL_DIRECT_ACCESS_WDATA_0_REG_OFFSET,
795 value & UINT32_MAX);
796 mmio_region_write32(otp->base_addr, OTP_CTRL_DIRECT_ACCESS_WDATA_1_REG_OFFSET,
797 value >> 32);
798
799 uint32_t cmd =
800 bitfield_bit32_write(0, OTP_CTRL_DIRECT_ACCESS_CMD_WR_BIT, true);
801 mmio_region_write32(otp->base_addr, OTP_CTRL_DIRECT_ACCESS_CMD_REG_OFFSET,
802 cmd);
803
804 return kDifOk;
805}
806
807dif_result_t dif_otp_ctrl_dai_digest(const dif_otp_ctrl_t *otp,
808 dif_otp_ctrl_partition_t partition,
809 uint64_t digest) {
810 if (otp == NULL || partition >= ARRAYSIZE(kPartitions)) {
811 return kDifBadArg;
812 }
813
814 // Not all partitions have a digest.
815 if (!kPartitions[partition].has_digest) {
816 return kDifError;
817 }
818
819 // For software partitions, the digest must be nonzero; for all other
820 // partitions it must be zero.
821 bool is_sw = kPartitions[partition].is_software;
822 if (is_sw == (digest == 0)) {
823 return kDifBadArg;
824 }
825
826 uint32_t address = kPartitions[partition].start_addr;
827 if (is_sw) {
828 address += kPartitions[partition].len - sizeof(digest);
829 }
830 mmio_region_write32(otp->base_addr, OTP_CTRL_DIRECT_ACCESS_ADDRESS_REG_OFFSET,
831 address);
832
833 if (digest != 0) {
834 mmio_region_write32(otp->base_addr,
835 OTP_CTRL_DIRECT_ACCESS_WDATA_0_REG_OFFSET,
836 digest & 0xffffffff);
837 mmio_region_write32(otp->base_addr,
838 OTP_CTRL_DIRECT_ACCESS_WDATA_1_REG_OFFSET,
839 digest >> 32);
840 }
841
842 bitfield_bit32_index_t cmd_bit = is_sw
843 ? OTP_CTRL_DIRECT_ACCESS_CMD_WR_BIT
844 : OTP_CTRL_DIRECT_ACCESS_CMD_DIGEST_BIT;
845 uint32_t cmd = bitfield_bit32_write(0, cmd_bit, true);
846 mmio_region_write32(otp->base_addr, OTP_CTRL_DIRECT_ACCESS_CMD_REG_OFFSET,
847 cmd);
848
849 return kDifOk;
850}
851
852static bool get_digest_regs(dif_otp_ctrl_partition_t partition, ptrdiff_t *reg0,
853 ptrdiff_t *reg1) {
854 switch (partition) {
856 *reg0 = OTP_CTRL_VENDOR_TEST_DIGEST_0_REG_OFFSET;
857 *reg1 = OTP_CTRL_VENDOR_TEST_DIGEST_1_REG_OFFSET;
858 break;
860 *reg0 = OTP_CTRL_CREATOR_SW_CFG_DIGEST_0_REG_OFFSET;
861 *reg1 = OTP_CTRL_CREATOR_SW_CFG_DIGEST_1_REG_OFFSET;
862 break;
864 *reg0 = OTP_CTRL_OWNER_SW_CFG_DIGEST_0_REG_OFFSET;
865 *reg1 = OTP_CTRL_OWNER_SW_CFG_DIGEST_1_REG_OFFSET;
866 break;
867#if defined(OPENTITAN_IS_EARLGREY)
868 case kDifOtpCtrlPartitionRotCreatorAuthCodesign:
869 *reg0 = OTP_CTRL_ROT_CREATOR_AUTH_CODESIGN_DIGEST_0_REG_OFFSET;
870 *reg1 = OTP_CTRL_ROT_CREATOR_AUTH_CODESIGN_DIGEST_1_REG_OFFSET;
871 break;
872 case kDifOtpCtrlPartitionRotCreatorAuthState:
873 *reg0 = OTP_CTRL_ROT_CREATOR_AUTH_STATE_DIGEST_0_REG_OFFSET;
874 *reg1 = OTP_CTRL_ROT_CREATOR_AUTH_STATE_DIGEST_1_REG_OFFSET;
875 break;
876#elif defined(OPENTITAN_IS_DARJEELING)
877 case kDifOtpCtrlPartitionRotCreatorAuth:
878 *reg0 = OTP_CTRL_ROT_CREATOR_AUTH_DIGEST_0_REG_OFFSET;
879 *reg1 = OTP_CTRL_ROT_CREATOR_AUTH_DIGEST_1_REG_OFFSET;
880 break;
881 case kDifOtpCtrlPartitionRotOwnerAuthSlot0:
882 *reg0 = OTP_CTRL_ROT_OWNER_AUTH_SLOT0_DIGEST_0_REG_OFFSET;
883 *reg1 = OTP_CTRL_ROT_OWNER_AUTH_SLOT0_DIGEST_1_REG_OFFSET;
884 break;
885 case kDifOtpCtrlPartitionRotOwnerAuthSlot1:
886 *reg0 = OTP_CTRL_ROT_OWNER_AUTH_SLOT1_DIGEST_0_REG_OFFSET;
887 *reg1 = OTP_CTRL_ROT_OWNER_AUTH_SLOT1_DIGEST_1_REG_OFFSET;
888 break;
889 case kDifOtpCtrlPartitionPlatIntegAuthSlot0:
890 *reg0 = OTP_CTRL_PLAT_INTEG_AUTH_SLOT0_DIGEST_0_REG_OFFSET;
891 *reg1 = OTP_CTRL_PLAT_INTEG_AUTH_SLOT0_DIGEST_1_REG_OFFSET;
892 break;
893 case kDifOtpCtrlPartitionPlatIntegAuthSlot1:
894 *reg0 = OTP_CTRL_PLAT_INTEG_AUTH_SLOT1_DIGEST_0_REG_OFFSET;
895 *reg1 = OTP_CTRL_PLAT_INTEG_AUTH_SLOT1_DIGEST_1_REG_OFFSET;
896 break;
897 case kDifOtpCtrlPartitionPlatOwnerAuthSlot0:
898 *reg0 = OTP_CTRL_PLAT_OWNER_AUTH_SLOT0_DIGEST_0_REG_OFFSET;
899 *reg1 = OTP_CTRL_PLAT_OWNER_AUTH_SLOT0_DIGEST_1_REG_OFFSET;
900 break;
901 case kDifOtpCtrlPartitionPlatOwnerAuthSlot1:
902 *reg0 = OTP_CTRL_PLAT_OWNER_AUTH_SLOT1_DIGEST_0_REG_OFFSET;
903 *reg1 = OTP_CTRL_PLAT_OWNER_AUTH_SLOT1_DIGEST_1_REG_OFFSET;
904 break;
905 case kDifOtpCtrlPartitionPlatOwnerAuthSlot2:
906 *reg0 = OTP_CTRL_PLAT_OWNER_AUTH_SLOT2_DIGEST_0_REG_OFFSET;
907 *reg1 = OTP_CTRL_PLAT_OWNER_AUTH_SLOT2_DIGEST_1_REG_OFFSET;
908 break;
909 case kDifOtpCtrlPartitionPlatOwnerAuthSlot3:
910 *reg0 = OTP_CTRL_PLAT_OWNER_AUTH_SLOT3_DIGEST_0_REG_OFFSET;
911 *reg1 = OTP_CTRL_PLAT_OWNER_AUTH_SLOT3_DIGEST_1_REG_OFFSET;
912 break;
913 case kDifOtpCtrlPartitionRomPatch:
914 *reg0 = OTP_CTRL_ROM_PATCH_DIGEST_0_REG_OFFSET;
915 *reg1 = OTP_CTRL_ROM_PATCH_DIGEST_1_REG_OFFSET;
916 break;
917#else
918#error "dif_otp_ctrl does not support this top"
919#endif
921 *reg0 = OTP_CTRL_HW_CFG0_DIGEST_0_REG_OFFSET;
922 *reg1 = OTP_CTRL_HW_CFG0_DIGEST_1_REG_OFFSET;
923 break;
925 *reg0 = OTP_CTRL_HW_CFG1_DIGEST_0_REG_OFFSET;
926 *reg1 = OTP_CTRL_HW_CFG1_DIGEST_1_REG_OFFSET;
927 break;
929 *reg0 = OTP_CTRL_SECRET0_DIGEST_0_REG_OFFSET;
930 *reg1 = OTP_CTRL_SECRET0_DIGEST_1_REG_OFFSET;
931 break;
933 *reg0 = OTP_CTRL_SECRET1_DIGEST_0_REG_OFFSET;
934 *reg1 = OTP_CTRL_SECRET1_DIGEST_1_REG_OFFSET;
935 break;
937 *reg0 = OTP_CTRL_SECRET2_DIGEST_0_REG_OFFSET;
938 *reg1 = OTP_CTRL_SECRET2_DIGEST_1_REG_OFFSET;
939 break;
940#if defined(OPENTITAN_IS_DARJEELING)
941 case kDifOtpCtrlPartitionSecret3:
942 *reg0 = OTP_CTRL_SECRET3_DIGEST_0_REG_OFFSET;
943 *reg1 = OTP_CTRL_SECRET3_DIGEST_1_REG_OFFSET;
944 break;
945#elif defined(OPENTITAN_IS_EARLGREY)
946// Earlgrey only has 3 secret partitions.
947#else
948#error "dif_otp_ctrl does not support this top"
949#endif
950 default:
951 return false;
952 }
953
954 return true;
955}
956
957dif_result_t dif_otp_ctrl_is_digest_computed(const dif_otp_ctrl_t *otp,
958 dif_otp_ctrl_partition_t partition,
959 bool *is_computed) {
960 if (otp == NULL || is_computed == NULL) {
961 return kDifBadArg;
962 }
963
964 ptrdiff_t reg0, reg1;
965 if (!get_digest_regs(partition, &reg0, &reg1)) {
966 return kDifBadArg;
967 }
968
969 uint64_t value = mmio_region_read32(otp->base_addr, reg1);
970 value <<= 32;
971 value |= mmio_region_read32(otp->base_addr, reg0);
972
973 *is_computed = value != 0;
974
975 return kDifOk;
976}
977
978dif_result_t dif_otp_ctrl_get_digest(const dif_otp_ctrl_t *otp,
979 dif_otp_ctrl_partition_t partition,
980 uint64_t *digest) {
981 if (otp == NULL || digest == NULL) {
982 return kDifBadArg;
983 }
984
985 ptrdiff_t reg0, reg1;
986 if (!get_digest_regs(partition, &reg0, &reg1)) {
987 return kDifBadArg;
988 }
989
990 uint64_t value = mmio_region_read32(otp->base_addr, reg1);
991 value <<= 32;
992 value |= mmio_region_read32(otp->base_addr, reg0);
993
994 if (value == 0) {
995 return kDifError;
996 }
997 *digest = value;
998
999 return kDifOk;
1000}
1001
1002dif_result_t dif_otp_ctrl_read_blocking(const dif_otp_ctrl_t *otp,
1003 dif_otp_ctrl_partition_t partition,
1004 uint32_t address, uint32_t *buf,
1005 size_t len) {
1006 if (otp == NULL || partition >= ARRAYSIZE(kPartitions) || buf == NULL) {
1007 return kDifBadArg;
1008 }
1009
1010 if (!kPartitions[partition].is_software) {
1011 return kDifError;
1012 }
1013
1014 if ((address & kPartitions[partition].align_mask) != 0) {
1015 return kDifUnaligned;
1016 }
1017
1018 if (address + len >= kPartitions[partition].len) {
1019 return kDifOutOfRange;
1020 }
1021
1022 uint32_t reg_offset = OTP_CTRL_SW_CFG_WINDOW_REG_OFFSET +
1023 kPartitions[partition].start_addr + address;
1024 mmio_region_memcpy_from_mmio32(otp->base_addr, reg_offset, buf,
1025 len * sizeof(uint32_t));
1026 return kDifOk;
1027}