Merge branch 'bugfix/wl_encryption' into 'master'

Bugfix: WL works with encrypted flash

See merge request idf/esp-idf!2530
This commit is contained in:
Angus Gratton 2018-08-01 15:05:47 +08:00
commit 7ea4db72de
17 changed files with 455 additions and 75 deletions

View file

@ -115,3 +115,12 @@ BYTE ff_diskio_get_pdrv_wl(wl_handle_t flash_handle)
}
return 0xff;
}
void ff_diskio_clear_pdrv_wl(wl_handle_t flash_handle)
{
for (int i = 0; i < FF_VOLUMES; i++) {
if (flash_handle == ff_wl_handles[i]) {
ff_wl_handles[i] = WL_INVALID_HANDLE;
}
}
}

View file

@ -31,6 +31,7 @@ extern "C" {
*/
esp_err_t ff_diskio_register_wl_partition(BYTE pdrv, wl_handle_t flash_handle);
BYTE ff_diskio_get_pdrv_wl(wl_handle_t flash_handle);
void ff_diskio_clear_pdrv_wl(wl_handle_t flash_handle);
#ifdef __cplusplus
}

View file

@ -124,6 +124,7 @@ esp_err_t esp_vfs_fat_spiflash_unmount(const char *base_path, wl_handle_t wl_han
f_mount(0, drv, 0);
ff_diskio_unregister(pdrv);
ff_diskio_clear_pdrv_wl(wl_handle);
// release partition driver
esp_err_t err_drv = wl_unmount(wl_handle);
esp_err_t err = esp_vfs_fat_unregister_path(base_path);

View file

@ -214,6 +214,11 @@ TEST_CASE("(SD) mount two FAT partitions, SDMMC and WL, at the same time", "[fat
const char* str_sd = "this is sd\n";
const char* str_wl = "this is spiflash\n";
/* Erase flash before the firs use */
const esp_partition_t *test_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "flash_test");
esp_partition_erase_range(test_partition, 0, test_partition->size);
printf("Partition erased: addr- 0x%08x, size- 0x%08x\n", test_partition->address, test_partition->size);
/* Mount FATFS in SD can WL at the same time. Create a file on each FS */
wl_handle_t wl_handle = WL_INVALID_HANDLE;
test_setup();

View file

@ -2,7 +2,8 @@ SOURCE_FILES := \
app_update/esp_ota_eps.c \
log/log.c \
newlib/lock.c \
esp32/crc.cpp
esp32/crc.cpp \
esp32/esp_random.c
INCLUDE_DIRS := \
../include \

View file

@ -0,0 +1,14 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <time.h>
#include <string.h>
#include <stddef.h>
#include "esp_system.h"
uint32_t esp_random(void)
{
return (uint32_t)rand();
}

View file

@ -0,0 +1,15 @@
#pragma once
#include <stdint.h>
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
uint32_t esp_random(void);
#ifdef __cplusplus
}
#endif

View file

@ -44,11 +44,13 @@ void esp_log_write(esp_log_level_t level, const char* tag, const char* format, .
#define ESP_LOGE( tag, format, ... ) if (LOG_LOCAL_LEVEL >= ESP_LOG_ERROR) { esp_log_write(ESP_LOG_ERROR, tag, LOG_FORMAT(E, format), esp_log_timestamp(), tag, ##__VA_ARGS__); }
#define ESP_LOGV( tag, format, ... ) if (LOG_LOCAL_LEVEL >= ESP_LOG_VERBOSE) { esp_log_write(ESP_LOG_VERBOSE, tag, LOG_FORMAT(V, format), esp_log_timestamp(), tag, ##__VA_ARGS__); }
#define ESP_LOGW( tag, format, ... ) if (LOG_LOCAL_LEVEL >= ESP_LOG_WARN) { esp_log_write(ESP_LOG_WARN, tag, LOG_FORMAT(W, format), esp_log_timestamp(), tag, ##__VA_ARGS__); }
#define ESP_LOGI( tag, format, ... ) if (LOG_LOCAL_LEVEL >= ESP_LOG_INFO) { esp_log_write(ESP_LOG_INFO, tag, LOG_FORMAT(E, format), esp_log_timestamp(), tag, ##__VA_ARGS__); }
#define ESP_LOGD( tag, format, ... ) if (LOG_LOCAL_LEVEL >= ESP_LOG_DEBUG) { esp_log_write(ESP_LOG_DEBUG, tag, LOG_FORMAT(D, format), esp_log_timestamp(), tag, ##__VA_ARGS__); }
#define ESP_LOGW( tag, format, ... ) if (LOG_LOCAL_LEVEL >= ESP_LOG_WARN) { esp_log_write(ESP_LOG_WARN, tag, LOG_FORMAT(W, format), esp_log_timestamp(), tag, ##__VA_ARGS__); }
#define ESP_LOGV( tag, format, ... ) if (LOG_LOCAL_LEVEL >= ESP_LOG_VERBOSE) { esp_log_write(ESP_LOG_VERBOSE, tag, LOG_FORMAT(V, format), esp_log_timestamp(), tag, ##__VA_ARGS__); }
// Assume that flash encryption is not enabled. Put here since in partition.c
// esp_log.h is included later than esp_flash_encrypt.h.

View file

@ -12,11 +12,13 @@
// limitations under the License.
#include <stdio.h>
#include "esp_system.h"
#include "esp_log.h"
#include "WL_Flash.h"
#include <stdlib.h>
#include "crc32.h"
#include <string.h>
#include <stddef.h>
static const char *TAG = "wl_flash";
#ifndef WL_CFG_CRC_CONST
@ -55,9 +57,12 @@ esp_err_t WL_Flash::config(wl_config_t *cfg, Flash_Access *flash_drv)
cfg->version,
(uint32_t) cfg->temp_buff_size);
cfg->crc = crc32::crc32_le(WL_CFG_CRC_CONST, (const unsigned char *)cfg, sizeof(wl_config_t) - sizeof(cfg->crc));
cfg->crc = crc32::crc32_le(WL_CFG_CRC_CONST, (const unsigned char *)cfg, offsetof(wl_config_t, crc));
esp_err_t result = ESP_OK;
memcpy(&this->cfg, cfg, sizeof(wl_config_t));
if (this->cfg.temp_buff_size < this->cfg.wr_size) {
this->cfg.temp_buff_size = this->cfg.wr_size;
}
this->configured = false;
if (cfg == NULL) {
result = ESP_ERR_INVALID_ARG;
@ -74,7 +79,6 @@ esp_err_t WL_Flash::config(wl_config_t *cfg, Flash_Access *flash_drv)
}
WL_RESULT_CHECK(result);
this->temp_buff = (uint8_t *)malloc(this->cfg.temp_buff_size);
this->state_size = this->cfg.sector_size;
if (this->state_size < (sizeof(wl_state_t) + (this->cfg.full_mem_size / this->cfg.sector_size)*this->cfg.wr_size)) {
this->state_size = ((sizeof(wl_state_t) + (this->cfg.full_mem_size / this->cfg.sector_size) * this->cfg.wr_size) + this->cfg.sector_size - 1) / this->cfg.sector_size;
@ -87,11 +91,27 @@ esp_err_t WL_Flash::config(wl_config_t *cfg, Flash_Access *flash_drv)
this->addr_state1 = this->cfg.start_addr + this->cfg.full_mem_size - this->state_size * 2 - this->cfg_size; // allocate data at the end of memory
this->addr_state2 = this->cfg.start_addr + this->cfg.full_mem_size - this->state_size * 1 - this->cfg_size; // allocate data at the end of memory
ptrdiff_t flash_sz = ((this->cfg.full_mem_size - this->state_size * 2 - this->cfg_size) / this->cfg.page_size - 1) * this->cfg.page_size; // -1 remove dummy block
this->flash_size = ((this->cfg.full_mem_size - this->state_size * 2 - this->cfg_size) / this->cfg.page_size - 1) * this->cfg.page_size; // -1 remove dummy block
ESP_LOGV(TAG, "%s - this->addr_state1=0x%08x", __func__, (uint32_t) this->addr_state1);
ESP_LOGV(TAG, "%s - this->addr_state2=0x%08x", __func__, (uint32_t) this->addr_state2);
ESP_LOGD(TAG, "%s - config result: state_size=0x%08x, cfg_size=0x%08x, addr_cfg=0x%08x, addr_state1=0x%08x, addr_state2=0x%08x, flash_size=0x%08x", __func__,
(uint32_t) this->state_size,
(uint32_t) this->cfg_size,
(uint32_t) this->addr_cfg,
(uint32_t) this->addr_state1,
(uint32_t) this->addr_state2,
(uint32_t) this->flash_size
);
if (flash_sz <= 0) {
result = ESP_ERR_INVALID_ARG;
}
WL_RESULT_CHECK(result);
this->temp_buff = (uint8_t *)malloc(this->cfg.temp_buff_size);
if (this->temp_buff == NULL) {
result = ESP_ERR_NO_MEM;
}
WL_RESULT_CHECK(result);
this->configured = true;
return ESP_OK;
}
@ -112,12 +132,12 @@ esp_err_t WL_Flash::init()
result = this->flash_drv->read(this->addr_state2, state_copy, sizeof(wl_state_t));
WL_RESULT_CHECK(result);
int check_size = sizeof(wl_state_t) - sizeof(uint32_t);
int check_size = offsetof(wl_state_t, crc);
// Chech CRC and recover state
uint32_t crc1 = crc32::crc32_le(WL_CFG_CRC_CONST, (uint8_t *)&this->state, check_size);
uint32_t crc2 = crc32::crc32_le(WL_CFG_CRC_CONST, (uint8_t *)state_copy, check_size);
ESP_LOGD(TAG, "%s - config ID=%i, stored ID=%i, access_count=%i, block_size=%i, max_count=%i, pos=%i, move_count=%i",
ESP_LOGD(TAG, "%s - config ID=%i, stored ID=%i, access_count=%i, block_size=%i, max_count=%i, pos=%i, move_count=0x%8.8X",
__func__,
this->cfg.version,
this->state.version,
@ -127,8 +147,7 @@ esp_err_t WL_Flash::init()
this->state.pos,
this->state.move_count);
ESP_LOGD(TAG, "%s starts: crc1=%i, crc2 = %i, this->state.crc=%i, state_copy->crc=%i", __func__, crc1, crc2, this->state.crc, state_copy->crc);
ESP_LOGD(TAG, "%s starts: crc1= 0x%08x, crc2 = 0x%08x, this->state.crc= 0x%08x, state_copy->crc= 0x%08x, version=%i, read_version=%i", __func__, crc1, crc2, this->state.crc, state_copy->crc, this->cfg.version, this->state.version);
if ((crc1 == this->state.crc) && (crc2 == state_copy->crc)) {
// The state is OK. Check the ID
if (this->state.version != this->cfg.version) {
@ -143,22 +162,30 @@ esp_err_t WL_Flash::init()
result = this->flash_drv->write(this->addr_state2, &this->state, sizeof(wl_state_t));
WL_RESULT_CHECK(result);
for (size_t i = 0; i < ((this->cfg.full_mem_size / this->cfg.sector_size)*this->cfg.wr_size); i++) {
uint8_t pos_bits = 0;
result = this->flash_drv->read(this->addr_state1 + sizeof(wl_state_t) + i, &pos_bits, 1);
bool pos_bits;
result = this->flash_drv->read(this->addr_state1 + sizeof(wl_state_t) + i * this->cfg.wr_size, this->temp_buff, this->cfg.wr_size);
WL_RESULT_CHECK(result);
if (pos_bits != 0xff) {
result = this->flash_drv->write(this->addr_state2 + sizeof(wl_state_t) + i, &pos_bits, 1);
pos_bits = this->OkBuffSet(i);
if (pos_bits == true) {
//this->fillOkBuff(i);
result = this->flash_drv->write(this->addr_state2 + sizeof(wl_state_t) + i * this->cfg.wr_size, this->temp_buff, this->cfg.wr_size);
WL_RESULT_CHECK(result);
}
}
}
ESP_LOGD(TAG, "%s: crc1=%i, crc2 = %i, result=%i", __func__, crc1, crc2, result);
ESP_LOGD(TAG, "%s: crc1=0x%08x, crc2 = 0x%08x, result= 0x%08x", __func__, crc1, crc2, (uint32_t)result);
result = this->recoverPos();
WL_RESULT_CHECK(result);
}
} else if ((crc1 != this->state.crc) && (crc2 != state_copy->crc)) { // This is just new flash
result = this->initSections();
WL_RESULT_CHECK(result);
} else if ((crc1 != this->state.crc) && (crc2 != state_copy->crc)) { // This is just new flash or new version
// Check if this is new version or just new instance of WL
ESP_LOGD(TAG, "%s: try to update version - crc1= 0x%08x, crc2 = 0x%08x, result= 0x%08x", __func__, (uint32_t)crc1, (uint32_t)crc2, (uint32_t)result);
result = this->updateVersion();
if (result == ESP_FAIL) {
ESP_LOGD(TAG, "%s: init flash sections", __func__);
result = this->initSections();
WL_RESULT_CHECK(result);
}
result = this->recoverPos();
WL_RESULT_CHECK(result);
} else {
@ -169,11 +196,12 @@ esp_err_t WL_Flash::init()
result = this->flash_drv->write(this->addr_state2, &this->state, sizeof(wl_state_t));
WL_RESULT_CHECK(result);
for (size_t i = 0; i < ((this->cfg.full_mem_size / this->cfg.sector_size) * this->cfg.wr_size); i++) {
uint8_t pos_bits = 0;
result = this->flash_drv->read(this->addr_state1 + sizeof(wl_state_t) + i, &pos_bits, 1);
bool pos_bits;
result = this->flash_drv->read(this->addr_state1 + sizeof(wl_state_t) + i * this->cfg.wr_size, this->temp_buff, this->cfg.wr_size);
WL_RESULT_CHECK(result);
if (pos_bits != 0xff) {
result = this->flash_drv->write(this->addr_state2 + sizeof(wl_state_t) + i, &pos_bits, 1);
pos_bits = this->OkBuffSet(i);
if (pos_bits == true) {
result = this->flash_drv->write(this->addr_state2 + sizeof(wl_state_t) + i * this->cfg.wr_size, this->temp_buff, this->cfg.wr_size);
WL_RESULT_CHECK(result);
}
}
@ -185,11 +213,13 @@ esp_err_t WL_Flash::init()
result = this->flash_drv->write(this->addr_state1, state_copy, sizeof(wl_state_t));
WL_RESULT_CHECK(result);
for (size_t i = 0; i < ((this->cfg.full_mem_size / this->cfg.sector_size) * this->cfg.wr_size); i++) {
uint8_t pos_bits = 0;
result = this->flash_drv->read(this->addr_state2 + sizeof(wl_state_t) + i, &pos_bits, 1);
bool pos_bits;
result = this->flash_drv->read(this->addr_state2 + sizeof(wl_state_t) + i * this->cfg.wr_size, this->temp_buff, this->cfg.wr_size);
WL_RESULT_CHECK(result);
if (pos_bits != 0xff) {
result = this->flash_drv->write(this->addr_state1 + sizeof(wl_state_t) + i, &pos_bits, 1);
pos_bits = this->OkBuffSet(i);
if (pos_bits == true) {
result = this->flash_drv->write(this->addr_state1 + sizeof(wl_state_t) + i * this->cfg.wr_size, this->temp_buff, this->cfg.wr_size);
WL_RESULT_CHECK(result);
}
}
@ -206,10 +236,11 @@ esp_err_t WL_Flash::init()
}
if (result != ESP_OK) {
this->initialized = false;
ESP_LOGE(TAG, "%s: returned 0x%x", __func__, result);
ESP_LOGE(TAG, "%s: returned 0x%08x", __func__, (uint32_t)result);
return result;
}
this->initialized = true;
ESP_LOGD(TAG, "%s - move_count= 0x%08x", __func__, (uint32_t)this->state.move_count);
return ESP_OK;
}
@ -217,20 +248,25 @@ esp_err_t WL_Flash::recoverPos()
{
esp_err_t result = ESP_OK;
size_t position = 0;
ESP_LOGV(TAG, "%s start", __func__);
for (size_t i = 0; i < this->state.max_pos; i++) {
uint8_t pos_bits = 0;
result = this->flash_drv->read(this->addr_state1 + sizeof(wl_state_t) + i * this->cfg.wr_size, &pos_bits, 1);
WL_RESULT_CHECK(result);
bool pos_bits;
position = i;
if (pos_bits == 0xff) {
result = this->flash_drv->read(this->addr_state1 + sizeof(wl_state_t) + i * this->cfg.wr_size, this->temp_buff, this->cfg.wr_size);
pos_bits = this->OkBuffSet(i);
WL_RESULT_CHECK(result);
ESP_LOGV(TAG, "%s - check pos: result=0x%08x, position= %i, pos_bits= 0x%08x", __func__, (uint32_t)result, (uint32_t)position, (uint32_t)pos_bits);
if (pos_bits == false) {
break; // we have found position
}
}
this->state.pos = position;
if (this->state.pos == this->state.max_pos) {
this->state.pos--;
}
ESP_LOGD(TAG, "%s - this->state.pos=0x%08x, result=%08x", __func__, this->state.pos, result);
ESP_LOGD(TAG, "%s - this->state.pos= 0x%08x, position= 0x%08x, result= 0x%08x, max_pos= 0x%08x", __func__, (uint32_t)this->state.pos, (uint32_t)position, (uint32_t)result, (uint32_t)this->state.max_pos);
ESP_LOGV(TAG, "%s done", __func__);
return result;
}
@ -247,11 +283,12 @@ esp_err_t WL_Flash::initSections()
}
this->state.version = this->cfg.version;
this->state.block_size = this->cfg.page_size;
this->used_bits = 0;
this->state.device_id = esp_random();
memset(this->state.reserved, 0, sizeof(this->state.reserved));
this->state.max_pos = 1 + this->flash_size / this->cfg.page_size;
this->state.crc = crc32::crc32_le(WL_CFG_CRC_CONST, (uint8_t *)&this->state, sizeof(wl_state_t) - sizeof(uint32_t));
this->state.crc = crc32::crc32_le(WL_CFG_CRC_CONST, (uint8_t *)&this->state, offsetof(wl_state_t, crc));
result = this->flash_drv->erase_range(this->addr_state1, this->state_size);
WL_RESULT_CHECK(result);
@ -268,11 +305,126 @@ esp_err_t WL_Flash::initSections()
result = this->flash_drv->write(this->addr_cfg, &this->cfg, sizeof(wl_config_t));
WL_RESULT_CHECK(result);
ESP_LOGD(TAG, "%s - this->state->max_count=%08x, this->state->max_pos=%08x", __func__, this->state.max_count, this->state.max_pos);
ESP_LOGD(TAG, "%s - result=%08x", __func__, result);
ESP_LOGD(TAG, "%s - this->state->max_count= 0x%08x, this->state->max_pos= 0x%08x", __func__, this->state.max_count, this->state.max_pos);
ESP_LOGD(TAG, "%s - result= 0x%08x", __func__, result);
return result;
}
esp_err_t WL_Flash::updateVersion()
{
esp_err_t result = ESP_OK;
result = this->updateV1_V2();
if (result == ESP_OK) {
return result;
}
// check next version
return result;
}
esp_err_t WL_Flash::updateV1_V2()
{
esp_err_t result = ESP_OK;
// Check crc for old version and old version
ESP_LOGV(TAG, "%s start", __func__);
int check_size = offsetof(wl_state_t, device_id);
// Chech CRC and recover state
uint32_t crc1 = crc32::crc32_le(WL_CFG_CRC_CONST, (uint8_t *)&this->state, check_size);
wl_state_t sa_copy;
wl_state_t *state_copy = &sa_copy;
result = this->flash_drv->read(this->addr_state2, state_copy, sizeof(wl_state_t));
WL_RESULT_CHECK(result);
uint32_t crc2 = crc32::crc32_le(WL_CFG_CRC_CONST, (uint8_t *)state_copy, check_size);
// For V1 crc in place of device_id and version
uint32_t v1_crc1 = this->state.device_id;
uint32_t v1_crc2 = state_copy->device_id;
ESP_LOGD(TAG, "%s - process crc1=0x%08x, crc2=0x%08x, v1_crc1=0x%08x, v1_crc2=0x%08x, version=%i", __func__, crc1, crc2, v1_crc1, v1_crc2, this->state.version);
if ((crc1 == v1_crc1) && (crc2 == v1_crc2) && (v1_crc1 == v1_crc2) && (this->state.version == 1) && (state_copy->version == 1)) {
// Here we have to update all internal structures
ESP_LOGI(TAG, "%s Update from V1 to V2, crc=0x%08x, ", __func__, crc1);
uint32_t pos = 0;
for (size_t i = 0; i < this->state.max_pos; i++) {
uint8_t pos_bits;
result = this->flash_drv->read(this->addr_state1 + sizeof(wl_state_t) + i * this->cfg.wr_size, &pos_bits, 1);
WL_RESULT_CHECK(result);
ESP_LOGV(TAG, "%s- result= 0x%08x, pos= %i, pos_bits= 0x%08x", __func__, (uint32_t)result, (uint32_t)pos, (uint32_t)pos_bits);
pos = i;
if (pos_bits == 0xff) {
break; // we have found position
}
}
ESP_LOGI(TAG, "%s max_pos=%i, pos=%i, state.ver=%i, state2.ver=%i", __func__, (uint32_t)this->state.max_pos, (uint32_t)pos, (uint32_t)this->state.version, (uint32_t)state_copy->version);
if (pos == this->state.max_pos) {
pos--;
}
WL_RESULT_CHECK(result);
this->state.version = 2;
this->state.pos = 0;
this->state.crc = crc32::crc32_le(WL_CFG_CRC_CONST, (uint8_t *)&this->state, offsetof(wl_state_t, crc));
this->state.device_id = esp_random();
memset(this->state.reserved, 0, sizeof(this->state.reserved));
result = this->flash_drv->erase_range(this->addr_state1, this->state_size);
WL_RESULT_CHECK(result);
result = this->flash_drv->write(this->addr_state1, &this->state, sizeof(wl_state_t));
WL_RESULT_CHECK(result);
memset(this->temp_buff, 0, this->cfg.wr_size);
for (uint32_t i = 0 ; i <= pos; i++) {
this->fillOkBuff(i);
result = this->flash_drv->write(this->addr_state1 + sizeof(wl_state_t) + i * this->cfg.wr_size, this->temp_buff, this->cfg.wr_size);
WL_RESULT_CHECK(result);
}
result = this->flash_drv->erase_range(this->addr_state2, this->state_size);
WL_RESULT_CHECK(result);
result = this->flash_drv->write(this->addr_state2, &this->state, sizeof(wl_state_t));
WL_RESULT_CHECK(result);
ESP_LOGD(TAG, "%s - move_count= 0x%08x, pos= 0x%08x", __func__, this->state.move_count, this->state.pos);
memset(this->temp_buff, 0, this->cfg.wr_size);
for (uint32_t i = 0 ; i <= pos; i++) {
this->fillOkBuff(i);
result = this->flash_drv->write(this->addr_state2 + sizeof(wl_state_t) + i * this->cfg.wr_size, this->temp_buff, this->cfg.wr_size);
WL_RESULT_CHECK(result);
}
this->state.pos = pos;
return result;
}
return ESP_FAIL;
}
void WL_Flash::fillOkBuff(int n)
{
uint32_t *buff = (uint32_t *)this->temp_buff;
for (int i = 0 ; i < 4 ; i++) {
buff[i] = this->state.device_id + n * 4 + i;
buff[i] = crc32::crc32_le(WL_CFG_CRC_CONST, (uint8_t *)&buff[i], sizeof(uint32_t));
}
}
bool WL_Flash::OkBuffSet(int n)
{
bool result = true;
uint32_t *data_buff = (uint32_t *)this->temp_buff;
for (int i = 0 ; i < 4 ; i++) {
uint32_t data = this->state.device_id + n * 4 + i;
uint32_t crc = crc32::crc32_le(WL_CFG_CRC_CONST, (uint8_t *)&data, sizeof(uint32_t));
if (crc != data_buff[i]) {
result = false;
}
}
return result;
}
esp_err_t WL_Flash::updateWL()
{
esp_err_t result = ESP_OK;
@ -282,7 +434,7 @@ esp_err_t WL_Flash::updateWL()
}
// Here we have to move the block and increase the state
this->state.access_count = 0;
ESP_LOGV(TAG, "%s - access_count=0x%08x, pos=0x%08x", __func__, this->state.access_count, this->state.pos);
ESP_LOGV(TAG, "%s - access_count= 0x%08x, pos= 0x%08x", __func__, this->state.access_count, this->state.pos);
// copy data to dummy block
size_t data_addr = this->state.pos + 1; // next block, [pos+1] copy to [pos]
if (data_addr >= this->state.max_pos) {
@ -292,7 +444,7 @@ esp_err_t WL_Flash::updateWL()
this->dummy_addr = this->cfg.start_addr + this->state.pos * this->cfg.page_size;
result = this->flash_drv->erase_range(this->dummy_addr, this->cfg.page_size);
if (result != ESP_OK) {
ESP_LOGE(TAG, "%s - erase wl dummy sector result=%08x", __func__, result);
ESP_LOGE(TAG, "%s - erase wl dummy sector result= 0x%08x", __func__, result);
this->state.access_count = this->state.max_count - 1; // we will update next time
return result;
}
@ -301,13 +453,13 @@ esp_err_t WL_Flash::updateWL()
for (size_t i = 0; i < copy_count; i++) {
result = this->flash_drv->read(data_addr + i * this->cfg.temp_buff_size, this->temp_buff, this->cfg.temp_buff_size);
if (result != ESP_OK) {
ESP_LOGE(TAG, "%s - not possible to read buffer, will try next time, result=%08x", __func__, result);
ESP_LOGE(TAG, "%s - not possible to read buffer, will try next time, result= 0x%08x", __func__, result);
this->state.access_count = this->state.max_count - 1; // we will update next time
return result;
}
result = this->flash_drv->write(this->dummy_addr + i * this->cfg.temp_buff_size, this->temp_buff, this->cfg.temp_buff_size);
if (result != ESP_OK) {
ESP_LOGE(TAG, "%s - not possible to write buffer, will try next time, result=%08x", __func__, result);
ESP_LOGE(TAG, "%s - not possible to write buffer, will try next time, result= 0x%08x", __func__, result);
this->state.access_count = this->state.max_count - 1; // we will update next time
return result;
}
@ -316,17 +468,18 @@ esp_err_t WL_Flash::updateWL()
// Here we will update structures...
// Update bits and save to flash:
uint32_t byte_pos = this->state.pos * this->cfg.wr_size;
this->used_bits = 0;
this->fillOkBuff(this->state.pos);
// write state to mem. We updating only affected bits
result |= this->flash_drv->write(this->addr_state1 + sizeof(wl_state_t) + byte_pos, &this->used_bits, this->cfg.wr_size);
result |= this->flash_drv->write(this->addr_state1 + sizeof(wl_state_t) + byte_pos, this->temp_buff, this->cfg.wr_size);
if (result != ESP_OK) {
ESP_LOGE(TAG, "%s - update position 1 result=%08x", __func__, result);
ESP_LOGE(TAG, "%s - update position 1 result= 0x%08x", __func__, result);
this->state.access_count = this->state.max_count - 1; // we will update next time
return result;
}
result |= this->flash_drv->write(this->addr_state2 + sizeof(wl_state_t) + byte_pos, &this->used_bits, this->cfg.wr_size);
this->fillOkBuff(this->state.pos);
result |= this->flash_drv->write(this->addr_state2 + sizeof(wl_state_t) + byte_pos, this->temp_buff, this->cfg.wr_size);
if (result != ESP_OK) {
ESP_LOGE(TAG, "%s - update position 2 result=%08x", __func__, result);
ESP_LOGE(TAG, "%s - update position 2 result= 0x%08x", __func__, result);
this->state.access_count = this->state.max_count - 1; // we will update next time
return result;
}
@ -340,7 +493,7 @@ esp_err_t WL_Flash::updateWL()
this->state.move_count = 0;
}
// write main state
this->state.crc = crc32::crc32_le(WL_CFG_CRC_CONST, (uint8_t *)&this->state, sizeof(wl_state_t) - sizeof(uint32_t));
this->state.crc = crc32::crc32_le(WL_CFG_CRC_CONST, (uint8_t *)&this->state, offsetof(wl_state_t, crc));
result = this->flash_drv->erase_range(this->addr_state1, this->state_size);
WL_RESULT_CHECK(result);
@ -350,13 +503,13 @@ esp_err_t WL_Flash::updateWL()
WL_RESULT_CHECK(result);
result = this->flash_drv->write(this->addr_state2, &this->state, sizeof(wl_state_t));
WL_RESULT_CHECK(result);
ESP_LOGD(TAG, "%s - move_count=%08x", __func__, this->state.move_count);
ESP_LOGD(TAG, "%s - move_count= 0x%08x, pos= 0x%08x, ", __func__, this->state.move_count, this->state.pos);
}
// Save structures to the flash... and check result
if (result == ESP_OK) {
ESP_LOGV(TAG, "%s - result=%08x", __func__, result);
ESP_LOGV(TAG, "%s - result= 0x%08x", __func__, result);
} else {
ESP_LOGE(TAG, "%s - result=%08x", __func__, result);
ESP_LOGE(TAG, "%s - result= 0x%08x", __func__, result);
}
return result;
}
@ -369,7 +522,7 @@ size_t WL_Flash::calcAddr(size_t addr)
} else {
result += this->cfg.page_size;
}
ESP_LOGV(TAG, "%s - addr=0x%08x -> result=0x%08x", __func__, (uint32_t) addr, (uint32_t) result);
ESP_LOGV(TAG, "%s - addr= 0x%08x -> result= 0x%08x, dummy_addr= 0x%08x", __func__, (uint32_t) addr, (uint32_t) result, (uint32_t)dummy_addr);
return result;
}
@ -396,7 +549,7 @@ esp_err_t WL_Flash::erase_sector(size_t sector)
if (!this->initialized) {
return ESP_ERR_INVALID_STATE;
}
ESP_LOGV(TAG, "%s - sector=0x%08x", __func__, (uint32_t) sector);
ESP_LOGD(TAG, "%s - sector= 0x%08x", __func__, (uint32_t) sector);
result = this->updateWL();
WL_RESULT_CHECK(result);
size_t virt_addr = this->calcAddr(sector * this->cfg.sector_size);
@ -410,14 +563,14 @@ esp_err_t WL_Flash::erase_range(size_t start_address, size_t size)
if (!this->initialized) {
return ESP_ERR_INVALID_STATE;
}
ESP_LOGV(TAG, "%s - start_address=0x%08x, size=0x%08x", __func__, (uint32_t) start_address, (uint32_t) size);
ESP_LOGD(TAG, "%s - start_address= 0x%08x, size= 0x%08x", __func__, (uint32_t) start_address, (uint32_t) size);
size_t erase_count = (size + this->cfg.sector_size - 1) / this->cfg.sector_size;
size_t start_sector = start_address / this->cfg.sector_size;
for (size_t i = 0; i < erase_count; i++) {
result = this->erase_sector(start_sector + i);
WL_RESULT_CHECK(result);
}
ESP_LOGV(TAG, "%s - result=%08x", __func__, result);
ESP_LOGV(TAG, "%s - result= 0x%08x", __func__, result);
return result;
}
@ -427,7 +580,7 @@ esp_err_t WL_Flash::write(size_t dest_addr, const void *src, size_t size)
if (!this->initialized) {
return ESP_ERR_INVALID_STATE;
}
ESP_LOGV(TAG, "%s - dest_addr=0x%08x, size=0x%08x", __func__, (uint32_t) dest_addr, (uint32_t) size);
ESP_LOGD(TAG, "%s - dest_addr= 0x%08x, size= 0x%08x", __func__, (uint32_t) dest_addr, (uint32_t) size);
uint32_t count = (size - 1) / this->cfg.page_size;
for (size_t i = 0; i < count; i++) {
size_t virt_addr = this->calcAddr(dest_addr + i * this->cfg.page_size);
@ -446,10 +599,11 @@ esp_err_t WL_Flash::read(size_t src_addr, void *dest, size_t size)
if (!this->initialized) {
return ESP_ERR_INVALID_STATE;
}
ESP_LOGV(TAG, "%s - src_addr=0x%08x, size=0x%08x", __func__, (uint32_t) src_addr, (uint32_t) size);
ESP_LOGD(TAG, "%s - src_addr= 0x%08x, size= 0x%08x", __func__, (uint32_t) src_addr, (uint32_t) size);
uint32_t count = (size - 1) / this->cfg.page_size;
for (size_t i = 0; i < count; i++) {
size_t virt_addr = this->calcAddr(src_addr + i * this->cfg.page_size);
ESP_LOGV(TAG, "%s - real_addr= 0x%08x, size= 0x%08x", __func__, (uint32_t) (this->cfg.start_addr + virt_addr), (uint32_t) size);
result = this->flash_drv->read(this->cfg.start_addr + virt_addr, &((uint8_t *)dest)[i * this->cfg.page_size], this->cfg.page_size);
WL_RESULT_CHECK(result);
}
@ -473,6 +627,6 @@ esp_err_t WL_Flash::flush()
esp_err_t result = ESP_OK;
this->state.access_count = this->state.max_count - 1;
result = this->updateWL();
ESP_LOGV(TAG, "%s - result=%08x", __func__, result);
ESP_LOGD(TAG, "%s - result= 0x%08x, move_count= 0x%08x", __func__, result, this->state.move_count);
return result;
}

View file

@ -5,6 +5,11 @@ Wear Levelling Component (WLC) it is a software component that is implemented to
The WLC do not have internal cache. When write operation is finished, that means that data was really stored to the flash.
As a parameter the WLC requires the driver to access the flash device. The driver has to implement Flash_Access interface.
The WLC Versioning and Compatibility
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The WLC accept data formats from older version. Latest version of the WLC will update data format from older versions to the current one.
Current implementation of WLC has version 2. The data format from current version incompatible with data format from previous versions, and could not be
used with previous versions.
The WLC Files
^^^^^^^^^^^^^^^
@ -49,9 +54,9 @@ The wl_config_t contains configuration parameters for the WLC component.
Internal Memory Organization
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The WLC divide the memory that are define by start_addr and full_mem_size to three regions:
- Configuration
- Data
- States
- Configuration
The Configuration region used to store configuration information. The user can use it to recover the WLC from memory dump.
The Data - is a region where user data stored.

View file

@ -65,12 +65,17 @@ protected:
uint32_t cfg_size;
uint8_t *temp_buff = NULL;
size_t dummy_addr;
uint8_t used_bits;
uint32_t pos_data[4];
esp_err_t initSections();
esp_err_t updateWL();
esp_err_t recoverPos();
size_t calcAddr(size_t addr);
esp_err_t updateVersion();
esp_err_t updateV1_V2();
void fillOkBuff(int n);
bool OkBuffSet(int n);
};
#endif // _WL_Flash_H_

View file

@ -19,7 +19,15 @@
* @brief This structure is used to store current state of flash access
*
*/
typedef struct WL_State_s {
#if defined(_MSC_VER)
#define ALIGNED_(x) __declspec(align(x))
#else
#if defined(__GNUC__)
#define ALIGNED_(x) __attribute__ ((aligned(x)))
#endif
#endif
typedef struct ALIGNED_(32) WL_State_s {
public:
uint32_t pos; /*!< current dummy block position*/
uint32_t max_pos; /*!< maximum amount of positions*/
@ -28,7 +36,14 @@ public:
uint32_t max_count; /*!< max access count when block will be moved*/
uint32_t block_size; /*!< size of move block*/
uint32_t version; /*!< state id used to identify the version of current libary implementaion*/
uint32_t device_id; /*!< ID of current WL instance*/
uint32_t reserved[7]; /*!< Reserved space for future use*/
uint32_t crc; /*!< CRC of structure*/
} wl_state_t;
#ifndef _MSC_VER // MSVS has different format for this define
static_assert(sizeof(wl_state_t) % 16 == 0, "Size of wl_state_t structure should be compatible with flash encryption");
#endif // _MSC_VER
#endif // _WL_State_H_

View file

@ -1 +1,2 @@
COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive
COMPONENT_EMBED_FILES := test_partition_v1.bin

Binary file not shown.

View file

@ -6,6 +6,8 @@
#include "freertos/portable.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_clk.h"
#include "soc/cpu.h"
TEST_CASE("wl_unmount doesn't leak memory", "[wear_levelling]")
{
@ -17,7 +19,13 @@ TEST_CASE("wl_unmount doesn't leak memory", "[wear_levelling]")
TEST_ESP_OK(wl_mount(partition, &handle));
wl_unmount(handle);
size_t size_after = xPortGetFreeHeapSize();
TEST_ASSERT_EQUAL_UINT32(size_before, size_after);
// Original code:
//TEST_ASSERT_EQUAL_HEX32(size_before, size_after);
// Workaround for problem with heap size calculation:
ptrdiff_t stack_diff = size_before - size_after;
stack_diff = abs(stack_diff);
if (stack_diff > 8) TEST_ASSERT_EQUAL(0, stack_diff);
}
TEST_CASE("wl_mount check partition parameters", "[wear_levelling][ignore]")
@ -27,22 +35,39 @@ TEST_CASE("wl_mount check partition parameters", "[wear_levelling][ignore]")
memcpy(&fake_partition, test_partition, sizeof(fake_partition));
wl_handle_t handle;
size_t size_before, size_after;
wl_unmount(WL_INVALID_HANDLE);
// test small partition
fake_partition.size = SPI_FLASH_SEC_SIZE;
size_before = xPortGetFreeHeapSize();
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, wl_mount(&fake_partition, &handle));
size_after = xPortGetFreeHeapSize();
TEST_ASSERT_EQUAL_HEX32(size_before, size_after);
// currently this test leaks memory
esp_partition_erase_range(test_partition, 0, test_partition->size);
// test small partition: result should be error
for (int i=0 ; i< 5 ; i++)
{
fake_partition.size = SPI_FLASH_SEC_SIZE*(i);
size_before = xPortGetFreeHeapSize();
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, wl_mount(&fake_partition, &handle));
size_after = xPortGetFreeHeapSize();
// test slightly bigger partition
fake_partition.size = SPI_FLASH_SEC_SIZE * 3;
// Original code:
//TEST_ASSERT_EQUAL_HEX32(size_before, size_after);
// Workaround for problem with heap size calculation:
ptrdiff_t stack_diff = size_before - size_after;
stack_diff = abs(stack_diff);
if (stack_diff > 8) TEST_ASSERT_EQUAL(0, stack_diff);
}
// test minimum size partition: result should be OK
fake_partition.size = SPI_FLASH_SEC_SIZE * 5;
size_before = xPortGetFreeHeapSize();
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, wl_mount(&fake_partition, &handle));
TEST_ESP_OK(wl_mount(&fake_partition, &handle));
wl_unmount(handle);
printf("Test done\n");
size_after = xPortGetFreeHeapSize();
TEST_ASSERT_EQUAL_HEX32(size_before, size_after);
// currently this test hangs
// Original code:
//TEST_ASSERT_EQUAL_HEX32(size_before, size_after);
// Workaround for problem with heap size calculation:
ptrdiff_t stack_diff = size_before - size_after;
stack_diff = abs(stack_diff);
if (stack_diff > 8) TEST_ASSERT_EQUAL(0, stack_diff);
}
typedef struct {
@ -151,3 +176,130 @@ TEST_CASE("multiple tasks can access wl handle simultaneously", "[wear_levelling
vSemaphoreDelete(args4.done);
wl_unmount(handle);
}
#define TEST_SECTORS_COUNT 8
static void check_mem_data(wl_handle_t handle, uint32_t init_val, uint32_t* buff)
{
size_t sector_size = wl_sector_size(handle);
for (int m=0 ; m < TEST_SECTORS_COUNT ; m++) {
TEST_ESP_OK(wl_read(handle, sector_size * m, buff, sector_size));
for (int i=0 ; i< sector_size/sizeof(uint32_t) ; i++) {
uint32_t compare_val = init_val + i + m*sector_size;
TEST_ASSERT_EQUAL( buff[i], compare_val);
}
}
}
// We write complete memory with defined data
// And then write one sector many times.
// A data in other secors should be the same.
// We do this also with unmount
TEST_CASE("multiple write is correct", "[wear_levelling]")
{
const esp_partition_t *partition = get_test_data_partition();
esp_partition_t fake_partition;
memcpy(&fake_partition, partition, sizeof(fake_partition));
fake_partition.size = SPI_FLASH_SEC_SIZE*(4 + TEST_SECTORS_COUNT);
wl_handle_t handle;
TEST_ESP_OK(wl_mount(&fake_partition, &handle));
size_t sector_size = wl_sector_size(handle);
// Erase 8 sectors
TEST_ESP_OK(wl_erase_range(handle, 0, sector_size * TEST_SECTORS_COUNT));
// Write data to all sectors
printf("Check 1 sector_size=0x%08x\n", sector_size);
// Set initial random value
uint32_t init_val = rand();
uint32_t* buff = (uint32_t*)malloc(sector_size);
for (int m=0 ; m < TEST_SECTORS_COUNT ; m++) {
for (int i=0 ; i< sector_size/sizeof(uint32_t) ; i++) {
buff[i] = init_val + i + m*sector_size;
}
TEST_ESP_OK(wl_erase_range(handle, sector_size*m, sector_size));
TEST_ESP_OK(wl_write(handle, sector_size*m, buff, sector_size));
}
check_mem_data(handle, init_val, buff);
uint32_t start;
RSR(CCOUNT, start);
for (int m=0 ; m< 100000 ; m++) {
uint32_t sector = m % TEST_SECTORS_COUNT;
for (int i=0 ; i< sector_size/sizeof(uint32_t) ; i++) {
buff[i] = init_val + i + sector*sector_size;
}
TEST_ESP_OK(wl_erase_range(handle, sector_size*sector, sector_size));
TEST_ESP_OK(wl_write(handle, sector_size*sector, buff, sector_size));
check_mem_data(handle, init_val, buff);
uint32_t end;
RSR(CCOUNT, end);
uint32_t ms = (end - start) / (esp_clk_cpu_freq() / 1000);
printf("loop %4i pass, time= %ims\n", m, ms);
if (ms > 10000) {
break;
}
}
free(buff);
wl_unmount(handle);
}
extern const uint8_t test_partition_v1_bin_start[] asm("_binary_test_partition_v1_bin_start");
extern const uint8_t test_partition_v1_bin_end[] asm("_binary_test_partition_v1_bin_end");
#define COMPARE_START_CONST 0x12340000
// We write to partition prepared image with V1
// Then we convert image to new version and verifying the data
TEST_CASE("Version update test", "[wear_levelling]")
{
const esp_partition_t *partition = get_test_data_partition();
esp_partition_t fake_partition;
memcpy(&fake_partition, partition, sizeof(fake_partition));
if (partition->encrypted)
{
printf("Update from V1 to V2 will not work.\n");
return;
}
fake_partition.size = (size_t)(test_partition_v1_bin_end - test_partition_v1_bin_start);
printf("Data file size = %i, partition address = 0x%08x, file addr=0x%08x\n", (uint32_t)fake_partition.size, (uint32_t)fake_partition.address, (uint32_t)test_partition_v1_bin_start);
esp_partition_erase_range(&fake_partition, 0, fake_partition.size);
esp_partition_write(&fake_partition, 0, test_partition_v1_bin_start, fake_partition.size);
wl_handle_t handle;
TEST_ESP_OK(wl_mount(&fake_partition, &handle));
size_t sector_size = wl_sector_size(handle);
uint32_t* buff = (uint32_t*)malloc(sector_size);
uint32_t init_val = COMPARE_START_CONST;
int test_count = fake_partition.size/sector_size - 4;
for (int m=0 ; m < test_count; m++) {
TEST_ESP_OK(wl_read(handle, sector_size * m, buff, sector_size));
for (int i=0 ; i< sector_size/sizeof(uint32_t) ; i++) {
uint32_t compare_val = init_val + i + m*sector_size;
if (buff[i] != compare_val)
{
printf("error compare: 0x%08x != 0x%08x \n", buff[i], compare_val);
}
TEST_ASSERT_EQUAL( buff[i], compare_val);
}
}
free(buff);
wl_unmount(handle);
}

View file

@ -16,6 +16,7 @@ SPI_FLASH_SIM_LIB := libspi_flash.a
include Makefile.files
all: test
ifndef SDKCONFIG
SDKCONFIG_DIR := $(dir $(realpath sdkconfig/sdkconfig.h))

View file

@ -45,7 +45,7 @@
#endif //WL_DEFAULT_START_ADDR
#ifndef WL_CURRENT_VERSION
#define WL_CURRENT_VERSION 1
#define WL_CURRENT_VERSION 2
#endif //WL_CURRENT_VERSION
typedef struct {
@ -174,7 +174,6 @@ esp_err_t wl_unmount(wl_handle_t handle)
_lock_acquire(&s_instances_lock);
result = check_handle(handle, __func__);
if (result == ESP_OK) {
ESP_LOGV(TAG, "deleting handle 0x%08x", handle);
// We have to flush state of the component
result = s_instances[handle].instance->flush();
// We use placement new in wl_mount, so call destructor directly