efuse: Burn operation does not block reading

This commit is contained in:
Konstantin Kondrashov 2021-06-18 11:52:47 +08:00 committed by Angus Gratton
parent 4d604ee6d2
commit 4c0cf40a39
13 changed files with 174 additions and 16 deletions

View File

@ -474,6 +474,13 @@ UT_006:
- UT_T1_SPIMODE
- psram
UT_007:
extends: .unit_test_esp32_template
parallel: 2
tags:
- ESP32_IDF
- UT_T1_1
UT_008:
extends: .unit_test_esp32_template
tags:

View File

@ -72,7 +72,7 @@ void esp_efuse_utility_clear_program_registers(void)
}
// Burn values written to the efuse write registers
void esp_efuse_utility_burn_efuses(void)
void esp_efuse_utility_burn_chip(void)
{
#ifdef CONFIG_EFUSE_VIRTUAL
ESP_LOGW(TAG, "Virtual efuses enabled: Not really burning eFuses");

View File

@ -75,7 +75,7 @@ void esp_efuse_utility_clear_program_registers(void)
}
// Burn values written to the efuse write registers
void esp_efuse_utility_burn_efuses(void)
void esp_efuse_utility_burn_chip(void)
{
#ifdef CONFIG_EFUSE_VIRTUAL
ESP_LOGW(TAG, "Virtual efuses enabled: Not really burning eFuses");

View File

@ -68,7 +68,7 @@ void esp_efuse_utility_clear_program_registers(void)
}
// Burn values written to the efuse write registers
void esp_efuse_utility_burn_efuses(void)
void esp_efuse_utility_burn_chip(void)
{
#ifdef CONFIG_EFUSE_VIRTUAL
ESP_LOGW(TAG, "Virtual efuses enabled: Not really burning eFuses");

View File

@ -68,7 +68,7 @@ void esp_efuse_utility_clear_program_registers(void)
}
// Burn values written to the efuse write registers
void esp_efuse_utility_burn_efuses(void)
void esp_efuse_utility_burn_chip(void)
{
#ifdef CONFIG_EFUSE_VIRTUAL
ESP_LOGW(TAG, "Virtual efuses enabled: Not really burning eFuses");

View File

@ -33,6 +33,7 @@ extern "C" {
#define ESP_ERR_EFUSE_REPEATED_PROG (ESP_ERR_EFUSE + 0x03) /*!< Error repeated programming of programmed bits is strictly forbidden. */
#define ESP_ERR_CODING (ESP_ERR_EFUSE + 0x04) /*!< Error while a encoding operation. */
#define ESP_ERR_NOT_ENOUGH_UNUSED_KEY_BLOCKS (ESP_ERR_EFUSE + 0x05) /*!< Error not enough unused key blocks available */
#define ESP_ERR_DAMAGED_READING (ESP_ERR_EFUSE + 0x06) /*!< Error. Burn or reset was done during a reading operation leads to damage read data. This error is internal to the efuse component and not returned by any public API. */
/**
* @brief Type definition for an eFuse field

View File

@ -96,6 +96,16 @@ esp_err_t esp_efuse_utility_fill_buff(unsigned int num_reg, esp_efuse_block_t ef
*/
void esp_efuse_utility_burn_efuses(void);
/**
* @brief Chip specific operations to perform the burn of values written to the efuse write registers.
*
* @note Use esp_efuse_utility_burn_efuses() to burn efuses.
*
* If CONFIG_EFUSE_VIRTUAL is set, writing will not be performed.
* After the function is completed, the writing registers are cleared.
*/
void esp_efuse_utility_burn_chip(void);
/**
* @brief Returns the number of array elements for placing these "bits" in an array with the length of each element equal to "size_of_base".
*/

View File

@ -17,6 +17,8 @@ const static char *TAG = "efuse";
#define EFUSE_LOCK_ACQUIRE_RECURSIVE()
#define EFUSE_LOCK_RELEASE_RECURSIVE()
#else
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include <sys/lock.h>
static _lock_t s_efuse_lock;
#define EFUSE_LOCK_ACQUIRE_RECURSIVE() _lock_acquire_recursive(&s_efuse_lock)
@ -30,15 +32,20 @@ static int s_batch_writing_mode = 0;
// read value from EFUSE, writing it into an array
esp_err_t esp_efuse_read_field_blob(const esp_efuse_desc_t* field[], void* dst, size_t dst_size_bits)
{
EFUSE_LOCK_ACQUIRE_RECURSIVE();
esp_err_t err = ESP_OK;
if (field == NULL || dst == NULL || dst_size_bits == 0) {
err = ESP_ERR_INVALID_ARG;
} else {
memset((uint8_t *)dst, 0, esp_efuse_utility_get_number_of_items(dst_size_bits, 8));
err = esp_efuse_utility_process(field, dst, dst_size_bits, esp_efuse_utility_fill_buff);
do {
memset((uint8_t *)dst, 0, esp_efuse_utility_get_number_of_items(dst_size_bits, 8));
err = esp_efuse_utility_process(field, dst, dst_size_bits, esp_efuse_utility_fill_buff);
#ifndef BOOTLOADER_BUILD
if (err == ESP_ERR_DAMAGED_READING) {
vTaskDelay(1);
}
#endif // BOOTLOADER_BUILD
} while (err == ESP_ERR_DAMAGED_READING);
}
EFUSE_LOCK_RELEASE_RECURSIVE();
return err;
}
@ -53,15 +60,20 @@ bool esp_efuse_read_field_bit(const esp_efuse_desc_t *field[])
// read number of bits programmed as "1" in the particular field
esp_err_t esp_efuse_read_field_cnt(const esp_efuse_desc_t* field[], size_t* out_cnt)
{
EFUSE_LOCK_ACQUIRE_RECURSIVE();
esp_err_t err = ESP_OK;
if (field == NULL || out_cnt == NULL) {
err = ESP_ERR_INVALID_ARG;
} else {
*out_cnt = 0;
err = esp_efuse_utility_process(field, out_cnt, 0, esp_efuse_utility_count_once);
do {
*out_cnt = 0;
err = esp_efuse_utility_process(field, out_cnt, 0, esp_efuse_utility_count_once);
#ifndef BOOTLOADER_BUILD
if (err == ESP_ERR_DAMAGED_READING) {
vTaskDelay(1);
}
#endif // BOOTLOADER_BUILD
} while (err == ESP_ERR_DAMAGED_READING);
}
EFUSE_LOCK_RELEASE_RECURSIVE();
return err;
}
@ -163,9 +175,10 @@ int esp_efuse_get_field_size(const esp_efuse_desc_t* field[])
// reading efuse register.
uint32_t esp_efuse_read_reg(esp_efuse_block_t blk, unsigned int num_reg)
{
EFUSE_LOCK_ACQUIRE_RECURSIVE();
uint32_t ret_val = esp_efuse_utility_read_reg(blk, num_reg);
EFUSE_LOCK_RELEASE_RECURSIVE();
uint32_t ret_val = 0;
esp_err_t err = esp_efuse_read_block(blk, &ret_val, num_reg * 32, 32);
assert(err == ESP_OK);
(void)err;
return ret_val;
}

View File

@ -14,6 +14,11 @@
static const char *TAG = "efuse";
// This counter is used to implement independent read access for efuses.
// During the read operation, the counter should be unchanged and even.
// If it is not so, we must repeat the read to make sure that the burn operation does not affect the read data.
static volatile unsigned s_burn_counter = 0;
// Array for emulate efuse registers.
#ifdef CONFIG_EFUSE_VIRTUAL
uint32_t virt_blocks[EFUSE_BLK_MAX][COUNT_EFUSE_REG_PER_BLOCK];
@ -50,6 +55,7 @@ esp_err_t esp_efuse_utility_process(const esp_efuse_desc_t* field[], void* ptr,
int req_size = (ptr_size_bits == 0) ? field_len : MIN(ptr_size_bits, field_len);
int i = 0;
unsigned count_before = s_burn_counter;
while (err == ESP_OK && req_size > bits_counter && field[i] != NULL) {
if (check_range_of_bits(field[i]->efuse_block, field[i]->bit_start, field[i]->bit_count) == false) {
ESP_EARLY_LOGE(TAG, "Range of data does not match the coding scheme");
@ -72,6 +78,12 @@ esp_err_t esp_efuse_utility_process(const esp_efuse_desc_t* field[], void* ptr,
}
i++;
}
unsigned count_after = s_burn_counter;
if (err == ESP_OK &&
(func_proc == esp_efuse_utility_fill_buff || func_proc == esp_efuse_utility_count_once) && // these functions are used for read APIs: read_field_blob and read_field_cnt
(count_before != count_after || (count_after & 1) == 1)) {
err = ESP_ERR_DAMAGED_READING;
}
assert(bits_counter <= req_size);
return err;
}
@ -141,7 +153,9 @@ esp_err_t esp_efuse_utility_write_cnt(unsigned int num_reg, esp_efuse_block_t ef
// Reset efuse write registers
void esp_efuse_utility_reset(void)
{
++s_burn_counter;
esp_efuse_utility_clear_program_registers();
++s_burn_counter;
for (int num_block = EFUSE_BLK0; num_block < EFUSE_BLK_MAX; num_block++) {
for (uint32_t addr_wr_block = range_write_addr_blocks[num_block].start; addr_wr_block <= range_write_addr_blocks[num_block].end; addr_wr_block += 4) {
REG_WRITE(addr_wr_block, 0);
@ -149,6 +163,14 @@ void esp_efuse_utility_reset(void)
}
}
// Burn values written to the efuse write registers
void esp_efuse_utility_burn_efuses(void)
{
++s_burn_counter;
esp_efuse_utility_burn_chip();
++s_burn_counter;
}
// Erase the virt_blocks array.
void esp_efuse_utility_erase_virt_blocks(void)
{

View File

@ -669,7 +669,7 @@ static void task2(void* arg)
TEST_ESP_OK(esp_efuse_read_field_blob(ESP_EFUSE_MAC_FACTORY, &mac, sizeof(mac) * 8));
int64_t t2 = esp_timer_get_time();
int diff_ms = (t2 - t1) / 1000;
TEST_ASSERT_GREATER_THAN(delay_ms, diff_ms);
TEST_ASSERT_GREATER_THAN(diff_ms, delay_ms);
ESP_LOGI(TAG, "read MAC address: %02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
xSemaphoreGive(sema);
@ -788,8 +788,100 @@ TEST_CASE("Test a write/read protection", "[efuse]")
esp_efuse_utility_erase_virt_blocks();
}
static volatile bool cmd_stop_reset_task1;
static void efuse_burn_task(void* arg)
{
SemaphoreHandle_t sema = (SemaphoreHandle_t) arg;
ESP_LOGI(TAG, "Start burn task");
size_t test3_len_6 = 2;
while (!cmd_stop_reset_task1) {
esp_efuse_utility_update_virt_blocks();
esp_efuse_utility_reset();
TEST_ESP_OK(esp_efuse_write_field_cnt(ESP_EFUSE_TEST3_LEN_6, test3_len_6));
}
xSemaphoreGive(sema);
ESP_LOGI(TAG, "Stop burn task");
vTaskDelete(NULL);
}
static void efuse_read_task(void* arg)
{
SemaphoreHandle_t sema = (SemaphoreHandle_t) arg;
ESP_LOGI(TAG, "Start read task");
size_t test3_len_6 = 0;
while (!cmd_stop_reset_task1) {
TEST_ESP_OK(esp_efuse_read_field_blob(ESP_EFUSE_TEST3_LEN_6, &test3_len_6, 6));
}
xSemaphoreGive(sema);
ESP_LOGI(TAG, "Stop read task");
vTaskDelete(NULL);
}
TEST_CASE("Check a case when ESP_ERR_DAMAGED_READING occurs and read and burn are not blocked", "[efuse]")
{
cmd_stop_reset_task1 = false;
TaskHandle_t read_task_hdl;
xSemaphoreHandle sema[2];
sema[0] = xSemaphoreCreateBinary();
sema[1] = xSemaphoreCreateBinary();
esp_efuse_utility_update_virt_blocks();
esp_efuse_utility_debug_dump_blocks();
xTaskCreatePinnedToCore(efuse_burn_task, "efuse_burn_task", 3072, sema[0], 2, NULL, 0);
xTaskCreatePinnedToCore(efuse_read_task, "efuse_read_task", 3072, sema[1], 2, &read_task_hdl, 0);
vTaskDelay(10 / portTICK_PERIOD_MS);
for (unsigned i = 1; i < 30; ++i) {
vTaskPrioritySet(read_task_hdl, 2 + i % 2);
vTaskDelay(10 / portTICK_PERIOD_MS);
}
vTaskDelay(10 / portTICK_PERIOD_MS);
cmd_stop_reset_task1 = true;
vTaskDelay(10 / portTICK_PERIOD_MS);
TEST_ASSERT_EQUAL(pdPASS, xSemaphoreTake(sema[0], 1000 / portTICK_PERIOD_MS));
TEST_ASSERT_EQUAL(pdPASS, xSemaphoreTake(sema[1], 1000 / portTICK_PERIOD_MS));
vSemaphoreDelete(sema[0]);
vSemaphoreDelete(sema[1]);
}
#endif // #ifdef CONFIG_EFUSE_VIRTUAL
#ifndef CONFIG_FREERTOS_UNICORE
static volatile bool cmd_stop_reset_task;
static void reset_task(void* arg)
{
ESP_LOGI(TAG, "Start reset task");
while (!cmd_stop_reset_task) {
esp_efuse_utility_reset();
vTaskDelay(1);
}
vTaskDelete(NULL);
}
TEST_CASE("Check a case when ESP_ERR_DAMAGED_READING occurs during reading efuses", "[efuse]")
{
cmd_stop_reset_task = false;
esp_efuse_utility_update_virt_blocks();
esp_efuse_utility_debug_dump_blocks();
uint8_t mac[6];
TEST_ESP_OK(esp_efuse_read_field_blob(ESP_EFUSE_MAC_FACTORY, &mac, sizeof(mac) * 8));
ESP_LOGI(TAG, "read MAC address: %02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
xTaskCreatePinnedToCore(reset_task, "reset_task", 3072, NULL, UNITY_FREERTOS_PRIORITY - 1, NULL, 1);
uint8_t new_mac[6];
for (int i = 0; i < 1000; ++i) {
TEST_ESP_OK(esp_efuse_read_field_blob(ESP_EFUSE_MAC_FACTORY, &new_mac, sizeof(new_mac) * 8));
TEST_ASSERT_EQUAL_HEX8_ARRAY(mac, new_mac, sizeof(mac));
}
cmd_stop_reset_task = true;
ESP_LOGI(TAG, "read new MAC address: %02x:%02x:%02x:%02x:%02x:%02x", new_mac[0], new_mac[1], new_mac[2], new_mac[3], new_mac[4], new_mac[5]);
vTaskDelay(100 / portTICK_PERIOD_MS);
}
#endif // if not CONFIG_FREERTOS_UNICORE
#ifdef CONFIG_IDF_ENV_FPGA
TEST_CASE("Test a real write (FPGA)", "[efuse]")
{

View File

@ -296,6 +296,13 @@ static const esp_err_msg_t esp_err_msg_table[] = {
# endif
# ifdef ESP_ERR_NOT_ENOUGH_UNUSED_KEY_BLOCKS
ERR_TBL_IT(ESP_ERR_NOT_ENOUGH_UNUSED_KEY_BLOCKS), /* 5637 0x1605 Error not enough unused key blocks available */
# endif
# ifdef ESP_ERR_DAMAGED_READING
ERR_TBL_IT(ESP_ERR_DAMAGED_READING), /* 5638 0x1606 Error. Burn or reset was done during a
reading operation leads to damage read
data. This error is internal to the
efuse component and not returned by any
public API. */
# endif
// components/bootloader_support/include/esp_image_format.h
# ifdef ESP_ERR_IMAGE_BASE

View File

@ -3,3 +3,4 @@ CONFIG_BOOTLOADER_APP_ANTI_ROLLBACK=y
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_example.csv"
CONFIG_PARTITION_TABLE_OFFSET=0x9000

View File

@ -0,0 +1,5 @@
CONFIG_BOOTLOADER_LOG_LEVEL_VERBOSE=y
CONFIG_BOOTLOADER_LOG_LEVEL=5
CONFIG_LOG_DEFAULT_LEVEL_VERBOSE=y
CONFIG_LOG_DEFAULT_LEVEL=5
CONFIG_PARTITION_TABLE_OFFSET=0x9000