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  */
23 static 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 
34 dif_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,
47  config.integrity_period_mask);
48  mmio_region_write32(otp->base_addr,
49  OTP_CTRL_CONSISTENCY_CHECK_PERIOD_REG_OFFSET,
51 
52  return kDifOk;
53 }
54 
55 dif_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 
70 dif_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 
85 dif_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 
98 dif_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 
112 dif_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 
124 dif_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 
134 dif_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 
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 
157 static 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 
252 dif_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 
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 
288 dif_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 =
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 
421 typedef 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  */
457 
458 // This is generates too many lines with different formatting variants, so
459 // We opt to just disable formatting.
460 // clang-format off
461 static 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 
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 
670 dif_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 
697 dif_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 
708 dif_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 
722 dif_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 
766 dif_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 
807 dif_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 
852 static 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 
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 
978 dif_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 
1002 dif_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 }