fix(spi_flash): Fix issue that flash encryption failed while rom_impl config is enabled

The issue is `esp_flash_write_encryped` function in ROM on ESP32C3, ESP32S3
calls legacy implementation, which uses old configuration. And this causes
write fails.
The solution in this commit is to compile and link this function(and related)
in IRAM instead of the ROM one.
The IRAM cost increases around 1.2KB after the fix
This commit is contained in:
Cao Sen Miao 2024-02-22 18:05:47 +08:00
parent 7cf8cc79a1
commit 44e16a6401
10 changed files with 217 additions and 153 deletions

View File

@ -35,6 +35,10 @@ config ESP_ROM_HAS_ERASE_0_REGION_BUG
bool
default y
config ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV
bool
default y
config ESP_ROM_GET_CLK_FREQ
bool
default y

View File

@ -14,6 +14,7 @@
#define ESP_ROM_USB_SERIAL_DEVICE_NUM (3) // UART uses USB_SERIAL_JTAG port in ROM.
#define ESP_ROM_HAS_RETARGETABLE_LOCKING (1) // ROM was built with retargetable locking
#define ESP_ROM_HAS_ERASE_0_REGION_BUG (1) // ROM has esp_flash_erase_region(size=0) bug
#define ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV (1) // `esp_flash_write_encrypted` in ROM has bug.
#define ESP_ROM_GET_CLK_FREQ (1) // Get clk frequency with rom function `ets_get_cpu_frequency`
#define ESP_ROM_NEEDS_SWSETUP_WORKAROUND (1) // ROM uses 32-bit time_t. A workaround is required to prevent printf functions from crashing
#define ESP_ROM_HAS_LAYOUT_TABLE (1) // ROM has the layout table

View File

@ -39,6 +39,10 @@ config ESP_ROM_HAS_ERASE_0_REGION_BUG
bool
default y
config ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV
bool
default y
config ESP_ROM_GET_CLK_FREQ
bool
default y

View File

@ -15,6 +15,7 @@
#define ESP_ROM_USB_OTG_NUM (3) // The serial port ID (UART, USB, ...) of USB_OTG CDC in the ROM.
#define ESP_ROM_USB_SERIAL_DEVICE_NUM (4) // The serial port ID (UART, USB, ...) of USB_SERIAL_JTAG in the ROM.
#define ESP_ROM_HAS_ERASE_0_REGION_BUG (1) // ROM has esp_flash_erase_region(size=0) bug
#define ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV (1) // `esp_flash_write_encrypted` in ROM has bug.
#define ESP_ROM_GET_CLK_FREQ (1) // Get clk frequency with rom function `ets_get_cpu_frequency`
#define ESP_ROM_HAS_HAL_WDT (1) // ROM has the implementation of Watchdog HAL driver
#define ESP_ROM_NEEDS_SWSETUP_WORKAROUND (1) // ROM uses 32-bit time_t. A workaround is required to prevent printf functions from crashing

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -147,12 +147,12 @@ _Static_assert(sizeof(io_mode_str)/IO_STR_LEN == SPI_FLASH_READ_MODE_MAX, "the i
esp_err_t esp_flash_read_chip_id(esp_flash_t* chip, uint32_t* flash_id);
#ifndef CONFIG_SPI_FLASH_ROM_IMPL
#if !CONFIG_SPI_FLASH_ROM_IMPL || ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV
static esp_err_t spiflash_start_default(esp_flash_t *chip);
static esp_err_t spiflash_end_default(esp_flash_t *chip, esp_err_t err);
static esp_err_t check_chip_pointer_default(esp_flash_t **inout_chip);
static esp_err_t flash_end_flush_cache(esp_flash_t* chip, esp_err_t err, bool bus_acquired, uint32_t address, uint32_t length);
#endif //CONFIG_SPI_FLASH_ROM_IMPL
#endif // !CONFIG_SPI_FLASH_ROM_IMPL || ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV
typedef struct {
esp_err_t (*start)(esp_flash_t *chip);
@ -161,7 +161,7 @@ typedef struct {
esp_err_t (*flash_end_flush_cache)(esp_flash_t* chip, esp_err_t err, bool bus_acquired, uint32_t address, uint32_t length);
} rom_spiflash_api_func_t;
#ifndef CONFIG_SPI_FLASH_ROM_IMPL
#if !CONFIG_SPI_FLASH_ROM_IMPL || ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV
// These functions can be placed in the ROM. For now we use the code in IDF.
DRAM_ATTR static rom_spiflash_api_func_t default_spiflash_rom_api = {
.start = spiflash_start_default,
@ -174,14 +174,14 @@ DRAM_ATTR rom_spiflash_api_func_t *rom_spiflash_api_funcs = &default_spiflash_ro
#else
extern rom_spiflash_api_func_t *esp_flash_api_funcs;
#define rom_spiflash_api_funcs esp_flash_api_funcs
#endif // CONFIG_SPI_FLASH_ROM_IMPL
#endif // !CONFIG_SPI_FLASH_ROM_IMPL || ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV
/* Static function to notify OS of a new SPI flash operation.
If returns an error result, caller must abort. If returns ESP_OK, caller must
call rom_spiflash_api_funcs->end() before returning.
*/
#ifndef CONFIG_SPI_FLASH_ROM_IMPL
#if !CONFIG_SPI_FLASH_ROM_IMPL || ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV
static esp_err_t IRAM_ATTR spiflash_start_default(esp_flash_t *chip)
{
if (chip->os_func != NULL && chip->os_func->start != NULL) {
@ -240,7 +240,7 @@ static IRAM_ATTR esp_err_t flash_end_flush_cache(esp_flash_t* chip, esp_err_t er
}
return rom_spiflash_api_funcs->end(chip, err);
}
#endif //CONFIG_SPI_FLASH_ROM_IMPL
#endif // !CONFIG_SPI_FLASH_ROM_IMPL || ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV
/* Top-level API functions, calling into chip_drv functions via chip->drv */
@ -1122,6 +1122,82 @@ restore_cache:
return err;
}
inline static IRAM_ATTR bool regions_overlap(uint32_t a_start, uint32_t a_len,uint32_t b_start, uint32_t b_len)
{
uint32_t a_end = a_start + a_len;
uint32_t b_end = b_start + b_len;
return (a_end > b_start && b_end > a_start);
}
esp_err_t IRAM_ATTR esp_flash_read_encrypted(esp_flash_t *chip, uint32_t address, void *out_buffer, uint32_t length)
{
esp_err_t err = rom_spiflash_api_funcs->chip_check(&chip);
if (err != ESP_OK) return err;
if (address + length > g_rom_flashchip.chip_size) {
return ESP_ERR_INVALID_SIZE;
}
if (length == 0) {
return ESP_OK;
}
if (out_buffer == NULL) {
return ESP_ERR_INVALID_ARG;
}
COUNTER_START();
const uint8_t *map;
spi_flash_mmap_handle_t map_handle;
size_t map_src = address & ~(SPI_FLASH_MMU_PAGE_SIZE - 1);
size_t map_size = length + (address - 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(out_buffer, map + (address - map_src), length);
spi_flash_munmap(map_handle);
COUNTER_ADD_BYTES(read, length);
COUNTER_STOP(read);
return err;
}
// test only, non-public
IRAM_ATTR esp_err_t esp_flash_get_io_mode(esp_flash_t* chip, bool* qe)
{
esp_err_t err = rom_spiflash_api_funcs->chip_check(&chip);
VERIFY_CHIP_OP(get_io_mode);
esp_flash_io_mode_t io_mode;
err = rom_spiflash_api_funcs->start(chip);
if (err != ESP_OK) {
return err;
}
err = chip->chip_drv->get_io_mode(chip, &io_mode);
err = rom_spiflash_api_funcs->end(chip, err);
if (err == ESP_OK) {
*qe = (io_mode == SPI_FLASH_QOUT);
}
return err;
}
IRAM_ATTR esp_err_t esp_flash_set_io_mode(esp_flash_t* chip, bool qe)
{
esp_err_t err = rom_spiflash_api_funcs->chip_check(&chip);
VERIFY_CHIP_OP(set_io_mode);
chip->read_mode = (qe? SPI_FLASH_QOUT: SPI_FLASH_SLOWRD);
err = rom_spiflash_api_funcs->start(chip);
if (err != ESP_OK) {
return err;
}
err = chip->chip_drv->set_io_mode(chip);
return rom_spiflash_api_funcs->end(chip, err);
}
#endif //CONFIG_SPI_FLASH_ROM_IMPL
#if !CONFIG_SPI_FLASH_ROM_IMPL || ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV
// use `esp_flash_write_encrypted` ROM version not in C3 and S3
FORCE_INLINE_ATTR esp_err_t s_encryption_write_lock(esp_flash_t *chip) {
#if CONFIG_IDF_TARGET_ESP32S2
esp_crypto_dma_lock_acquire();
@ -1337,78 +1413,7 @@ restore_cache:
return err;
}
inline static IRAM_ATTR bool regions_overlap(uint32_t a_start, uint32_t a_len,uint32_t b_start, uint32_t b_len)
{
uint32_t a_end = a_start + a_len;
uint32_t b_end = b_start + b_len;
return (a_end > b_start && b_end > a_start);
}
esp_err_t IRAM_ATTR esp_flash_read_encrypted(esp_flash_t *chip, uint32_t address, void *out_buffer, uint32_t length)
{
esp_err_t err = rom_spiflash_api_funcs->chip_check(&chip);
if (err != ESP_OK) return err;
if (address + length > g_rom_flashchip.chip_size) {
return ESP_ERR_INVALID_SIZE;
}
if (length == 0) {
return ESP_OK;
}
if (out_buffer == NULL) {
return ESP_ERR_INVALID_ARG;
}
COUNTER_START();
const uint8_t *map;
spi_flash_mmap_handle_t map_handle;
size_t map_src = address & ~(SPI_FLASH_MMU_PAGE_SIZE - 1);
size_t map_size = length + (address - 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(out_buffer, map + (address - map_src), length);
spi_flash_munmap(map_handle);
COUNTER_ADD_BYTES(read, length);
COUNTER_STOP(read);
return err;
}
// test only, non-public
IRAM_ATTR esp_err_t esp_flash_get_io_mode(esp_flash_t* chip, bool* qe)
{
esp_err_t err = rom_spiflash_api_funcs->chip_check(&chip);
VERIFY_CHIP_OP(get_io_mode);
esp_flash_io_mode_t io_mode;
err = rom_spiflash_api_funcs->start(chip);
if (err != ESP_OK) {
return err;
}
err = chip->chip_drv->get_io_mode(chip, &io_mode);
err = rom_spiflash_api_funcs->end(chip, err);
if (err == ESP_OK) {
*qe = (io_mode == SPI_FLASH_QOUT);
}
return err;
}
IRAM_ATTR esp_err_t esp_flash_set_io_mode(esp_flash_t* chip, bool qe)
{
esp_err_t err = rom_spiflash_api_funcs->chip_check(&chip);
VERIFY_CHIP_OP(set_io_mode);
chip->read_mode = (qe? SPI_FLASH_QOUT: SPI_FLASH_SLOWRD);
err = rom_spiflash_api_funcs->start(chip);
if (err != ESP_OK) {
return err;
}
err = chip->chip_drv->set_io_mode(chip);
return rom_spiflash_api_funcs->end(chip, err);
}
#endif //CONFIG_SPI_FLASH_ROM_IMPL
#endif // !CONFIG_SPI_FLASH_ROM_IMPL || ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV
//init suspend mode cmd, uses internal.
esp_err_t esp_flash_suspend_cmd_init(esp_flash_t* chip)

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -13,6 +13,7 @@
#include "esp_log.h"
#include "esp_attr.h"
#include "esp_private/spi_flash_os.h"
#include "esp_rom_caps.h"
typedef struct flash_chip_dummy {
uint8_t dio_dummy_bitlen;
@ -303,69 +304,6 @@ esp_err_t spi_flash_chip_generic_write(esp_flash_t *chip, const void *buffer, ui
return err;
}
esp_err_t spi_flash_chip_generic_write_encrypted(esp_flash_t *chip, const void *buffer, uint32_t address, uint32_t length)
{
spi_flash_encryption_t *esp_flash_encryption = &esp_flash_encryption_default;
esp_err_t err = ESP_OK;
// Encryption must happen on main flash.
if (chip != esp_flash_default_chip) {
return ESP_ERR_NOT_SUPPORTED;
}
/* Check if the buffer and length can qualify the requirments */
if (esp_flash_encryption->flash_encryption_check(address, length) != true) {
return ESP_ERR_NOT_SUPPORTED;
}
const uint8_t *data_bytes = (const uint8_t *)buffer;
esp_flash_encryption->flash_encryption_enable();
while (length > 0) {
int block_size;
/* Write the largest block if possible */
if (address % 64 == 0 && length >= 64) {
block_size = 64;
} else if (address % 32 == 0 && length >= 32) {
block_size = 32;
} else {
block_size = 16;
}
// Prepare the flash chip (same time as AES operation, for performance)
esp_flash_encryption->flash_encryption_data_prepare(address, (uint32_t *)data_bytes, block_size);
err = chip->chip_drv->set_chip_write_protect(chip, false);
if (err != ESP_OK) {
return err;
}
// Waiting for encrypting buffer to finish and making result visible for SPI1
esp_flash_encryption->flash_encryption_done();
// Note: For encryption function, after write flash command is sent. The hardware will write the encrypted buffer
// prepared in XTS_FLASH_ENCRYPTION register in function `flash_encryption_data_prepare`, instead of the origin
// buffer named `data_bytes`.
err = chip->chip_drv->write(chip, (uint32_t *)data_bytes, address, length);
if (err != ESP_OK) {
return err;
}
err = chip->chip_drv->wait_idle(chip, chip->chip_drv->timeout->page_program_timeout);
if (err != ESP_OK) {
return err;
}
// Note: we don't wait for idle status here, because this way
// the AES peripheral can start encrypting the next
// block while the SPI flash chip is busy completing the write
esp_flash_encryption->flash_encryption_destroy();
length -= block_size;
data_bytes += block_size;
address += block_size;
}
esp_flash_encryption->flash_encryption_disable();
return err;
}
esp_err_t spi_flash_chip_generic_set_write_protect(esp_flash_t *chip, bool write_protect)
{
esp_err_t err = ESP_OK;
@ -562,6 +500,71 @@ esp_err_t spi_flash_chip_generic_set_io_mode(esp_flash_t *chip)
}
#endif // CONFIG_SPI_FLASH_ROM_IMPL
#if !CONFIG_SPI_FLASH_ROM_IMPL || ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV
esp_err_t spi_flash_chip_generic_write_encrypted(esp_flash_t *chip, const void *buffer, uint32_t address, uint32_t length)
{
spi_flash_encryption_t *esp_flash_encryption = &esp_flash_encryption_default;
esp_err_t err = ESP_OK;
// Encryption must happen on main flash.
if (chip != esp_flash_default_chip) {
return ESP_ERR_NOT_SUPPORTED;
}
/* Check if the buffer and length can qualify the requirments */
if (esp_flash_encryption->flash_encryption_check(address, length) != true) {
return ESP_ERR_NOT_SUPPORTED;
}
const uint8_t *data_bytes = (const uint8_t *)buffer;
esp_flash_encryption->flash_encryption_enable();
while (length > 0) {
int block_size;
/* Write the largest block if possible */
if (address % 64 == 0 && length >= 64) {
block_size = 64;
} else if (address % 32 == 0 && length >= 32) {
block_size = 32;
} else {
block_size = 16;
}
// Prepare the flash chip (same time as AES operation, for performance)
esp_flash_encryption->flash_encryption_data_prepare(address, (uint32_t *)data_bytes, block_size);
err = chip->chip_drv->set_chip_write_protect(chip, false);
if (err != ESP_OK) {
return err;
}
// Waiting for encrypting buffer to finish and making result visible for SPI1
esp_flash_encryption->flash_encryption_done();
// Note: For encryption function, after write flash command is sent. The hardware will write the encrypted buffer
// prepared in XTS_FLASH_ENCRYPTION register in function `flash_encryption_data_prepare`, instead of the origin
// buffer named `data_bytes`.
err = chip->chip_drv->write(chip, (uint32_t *)data_bytes, address, length);
if (err != ESP_OK) {
return err;
}
err = chip->chip_drv->wait_idle(chip, chip->chip_drv->timeout->page_program_timeout);
if (err != ESP_OK) {
return err;
}
// Note: we don't wait for idle status here, because this way
// the AES peripheral can start encrypting the next
// block while the SPI flash chip is busy completing the write
esp_flash_encryption->flash_encryption_destroy();
length -= block_size;
data_bytes += block_size;
address += block_size;
}
esp_flash_encryption->flash_encryption_disable();
return err;
}
#endif // !CONFIG_SPI_FLASH_ROM_IMPL || ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV
esp_err_t spi_flash_chip_generic_read_unique_id(esp_flash_t *chip, uint64_t* flash_unique_id)
{
uint64_t unique_id_buf = 0;

View File

@ -1,6 +1,5 @@
# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
import pytest
from pytest_embedded import Dut
@ -19,12 +18,26 @@ def test_flash_encryption(dut: Dut) -> None:
dut.run_all_single_board_cases()
@pytest.mark.esp32c3
@pytest.mark.flash_encryption
@pytest.mark.parametrize(
'config',
[
'rom_impl',
],
indirect=True,
)
def test_flash_encryption_rom_impl(dut: Dut) -> None:
dut.run_all_single_board_cases()
@pytest.mark.esp32s3
@pytest.mark.flash_encryption_f4r8
@pytest.mark.parametrize(
'config',
[
'release_f4r8',
'rom_impl',
],
indirect=True,
)

View File

@ -0,0 +1,5 @@
CONFIG_ESP_TASK_WDT_EN=n
CONFIG_SPI_FLASH_ROM_IMPL=y
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y

View File

@ -1,8 +1,5 @@
# SPDX-FileCopyrightText: 2018-2022 Espressif Systems (Shanghai) CO LTD
# SPDX-FileCopyrightText: 2018-2024 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
from __future__ import print_function
import binascii
from collections import namedtuple
from io import BytesIO
@ -19,10 +16,7 @@ from pytest_embedded import Dut
# espefuse.py --do-not-confirm -p $ESPPORT burn_efuse FLASH_CRYPT_CONFIG 0xf
# espefuse.py --do-not-confirm -p $ESPPORT burn_efuse FLASH_CRYPT_CNT 0x1
# espefuse.py --do-not-confirm -p $ESPPORT burn_key flash_encryption key.bin
@pytest.mark.esp32
@pytest.mark.esp32c3
@pytest.mark.flash_encryption
def test_examples_security_flash_encryption(dut: Dut) -> None:
def _test_flash_encryption(dut: Dut) -> None:
# Erase the nvs_key partition
dut.serial.erase_partition('nvs_key')
# calculate the expected ciphertext
@ -66,3 +60,23 @@ def test_examples_security_flash_encryption(dut: Dut) -> None:
]
for line in lines:
dut.expect(line, timeout=20)
@pytest.mark.esp32
@pytest.mark.esp32c3
@pytest.mark.flash_encryption
def test_examples_security_flash_encryption(dut: Dut) -> None:
_test_flash_encryption(dut)
@pytest.mark.esp32c3
@pytest.mark.flash_encryption
@pytest.mark.parametrize(
'config',
[
'rom_impl',
],
indirect=True,
)
def test_examples_security_flash_encryption_rom_impl(dut: Dut) -> None:
_test_flash_encryption(dut)

View File

@ -0,0 +1,14 @@
# Configurations for flash encryption.
CONFIG_SECURE_FLASH_ENC_ENABLED=y
CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT=y
CONFIG_SECURE_BOOT_ALLOW_ROM_BASIC=y
CONFIG_SECURE_BOOT_ALLOW_JTAG=y
CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_ENC=y
CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_DEC=y
CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_CACHE=y
CONFIG_SECURE_FLASH_REQUIRE_ALREADY_ENABLED=y
CONFIG_SPI_FLASH_ROM_IMPL=y
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y