]> granicus.if.org Git - esp-idf/blobdiff - components/spi_flash/flash_ops.c
console/linenoise: support buffered stdout
[esp-idf] / components / spi_flash / flash_ops.c
index bbc65e4ca1d62f81dce9e2772bfa5026f0cdcd0d..89f9ef5e141fd7256edf0e384a2857d10e8bb225 100644 (file)
@@ -31,6 +31,8 @@
 #include "esp_spi_flash.h"
 #include "esp_log.h"
 #include "esp_clk.h"
+#include "esp_flash_partitions.h"
+#include "esp_ota_ops.h"
 #include "cache_utils.h"
 
 /* bytes erased by SPIEraseBlock() ROM function */
@@ -42,8 +44,9 @@
 #define MAX_WRITE_CHUNK 8192
 #define MAX_READ_CHUNK 16384
 
+static const char *TAG __attribute__((unused)) = "spi_flash";
+
 #if CONFIG_SPI_FLASH_ENABLE_COUNTERS
-static const char *TAG = "spi_flash";
 static spi_flash_counters_t s_flash_stats;
 
 #define COUNTER_START()     uint32_t ts_begin = xthal_get_ccount()
@@ -83,6 +86,45 @@ const DRAM_ATTR spi_flash_guard_funcs_t g_flash_guard_no_os_ops = {
 
 static const spi_flash_guard_funcs_t *s_flash_guard_ops;
 
+#ifdef CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS
+#define UNSAFE_WRITE_ADDRESS abort()
+#else
+#define UNSAFE_WRITE_ADDRESS return false
+#endif
+
+
+/* CHECK_WRITE_ADDRESS macro to fail writes which land in the
+   bootloader, partition table, or running application region.
+*/
+#if CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED
+#define CHECK_WRITE_ADDRESS(ADDR, SIZE)
+#else /* FAILS or ABORTS */
+#define CHECK_WRITE_ADDRESS(ADDR, SIZE) do {                            \
+        if (!is_safe_write_address(ADDR, SIZE)) {                       \
+            return ESP_ERR_INVALID_ARG;                                 \
+        }                                                               \
+    } while(0)
+#endif // CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED
+
+static __attribute__((unused)) bool is_safe_write_address(size_t addr, size_t size)
+{
+    bool result = true;
+    if (addr <= ESP_PARTITION_TABLE_OFFSET + ESP_PARTITION_TABLE_MAX_LEN) {
+        UNSAFE_WRITE_ADDRESS;
+    }
+
+    const esp_partition_t *p = esp_ota_get_running_partition();
+    if (addr >= p->address && addr < p->address + p->size) {
+        UNSAFE_WRITE_ADDRESS;
+    }
+    if (addr < p->address && addr + size > p->address) {
+        UNSAFE_WRITE_ADDRESS;
+    }
+
+    return result;
+}
+
+
 void spi_flash_init()
 {
     spi_flash_init_lock();
@@ -96,6 +138,11 @@ void IRAM_ATTR spi_flash_guard_set(const spi_flash_guard_funcs_t *funcs)
     s_flash_guard_ops = funcs;
 }
 
+const spi_flash_guard_funcs_t *IRAM_ATTR spi_flash_guard_get()
+{
+    return s_flash_guard_ops;
+}
+
 size_t IRAM_ATTR spi_flash_get_chip_size()
 {
     return g_rom_flashchip.chip_size;
@@ -146,11 +193,13 @@ static esp_rom_spiflash_result_t IRAM_ATTR spi_flash_unlock()
 
 esp_err_t IRAM_ATTR spi_flash_erase_sector(size_t sec)
 {
+    CHECK_WRITE_ADDRESS(sec * SPI_FLASH_SEC_SIZE, SPI_FLASH_SEC_SIZE);
     return spi_flash_erase_range(sec * SPI_FLASH_SEC_SIZE, SPI_FLASH_SEC_SIZE);
 }
 
 esp_err_t IRAM_ATTR spi_flash_erase_range(uint32_t start_addr, uint32_t size)
 {
+    CHECK_WRITE_ADDRESS(start_addr, size);
     if (start_addr % SPI_FLASH_SEC_SIZE != 0) {
         return ESP_ERR_INVALID_ARG;
     }
@@ -169,7 +218,7 @@ esp_err_t IRAM_ATTR spi_flash_erase_range(uint32_t start_addr, uint32_t size)
     if (rc == ESP_ROM_SPIFLASH_RESULT_OK) {
         for (size_t sector = start; sector != end && rc == ESP_ROM_SPIFLASH_RESULT_OK; ) {
             spi_flash_guard_start();
-            if (sector % sectors_per_block == 0 && end - sector > sectors_per_block) {
+            if (sector % sectors_per_block == 0 && end - sector >= sectors_per_block) {
                 rc = esp_rom_spiflash_erase_block(sector / sectors_per_block);
                 sector += sectors_per_block;
                 COUNTER_ADD_BYTES(erase, sectors_per_block * SPI_FLASH_SEC_SIZE);
@@ -185,8 +234,84 @@ esp_err_t IRAM_ATTR spi_flash_erase_range(uint32_t start_addr, uint32_t size)
     return spi_flash_translate_rc(rc);
 }
 
+/* Wrapper around esp_rom_spiflash_write() that verifies data as written if CONFIG_SPI_FLASH_VERIFY_WRITE is set.
+
+   If CONFIG_SPI_FLASH_VERIFY_WRITE is not set, this is esp_rom_spiflash_write().
+*/
+static IRAM_ATTR esp_rom_spiflash_result_t spi_flash_write_inner(uint32_t target, const uint32_t *src_addr, int32_t len)
+{
+#ifndef CONFIG_SPI_FLASH_VERIFY_WRITE
+    return esp_rom_spiflash_write(target, src_addr, len);
+#else // CONFIG_SPI_FLASH_VERIFY_WRITE
+    esp_rom_spiflash_result_t res = ESP_ROM_SPIFLASH_RESULT_OK;
+    assert(len % sizeof(uint32_t) == 0);
+
+    uint32_t before_buf[ESP_ROM_SPIFLASH_BUFF_BYTE_READ_NUM / sizeof(uint32_t)];
+    uint32_t after_buf[ESP_ROM_SPIFLASH_BUFF_BYTE_READ_NUM / sizeof(uint32_t)];
+    int32_t remaining = len;
+    for(int i = 0; i < len; i += sizeof(before_buf)) {
+        int i_w = i / sizeof(uint32_t); // index in words (i is an index in bytes)
+
+        int32_t read_len = MIN(sizeof(before_buf), remaining);
+
+        // Read "before" contents from flash
+        res = esp_rom_spiflash_read(target + i, before_buf, read_len);
+        if (res != ESP_ROM_SPIFLASH_RESULT_OK) {
+            break;
+        }
+
+#ifdef CONFIG_SPI_FLASH_WARN_SETTING_ZERO_TO_ONE
+        for (int r = 0; r < read_len; r += sizeof(uint32_t)) {
+            int r_w = r / sizeof(uint32_t); // index in words (r is index in bytes)
+
+            uint32_t write = src_addr[i_w + r_w];
+            uint32_t before = before_buf[r_w];
+            if ((before & write) != write) {
+                spi_flash_guard_end();
+                ESP_LOGW(TAG, "Write at offset 0x%x requests 0x%08x but will write 0x%08x -> 0x%08x",
+                         target + i + r, write, before, before & write);
+                spi_flash_guard_start();
+            }
+        }
+#endif
+
+        res = esp_rom_spiflash_write(target + i, &src_addr[i_w], read_len);
+        if (res != ESP_ROM_SPIFLASH_RESULT_OK) {
+            break;
+        }
+
+        res = esp_rom_spiflash_read(target + i, after_buf, read_len);
+        if (res != ESP_ROM_SPIFLASH_RESULT_OK) {
+            break;
+        }
+
+        for (int r = 0; r < read_len; r += sizeof(uint32_t)) {
+            int r_w = r / sizeof(uint32_t); // index in words (r is index in bytes)
+
+            uint32_t expected = src_addr[i_w + r_w] & before_buf[r_w];
+            uint32_t actual = after_buf[r_w];
+            if (expected != actual) {
+#ifdef CONFIG_SPI_FLASH_LOG_FAILED_WRITE
+                spi_flash_guard_end();
+                ESP_LOGE(TAG, "Bad write at offset 0x%x expected 0x%08x readback 0x%08x", target + i + r, expected, actual);
+                spi_flash_guard_start();
+#endif
+                res = ESP_ROM_SPIFLASH_RESULT_ERR;
+            }
+        }
+        if (res != ESP_ROM_SPIFLASH_RESULT_OK) {
+            break;
+        }
+        remaining -= read_len;
+    }
+    return res;
+#endif // CONFIG_SPI_FLASH_VERIFY_WRITE
+}
+
+
 esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size)
 {
+    CHECK_WRITE_ADDRESS(dst, size);
     // Out of bound writes are checked in ROM code, but we can give better
     // error code here
     if (dst + size > g_rom_flashchip.chip_size) {
@@ -220,7 +345,7 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size)
         uint32_t t = 0xffffffff;
         memcpy(((uint8_t *) &t) + (dst - left_off), srcc, left_size);
         spi_flash_guard_start();
-        rc = esp_rom_spiflash_write(left_off, &t, 4);
+        rc = spi_flash_write_inner(left_off, &t, 4);
         spi_flash_guard_end();
         if (rc != ESP_ROM_SPIFLASH_RESULT_OK) {
             goto out;
@@ -247,7 +372,7 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size)
                 write_src = (const uint8_t *)write_buf;
             }
             spi_flash_guard_start();
-            rc = esp_rom_spiflash_write(dst + mid_off, (const uint32_t *) write_src, write_size);
+            rc = spi_flash_write_inner(dst + mid_off, (const uint32_t *) write_src, write_size);
             spi_flash_guard_end();
             COUNTER_ADD_BYTES(write, write_size);
             mid_size -= write_size;
@@ -262,7 +387,7 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size)
         uint32_t t = 0xffffffff;
         memcpy(&t, srcc + right_off, right_size);
         spi_flash_guard_start();
-        rc = esp_rom_spiflash_write(dst + right_off, &t, 4);
+        rc = spi_flash_write_inner(dst + right_off, &t, 4);
         spi_flash_guard_end();
         if (rc != ESP_ROM_SPIFLASH_RESULT_OK) {
             goto out;
@@ -281,6 +406,7 @@ out:
 
 esp_err_t IRAM_ATTR spi_flash_write_encrypted(size_t dest_addr, const void *src, size_t size)
 {
+    CHECK_WRITE_ADDRESS(dest_addr, size);
     const uint8_t *ssrc = (const uint8_t *)src;
     if ((dest_addr % 16) != 0) {
         return ESP_ERR_INVALID_ARG;