mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 03:00:34 +03:00
fix(mmap): fixed some API read wrong data via mmap when flash being erased/written while XIP on PSRAM
Before: The cache won't be disabled when XIP on psram. But during flash erasing/programming, read data will be courrupt. When XIP in psram is enabled, the image is not mapped to the cache so usually there will be no flash access. The only way to read from flash is via the driver or use mmap. The driver has protection during erasing, while th mmap region not. Now: Mmap APIs provide a flag to make mmap->unmap region mutually exclusive to flash erase/programming when XIP from psram. SPI Flash write APIs will benefit from this. When the flag is used, no concurrent access to mapped region will happen while writing; otherwise the cache will be disable to avoid data corruption. Most ESP-IDF APIs calls mmap with this flag. As for users calling mmap-like APIs directly, they can choose whether to enable this by a flag. Closes https://github.com/espressif/esp-idf/issues/14897
This commit is contained in:
committed by
Michael (XIAO Xufeng)
parent
39a219331c
commit
3e8389cc31
@@ -1,8 +1,11 @@
|
||||
set(srcs "test_app_main.c"
|
||||
"test_flash_mmap.c")
|
||||
"test_mmap_utils.c"
|
||||
"test_flash_mmap.c"
|
||||
"test_mmap_api_concurrent.c")
|
||||
|
||||
# In order for the cases defined by `TEST_CASE` to be linked into the final elf,
|
||||
# the component can be registered as WHOLE_ARCHIVE
|
||||
idf_component_register(SRCS ${srcs}
|
||||
PRIV_REQUIRES unity test_utils spi_flash esp_partition efuse
|
||||
PRIV_REQUIRES unity test_utils spi_flash bootloader_support esp_partition esp_mm efuse
|
||||
PRIV_INCLUDE_DIRS .
|
||||
WHOLE_ARCHIVE)
|
||||
|
||||
@@ -20,114 +20,35 @@
|
||||
#include "esp_flash.h"
|
||||
|
||||
#include "test_utils.h"
|
||||
#include "test_mmap_utils.h"
|
||||
|
||||
|
||||
static uint32_t buffer[1024];
|
||||
|
||||
/* read-only region used for mmap tests, initialised in setup_mmap_tests() */
|
||||
static uint32_t start;
|
||||
static uint32_t end;
|
||||
|
||||
static spi_flash_mmap_handle_t handle1, handle2, handle3;
|
||||
|
||||
static esp_err_t spi_flash_read_maybe_encrypted(size_t src_addr, void *des_addr, size_t size)
|
||||
{
|
||||
if (!esp_efuse_is_flash_encryption_enabled()) {
|
||||
return esp_flash_read(NULL, des_addr, src_addr, size);
|
||||
} else {
|
||||
return esp_flash_read_encrypted(NULL, src_addr, des_addr, size);
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t spi_flash_write_maybe_encrypted(size_t des_addr, const void *src_addr, size_t size)
|
||||
{
|
||||
if (!esp_efuse_is_flash_encryption_enabled()) {
|
||||
return esp_flash_write(NULL, src_addr, des_addr, size);
|
||||
} else {
|
||||
return esp_flash_write_encrypted(NULL, des_addr, src_addr, size);
|
||||
}
|
||||
}
|
||||
|
||||
static void setup_mmap_tests(void)
|
||||
{
|
||||
if (start == 0) {
|
||||
const esp_partition_t *part = get_test_data_partition();
|
||||
start = part->address;
|
||||
end = part->address + part->size;
|
||||
printf("Test data partition @ 0x%"PRIx32" - 0x%"PRIx32"\n", start, end);
|
||||
}
|
||||
TEST_ASSERT(end > start);
|
||||
TEST_ASSERT(end - start >= 512 * 1024);
|
||||
|
||||
/* clean up any mmap handles left over from failed tests */
|
||||
if (handle1) {
|
||||
spi_flash_munmap(handle1);
|
||||
handle1 = 0;
|
||||
}
|
||||
if (handle2) {
|
||||
spi_flash_munmap(handle2);
|
||||
handle2 = 0;
|
||||
}
|
||||
if (handle3) {
|
||||
spi_flash_munmap(handle3);
|
||||
handle3 = 0;
|
||||
}
|
||||
|
||||
/* prepare flash contents */
|
||||
srand(0);
|
||||
for (int block = start / 0x10000; block < end / 0x10000; ++block) {
|
||||
for (int sector = 0; sector < 16; ++sector) {
|
||||
uint32_t abs_sector = (block * 16) + sector;
|
||||
uint32_t sector_offs = abs_sector * SPI_FLASH_SEC_SIZE;
|
||||
bool sector_needs_write = false;
|
||||
|
||||
TEST_ESP_OK( spi_flash_read_maybe_encrypted(sector_offs, buffer, sizeof(buffer)) );
|
||||
|
||||
for (uint32_t word = 0; word < 1024; ++word) {
|
||||
uint32_t val = rand();
|
||||
if (block == start / 0x10000 && sector == 0 && word == 0) {
|
||||
printf("setup_mmap_tests(): first prepped word: 0x%08"PRIx32" (flash holds 0x%08"PRIx32")\n", val, buffer[word]);
|
||||
}
|
||||
if (buffer[word] != val) {
|
||||
buffer[word] = val;
|
||||
sector_needs_write = true;
|
||||
}
|
||||
}
|
||||
/* Only rewrite the sector if it has changed */
|
||||
if (sector_needs_write) {
|
||||
TEST_ESP_OK( esp_flash_erase_region(NULL, (uint16_t) abs_sector * SPI_FLASH_SEC_SIZE, SPI_FLASH_SEC_SIZE) );
|
||||
TEST_ESP_OK( spi_flash_write_maybe_encrypted(sector_offs, (const uint8_t *) buffer, sizeof(buffer)) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Can get correct data in existing mapped region", "[spi_flash][mmap]")
|
||||
{
|
||||
setup_mmap_tests();
|
||||
|
||||
printf("Mapping %"PRIx32" (+%"PRIx32")\n", start, end - start);
|
||||
printf("Mapping %"PRIx32" (+%"PRIx32")\n", test_start, test_end - test_start);
|
||||
const void *ptr1;
|
||||
TEST_ESP_OK( spi_flash_mmap(start, end - start, SPI_FLASH_MMAP_DATA, &ptr1, &handle1) );
|
||||
TEST_ESP_OK( spi_flash_mmap(test_start, test_end - test_start, SPI_FLASH_MMAP_FLAG_DATA | SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE, &ptr1, &handle1) );
|
||||
printf("mmap_res: handle=%"PRIx32" ptr=%p\n", (uint32_t)handle1, ptr1);
|
||||
|
||||
/* Remap in the previously mapped region itself */
|
||||
uint32_t new_start = start + CONFIG_MMU_PAGE_SIZE;
|
||||
printf("Mapping %"PRIx32" (+%"PRIx32")\n", new_start, end - new_start);
|
||||
uint32_t new_start = test_start + CONFIG_MMU_PAGE_SIZE;
|
||||
printf("Mapping %"PRIx32" (+%"PRIx32")\n", new_start, test_end - new_start);
|
||||
const void *ptr2;
|
||||
TEST_ESP_OK( spi_flash_mmap(new_start, end - new_start, SPI_FLASH_MMAP_DATA, &ptr2, &handle2) );
|
||||
TEST_ESP_OK( spi_flash_mmap(new_start, test_end - new_start, SPI_FLASH_MMAP_FLAG_DATA | SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE, &ptr2, &handle2) );
|
||||
printf("mmap_res: handle=%"PRIx32" ptr=%p\n", (uint32_t)handle2, ptr2);
|
||||
|
||||
const void *src1 = (void *) ((uint32_t) ptr1 + CONFIG_MMU_PAGE_SIZE);
|
||||
const void *src2 = ptr2;
|
||||
/* Memory contents should be identical - as the region is same */
|
||||
TEST_ASSERT_EQUAL(0, memcmp(src1, src2, end - new_start));
|
||||
TEST_ASSERT_EQUAL(0, memcmp(src1, src2, test_end - new_start));
|
||||
|
||||
spi_flash_munmap(handle1);
|
||||
handle1 = 0;
|
||||
spi_flash_munmap(handle2);
|
||||
handle2 = 0;
|
||||
TEST_ASSERT_EQUAL_PTR(NULL, spi_flash_phys2cache(start, SPI_FLASH_MMAP_DATA));
|
||||
TEST_ASSERT_EQUAL_PTR(NULL, spi_flash_phys2cache(test_start, SPI_FLASH_MMAP_DATA));
|
||||
}
|
||||
|
||||
TEST_CASE("Can mmap into data address space", "[spi_flash][mmap]")
|
||||
@@ -135,14 +56,14 @@ TEST_CASE("Can mmap into data address space", "[spi_flash][mmap]")
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
setup_mmap_tests();
|
||||
|
||||
printf("Mapping %"PRIx32" (+%"PRIx32")\n", start, end - start);
|
||||
printf("Mapping %"PRIx32" (+%"PRIx32")\n", test_start, test_end - test_start);
|
||||
const void *ptr1;
|
||||
TEST_ESP_OK( spi_flash_mmap(start, end - start, SPI_FLASH_MMAP_DATA, &ptr1, &handle1) );
|
||||
TEST_ESP_OK( spi_flash_mmap(test_start, test_end - test_start, SPI_FLASH_MMAP_FLAG_DATA | SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE, &ptr1, &handle1) );
|
||||
printf("mmap_res: handle=%"PRIx32" ptr=%p\n", (uint32_t)handle1, ptr1);
|
||||
|
||||
srand(0);
|
||||
const uint32_t *data = (const uint32_t *) ptr1;
|
||||
for (int block = 0; block < (end - start) / 0x10000; ++block) {
|
||||
for (int block = 0; block < (test_end - test_start) / 0x10000; ++block) {
|
||||
printf("block %d\n", block);
|
||||
for (int sector = 0; sector < 16; ++sector) {
|
||||
printf("sector %d\n", sector);
|
||||
@@ -151,25 +72,25 @@ TEST_CASE("Can mmap into data address space", "[spi_flash][mmap]")
|
||||
}
|
||||
}
|
||||
}
|
||||
printf("Mapping %"PRIx32" (+%x)\n", start - 0x10000, 0x20000);
|
||||
printf("Mapping %"PRIx32" (+%x)\n", test_start - 0x10000, 0x20000);
|
||||
const void *ptr2;
|
||||
TEST_ESP_OK( spi_flash_mmap(start - 0x10000, 0x20000, SPI_FLASH_MMAP_DATA, &ptr2, &handle2) );
|
||||
TEST_ESP_OK( spi_flash_mmap(test_start - 0x10000, 0x20000, SPI_FLASH_MMAP_FLAG_DATA | SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE, &ptr2, &handle2) );
|
||||
printf("mmap_res: handle=%"PRIx32" ptr=%p\n", (uint32_t)handle2, ptr2);
|
||||
|
||||
TEST_ASSERT_EQUAL_HEX32(start - 0x10000, spi_flash_cache2phys(ptr2));
|
||||
TEST_ASSERT_EQUAL_HEX32(test_start - 0x10000, spi_flash_cache2phys(ptr2));
|
||||
|
||||
TEST_ASSERT_EQUAL_PTR(ptr2, spi_flash_phys2cache(start - 0x10000, SPI_FLASH_MMAP_DATA));
|
||||
TEST_ASSERT_EQUAL_PTR(ptr2, spi_flash_phys2cache(test_start - 0x10000, SPI_FLASH_MMAP_DATA));
|
||||
|
||||
printf("Mapping %"PRIx32" (+%x)\n", start, 0x10000);
|
||||
printf("Mapping %"PRIx32" (+%x)\n", test_start, 0x10000);
|
||||
const void *ptr3;
|
||||
ret = spi_flash_mmap(start, 0x10000, SPI_FLASH_MMAP_DATA, &ptr3, &handle3);
|
||||
ret = spi_flash_mmap(test_start, 0x10000, SPI_FLASH_MMAP_FLAG_DATA | SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE, &ptr3, &handle3);
|
||||
printf("ret: 0x%x\n", ret);
|
||||
TEST_ASSERT(ret == ESP_OK);
|
||||
printf("mmap_res: handle=%"PRIx32" ptr=%p\n", (uint32_t)handle3, ptr3);
|
||||
|
||||
TEST_ASSERT_EQUAL_HEX32(start, spi_flash_cache2phys(ptr3));
|
||||
TEST_ASSERT_EQUAL_PTR(ptr3, spi_flash_phys2cache(start, SPI_FLASH_MMAP_DATA));
|
||||
TEST_ASSERT_EQUAL_PTR((intptr_t)ptr3 + 0x4444, spi_flash_phys2cache(start + 0x4444, SPI_FLASH_MMAP_DATA));
|
||||
TEST_ASSERT_EQUAL_HEX32(test_start, spi_flash_cache2phys(ptr3));
|
||||
TEST_ASSERT_EQUAL_PTR(ptr3, spi_flash_phys2cache(test_start, SPI_FLASH_MMAP_DATA));
|
||||
TEST_ASSERT_EQUAL_PTR((intptr_t)ptr3 + 0x4444, spi_flash_phys2cache(test_start + 0x4444, SPI_FLASH_MMAP_DATA));
|
||||
|
||||
printf("Unmapping handle1\n");
|
||||
spi_flash_munmap(handle1);
|
||||
@@ -183,8 +104,8 @@ TEST_CASE("Can mmap into data address space", "[spi_flash][mmap]")
|
||||
spi_flash_munmap(handle3);
|
||||
handle3 = 0;
|
||||
|
||||
printf("start corresponding vaddr: 0x%x\n", (int)spi_flash_phys2cache(start, SPI_FLASH_MMAP_DATA));
|
||||
TEST_ASSERT_EQUAL_PTR(NULL, spi_flash_phys2cache(start, SPI_FLASH_MMAP_DATA));
|
||||
printf("start corresponding vaddr: 0x%x\n", (int)spi_flash_phys2cache(test_start, SPI_FLASH_MMAP_DATA));
|
||||
TEST_ASSERT_EQUAL_PTR(NULL, spi_flash_phys2cache(test_start, SPI_FLASH_MMAP_DATA));
|
||||
}
|
||||
|
||||
#if !CONFIG_SPI_FLASH_ROM_IMPL //flash mmap API in ROM does not support mmap into instruction address
|
||||
@@ -192,47 +113,47 @@ TEST_CASE("Can mmap into instruction address space", "[spi_flash][mmap]")
|
||||
{
|
||||
setup_mmap_tests();
|
||||
|
||||
printf("Mapping %"PRIx32" (+%"PRIx32")\n", start, end - start);
|
||||
spi_flash_mmap_handle_t handle1;
|
||||
printf("Mapping %"PRIx32" (+%"PRIx32")\n", test_start, test_end - test_start);
|
||||
const void *ptr1;
|
||||
TEST_ESP_OK( spi_flash_mmap(start, end - start, SPI_FLASH_MMAP_INST, &ptr1, &handle1) );
|
||||
TEST_ESP_OK( spi_flash_mmap(test_start, test_end - test_start, SPI_FLASH_MMAP_FLAG_INST | SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE, &ptr1, &handle1) );
|
||||
printf("mmap_res: handle=%"PRIx32" ptr=%p\n", (uint32_t)handle1, ptr1);
|
||||
|
||||
srand(0);
|
||||
const uint32_t *data = (const uint32_t *) ptr1;
|
||||
for (int block = 0; block < (end - start) / 0x10000; ++block) {
|
||||
for (int block = 0; block < (test_end - test_start) / 0x10000; ++block) {
|
||||
for (int sector = 0; sector < 16; ++sector) {
|
||||
for (uint32_t word = 0; word < 1024; ++word) {
|
||||
TEST_ASSERT_EQUAL_UINT32(rand(), data[(block * 16 + sector) * 1024 + word]);
|
||||
}
|
||||
}
|
||||
}
|
||||
printf("Mapping %"PRIx32" (+%x)\n", start - 0x10000, 0x20000);
|
||||
spi_flash_mmap_handle_t handle2;
|
||||
printf("Mapping %"PRIx32" (+%x)\n", test_start - 0x10000, 0x20000);
|
||||
const void *ptr2;
|
||||
TEST_ESP_OK( spi_flash_mmap(start - 0x10000, 0x20000, SPI_FLASH_MMAP_INST, &ptr2, &handle2) );
|
||||
TEST_ESP_OK( spi_flash_mmap(test_start - 0x10000, 0x20000, SPI_FLASH_MMAP_FLAG_INST | SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE, &ptr2, &handle2) );
|
||||
printf("mmap_res: handle=%"PRIx32" ptr=%p\n", (uint32_t)handle2, ptr2);
|
||||
|
||||
TEST_ASSERT_EQUAL_HEX32(start - 0x10000, spi_flash_cache2phys(ptr2));
|
||||
TEST_ASSERT_EQUAL_PTR(ptr2, spi_flash_phys2cache(start - 0x10000, SPI_FLASH_MMAP_INST));
|
||||
TEST_ASSERT_EQUAL_HEX32(test_start - 0x10000, spi_flash_cache2phys(ptr2));
|
||||
TEST_ASSERT_EQUAL_PTR(ptr2, spi_flash_phys2cache(test_start - 0x10000, SPI_FLASH_MMAP_INST));
|
||||
|
||||
printf("Mapping %"PRIx32" (+%x)\n", start, 0x10000);
|
||||
spi_flash_mmap_handle_t handle3;
|
||||
printf("Mapping %"PRIx32" (+%x)\n", test_start, 0x10000);
|
||||
const void *ptr3;
|
||||
TEST_ESP_OK( spi_flash_mmap(start, 0x10000, SPI_FLASH_MMAP_INST, &ptr3, &handle3) );
|
||||
TEST_ESP_OK( spi_flash_mmap(test_start, 0x10000, SPI_FLASH_MMAP_FLAG_INST | SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE, &ptr3, &handle3) );
|
||||
printf("mmap_res: handle=%"PRIx32" ptr=%p\n", (uint32_t)handle3, ptr3);
|
||||
|
||||
TEST_ASSERT_EQUAL_HEX32(start, spi_flash_cache2phys(ptr3));
|
||||
TEST_ASSERT_EQUAL_PTR(ptr3, spi_flash_phys2cache(start, SPI_FLASH_MMAP_INST));
|
||||
TEST_ASSERT_EQUAL_HEX32(test_start, spi_flash_cache2phys(ptr3));
|
||||
TEST_ASSERT_EQUAL_PTR(ptr3, spi_flash_phys2cache(test_start, SPI_FLASH_MMAP_INST));
|
||||
|
||||
printf("Unmapping handle1\n");
|
||||
spi_flash_munmap(handle1);
|
||||
handle1 = 0;
|
||||
|
||||
printf("Unmapping handle2\n");
|
||||
spi_flash_munmap(handle2);
|
||||
handle2 = 0;
|
||||
|
||||
printf("Unmapping handle3\n");
|
||||
spi_flash_munmap(handle3);
|
||||
handle3 = 0;
|
||||
}
|
||||
#endif // !CONFIG_SPI_FLASH_ROM_IMPL
|
||||
|
||||
@@ -243,10 +164,10 @@ TEST_CASE("Can mmap unordered pages into contiguous memory", "[spi_flash][mmap]"
|
||||
int startpage;
|
||||
|
||||
setup_mmap_tests();
|
||||
nopages = (end - start) / SPI_FLASH_MMU_PAGE_SIZE;
|
||||
nopages = (test_end - test_start) / SPI_FLASH_MMU_PAGE_SIZE;
|
||||
pages = alloca(sizeof(int) * nopages);
|
||||
|
||||
startpage = start / SPI_FLASH_MMU_PAGE_SIZE;
|
||||
startpage = test_start / SPI_FLASH_MMU_PAGE_SIZE;
|
||||
|
||||
//make inverse mapping: virt 0 -> page (nopages-1), virt 1 -> page (nopages-2), ...
|
||||
for (int i = 0; i < nopages; i++) {
|
||||
@@ -256,9 +177,8 @@ TEST_CASE("Can mmap unordered pages into contiguous memory", "[spi_flash][mmap]"
|
||||
|
||||
printf("Attempting mapping of unordered pages to contiguous memory area\n");
|
||||
|
||||
spi_flash_mmap_handle_t handle1;
|
||||
const void *ptr1;
|
||||
TEST_ESP_OK( spi_flash_mmap_pages(pages, nopages, SPI_FLASH_MMAP_DATA, &ptr1, &handle1) );
|
||||
TEST_ESP_OK( spi_flash_mmap_pages(pages, nopages, SPI_FLASH_MMAP_FLAG_DATA | SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE, &ptr1, &handle1) );
|
||||
printf("mmap_res: handle=%"PRIx32" ptr=%p\n", (uint32_t)handle1, ptr1);
|
||||
|
||||
#if (CONFIG_MMU_PAGE_SIZE == 0x10000)
|
||||
@@ -283,6 +203,7 @@ TEST_CASE("Can mmap unordered pages into contiguous memory", "[spi_flash][mmap]"
|
||||
|
||||
printf("Unmapping handle1\n");
|
||||
spi_flash_munmap(handle1);
|
||||
handle1 = 0;
|
||||
}
|
||||
|
||||
TEST_CASE("flash_mmap invalidates just-written data", "[spi_flash][mmap]")
|
||||
@@ -297,10 +218,10 @@ TEST_CASE("flash_mmap invalidates just-written data", "[spi_flash][mmap]")
|
||||
TEST_IGNORE_MESSAGE("flash encryption enabled, spi_flash_write_encrypted() test won't pass as-is");
|
||||
}
|
||||
|
||||
TEST_ESP_OK( esp_flash_erase_region(NULL, start, SPI_FLASH_SEC_SIZE) );
|
||||
TEST_ESP_OK( esp_flash_erase_region(NULL, test_start, SPI_FLASH_SEC_SIZE) );
|
||||
|
||||
/* map erased test region to ptr1 */
|
||||
TEST_ESP_OK( spi_flash_mmap(start, test_size, SPI_FLASH_MMAP_DATA, &ptr1, &handle1) );
|
||||
TEST_ESP_OK( spi_flash_mmap(test_start, test_size, SPI_FLASH_MMAP_FLAG_DATA | SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE, &ptr1, &handle1) );
|
||||
printf("mmap_res ptr1: handle=%"PRIx32" ptr=%p\n", (uint32_t)handle1, ptr1);
|
||||
|
||||
/* verify it's all 0xFF */
|
||||
@@ -315,14 +236,14 @@ TEST_CASE("flash_mmap invalidates just-written data", "[spi_flash][mmap]")
|
||||
/* write flash region to 0xEE */
|
||||
uint8_t buf[test_size];
|
||||
memset(buf, 0xEE, test_size);
|
||||
TEST_ESP_OK( esp_flash_write(NULL, buf, start, test_size) );
|
||||
TEST_ESP_OK( esp_flash_write(NULL, buf, test_start, test_size) );
|
||||
|
||||
/* re-map the test region at ptr1.
|
||||
|
||||
this is a fresh mmap call so should trigger a cache flush,
|
||||
ensuring we see the updated flash.
|
||||
*/
|
||||
TEST_ESP_OK( spi_flash_mmap(start, test_size, SPI_FLASH_MMAP_DATA, &ptr1, &handle1) );
|
||||
TEST_ESP_OK( spi_flash_mmap(test_start, test_size, SPI_FLASH_MMAP_FLAG_DATA | SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE, &ptr1, &handle1) );
|
||||
printf("mmap_res ptr1 #2: handle=%"PRIx32" ptr=%p\n", (uint32_t)handle1, ptr1);
|
||||
|
||||
/* assert that ptr1 now maps to the new values on flash,
|
||||
@@ -339,14 +260,14 @@ TEST_CASE("flash_mmap can mmap after get enough free MMU pages", "[spi_flash][mm
|
||||
//this test case should make flash size >= 4MB, because max size of Dcache can mapped is 4MB
|
||||
setup_mmap_tests();
|
||||
|
||||
printf("Mapping %"PRIx32" (+%"PRIx32")\n", start, end - start);
|
||||
printf("Mapping %"PRIx32" (+%"PRIx32")\n", test_start, test_end - test_start);
|
||||
const void *ptr1;
|
||||
TEST_ESP_OK( spi_flash_mmap(start, end - start, SPI_FLASH_MMAP_DATA, &ptr1, &handle1) );
|
||||
TEST_ESP_OK( spi_flash_mmap(test_start, test_end - test_start, SPI_FLASH_MMAP_FLAG_DATA | SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE, &ptr1, &handle1) );
|
||||
printf("mmap_res: handle=%"PRIx32" ptr=%p\n", (uint32_t)handle1, ptr1);
|
||||
|
||||
srand(0);
|
||||
const uint32_t *data = (const uint32_t *) ptr1;
|
||||
for (int block = 0; block < (end - start) / 0x10000; ++block) {
|
||||
for (int block = 0; block < (test_end - test_start) / 0x10000; ++block) {
|
||||
printf("block %d\n", block);
|
||||
for (int sector = 0; sector < 16; ++sector) {
|
||||
printf("sector %d\n", sector);
|
||||
@@ -363,7 +284,7 @@ TEST_CASE("flash_mmap can mmap after get enough free MMU pages", "[spi_flash][mm
|
||||
|
||||
printf("Mapping %x (+%"PRIx32")\n", 0, free_pages * SPI_FLASH_MMU_PAGE_SIZE);
|
||||
const void *ptr2;
|
||||
TEST_ESP_OK( spi_flash_mmap(0, free_pages * SPI_FLASH_MMU_PAGE_SIZE, SPI_FLASH_MMAP_DATA, &ptr2, &handle2) );
|
||||
TEST_ESP_OK( spi_flash_mmap(0, free_pages * SPI_FLASH_MMU_PAGE_SIZE, SPI_FLASH_MMAP_FLAG_DATA | SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE, &ptr2, &handle2) );
|
||||
printf("mmap_res: handle=%"PRIx32" ptr=%p\n", (uint32_t)handle2, ptr2);
|
||||
|
||||
printf("Unmapping handle1\n");
|
||||
@@ -374,7 +295,7 @@ TEST_CASE("flash_mmap can mmap after get enough free MMU pages", "[spi_flash][mm
|
||||
spi_flash_munmap(handle2);
|
||||
handle2 = 0;
|
||||
|
||||
TEST_ASSERT_EQUAL_PTR(NULL, spi_flash_phys2cache(start, SPI_FLASH_MMAP_DATA));
|
||||
TEST_ASSERT_EQUAL_PTR(NULL, spi_flash_phys2cache(test_start, SPI_FLASH_MMAP_DATA));
|
||||
}
|
||||
|
||||
TEST_CASE("phys2cache/cache2phys basic checks", "[spi_flash][mmap]")
|
||||
@@ -432,15 +353,15 @@ TEST_CASE("mmap consistent with phys2cache/cache2phys", "[spi_flash][mmap]")
|
||||
|
||||
TEST_ASSERT_EQUAL_HEX(SPI_FLASH_CACHE2PHYS_FAIL, spi_flash_cache2phys(ptr));
|
||||
|
||||
TEST_ESP_OK( spi_flash_mmap(start, test_size, SPI_FLASH_MMAP_DATA, &ptr, &handle1) );
|
||||
TEST_ESP_OK( spi_flash_mmap(test_start, test_size, SPI_FLASH_MMAP_FLAG_DATA | SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE, &ptr, &handle1) );
|
||||
TEST_ASSERT_NOT_NULL(ptr);
|
||||
TEST_ASSERT_NOT_EQUAL(0, handle1);
|
||||
|
||||
TEST_ASSERT_EQUAL_HEX(start, spi_flash_cache2phys(ptr));
|
||||
TEST_ASSERT_EQUAL_HEX(start + 1024, spi_flash_cache2phys((void *)((intptr_t)ptr + 1024)));
|
||||
TEST_ASSERT_EQUAL_HEX(start + 3000, spi_flash_cache2phys((void *)((intptr_t)ptr + 3000)));
|
||||
TEST_ASSERT_EQUAL_HEX(test_start, spi_flash_cache2phys(ptr));
|
||||
TEST_ASSERT_EQUAL_HEX(test_start + 1024, spi_flash_cache2phys((void *)((intptr_t)ptr + 1024)));
|
||||
TEST_ASSERT_EQUAL_HEX(test_start + 3000, spi_flash_cache2phys((void *)((intptr_t)ptr + 3000)));
|
||||
/* this pointer lands in a different MMU table entry */
|
||||
TEST_ASSERT_EQUAL_HEX(start + test_size - 4, spi_flash_cache2phys((void *)((intptr_t)ptr + test_size - 4)));
|
||||
TEST_ASSERT_EQUAL_HEX(test_start + test_size - 4, spi_flash_cache2phys((void *)((intptr_t)ptr + test_size - 4)));
|
||||
|
||||
spi_flash_munmap(handle1);
|
||||
handle1 = 0;
|
||||
@@ -468,14 +389,16 @@ TEST_CASE("munmap followed by mmap flushes cache", "[spi_flash][mmap]")
|
||||
const uint32_t *data;
|
||||
esp_partition_mmap_handle_t handle;
|
||||
TEST_ESP_OK( esp_partition_mmap(p, 0, SPI_FLASH_MMU_PAGE_SIZE,
|
||||
ESP_PARTITION_MMAP_DATA, (const void **) &data, &handle) );
|
||||
ESP_PARTITION_MMAP_DATA | ESP_PARTITION_MMAP_BLOCKS_WRITE, (const void **) &data, &handle) );
|
||||
uint32_t buf[16];
|
||||
memcpy(buf, data, sizeof(buf));
|
||||
|
||||
esp_partition_munmap(handle);
|
||||
|
||||
TEST_ESP_OK( esp_partition_mmap(p, SPI_FLASH_MMU_PAGE_SIZE, SPI_FLASH_MMU_PAGE_SIZE,
|
||||
ESP_PARTITION_MMAP_DATA, (const void **) &data, &handle) );
|
||||
ESP_PARTITION_MMAP_DATA | ESP_PARTITION_MMAP_BLOCKS_WRITE, (const void **) &data, &handle) );
|
||||
TEST_ASSERT_NOT_EQUAL(0, memcmp(buf, data, sizeof(buf)));
|
||||
esp_partition_munmap(handle);
|
||||
}
|
||||
|
||||
TEST_CASE("no stale data read post mmap and write partition", "[spi_flash][mmap]")
|
||||
|
||||
@@ -0,0 +1,381 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
#include "test_utils.h"
|
||||
#include "test_mmap_utils.h"
|
||||
#include "spi_flash_mmap.h"
|
||||
#include "esp_flash.h"
|
||||
#include "esp_cache.h"
|
||||
|
||||
|
||||
//XIP_PSRAM or RAM_APP enabled
|
||||
#if (CONFIG_SPIRAM_FETCH_INSTRUCTIONS && CONFIG_SPIRAM_RODATA) || CONFIG_APP_BUILD_TYPE_RAM
|
||||
#define EXECUTE_IN_FLASH 0
|
||||
#else
|
||||
#define EXECUTE_IN_FLASH 1
|
||||
#endif
|
||||
|
||||
TEST_CASE("flash_mmap allows esp_flash erase/write/read when mapped", "[spi_flash][mmap]")
|
||||
{
|
||||
const void *ptr1;
|
||||
const size_t test_size = 128;
|
||||
|
||||
spi_flash_mmap_handle_t handle1;
|
||||
setup_mmap_tests();
|
||||
|
||||
/* map erased test region to ptr1 */
|
||||
TEST_ESP_OK( spi_flash_mmap(test_start, test_size, SPI_FLASH_MMAP_FLAG_DATA, &ptr1, &handle1) );
|
||||
printf("mmap_res ptr1: handle=%"PRIx32" ptr=%p\n", (uint32_t)handle1, ptr1);
|
||||
|
||||
//Make sure ptr1 is cached
|
||||
volatile uint8_t val = 0xff;
|
||||
for (int i = 0; i < test_size; i++) {
|
||||
val = val ^ ((uint8_t *)ptr1)[i];
|
||||
}
|
||||
|
||||
TEST_ESP_OK( esp_flash_erase_region(NULL, test_start, SPI_FLASH_SEC_SIZE) );
|
||||
|
||||
/* verify it's all 0xFF after the erase operation */
|
||||
for (int i = 0; i < test_size; i++) {
|
||||
TEST_ASSERT_EQUAL_HEX(0xFF, ((uint8_t *)ptr1)[i]);
|
||||
}
|
||||
|
||||
/* unmap the erased region */
|
||||
spi_flash_munmap(handle1);
|
||||
handle1 = 0;
|
||||
|
||||
/* write flash region to 0xEE */
|
||||
uint8_t buf[test_size];
|
||||
uint8_t read_buf[test_size];
|
||||
memset(buf, 0xEE, test_size);
|
||||
TEST_ESP_OK( esp_flash_write(NULL, buf, test_start, test_size) );
|
||||
|
||||
/* re-map the test region at ptr1.
|
||||
|
||||
this is a fresh mmap call so should trigger a cache flush,
|
||||
ensuring we see the updated flash.
|
||||
*/
|
||||
TEST_ESP_OK( spi_flash_mmap(test_start, test_size, SPI_FLASH_MMAP_FLAG_DATA, &ptr1, &handle1) );
|
||||
printf("mmap_res ptr1 #2: handle=%"PRIx32" ptr=%p\n", (uint32_t)handle1, ptr1);
|
||||
|
||||
/* assert that ptr1 now maps to the new values on flash,
|
||||
ie contents of buf array.
|
||||
*/
|
||||
TEST_ASSERT_EQUAL_HEX8_ARRAY(buf, ptr1, test_size);
|
||||
|
||||
memset(buf, 0x66, test_size); //this data is select on purpose (can be modified from 0xEE set above)
|
||||
TEST_ESP_OK( esp_flash_write(NULL, buf, test_start, test_size) );
|
||||
TEST_ASSERT_EQUAL_HEX8_ARRAY(buf, ptr1, test_size);
|
||||
|
||||
memset(read_buf, 0x33, test_size);
|
||||
TEST_ESP_OK( esp_flash_read(NULL, read_buf, test_start, test_size) );
|
||||
TEST_ASSERT_EQUAL_HEX8_ARRAY(buf, read_buf, test_size);
|
||||
|
||||
spi_flash_munmap(handle1);
|
||||
handle1 = 0;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
uint8_t expected_data[128];
|
||||
const size_t test_size;
|
||||
SemaphoreHandle_t mmap_start; //Given from main to mmap, to start mmap
|
||||
SemaphoreHandle_t mmap_end; //Given from mmap to main indicating end of mmap and start another round
|
||||
bool finish;
|
||||
bool use_flag_blocks_write;
|
||||
} test_mmap_concurrent_ctx_t;
|
||||
|
||||
//Delay time should be longer than the erasing time
|
||||
#define MMAP_DELAY (1000 / portTICK_PERIOD_MS)
|
||||
|
||||
static void mmap_task(void* args)
|
||||
{
|
||||
test_mmap_concurrent_ctx_t *ctx = (test_mmap_concurrent_ctx_t*)args;
|
||||
const uint32_t mmap_start = test_start;
|
||||
const size_t mmap_len = CONFIG_MMU_PAGE_SIZE;
|
||||
|
||||
while (1) {
|
||||
xSemaphoreTake(ctx->mmap_start, portMAX_DELAY);
|
||||
if (ctx->finish) {
|
||||
break;
|
||||
}
|
||||
uint32_t extra_flags = ctx->use_flag_blocks_write? SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE: 0;
|
||||
|
||||
const void *ptr1;
|
||||
TEST_ESP_OK( spi_flash_mmap(mmap_start, mmap_len, SPI_FLASH_MMAP_FLAG_DATA | extra_flags, &ptr1, &handle1) );
|
||||
printf("mmap_res ptr1: handle=%"PRIx32" ptr=%p\n", (uint32_t)handle1, ptr1);
|
||||
|
||||
TEST_ASSERT_EQUAL_UINT8_ARRAY(ctx->expected_data, ptr1, ctx->test_size);
|
||||
|
||||
//Delay enough time so that API on other task must happen if it can.
|
||||
vTaskDelay(MMAP_DELAY);
|
||||
|
||||
TEST_ASSERT_EQUAL_UINT8_ARRAY(ctx->expected_data, ptr1, ctx->test_size);
|
||||
|
||||
/* unmap the region for test*/
|
||||
spi_flash_munmap(handle1);
|
||||
handle1 = 0;
|
||||
|
||||
xSemaphoreGive(ctx->mmap_end);
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
|
||||
activate main
|
||||
|
||||
main ->>+ mmap : Semphr start
|
||||
|
||||
mmap -> mmap : mmap
|
||||
mmap -> mmap : read & verify data
|
||||
mmap ->>+ mmap : Delay start
|
||||
|
||||
main ->>+ main : flash erase (block starts)
|
||||
|
||||
mmap ->>- mmap : Delay end
|
||||
|
||||
|
||||
mmap-> mmap : read & verify data
|
||||
|
||||
mmap -> mmap : unmap
|
||||
mmap ->> main : unblocks
|
||||
deactivate main
|
||||
|
||||
main -> main : flash erase starts
|
||||
main ->- main : check if unmapped
|
||||
|
||||
mmap ->>- main : Semphr ret
|
||||
|
||||
activate main
|
||||
main ->>+ mmap : Semphr start
|
||||
mmap -> mmap : ...
|
||||
deactivate mmap
|
||||
|
||||
deactivate main
|
||||
```
|
||||
*/
|
||||
|
||||
static void check_mmap_executed_and_wait(test_mmap_concurrent_ctx_t* ctx, bool expect_unmap)
|
||||
{
|
||||
portBASE_TYPE unmapped = xSemaphoreTake(ctx->mmap_end, 0);
|
||||
if (expect_unmap) {
|
||||
TEST_ASSERT_EQUAL_INT(pdTRUE, unmapped);
|
||||
} else {
|
||||
TEST_ASSERT_EQUAL_INT(pdFALSE, unmapped);
|
||||
|
||||
//wait until we can start the next operation
|
||||
xSemaphoreTake(ctx->mmap_end, portMAX_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_concurrent_mmap_core(test_mmap_concurrent_ctx_t *ctx, int test_size, bool use_flag_blocks_write)
|
||||
{
|
||||
const uint32_t api_addr = test_start + CONFIG_MMU_PAGE_SIZE;
|
||||
uint8_t read_buf[test_size];
|
||||
uint8_t buf[test_size];
|
||||
bool expect_blocked = false;
|
||||
|
||||
ctx->use_flag_blocks_write = use_flag_blocks_write;
|
||||
|
||||
// For erase operation, it should take the mmap mutex, and will start after unmap.
|
||||
printf("test erase in mmap...\n");
|
||||
xSemaphoreGive(ctx->mmap_start);
|
||||
esp_rom_delay_us(1000); //delay 1ms to make sure mmap is done
|
||||
TEST_ESP_OK(esp_flash_erase_region(NULL, api_addr, SPI_FLASH_SEC_SIZE));
|
||||
expect_blocked = use_flag_blocks_write;
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
expect_blocked = false;
|
||||
#endif
|
||||
check_mmap_executed_and_wait(ctx, expect_blocked);
|
||||
|
||||
// Read operations do NOT set ESP_FLASH_START_FLAG_NO_READ, so they are never blocked by mmap.
|
||||
printf("test read in mmap...\n");
|
||||
xSemaphoreGive(ctx->mmap_start);
|
||||
esp_rom_delay_us(1000); //delay 1ms to make sure mmap is done
|
||||
TEST_ESP_OK(esp_flash_read(NULL, read_buf, api_addr, test_size));
|
||||
check_mmap_executed_and_wait(ctx, false);
|
||||
|
||||
// For write operation, it's same as erase operation.
|
||||
printf("test write in mmap...\n");
|
||||
xSemaphoreGive(ctx->mmap_start);
|
||||
/* write flash region to 0xEE */
|
||||
memset(buf, 0xEE, test_size);
|
||||
esp_rom_delay_us(1000); //delay 1ms to make sure mmap is done
|
||||
TEST_ESP_OK(esp_flash_write(NULL, buf, api_addr, test_size));
|
||||
expect_blocked = use_flag_blocks_write;
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
expect_blocked = false;
|
||||
#endif
|
||||
check_mmap_executed_and_wait(ctx, expect_blocked);
|
||||
|
||||
printf("test read in mmap...\n");
|
||||
xSemaphoreGive(ctx->mmap_start);
|
||||
esp_rom_delay_us(1000); //delay 1ms to make sure mmap is done
|
||||
TEST_ESP_OK(esp_flash_read(NULL, read_buf, api_addr, test_size));
|
||||
check_mmap_executed_and_wait(ctx, false);
|
||||
}
|
||||
|
||||
TEST_CASE("flash_mmap concurrent access to flash erase/prog", "[spi_flash][mmap]")
|
||||
{
|
||||
setup_mmap_tests();
|
||||
const int test_size = 128;
|
||||
|
||||
test_mmap_concurrent_ctx_t ctx = {
|
||||
.finish = false,
|
||||
.test_size = test_size,
|
||||
.use_flag_blocks_write = true
|
||||
};
|
||||
ctx.mmap_start = xSemaphoreCreateBinary();
|
||||
TEST_ASSERT_NOT_NULL(ctx.mmap_start);
|
||||
ctx.mmap_end = xSemaphoreCreateBinary();
|
||||
TEST_ASSERT_NOT_NULL(ctx.mmap_end);
|
||||
TEST_ESP_OK(esp_flash_read(NULL, ctx.expected_data, test_start, test_size));
|
||||
|
||||
TaskHandle_t task_handle;
|
||||
//Create task with higher priority so that once semphr given, task is unblocked immediately.
|
||||
TEST_ASSERT_EQUAL(pdTRUE, xTaskCreate(mmap_task, "mmap_task", 4096, &ctx, 5, &task_handle));
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
test_concurrent_mmap_core(&ctx, test_size, true);
|
||||
test_concurrent_mmap_core(&ctx, test_size, false);
|
||||
}
|
||||
|
||||
ctx.finish = true;
|
||||
xSemaphoreGive(ctx.mmap_start);
|
||||
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);//wait for mmap task to delete
|
||||
|
||||
//recycle the context
|
||||
vSemaphoreDelete(ctx.mmap_start);
|
||||
vSemaphoreDelete(ctx.mmap_end);
|
||||
}
|
||||
|
||||
/* Number of erase rounds for the non-overlapping concurrent erase test */
|
||||
#define CONCURRENT_ERASE_ROUNDS 10
|
||||
|
||||
typedef struct {
|
||||
uint8_t expected_data[128];
|
||||
uint32_t region_a_start;
|
||||
uint32_t region_b_start;
|
||||
size_t test_size;
|
||||
bool finish;
|
||||
bool use_flag_blocks_write;
|
||||
bool data_ok;
|
||||
SemaphoreHandle_t erase_done;
|
||||
SemaphoreHandle_t erase_op_start;
|
||||
} test_mmap_erase_concurrent_ctx_t;
|
||||
|
||||
static void mmap_verify_task(void *args)
|
||||
{
|
||||
test_mmap_erase_concurrent_ctx_t *ctx = (test_mmap_erase_concurrent_ctx_t *)args;
|
||||
uint32_t extra_flags = ctx->use_flag_blocks_write? SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE: 0;
|
||||
|
||||
xSemaphoreTake(ctx->erase_op_start, portMAX_DELAY);
|
||||
|
||||
while (!ctx->finish) {
|
||||
//For single core case, delay 1ms to allow erase task to start and finish
|
||||
vTaskDelay(1);
|
||||
spi_flash_mmap_handle_t verify_handle;
|
||||
const void *ptr;
|
||||
esp_err_t err = spi_flash_mmap(ctx->region_a_start, ctx->test_size, SPI_FLASH_MMAP_FLAG_DATA | extra_flags, &ptr, &verify_handle);
|
||||
if (err != ESP_OK) {
|
||||
ctx->data_ok = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (memcmp(ctx->expected_data, ptr, ctx->test_size) != 0) {
|
||||
ctx->data_ok = false;
|
||||
spi_flash_munmap(verify_handle);
|
||||
break;
|
||||
}
|
||||
|
||||
spi_flash_munmap(verify_handle);
|
||||
printf("mmap_verify_task: data ok, calling munmap\n");
|
||||
}
|
||||
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static void erase_task(void *args)
|
||||
{
|
||||
test_mmap_erase_concurrent_ctx_t *ctx = (test_mmap_erase_concurrent_ctx_t *)args;
|
||||
xSemaphoreGive(ctx->erase_op_start);
|
||||
|
||||
for (int i = 0; i < CONCURRENT_ERASE_ROUNDS; i++) {
|
||||
printf("erase region B: round %d/%d\n", i + 1, CONCURRENT_ERASE_ROUNDS);
|
||||
TEST_ESP_OK(esp_flash_erase_region(NULL, ctx->region_b_start, SPI_FLASH_SEC_SIZE));
|
||||
printf("erase region B: round %d done\n", i + 1);
|
||||
}
|
||||
|
||||
ctx->finish = true;
|
||||
xSemaphoreGive(ctx->erase_done);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static void test_concurrent_erase_core(test_mmap_erase_concurrent_ctx_t *ctx, bool use_flag_blocks_write)
|
||||
{
|
||||
ctx->finish = false;
|
||||
ctx->use_flag_blocks_write = use_flag_blocks_write;
|
||||
ctx->erase_op_start = xSemaphoreCreateBinary();
|
||||
TEST_ASSERT_NOT_NULL(ctx->erase_op_start);
|
||||
ctx->erase_done = xSemaphoreCreateBinary();
|
||||
TEST_ASSERT_NOT_NULL(ctx->erase_done);
|
||||
|
||||
printf("test_concurrent_erase_core: use_flag_blocks_write=%d\n", use_flag_blocks_write);
|
||||
|
||||
//Let mmap happen during erasing
|
||||
TEST_ASSERT_EQUAL(pdTRUE, xTaskCreate(mmap_verify_task, "mmap_verify", 4096, ctx, 6, NULL));
|
||||
TEST_ASSERT_EQUAL(pdTRUE, xTaskCreate(erase_task, "erase_task", 4096, ctx, 5, NULL));
|
||||
|
||||
//Wait for erase task to complete all rounds, then stop mmap task
|
||||
xSemaphoreTake(ctx->erase_done, portMAX_DELAY);
|
||||
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||
|
||||
vSemaphoreDelete(ctx->erase_done);
|
||||
vSemaphoreDelete(ctx->erase_op_start);
|
||||
}
|
||||
|
||||
TEST_CASE("flash_mmap region unaffected by concurrent flash erase of separate region", "[spi_flash][mmap]")
|
||||
{
|
||||
setup_mmap_tests();
|
||||
|
||||
const size_t test_size = 128;
|
||||
|
||||
/* Region A: beginning of test partition (mmap + verify target) */
|
||||
uint32_t region_a = test_start;
|
||||
/* Region B: second sector of test partition (erase target), does not overlap with region A */
|
||||
uint32_t region_b = test_start + SPI_FLASH_SEC_SIZE;
|
||||
|
||||
//Prepare random data to write to region A
|
||||
uint8_t write_buf[128];
|
||||
srand(789);
|
||||
for (int i = 0; i < test_size; i ++) {
|
||||
write_buf[i] = rand() % 0xff;
|
||||
}
|
||||
TEST_ESP_OK(esp_flash_erase_region(NULL, region_a, SPI_FLASH_SEC_SIZE));
|
||||
TEST_ESP_OK(esp_flash_write(NULL, write_buf, region_a, test_size));
|
||||
|
||||
test_mmap_erase_concurrent_ctx_t ctx = {
|
||||
.region_a_start = region_a,
|
||||
.region_b_start = region_b,
|
||||
.test_size = test_size,
|
||||
.data_ok = true,
|
||||
};
|
||||
memcpy(ctx.expected_data, write_buf, test_size);
|
||||
|
||||
test_concurrent_erase_core(&ctx, false);
|
||||
test_concurrent_erase_core(&ctx, true);
|
||||
|
||||
TEST_ASSERT_TRUE_MESSAGE(ctx.data_ok, "Region A data was corrupted during concurrent erase of region B");
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include <inttypes.h>
|
||||
#include <esp_partition.h>
|
||||
#include <esp_efuse.h>
|
||||
|
||||
#include "test_utils.h"
|
||||
#include "test_mmap_utils.h"
|
||||
#include "esp_flash.h"
|
||||
|
||||
|
||||
/* read-only region used for mmap tests, initialised in setup_mmap_tests() */
|
||||
uint32_t test_start;
|
||||
uint32_t test_end;
|
||||
|
||||
spi_flash_mmap_handle_t handle1, handle2, handle3;
|
||||
|
||||
static uint32_t buffer[1024];
|
||||
|
||||
|
||||
esp_err_t spi_flash_read_maybe_encrypted(size_t src_addr, void *des_addr, size_t size)
|
||||
{
|
||||
if (!esp_efuse_is_flash_encryption_enabled()) {
|
||||
return esp_flash_read(NULL, des_addr, src_addr, size);
|
||||
} else {
|
||||
return esp_flash_read_encrypted(NULL, src_addr, des_addr, size);
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t spi_flash_write_maybe_encrypted(size_t des_addr, const void *src_addr, size_t size)
|
||||
{
|
||||
if (!esp_efuse_is_flash_encryption_enabled()) {
|
||||
return esp_flash_write(NULL, src_addr, des_addr, size);
|
||||
} else {
|
||||
return esp_flash_write_encrypted(NULL, des_addr, src_addr, size);
|
||||
}
|
||||
}
|
||||
|
||||
void setup_mmap_tests(void)
|
||||
{
|
||||
if (test_start == 0) {
|
||||
const esp_partition_t *part = get_test_data_partition();
|
||||
test_start = part->address;
|
||||
test_end = part->address + part->size;
|
||||
printf("Test data partition @ 0x%"PRIx32" - 0x%"PRIx32"\n", test_start, test_end);
|
||||
}
|
||||
TEST_ASSERT(test_end > test_start);
|
||||
TEST_ASSERT(test_end - test_start >= 512 * 1024);
|
||||
|
||||
/* clean up any mmap handles left over from failed tests */
|
||||
if (handle1) {
|
||||
spi_flash_munmap(handle1);
|
||||
handle1 = 0;
|
||||
}
|
||||
if (handle2) {
|
||||
spi_flash_munmap(handle2);
|
||||
handle2 = 0;
|
||||
}
|
||||
if (handle3) {
|
||||
spi_flash_munmap(handle3);
|
||||
handle3 = 0;
|
||||
}
|
||||
|
||||
/* prepare flash contents */
|
||||
srand(0);
|
||||
for (int block = test_start / 0x10000; block < test_end / 0x10000; ++block) {
|
||||
for (int sector = 0; sector < 16; ++sector) {
|
||||
uint32_t abs_sector = (block * 16) + sector;
|
||||
uint32_t sector_offs = abs_sector * SPI_FLASH_SEC_SIZE;
|
||||
bool sector_needs_write = false;
|
||||
|
||||
TEST_ESP_OK( spi_flash_read_maybe_encrypted(sector_offs, buffer, sizeof(buffer)) );
|
||||
|
||||
for (uint32_t word = 0; word < 1024; ++word) {
|
||||
uint32_t val = rand();
|
||||
if (block == test_start / 0x10000 && sector == 0 && word == 0) {
|
||||
printf("setup_mmap_tests(): first prepped word: 0x%08"PRIx32" (flash holds 0x%08"PRIx32")\n", val, buffer[word]);
|
||||
}
|
||||
if (buffer[word] != val) {
|
||||
buffer[word] = val;
|
||||
sector_needs_write = true;
|
||||
}
|
||||
}
|
||||
/* Only rewrite the sector if it has changed */
|
||||
if (sector_needs_write) {
|
||||
TEST_ESP_OK( esp_flash_erase_region(NULL, (uint16_t) abs_sector * SPI_FLASH_SEC_SIZE, SPI_FLASH_SEC_SIZE) );
|
||||
TEST_ESP_OK( spi_flash_write_maybe_encrypted(sector_offs, (const uint8_t *) buffer, sizeof(buffer)) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "spi_flash_mmap.h"
|
||||
|
||||
extern void setup_mmap_tests(void);
|
||||
extern uint32_t test_start;
|
||||
extern uint32_t test_end;
|
||||
|
||||
//Use and maintain these handles. When next test starts, setup_mmap_tests will unmap these handle if they are not zero.
|
||||
extern spi_flash_mmap_handle_t handle1, handle2, handle3;
|
||||
|
||||
esp_err_t spi_flash_read_maybe_encrypted(size_t src_addr, void *des_addr, size_t size);
|
||||
esp_err_t spi_flash_write_maybe_encrypted(size_t des_addr, const void *src_addr, size_t size);
|
||||
@@ -68,3 +68,16 @@ def test_flash_mmap_psram(dut: Dut) -> None:
|
||||
@idf_parametrize('target', ['supported_targets'], indirect=['target'])
|
||||
def test_flash_mmap_xip_psram_rom_impl(dut: Dut) -> None:
|
||||
dut.run_all_single_board_cases(timeout=30)
|
||||
|
||||
|
||||
@pytest.mark.flash_suspend
|
||||
@pytest.mark.parametrize(
|
||||
'config',
|
||||
[
|
||||
'suspend_with_rom_impl',
|
||||
],
|
||||
indirect=True,
|
||||
)
|
||||
@idf_parametrize('target', ['esp32c3'], indirect=['target'])
|
||||
def test_flash_mmap_suspend_with_rom_impl(dut: Dut) -> None:
|
||||
dut.run_all_single_board_cases(timeout=30)
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
CONFIG_SPI_FLASH_ROM_IMPL=y
|
||||
CONFIG_SPI_FLASH_AUTO_SUSPEND=y
|
||||
CONFIG_SPI_FLASH_FORCE_ENABLE_XMC_C_SUSPEND=y
|
||||
Reference in New Issue
Block a user