mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 03:00:34 +03:00
Merge branch 'feature/efuse_dump' into 'master'
feat(efuse): Support efuse token dump Closes IDF-14046 See merge request espressif/esp-idf!41754
This commit is contained in:
@@ -25,6 +25,7 @@ else()
|
||||
endif()
|
||||
|
||||
list(APPEND srcs "src/esp_efuse_api.c"
|
||||
"src/esp_efuse_dump.c"
|
||||
"src/esp_efuse_fields.c"
|
||||
"src/esp_efuse_utility.c"
|
||||
"src/efuse_controller/keys/${type}/esp_efuse_api_key.c")
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
menu "eFuse Bit Manager"
|
||||
|
||||
config EFUSE_ENABLE_STAGED_TOKEN_API
|
||||
bool "Enable staged eFuse token dump API"
|
||||
default n
|
||||
help
|
||||
Allows esp_efuse_token_dump() to produce dump types that include
|
||||
staged eFuse writes. Staging refers to pending eFuse writes
|
||||
prepared in batch mode but not yet burned to hardware.
|
||||
|
||||
Token dump types:
|
||||
- EFSR: current eFuse read state.
|
||||
- EFSW: pending write state.
|
||||
- EFSRW: combined read and pending write state.
|
||||
|
||||
Pending write tokens may contain sensitive data, including
|
||||
plaintext keys, before eFuses are burned and read-protected.
|
||||
Keep this option disabled in production firmware unless staged
|
||||
token workflows are explicitly required.
|
||||
|
||||
config EFUSE_CUSTOM_TABLE
|
||||
bool "Use custom eFuse table"
|
||||
default n
|
||||
|
||||
@@ -881,6 +881,112 @@ typedef enum {
|
||||
esp_err_t esp_efuse_enable_ecdsa_p192_curve_mode(void);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Flags specifying which source(s) to use when dumping eFUSE data.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Prefix used in device log lines to trigger automatic token decoding in idf.py monitor.
|
||||
*/
|
||||
#define ESP_EFUSE_MONITOR_EXECUTE_ESPEFUSE_SUMMARY "IDF_MONITOR_EXECUTE_ESPEFUSE_SUMMARY"
|
||||
|
||||
/**
|
||||
* @brief Prefix used in device log lines to trigger token dump decoding in idf.py monitor.
|
||||
*/
|
||||
#define ESP_EFUSE_MONITOR_EXECUTE_ESPEFUSE_DUMP "IDF_MONITOR_EXECUTE_ESPEFUSE_DUMP"
|
||||
|
||||
/**
|
||||
* @brief Recommended minimum buffer length for esp_efuse_token_dump().
|
||||
*/
|
||||
#define ESP_EFUSE_TOKEN_DUMP_MIN_LEN 512
|
||||
|
||||
typedef enum {
|
||||
ESP_EFUSE_TOKEN_FROM_READ = 1, /**< Dump from the eFUSE read registers (the final, permanently programmed values). */
|
||||
ESP_EFUSE_TOKEN_FROM_STAGED = 2, /**< Dump from write/staging registers or programming buffer (pending or staged writes). This can expose keys in plaintext because it shows values that are not yet burned and not yet read-protected. Requires CONFIG_EFUSE_ENABLE_STAGED_TOKEN_API. */
|
||||
ESP_EFUSE_TOKEN_FROM_READ_STAGED = ESP_EFUSE_TOKEN_FROM_READ | ESP_EFUSE_TOKEN_FROM_STAGED, /**< Combine both read and staged sources to show committed and pending values. The staged portion can expose keys in plaintext because it shows values that are not yet burned and not yet read-protected. Requires CONFIG_EFUSE_ENABLE_STAGED_TOKEN_API. */
|
||||
} esp_efuse_token_type_t;
|
||||
|
||||
/**
|
||||
* @brief Print a single-line dump token that serializes all eFuse blocks.
|
||||
*
|
||||
* Token formats (null-terminated strings):
|
||||
* - Read efuse area EFSR:chip_name:chip_version:b64_blocks:b64_cerr:b64_crc32
|
||||
* - Staged efuse area EFSW:chip_name:chip_version:b64_blocks:b64_cerr:b64_crc32
|
||||
* - Combination of two areas (read and staged) EFSRW:...
|
||||
*
|
||||
* This token is useful when a host tool cannot read the device directly (for example,
|
||||
* when UART download mode is disabled, secure download is enabled).
|
||||
* Copy the entire token string and decode it on a host that has access to espefuse.
|
||||
*
|
||||
* Example (decode token and show only active fields):
|
||||
* @code
|
||||
* espefuse --token EFSR:esp32:300:AAABAAAAAAAAAAAAAIAAAAAAAAAAABAAAAAAAA::oKGio6SlpqeoqaqrrK2ur7CxsrO0tba3uLm6u7y9vr8:::fPaC-A summary --active
|
||||
* @endcode
|
||||
*
|
||||
* Where:
|
||||
* - token_marker = EFSR, EFSW, or EFSRW
|
||||
* - chip_name = CONFIG_IDF_TARGET (e.g., "esp32c5")
|
||||
* - chip_version = chip version (e.g., "100" for v1.0). version = major wafer version * 100 + minor wafer version.
|
||||
* - b64_blocks = concatenation of all blocks’ 32-bit words (little-endian byte order),
|
||||
* encoded as Base64URL without padding, for BLK0..BLK_MAX-1.
|
||||
* - b64_cerr = optional coding-error snapshot.
|
||||
* - b64_crc32 = crc32("token_marker:chip:ver:b64_blocks:b64_cerr:")
|
||||
* b64 - base 64 format (Base64URL, UNPADDED)
|
||||
*
|
||||
* @note Dump modes that include staged data (ESP_EFUSE_TOKEN_FROM_STAGED and
|
||||
* ESP_EFUSE_TOKEN_FROM_READ_STAGED) can expose sensitive data, including
|
||||
* plaintext key material, because they show values before they are
|
||||
* burned and before read-protection is applied. Treat EFSW/EFSRW tokens
|
||||
* as sensitive artifacts and enable them only with CONFIG_EFUSE_ENABLE_STAGED_TOKEN_API.
|
||||
*
|
||||
* @note When @p buf is NULL, the token is printed with esp_log() using a
|
||||
* non-constrained logging configuration. If the token must be emitted
|
||||
* from a constrained environment, pass a buffer to this function and
|
||||
* print or transport the resulting token with a constrained-safe method.
|
||||
*
|
||||
* @param dump_type Select which efuse data to dump: read, staged writes, or both.
|
||||
* @param buf Buffer to store the resulting token string. If NULL, output goes to console.
|
||||
* @param buf_len Length of the buffer. Must be at least ESP_EFUSE_TOKEN_DUMP_MIN_LEN bytes to hold
|
||||
* the full token for esp32xx series.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success.
|
||||
* - ESP_ERR_NOT_SUPPORTED if @p dump_type requests staged data and
|
||||
* CONFIG_EFUSE_ENABLE_STAGED_TOKEN_API is disabled to avoid exposing
|
||||
* staged values, including plaintext key material, before burn/read-protect.
|
||||
* - ESP_ERR_INVALID_ARG if @p dump_type is invalid.
|
||||
* - ESP_ERR_INVALID_SIZE if buf_len is too small
|
||||
*/
|
||||
esp_err_t esp_efuse_token_dump(esp_efuse_token_type_t dump_type, char *buf, size_t buf_len);
|
||||
|
||||
/**
|
||||
* @brief Burns the EFSW token dump
|
||||
*
|
||||
* @note The EFSW token dump can be produced from a host or from-device utility. Examples:
|
||||
* - Host: `espefuse burn-bit BLOCK2 1 --show-token`
|
||||
* - Device: `esp_efuse_token_dump(ESP_EFUSE_TOKEN_FROM_STAGED, buf, len)`
|
||||
*
|
||||
* EFSW:chip_name:chip_version:b64_blocks::b64_crc32
|
||||
*
|
||||
* The function validates:
|
||||
* - Token marker EFSW
|
||||
* - Chip name (must match CONFIG_IDF_TARGET)
|
||||
* - Chip version is validated unless ignore_ver is set to true. The major version must be equal.
|
||||
* - Chip version unless ignore_ver is true
|
||||
* - CRC32
|
||||
*
|
||||
* @param token_in Null-terminated EFSW token string.
|
||||
* @param ignore_ver If true, skip enforcing the wafer chip version in the token.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success (token parsed and write efuse area is populated).
|
||||
* - ESP_ERR_INVALID_ARG on format/mismatch errors (bad token marker/chip/ver/layout)
|
||||
* - ESP_ERR_INVALID_CRC if CRC verification fails
|
||||
* - ESP_ERR_INVALID_VERSION if chip version mismatches and ignore_ver is false
|
||||
* - Other esp_err_t from lower layers if writing/burning fails
|
||||
*/
|
||||
esp_err_t esp_efuse_token_burn(const char *token_in, bool ignore_ver);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2017-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2017-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -33,6 +33,13 @@ typedef struct {
|
||||
uintptr_t end;
|
||||
} esp_efuse_range_addr_t;
|
||||
|
||||
/**
|
||||
* @brief Current nesting level of eFuse batch write mode.
|
||||
*
|
||||
* This state is shared between the public eFuse API implementation.
|
||||
*/
|
||||
extern int s_batch_writing_mode;
|
||||
|
||||
/**
|
||||
* @brief This is type of function that will handle the efuse field register.
|
||||
*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2017-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2017-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -13,19 +13,28 @@
|
||||
|
||||
ESP_LOG_ATTR_TAG(TAG, "efuse");
|
||||
|
||||
#ifdef NON_OS_BUILD
|
||||
#define EFUSE_LOCK_ACQUIRE_RECURSIVE()
|
||||
#define EFUSE_LOCK_RELEASE_RECURSIVE()
|
||||
#else
|
||||
#if !NON_OS_BUILD
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include <sys/lock.h>
|
||||
static _lock_t s_efuse_lock;
|
||||
#define EFUSE_LOCK_ACQUIRE_RECURSIVE() _lock_acquire_recursive(&s_efuse_lock)
|
||||
#define EFUSE_LOCK_RELEASE_RECURSIVE() _lock_release_recursive(&s_efuse_lock)
|
||||
#endif
|
||||
|
||||
static int s_batch_writing_mode = 0;
|
||||
void esp_efuse_lock_acquire(void)
|
||||
{
|
||||
#if !NON_OS_BUILD
|
||||
_lock_acquire_recursive(&s_efuse_lock);
|
||||
#endif
|
||||
}
|
||||
|
||||
void esp_efuse_lock_release(void)
|
||||
{
|
||||
#if !NON_OS_BUILD
|
||||
_lock_release_recursive(&s_efuse_lock);
|
||||
#endif
|
||||
}
|
||||
|
||||
int s_batch_writing_mode = 0;
|
||||
|
||||
// Public API functions
|
||||
|
||||
@@ -80,7 +89,7 @@ esp_err_t esp_efuse_read_field_cnt(const esp_efuse_desc_t* field[], size_t* out_
|
||||
// write array to EFUSE
|
||||
esp_err_t esp_efuse_write_field_blob(const esp_efuse_desc_t* field[], const void* src, size_t src_size_bits)
|
||||
{
|
||||
EFUSE_LOCK_ACQUIRE_RECURSIVE();
|
||||
esp_efuse_lock_acquire();
|
||||
esp_err_t err = ESP_OK;
|
||||
if (field == NULL || src == NULL || src_size_bits == 0) {
|
||||
err = ESP_ERR_INVALID_ARG;
|
||||
@@ -100,14 +109,14 @@ esp_err_t esp_efuse_write_field_blob(const esp_efuse_desc_t* field[], const void
|
||||
esp_efuse_utility_reset();
|
||||
}
|
||||
}
|
||||
EFUSE_LOCK_RELEASE_RECURSIVE();
|
||||
esp_efuse_lock_release();
|
||||
return err;
|
||||
}
|
||||
|
||||
// program cnt bits to "1"
|
||||
esp_err_t esp_efuse_write_field_cnt(const esp_efuse_desc_t* field[], size_t cnt)
|
||||
{
|
||||
EFUSE_LOCK_ACQUIRE_RECURSIVE();
|
||||
esp_efuse_lock_acquire();
|
||||
esp_err_t err = ESP_OK;
|
||||
if (field == NULL || cnt == 0) {
|
||||
err = ESP_ERR_INVALID_ARG;
|
||||
@@ -135,7 +144,7 @@ esp_err_t esp_efuse_write_field_cnt(const esp_efuse_desc_t* field[], size_t cnt)
|
||||
esp_efuse_utility_reset();
|
||||
}
|
||||
}
|
||||
EFUSE_LOCK_RELEASE_RECURSIVE();
|
||||
esp_efuse_lock_release();
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -185,7 +194,7 @@ uint32_t esp_efuse_read_reg(esp_efuse_block_t blk, unsigned int num_reg)
|
||||
// writing efuse register.
|
||||
esp_err_t esp_efuse_write_reg(esp_efuse_block_t blk, unsigned int num_reg, uint32_t val)
|
||||
{
|
||||
EFUSE_LOCK_ACQUIRE_RECURSIVE();
|
||||
esp_efuse_lock_acquire();
|
||||
if (s_batch_writing_mode == 0) {
|
||||
esp_efuse_utility_reset();
|
||||
}
|
||||
@@ -199,7 +208,7 @@ esp_err_t esp_efuse_write_reg(esp_efuse_block_t blk, unsigned int num_reg, uint3
|
||||
}
|
||||
esp_efuse_utility_reset();
|
||||
}
|
||||
EFUSE_LOCK_RELEASE_RECURSIVE();
|
||||
esp_efuse_lock_release();
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -245,7 +254,7 @@ esp_err_t esp_efuse_write_block(esp_efuse_block_t blk, const void* src_key, size
|
||||
|
||||
esp_err_t esp_efuse_batch_write_begin(void)
|
||||
{
|
||||
EFUSE_LOCK_ACQUIRE_RECURSIVE();
|
||||
esp_efuse_lock_acquire();
|
||||
assert(s_batch_writing_mode >= 0);
|
||||
if (++s_batch_writing_mode == 1) {
|
||||
esp_efuse_utility_reset();
|
||||
@@ -263,7 +272,7 @@ esp_err_t esp_efuse_batch_write_cancel(void)
|
||||
if (--s_batch_writing_mode == 0) {
|
||||
esp_efuse_utility_reset();
|
||||
ESP_LOGI(TAG, "Batch mode of writing fields is cancelled");
|
||||
EFUSE_LOCK_RELEASE_RECURSIVE();
|
||||
esp_efuse_lock_release();
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -282,7 +291,7 @@ esp_err_t esp_efuse_batch_write_commit(void)
|
||||
} else {
|
||||
esp_efuse_utility_reset();
|
||||
}
|
||||
EFUSE_LOCK_RELEASE_RECURSIVE();
|
||||
esp_efuse_lock_release();
|
||||
return err;
|
||||
}
|
||||
return ESP_OK;
|
||||
@@ -349,8 +358,8 @@ esp_err_t esp_efuse_destroy_block(esp_efuse_block_t block)
|
||||
if (block < EFUSE_BLK_KEY0 || block >= EFUSE_BLK_KEY_MAX) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
EFUSE_LOCK_ACQUIRE_RECURSIVE();
|
||||
esp_efuse_lock_acquire();
|
||||
esp_err_t error = destroy_block(block);
|
||||
EFUSE_LOCK_RELEASE_RECURSIVE();
|
||||
esp_efuse_lock_release();
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,583 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "esp_rom_crc.h"
|
||||
#include "hal/efuse_hal.h"
|
||||
#include "esp_efuse.h"
|
||||
#include "esp_efuse_utility.h"
|
||||
#include "esp_private/log_lock.h"
|
||||
#include "esp_private/log_util.h"
|
||||
#include "esp_log.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
static const char *b64_table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
||||
extern const esp_efuse_range_addr_t range_read_addr_blocks[];
|
||||
extern const esp_efuse_range_addr_t range_write_addr_blocks[];
|
||||
|
||||
extern void esp_efuse_lock_acquire(void);
|
||||
extern void esp_efuse_lock_release(void);
|
||||
|
||||
ESP_LOG_ATTR_TAG(TAG, "efuse");
|
||||
|
||||
// -------------------------- EFSR/W/RW token dump ----------------------------
|
||||
|
||||
/*
|
||||
* Context structure for output function
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t crc; /**< CRC32 of dumped data */
|
||||
char *buf; /**< Buffer for dumped data. If NULL, output to log */
|
||||
unsigned buf_idx; /**< Current index in buf */
|
||||
size_t buf_len; /**< Maximum length of buf */
|
||||
} out_dump_ctx_t;
|
||||
|
||||
static esp_err_t output(const char *s, out_dump_ctx_t *out_ctx)
|
||||
{
|
||||
if (s == NULL || out_ctx == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
unsigned slen = strlen(s);
|
||||
if (out_ctx->buf) {
|
||||
unsigned len = s[0] == '\0' ? 1 : slen;
|
||||
if (out_ctx->buf_idx + len < out_ctx->buf_len) {
|
||||
memcpy(&out_ctx->buf[out_ctx->buf_idx], s, len);
|
||||
out_ctx->buf_idx += len;
|
||||
} else {
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
} else {
|
||||
esp_log_config_t config = {
|
||||
.opts = {
|
||||
.log_level = ESP_LOG_INFO,
|
||||
.constrained_env = 0,
|
||||
.require_formatting = 0,
|
||||
.dis_color = 1,
|
||||
.dis_timestamp = 1,
|
||||
.binary_mode = 0,
|
||||
},
|
||||
};
|
||||
esp_log(config, NULL, "%s", (s[0] == '\0') ? "\n" : s);
|
||||
}
|
||||
out_ctx->crc = esp_rom_crc32_le(out_ctx->crc, (const uint8_t *)s, slen);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// --------------------- Streaming Base64URL encoder (no '=') ----------------
|
||||
|
||||
/*
|
||||
* Base64URL encoding stream structure
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t rem[3]; // Remaining bytes
|
||||
int rlen; // Number of remaining bytes in rem[]
|
||||
} b64_stream_t;
|
||||
|
||||
static esp_err_t b64_output(uint32_t data, out_dump_ctx_t *out_ctx)
|
||||
{
|
||||
// data contains 24 bits (3 bytes)
|
||||
char buffer[5] = {
|
||||
b64_table[(data >> 18) & 63],
|
||||
b64_table[(data >> 12) & 63],
|
||||
b64_table[(data >> 6) & 63],
|
||||
b64_table[ data & 63],
|
||||
0,
|
||||
};
|
||||
return output(buffer, out_ctx);
|
||||
}
|
||||
|
||||
static esp_err_t b64_convert(b64_stream_t *s, const uint32_t *data, size_t num_words, out_dump_ctx_t *out_ctx)
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
uint8_t *p = (uint8_t *)data;
|
||||
size_t n = num_words * 4; // bytes
|
||||
|
||||
// Complete leftover first
|
||||
if (s->rlen) {
|
||||
while (s->rlen < 3 && n) {
|
||||
s->rem[s->rlen++] = *p++;
|
||||
--n;
|
||||
}
|
||||
if (s->rlen == 3) {
|
||||
uint32_t v = ((uint32_t)s->rem[0] << 16) | ((uint32_t)s->rem[1] << 8) | s->rem[2];
|
||||
ESP_EFUSE_CHK(b64_output(v, out_ctx));
|
||||
s->rlen = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Fast path: 3-byte groups
|
||||
while (n >= 3) {
|
||||
uint32_t v = ((uint32_t)p[0] << 16) | ((uint32_t)p[1] << 8) | p[2];
|
||||
ESP_EFUSE_CHK(b64_output(v, out_ctx));
|
||||
p += 3;
|
||||
n -= 3;
|
||||
}
|
||||
|
||||
// Remainder
|
||||
while (n--) {
|
||||
s->rem[s->rlen++] = *p++;
|
||||
}
|
||||
err_exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
static esp_err_t b64_flush(b64_stream_t *s, out_dump_ctx_t *out_ctx)
|
||||
{
|
||||
char buffer[5] = { 0 };
|
||||
if (s->rlen == 0) {
|
||||
return ESP_OK;
|
||||
} else if (s->rlen == 1) {
|
||||
uint32_t data = ((uint32_t)s->rem[0] << 16);
|
||||
buffer[0] = b64_table[(data >> 18) & 63];
|
||||
buffer[1] = b64_table[(data >> 12) & 63];
|
||||
buffer[2] = 0;
|
||||
} else if (s->rlen == 2) {
|
||||
uint32_t data = ((uint32_t)s->rem[0] << 16) | ((uint32_t)s->rem[1] << 8);
|
||||
buffer[0] = b64_table[(data >> 18) & 63];
|
||||
buffer[1] = b64_table[(data >> 12) & 63];
|
||||
buffer[2] = b64_table[(data >> 6) & 63];
|
||||
buffer[3] = 0;
|
||||
}
|
||||
esp_err_t err = output(buffer, out_ctx);
|
||||
s->rlen = 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
// Reading efuse register.
|
||||
static uint32_t read_of_write_reg(esp_efuse_block_t blk, unsigned int num_reg)
|
||||
{
|
||||
assert(blk >= 0 && blk < EFUSE_BLK_MAX);
|
||||
assert(num_reg <= (range_write_addr_blocks[blk].end - range_write_addr_blocks[blk].start) / sizeof(uint32_t));
|
||||
return REG_READ(range_write_addr_blocks[blk].start + num_reg * 4);
|
||||
}
|
||||
|
||||
static uint32_t get_data(esp_efuse_block_t blk, unsigned int num_reg, esp_efuse_token_type_t dump_type)
|
||||
{
|
||||
uint32_t data = 0;
|
||||
if (dump_type & ESP_EFUSE_TOKEN_FROM_READ) {
|
||||
data = esp_efuse_utility_read_reg(blk, num_reg);
|
||||
}
|
||||
if (dump_type & ESP_EFUSE_TOKEN_FROM_STAGED) {
|
||||
data |= read_of_write_reg(blk, num_reg);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
static bool is_block_empty(esp_efuse_block_t blk, esp_efuse_token_type_t dump_type)
|
||||
{
|
||||
bool ret = true;
|
||||
int num_reg = 0;
|
||||
for (uintptr_t addr_rd_block = range_read_addr_blocks[blk].start; addr_rd_block <= range_read_addr_blocks[blk].end; addr_rd_block += 4, ++num_reg) {
|
||||
if (get_data(blk, num_reg, dump_type) != 0) {
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// -------------------------- Dump helpers ------------------------------
|
||||
|
||||
static esp_err_t output_chip_version(out_dump_ctx_t *out_ctx)
|
||||
{
|
||||
char str_chip_version[5];
|
||||
/* Encode chip version as a decimal string (major * 100 + minor).
|
||||
* The major and minor versions are stored in eFuse fields (typically 2-3 bits for major version).
|
||||
* Maximum major version is 7 (3 bits), allowing versions like "702" (7.02).
|
||||
* Buffer size of 5 chars safely accommodates up to 4 digits plus null terminator.
|
||||
* If the major version field is extended in the future, this function will still work
|
||||
* correctly since esp_log_util_cvt_dec() handles arbitrary values and the buffer has
|
||||
* sufficient capacity.
|
||||
*/
|
||||
esp_log_util_cvt_dec(efuse_hal_chip_revision(), 3, str_chip_version);
|
||||
return output(str_chip_version, out_ctx);
|
||||
}
|
||||
|
||||
static esp_err_t output_efuse_blocks(esp_efuse_token_type_t dump_type, out_dump_ctx_t *out_ctx)
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
b64_stream_t enc = { 0 };
|
||||
|
||||
for (esp_efuse_block_t blk = EFUSE_BLK0; blk < EFUSE_BLK_MAX; blk++) {
|
||||
if (!is_block_empty(blk, dump_type)) {
|
||||
int num_reg = 0;
|
||||
for (uintptr_t addr_rd_block = range_read_addr_blocks[blk].start; addr_rd_block <= range_read_addr_blocks[blk].end; addr_rd_block += 4, ++num_reg) {
|
||||
uint32_t data = get_data(blk, num_reg, dump_type);
|
||||
|
||||
ESP_EFUSE_CHK(b64_convert(&enc, &data, 1, out_ctx));
|
||||
}
|
||||
ESP_EFUSE_CHK(b64_flush(&enc, out_ctx));
|
||||
}
|
||||
if (blk != EFUSE_BLK_MAX - 1) {
|
||||
ESP_EFUSE_CHK(output(":", out_ctx));
|
||||
}
|
||||
}
|
||||
err_exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
static esp_err_t output_coding_error_data(out_dump_ctx_t *out_ctx)
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
uint32_t data[] = {
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
efuse_ll_get_coding_error(0)
|
||||
#elif CONFIG_IDF_TARGET_ESP32C2
|
||||
efuse_ll_get_coding_error(0),
|
||||
efuse_ll_get_coding_error(1)
|
||||
#elif CONFIG_IDF_TARGET_ESP32S31
|
||||
efuse_ll_get_coding_error(0),
|
||||
efuse_ll_get_coding_error(1),
|
||||
efuse_ll_get_coding_error(2),
|
||||
efuse_ll_get_coding_error(3),
|
||||
efuse_ll_get_coding_error(4),
|
||||
efuse_ll_get_coding_error(5),
|
||||
efuse_ll_get_coding_error(6),
|
||||
efuse_ll_get_coding_error(7),
|
||||
efuse_ll_get_coding_error(8),
|
||||
efuse_ll_get_coding_error(9)
|
||||
#else
|
||||
efuse_ll_get_coding_error(0),
|
||||
efuse_ll_get_coding_error(1),
|
||||
efuse_ll_get_coding_error(2),
|
||||
efuse_ll_get_coding_error(3),
|
||||
efuse_ll_get_coding_error(4),
|
||||
efuse_ll_get_coding_error(5),
|
||||
efuse_ll_get_coding_error(6)
|
||||
#endif
|
||||
};
|
||||
|
||||
bool all_zero = true;
|
||||
for (int i = 0; i < sizeof(data) / sizeof(data[0]); ++i) {
|
||||
if (data[i] != 0) {
|
||||
all_zero = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!all_zero) {
|
||||
b64_stream_t enc = { 0 };
|
||||
ESP_EFUSE_CHK(b64_convert(&enc, data, sizeof(data) / sizeof(data[0]), out_ctx));
|
||||
ESP_EFUSE_CHK(b64_flush(&enc, out_ctx));
|
||||
}
|
||||
err_exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
static esp_err_t output_crc32(out_dump_ctx_t *out_ctx)
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
b64_stream_t enc = { 0 };
|
||||
uint32_t crc = out_ctx->crc;
|
||||
ESP_EFUSE_CHK(b64_convert(&enc, &crc, 1, out_ctx));
|
||||
ESP_EFUSE_CHK(b64_flush(&enc, out_ctx));
|
||||
err_exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
static esp_err_t split_token(const char *token, const char **f4_blocks, const char **f6_crc)
|
||||
{
|
||||
// 6 fields: magic_str, chip, ver, blocks, cerr, crc
|
||||
const char *fields[] = { token, NULL, NULL, NULL, NULL, NULL };
|
||||
int field_counter = 1;
|
||||
unsigned number_of_blocks = 0;
|
||||
const unsigned block_start_position = 4;
|
||||
bool skip_blocks = false;
|
||||
for (; *token; ++token) {
|
||||
if (*token == ':') {
|
||||
if (field_counter >= sizeof(fields) / sizeof(fields[0])) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (skip_blocks) {
|
||||
if (number_of_blocks++ < EFUSE_BLK_MAX - 1) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
fields[field_counter++] = token + 1;
|
||||
if (field_counter == block_start_position) {
|
||||
skip_blocks = true;
|
||||
}
|
||||
}
|
||||
if (*token == '\0') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (field_counter != sizeof(fields) / sizeof(fields[0])) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
// *f1_magic = fields[0];
|
||||
// *f2_chip = fields[1];
|
||||
// *f3_ver = fields[2];
|
||||
*f4_blocks = fields[3];
|
||||
// *f5_cerr = fields[4];
|
||||
*f6_crc = fields[5];
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/* Base64URL is UNPADDED (no '=');
|
||||
* It maps a value char to its 6-bit value
|
||||
*/
|
||||
static unsigned char b64url_val(char c)
|
||||
{
|
||||
if (c >= 'A' && c <= 'Z') return (unsigned char)(c - 'A'); // 0..25
|
||||
if (c >= 'a' && c <= 'z') return (unsigned char)(c - 'a' + 26); // 26..51
|
||||
if (c >= '0' && c <= '9') return (unsigned char)(c - '0' + 52); // 52..61
|
||||
if (c == '-') return 62; // 62
|
||||
if (c == '_') return 63; // 63
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
// Decode one 24-bit chunk from 4 Base64URL chars.
|
||||
static uint32_t b64_quartet24(const char *p)
|
||||
{
|
||||
if (p == NULL) {
|
||||
return 0;
|
||||
}
|
||||
uint32_t v0 = b64url_val(p[0]);
|
||||
uint32_t v1 = b64url_val(p[1]);
|
||||
uint32_t v2 = b64url_val(p[2]);
|
||||
uint32_t v3 = b64url_val(p[3]);
|
||||
return (v0 << 18) | (v1 << 12) | (v2 << 6) | v3; // 24 useful bits
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the little-endian 32-bit word at idx position
|
||||
* Each 4 b64 chars -> 3 bytes. Find the quartet enclosing byte idx.
|
||||
*/
|
||||
static uint32_t b64_decode_u32(const char *in, unsigned idx)
|
||||
{
|
||||
const size_t q = (idx / 3) * 4; // starting quartet (char index)
|
||||
|
||||
// Decode two consecutive quartets (always sufficient to cover 4 bytes)
|
||||
const uint32_t q0 = b64_quartet24(&in[q]);
|
||||
const uint32_t b0 = ((q0 >> 16) & 0xFF);
|
||||
const uint32_t b1 = ((q0 >> 8) & 0xFF);
|
||||
const uint32_t b2 = ( q0 & 0xFF);
|
||||
|
||||
const uint32_t q1 = b64_quartet24(&in[q + 4]);
|
||||
const uint32_t c0 = ((q1 >> 16) & 0xFF);
|
||||
const uint32_t c1 = ((q1 >> 8) & 0xFF);
|
||||
const uint32_t c2 = ( q1 & 0xFF);
|
||||
|
||||
uint32_t out;
|
||||
const int r = idx % 3;
|
||||
if (r == 0) {
|
||||
// bytes: b0 b1 b2 c0
|
||||
out = b0 | (b1 << 8) | (b2 << 16) | (c0 << 24);
|
||||
} else if (r == 1) {
|
||||
// bytes: b1 b2 c0 c1
|
||||
out = b1 | (b2 << 8) | (c0 << 16) | (c1 << 24);
|
||||
} else { // r == 2
|
||||
// bytes: b2 c0 c1 c2
|
||||
out = b2 | (c0 << 8) | (c1 << 16) | (c2 << 24);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static bool efuse_block_can_be_written(esp_efuse_block_t blk, esp_efuse_coding_scheme_t coding_scheme)
|
||||
{
|
||||
if (coding_scheme == EFUSE_CODING_SCHEME_NONE) {
|
||||
bool blk_can_be_written = true;
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
if (blk == EFUSE_BLK_KEY0 || blk == EFUSE_BLK_KEY1) {
|
||||
blk_can_be_written = esp_efuse_key_block_unused(blk);
|
||||
} else if (blk == EFUSE_BLK3) {
|
||||
blk_can_be_written = !esp_efuse_get_key_dis_write(blk);
|
||||
}
|
||||
#endif // CONFIG_IDF_TARGET_ESP32
|
||||
return blk_can_be_written;
|
||||
} else {
|
||||
return (blk >= EFUSE_BLK_KEY0 && blk < EFUSE_BLK_KEY_MAX)
|
||||
? esp_efuse_key_block_unused(blk)
|
||||
: is_block_empty(blk, ESP_EFUSE_TOKEN_FROM_READ);
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t validate_chip_version(const char *token_in, bool ignore_ver, unsigned int *out_len)
|
||||
{
|
||||
// Parse version from token (up to 4 digits)
|
||||
char token_version[5] = { 0 };
|
||||
unsigned int len = 0;
|
||||
while (len < 4 && token_in[len] != ':' && token_in[len] != '\0') {
|
||||
token_version[len] = token_in[len];
|
||||
len++;
|
||||
}
|
||||
|
||||
if (len < 3) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (!ignore_ver) {
|
||||
// Compare major versions (divide by 100)
|
||||
unsigned int token_major = atoi(token_version) / 100;
|
||||
if (token_major != efuse_hal_get_major_chip_version()) { // TODO: use esp_chip_revision()
|
||||
return ESP_ERR_INVALID_VERSION;
|
||||
}
|
||||
}
|
||||
|
||||
*out_len = len;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t validate_token(const char *token_in, bool ignore_ver, const char **b64_blocks)
|
||||
{
|
||||
if (!token_in || !b64_blocks) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
const char *b64_crc;
|
||||
if (split_token(token_in, b64_blocks, &b64_crc) != ESP_OK) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
unsigned pos = 0;
|
||||
unsigned len = sizeof("EFSW") - 1;
|
||||
if (strncmp(&token_in[pos], "EFSW", len) != 0) { // magic check
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
pos += len + 1; // skip ':'
|
||||
|
||||
len = sizeof(CONFIG_IDF_TARGET) - 1;
|
||||
if (strncmp(&token_in[pos], CONFIG_IDF_TARGET, len) != 0 || token_in[pos + len] != ':') {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
pos += len + 1; // skip ':'
|
||||
|
||||
esp_err_t err = validate_chip_version(&token_in[pos], ignore_ver, &len);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
pos += len + 1; // skip ':'
|
||||
|
||||
const int token_len = strlen(token_in) - 6; // len of token without crc part
|
||||
if (esp_rom_crc32_le(0, (const uint8_t *)token_in, token_len) != b64_decode_u32(b64_crc, 0)) {
|
||||
return ESP_ERR_INVALID_CRC;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// ESSR:chip_name:chip_version:b64_bock0:b64_bock1:...:b64_bock10:b64_errors:b64_crc32
|
||||
esp_err_t esp_efuse_token_dump(esp_efuse_token_type_t dump_type, char *buf, size_t buf_len)
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
bool log_output = (buf == NULL);
|
||||
|
||||
out_dump_ctx_t out_ctx = {
|
||||
.crc = 0,
|
||||
.buf = buf,
|
||||
.buf_len = buf_len,
|
||||
};
|
||||
|
||||
// 1. token name
|
||||
const char *efs_marker[3] = {
|
||||
"EFSR",
|
||||
"EFSW",
|
||||
"EFSRW",
|
||||
};
|
||||
if (dump_type == 0 || dump_type > (ESP_EFUSE_TOKEN_FROM_READ | ESP_EFUSE_TOKEN_FROM_STAGED)) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
#if !CONFIG_EFUSE_ENABLE_STAGED_TOKEN_API
|
||||
if (dump_type & ESP_EFUSE_TOKEN_FROM_STAGED) {
|
||||
ESP_LOGW(TAG, "Staged token support is disabled because it can expose "
|
||||
"keys in plaintext before burn/read-protect. Enable "
|
||||
"CONFIG_EFUSE_ENABLE_STAGED_TOKEN_API to use staged "
|
||||
"eFuse token dump modes.");
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
#endif
|
||||
if (log_output) {
|
||||
esp_log_impl_lock();
|
||||
}
|
||||
ESP_EFUSE_CHK(output(efs_marker[dump_type - 1], &out_ctx));
|
||||
|
||||
// 2. chip_name
|
||||
ESP_EFUSE_CHK(output(":" CONFIG_IDF_TARGET ":", &out_ctx));
|
||||
|
||||
// 3. chip_version
|
||||
ESP_EFUSE_CHK(output_chip_version(&out_ctx));
|
||||
|
||||
// 4. b64_efuse_blocks
|
||||
ESP_EFUSE_CHK(output(":", &out_ctx));
|
||||
ESP_EFUSE_CHK(output_efuse_blocks(dump_type, &out_ctx));
|
||||
|
||||
// 5. b64_coding_error_data
|
||||
ESP_EFUSE_CHK(output(":", &out_ctx));
|
||||
ESP_EFUSE_CHK(output_coding_error_data(&out_ctx));
|
||||
|
||||
// 6. b64_crc32
|
||||
ESP_EFUSE_CHK(output(":", &out_ctx));
|
||||
ESP_EFUSE_CHK(output_crc32(&out_ctx));
|
||||
|
||||
ESP_EFUSE_CHK(output("\0", &out_ctx));
|
||||
|
||||
err_exit:
|
||||
if (log_output) {
|
||||
esp_log_impl_unlock();
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t esp_efuse_token_burn(const char *token_in, bool ignore_ver)
|
||||
{
|
||||
const char *b64_blocks;
|
||||
esp_err_t err = validate_token(token_in, ignore_ver, &b64_blocks);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_efuse_lock_acquire();
|
||||
if (s_batch_writing_mode == 0) {
|
||||
esp_efuse_utility_reset();
|
||||
}
|
||||
|
||||
int idx = 0;
|
||||
for (esp_efuse_block_t blk = EFUSE_BLK0; blk < EFUSE_BLK_MAX; blk++) {
|
||||
if (b64_blocks[idx] == ':') {
|
||||
idx++;
|
||||
if (b64_blocks[idx] == ':') {
|
||||
continue; // dump for current block is empty, skip it.
|
||||
}
|
||||
}
|
||||
esp_efuse_coding_scheme_t coding_scheme = esp_efuse_get_coding_scheme(blk);
|
||||
bool blk_can_be_written = efuse_block_can_be_written(blk, coding_scheme);
|
||||
int num_reg = 0;
|
||||
for (uintptr_t a = range_write_addr_blocks[blk].start; a <= range_write_addr_blocks[blk].end; a += 4, ++num_reg) {
|
||||
uint32_t reg_to_write = b64_decode_u32(&b64_blocks[idx], num_reg * 4);
|
||||
if (reg_to_write == 0) {
|
||||
continue;
|
||||
}
|
||||
if (blk_can_be_written) {
|
||||
if (coding_scheme == EFUSE_CODING_SCHEME_NONE) {
|
||||
reg_to_write &= ~esp_efuse_utility_read_reg(blk, num_reg); // remove already set bits
|
||||
}
|
||||
err = esp_efuse_utility_write_reg(blk, num_reg, reg_to_write);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "eFuse BLOCK%d is not empty. Skip updating it from token dump.", blk);
|
||||
err = ESP_FAIL;
|
||||
}
|
||||
if (err != ESP_OK) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
idx += (16 * num_reg + 2) / 3;
|
||||
if (err != ESP_OK) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (s_batch_writing_mode == 0) {
|
||||
if (err == ESP_OK) {
|
||||
err = esp_efuse_utility_apply_new_coding_scheme();
|
||||
if (err == ESP_OK) {
|
||||
err = esp_efuse_utility_burn_efuses();
|
||||
}
|
||||
}
|
||||
esp_efuse_utility_reset();
|
||||
}
|
||||
esp_efuse_lock_release();
|
||||
return err;
|
||||
}
|
||||
@@ -883,3 +883,109 @@ TEST_CASE("Test deferred WR_DIS programming", "[efuse]")
|
||||
TEST_ASSERT_TRUE(esp_efuse_get_key_dis_write(EFUSE_BLK_KEY0));
|
||||
TEST_ASSERT_TRUE(esp_efuse_get_key_dis_read(EFUSE_BLK_KEY0));
|
||||
}
|
||||
|
||||
TEST_CASE("Test token dump", "[efuse]")
|
||||
{
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32
|
||||
const char *valid_token = "EFSW:esp32:300:AAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA:AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA:oKGio6SlpqeoqaqrrK2ur7CxsrO0tba3uLm6u7y9vr8:::x8WPiQ";
|
||||
#elif CONFIG_IDF_TARGET_ESP32C2
|
||||
const char *valid_token = "EFSW:esp32c2:000:gAAAAAEEAAA::AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA:v769vLu6ubi3trW0s7KxsK-urayrqqmop6alpKOioaA::4d9CPw";
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||
const char *valid_token = "EFSW:esp32c3:004:AAGAAAEAAAAAAAAEAAAAAAAAAAAAAAAA::AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA::v769vLu6ubi3trW0s7KxsK-urayrqqmop6alpKOioaA::::::::n9AtsQ";
|
||||
#elif CONFIG_IDF_TARGET_LINUX
|
||||
const char *valid_token = "EFSW:linux:000:AAGAAAEAAAAAAAAEAAAAAAAAAAAAAAAA::AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA::v769vLu6ubi3trW0s7KxsK-urayrqqmop6alpKOioaA::::::::dq_wIQ";
|
||||
#else
|
||||
const char *valid_token = NULL;
|
||||
return;
|
||||
#endif
|
||||
|
||||
esp_efuse_utility_reset();
|
||||
esp_efuse_utility_erase_virt_blocks();
|
||||
|
||||
esp_rom_printf("Initial read token:\n");
|
||||
esp_efuse_token_dump(ESP_EFUSE_TOKEN_FROM_READ, NULL, 0);
|
||||
|
||||
TEST_ESP_OK(esp_efuse_batch_write_begin());
|
||||
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_efuse_token_burn(NULL, true));
|
||||
|
||||
char test_token[256];
|
||||
|
||||
printf(" - Test bad header\n");
|
||||
strcpy(test_token, valid_token);
|
||||
test_token[0] = 'X';
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_efuse_token_burn(test_token, true));
|
||||
|
||||
printf(" - Test not allowed token type\n");
|
||||
strcpy(test_token, valid_token);
|
||||
test_token[3] = 'R';
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_efuse_token_burn(test_token, false));
|
||||
|
||||
printf(" - Test bad chip name\n");
|
||||
strcpy(test_token, valid_token);
|
||||
char *chip_name_start = strchr(test_token, ':') + 1;
|
||||
chip_name_start[0] = 'X';
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_efuse_token_burn(test_token, true));
|
||||
|
||||
printf(" - Test chip name prefix only\n");
|
||||
strcpy(test_token, valid_token);
|
||||
chip_name_start = strchr(test_token, ':') + 1;
|
||||
char *chip_name_end = strchr(chip_name_start, ':');
|
||||
memmove(chip_name_end + 2, chip_name_end, strlen(chip_name_end) + 1);
|
||||
memcpy(chip_name_end, "X9", 2);
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_efuse_token_burn(test_token, true));
|
||||
|
||||
printf(" - Test bad version\n");
|
||||
strcpy(test_token, valid_token);
|
||||
chip_name_start = strchr(test_token, ':') + 1;
|
||||
char *version_start = strchr(chip_name_start, ':') + 1;
|
||||
memcpy(version_start, "999", 3);
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_VERSION, esp_efuse_token_burn(test_token, false));
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_CRC, esp_efuse_token_burn(test_token, true));
|
||||
|
||||
printf(" - Test missing version\n");
|
||||
memcpy(version_start, ":", 1);
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_efuse_token_burn(test_token, true));
|
||||
|
||||
printf(" - Test short version\n");
|
||||
memcpy(version_start, "99:", 3);
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_efuse_token_burn(test_token, true));
|
||||
|
||||
printf(" - Test non-numeric version\n");
|
||||
memcpy(version_start, "0A0", 3);
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_CRC, esp_efuse_token_burn(test_token, true));
|
||||
|
||||
printf(" - Test 4-digit version\n");
|
||||
memcpy(version_start, "1000", 4);
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_efuse_token_burn(test_token, true));
|
||||
|
||||
printf(" - Test bad CRC\n");
|
||||
strcpy(test_token, valid_token);
|
||||
test_token[strlen(test_token) - 3] = 'X';
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_CRC, esp_efuse_token_burn(test_token, true));
|
||||
|
||||
printf(" - Test corrupted first data block\n");
|
||||
strcpy(test_token, valid_token);
|
||||
char *first_block_start = strchr(version_start, ':') + 1;
|
||||
first_block_start = strchr(first_block_start, ':') + 1;
|
||||
first_block_start = strchr(first_block_start, ':') + 1;
|
||||
char *first_block_end = strchr(first_block_start, ':');
|
||||
memset(first_block_start, ':', first_block_end - first_block_start);
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_efuse_token_burn(test_token, false));
|
||||
|
||||
printf(" - Test valid token burn\n");
|
||||
esp_rom_printf("BURN token:\n%s\n", valid_token);
|
||||
TEST_ESP_OK(esp_efuse_token_burn(valid_token, true)); // ignore chip version
|
||||
esp_rom_printf("Staged token:\n");
|
||||
esp_rom_printf(" - from console:\n");
|
||||
TEST_ESP_OK(esp_efuse_token_dump(ESP_EFUSE_TOKEN_FROM_STAGED, NULL, 0));
|
||||
esp_rom_printf(" - from buffer:\n");
|
||||
char token_ready_burn[256];
|
||||
TEST_ESP_OK(esp_efuse_token_dump(ESP_EFUSE_TOKEN_FROM_STAGED, token_ready_burn, sizeof(token_ready_burn)));
|
||||
esp_rom_printf("%s\n", token_ready_burn);
|
||||
// cut off crc part and starting part to exclude chip version from comparison
|
||||
const int b64_crc_len = 6;
|
||||
TEST_ASSERT_EQUAL_STRING_LEN_MESSAGE(&valid_token[16], &token_ready_burn[16], strlen(&valid_token[16]) - b64_crc_len, "Tokens mismatch");
|
||||
|
||||
TEST_ESP_OK(esp_efuse_batch_write_cancel());
|
||||
}
|
||||
|
||||
@@ -8,3 +8,4 @@ CONFIG_COMPILER_STACK_CHECK=y
|
||||
CONFIG_ESP_TASK_WDT_INIT=n
|
||||
|
||||
CONFIG_EFUSE_VIRTUAL=y
|
||||
CONFIG_EFUSE_ENABLE_STAGED_TOKEN_API=y
|
||||
|
||||
@@ -180,6 +180,12 @@ __attribute__((always_inline)) static inline uint32_t efuse_ll_get_adc2_tp_high(
|
||||
return EFUSE.blk3_rdata3.rd_adc2_tp_high;
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) static inline uint32_t efuse_ll_get_coding_error(unsigned index)
|
||||
{
|
||||
(void) index;
|
||||
return EFUSE.dec_status.val;
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) static inline bool efuse_ll_get_dec_warnings(unsigned block)
|
||||
{
|
||||
if (block == 0 || block > 4) {
|
||||
|
||||
@@ -149,6 +149,14 @@ __attribute__((always_inline)) static inline uint32_t efuse_ll_get_rtc_ldo_act_d
|
||||
return EFUSE.rd_blk2_data5.rtc_ldo_act_dbias13;
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) static inline uint32_t efuse_ll_get_coding_error(unsigned index)
|
||||
{
|
||||
if (index == 0) {
|
||||
return EFUSE.rd_repeat_err.val;
|
||||
}
|
||||
return EFUSE.rd_rs_err.val;
|
||||
}
|
||||
|
||||
/******************* eFuse control functions *************************/
|
||||
|
||||
__attribute__((always_inline)) static inline bool efuse_ll_get_read_cmd(void)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -124,6 +124,28 @@ __attribute__((always_inline)) static inline uint32_t efuse_ll_get_dig_dbias_hvt
|
||||
return EFUSE.rd_mac_spi_sys_5.dig_dbias_hvt;
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) static inline uint32_t efuse_ll_get_coding_error(unsigned index)
|
||||
{
|
||||
switch (index) {
|
||||
case 0:
|
||||
return EFUSE.rd_repeat_err0.val;
|
||||
case 1:
|
||||
return EFUSE.rd_repeat_err1.val;
|
||||
case 2:
|
||||
return EFUSE.rd_repeat_err2.val;
|
||||
case 3:
|
||||
return EFUSE.rd_repeat_err3.val;
|
||||
case 4:
|
||||
return EFUSE.rd_repeat_err4.val;
|
||||
case 5:
|
||||
return EFUSE.rd_rs_err0.val;
|
||||
case 6:
|
||||
return EFUSE.rd_rs_err1.val;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/******************* eFuse control functions *************************/
|
||||
|
||||
__attribute__((always_inline)) static inline bool efuse_ll_get_read_cmd(void)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -139,6 +139,28 @@ __attribute__((always_inline)) static inline uint32_t efuse_ll_get_recovery_boot
|
||||
return (EFUSE.rd_repeat_data2.recovery_bootloader_flash_sector_hi << 9) | EFUSE.rd_repeat_data4.recovery_bootloader_flash_sector_lo;
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) static inline uint32_t efuse_ll_get_coding_error(unsigned index)
|
||||
{
|
||||
switch (index) {
|
||||
case 0:
|
||||
return EFUSE.rd_repeat_data_err0.val;
|
||||
case 1:
|
||||
return EFUSE.rd_repeat_data_err1.val;
|
||||
case 2:
|
||||
return EFUSE.rd_repeat_data_err2.val;
|
||||
case 3:
|
||||
return EFUSE.rd_repeat_data_err3.val;
|
||||
case 4:
|
||||
return EFUSE.rd_repeat_data_err4.val;
|
||||
case 5:
|
||||
return EFUSE.rd_rs_data_err0.val;
|
||||
case 6:
|
||||
return EFUSE.rd_rs_data_err1.val;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/******************* eFuse control functions *************************/
|
||||
|
||||
__attribute__((always_inline)) static inline bool efuse_ll_get_read_cmd(void)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -132,6 +132,28 @@ __attribute__((always_inline)) static inline uint32_t efuse_ll_get_ocode(void)
|
||||
return EFUSE.rd_sys_part1_data4.ocode;
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) static inline uint32_t efuse_ll_get_coding_error(unsigned index)
|
||||
{
|
||||
switch (index) {
|
||||
case 0:
|
||||
return EFUSE.rd_repeat_err0.val;
|
||||
case 1:
|
||||
return EFUSE.rd_repeat_err1.val;
|
||||
case 2:
|
||||
return EFUSE.rd_repeat_err2.val;
|
||||
case 3:
|
||||
return EFUSE.rd_repeat_err3.val;
|
||||
case 4:
|
||||
return EFUSE.rd_repeat_err4.val;
|
||||
case 5:
|
||||
return EFUSE.rd_rs_err0.val;
|
||||
case 6:
|
||||
return EFUSE.rd_rs_err1.val;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/******************* eFuse control functions *************************/
|
||||
|
||||
__attribute__((always_inline)) static inline bool efuse_ll_get_read_cmd(void)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -144,6 +144,28 @@ __attribute__((always_inline)) static inline uint32_t efuse_ll_get_recovery_boot
|
||||
return EFUSE0.rd_repeat_data3.recovery_bootloader_flash_sector;
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) static inline uint32_t efuse_ll_get_coding_error(unsigned index)
|
||||
{
|
||||
switch (index) {
|
||||
case 0:
|
||||
return EFUSE0.rd_repeat_data_err0.val;
|
||||
case 1:
|
||||
return EFUSE0.rd_repeat_data_err1.val;
|
||||
case 2:
|
||||
return EFUSE0.rd_repeat_data_err2.val;
|
||||
case 3:
|
||||
return EFUSE0.rd_repeat_data_err3.val;
|
||||
case 4:
|
||||
return EFUSE0.rd_repeat_data_err4.val;
|
||||
case 5:
|
||||
return EFUSE0.rd_rs_data_err0.val;
|
||||
case 6:
|
||||
return EFUSE0.rd_rs_data_err1.val;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/******************* eFuse control functions *************************/
|
||||
|
||||
__attribute__((always_inline)) static inline bool efuse_ll_get_read_cmd(void)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -113,6 +113,28 @@ __attribute__((always_inline)) static inline uint32_t efuse_ll_get_ecdsa_key_blk
|
||||
return EFUSE.conf.cfg_ecdsa_blk;
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) static inline uint32_t efuse_ll_get_coding_error(unsigned index)
|
||||
{
|
||||
switch (index) {
|
||||
case 0:
|
||||
return EFUSE.rd_repeat_err0.val;
|
||||
case 1:
|
||||
return EFUSE.rd_repeat_err1.val;
|
||||
case 2:
|
||||
return EFUSE.rd_repeat_err2.val;
|
||||
case 3:
|
||||
return EFUSE.rd_repeat_err3.val;
|
||||
case 4:
|
||||
return EFUSE.rd_repeat_err4.val;
|
||||
case 5:
|
||||
return EFUSE.rd_rs_err0.val;
|
||||
case 6:
|
||||
return EFUSE.rd_rs_err1.val;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/******************* eFuse control functions *************************/
|
||||
|
||||
__attribute__((always_inline)) static inline bool efuse_ll_get_read_cmd(void)
|
||||
|
||||
@@ -98,6 +98,28 @@ __attribute__((always_inline)) static inline uint32_t efuse_ll_get_ecdsa_key_hi_
|
||||
return EFUSE.status.cur_ecdsa_h_blk;
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) static inline uint32_t efuse_ll_get_coding_error(unsigned index)
|
||||
{
|
||||
switch (index) {
|
||||
case 0:
|
||||
return EFUSE.rd_repeat_data_err0.val;
|
||||
case 1:
|
||||
return EFUSE.rd_repeat_data_err1.val;
|
||||
case 2:
|
||||
return EFUSE.rd_repeat_data_err2.val;
|
||||
case 3:
|
||||
return EFUSE.rd_repeat_data_err3.val;
|
||||
case 4:
|
||||
return EFUSE.rd_repeat_data_err4.val;
|
||||
case 5:
|
||||
return EFUSE.rd_rs_data_err0.val;
|
||||
case 6:
|
||||
return EFUSE.rd_rs_data_err1.val;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/******************* eFuse control functions *************************/
|
||||
|
||||
__attribute__((always_inline)) static inline bool efuse_ll_get_read_cmd(void)
|
||||
|
||||
@@ -97,6 +97,28 @@ __attribute__((always_inline)) static inline uint32_t efuse_ll_get_ecdsa_key_blk
|
||||
return EFUSE.ecdsa.cur_ecdsa_p256_blk;
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) static inline uint32_t efuse_ll_get_coding_error(unsigned index)
|
||||
{
|
||||
switch (index) {
|
||||
case 0:
|
||||
return EFUSE.rd_repeat_data_err0.val;
|
||||
case 1:
|
||||
return EFUSE.rd_repeat_data_err1.val;
|
||||
case 2:
|
||||
return EFUSE.rd_repeat_data_err2.val;
|
||||
case 3:
|
||||
return EFUSE.rd_repeat_data_err3.val;
|
||||
case 4:
|
||||
return EFUSE.rd_repeat_data_err4.val;
|
||||
case 5:
|
||||
return EFUSE.rd_rs_data_err0.val;
|
||||
case 6:
|
||||
return EFUSE.rd_rs_data_err1.val;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/******************* eFuse control functions *************************/
|
||||
|
||||
__attribute__((always_inline)) static inline bool efuse_ll_get_read_cmd(void)
|
||||
|
||||
@@ -132,6 +132,28 @@ __attribute__((always_inline)) static inline uint32_t efuse_ll_get_recovery_boot
|
||||
}
|
||||
#endif
|
||||
|
||||
__attribute__((always_inline)) static inline uint32_t efuse_ll_get_coding_error(unsigned index)
|
||||
{
|
||||
switch (index) {
|
||||
case 0:
|
||||
return EFUSE.rd_repeat_err0.val;
|
||||
case 1:
|
||||
return EFUSE.rd_repeat_err1.val;
|
||||
case 2:
|
||||
return EFUSE.rd_repeat_err2.val;
|
||||
case 3:
|
||||
return EFUSE.rd_repeat_err3.val;
|
||||
case 4:
|
||||
return EFUSE.rd_repeat_err4.val;
|
||||
case 5:
|
||||
return EFUSE.rd_rs_err0.val;
|
||||
case 6:
|
||||
return EFUSE.rd_rs_err1.val;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/******************* eFuse control functions *************************/
|
||||
|
||||
__attribute__((always_inline)) static inline bool efuse_ll_get_read_cmd(void)
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "soc/soc_caps.h"
|
||||
#include "hal/efuse_ll.h"
|
||||
#include_next "hal/efuse_hal.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -121,6 +121,28 @@ __attribute__((always_inline)) static inline uint32_t efuse_ll_get_ocode(void)
|
||||
return (((EFUSE.rd_sys_part1_data4.val >> 16) & 0x7) << 4) + (EFUSE.rd_sys_part1_data4.val & 0xF);
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) static inline uint32_t efuse_ll_get_coding_error(unsigned index)
|
||||
{
|
||||
switch (index) {
|
||||
case 0:
|
||||
return EFUSE.rd_repeat_err0.val;
|
||||
case 1:
|
||||
return EFUSE.rd_repeat_err1.val;
|
||||
case 2:
|
||||
return EFUSE.rd_repeat_err2.val;
|
||||
case 3:
|
||||
return EFUSE.rd_repeat_err3.val;
|
||||
case 4:
|
||||
return EFUSE.rd_repeat_err4.val;
|
||||
case 5:
|
||||
return EFUSE.rd_rs_err0.val;
|
||||
case 6:
|
||||
return EFUSE.rd_rs_err1.val;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/******************* eFuse control functions *************************/
|
||||
|
||||
__attribute__((always_inline)) static inline bool efuse_ll_get_read_cmd(void)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -124,6 +124,28 @@ __attribute__((always_inline)) static inline uint32_t efuse_ll_get_dig_dbias_hvt
|
||||
return EFUSE.rd_mac_spi_sys_5.dig_dbias_hvt;
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) static inline uint32_t efuse_ll_get_coding_error(unsigned index)
|
||||
{
|
||||
switch (index) {
|
||||
case 0:
|
||||
return EFUSE.rd_repeat_err0.val;
|
||||
case 1:
|
||||
return EFUSE.rd_repeat_err1.val;
|
||||
case 2:
|
||||
return EFUSE.rd_repeat_err2.val;
|
||||
case 3:
|
||||
return EFUSE.rd_repeat_err3.val;
|
||||
case 4:
|
||||
return EFUSE.rd_repeat_err4.val;
|
||||
case 5:
|
||||
return EFUSE.rd_rs_err0.val;
|
||||
case 6:
|
||||
return EFUSE.rd_rs_err1.val;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/******************* eFuse control functions *************************/
|
||||
|
||||
__attribute__((always_inline)) static inline bool efuse_ll_get_read_cmd(void)
|
||||
|
||||
@@ -99,6 +99,34 @@ __attribute__((always_inline)) static inline uint32_t efuse_ll_get_recovery_boot
|
||||
return EFUSE.rd_repeat_data5.recovery_bootloader_flash_sector;
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) static inline uint32_t efuse_ll_get_coding_error(unsigned index)
|
||||
{
|
||||
switch (index) {
|
||||
case 0:
|
||||
return EFUSE.rd_repeat_data_err0.val;
|
||||
case 1:
|
||||
return EFUSE.rd_repeat_data_err1.val;
|
||||
case 2:
|
||||
return EFUSE.rd_repeat_data_err2.val;
|
||||
case 3:
|
||||
return EFUSE.rd_repeat_data_err3.val;
|
||||
case 4:
|
||||
return EFUSE.rd_repeat_data_err4.val;
|
||||
case 5:
|
||||
return EFUSE.rd_repeat_data_err5.val;
|
||||
case 6:
|
||||
return EFUSE.rd_repeat_data_err6.val;
|
||||
case 7:
|
||||
return EFUSE.rd_repeat_data_err7.val;
|
||||
case 8:
|
||||
return EFUSE.rd_rs_data_err0.val;
|
||||
case 9:
|
||||
return EFUSE.rd_rs_data_err1.val;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/******************* eFuse control functions *************************/
|
||||
|
||||
__attribute__((always_inline)) static inline bool efuse_ll_get_read_cmd(void)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -117,6 +117,12 @@ __attribute__((always_inline)) static inline uint32_t efuse_ll_get_dig_dbias_hvt
|
||||
return 0;
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) static inline uint32_t efuse_ll_get_coding_error(unsigned index)
|
||||
{
|
||||
(void) index;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/******************* eFuse control functions *************************/
|
||||
|
||||
__attribute__((always_inline)) static inline bool efuse_ll_get_read_cmd(void)
|
||||
|
||||
@@ -4073,16 +4073,16 @@ typedef struct {
|
||||
volatile efuse_rd_sys_part2_data5_reg_t rd_sys_part2_data5;
|
||||
volatile efuse_rd_sys_part2_data6_reg_t rd_sys_part2_data6;
|
||||
volatile efuse_rd_sys_part2_data7_reg_t rd_sys_part2_data7;
|
||||
volatile efuse_rd_repeat_data_err0_reg_t rd_repeat_data_err0;
|
||||
volatile efuse_rd_repeat_data_err1_reg_t rd_repeat_data_err1;
|
||||
volatile efuse_rd_repeat_data_err2_reg_t rd_repeat_data_err2;
|
||||
volatile efuse_rd_repeat_data_err3_reg_t rd_repeat_data_err3;
|
||||
volatile efuse_rd_repeat_data_err4_reg_t rd_repeat_data_err4;
|
||||
volatile efuse_rd_repeat_data_err0_reg_t rd_repeat_err0;
|
||||
volatile efuse_rd_repeat_data_err1_reg_t rd_repeat_err1;
|
||||
volatile efuse_rd_repeat_data_err2_reg_t rd_repeat_err2;
|
||||
volatile efuse_rd_repeat_data_err3_reg_t rd_repeat_err3;
|
||||
volatile efuse_rd_repeat_data_err4_reg_t rd_repeat_err4;
|
||||
uint32_t reserved_190[8];
|
||||
volatile efuse_ecdsa_reg_t ecdsa;
|
||||
uint32_t reserved_1b4[3];
|
||||
volatile efuse_rd_rs_data_err0_reg_t rd_rs_data_err0;
|
||||
volatile efuse_rd_rs_data_err1_reg_t rd_rs_data_err1;
|
||||
volatile efuse_rd_rs_data_err0_reg_t rd_rs_err0;
|
||||
volatile efuse_rd_rs_data_err1_reg_t rd_rs_err1;
|
||||
volatile efuse_clk_reg_t clk;
|
||||
volatile efuse_conf_reg_t conf;
|
||||
volatile efuse_status_reg_t status;
|
||||
|
||||
Reference in New Issue
Block a user