Skip to content

Commit d602682

Browse files
committedMar 5, 2020
spi_flash: Fix over-allocation and OOM crash when reading from SPI flash to PSRAM buffers
Previously would try allocate buffer of minimum size 16KB not maximum size 16KB, causing out of memory errors for any large reads, or if less than 16KB contiguous free heap. Also, if using legacy API and internal allocation failed then implementation would abort() instead of returning the error to the caller. Added test for using large buffers in PSRAM. Closes #4769 Also reported on forum: https://esp32.com/viewtopic.php?f=13&t=14304&p=55972
1 parent e7a3387 commit d602682

File tree

3 files changed

+62
-15
lines changed

3 files changed

+62
-15
lines changed
 

‎components/spi_flash/esp_flash_api.c

+14-7
Original file line numberDiff line numberDiff line change
@@ -498,11 +498,13 @@ esp_err_t IRAM_ATTR esp_flash_read(esp_flash_t *chip, void *buffer, uint32_t add
498498
uint8_t* temp_buffer = NULL;
499499

500500
if (!direct_read) {
501-
uint32_t length_to_allocate = MAX(MAX_READ_CHUNK, length);
501+
uint32_t length_to_allocate = MIN(MAX_READ_CHUNK, length);
502502
length_to_allocate = (length_to_allocate+3)&(~3);
503503
temp_buffer = heap_caps_malloc(length_to_allocate, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
504-
ESP_LOGV(TAG, "allocate temp buffer: %p", temp_buffer);
505-
if (temp_buffer == NULL) return ESP_ERR_NO_MEM;
504+
ESP_LOGV(TAG, "allocate temp buffer: %p (%d)", temp_buffer, length_to_allocate);
505+
if (temp_buffer == NULL) {
506+
return ESP_ERR_NO_MEM;
507+
}
506508
}
507509

508510
esp_err_t err = ESP_OK;
@@ -682,27 +684,32 @@ esp_err_t esp_flash_app_disable_protect(bool disable)
682684

683685
#ifndef CONFIG_SPI_FLASH_USE_LEGACY_IMPL
684686

687+
/* Translate any ESP_ERR_FLASH_xxx error code (new API) to a generic ESP_ERR_xyz error code
688+
*/
685689
static IRAM_ATTR esp_err_t spi_flash_translate_rc(esp_err_t err)
686690
{
687691
switch (err) {
688692
case ESP_OK:
689-
return ESP_OK;
690693
case ESP_ERR_INVALID_ARG:
691-
return ESP_ERR_INVALID_ARG;
694+
case ESP_ERR_NO_MEM:
695+
return err;
696+
692697
case ESP_ERR_FLASH_NOT_INITIALISED:
693698
case ESP_ERR_FLASH_PROTECTED:
694699
return ESP_ERR_INVALID_STATE;
700+
695701
case ESP_ERR_NOT_FOUND:
696702
case ESP_ERR_FLASH_UNSUPPORTED_HOST:
697703
case ESP_ERR_FLASH_UNSUPPORTED_CHIP:
698704
return ESP_ERR_NOT_SUPPORTED;
705+
699706
case ESP_ERR_FLASH_NO_RESPONSE:
700707
return ESP_ERR_INVALID_RESPONSE;
708+
701709
default:
702-
ESP_EARLY_LOGE(TAG, "unexpected spi flash error code: %x", err);
710+
ESP_EARLY_LOGE(TAG, "unexpected spi flash error code: 0x%x", err);
703711
abort();
704712
}
705-
return ESP_OK;
706713
}
707714

708715
esp_err_t IRAM_ATTR spi_flash_erase_range(uint32_t start_addr, uint32_t size)

‎components/spi_flash/include/esp_flash.h

+1-2
Original file line numberDiff line numberDiff line change
@@ -230,8 +230,7 @@ esp_err_t esp_flash_set_protected_region(esp_flash_t *chip, const esp_flash_regi
230230
*
231231
* @return
232232
* - ESP_OK: success
233-
* - ESP_ERR_NO_MEM: the buffer is not valid, however failed to malloc on
234-
* the heap.
233+
* - ESP_ERR_NO_MEM: Buffer is in external PSRAM which cannot be concurrently accessed, and a temporary internal buffer could not be allocated.
235234
* - or a flash error code if operation failed.
236235
*/
237236
esp_err_t esp_flash_read(esp_flash_t *chip, void *buffer, uint32_t address, uint32_t length);

‎components/spi_flash/test/test_esp_flash.c

+47-6
Original file line numberDiff line numberDiff line change
@@ -68,17 +68,25 @@ static uint8_t sector_buf[4096];
6868
#define VSPI_PIN_NUM_CS FSPI_PIN_NUM_CS
6969
#endif
7070

71-
#define ALL_TEST_NUM (sizeof(config_list)/sizeof(flashtest_config_t))
71+
#define TEST_CONFIG_NUM (sizeof(config_list)/sizeof(flashtest_config_t))
72+
7273
typedef void (*flash_test_func_t)(esp_flash_t* chip);
7374

75+
/* Use FLASH_TEST_CASE for SPI flash tests that only use the main SPI flash chip
76+
*/
7477
#define FLASH_TEST_CASE(STR, FUNC_TO_RUN) \
75-
TEST_CASE(STR, "[esp_flash]") {flash_test_func(FUNC_TO_RUN, 1);}
78+
TEST_CASE(STR, "[esp_flash]") {flash_test_func(FUNC_TO_RUN, 1 /* first index reserved for main flash */ );}
79+
80+
/* Use FLASH_TEST_CASE_3 for tests which also run on external flash, which sits in the place of PSRAM
81+
(these tests are incompatible with PSRAM)
82+
83+
These tests run for all the flash chip configs shown in config_list, below (internal and external).
84+
*/
7685
#if defined(CONFIG_SPIRAM_SUPPORT) || TEMPORARY_DISABLED_FOR_TARGETS(ESP32S2BETA)
77-
// These tests needs external flash, right on the place of psram
7886
#define FLASH_TEST_CASE_3(STR, FUNCT_TO_RUN)
7987
#else
8088
#define FLASH_TEST_CASE_3(STR, FUNC_TO_RUN) \
81-
TEST_CASE(STR", 3 chips", "[esp_flash][test_env=UT_T1_ESP_FLASH]") {flash_test_func(FUNC_TO_RUN, ALL_TEST_NUM);}
89+
TEST_CASE(STR", 3 chips", "[esp_flash][test_env=UT_T1_ESP_FLASH]") {flash_test_func(FUNC_TO_RUN, TEST_CONFIG_NUM);}
8290
#endif
8391

8492
//currently all the configs are the same with esp_flash_spi_device_config_t, no more information required
@@ -271,12 +279,14 @@ void teardown_test_chip(esp_flash_t* chip, spi_host_device_t host)
271279
static void flash_test_func(flash_test_func_t func, int test_num)
272280
{
273281
for (int i = 0; i < test_num; i++) {
282+
ESP_LOGI(TAG, "Testing config %d/%d", i, test_num);
274283
flashtest_config_t* config = &config_list[i];
275284
esp_flash_t* chip;
276285
setup_new_chip(config, &chip);
277286
(*func)(chip);
278287
teardown_test_chip(chip, config->host_id);
279288
}
289+
ESP_LOGI(TAG, "Completed %d configs", test_num);
280290
}
281291

282292
/* ---------- Test code start ------------*/
@@ -615,7 +625,7 @@ TEST_CASE("SPI flash test reading with all speed/mode permutations", "[esp_flash
615625
#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32S2BETA)
616626
TEST_CASE("SPI flash test reading with all speed/mode permutations, 3 chips", "[esp_flash][test_env=UT_T1_ESP_FLASH]")
617627
{
618-
for (int i = 0; i < ALL_TEST_NUM; i++) {
628+
for (int i = 0; i < TEST_CONFIG_NUM; i++) {
619629
test_permutations(&config_list[i]);
620630
}
621631
}
@@ -685,4 +695,35 @@ static void test_write_large_buffer(esp_flash_t *chip, const uint8_t *source, si
685695

686696
write_large_buffer(chip, part, source, length);
687697
read_and_check(chip, part, source, length);
688-
}
698+
}
699+
700+
#ifdef CONFIG_SPIRAM_USE_MALLOC
701+
702+
static void test_flash_read_large_psram_buffer(esp_flash_t *chip)
703+
{
704+
const size_t BUF_SZ = 256 * 1024; // Too large for internal RAM
705+
const size_t INTERNAL_BUF_SZ = 1024; // Should fit in internal RAM
706+
_Static_assert(BUF_SZ % INTERNAL_BUF_SZ == 0, "should be a multiple");
707+
const size_t TEST_OFFS = 0x1000; // Can be any offset, really
708+
709+
uint8_t *buf = heap_caps_malloc(BUF_SZ, MALLOC_CAP_8BIT|MALLOC_CAP_SPIRAM);
710+
TEST_ASSERT_NOT_NULL(buf);
711+
712+
ESP_ERROR_CHECK( esp_flash_read(chip, buf, TEST_OFFS, BUF_SZ) );
713+
714+
// Read back the same into smaller internal memory buffer and check it all matches
715+
uint8_t *ibuf = heap_caps_malloc(INTERNAL_BUF_SZ, MALLOC_CAP_8BIT|MALLOC_CAP_INTERNAL);
716+
TEST_ASSERT_NOT_NULL(ibuf);
717+
718+
for (int i = 0; i < BUF_SZ; i += INTERNAL_BUF_SZ) {
719+
ESP_ERROR_CHECK( esp_flash_read(chip, ibuf, TEST_OFFS + i, INTERNAL_BUF_SZ) );
720+
TEST_ASSERT_EQUAL_HEX8_ARRAY(buf + i, ibuf, INTERNAL_BUF_SZ);
721+
}
722+
723+
free(ibuf);
724+
free(buf);
725+
}
726+
727+
FLASH_TEST_CASE("esp_flash_read large PSRAM buffer", test_flash_read_large_psram_buffer);
728+
729+
#endif

0 commit comments

Comments
 (0)
Please sign in to comment.