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_EARLGREY)
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_DARJEELING)
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 kDifOtpCtrlPartitionRotCreatorAuth:
236 *reg_offset = OTP_CTRL_ROT_CREATOR_AUTH_READ_LOCK_REG_OFFSET;
237 *index =
238 OTP_CTRL_ROT_CREATOR_AUTH_READ_LOCK_ROT_CREATOR_AUTH_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#else
289#error "dif_otp_ctrl does not support this top"
290#endif
291 default:
292 return false;
293 }
294 return true;
295}
296
297dif_result_t dif_otp_ctrl_lock_reading(const dif_otp_ctrl_t *otp,
298 dif_otp_ctrl_partition_t partition) {
299 if (otp == NULL) {
300 return kDifBadArg;
301 }
302
303 ptrdiff_t offset;
305 if (!sw_read_lock_reg_offset(partition, &offset, &index)) {
306 return kDifBadArg;
307 }
308
309 uint32_t reg = bitfield_bit32_write(0, index, false);
310 mmio_region_write32(otp->base_addr, offset, reg);
311
312 return kDifOk;
313}
314
315dif_result_t dif_otp_ctrl_reading_is_locked(const dif_otp_ctrl_t *otp,
316 dif_otp_ctrl_partition_t partition,
317 bool *is_locked) {
318 if (otp == NULL || is_locked == NULL) {
319 return kDifBadArg;
320 }
321
322 ptrdiff_t offset;
324 if (!sw_read_lock_reg_offset(partition, &offset, &index)) {
325 return kDifBadArg;
326 }
327
328 uint32_t reg = mmio_region_read32(otp->base_addr, offset);
329 *is_locked = !bitfield_bit32_read(reg, index);
330 return kDifOk;
331}
332
333dif_result_t dif_otp_ctrl_get_status(const dif_otp_ctrl_t *otp,
335 if (otp == NULL || status == NULL) {
336 return kDifBadArg;
337 }
338
339 status->codes = 0;
340
341 // Read main STATUS register
342 uint32_t status_code_reg =
343 mmio_region_read32(otp->base_addr, OTP_CTRL_STATUS_REG_OFFSET);
344
345 // Only read PARTITION_STATUS_0 register if PARTITION_ERROR bit is set
346 if (bitfield_bit32_read(status_code_reg,
347 OTP_CTRL_STATUS_PARTITION_ERROR_BIT)) {
348 uint32_t num_part_status_regs = (kDifOtpCtrlNumberOfPartitions + 31) / 32;
349 for (int status_reg_num = 0; status_reg_num < num_part_status_regs;
350 ++status_reg_num) {
351 uint32_t partition_status_reg = mmio_region_read32(
352 otp->base_addr,
353 (ptrdiff_t)(OTP_CTRL_PARTITION_STATUS_0_REG_OFFSET +
354 (ptrdiff_t)sizeof(uint32_t) * status_reg_num));
355 // Process partition status bits
356 for (int status_idx = 0; status_idx < 32; ++status_idx) {
357 uint32_t partition_number =
358 (uint32_t)status_reg_num * 32 + (uint32_t)status_idx;
359 if (partition_number > kDifOtpCtrlNumberOfPartitions) {
360 break;
361 }
362 // If the error is not present at all, we clear its cause and bail
363 // immediately.
364 if (!bitfield_bit32_read(partition_status_reg,
365 (bitfield_bit32_index_t)status_idx)) {
366 status->causes[partition_number] = kDifOtpCtrlErrorOk;
367 continue;
368 }
369
370 // Set bit for partition error
371 status->codes = bitfield_bit32_write(
372 status->codes,
373 (bitfield_bit32_index_t)OTP_CTRL_STATUS_PARTITION_ERROR_BIT, true);
374
375 // Read and decode err_code register
377 if (get_error_code(otp, partition_number, &err) != kDifOk) {
378 return kDifError;
379 }
380 status->causes[partition_number] = err;
381 }
382 }
383 } else {
384 // No partition errors, clear all partition error causes
385 for (int i = 0; i < kDifOtpCtrlNumberOfPartitions; ++i) {
386 status->causes[i] = kDifOtpCtrlErrorOk;
387 }
388 }
389
390 // Process DAI/LCI status bits from main STATUS register
393 uint32_t err_code_index = kDifOtpCtrlNumberOfPartitions - 1 + (uint32_t)i;
395
396 if (bitfield_bit32_read(status_code_reg, (bitfield_bit32_index_t)i)) {
397 // Set error status code
398 status->codes =
399 bitfield_bit32_write(status->codes, (bitfield_bit32_index_t)i, true);
400
401 // Get error cause
402 if (get_error_code(otp, err_code_index, &err) != kDifOk) {
403 return kDifError;
404 }
405 }
406
407 status->causes[err_code_index] = err;
408 }
409
410 // Process other status bits from main STATUS register
412 ++i) {
413 if (!bitfield_bit32_read(status_code_reg, (bitfield_bit32_index_t)i)) {
414 continue;
415 }
416 // Set error status code
417 status->codes =
418 bitfield_bit32_write(status->codes, (bitfield_bit32_index_t)i, true);
419 }
420
421 return kDifOk;
422}
423
424typedef struct partition_info {
425 /**
426 * The absolute OTP address at which this partition starts.
427 */
428 uint32_t start_addr;
429 /**
430 * The length of this partition, in bytes, including the digest and the
431 * zeroization marker.
432 *
433 * If the partition has a digest, it is expected to be at address
434 * `start_addr + len - sizeof(uint64_t)`, or at
435 * `start_addr + len - 2 * sizeof(uint64_t)` if the partition is zeroizable.
436 */
437 uint32_t len;
438 /**
439 * The alignment mask for this partition.
440 *
441 * A valid address for this partition must be such that
442 * `addr & align_mask == 0`.
443 */
444 uint32_t align_mask;
445
446 /**
447 * Whether this is a software-managed partition with a software-managed
448 * digest.
449 */
451
452 /**
453 * Whether this partition has a digest field.
454 */
456
457 /**
458 * Whether this partition is the lifecycle partition.
459 */
461
462 /**
463 * Whether this partition is zeroizable.
464 */
466} partition_info_t;
467
468/**
469 * Returns the effective size of a partition in bytes, i.e., the size of the
470 * fields that can be written directly via the DAI. This excludes the digest
471 * field and zeroization marker at the end of the partition.
472 */
473static uint32_t partition_effective_size(const partition_info_t *info) {
474 return info->len -
475 (info->has_digest + info->is_zeroizable) * (uint32_t)sizeof(uint64_t);
476}
477
478// This is generates too many lines with different formatting variants, so
479// We opt to just disable formatting.
480// clang-format off
481static const partition_info_t kPartitions[] = {
483 .start_addr = OTP_CTRL_PARAM_VENDOR_TEST_OFFSET,
484 .len = OTP_CTRL_PARAM_VENDOR_TEST_SIZE,
485 .align_mask = 0x3,
486 .is_software = true,
487 .has_digest = true,
488 .is_lifecycle = false,
489 .is_zeroizable = false},
491 .start_addr = OTP_CTRL_PARAM_CREATOR_SW_CFG_OFFSET,
492 .len = OTP_CTRL_PARAM_CREATOR_SW_CFG_SIZE,
493 .align_mask = 0x3,
494 .is_software = true,
495 .has_digest = true,
496 .is_lifecycle = false,
497 .is_zeroizable = false},
499 .start_addr = OTP_CTRL_PARAM_OWNER_SW_CFG_OFFSET,
500 .len = OTP_CTRL_PARAM_OWNER_SW_CFG_SIZE,
501 .align_mask = 0x3,
502 .is_software = true,
503 .has_digest = true,
504 .is_lifecycle = false,
505 .is_zeroizable = false},
506#if defined(OPENTITAN_IS_EARLGREY)
507 [kDifOtpCtrlPartitionRotCreatorAuthCodesign] = {
508 .start_addr = OTP_CTRL_PARAM_ROT_CREATOR_AUTH_CODESIGN_OFFSET,
509 .len = OTP_CTRL_PARAM_ROT_CREATOR_AUTH_CODESIGN_SIZE,
510 .align_mask = 0x3,
511 .is_software = true,
512 .has_digest = true,
513 .is_lifecycle = false,
514 .is_zeroizable = false},
515 [kDifOtpCtrlPartitionRotCreatorAuthState] = {
516 .start_addr = OTP_CTRL_PARAM_ROT_CREATOR_AUTH_STATE_OFFSET,
517 .len = OTP_CTRL_PARAM_ROT_CREATOR_AUTH_STATE_SIZE,
518 .align_mask = 0x3,
519 .is_software = true,
520 .has_digest = true,
521 .is_lifecycle = false,
522 .is_zeroizable = false},
523#elif defined(OPENTITAN_IS_DARJEELING)
524 [kDifOtpCtrlPartitionOwnershipSlotState] = {
525 .start_addr = OTP_CTRL_PARAM_OWNERSHIP_SLOT_STATE_OFFSET,
526 .len = OTP_CTRL_PARAM_OWNERSHIP_SLOT_STATE_SIZE,
527 .align_mask = 0x3,
528 .is_software = true,
529 .has_digest = false,
530 .is_lifecycle = false,
531 .is_zeroizable = false},
532 [kDifOtpCtrlPartitionRotCreatorAuth] = {
533 .start_addr = OTP_CTRL_PARAM_ROT_CREATOR_AUTH_OFFSET,
534 .len = OTP_CTRL_PARAM_ROT_CREATOR_AUTH_SIZE,
535 .align_mask = 0x3,
536 .is_software = true,
537 .has_digest = true,
538 .is_lifecycle = false,
539 .is_zeroizable = false},
540 [kDifOtpCtrlPartitionRotOwnerAuthSlot0] = {
541 .start_addr = OTP_CTRL_PARAM_ROT_OWNER_AUTH_SLOT0_OFFSET,
542 .len = OTP_CTRL_PARAM_ROT_OWNER_AUTH_SLOT0_SIZE,
543 .align_mask = 0x3,
544 .is_software = true,
545 .has_digest = true,
546 .is_lifecycle = false,
547 .is_zeroizable = false},
548 [kDifOtpCtrlPartitionRotOwnerAuthSlot1] = {
549 .start_addr = OTP_CTRL_PARAM_ROT_OWNER_AUTH_SLOT1_OFFSET,
550 .len = OTP_CTRL_PARAM_ROT_OWNER_AUTH_SLOT1_SIZE,
551 .align_mask = 0x3,
552 .is_software = true,
553 .has_digest = true,
554 .is_lifecycle = false,
555 .is_zeroizable = false},
556 [kDifOtpCtrlPartitionPlatIntegAuthSlot0] = {
557 .start_addr = OTP_CTRL_PARAM_PLAT_INTEG_AUTH_SLOT0_OFFSET,
558 .len = OTP_CTRL_PARAM_PLAT_INTEG_AUTH_SLOT0_SIZE,
559 .align_mask = 0x3,
560 .is_software = true,
561 .has_digest = true,
562 .is_lifecycle = false,
563 .is_zeroizable = false},
564 [kDifOtpCtrlPartitionPlatIntegAuthSlot1] = {
565 .start_addr = OTP_CTRL_PARAM_PLAT_INTEG_AUTH_SLOT1_OFFSET,
566 .len = OTP_CTRL_PARAM_PLAT_INTEG_AUTH_SLOT1_SIZE,
567 .align_mask = 0x3,
568 .is_software = true,
569 .has_digest = true,
570 .is_lifecycle = false,
571 .is_zeroizable = false},
572 [kDifOtpCtrlPartitionPlatOwnerAuthSlot0] = {
573 .start_addr = OTP_CTRL_PARAM_PLAT_OWNER_AUTH_SLOT0_OFFSET,
574 .len = OTP_CTRL_PARAM_PLAT_OWNER_AUTH_SLOT0_SIZE,
575 .align_mask = 0x3,
576 .is_software = true,
577 .has_digest = true,
578 .is_lifecycle = false,
579 .is_zeroizable = false},
580 [kDifOtpCtrlPartitionPlatOwnerAuthSlot1] = {
581 .start_addr = OTP_CTRL_PARAM_PLAT_OWNER_AUTH_SLOT1_OFFSET,
582 .len = OTP_CTRL_PARAM_PLAT_OWNER_AUTH_SLOT1_SIZE,
583 .align_mask = 0x3,
584 .is_software = true,
585 .has_digest = true,
586 .is_lifecycle = false,
587 .is_zeroizable = false},
588 [kDifOtpCtrlPartitionPlatOwnerAuthSlot2] = {
589 .start_addr = OTP_CTRL_PARAM_PLAT_OWNER_AUTH_SLOT2_OFFSET,
590 .len = OTP_CTRL_PARAM_PLAT_OWNER_AUTH_SLOT2_SIZE,
591 .align_mask = 0x3,
592 .is_software = true,
593 .has_digest = true,
594 .is_lifecycle = false,
595 .is_zeroizable = false},
596 [kDifOtpCtrlPartitionPlatOwnerAuthSlot3] = {
597 .start_addr = OTP_CTRL_PARAM_PLAT_OWNER_AUTH_SLOT3_OFFSET,
598 .len = OTP_CTRL_PARAM_PLAT_OWNER_AUTH_SLOT3_SIZE,
599 .align_mask = 0x3,
600 .is_software = true,
601 .has_digest = true,
602 .is_lifecycle = false,
603 .is_zeroizable = false},
604 [kDifOtpCtrlPartitionExtNvm] = {
605 .start_addr = OTP_CTRL_PARAM_EXT_NVM_OFFSET,
606 .len = OTP_CTRL_PARAM_EXT_NVM_SIZE,
607 .align_mask = 0x3,
608 .is_software = true,
609 .has_digest = false,
610 .is_lifecycle = false,
611 .is_zeroizable = false},
612 [kDifOtpCtrlPartitionRomPatch] = {
613 .start_addr = OTP_CTRL_PARAM_ROM_PATCH_OFFSET,
614 .len = OTP_CTRL_PARAM_ROM_PATCH_SIZE,
615 .align_mask = 0x3,
616 .is_software = true,
617 .has_digest = true,
618 .is_lifecycle = false,
619 .is_zeroizable = false},
620#else
621#error "dif_otp_ctrl does not support this top"
622#endif
624 .start_addr = OTP_CTRL_PARAM_HW_CFG0_OFFSET,
625 .len = OTP_CTRL_PARAM_HW_CFG0_SIZE,
626 .align_mask = 0x3,
627 .is_software = false,
628 .has_digest = true,
629 .is_lifecycle = false,
630 .is_zeroizable = false},
632 .start_addr = OTP_CTRL_PARAM_HW_CFG1_OFFSET,
633 .len = OTP_CTRL_PARAM_HW_CFG1_SIZE,
634 .align_mask = 0x3,
635 .is_software = false,
636 .has_digest = true,
637 .is_lifecycle = false,
638 .is_zeroizable = false},
640 .start_addr = OTP_CTRL_PARAM_SECRET0_OFFSET,
641 .len = OTP_CTRL_PARAM_SECRET0_SIZE,
642 .align_mask = 0x7,
643 .is_software = false,
644 .has_digest = true,
645 .is_lifecycle = false,
646 .is_zeroizable = true},
648 .start_addr = OTP_CTRL_PARAM_SECRET1_OFFSET,
649 .len = OTP_CTRL_PARAM_SECRET1_SIZE,
650 .align_mask = 0x7,
651 .is_software = false,
652 .has_digest = true,
653 .is_lifecycle = false,
654 .is_zeroizable = true},
656 .start_addr = OTP_CTRL_PARAM_SECRET2_OFFSET,
657 .len = OTP_CTRL_PARAM_SECRET2_SIZE,
658 .align_mask = 0x7,
659 .is_software = false,
660 .has_digest = true,
661 .is_lifecycle = false,
662 .is_zeroizable = true},
663#if defined(OPENTITAN_IS_DARJEELING)
664 [kDifOtpCtrlPartitionSecret3] = {
665 .start_addr = OTP_CTRL_PARAM_SECRET3_OFFSET,
666 .len = OTP_CTRL_PARAM_SECRET3_SIZE,
667 .align_mask = 0x7,
668 .is_software = false,
669 .has_digest = true,
670 .is_lifecycle = false,
671 .is_zeroizable = true},
672#elif defined(OPENTITAN_IS_EARLGREY)
673// Earlgrey only has 3 secret partitions.
674#else
675#error "dif_otp_ctrl does not support this top"
676#endif
678 .start_addr = OTP_CTRL_PARAM_LIFE_CYCLE_OFFSET,
679 .len = OTP_CTRL_PARAM_LIFE_CYCLE_SIZE,
680 .align_mask = 0x3,
681 .is_software = false,
682 .has_digest = false,
683 .is_lifecycle = true,
684 .is_zeroizable = false},
685};
686// clang-format on
687
688dif_result_t dif_otp_ctrl_relative_address(dif_otp_ctrl_partition_t partition,
689 uint32_t abs_address,
690 uint32_t *relative_address) {
691 *relative_address = 0;
692
693 if (partition >= ARRAYSIZE(kPartitions)) {
694 return kDifBadArg;
695 }
696
697 if ((abs_address & kPartitions[partition].align_mask) != 0) {
698 return kDifUnaligned;
699 }
700
701 if (abs_address < kPartitions[partition].start_addr) {
702 return kDifOutOfRange;
703 }
704
705 *relative_address = abs_address - kPartitions[partition].start_addr;
706 if (*relative_address >= kPartitions[partition].len) {
707 *relative_address = 0;
708 return kDifOutOfRange;
709 }
710
711 return kDifOk;
712}
713
714dif_result_t dif_otp_ctrl_dai_read_start(const dif_otp_ctrl_t *otp,
715 dif_otp_ctrl_partition_t partition,
716 uint32_t address) {
717 if (otp == NULL || partition >= ARRAYSIZE(kPartitions)) {
718 return kDifBadArg;
719 }
720
721 if ((address & kPartitions[partition].align_mask) != 0) {
722 return kDifUnaligned;
723 }
724
725 if (address >= kPartitions[partition].len) {
726 return kDifOutOfRange;
727 }
728
729 address += kPartitions[partition].start_addr;
730 mmio_region_write32(otp->base_addr, OTP_CTRL_DIRECT_ACCESS_ADDRESS_REG_OFFSET,
731 address);
732
733 uint32_t cmd =
734 bitfield_bit32_write(0, OTP_CTRL_DIRECT_ACCESS_CMD_RD_BIT, true);
735 mmio_region_write32(otp->base_addr, OTP_CTRL_DIRECT_ACCESS_CMD_REG_OFFSET,
736 cmd);
737
738 return kDifOk;
739}
740
741dif_result_t dif_otp_ctrl_dai_read32_end(const dif_otp_ctrl_t *otp,
742 uint32_t *value) {
743 if (otp == NULL || value == NULL) {
744 return kDifBadArg;
745 }
746
747 *value = mmio_region_read32(otp->base_addr,
748 OTP_CTRL_DIRECT_ACCESS_RDATA_0_REG_OFFSET);
749 return kDifOk;
750}
751
752dif_result_t dif_otp_ctrl_dai_read64_end(const dif_otp_ctrl_t *otp,
753 uint64_t *value) {
754 if (otp == NULL || value == NULL) {
755 return kDifBadArg;
756 }
757
758 *value = mmio_region_read32(otp->base_addr,
759 OTP_CTRL_DIRECT_ACCESS_RDATA_1_REG_OFFSET);
760 *value <<= 32;
761 *value |= mmio_region_read32(otp->base_addr,
762 OTP_CTRL_DIRECT_ACCESS_RDATA_0_REG_OFFSET);
763 return kDifOk;
764}
765
766dif_result_t dif_otp_ctrl_dai_program32(const dif_otp_ctrl_t *otp,
767 dif_otp_ctrl_partition_t partition,
768 uint32_t address, uint32_t value) {
769 if (otp == NULL || partition >= ARRAYSIZE(kPartitions)) {
770 return kDifBadArg;
771 }
772
773 // Ensure that we are writing to a 32-bit-access partition by checking that
774 // the alignment mask is 0b11.
775 //
776 // Note furthermore that the LC partition is *not* writeable, so we eject
777 // here.
778 if (kPartitions[partition].align_mask != 0x3 ||
779 kPartitions[partition].is_lifecycle) {
780 return kDifError;
781 }
782
783 if ((address & kPartitions[partition].align_mask) != 0) {
784 return kDifUnaligned;
785 }
786
787 // NOTE: The bounds check is tightened here, since we disallow writing the
788 // digest and zeroization fields directly.
789 if (address >= partition_effective_size(&kPartitions[partition])) {
790 return kDifOutOfRange;
791 }
792
793 address += kPartitions[partition].start_addr;
794 mmio_region_write32(otp->base_addr, OTP_CTRL_DIRECT_ACCESS_ADDRESS_REG_OFFSET,
795 address);
796
797 mmio_region_write32(otp->base_addr, OTP_CTRL_DIRECT_ACCESS_WDATA_0_REG_OFFSET,
798 value);
799
800 uint32_t cmd =
801 bitfield_bit32_write(0, OTP_CTRL_DIRECT_ACCESS_CMD_WR_BIT, true);
802 mmio_region_write32(otp->base_addr, OTP_CTRL_DIRECT_ACCESS_CMD_REG_OFFSET,
803 cmd);
804
805 return kDifOk;
806}
807
808dif_result_t dif_otp_ctrl_dai_program64(const dif_otp_ctrl_t *otp,
809 dif_otp_ctrl_partition_t partition,
810 uint32_t address, uint64_t value) {
811 if (otp == NULL || partition >= ARRAYSIZE(kPartitions)) {
812 return kDifBadArg;
813 }
814
815 // Ensure that we are writing to a 64-bit-access partition by checking that
816 // the alignment mask is 0b111.
817 if (kPartitions[partition].align_mask != 0x7) {
818 return kDifError;
819 }
820
821 if ((address & kPartitions[partition].align_mask) != 0) {
822 return kDifUnaligned;
823 }
824
825 // NOTE: The bounds check is tightened here, since we disallow writing the
826 // digest and zeroization fields directly.
827 if (address >= partition_effective_size(&kPartitions[partition])) {
828 return kDifOutOfRange;
829 }
830
831 address += kPartitions[partition].start_addr;
832 mmio_region_write32(otp->base_addr, OTP_CTRL_DIRECT_ACCESS_ADDRESS_REG_OFFSET,
833 address);
834
835 mmio_region_write32(otp->base_addr, OTP_CTRL_DIRECT_ACCESS_WDATA_0_REG_OFFSET,
836 value & UINT32_MAX);
837 mmio_region_write32(otp->base_addr, OTP_CTRL_DIRECT_ACCESS_WDATA_1_REG_OFFSET,
838 value >> 32);
839
840 uint32_t cmd =
841 bitfield_bit32_write(0, OTP_CTRL_DIRECT_ACCESS_CMD_WR_BIT, true);
842 mmio_region_write32(otp->base_addr, OTP_CTRL_DIRECT_ACCESS_CMD_REG_OFFSET,
843 cmd);
844
845 return kDifOk;
846}
847
848dif_result_t dif_otp_ctrl_dai_digest(const dif_otp_ctrl_t *otp,
849 dif_otp_ctrl_partition_t partition,
850 uint64_t digest) {
851 if (otp == NULL || partition >= ARRAYSIZE(kPartitions)) {
852 return kDifBadArg;
853 }
854
855 // Not all partitions have a digest.
856 if (!kPartitions[partition].has_digest) {
857 return kDifError;
858 }
859
860 // For software partitions, the digest must be nonzero; for all other
861 // partitions it must be zero.
862 bool is_sw = kPartitions[partition].is_software;
863 if (is_sw == (digest == 0)) {
864 return kDifBadArg;
865 }
866
867 uint32_t address = kPartitions[partition].start_addr;
868 if (is_sw) {
869 // The digest is located right after the writeable fields.
870 address += partition_effective_size(&kPartitions[partition]);
871 }
872 mmio_region_write32(otp->base_addr, OTP_CTRL_DIRECT_ACCESS_ADDRESS_REG_OFFSET,
873 address);
874
875 if (digest != 0) {
876 mmio_region_write32(otp->base_addr,
877 OTP_CTRL_DIRECT_ACCESS_WDATA_0_REG_OFFSET,
878 digest & 0xffffffff);
879 mmio_region_write32(otp->base_addr,
880 OTP_CTRL_DIRECT_ACCESS_WDATA_1_REG_OFFSET,
881 digest >> 32);
882 }
883
884 bitfield_bit32_index_t cmd_bit = is_sw
885 ? OTP_CTRL_DIRECT_ACCESS_CMD_WR_BIT
886 : OTP_CTRL_DIRECT_ACCESS_CMD_DIGEST_BIT;
887 uint32_t cmd = bitfield_bit32_write(0, cmd_bit, true);
888 mmio_region_write32(otp->base_addr, OTP_CTRL_DIRECT_ACCESS_CMD_REG_OFFSET,
889 cmd);
890
891 return kDifOk;
892}
893
894static bool get_digest_regs(dif_otp_ctrl_partition_t partition, ptrdiff_t *reg0,
895 ptrdiff_t *reg1) {
896 switch (partition) {
898 *reg0 = OTP_CTRL_VENDOR_TEST_DIGEST_0_REG_OFFSET;
899 *reg1 = OTP_CTRL_VENDOR_TEST_DIGEST_1_REG_OFFSET;
900 break;
902 *reg0 = OTP_CTRL_CREATOR_SW_CFG_DIGEST_0_REG_OFFSET;
903 *reg1 = OTP_CTRL_CREATOR_SW_CFG_DIGEST_1_REG_OFFSET;
904 break;
906 *reg0 = OTP_CTRL_OWNER_SW_CFG_DIGEST_0_REG_OFFSET;
907 *reg1 = OTP_CTRL_OWNER_SW_CFG_DIGEST_1_REG_OFFSET;
908 break;
909#if defined(OPENTITAN_IS_EARLGREY)
910 case kDifOtpCtrlPartitionRotCreatorAuthCodesign:
911 *reg0 = OTP_CTRL_ROT_CREATOR_AUTH_CODESIGN_DIGEST_0_REG_OFFSET;
912 *reg1 = OTP_CTRL_ROT_CREATOR_AUTH_CODESIGN_DIGEST_1_REG_OFFSET;
913 break;
914 case kDifOtpCtrlPartitionRotCreatorAuthState:
915 *reg0 = OTP_CTRL_ROT_CREATOR_AUTH_STATE_DIGEST_0_REG_OFFSET;
916 *reg1 = OTP_CTRL_ROT_CREATOR_AUTH_STATE_DIGEST_1_REG_OFFSET;
917 break;
918#elif defined(OPENTITAN_IS_DARJEELING)
919 case kDifOtpCtrlPartitionRotCreatorAuth:
920 *reg0 = OTP_CTRL_ROT_CREATOR_AUTH_DIGEST_0_REG_OFFSET;
921 *reg1 = OTP_CTRL_ROT_CREATOR_AUTH_DIGEST_1_REG_OFFSET;
922 break;
923 case kDifOtpCtrlPartitionRotOwnerAuthSlot0:
924 *reg0 = OTP_CTRL_ROT_OWNER_AUTH_SLOT0_DIGEST_0_REG_OFFSET;
925 *reg1 = OTP_CTRL_ROT_OWNER_AUTH_SLOT0_DIGEST_1_REG_OFFSET;
926 break;
927 case kDifOtpCtrlPartitionRotOwnerAuthSlot1:
928 *reg0 = OTP_CTRL_ROT_OWNER_AUTH_SLOT1_DIGEST_0_REG_OFFSET;
929 *reg1 = OTP_CTRL_ROT_OWNER_AUTH_SLOT1_DIGEST_1_REG_OFFSET;
930 break;
931 case kDifOtpCtrlPartitionPlatIntegAuthSlot0:
932 *reg0 = OTP_CTRL_PLAT_INTEG_AUTH_SLOT0_DIGEST_0_REG_OFFSET;
933 *reg1 = OTP_CTRL_PLAT_INTEG_AUTH_SLOT0_DIGEST_1_REG_OFFSET;
934 break;
935 case kDifOtpCtrlPartitionPlatIntegAuthSlot1:
936 *reg0 = OTP_CTRL_PLAT_INTEG_AUTH_SLOT1_DIGEST_0_REG_OFFSET;
937 *reg1 = OTP_CTRL_PLAT_INTEG_AUTH_SLOT1_DIGEST_1_REG_OFFSET;
938 break;
939 case kDifOtpCtrlPartitionPlatOwnerAuthSlot0:
940 *reg0 = OTP_CTRL_PLAT_OWNER_AUTH_SLOT0_DIGEST_0_REG_OFFSET;
941 *reg1 = OTP_CTRL_PLAT_OWNER_AUTH_SLOT0_DIGEST_1_REG_OFFSET;
942 break;
943 case kDifOtpCtrlPartitionPlatOwnerAuthSlot1:
944 *reg0 = OTP_CTRL_PLAT_OWNER_AUTH_SLOT1_DIGEST_0_REG_OFFSET;
945 *reg1 = OTP_CTRL_PLAT_OWNER_AUTH_SLOT1_DIGEST_1_REG_OFFSET;
946 break;
947 case kDifOtpCtrlPartitionPlatOwnerAuthSlot2:
948 *reg0 = OTP_CTRL_PLAT_OWNER_AUTH_SLOT2_DIGEST_0_REG_OFFSET;
949 *reg1 = OTP_CTRL_PLAT_OWNER_AUTH_SLOT2_DIGEST_1_REG_OFFSET;
950 break;
951 case kDifOtpCtrlPartitionPlatOwnerAuthSlot3:
952 *reg0 = OTP_CTRL_PLAT_OWNER_AUTH_SLOT3_DIGEST_0_REG_OFFSET;
953 *reg1 = OTP_CTRL_PLAT_OWNER_AUTH_SLOT3_DIGEST_1_REG_OFFSET;
954 break;
955 case kDifOtpCtrlPartitionRomPatch:
956 *reg0 = OTP_CTRL_ROM_PATCH_DIGEST_0_REG_OFFSET;
957 *reg1 = OTP_CTRL_ROM_PATCH_DIGEST_1_REG_OFFSET;
958 break;
959#else
960#error "dif_otp_ctrl does not support this top"
961#endif
963 *reg0 = OTP_CTRL_HW_CFG0_DIGEST_0_REG_OFFSET;
964 *reg1 = OTP_CTRL_HW_CFG0_DIGEST_1_REG_OFFSET;
965 break;
967 *reg0 = OTP_CTRL_HW_CFG1_DIGEST_0_REG_OFFSET;
968 *reg1 = OTP_CTRL_HW_CFG1_DIGEST_1_REG_OFFSET;
969 break;
971 *reg0 = OTP_CTRL_SECRET0_DIGEST_0_REG_OFFSET;
972 *reg1 = OTP_CTRL_SECRET0_DIGEST_1_REG_OFFSET;
973 break;
975 *reg0 = OTP_CTRL_SECRET1_DIGEST_0_REG_OFFSET;
976 *reg1 = OTP_CTRL_SECRET1_DIGEST_1_REG_OFFSET;
977 break;
979 *reg0 = OTP_CTRL_SECRET2_DIGEST_0_REG_OFFSET;
980 *reg1 = OTP_CTRL_SECRET2_DIGEST_1_REG_OFFSET;
981 break;
982#if defined(OPENTITAN_IS_DARJEELING)
983 case kDifOtpCtrlPartitionSecret3:
984 *reg0 = OTP_CTRL_SECRET3_DIGEST_0_REG_OFFSET;
985 *reg1 = OTP_CTRL_SECRET3_DIGEST_1_REG_OFFSET;
986 break;
987#elif defined(OPENTITAN_IS_EARLGREY)
988// Earlgrey only has 3 secret partitions.
989#else
990#error "dif_otp_ctrl does not support this top"
991#endif
992 default:
993 return false;
994 }
995
996 return true;
997}
998
999dif_result_t dif_otp_ctrl_is_digest_computed(const dif_otp_ctrl_t *otp,
1000 dif_otp_ctrl_partition_t partition,
1001 bool *is_computed) {
1002 if (otp == NULL || is_computed == NULL) {
1003 return kDifBadArg;
1004 }
1005
1006 ptrdiff_t reg0, reg1;
1007 if (!get_digest_regs(partition, &reg0, &reg1)) {
1008 return kDifBadArg;
1009 }
1010
1011 uint64_t value = mmio_region_read32(otp->base_addr, reg1);
1012 value <<= 32;
1013 value |= mmio_region_read32(otp->base_addr, reg0);
1014
1015 *is_computed = value != 0;
1016
1017 return kDifOk;
1018}
1019
1020dif_result_t dif_otp_ctrl_get_digest(const dif_otp_ctrl_t *otp,
1021 dif_otp_ctrl_partition_t partition,
1022 uint64_t *digest) {
1023 if (otp == NULL || digest == NULL) {
1024 return kDifBadArg;
1025 }
1026
1027 ptrdiff_t reg0, reg1;
1028 if (!get_digest_regs(partition, &reg0, &reg1)) {
1029 return kDifBadArg;
1030 }
1031
1032 uint64_t value = mmio_region_read32(otp->base_addr, reg1);
1033 value <<= 32;
1034 value |= mmio_region_read32(otp->base_addr, reg0);
1035
1036 if (value == 0) {
1037 return kDifError;
1038 }
1039 *digest = value;
1040
1041 return kDifOk;
1042}
1043
1044dif_result_t dif_otp_ctrl_read_blocking(const dif_otp_ctrl_t *otp,
1045 dif_otp_ctrl_partition_t partition,
1046 uint32_t address, uint32_t *buf,
1047 size_t len) {
1048 if (otp == NULL || partition >= ARRAYSIZE(kPartitions) || buf == NULL) {
1049 return kDifBadArg;
1050 }
1051
1052 if (!kPartitions[partition].is_software) {
1053 return kDifError;
1054 }
1055
1056 if ((address & kPartitions[partition].align_mask) != 0) {
1057 return kDifUnaligned;
1058 }
1059
1060 if (address + len >= kPartitions[partition].len) {
1061 return kDifOutOfRange;
1062 }
1063
1064 uint32_t reg_offset = OTP_CTRL_SW_CFG_WINDOW_REG_OFFSET +
1065 kPartitions[partition].start_addr + address;
1066 mmio_region_memcpy_from_mmio32(otp->base_addr, reg_offset, buf,
1067 len * sizeof(uint32_t));
1068 return kDifOk;
1069}