Merge branch 'bugfix/fix_flash_encryption_rom_c3_v5.1' into 'release/v5.1'

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

See merge request espressif/esp-idf!29351
This commit is contained in:
Jiang Jiang Jian 2024-03-05 11:00:07 +08:00
commit 8aa5610689
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-2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -146,12 +146,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);
@ -160,7 +160,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,
@ -173,14 +173,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) {
@ -239,7 +239,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 */
@ -1121,6 +1121,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
esp_err_t IRAM_ATTR esp_flash_write_encrypted(esp_flash_t *chip, uint32_t address, const void *buffer, uint32_t length)
{
esp_err_t ret = ESP_FAIL;
@ -1286,78 +1362,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-2022 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