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