From 95c150fe2c77af7f7d4f19d4807aaea0a818e9a6 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 4 Jan 2017 17:37:15 +1100 Subject: [PATCH 1/5] spiflash: Add spi_flash_read_encrypted() function --- components/spi_flash/flash_ops.c | 27 ++++++++++++++++++++ components/spi_flash/include/esp_spi_flash.h | 17 ++++++++++++ 2 files changed, 44 insertions(+) diff --git a/components/spi_flash/flash_ops.c b/components/spi_flash/flash_ops.c index a74558a96f..22070eb5ff 100644 --- a/components/spi_flash/flash_ops.c +++ b/components/spi_flash/flash_ops.c @@ -381,6 +381,33 @@ out: return spi_flash_translate_rc(rc); } +#define FLASH_PAGE_SIZE 0x10000 + +esp_err_t IRAM_ATTR spi_flash_read_encrypted(size_t src, void *dstv, size_t size) +{ + if (src + size > g_rom_flashchip.chip_size) { + return ESP_ERR_INVALID_SIZE; + } + if (size == 0) { + return ESP_OK; + } + + esp_err_t err; + const uint8_t *map; + spi_flash_mmap_handle_t map_handle; + size_t map_src = src & ~(FLASH_PAGE_SIZE-1); + size_t map_size = size + (src - map_src); + + err = spi_flash_mmap(map_src, map_size, SPI_FLASH_MMAP_DATA, (const void **)&map, &map_handle); + if (err != ESP_OK) { + return err; + } + memcpy(dstv, map + (src - map_src), size); + spi_flash_munmap(map_handle); + return err; +} + + static esp_err_t spi_flash_translate_rc(SpiFlashOpResult rc) { switch (rc) { diff --git a/components/spi_flash/include/esp_spi_flash.h b/components/spi_flash/include/esp_spi_flash.h index bf897e8995..d0b6cd20b9 100644 --- a/components/spi_flash/include/esp_spi_flash.h +++ b/components/spi_flash/include/esp_spi_flash.h @@ -116,6 +116,23 @@ esp_err_t spi_flash_write_encrypted(size_t dest_addr, const void *src, size_t si */ esp_err_t spi_flash_read(size_t src_addr, void *dest, size_t size); + +/** + * @brief Read data from Encrypted Flash. + * + * If flash encryption is enabled, this function will transparently decrypt data as it is read. + * If flash encryption is not enabled, this function behaves the same as spi_flash_read(). + * + * See @ref esp_flash_encryption_enabled() for a function to check if flash encryption is enabled. + * + * @param src source address of the data in Flash. + * @param dest pointer to the destination buffer + * @param size length of data + * + * @return esp_err_t + */ +esp_err_t spi_flash_read_encrypted(size_t src, void *dstv, size_t size); + /** * @brief Enumeration which specifies memory space requested in an mmap call */ From 36ccdee6ecd35c4b60eb95b97c510ba4794cb645 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 5 Jan 2017 15:51:02 +1100 Subject: [PATCH 2/5] spi_flash: Flush flash cache if flash_mmap()ing a written-to page Without this, it's possible for stale information to be read from cache via mmap, even if the MMU table entry had been invalidated prior to writing flash (if the same MMU table entry was re-used after writing flash.) --- components/spi_flash/cache_utils.h | 8 +++ components/spi_flash/flash_mmap.c | 69 +++++++++++++++++++++ components/spi_flash/flash_ops.c | 10 +++ components/spi_flash/test/test_config.h | 24 +++++++ components/spi_flash/test/test_mmap.c | 54 +++++++++++++++- components/spi_flash/test/test_read_write.c | 4 +- components/spi_flash/test/test_spi_flash.c | 7 ++- 7 files changed, 170 insertions(+), 6 deletions(-) create mode 100644 components/spi_flash/test/test_config.h diff --git a/components/spi_flash/cache_utils.h b/components/spi_flash/cache_utils.h index 598b8fba77..9cd9ad6b49 100644 --- a/components/spi_flash/cache_utils.h +++ b/components/spi_flash/cache_utils.h @@ -48,4 +48,12 @@ void spi_flash_disable_interrupts_caches_and_other_cpu_no_os(); // This function is implied to be called when other CPU is not running or running code from IRAM. void spi_flash_enable_interrupts_caches_no_os(); +// Mark the pages containing a flash region as having been +// erased or written to. This means the flash cache needs +// to be evicted before these pages can be flash_mmap()ed again, +// as they may contain stale data +// +// Only call this while holding spi_flash_op_lock() +void spi_flash_mark_modified_region(uint32_t start_addr, uint32_t length); + #endif //ESP_SPI_FLASH_CACHE_UTILS_H diff --git a/components/spi_flash/flash_mmap.c b/components/spi_flash/flash_mmap.c index 15f75f3634..0b83ac33c0 100644 --- a/components/spi_flash/flash_mmap.c +++ b/components/spi_flash/flash_mmap.c @@ -46,6 +46,15 @@ #define VADDR1_FIRST_USABLE_ADDR 0x400D0000 #define PRO_IRAM0_FIRST_USABLE_PAGE ((VADDR1_FIRST_USABLE_ADDR - VADDR1_START_ADDR) / FLASH_PAGE_SIZE + 64) +/* Ensure pages in a region haven't been marked as written via + spi_flash_mark_modified_region(). If the page has + been written, flush the entire flash cache before returning. + + This ensures stale cache entries are never read after fresh calls + to spi_flash_mmap(), while keeping the number of cache flushes to a + minimum. +*/ +static void spi_flash_ensure_unmodified_region(size_t start_addr, size_t length); typedef struct mmap_entry_{ uint32_t handle; @@ -91,7 +100,11 @@ esp_err_t IRAM_ATTR spi_flash_mmap(size_t src_addr, size_t size, spi_flash_mmap_ if (src_addr + size > g_rom_flashchip.chip_size) { return ESP_ERR_INVALID_ARG; } + spi_flash_disable_interrupts_caches_and_other_cpu(); + + spi_flash_ensure_unmodified_region(src_addr, size); + if (s_mmap_page_refcnt[0] == 0) { spi_flash_mmap_init(); } @@ -212,3 +225,59 @@ void spi_flash_mmap_dump() } } } + +/* 256-bit (up to 16MB of 64KB pages) bitset of all flash pages + that have been written to since last cache flush. + + Before mmaping a page, need to flush caches if that page has been + written to. + + Note: It's possible to do some additional performance tweaks to + this algorithm, as we actually only need to flush caches if a page + was first mmapped, then written to, then is about to be mmaped a + second time. This is a fair bit more complex though, so unless + there's an access pattern that this would significantly boost then + it's probably not worth it. +*/ +static uint32_t written_pages[256/32]; + +static void update_written_pages(size_t start_addr, size_t length, bool mark); + +void IRAM_ATTR spi_flash_mark_modified_region(size_t start_addr, size_t length) +{ + update_written_pages(start_addr, length, true); +} + +static void IRAM_ATTR spi_flash_ensure_unmodified_region(size_t start_addr, size_t length) +{ + update_written_pages(start_addr, length, false); +} + +/* generic implementation for the previous two functions */ +static inline IRAM_ATTR void update_written_pages(size_t start_addr, size_t length, bool mark) +{ + for (uint32_t addr = start_addr; addr < start_addr + length; addr += FLASH_PAGE_SIZE) { + int page = addr / FLASH_PAGE_SIZE; + if (page >= 256) { + return; /* invalid address */ + } + + int idx = page / 32; + uint32_t bit = 1 << (page % 32); + + if (mark) { + written_pages[idx] |= bit; + } else if (written_pages[idx] & bit) { + /* it is tempting to write a version of this that only + flushes each CPU's cache as needed. However this is + tricky because mmaped memory can be used on un-pinned + cores, or the pointer passed between CPUs. + */ + Cache_Flush(0); +#ifndef CONFIG_FREERTOS_UNICORE + Cache_Flush(1); +#endif + bzero(written_pages, sizeof(written_pages)); + } + } +} diff --git a/components/spi_flash/flash_ops.c b/components/spi_flash/flash_ops.c index 22070eb5ff..4bcb7b3aef 100644 --- a/components/spi_flash/flash_ops.c +++ b/components/spi_flash/flash_ops.c @@ -250,6 +250,11 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size) } out: COUNTER_STOP(write); + + spi_flash_op_lock(); + spi_flash_mark_modified_region(dst, size); + spi_flash_op_unlock(); + return spi_flash_translate_rc(rc); } @@ -286,6 +291,11 @@ esp_err_t IRAM_ATTR spi_flash_write_encrypted(size_t dest_addr, const void *src, bzero(encrypt_buf, sizeof(encrypt_buf)); } COUNTER_ADD_BYTES(write, size); + + spi_flash_op_lock(); + spi_flash_mark_modified_region(dest_addr, size); + spi_flash_op_unlock(); + return spi_flash_translate_rc(rc); } diff --git a/components/spi_flash/test/test_config.h b/components/spi_flash/test/test_config.h new file mode 100644 index 0000000000..45e73661bb --- /dev/null +++ b/components/spi_flash/test/test_config.h @@ -0,0 +1,24 @@ +// Copyright 2010-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Common header for SPI flash test data +#pragma once + +/* Define a region of flash we can mess up for testing... + + This is pretty ugly, better to do something with a partition but + this is OK for now. + */ +#define TEST_REGION_START 0x180000 +#define TEST_REGION_END 0x1E0000 diff --git a/components/spi_flash/test/test_mmap.c b/components/spi_flash/test/test_mmap.c index d3d16802ea..464a876428 100644 --- a/components/spi_flash/test/test_mmap.c +++ b/components/spi_flash/test/test_mmap.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -7,14 +8,17 @@ #include #include #include +#include -uint32_t buffer[1024]; +#include "test_config.h" +static uint32_t buffer[1024]; + +/* read-only region used for mmap tests */ static const uint32_t start = 0x100000; static const uint32_t end = 0x200000; - TEST_CASE("Prepare data for mmap tests", "[mmap]") { srand(0); @@ -81,3 +85,49 @@ TEST_CASE("Can mmap into data address space", "[mmap]") printf("Unmapping handle3\n"); spi_flash_munmap(handle3); } + +TEST_CASE("flash_mmap invalidates just-written data", "[spi_flash]") +{ + spi_flash_mmap_handle_t handle1; + const void *ptr1; + + const size_t test_size = 128; + + if (esp_flash_encryption_enabled()) { + TEST_IGNORE_MESSAGE("flash encryption enabled, spi_flash_write_encrypted() test won't pass as-is"); + } + + ESP_ERROR_CHECK( spi_flash_erase_sector(TEST_REGION_START / SPI_FLASH_SEC_SIZE) ); + + /* map erased test region to ptr1 */ + ESP_ERROR_CHECK( spi_flash_mmap(TEST_REGION_START, test_size, SPI_FLASH_MMAP_DATA, &ptr1, &handle1) ); + printf("mmap_res ptr1: handle=%d ptr=%p\n", handle1, ptr1); + + /* verify it's all 0xFF */ + 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); + + /* write flash region to 0xEE */ + uint8_t buf[test_size]; + memset(buf, 0xEE, test_size); + ESP_ERROR_CHECK( spi_flash_write(TEST_REGION_START, buf, 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. + */ + ESP_ERROR_CHECK( spi_flash_mmap(TEST_REGION_START, test_size, SPI_FLASH_MMAP_DATA, &ptr1, &handle1) ); + printf("mmap_res ptr1 #2: handle=%d ptr=%p\n", 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); + + spi_flash_munmap(handle1); +} diff --git a/components/spi_flash/test/test_read_write.c b/components/spi_flash/test/test_read_write.c index c9067463e4..37edbbb944 100644 --- a/components/spi_flash/test/test_read_write.c +++ b/components/spi_flash/test/test_read_write.c @@ -27,8 +27,10 @@ #include "soc/timer_group_struct.h" #include "soc/timer_group_reg.h" +#include "test_config.h" + /* Base offset in flash for tests. */ -#define FLASH_BASE 0x120000 +#define FLASH_BASE TEST_REGION_START #ifndef CONFIG_SPI_FLASH_MINIMAL_TEST #define CONFIG_SPI_FLASH_MINIMAL_TEST 1 diff --git a/components/spi_flash/test/test_spi_flash.c b/components/spi_flash/test/test_spi_flash.c index ecc472ac0e..90d0cc1fdc 100644 --- a/components/spi_flash/test/test_spi_flash.c +++ b/components/spi_flash/test/test_spi_flash.c @@ -7,6 +7,8 @@ #include #include +#include "test_config.h" + struct flash_test_ctx { uint32_t offset; bool fail; @@ -31,8 +33,7 @@ static void flash_test_task(void *arg) vTaskDelay(0 / portTICK_PERIOD_MS); uint32_t val = 0xabcd1234; - const uint32_t n = 4096; - for (uint32_t offset = 0; offset < n; offset += 4) { + for (uint32_t offset = 0; offset < SPI_FLASH_SEC_SIZE; offset += 4) { if (spi_flash_write(sector * SPI_FLASH_SEC_SIZE + offset, (const uint8_t *) &val, 4) != ESP_OK) { printf("Write failed at offset=%d\r\n", offset); ctx->fail = true; @@ -44,7 +45,7 @@ static void flash_test_task(void *arg) vTaskDelay(0 / portTICK_PERIOD_MS); uint32_t val_read; - for (uint32_t offset = 0; offset < n; offset += 4) { + for (uint32_t offset = 0; offset < SPI_FLASH_SEC_SIZE; offset += 4) { if (spi_flash_read(sector * SPI_FLASH_SEC_SIZE + offset, (uint8_t *) &val_read, 4) != ESP_OK) { printf("Read failed at offset=%d\r\n", offset); ctx->fail = true; From adc590ff69181fa5f4b1088f06446ee2218c2149 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 4 Jan 2017 17:53:15 +1100 Subject: [PATCH 3/5] spi_flash_write_encrypted: Allow 16-byte aligned block writes As each 32 byte write has two identical 16 byte AES blocks, it's possible to write them separately. --- components/spi_flash/flash_ops.c | 54 ++++++++--- components/spi_flash/include/esp_spi_flash.h | 14 +-- .../spi_flash/test/test_flash_encryption.c | 91 +++++++++++++++++++ 3 files changed, 140 insertions(+), 19 deletions(-) create mode 100644 components/spi_flash/test/test_flash_encryption.c diff --git a/components/spi_flash/flash_ops.c b/components/spi_flash/flash_ops.c index 4bcb7b3aef..76a5a6171d 100644 --- a/components/spi_flash/flash_ops.c +++ b/components/spi_flash/flash_ops.c @@ -90,7 +90,7 @@ size_t spi_flash_get_chip_size() return g_rom_flashchip.chip_size; } -SpiFlashOpResult IRAM_ATTR spi_flash_unlock() +static SpiFlashOpResult IRAM_ATTR spi_flash_unlock() { static bool unlocked = false; if (!unlocked) { @@ -260,30 +260,58 @@ out: esp_err_t IRAM_ATTR spi_flash_write_encrypted(size_t dest_addr, const void *src, size_t size) { - if ((dest_addr % 32) != 0) { + const uint8_t *ssrc = (const uint8_t *)src; + if ((dest_addr % 16) != 0) { return ESP_ERR_INVALID_ARG; } - if ((size % 32) != 0) { + if ((size % 16) != 0) { return ESP_ERR_INVALID_SIZE; } - if ((uint32_t) src < 0x3ff00000) { - // if source address is in DROM, we won't be able to read it - // from within SPIWrite - // TODO: consider buffering source data using heap and writing it anyway? - return ESP_ERR_INVALID_ARG; - } + COUNTER_START(); spi_flash_disable_interrupts_caches_and_other_cpu(); SpiFlashOpResult rc; rc = spi_flash_unlock(); + spi_flash_enable_interrupts_caches_and_other_cpu(); + if (rc == SPI_FLASH_RESULT_OK) { /* SPI_Encrypt_Write encrypts data in RAM as it writes, so copy to a temporary buffer - 32 bytes at a time. + + Each call to SPI_Encrypt_Write takes a 32 byte "row" of + data to encrypt, and each row is two 16 byte AES blocks + that share a key (as derived from flash address). */ - uint32_t encrypt_buf[32/sizeof(uint32_t)]; - for (size_t i = 0; i < size; i += 32) { - memcpy(encrypt_buf, ((const uint8_t *)src) + i, 32); - rc = SPI_Encrypt_Write((uint32_t) dest_addr + i, encrypt_buf, 32); + uint8_t encrypt_buf[32] __attribute__((aligned(4))); + uint32_t row_size; + for (size_t i = 0; i < size; i += row_size) { + uint32_t row_addr = dest_addr + i; + if (i == 0 && (row_addr % 32) != 0) { + /* writing to second block of a 32 byte row */ + row_size = 16; + row_addr -= 16; + /* copy to second block in buffer */ + memcpy(encrypt_buf + 16, ssrc + i, 16); + /* decrypt the first block from flash, will reencrypt to same bytes */ + spi_flash_read_encrypted(row_addr, encrypt_buf, 16); + } + else if (size - i == 16) { + /* 16 bytes left, is first block of a 32 byte row */ + row_size = 16; + /* copy to first block in buffer */ + memcpy(encrypt_buf, ssrc + i, 16); + /* decrypt the second block from flash, will reencrypt to same bytes */ + spi_flash_read_encrypted(row_addr + 16, encrypt_buf + 16, 16); + } + else { + /* Writing a full 32 byte row (2 blocks) */ + row_size = 32; + memcpy(encrypt_buf, ssrc + i, 32); + } + + spi_flash_disable_interrupts_caches_and_other_cpu(); + rc = SPI_Encrypt_Write(row_addr, (uint32_t *)encrypt_buf, 32); + spi_flash_enable_interrupts_caches_and_other_cpu(); if (rc != SPI_FLASH_RESULT_OK) { break; } diff --git a/components/spi_flash/include/esp_spi_flash.h b/components/spi_flash/include/esp_spi_flash.h index d0b6cd20b9..32ab3a6853 100644 --- a/components/spi_flash/include/esp_spi_flash.h +++ b/components/spi_flash/include/esp_spi_flash.h @@ -92,14 +92,16 @@ esp_err_t spi_flash_write(size_t dest_addr, const void *src, size_t size); * * @note Flash encryption must be enabled for this function to work. * - * @note Address in flash, dest, has to be 32-byte aligned. + * @note Destination flash address and length must be 16-byte + * aligned. Due to hardware limitations, this function is more + * efficient if both these arguments are 32-byte aligned. This is + * because the encryption engine natively deals with 32-byte rows of + * two AES blocks. Writing half a row (16 bytes) requires reading out + * the other 16 bytes and re-encrypting them back to the same value. * - * @note If source address is in DROM, this function will return - * ESP_ERR_INVALID_ARG. - * - * @param dest_addr destination address in Flash. Must be a multiple of 32 bytes. + * @param dest_addr destination address in Flash. Must be a multiple of 16 bytes. * @param src pointer to the source buffer. - * @param size length of data, in bytes. Must be a multiple of 32 bytes. + * @param size length of data, in bytes. Must be a multiple of 16 bytes. * * @return esp_err_t */ diff --git a/components/spi_flash/test/test_flash_encryption.c b/components/spi_flash/test/test_flash_encryption.c new file mode 100644 index 0000000000..b8d8018ae4 --- /dev/null +++ b/components/spi_flash/test/test_flash_encryption.c @@ -0,0 +1,91 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "test_config.h" + +static void test_encrypted_write(size_t offset, const uint8_t *data, size_t length); +static void verify_erased_flash(size_t offset, size_t length); + +TEST_CASE("test 16 byte encrypted writes", "[spi_flash]") +{ + if (!esp_flash_encryption_enabled()) { + TEST_IGNORE_MESSAGE("flash encryption disabled, skipping spi_flash_write_encrypted() tests"); + } + + TEST_ASSERT_EQUAL_HEX(ESP_OK, + spi_flash_erase_sector(TEST_REGION_START / SPI_FLASH_SEC_SIZE)); + + uint8_t fortyeight_bytes[0x30]; // 0, 1, 2, 3, 4... 47 + for(int i = 0; i < sizeof(fortyeight_bytes); i++) { + fortyeight_bytes[i] = i; + } + + /* Verify unaligned start or length fails */ + TEST_ASSERT_EQUAL_HEX(ESP_ERR_INVALID_ARG, + spi_flash_write_encrypted(TEST_REGION_START+1, fortyeight_bytes, 32)); + + TEST_ASSERT_EQUAL_HEX(ESP_ERR_INVALID_SIZE, + spi_flash_write_encrypted(TEST_REGION_START, fortyeight_bytes, 15)); + + /* ensure nothing happened to the flash yet */ + verify_erased_flash(TEST_REGION_START, 0x20); + + /* Write 32 byte block, this is the "normal" encrypted write */ + test_encrypted_write(TEST_REGION_START, fortyeight_bytes, 0x20); + verify_erased_flash(TEST_REGION_START + 0x20, 0x20); + + /* Slip in an unaligned spi_flash_read_encrypted() test */ + uint8_t buf[0x10]; + spi_flash_read_encrypted(TEST_REGION_START+0x10, buf, 0x10); + TEST_ASSERT_EQUAL_HEX8_ARRAY(fortyeight_bytes+0x10, buf, 16); + + /* Write 16 bytes unaligned */ + test_encrypted_write(TEST_REGION_START + 0x30, fortyeight_bytes, 0x10); + /* the 16 byte regions before and after the 16 bytes we just wrote should still be 0xFF */ + verify_erased_flash(TEST_REGION_START + 0x20, 0x10); + verify_erased_flash(TEST_REGION_START + 0x40, 0x10); + + /* Write 48 bytes starting at a 32-byte aligned offset */ + test_encrypted_write(TEST_REGION_START + 0x40, fortyeight_bytes, 0x30); + /* 16 bytes after this write should still be 0xFF -unencrypted- */ + verify_erased_flash(TEST_REGION_START + 0x70, 0x10); + + /* Write 48 bytes starting at a 16-byte aligned offset */ + test_encrypted_write(TEST_REGION_START + 0x90, fortyeight_bytes, 0x30); + /* 16 bytes after this write should still be 0xFF -unencrypted- */ + verify_erased_flash(TEST_REGION_START + 0x120, 0x10); +} + +static void test_encrypted_write(size_t offset, const uint8_t *data, size_t length) +{ + uint8_t readback[length]; + printf("encrypt %d bytes at 0x%x\n", length, offset); + TEST_ASSERT_EQUAL_HEX(ESP_OK, + spi_flash_write_encrypted(offset, data, length)); + + TEST_ASSERT_EQUAL_HEX(ESP_OK, + spi_flash_read_encrypted(offset, readback, length)); + + TEST_ASSERT_EQUAL_HEX8_ARRAY(data, readback, length); +} + +static void verify_erased_flash(size_t offset, size_t length) +{ + uint8_t readback[length]; + printf("verify erased 0x%x - 0x%x\n", offset, offset + length); + TEST_ASSERT_EQUAL_HEX(ESP_OK, + spi_flash_read(offset, readback, length)); + for (int i = 0; i < length; i++) { + char message[32]; + sprintf(message, "unerased flash @ 0x%08x", offset + i); + TEST_ASSERT_EQUAL_HEX_MESSAGE(0xFF, readback[i], message); + } +} + From 14aa1d1b3e979e3f85b0c4669e1bd0e00cb9837b Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 6 Jan 2017 11:17:29 +1100 Subject: [PATCH 4/5] docs: spi_flash: Add flash encryption ops, remove boilerplate --- components/spi_flash/include/esp_partition.h | 7 +++++++ components/spi_flash/include/esp_spi_flash.h | 18 ++++++++++-------- docs/api/storage/spi_flash.rst | 11 ++++------- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/components/spi_flash/include/esp_partition.h b/components/spi_flash/include/esp_partition.h index b149e10234..28f8551dcb 100644 --- a/components/spi_flash/include/esp_partition.h +++ b/components/spi_flash/include/esp_partition.h @@ -191,6 +191,13 @@ esp_err_t esp_partition_read(const esp_partition_t* partition, * Before writing data to flash, corresponding region of flash needs to be erased. * This can be done using esp_partition_erase_range function. * + * Partitions marked with an encryption flag will automatically be + * written via the spi_flash_write_encrypted() function. If writing to + * an encrypted partition, all write offsets and lengths must be + * multiples of 16 bytes. See the spi_flash_write_encrypted() function + * for more details. Unencrypted partitions do not have this + * restriction. + * * @param partition Pointer to partition structure obtained using * esp_partition_find_first or esp_partition_get. * Must be non-NULL. diff --git a/components/spi_flash/include/esp_spi_flash.h b/components/spi_flash/include/esp_spi_flash.h index 32ab3a6853..3761ec665d 100644 --- a/components/spi_flash/include/esp_spi_flash.h +++ b/components/spi_flash/include/esp_spi_flash.h @@ -92,12 +92,14 @@ esp_err_t spi_flash_write(size_t dest_addr, const void *src, size_t size); * * @note Flash encryption must be enabled for this function to work. * - * @note Destination flash address and length must be 16-byte - * aligned. Due to hardware limitations, this function is more - * efficient if both these arguments are 32-byte aligned. This is - * because the encryption engine natively deals with 32-byte rows of - * two AES blocks. Writing half a row (16 bytes) requires reading out - * the other 16 bytes and re-encrypting them back to the same value. + * @note Flash encryption must be enabled when calling this function. + * If flash encryption is disabled, the function returns + * ESP_ERR_INVALID_STATE. Use esp_flash_encryption_enabled() + * function to determine if flash encryption is enabled. + * + * @note Both dest_addr and size must be multiples of 16 bytes. For + * absolute best performance, both dest_addr and size arguments should + * be multiples of 32 bytes. * * @param dest_addr destination address in Flash. Must be a multiple of 16 bytes. * @param src pointer to the source buffer. @@ -125,7 +127,7 @@ esp_err_t spi_flash_read(size_t src_addr, void *dest, size_t size); * If flash encryption is enabled, this function will transparently decrypt data as it is read. * If flash encryption is not enabled, this function behaves the same as spi_flash_read(). * - * See @ref esp_flash_encryption_enabled() for a function to check if flash encryption is enabled. + * See esp_flash_encryption_enabled() for a function to check if flash encryption is enabled. * * @param src source address of the data in Flash. * @param dest pointer to the destination buffer @@ -133,7 +135,7 @@ esp_err_t spi_flash_read(size_t src_addr, void *dest, size_t size); * * @return esp_err_t */ -esp_err_t spi_flash_read_encrypted(size_t src, void *dstv, size_t size); +esp_err_t spi_flash_read_encrypted(size_t src, void *dest, size_t size); /** * @brief Enumeration which specifies memory space requested in an mmap call diff --git a/docs/api/storage/spi_flash.rst b/docs/api/storage/spi_flash.rst index ab03b38faa..2530db6d42 100644 --- a/docs/api/storage/spi_flash.rst +++ b/docs/api/storage/spi_flash.rst @@ -1,12 +1,5 @@ .. include:: ../../../components/spi_flash/README.rst -Application Example -------------------- - -`Instructions`_ - -.. _Instructions: ../template.html - API Reference ------------- @@ -15,6 +8,7 @@ Header Files * :component_file:`spi_flash/include/esp_spi_flash.h` * :component_file:`spi_flash/include/esp_partition.h` + * :component_file:`esp32/include/esp_flash_encrypt.h` Macros ^^^^^^ @@ -51,7 +45,9 @@ Functions .. doxygenfunction:: spi_flash_erase_sector .. doxygenfunction:: spi_flash_erase_range .. doxygenfunction:: spi_flash_write +.. doxygenfunction:: spi_flash_write_encrypted .. doxygenfunction:: spi_flash_read +.. doxygenfunction:: spi_flash_read_encrypted .. doxygenfunction:: spi_flash_mmap .. doxygenfunction:: spi_flash_munmap .. doxygenfunction:: spi_flash_mmap_dump @@ -64,4 +60,5 @@ Functions .. doxygenfunction:: esp_partition_write .. doxygenfunction:: esp_partition_erase_range .. doxygenfunction:: esp_partition_mmap +.. doxygenfunction:: esp_flash_encryption_enabled From d4462664b781f609622905003c9248b87477053d Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 6 Jan 2017 11:24:24 +1100 Subject: [PATCH 5/5] spi_flash: Move FLASH_PAGE_SIZE constant into esp_spi_flash.h --- components/spi_flash/flash_mmap.c | 13 ++++++------- components/spi_flash/flash_ops.c | 4 +--- components/spi_flash/include/esp_spi_flash.h | 5 ++++- docs/api/storage/spi_flash.rst | 1 + 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/components/spi_flash/flash_mmap.c b/components/spi_flash/flash_mmap.c index 0b83ac33c0..837fe0cc37 100644 --- a/components/spi_flash/flash_mmap.c +++ b/components/spi_flash/flash_mmap.c @@ -39,12 +39,11 @@ #define REGIONS_COUNT 4 #define PAGES_PER_REGION 64 -#define FLASH_PAGE_SIZE 0x10000 #define INVALID_ENTRY_VAL 0x100 #define VADDR0_START_ADDR 0x3F400000 #define VADDR1_START_ADDR 0x40000000 #define VADDR1_FIRST_USABLE_ADDR 0x400D0000 -#define PRO_IRAM0_FIRST_USABLE_PAGE ((VADDR1_FIRST_USABLE_ADDR - VADDR1_START_ADDR) / FLASH_PAGE_SIZE + 64) +#define PRO_IRAM0_FIRST_USABLE_PAGE ((VADDR1_FIRST_USABLE_ADDR - VADDR1_START_ADDR) / SPI_FLASH_MMU_PAGE_SIZE + 64) /* Ensure pages in a region haven't been marked as written via spi_flash_mark_modified_region(). If the page has @@ -124,8 +123,8 @@ esp_err_t IRAM_ATTR spi_flash_mmap(size_t src_addr, size_t size, spi_flash_mmap_ region_addr = VADDR1_FIRST_USABLE_ADDR; } // region which should be mapped - int phys_page = src_addr / FLASH_PAGE_SIZE; - int page_count = (size + FLASH_PAGE_SIZE - 1) / FLASH_PAGE_SIZE; + int phys_page = src_addr / SPI_FLASH_MMU_PAGE_SIZE; + int page_count = (size + SPI_FLASH_MMU_PAGE_SIZE - 1) / SPI_FLASH_MMU_PAGE_SIZE; // The following part searches for a range of MMU entries which can be used. // Algorithm is essentially naïve strstr algorithm, except that unused MMU // entries are treated as wildcards. @@ -171,7 +170,7 @@ esp_err_t IRAM_ATTR spi_flash_mmap(size_t src_addr, size_t size, spi_flash_mmap_ new_entry->count = page_count; new_entry->handle = ++s_mmap_last_handle; *out_handle = new_entry->handle; - *out_ptr = (void*) (region_addr + start * FLASH_PAGE_SIZE); + *out_ptr = (void*) (region_addr + start * SPI_FLASH_MMU_PAGE_SIZE); ret = ESP_OK; } spi_flash_enable_interrupts_caches_and_other_cpu(); @@ -256,8 +255,8 @@ static void IRAM_ATTR spi_flash_ensure_unmodified_region(size_t start_addr, size /* generic implementation for the previous two functions */ static inline IRAM_ATTR void update_written_pages(size_t start_addr, size_t length, bool mark) { - for (uint32_t addr = start_addr; addr < start_addr + length; addr += FLASH_PAGE_SIZE) { - int page = addr / FLASH_PAGE_SIZE; + for (uint32_t addr = start_addr; addr < start_addr + length; addr += SPI_FLASH_MMU_PAGE_SIZE) { + int page = addr / SPI_FLASH_MMU_PAGE_SIZE; if (page >= 256) { return; /* invalid address */ } diff --git a/components/spi_flash/flash_ops.c b/components/spi_flash/flash_ops.c index 76a5a6171d..0d3e0da87c 100644 --- a/components/spi_flash/flash_ops.c +++ b/components/spi_flash/flash_ops.c @@ -419,8 +419,6 @@ out: return spi_flash_translate_rc(rc); } -#define FLASH_PAGE_SIZE 0x10000 - esp_err_t IRAM_ATTR spi_flash_read_encrypted(size_t src, void *dstv, size_t size) { if (src + size > g_rom_flashchip.chip_size) { @@ -433,7 +431,7 @@ esp_err_t IRAM_ATTR spi_flash_read_encrypted(size_t src, void *dstv, size_t size esp_err_t err; const uint8_t *map; spi_flash_mmap_handle_t map_handle; - size_t map_src = src & ~(FLASH_PAGE_SIZE-1); + size_t map_src = src & ~(SPI_FLASH_MMU_PAGE_SIZE-1); size_t map_size = size + (src - map_src); err = spi_flash_mmap(map_src, map_size, SPI_FLASH_MMAP_DATA, (const void **)&map, &map_handle); diff --git a/components/spi_flash/include/esp_spi_flash.h b/components/spi_flash/include/esp_spi_flash.h index 3761ec665d..c1ad7d56fa 100644 --- a/components/spi_flash/include/esp_spi_flash.h +++ b/components/spi_flash/include/esp_spi_flash.h @@ -31,6 +31,8 @@ extern "C" { #define SPI_FLASH_SEC_SIZE 4096 /**< SPI Flash sector size */ +#define SPI_FLASH_MMU_PAGE_SIZE 0x10000 /**< Flash cache MMU mapping page size */ + /** * @brief Initialize SPI flash access driver * @@ -161,7 +163,8 @@ typedef uint32_t spi_flash_mmap_handle_t; * page allocation, use spi_flash_mmap_dump function. * * @param src_addr Physical address in flash where requested region starts. - * This address *must* be aligned to 64kB boundary. + * This address *must* be aligned to 64kB boundary + * (SPI_FLASH_MMU_PAGE_SIZE). * @param size Size of region which has to be mapped. This size will be rounded * up to a 64k boundary. * @param memory Memory space where the region should be mapped diff --git a/docs/api/storage/spi_flash.rst b/docs/api/storage/spi_flash.rst index 2530db6d42..cbb2be0d70 100644 --- a/docs/api/storage/spi_flash.rst +++ b/docs/api/storage/spi_flash.rst @@ -17,6 +17,7 @@ Macros .. doxygendefine:: ESP_ERR_FLASH_OP_FAIL .. doxygendefine:: ESP_ERR_FLASH_OP_TIMEOUT .. doxygendefine:: SPI_FLASH_SEC_SIZE +.. doxygendefine:: SPI_FLASH_MMU_PAGE_SIZE .. doxygendefine:: ESP_PARTITION_SUBTYPE_OTA Type Definitions