From 885097762fa4c8c43b2b2103766341b24bd32a57 Mon Sep 17 00:00:00 2001 From: Konstantin Kondrashov Date: Mon, 8 Sep 2025 10:17:16 +0300 Subject: [PATCH] feat(efuse): Support efuse token dump - efuse token dump is compatible with espefuse tool - EFSW dump can be burned on chip with esp_efuse_token_burn() --- components/efuse/CMakeLists.txt | 1 + components/efuse/Kconfig | 18 + components/efuse/include/esp_efuse.h | 106 ++++ .../efuse/private_include/esp_efuse_utility.h | 9 +- components/efuse/src/esp_efuse_api.c | 47 +- components/efuse/src/esp_efuse_dump.c | 583 ++++++++++++++++++ components/efuse/test_apps/main/test_efuse.c | 106 ++++ components/efuse/test_apps/sdkconfig.defaults | 1 + components/hal/esp32/include/hal/efuse_ll.h | 6 + components/hal/esp32c2/include/hal/efuse_ll.h | 8 + components/hal/esp32c3/include/hal/efuse_ll.h | 24 +- components/hal/esp32c5/include/hal/efuse_ll.h | 24 +- components/hal/esp32c6/include/hal/efuse_ll.h | 24 +- .../hal/esp32c61/include/hal/efuse_ll.h | 24 +- components/hal/esp32h2/include/hal/efuse_ll.h | 24 +- .../hal/esp32h21/include/hal/efuse_ll.h | 22 + components/hal/esp32h4/include/hal/efuse_ll.h | 22 + components/hal/esp32p4/include/hal/efuse_ll.h | 22 + .../hal/esp32s2/include/hal/efuse_hal.h | 1 + components/hal/esp32s2/include/hal/efuse_ll.h | 24 +- components/hal/esp32s3/include/hal/efuse_ll.h | 24 +- .../hal/esp32s31/include/hal/efuse_ll.h | 28 + components/hal/linux/include/hal/efuse_ll.h | 8 +- .../register/hw_ver3/soc/efuse_struct.h | 14 +- docs/en/api-guides/tools/idf-monitor.rst | 40 ++ docs/en/api-reference/system/efuse.rst | 208 +++++++ docs/zh_CN/api-guides/tools/idf-monitor.rst | 40 ++ docs/zh_CN/api-reference/system/efuse.rst | 208 ++++++- examples/system/efuse/README.md | 216 ++++++- examples/system/efuse/main/efuse_main.c | 26 +- examples/system/efuse/sdkconfig.defaults | 1 + 31 files changed, 1844 insertions(+), 65 deletions(-) create mode 100644 components/efuse/src/esp_efuse_dump.c diff --git a/components/efuse/CMakeLists.txt b/components/efuse/CMakeLists.txt index 28265fa6666..38517b25842 100644 --- a/components/efuse/CMakeLists.txt +++ b/components/efuse/CMakeLists.txt @@ -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") diff --git a/components/efuse/Kconfig b/components/efuse/Kconfig index 2e54a2443b2..1fe06b14123 100644 --- a/components/efuse/Kconfig +++ b/components/efuse/Kconfig @@ -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 diff --git a/components/efuse/include/esp_efuse.h b/components/efuse/include/esp_efuse.h index 94cd0ba215d..e610d5c054f 100644 --- a/components/efuse/include/esp_efuse.h +++ b/components/efuse/include/esp_efuse.h @@ -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 diff --git a/components/efuse/private_include/esp_efuse_utility.h b/components/efuse/private_include/esp_efuse_utility.h index 524f314bd7a..b10d93c05d9 100644 --- a/components/efuse/private_include/esp_efuse_utility.h +++ b/components/efuse/private_include/esp_efuse_utility.h @@ -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. * diff --git a/components/efuse/src/esp_efuse_api.c b/components/efuse/src/esp_efuse_api.c index fba14345777..97ee0a82424 100644 --- a/components/efuse/src/esp_efuse_api.c +++ b/components/efuse/src/esp_efuse_api.c @@ -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 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; } diff --git a/components/efuse/src/esp_efuse_dump.c b/components/efuse/src/esp_efuse_dump.c new file mode 100644 index 00000000000..a1d049e4ab4 --- /dev/null +++ b/components/efuse/src/esp_efuse_dump.c @@ -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; +} diff --git a/components/efuse/test_apps/main/test_efuse.c b/components/efuse/test_apps/main/test_efuse.c index 3ec6bd6fe42..c07f4f2b618 100644 --- a/components/efuse/test_apps/main/test_efuse.c +++ b/components/efuse/test_apps/main/test_efuse.c @@ -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()); +} diff --git a/components/efuse/test_apps/sdkconfig.defaults b/components/efuse/test_apps/sdkconfig.defaults index 64e3813c7ce..405131d8e62 100644 --- a/components/efuse/test_apps/sdkconfig.defaults +++ b/components/efuse/test_apps/sdkconfig.defaults @@ -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 diff --git a/components/hal/esp32/include/hal/efuse_ll.h b/components/hal/esp32/include/hal/efuse_ll.h index ae0662827c9..e7ee6aa5998 100644 --- a/components/hal/esp32/include/hal/efuse_ll.h +++ b/components/hal/esp32/include/hal/efuse_ll.h @@ -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) { diff --git a/components/hal/esp32c2/include/hal/efuse_ll.h b/components/hal/esp32c2/include/hal/efuse_ll.h index 8ab1ce4a34a..2524106dcbf 100644 --- a/components/hal/esp32c2/include/hal/efuse_ll.h +++ b/components/hal/esp32c2/include/hal/efuse_ll.h @@ -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) diff --git a/components/hal/esp32c3/include/hal/efuse_ll.h b/components/hal/esp32c3/include/hal/efuse_ll.h index e0893b00d44..c775bb830b6 100644 --- a/components/hal/esp32c3/include/hal/efuse_ll.h +++ b/components/hal/esp32c3/include/hal/efuse_ll.h @@ -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) diff --git a/components/hal/esp32c5/include/hal/efuse_ll.h b/components/hal/esp32c5/include/hal/efuse_ll.h index 4418a29e79c..e5d3bd05789 100644 --- a/components/hal/esp32c5/include/hal/efuse_ll.h +++ b/components/hal/esp32c5/include/hal/efuse_ll.h @@ -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) diff --git a/components/hal/esp32c6/include/hal/efuse_ll.h b/components/hal/esp32c6/include/hal/efuse_ll.h index a4ca1495e03..592271f77d6 100644 --- a/components/hal/esp32c6/include/hal/efuse_ll.h +++ b/components/hal/esp32c6/include/hal/efuse_ll.h @@ -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) diff --git a/components/hal/esp32c61/include/hal/efuse_ll.h b/components/hal/esp32c61/include/hal/efuse_ll.h index a920e21282f..5765d40af1f 100644 --- a/components/hal/esp32c61/include/hal/efuse_ll.h +++ b/components/hal/esp32c61/include/hal/efuse_ll.h @@ -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) diff --git a/components/hal/esp32h2/include/hal/efuse_ll.h b/components/hal/esp32h2/include/hal/efuse_ll.h index e683551c7d2..3acdcd42f76 100644 --- a/components/hal/esp32h2/include/hal/efuse_ll.h +++ b/components/hal/esp32h2/include/hal/efuse_ll.h @@ -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) diff --git a/components/hal/esp32h21/include/hal/efuse_ll.h b/components/hal/esp32h21/include/hal/efuse_ll.h index 5457ef61ce3..3f3d81d9d7f 100644 --- a/components/hal/esp32h21/include/hal/efuse_ll.h +++ b/components/hal/esp32h21/include/hal/efuse_ll.h @@ -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) diff --git a/components/hal/esp32h4/include/hal/efuse_ll.h b/components/hal/esp32h4/include/hal/efuse_ll.h index 1796d75c74b..0eb8f27f987 100644 --- a/components/hal/esp32h4/include/hal/efuse_ll.h +++ b/components/hal/esp32h4/include/hal/efuse_ll.h @@ -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) diff --git a/components/hal/esp32p4/include/hal/efuse_ll.h b/components/hal/esp32p4/include/hal/efuse_ll.h index 5b8c437115e..4eed9013a0e 100644 --- a/components/hal/esp32p4/include/hal/efuse_ll.h +++ b/components/hal/esp32p4/include/hal/efuse_ll.h @@ -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) diff --git a/components/hal/esp32s2/include/hal/efuse_hal.h b/components/hal/esp32s2/include/hal/efuse_hal.h index 64830a8bf17..ec1cf007ef7 100644 --- a/components/hal/esp32s2/include/hal/efuse_hal.h +++ b/components/hal/esp32s2/include/hal/efuse_hal.h @@ -9,6 +9,7 @@ #include #include #include "soc/soc_caps.h" +#include "hal/efuse_ll.h" #include_next "hal/efuse_hal.h" #ifdef __cplusplus diff --git a/components/hal/esp32s2/include/hal/efuse_ll.h b/components/hal/esp32s2/include/hal/efuse_ll.h index 6f325d4aad6..6cb39a4bd8b 100644 --- a/components/hal/esp32s2/include/hal/efuse_ll.h +++ b/components/hal/esp32s2/include/hal/efuse_ll.h @@ -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) diff --git a/components/hal/esp32s3/include/hal/efuse_ll.h b/components/hal/esp32s3/include/hal/efuse_ll.h index e49fc512208..90c49f15e13 100644 --- a/components/hal/esp32s3/include/hal/efuse_ll.h +++ b/components/hal/esp32s3/include/hal/efuse_ll.h @@ -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) diff --git a/components/hal/esp32s31/include/hal/efuse_ll.h b/components/hal/esp32s31/include/hal/efuse_ll.h index 3f1365ceac5..df8c426b1da 100644 --- a/components/hal/esp32s31/include/hal/efuse_ll.h +++ b/components/hal/esp32s31/include/hal/efuse_ll.h @@ -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) diff --git a/components/hal/linux/include/hal/efuse_ll.h b/components/hal/linux/include/hal/efuse_ll.h index 3b5c5fd350b..731027e93dd 100644 --- a/components/hal/linux/include/hal/efuse_ll.h +++ b/components/hal/linux/include/hal/efuse_ll.h @@ -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) diff --git a/components/soc/esp32p4/register/hw_ver3/soc/efuse_struct.h b/components/soc/esp32p4/register/hw_ver3/soc/efuse_struct.h index 2c6455515cb..04d10b7b776 100644 --- a/components/soc/esp32p4/register/hw_ver3/soc/efuse_struct.h +++ b/components/soc/esp32p4/register/hw_ver3/soc/efuse_struct.h @@ -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; diff --git a/docs/en/api-guides/tools/idf-monitor.rst b/docs/en/api-guides/tools/idf-monitor.rst index 9e2f5014955..a507d23f09b 100644 --- a/docs/en/api-guides/tools/idf-monitor.rst +++ b/docs/en/api-guides/tools/idf-monitor.rst @@ -361,6 +361,46 @@ Configuration File For more details on the configuration file, see the `IDF Monitor documentation`_. +Host-side Command Markers +========================= + +IDF Monitor can execute host-side helper commands when a device log line starts with a supported marker. + +For eFuse token decoding, print one of the following from firmware logs: + +* ``IDF_MONITOR_EXECUTE_ESPEFUSE_SUMMARY [extra arguments]`` +* ``IDF_MONITOR_EXECUTE_ESPEFUSE_DUMP `` + +When a marker is detected, monitor invokes ``espefuse`` on the host and prints the decoded output inline. + +Example 1 (summary with custom table): + +.. code-block:: text + + I (441) app: IDF_MONITOR_EXECUTE_ESPEFUSE_SUMMARY EFSR:esp32c3:100:... --extend-efuse-table main/esp_efuse_custom_table.csv + +This line triggers a host-side command equivalent to: + +.. code-block:: text + + espefuse --token EFSR:esp32c3:100:... --extend-efuse-table main/esp_efuse_custom_table.csv summary --active + +Example 2 (raw dump): + +.. code-block:: text + + I (442) app: IDF_MONITOR_EXECUTE_ESPEFUSE_DUMP EFSR:esp32c3:100:... + +This line triggers a host-side command equivalent to: + +.. code-block:: text + + espefuse --token EFSR:esp32c3:100:... dump + +.. important:: + + Treat token payloads as sensitive data (especially ``EFSW``/``EFSRW``), because they can expose key values in plaintext while those values are still staged, not yet burned, and not yet read-protected. For token formats and generation APIs, see :doc:`eFuse Manager <../../api-reference/system/efuse>`. + Known Issues with IDF Monitor ============================= diff --git a/docs/en/api-reference/system/efuse.rst b/docs/en/api-reference/system/efuse.rst index d4826e8b757..ccb085015ef 100644 --- a/docs/en/api-reference/system/efuse.rst +++ b/docs/en/api-reference/system/efuse.rst @@ -356,6 +356,8 @@ Access to the fields is via a pointer to the description structure. API function * :cpp:func:`esp_efuse_get_keypurpose_dis_write` - Returns a write protection of the key purpose field for an eFuse key block (for esp32 always true). * :cpp:func:`esp_efuse_key_block_unused` - Returns true if the key block is unused, false otherwise. * :cpp:func:`esp_efuse_destroy_block` - Destroys the data in this eFuse block. There are two things to do: (1) if write protection is not set, then the remaining unset bits are burned, (2) set read protection for this block if it is not locked. +* :cpp:func:`esp_efuse_token_dump` - Generates a compact, single-line eFuse token (``EFSR``, ``EFSW``, or ``EFSRW``) that can be copied from device logs and decoded on the host with ``espefuse --token ...`` (useful when direct eFuse access is not available). +* :cpp:func:`esp_efuse_token_burn` - Applies an ``EFSW`` token on the device by burning the staged eFuse writes encoded in the token (other token types are rejected). For frequently used fields, special functions are made, like this :cpp:func:`esp_efuse_get_pkg_ver`. @@ -577,6 +579,212 @@ Deferred WR_DIS Burning When burning staged data in BLOCK0, the ``WR_DIS`` bits are burned separately after all other BLOCK0 data to ensure the burn function can recover from coding errors via its retry mechanism. This approach guarantees that write-protection is applied only after other BLOCK0 data is successfully burned. +Token Dump +---------- + +The *token dump* feature provides a compact, single-line representation of an eFuse state that can be copied from device logs and decoded later on the host. This is designed for cases where reading eFuses directly with host tools is not possible or not convenient (for example, UART download is disabled, secure download is enabled, secure boot/flash encryption is deployed, or the device is remote). + +.. code-block:: none + + EFSR:esp32c3:004:AAGAAAEAAAAAAAAEAAAAAAAAAAAAAAAA:AAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAA:AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA::::::::::epNVBg + +A token can represent: + +* the currently programmed eFuses (read snapshot, `EFSR`), +* the staged (not yet burned) write set (write snapshot, `EFSW`) — in batch write mode you can dump the pending writes **before** calling :cpp:func:`esp_efuse_batch_write_commit`, +* both the programmed eFuses and the staged writes in a single token (combined snapshot, `EFSRW`). + +Tokens include a CRC32 checksum to detect truncation and accidental modifications. + +Typical Use Cases +^^^^^^^^^^^^^^^^^ + +* **Production/field diagnostics:** export the eFuse state from a locked-down device and decode it offline. +* **Post-provisioning verification:** confirm security configuration and key purposes after manufacturing steps. +* **Coding error investigation:** capture and share coding-error register snapshots alongside block data. +* **Audit and traceability:** store a token as a provisioning artifact for later review. +* **Staged write transfer:** generate ``EFSW`` in firmware (or on the host) and apply it later using a controlled workflow. + +Supported Workflows +^^^^^^^^^^^^^^^^^^^ + +* On the device: + + * :cpp:func:`esp_efuse_token_dump()` — always supports ``EFSR`` tokens; ``EFSW`` and ``EFSRW`` require :ref:`CONFIG_EFUSE_ENABLE_STAGED_TOKEN_API` because they expose staged values that are not yet burned and not yet read-protected. + * :cpp:func:`esp_efuse_token_burn()` — accepts an ``EFSW`` token and applies staged writes on the device. + +* On the host: + + * ``espefuse --token EFS... summary`` — decodes tokens and shows the eFuse summary without connecting to a device. + * ``espefuse dump --format EFSR`` — generates an ``EFSR`` token from a connected chip for sharing/backup. + * ``espefuse burn-efuse ... --show-token`` — generates an ``EFSW`` token representing staged writes. + +Security Note +^^^^^^^^^^^^^ + +.. important:: + + Tokens are not encrypted. Treat tokens as sensitive data: + + * Tokens can include **unique identifiers** (for example MAC/UUID-like fields) and **security-relevant configuration bits** (secure boot, flash encryption, JTAG/UART disablement, key purposes). + * ``EFSW`` tokens represent **staged writes**. They may include **write-only key data in plaintext** because the staged view shows values that have not yet been burned and are not yet protected by the eFuse read-protection bits. This can disclose provisioning intent and secret material before it is irreversibly locked down. + * ``EFSRW`` tokens include the same staged portion and therefore carry the same plaintext key-exposure risk. + * Even when certain eFuses are read-protected on the target, the token may still carry **operationally sensitive values**. + + If firmware exposes a console command, remote endpoint, or other runtime API that prints eFuse tokens, the firmware must authenticate and authorize that request before generating the token. + +Recommendations: + +* Share tokens only with trusted parties and via trusted channels (avoid public issue trackers). +* Store tokens as you would store other manufacturing/provisioning artifacts (restricted access, limited retention). +* Prefer ``EFSR`` tokens for diagnostics and auditing; use ``EFSW`` tokens only when you explicitly need to transfer staged write state. + +Token format +^^^^^^^^^^^^ + +A token is a colon-separated sequence: + +.. code-block:: none + + ::::...::: + +Fields: + +* ``token_name`` — one of ``EFSR``, ``EFSW``, or ``EFSRW``. +* ``chip`` — chip name (lowercase, without dashes), for example ``esp32c3``. +* ``ver`` — chip revision as three decimal digits (leading zeros), for example ``004``. Constructed from major and minor wafer version fields using the formula ``ver = major * 100 + minor``, where the major version occupies the first digit and the minor version occupies the last two digits. +* ``b64_block0 ... b64_blockN`` — Base64URL-encoded per-block data. Each block is a concatenation of 32-bit words in little-endian byte order. The number of blocks is not explicitly encoded in the format. It is derived from the chip type. The number of blocks can be determined by counting the colon separators (``:``) in the token. Empty blocks are represented as consecutive colons (``::``). +* ``b64_cerr`` — optional Base64URL-encoded coding-error registers snapshot. It may be empty if there are no errors. +* ``b64_crc32`` — Base64URL-encoded CRC32 over whole token ``"::::...:::"``. CRC32 is stored little-endian and encoded as unpadded Base64URL. + +A token can be decoded and interpreted correctly only when it is processed using the same target it was created for. ESP-IDF APIs and ``espefuse`` validate and rely on the following fields: chip name, chip revision, and block layout, as well as CRC32 integrity. If any of these do not match the target chip, decoding errors or missing fields may occur. + +Base64URL uses the same alphabet as Base64 but replaces ``+`` with ``-`` and ``/`` with ``_``, and omits padding (``=``). + +Generating Token On-Device +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Use :cpp:func:`esp_efuse_token_dump()` to create a token in a buffer or print it to the log: + +.. code-block:: c + + char token[1024]; /* size depends on target and eFuses */ + esp_efuse_token_type_t token_type = ESP_EFUSE_TOKEN_FROM_READ; + ESP_ERROR_CHECK(esp_efuse_token_dump(token_type, token, sizeof(token))); + ESP_LOGI(TAG, "IDF_MONITOR_EXECUTE_ESPEFUSE_SUMMARY %s", token); + +Token type values: + +* ``ESP_EFUSE_TOKEN_FROM_READ`` — token of programmed eFuses (starts with ``EFSR``). +* ``ESP_EFUSE_TOKEN_FROM_STAGED`` — token of staged writes (starts with ``EFSW``). It can expose keys in plaintext because it shows values that are not yet burned and not yet read-protected. Requires :ref:`CONFIG_EFUSE_ENABLE_STAGED_TOKEN_API`. Only this type can be burned back. +* ``ESP_EFUSE_TOKEN_FROM_READ_STAGED`` — combined token (starts with ``EFSRW``). It includes the same staged portion and the same plaintext key-exposure risk. Requires :ref:`CONFIG_EFUSE_ENABLE_STAGED_TOKEN_API`. + +If ``buf == NULL``, the token is printed to the console (INFO level) without colors, tag, or timestamp. + +Burn Token On-Device +^^^^^^^^^^^^^^^^^^^^ + +Use :cpp:func:`esp_efuse_token_burn()` to apply an ``EFSW`` token on the device by burning the staged eFuse writes encoded in the token. The function validates the token integrity (CRC32) and checks compatibility using the chip name and revision. The token is rejected if the **major** wafer version does not match the target chip. To bypass the version check, use the ignore argument. Example of burning a token: + +.. code-block:: c + + esp_efuse_batch_write_begin(); + esp_efuse_token_burn(token, false); // set true to ignore major version mismatch + esp_efuse_batch_write_commit(); + +You can skip the major-version check only when you know the token was generated for the same eFuse layout (for example, the same target with only a minor wafer revision difference); in that case the token version may differ only in the last two digits, while the first digit (major version) must normally match. + +ESP-IDF Monitor Integration +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When running ``idf.py monitor``, the host can automatically decode eFuse tokens printed by the target and display the result inline if the log line starts with one of the following markers: + +* ``IDF_MONITOR_EXECUTE_ESPEFUSE_SUMMARY`` → ``espefuse --token {ARGS} summary --active`` +* ``IDF_MONITOR_EXECUTE_ESPEFUSE_DUMP`` → ``espefuse --token dump`` + +``{ARGS}`` must include an eFuse token (``EFSR/EFSW/EFSRW``) and may include ``--extend-efuse-table `` to load custom eFuse definitions. + +Example: The following shows executing the summary command with ``--active`` to display only non-zero eFuse fields, which reduces output size. The ``--extend-efuse-table`` option loads a custom eFuse table definition: + +.. code-block:: text + + I (441) example: IDF_MONITOR_EXECUTE_ESPEFUSE_SUMMARY EFSR:esp32c3:100:AAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAA:zIH3-VVgAAAAAAAAAAAAS8kmEVKwQgYB:ZSd8yloMSAJssOWmfZQw8lFbphuTZH574QcV3ggAAAA:AAAAAAAAAAEayAcAAAAAAAAAAAAAAAAAAAAAAAAAAAA:::::::::ydrNkQ --extend-efuse-table main/esp_efuse_custom_table.csv + + --- Executing monitor command: espefuse --token EFSR:esp32c3:100:AAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAA:zIH3-VVgAAAAAAAAAAAAS8kmEVKwQgYB:ZSd8yloMSAJssOWmfZQw8lFbphuTZH574QcV3ggAAAA:AAAAAAAAAAEayAcAAAAAAAAAAAAAAAAAAAAAAAAAAAA:::::::::ydrNkQ --extend-efuse-table main/esp_efuse_custom_table.csv summary --active + espefuse v5.1.0 + + === Run "summary" command === + EFUSE_NAME (Block) Description = [Meaningful Value] [Readable/Writeable] (Hex Value) + ---------------------------------------------------------------------------------------- + Calibration fuses: + K_RTC_LDO (BLOCK1) BLOCK1 K_RTC_LDO = 77 R/W (0b1001101) + K_DIG_LDO (BLOCK1) BLOCK1 K_DIG_LDO = 68 R/W (0b1000100) + V_RTC_DBIAS20 (BLOCK1) BLOCK1 voltage of rtc dbias20 = 144 R/W (0x90) + V_DIG_DBIAS20 (BLOCK1) BLOCK1 voltage of digital dbias20 = 130 R/W (0x82) + DIG_DBIAS_HVT (BLOCK1) BLOCK1 digital dbias when hvt = 21 R/W (0b10101) + THRES_HVT (BLOCK1) BLOCK1 pvt threshold when hvt = 400 R/W (0b0110010000) + TEMP_CALIB (BLOCK2) Temperature calibration data = -10.600000000000001 R/W (0b101101010) + OCODE (BLOCK2) ADC OCode = 101 R/W (0x65) + ADC1_INIT_CODE_ATTEN0 (BLOCK2) ADC1 init code at atten0 = 442 R/W (0b0110111010) + ADC1_INIT_CODE_ATTEN1 (BLOCK2) ADC1 init code at atten1 = 588 R/W (0b1001001100) + ADC1_INIT_CODE_ATTEN2 (BLOCK2) ADC1 init code at atten2 = 612 R/W (0b1001100100) + ADC1_INIT_CODE_ATTEN3 (BLOCK2) ADC1 init code at atten3 = 735 R/W (0b1011011111) + ADC1_CAL_VOL_ATTEN0 (BLOCK2) ADC1 calibration voltage at atten0 = 535 R/W (0b1000010111) + ADC1_CAL_VOL_ATTEN1 (BLOCK2) ADC1 calibration voltage at atten1 = 31 R/W (0b0000011111) + ADC1_CAL_VOL_ATTEN2 (BLOCK2) ADC1 calibration voltage at atten2 = 533 R/W (0b1000010101) + ADC1_CAL_VOL_ATTEN3 (BLOCK2) ADC1 calibration voltage at atten3 = 567 R/W (0b1000110111) + + Config fuses: + ERR_RST_ENABLE (BLOCK0) Use BLOCK0 to check error record registers = with check R/W (0b1) + BLOCK_USR_DATA (BLOCK3) User data + = 00 00 00 00 00 00 00 01 1a c8 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 R/W + + Flash fuses: + FLASH_CAP (BLOCK1) Flash capacity = 4M R/W (0b001) + FLASH_TEMP (BLOCK1) Flash temperature = 105C R/W (0b01) + FLASH_VENDOR (BLOCK1) Flash vendor = XMC R/W (0b001) + + Identity fuses: + BLK_VERSION_MINOR (BLOCK1) BLK_VERSION_MINOR = 3 R/W (0b011) + WAFER_VERSION_MAJOR (BLOCK1) WAFER_VERSION_MAJOR = 1 R/W (0b01) + OPTIONAL_UNIQUE_ID (BLOCK2) Optional unique 128-bit ID + = 65 27 7c ca 5a 0c 48 02 6c b0 e5 a6 7d 94 30 f2 R/W + BLK_VERSION_MAJOR (BLOCK2) BLK_VERSION_MAJOR of BLOCK2 = With calibration R/W (0b01) + + Mac fuses: + MAC (BLOCK1) MAC address + = 60:55:f9:f7:81:cc (OK) R/W + + User fuses: + MODULE_VERSION (BLOCK3) Module version (56-63) = 1 R/W (0x01) + DEVICE_ROLE (BLOCK3) Device role (64-66) = 2 R/W (0b010) + SETTING_1 (BLOCK3) Setting 1 (67-72) = 3 R/W (0b000011) + SETTING_2 (BLOCK3) Setting 2 (73-77) = 4 R/W (0b00100) + CUSTOM_SECURE_VERSION (BLOCK3) Custom secure version (78-93) = 31 R/W (0x001f) + ... + +Example (dump): + +.. code-block:: text + + I (441) example: IDF_MONITOR_EXECUTE_ESPEFUSE_DUMP EFSR:esp32c3:100:AAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAA:zIH3-VVgAAAAAAAAAAAAS8kmEVKwQgYB:ZSd8yloMSAJssOWmfZQw8lFbphuTZH574QcV3ggAAAA:AAAAAAAAAAEayAcAAAAAAAAAAAAAAAAAAAAAAAAAAAA:::::::::ydrNkQ + + --- Executing monitor command: espefuse --token EFSR:esp32c3:100:AAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAA:zIH3-VVgAAAAAAAAAAAAS8kmEVKwQgYB:ZSd8yloMSAJssOWmfZQw8lFbphuTZH574QcV3ggAAAA:AAAAAAAAAAEayAcAAAAAAAAAAAAAAAAAAAAAAAAAAAA:::::::::ydrNkQ dump + espefuse v5.1.0 + + === Run "dump" command === + BLOCK0 ( ) [0 ] dump: 00000000 00000000 00000000 00000000 80000000 00000000 + MAC_SPI_8M_0 (BLOCK1 ) [1 ] dump: f9f781cc 00006055 00000000 4b000000 521126c9 010642b0 + BLOCK_SYS_DATA (BLOCK2 ) [2 ] dump: ca7c2765 02480c5a a6e5b06c f230947d 1ba65b51 7b7e6493 de1507e1 00000008 + BLOCK_USR_DATA (BLOCK3 ) [3 ] dump: 00000000 01000000 0007c81a 00000000 00000000 00000000 00000000 00000000 + BLOCK_KEY0 (BLOCK4 ) [4 ] dump: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 + BLOCK_KEY1 (BLOCK5 ) [5 ] dump: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 + BLOCK_KEY2 (BLOCK6 ) [6 ] dump: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 + BLOCK_KEY3 (BLOCK7 ) [7 ] dump: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 + BLOCK_KEY4 (BLOCK8 ) [8 ] dump: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 + BLOCK_KEY5 (BLOCK9 ) [9 ] dump: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 + BLOCK_SYS_DATA2 (BLOCK10 ) [10] dump: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 + Application Examples -------------------- diff --git a/docs/zh_CN/api-guides/tools/idf-monitor.rst b/docs/zh_CN/api-guides/tools/idf-monitor.rst index da462bd6ee5..218442edbd3 100644 --- a/docs/zh_CN/api-guides/tools/idf-monitor.rst +++ b/docs/zh_CN/api-guides/tools/idf-monitor.rst @@ -361,6 +361,46 @@ GDBStub 支持在运行时进行调试。GDBStub 在目标上运行,并通过 有关配置文件的更多详细信息,请参阅 `IDF 监视器文档`_。 +主机侧命令标记 +============== + +当设备日志行以支持的标记开头时,IDF 监视器可以执行主机侧辅助命令。 + +对于 eFuse token 解码,可在固件日志中输出以下任一格式: + +* ``IDF_MONITOR_EXECUTE_ESPEFUSE_SUMMARY [附加参数]`` +* ``IDF_MONITOR_EXECUTE_ESPEFUSE_DUMP `` + +检测到标记后,监视器会在主机侧调用 ``espefuse``,并以内联方式打印解码结果。 + +示例 1(带自定义表的 summary): + +.. code-block:: text + + I (441) app: IDF_MONITOR_EXECUTE_ESPEFUSE_SUMMARY EFSR:esp32c3:100:... --extend-efuse-table main/esp_efuse_custom_table.csv + +该日志行会触发等价于以下命令的主机侧执行: + +.. code-block:: text + + espefuse --token EFSR:esp32c3:100:... --extend-efuse-table main/esp_efuse_custom_table.csv summary --active + +示例 2(原始转储): + +.. code-block:: text + + I (442) app: IDF_MONITOR_EXECUTE_ESPEFUSE_DUMP EFSR:esp32c3:100:... + +该日志行会触发等价于以下命令的主机侧执行: + +.. code-block:: text + + espefuse --token EFSR:esp32c3:100:... dump + +.. important:: + + 请将 token 参数视为敏感数据(尤其是 ``EFSW``/``EFSRW``),因为它们可能在这些值仍处于暂存状态、尚未烧写、也尚未受读保护时,以明文暴露密钥值。关于 token 格式和生成 API,请参见 :doc:`eFuse 管理器 <../../api-reference/system/efuse>`。 + IDF 监视器已知问题 ================================= diff --git a/docs/zh_CN/api-reference/system/efuse.rst b/docs/zh_CN/api-reference/system/efuse.rst index 0410fcdb6f2..ebfd87d8bec 100644 --- a/docs/zh_CN/api-reference/system/efuse.rst +++ b/docs/zh_CN/api-reference/system/efuse.rst @@ -571,12 +571,218 @@ esptool 中包含一个用于读取/写入 {IDF_TARGET_NAME} eFuse 位的有用 .. include:: inc/espefuse_summary_{IDF_TARGET_NAME}_dump.rst 延迟烧写 WR_DIS ---------------- +----------------------- ``WR_DIS`` (写禁用)是一个特殊的 eFuse 字段,用于实现永久写保护。``WR_DIS`` 中的每个位用于禁止进一步烧写一个(或多个)关联的 eFuse 字段。一旦完成烧写,将无法再修改受影响的 eFuse 字段。 烧写 BLOCK0 中的暂存数据时,``WR_DIS`` 的各个位会在所有其他 BLOCK0 数据烧写完成后单独烧写,以确保在出现编码错误时,烧写函数能够通过重试机制进行恢复。这种方式保证了仅在其他 BLOCK0 数据成功烧写后,才对其施加写保护。 +Token Dump +---------- + +*eFuse Token 转储* 功能提供了一种紧凑的、单行的 eFuse 状态表示,可以从设备日志中复制并在主机上稍后解码。这适用于无法或不便直接使用主机工具读取 eFuse 的情况(例如,UART 下载被禁用、安全下载已启用、安全启动/flash 加密已部署或设备在远程位置)。 + +.. code-block:: none + + EFSR:esp32c3:004:AAGAAAEAAAAAAAAEAAAAAAAAAAAAAAAA:AAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAA:AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA::::::::::epNVBg + +Token 可以表示: + +* 当前烧写的 eFuse(读快照,``EFSR``), +* 暂存(尚未烧写)的写入集(写快照,``EFSW``)— 在批量写入模式下,可以在调用 :cpp:func:`esp_efuse_batch_write_commit` 之前转储待写入的数据, +* 单个 token 中同时包含已烧写的 eFuse 和暂存写入的综合快照(``EFSRW``)。 + +Token 包括一个 CRC32 校验和,用于检测截断和意外修改。 + +典型用例 +^^^^^^^^^^^^^^^^^ + +* **生产/现场诊断:** 从锁定的设备导出 eFuse 状态并离线解码。 +* **烧写后验证:** 确认生产步骤后的安全配置和密钥目的。 +* **编码错误调查:** 捕获和共享编码错误寄存器快照以及块数据。 +* **审计和可追溯性:** 将 token 作为配置工件存储以供后续审查。 +* **暂存写入转移:** 在固件(或主机)中生成 ``EFSW`` token,稍后使用受控工作流应用。 + +支持的工作流 +^^^^^^^^^^^^^^^^^^ + +* 在设备上: + + * :cpp:func:`esp_efuse_token_dump()` — 始终支持生成 ``EFSR`` token;``EFSW`` 和 ``EFSRW`` 需要启用 :ref:`CONFIG_EFUSE_ENABLE_STAGED_TOKEN_API`,因为它们会暴露尚未烧写、也尚未受读保护的暂存值。 + * :cpp:func:`esp_efuse_token_burn()` — 接受 ``EFSW`` token 并在设备上应用暂存写入。 + +* 在主机上: + + * ``espefuse --token EFS... summary`` — 解码 token 并显示 eFuse 摘要,无需连接到设备。 + * ``espefuse dump --format EFSR`` — 从已连接芯片生成 ``EFSR`` token 用于共享/备份。 + * ``espefuse burn-efuse ... --show-token`` — 生成表示暂存写入的 ``EFSW`` token。 + +安全注意事项 +^^^^^^^^^^^^^^^^^^ + +.. important:: + + Token 未加密。将 token 视为敏感数据: + + * Token 可能包含 **唯一标识符** (例如 MAC/UUID 类字段)和 **安全相关配置位** (安全启动、flash 加密、JTAG/UART 禁用、密钥用途)。 + * ``EFSW`` token 代表 **暂存写入** 。由于暂存视图展示的是尚未烧写、也尚未受 eFuse 读保护位保护的值,它们可能包含 **明文只写密钥数据** (例如 flash 加密密钥或其他在烧写后会被读保护的密钥)。这会在永久锁定前泄露配置意图和秘密材料。 + * ``EFSRW`` token 包含同样的暂存部分,因此也具有相同的明文密钥暴露风险。 + * 即使某些 eFuse 在目标上受读保护,token 仍可能携带 **操作敏感值** 。 + + 如果固件暴露用于在控制台、远程端点或其他运行时 API 中打印 eFuse token 的接口,固件必须在生成 token 前对该请求进行身份认证和授权。 + +建议: + +* 仅与信任方通过信任渠道共享 token(避免公开问题跟踪器)。 +* 像存储其他生产/配置工件那样存储 token(限制访问、限制保留期)。 +* 优先使用 ``EFSR`` token 进行诊断和审计;仅当明确需要转移暂存写入状态时才使用 ``EFSW`` token。 + +Token 格式 +^^^^^^^^^^ + +Token 是一个冒号分隔的序列: + +.. code-block:: none + + ::::...::: + +字段说明: + +* ``token_name`` — ``EFSR``、``EFSW`` 或 ``EFSRW`` 之一。 +* ``chip`` — 芯片名称(小写,不含破折号),例如 ``esp32c3``。 +* ``ver`` — 芯片版本为三位十进制数字(前导零),例如 ``004``。由主版本和次版本字段使用公式 ``ver = major * 100 + minor`` 构造,其中主版本占据第一位数字,次版本占据最后两位数字。 +* ``b64_block0 ... b64_blockN`` — Base64URL 编码的按块数据。每个块是 32 位字的连接(小端序)。块数不在格式中显式编码,而是从芯片类型派生的。块数可通过计数 token 中的冒号分隔符(``:``)来确定。空块表示为连续冒号(``::``)。 +* ``b64_cerr`` — 可选的 Base64URL 编码编码错误寄存器快照。如果没有错误,可能为空。 +* ``b64_crc32`` — Base64URL 编码的 CRC32,覆盖整个 token ``"::::...:::"``. CRC32 以小端序存储并编码为无填充 Base64URL。 + +Token 只能在使用与其创建目标相同的目标进行处理时才能正确解码和解释。ESP-IDF API 和 ``espefuse`` 验证并依赖于以下字段:芯片名称、芯片版本和块布局,以及 CRC32 完整性。如果这些不符合目标芯片,可能会导致解码错误或丢失字段。 + +Base64URL 使用与 Base64 相同的字母表,但将 ``+`` 替换为 ``-``,将 ``/`` 替换为 ``_``,并省略填充(``=``)。 + +在设备上生成 Token +^^^^^^^^^^^^^^^^^^ + +使用 :cpp:func:`esp_efuse_token_dump()` 在缓冲区中创建 token 或将其打印到日志: + +.. code-block:: c + + char token[1024]; /* 大小取决于目标和 eFuse */ + esp_efuse_token_type_t token_type = ESP_EFUSE_TOKEN_FROM_READ; + ESP_ERROR_CHECK(esp_efuse_token_dump(token_type, token, sizeof(token))); + ESP_LOGI(TAG, "IDF_MONITOR_EXECUTE_ESPEFUSE_SUMMARY %s", token); + +Token 类型值: + +* ``ESP_EFUSE_TOKEN_FROM_READ`` — 已烧写的 eFuse 的 token(以 ``EFSR`` 开头)。 +* ``ESP_EFUSE_TOKEN_FROM_STAGED`` — 暂存写入的 token(以 ``EFSW`` 开头)。它会显示尚未烧写、也尚未受读保护的值,因此可能以明文暴露密钥。需要启用 :ref:`CONFIG_EFUSE_ENABLE_STAGED_TOKEN_API`。仅此类型可被烧写回。 +* ``ESP_EFUSE_TOKEN_FROM_READ_STAGED`` — 组合 token(以 ``EFSRW`` 开头)。它包含同样的暂存部分,因此也具有相同的明文密钥暴露风险。需要启用 :ref:`CONFIG_EFUSE_ENABLE_STAGED_TOKEN_API`。 + +如果 ``buf == NULL``,token 将打印到控制台(INFO 级别),不包含颜色、标签或时间戳。 + +在设备上烧写 Token +^^^^^^^^^^^^^^^^^^ + +使用 :cpp:func:`esp_efuse_token_burn()` 在设备上应用 ``EFSW`` token,通过烧写 token 中编码的暂存 eFuse 写入。该函数验证 token 完整性(CRC32)并使用芯片名称和版本检查兼容性。如果 **主要** wafer 版本与目标芯片不匹配,token 将被拒绝。要绕过版本检查,请使用忽略参数。烧写 token 的示例: + +.. code-block:: c + + esp_efuse_batch_write_begin(); + esp_efuse_token_burn(token, false); // 设置为 true 以忽略主版本不匹配 + esp_efuse_batch_write_commit(); + +仅当您知道 token 是为相同 eFuse 布局生成的(例如相同目标,仅 wafer 次版本不同)时,才可跳过主版本检查;在这种情况下,token 版本可能仅在最后两位数字上不同,而第一位数字(主版本)必须正常匹配。 + +ESP-IDF 监视器集成 +^^^^^^^^^^^^^^^^^^ + +运行 ``idf.py monitor`` 时,如果日志行以以下标记之一开头,主机可以自动解码目标打印的 eFuse token 并以内联方式显示结果: + +* ``IDF_MONITOR_EXECUTE_ESPEFUSE_SUMMARY`` → ``espefuse --token {ARGS} summary --active`` +* ``IDF_MONITOR_EXECUTE_ESPEFUSE_DUMP`` → ``espefuse --token dump`` + +``{ARGS}`` 必须包含 eFuse token(``EFSR/EFSW/EFSRW``),并可能包含 ``--extend-efuse-table `` 以加载自定义 eFuse 定义。 + +示例:以下显示使用 ``--active`` 执行 summary 命令,仅显示非零 eFuse 字段以减少输出大小。``--extend-efuse-table`` 选项加载自定义 eFuse 表定义: + +.. code-block:: text + + I (441) example: IDF_MONITOR_EXECUTE_ESPEFUSE_SUMMARY EFSR:esp32c3:100:AAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAA:zIH3-VVgAAAAAAAAAAAAS8kmEVKwQgYB:ZSd8yloMSAJssOWmfZQw8lFbphuTZH574QcV3ggAAAA:AAAAAAAAAAEayAcAAAAAAAAAAAAAAAAAAAAAAAAAAAA:::::::::ydrNkQ --extend-efuse-table main/esp_efuse_custom_table.csv + + --- Executing monitor command: espefuse --token EFSR:esp32c3:100:AAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAA:zIH3-VVgAAAAAAAAAAAAS8kmEVKwQgYB:ZSd8yloMSAJssOWmfZQw8lFbphuTZH574QcV3ggAAAA:AAAAAAAAAAEayAcAAAAAAAAAAAAAAAAAAAAAAAAAAAA:::::::::ydrNkQ --extend-efuse-table main/esp_efuse_custom_table.csv summary --active + espefuse v5.1.0 + + === Run "summary" command === + EFUSE_NAME (Block) Description = [Meaningful Value] [Readable/Writeable] (Hex Value) + ———————————————————————————————————————————————————————————————————————————————————————— + Calibration fuses: + K_RTC_LDO (BLOCK1) BLOCK1 K_RTC_LDO = 77 R/W (0b1001101) + K_DIG_LDO (BLOCK1) BLOCK1 K_DIG_LDO = 68 R/W (0b1000100) + V_RTC_DBIAS20 (BLOCK1) BLOCK1 voltage of rtc dbias20 = 144 R/W (0x90) + V_DIG_DBIAS20 (BLOCK1) BLOCK1 voltage of digital dbias20 = 130 R/W (0x82) + DIG_DBIAS_HVT (BLOCK1) BLOCK1 digital dbias when hvt = 21 R/W (0b10101) + THRES_HVT (BLOCK1) BLOCK1 pvt threshold when hvt = 400 R/W (0b0110010000) + TEMP_CALIB (BLOCK2) Temperature calibration data = -10.600000000000001 R/W (0b101101010) + OCODE (BLOCK2) ADC OCode = 101 R/W (0x65) + ADC1_INIT_CODE_ATTEN0 (BLOCK2) ADC1 init code at atten0 = 442 R/W (0b0110111010) + ADC1_INIT_CODE_ATTEN1 (BLOCK2) ADC1 init code at atten1 = 588 R/W (0b1001001100) + ADC1_INIT_CODE_ATTEN2 (BLOCK2) ADC1 init code at atten2 = 612 R/W (0b1001100100) + ADC1_INIT_CODE_ATTEN3 (BLOCK2) ADC1 init code at atten3 = 735 R/W (0b1011011111) + ADC1_CAL_VOL_ATTEN0 (BLOCK2) ADC1 calibration voltage at atten0 = 535 R/W (0b1000010111) + ADC1_CAL_VOL_ATTEN1 (BLOCK2) ADC1 calibration voltage at atten1 = 31 R/W (0b0000011111) + ADC1_CAL_VOL_ATTEN2 (BLOCK2) ADC1 calibration voltage at atten2 = 533 R/W (0b1000010101) + ADC1_CAL_VOL_ATTEN3 (BLOCK2) ADC1 calibration voltage at atten3 = 567 R/W (0b1000110111) + + Config fuses: + ERR_RST_ENABLE (BLOCK0) Use BLOCK0 to check error record registers = with check R/W (0b1) + BLOCK_USR_DATA (BLOCK3) User data + = 00 00 00 00 00 00 00 01 1a c8 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 R/W + + Flash fuses: + FLASH_CAP (BLOCK1) Flash capacity = 4M R/W (0b001) + FLASH_TEMP (BLOCK1) Flash temperature = 105C R/W (0b01) + FLASH_VENDOR (BLOCK1) Flash vendor = XMC R/W (0b001) + + Identity fuses: + BLK_VERSION_MINOR (BLOCK1) BLK_VERSION_MINOR = 3 R/W (0b011) + WAFER_VERSION_MAJOR (BLOCK1) WAFER_VERSION_MAJOR = 1 R/W (0b01) + OPTIONAL_UNIQUE_ID (BLOCK2) Optional unique 128-bit ID + = 65 27 7c ca 5a 0c 48 02 6c b0 e5 a6 7d 94 30 f2 R/W + BLK_VERSION_MAJOR (BLOCK2) BLK_VERSION_MAJOR of BLOCK2 = With calibration R/W (0b01) + + Mac fuses: + MAC (BLOCK1) MAC address + = 60:55:f9:f7:81:cc (OK) R/W + + User fuses: + MODULE_VERSION (BLOCK3) Module version (56-63) = 1 R/W (0x01) + DEVICE_ROLE (BLOCK3) Device role (64-66) = 2 R/W (0b010) + SETTING_1 (BLOCK3) Setting 1 (67-72) = 3 R/W (0b000011) + SETTING_2 (BLOCK3) Setting 2 (73-77) = 4 R/W (0b00100) + CUSTOM_SECURE_VERSION (BLOCK3) Custom secure version (78-93) = 31 R/W (0x001f) + ... + +dump 示例: + +.. code-block:: text + + I (441) example: IDF_MONITOR_EXECUTE_ESPEFUSE_DUMP EFSR:esp32c3:100:AAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAA:zIH3-VVgAAAAAAAAAAAAS8kmEVKwQgYB:ZSd8yloMSAJssOWmfZQw8lFbphuTZH574QcV3ggAAAA:AAAAAAAAAAEayAcAAAAAAAAAAAAAAAAAAAAAAAAAAAA:::::::::ydrNkQ + + --- Executing monitor command: espefuse --token EFSR:esp32c3:100:AAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAA:zIH3-VVgAAAAAAAAAAAAS8kmEVKwQgYB:ZSd8yloMSAJssOWmfZQw8lFbphuTZH574QcV3ggAAAA:AAAAAAAAAAEayAcAAAAAAAAAAAAAAAAAAAAAAAAAAAA:::::::::ydrNkQ dump + espefuse v5.1.0 + + === Run "dump" command === + BLOCK0 ( ) [0 ] dump: 00000000 00000000 00000000 00000000 80000000 00000000 + MAC_SPI_8M_0 (BLOCK1 ) [1 ] dump: f9f781cc 00006055 00000000 4b000000 521126c9 010642b0 + BLOCK_SYS_DATA (BLOCK2 ) [2 ] dump: ca7c2765 02480c5a a6e5b06c f230947d 1ba65b51 7b7e6493 de1507e1 00000008 + BLOCK_USR_DATA (BLOCK3 ) [3 ] dump: 00000000 01000000 0007c81a 00000000 00000000 00000000 00000000 00000000 + BLOCK_KEY0 (BLOCK4 ) [4 ] dump: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 + BLOCK_KEY1 (BLOCK5 ) [5 ] dump: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 + BLOCK_KEY2 (BLOCK6 ) [6 ] dump: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 + BLOCK_KEY3 (BLOCK7 ) [7 ] dump: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 + BLOCK_KEY4 (BLOCK8 ) [8 ] dump: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 + BLOCK_KEY5 (BLOCK9 ) [9 ] dump: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 + BLOCK_SYS_DATA2 (BLOCK10 ) [10] dump: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 + 应用示例 ----------------- diff --git a/examples/system/efuse/README.md b/examples/system/efuse/README.md index 3097e517318..85dee61255d 100644 --- a/examples/system/efuse/README.md +++ b/examples/system/efuse/README.md @@ -84,6 +84,8 @@ idf.py -p PORT flash monitor (Replace PORT with the name of the serial port to use.) +Running `idf.py monitor` is important for this example, not only to view the UART log. The application prints special `IDF_MONITOR_EXECUTE_ESPEFUSE_* EFSR:..` messages, and `idf.py monitor` recognizes them, runs the corresponding `espefuse` command, and prints the decoded eFuse information in the console. If you only use `idf.py flash` or another serial terminal, you will only see the raw marker lines with an efuse token and will not get the additional `espefuse` output shown below. + (To exit the serial monitor, type ``Ctrl-]``.) See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. @@ -91,36 +93,194 @@ See the Getting Started Guide for full steps to configure and use ESP-IDF to bui ## Example Output -For ``None`` coding scheme: - +For ``RS`` coding scheme (esp32c3): ``` -I (0) cpu_start: Starting scheduler on APP CPU. -I (323) example: Coding Scheme NONE -I (323) example: Uses common and custom tables -I (333) example: read efuse fields -I (333) example: 1. read MAC address: d8:a0:1d:40:ac:90 -I (343) example: 2. read secure_version: 0 -I (343) example: 3. read custom fields -I (353) example: module_version = 0 -I (353) example: device_role = None -I (363) example: setting_1 = 0 -I (363) example: setting_2 = 0 -I (363) example: custom_secure_version = 0 -W (373) example: This example does not burn any efuse in reality only virtually -W (383) example: Write operations in efuse fields are performed virtually -I (383) example: write custom efuse fields -W (393) efuse: Virtual efuses enabled: Not really burning eFuses -W (403) efuse: Virtual efuses enabled: Not really burning eFuses -W (403) efuse: Virtual efuses enabled: Not really burning eFuses -W (413) efuse: Virtual efuses enabled: Not really burning eFuses -W (423) efuse: Virtual efuses enabled: Not really burning eFuses -I (423) example: module_version = 1 -I (433) example: device_role = Slave -I (433) example: setting_1 = 3 -I (433) example: setting_2 = 4 -I (443) example: custom_secure_version = 5 -I (443) example: Done +ESP-ROM:esp32c3-eco6-20230321 +Build:Mar 21 2023 +rst:0x1 (POWERON),boot:0xc (SPI_FAST_FLASH_BOOT) +SPIWP:0xee +mode:DIO, clock div:1 +load:0x3fcd5990,len:0x1750 +load:0x403cbf10,len:0xd00 +load:0x403ce710,len:0x304c +entry 0x403cbf1a +W (25) boot.esp32c3: eFuse virtual mode is enabled. If Secure boot or Flash encryption is enabled then it does not provide any security. FOR TESTING ONLY! +W (31) efuse: [Virtual] Loading virtual efuse blocks from real efuses +I (49) boot: ESP-IDF v6.1-dev-1458-ga7a9419ed8f-dirt 2nd stage bootloader +I (49) boot: compile time Dec 23 2025 13:37:44 +I (50) boot: chip revision: v1.0 +I (52) boot: efuse block revision: v1.3 +I (55) boot.esp32c3: SPI Speed : 80MHz +I (59) boot.esp32c3: SPI Mode : DIO +I (63) boot.esp32c3: SPI Flash Size : 2MB +I (67) boot: Enabling RNG early entropy source... +I (71) boot: Partition Table: +I (74) boot: ## Label Usage Type ST Offset Length +I (80) boot: 0 nvs WiFi data 01 02 00009000 00006000 +I (87) boot: 1 phy_init RF data 01 01 0000f000 00001000 +I (93) boot: 2 factory factory app 00 00 00010000 00100000 +I (100) boot: End of partition table +I (103) esp_image: segment 0: paddr=00010020 vaddr=3c010020 size=06a24h ( 27172) map +I (115) esp_image: segment 1: paddr=00016a4c vaddr=3fc89200 size=0175ch ( 5980) load +I (119) esp_image: segment 2: paddr=000181b0 vaddr=40380000 size=07e68h ( 32360) load +I (131) esp_image: segment 3: paddr=00020020 vaddr=42000020 size=0e6d4h ( 59092) map +I (142) esp_image: segment 4: paddr=0002e6fc vaddr=40387e68 size=011b4h ( 4532) load +I (143) esp_image: segment 5: paddr=0002f8b8 vaddr=50000000 size=00024h ( 36) load +I (151) boot: Loaded app from partition at offset 0x10000 +I (153) boot: Disabling RNG early entropy source... +I (169) cpu_start: Unicore app +I (177) cpu_start: GPIO 20 and 21 are used as console UART I/O pins +I (178) cpu_start: Pro cpu start user code +I (178) cpu_start: cpu freq: 160000000 Hz +I (180) app_init: Application information: +I (183) app_init: Project name: efuse +I (187) app_init: App version: v6.1-dev-1458-ga7a9419ed8f-dirt +I (193) app_init: Compile time: Dec 23 2025 13:37:47 +I (198) app_init: ELF file SHA256: 64e4d02c1... +I (203) app_init: ESP-IDF: v6.1-dev-1458-ga7a9419ed8f-dirt +I (209) efuse_init: Min chip rev: v0.3 +I (212) efuse_init: Max chip rev: v1.99 +I (216) efuse_init: Chip rev: v1.0 +I (220) heap_init: Initializing. RAM available for dynamic allocation: +I (227) heap_init: At 3FC8BE70 len 00034190 (208 KiB): RAM +I (232) heap_init: At 3FCC0000 len 0001C710 (113 KiB): Retention RAM +I (238) heap_init: At 3FCDC710 len 0000294C (10 KiB): Retention RAM +I (244) heap_init: At 50000024 len 00001FC4 (7 KiB): RTCRAM +I (250) spi_flash: detected chip: generic +I (253) spi_flash: flash io: dio +W (256) spi_flash: Detected size(4096k) larger than the size in the binary image header(2048k). Using the size in the binary image header. +W (268) efuse_init: eFuse virtual mode is enabled. If Secure boot or Flash encryption is enabled then it does not provide any security. FOR TESTING ONLY! +W (281) efuse: [Virtual] Loading virtual efuse blocks from real efuses +I (288) sleep_gpio: Configure to isolate all GPIO pins in sleep state +I (294) sleep_gpio: Enable automatic switching of GPIO sleep configuration +I (301) main_task: Started on CPU0 +I (301) main_task: Calling app_main() +I (301) example: Start eFuse example +I (301) example: Initial token dump to log +I (311) example: IDF_MONITOR_EXECUTE_ESPEFUSE_SUMMARY EFSR:esp32c3:100:AAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAA:zIH3-VVgAAAAAAAAAAAAS8kmEVKwQgYB:ZSd8yloMSAJssOWmfZQw8lFbphuTZH574QcV3ggAAAA::::::::::lcxoUA --extend-efuse-table main/esp_efuse_custom_table.csv +--- Executing monitor command: espefuse --token EFSR:esp32c3:100:AAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAA:zIH3-VVgAAAAAAAAAAAAS8kmEVKwQgYB:ZSd8yloMSAJssOWmfZQw8lFbphuTZH574QcV3ggAAAA::::::::::lcxoUA --extend-efuse-table main/esp_efuse_custom_table.csv summary --active +espefuse v5.1.0 + +=== Run "summary" command === +EFUSE_NAME (Block) Description = [Meaningful Value] [Readable/Writeable] (Hex Value) +---------------------------------------------------------------------------------------- +Calibration fuses: +K_RTC_LDO (BLOCK1) BLOCK1 K_RTC_LDO = 77 R/W (0b1001101) +K_DIG_LDO (BLOCK1) BLOCK1 K_DIG_LDO = 68 R/W (0b1000100) +V_RTC_DBIAS20 (BLOCK1) BLOCK1 voltage of rtc dbias20 = 144 R/W (0x90) +V_DIG_DBIAS20 (BLOCK1) BLOCK1 voltage of digital dbias20 = 130 R/W (0x82) +DIG_DBIAS_HVT (BLOCK1) BLOCK1 digital dbias when hvt = 21 R/W (0b10101) +THRES_HVT (BLOCK1) BLOCK1 pvt threshold when hvt = 400 R/W (0b0110010000) +TEMP_CALIB (BLOCK2) Temperature calibration data = -10.600000000000001 R/W (0b101101010) +OCODE (BLOCK2) ADC OCode = 101 R/W (0x65) +ADC1_INIT_CODE_ATTEN0 (BLOCK2) ADC1 init code at atten0 = 442 R/W (0b0110111010) +ADC1_INIT_CODE_ATTEN1 (BLOCK2) ADC1 init code at atten1 = 588 R/W (0b1001001100) +ADC1_INIT_CODE_ATTEN2 (BLOCK2) ADC1 init code at atten2 = 612 R/W (0b1001100100) +ADC1_INIT_CODE_ATTEN3 (BLOCK2) ADC1 init code at atten3 = 735 R/W (0b1011011111) +ADC1_CAL_VOL_ATTEN0 (BLOCK2) ADC1 calibration voltage at atten0 = 535 R/W (0b1000010111) +ADC1_CAL_VOL_ATTEN1 (BLOCK2) ADC1 calibration voltage at atten1 = 31 R/W (0b0000011111) +ADC1_CAL_VOL_ATTEN2 (BLOCK2) ADC1 calibration voltage at atten2 = 533 R/W (0b1000010101) +ADC1_CAL_VOL_ATTEN3 (BLOCK2) ADC1 calibration voltage at atten3 = 567 R/W (0b1000110111) + +Config fuses: +ERR_RST_ENABLE (BLOCK0) Use BLOCK0 to check error record registers = with check R/W (0b1) + +Flash fuses: +FLASH_CAP (BLOCK1) Flash capacity = 4M R/W (0b001) +FLASH_TEMP (BLOCK1) Flash temperature = 105C R/W (0b01) +FLASH_VENDOR (BLOCK1) Flash vendor = XMC R/W (0b001) + +Identity fuses: +BLK_VERSION_MINOR (BLOCK1) BLK_VERSION_MINOR = 3 R/W (0b011) +WAFER_VERSION_MAJOR (BLOCK1) WAFER_VERSION_MAJOR = 1 R/W (0b01) +OPTIONAL_UNIQUE_ID (BLOCK2) Optional unique 128-bit ID + = 65 27 7c ca 5a 0c 48 02 6c b0 e5 a6 7d 94 30 f2 R/W +BLK_VERSION_MAJOR (BLOCK2) BLK_VERSION_MAJOR of BLOCK2 = With calibration R/W (0b01) + +Jtag fuses: + +Mac fuses: +MAC (BLOCK1) MAC address + = 60:55:f9:f7:81:cc (OK) R/W + +Security fuses: + +Spi Pad fuses: + +Usb fuses: + +User fuses: + +Vdd fuses: + +Wdt fuses: + + +I (331) example: Coding Scheme RS (Reed-Solomon coding) +I (331) example: read efuse fields +I (341) example: 1. read MAC address: 60:55:f9:f7:81:cc +I (341) example: 2. read secure_version: 0 +I (351) example: 3. read custom fields +I (351) example: module_version = 0 +I (351) example: device_role = None +I (361) example: setting_1 = 0 +I (361) example: setting_2 = 0 +I (361) example: custom_secure_version = 0 +W (371) example: This example does not burn any efuse in reality only virtually +W (371) example: Write operations in efuse fields are performed virtually +I (381) example: write custom efuse fields +I (381) example: In the case of 3/4 or RS coding scheme, you cannot write efuse fields separately +I (391) example: You should use the batch mode of writing fields for this +I (401) efuse: Batch mode of writing fields is enabled +I (401) example: Token dump of staged eFuse writes: +I (411) example: IDF_MONITOR_EXECUTE_ESPEFUSE_SUMMARY EFSW:esp32c3:100::::AAAAAAAAAAEayAcAAAAAAAAAAAAAAAAAAAAAAAAAAAA:::::::::7jMAoA --extend-efuse-table main/esp_efuse_custom_table.csv +--- Executing monitor command: espefuse --token EFSW:esp32c3:100::::AAAAAAAAAAEayAcAAAAAAAAAAAAAAAAAAAAAAAAAAAA:::::::::7jMAoA --extend-efuse-table main/esp_efuse_custom_table.csv summary --active +espefuse v5.1.0 + +=== Run "summary" command === +EFUSE_NAME (Block) Description = [Meaningful Value] [Readable/Writeable] (Hex Value) +---------------------------------------------------------------------------------------- +Config fuses: +BLOCK_USR_DATA (BLOCK3) User data + = 00 00 00 00 00 00 00 01 1a c8 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 R/W + +Flash fuses: + +Identity fuses: +WAFER_VERSION_MAJOR (BLOCK1) WAFER_VERSION_MAJOR = 1 R/W (0b01) + +Jtag fuses: + +Mac fuses: + +Security fuses: + +Spi Pad fuses: + +Usb fuses: + +User fuses: +MODULE_VERSION (BLOCK3) Module version (56-63) = 1 R/W (0x01) +DEVICE_ROLE (BLOCK3) Device role (64-66) = 2 R/W (0b010) +SETTING_1 (BLOCK3) Setting 1 (67-72) = 3 R/W (0b000011) +SETTING_2 (BLOCK3) Setting 2 (73-77) = 4 R/W (0b00100) +CUSTOM_SECURE_VERSION (BLOCK3) Custom secure version (78-93) = 31 R/W (0x001f) + +Vdd fuses: + +Wdt fuses: + + +W (421) efuse: Virtual efuses enabled: Not really burning eFuses +I (431) efuse: Batch mode. Prepared fields are committed +I (431) example: module_version = 1 +I (441) example: device_role = Slave +I (441) example: setting_1 = 3 +I (441) example: setting_2 = 4 +I (451) example: custom_secure_version = 5 +I (451) example: Done +I (451) main_task: Returned from app_main() ``` And for ``3/4`` coding scheme: diff --git a/examples/system/efuse/main/efuse_main.c b/examples/system/efuse/main/efuse_main.c index 253da4f7721..0224f8f33b9 100644 --- a/examples/system/efuse/main/efuse_main.c +++ b/examples/system/efuse/main/efuse_main.c @@ -8,6 +8,7 @@ */ #include +#include #include "sdkconfig.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" @@ -37,6 +38,9 @@ typedef struct { uint16_t reserve; /*!< Reserve */ } device_desc_t; +static char token[1024]; +// Option, used only to demonstrate how to read custom eFuse table +static const char *add_custom_efuse_table = "--extend-efuse-table main/esp_efuse_custom_table.csv"; static void print_device_desc(device_desc_t *desc) { @@ -107,7 +111,18 @@ static void write_efuse_fields(device_desc_t *desc, esp_efuse_coding_scheme_t co ESP_ERROR_CHECK(esp_efuse_write_field_cnt(ESP_EFUSE_CUSTOM_SECURE_VERSION, desc->custom_secure_version)); if (coding_scheme == coding_scheme_for_batch_mode) { - ESP_ERROR_CHECK(esp_efuse_batch_write_commit()); + ESP_LOGI(TAG, "Token dump of staged eFuse writes:"); + ESP_LOGW(TAG, "STAGED token dump can expose sensitive data, as it includes pending writes that are not yet burned and not yet read-protected."); + ESP_ERROR_CHECK(esp_efuse_token_dump(ESP_EFUSE_TOKEN_FROM_STAGED, token, sizeof(token))); + ESP_LOGI(TAG, "%s %s %s", ESP_EFUSE_MONITOR_EXECUTE_ESPEFUSE_SUMMARY, token, add_custom_efuse_table); + memset(token, 0, sizeof(token)); + + if (esp_efuse_batch_write_commit() != ESP_OK) { + ESP_LOGE(TAG, "Failed to commit staged eFuses. Run the following command in console to see the state of eFuses:"); + ESP_ERROR_CHECK(esp_efuse_token_dump(ESP_EFUSE_TOKEN_FROM_READ, token, sizeof(token))); + ESP_LOGI(TAG, "%s %s", ESP_EFUSE_MONITOR_EXECUTE_ESPEFUSE_DUMP, token); + memset(token, 0, sizeof(token)); + } } } #endif // defined(CONFIG_EFUSE_VIRTUAL) || defined(CONFIG_EXAMPLE_TEST_RUN_USING_QEMU) @@ -139,6 +154,15 @@ void app_main(void) { ESP_LOGI(TAG, "Start eFuse example"); + ESP_LOGI(TAG, "Initial token dump to log"); + ESP_LOGW(TAG, "The token dump from the READ includes only the final, permanently programmed values that are read-protected."); + ESP_LOGW(TAG, "But it still should be handled securely, as it can include sensitive data such as unique identifiers, secure version numbers, etc."); + ESP_LOGW(TAG, "Clear the token after dumping"); + ESP_LOGW(TAG, "Share this token only with trusted parties and keep it secure"); + ESP_ERROR_CHECK(esp_efuse_token_dump(ESP_EFUSE_TOKEN_FROM_READ, token, sizeof(token))); + ESP_LOGI(TAG, "%s %s %s", ESP_EFUSE_MONITOR_EXECUTE_ESPEFUSE_SUMMARY, token, add_custom_efuse_table); + memset(token, 0, sizeof(token)); + #ifdef CONFIG_SECURE_FLASH_ENC_ENABLED if (esp_flash_encryption_cfg_verify_release_mode()) { ESP_LOGI(TAG, "Flash Encryption is in RELEASE mode"); diff --git a/examples/system/efuse/sdkconfig.defaults b/examples/system/efuse/sdkconfig.defaults index 88ec85884a4..da6f7a88a9d 100644 --- a/examples/system/efuse/sdkconfig.defaults +++ b/examples/system/efuse/sdkconfig.defaults @@ -1,2 +1,3 @@ CONFIG_EFUSE_CUSTOM_TABLE=y CONFIG_EFUSE_VIRTUAL=y +CONFIG_EFUSE_ENABLE_STAGED_TOKEN_API=y