Skip to content

Commit c31bf4e

Browse files
committed
espressif: flash: fix image wrong state after swap-scratch when flash encryption is enabled
When hardware flash encryption is enabled, force expected erased value (0xFF) into flash when erasing a region, and also always do a real erase before writing data into flash. This is handled on this implementation because MCUboot's state machine relies on erased valued data (0xFF) readed from a previously erased region that was not written yet, however when hardware flash encryption is enabled, the flash read always decrypts whats being read from flash, thus a region that was erased would not be read as what MCUboot expected (0xFF). Signed-off-by: Almir Okato <almir.okato@espressif.com>
1 parent b4174bb commit c31bf4e

File tree

4 files changed

+121
-17
lines changed

4 files changed

+121
-17
lines changed

boot/espressif/hal/src/flash_encrypt.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -423,7 +423,7 @@ static esp_err_t encrypt_primary_slot(void)
423423
* MCUboot header
424424
*/
425425
err = bootloader_flash_read(CONFIG_ESP_IMAGE0_PRIMARY_START_ADDRESS + 0x20,
426-
&img_header, sizeof(esp_image_load_header_t), true);
426+
&img_header, sizeof(esp_image_load_header_t), false);
427427
if (err != ESP_OK) {
428428
ESP_LOGE(TAG, "Failed to read slot img header");
429429
return err;
@@ -464,7 +464,7 @@ esp_err_t esp_flash_encrypt_region(uint32_t src_addr, size_t data_length)
464464
wdt_hal_feed(&rtc_wdt_ctx);
465465
wdt_hal_write_protect_enable(&rtc_wdt_ctx);
466466
uint32_t sec_start = i + src_addr;
467-
err = bootloader_flash_read(sec_start, buf, FLASH_SECTOR_SIZE, true);
467+
err = bootloader_flash_read(sec_start, buf, FLASH_SECTOR_SIZE, false);
468468
if (err != ESP_OK) {
469469
goto flash_failed;
470470
}

boot/espressif/port/esp_mcuboot.c

Lines changed: 76 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -236,26 +236,38 @@ int flash_area_read(const struct flash_area *fa, uint32_t off, void *dst,
236236
return 0;
237237
}
238238

239-
static bool aligned_flash_write(size_t dest_addr, const void *src, size_t size)
239+
static bool aligned_flash_write(size_t dest_addr, const void *src, size_t size, bool erase)
240240
{
241241
#ifdef CONFIG_SECURE_FLASH_ENC_ENABLED
242242
bool flash_encryption_enabled = esp_flash_encryption_enabled();
243243
#else
244244
bool flash_encryption_enabled = false;
245245
#endif
246-
size_t alignment = flash_encryption_enabled ? 32 : 4;
246+
247+
/* When flash encryption is enabled, write alignment is 32 bytes, however to avoid
248+
* inconsistences the region may be erased right before writting, thus the alignment
249+
* is set to the erase required alignment (FLASH_SECTOR_SIZE).
250+
* When flash encryption is not enabled, regular write alignment is 4 bytes.
251+
*/
252+
size_t alignment = flash_encryption_enabled ? (erase ? FLASH_SECTOR_SIZE : 32) : 4;
247253

248254
if (IS_ALIGNED(dest_addr, alignment) && IS_ALIGNED((uintptr_t)src, 4) && IS_ALIGNED(size, alignment)) {
249255
/* A single write operation is enough when all parameters are aligned */
250256

257+
if (flash_encryption_enabled && erase) {
258+
if (bootloader_flash_erase_range(dest_addr, size) != ESP_OK) {
259+
return false;
260+
}
261+
}
251262
return bootloader_flash_write(dest_addr, (void *)src, size, flash_encryption_enabled) == ESP_OK;
252263
}
253-
BOOT_LOG_DBG("%s: forcing unaligned write dest_addr: 0x%08x src: 0x%08x size: 0x%x", __func__, (uint32_t)dest_addr, (uint32_t)src, size);
264+
BOOT_LOG_DBG("%s: forcing unaligned write dest_addr: 0x%08x src: 0x%08x size: 0x%x erase: %c",
265+
__func__, (uint32_t)dest_addr, (uint32_t)src, size, erase ? 't' : 'f');
254266

255267
const uint32_t aligned_addr = ALIGN_DOWN(dest_addr, alignment);
256268
const uint32_t addr_offset = ALIGN_OFFSET(dest_addr, alignment);
257269
uint32_t bytes_remaining = size;
258-
uint8_t write_data[FLASH_BUFFER_SIZE] __attribute__((aligned(32))) = {0};
270+
uint8_t write_data[FLASH_SECTOR_SIZE] __attribute__((aligned(32))) = {0};
259271

260272
/* Perform a read operation considering an offset not aligned to 4-byte boundary */
261273

@@ -264,6 +276,11 @@ static bool aligned_flash_write(size_t dest_addr, const void *src, size_t size)
264276
return false;
265277
}
266278

279+
if (flash_encryption_enabled && erase) {
280+
if (bootloader_flash_erase_range(aligned_addr, ALIGN_UP(bytes, FLASH_SECTOR_SIZE)) != ESP_OK) {
281+
return false;
282+
}
283+
}
267284
uint32_t bytes_written = bytes - addr_offset;
268285
memcpy(&write_data[addr_offset], src, bytes_written);
269286

@@ -283,6 +300,12 @@ static bool aligned_flash_write(size_t dest_addr, const void *src, size_t size)
283300
return false;
284301
}
285302

303+
if (flash_encryption_enabled && erase) {
304+
if (bootloader_flash_erase_range(aligned_addr + offset, ALIGN_UP(bytes, FLASH_SECTOR_SIZE)) != ESP_OK) {
305+
return false;
306+
}
307+
}
308+
286309
memcpy(write_data, &((uint8_t *)src)[bytes_written], bytes);
287310

288311
if (bootloader_flash_write(aligned_addr + offset, write_data, ALIGN_UP(bytes, alignment), flash_encryption_enabled) != ESP_OK) {
@@ -300,7 +323,7 @@ static bool aligned_flash_write(size_t dest_addr, const void *src, size_t size)
300323
static bool aligned_flash_erase(size_t addr, size_t size)
301324
{
302325
if (IS_ALIGNED(addr, FLASH_SECTOR_SIZE) && IS_ALIGNED(size, FLASH_SECTOR_SIZE)) {
303-
/* A single write operation is enough when all parameters are aligned */
326+
/* A single erase operation is enough when all parameters are aligned */
304327

305328
return bootloader_flash_erase_range(addr, size) == ESP_OK;
306329
}
@@ -328,13 +351,13 @@ static bool aligned_flash_erase(size_t addr, size_t size)
328351

329352
/* Write first part of non-erased data */
330353
if(addr_offset > 0) {
331-
if (!aligned_flash_write(aligned_addr, write_data, addr_offset)) {
354+
if (!aligned_flash_write(aligned_addr, write_data, addr_offset, false)) {
332355
return false;
333356
}
334357
}
335358

336359
if(bytes < sizeof(write_data)) {
337-
if (!aligned_flash_write(aligned_addr + bytes, write_data + bytes, sizeof(write_data) - bytes)) {
360+
if (!aligned_flash_write(aligned_addr + bytes, write_data + bytes, sizeof(write_data) - bytes, false)) {
338361
return false;
339362
}
340363
}
@@ -356,7 +379,7 @@ static bool aligned_flash_erase(size_t addr, size_t size)
356379
}
357380

358381
if(bytes < sizeof(write_data)) {
359-
if (!aligned_flash_write(aligned_addr + offset + bytes, write_data + bytes, sizeof(write_data) - bytes)) {
382+
if (!aligned_flash_write(aligned_addr + offset + bytes, write_data + bytes, sizeof(write_data) - bytes, false)) {
360383
return false;
361384
}
362385
}
@@ -383,9 +406,19 @@ int flash_area_write(const struct flash_area *fa, uint32_t off, const void *src,
383406
}
384407

385408
const uint32_t start_addr = fa->fa_off + off;
386-
BOOT_LOG_DBG("%s: Addr: 0x%08x Length: %d", __func__, (int)start_addr, (int)len);
409+
bool erase = false;
410+
BOOT_LOG_DBG("%s: Addr: 0x%08x Length: %d (0x%x)", __func__, (int)start_addr, (int)len, (int)len);
387411

388-
if (!aligned_flash_write(start_addr, src, len)) {
412+
#ifdef CONFIG_SECURE_FLASH_ENC_ENABLED
413+
if (esp_flash_encryption_enabled()) {
414+
/* Ensuring flash region has been erased before writing in order to
415+
* avoid inconsistences when hardware flash encryption is enabled.
416+
*/
417+
erase = true;
418+
}
419+
#endif
420+
421+
if (!aligned_flash_write(start_addr, src, len, erase)) {
389422
BOOT_LOG_ERR("%s: Flash write failed", __func__);
390423
return -1;
391424
}
@@ -402,7 +435,7 @@ int flash_area_erase(const struct flash_area *fa, uint32_t off, uint32_t len)
402435
}
403436

404437
const uint32_t start_addr = fa->fa_off + off;
405-
BOOT_LOG_DBG("%s: Addr: 0x%08x Length: %d", __func__, (int)start_addr, (int)len);
438+
BOOT_LOG_DBG("%s: Addr: 0x%08x Length: %d (0x%x)", __func__, (int)start_addr, (int)len, (int)len);
406439

407440
if(!aligned_flash_erase(start_addr, len)) {
408441
BOOT_LOG_ERR("%s: Flash erase failed", __func__);
@@ -411,7 +444,38 @@ int flash_area_erase(const struct flash_area *fa, uint32_t off, uint32_t len)
411444

412445
flush_cache(start_addr, len);
413446

414-
#if VALIDATE_PROGRAM_OP
447+
#ifdef CONFIG_SECURE_FLASH_ENC_ENABLED
448+
uint8_t write_data[FLASH_BUFFER_SIZE];
449+
memset(write_data, flash_area_erased_val(fa), sizeof(write_data));
450+
uint32_t bytes_remaining = len;
451+
uint32_t offset = start_addr;
452+
453+
uint32_t bytes_written = MIN(sizeof(write_data), len);
454+
if (esp_flash_encryption_enabled()) {
455+
/* When hardware flash encryption is enabled, force expected erased
456+
* value (0xFF) into flash when erasing a region.
457+
*
458+
* This is handled on this implementation because MCUboot's state
459+
* machine relies on erased valued data (0xFF) readed from a
460+
* previously erased region that was not written yet, however when
461+
* hardware flash encryption is enabled, the flash read always
462+
* decrypts whats being read from flash, thus a region that was
463+
* erased would not be read as what MCUboot expected (0xFF).
464+
*/
465+
while (bytes_remaining != 0) {
466+
if (!aligned_flash_write(offset, write_data, bytes_written, false)) {
467+
BOOT_LOG_ERR("%s: Flash erase failed", __func__);
468+
return -1;
469+
}
470+
offset += bytes_written;
471+
bytes_remaining -= bytes_written;
472+
}
473+
}
474+
475+
flush_cache(start_addr, len);
476+
#endif
477+
478+
#if VALIDATE_PROGRAM_OP && !defined(CONFIG_SECURE_FLASH_ENC_ENABLED)
415479
for (size_t i = 0; i < len; i++) {
416480
uint8_t *val = (void *)(start_addr + i);
417481
if (*val != 0xff) {

docs/readme-espressif.md

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,22 @@ Additional configuration related to MCUboot features and slot partitioning may b
120120

121121
2. Flash MCUboot in your device:
122122

123+
---
124+
***Note***
125+
126+
*Prior to flashing the bootloader and/or application and booting for the first time, ensure
127+
that the secondary slot and scratch area are erased. This is important because flash erased
128+
value (`0xFF` in case of this port) read from the trailer registers are part of MCUboots
129+
update-state checking mechanism, thus unknown data or garbage could be potentially
130+
interpreted as a valid state and lead to an unexpected behavior. Flash can be erased
131+
entirely using:*
132+
133+
```bash
134+
esptool.py -p <PORT> erase_flash
135+
```
136+
137+
---
138+
123139
```bash
124140
ninja -C build/ flash
125141
```
@@ -522,10 +538,26 @@ CONFIG_SECURE_ENABLE_SECURE_ROM_DL_MODE=1
522538
523539
---
524540
541+
---
542+
***Note***
543+
544+
As recommended, ensure that the secondary slot and scratch area are **erased** prior to
545+
the first time boot. Hardware flash encryption is transparent to MCUboot and the first boot
546+
encryption process will encrypt the whole slots and scratch including their trailer regions,
547+
and as said before, erased value read from trailer registers is also an expected state of
548+
MCUboot update checking process. Erase flash command:
549+
550+
```bash
551+
esptool.py -p <PORT> erase_flash
552+
```
553+
554+
---
555+
525556
### [Signing the image when working with Flash Encryption](#signing-the-image-when-working-with-flash-encryption)
526557
527-
When enabling flash encryption, it is required to signed the image using 32-byte alignment:
528-
`--align 32 --max-align 32`.
558+
When enabling flash encryption, it is required to sign the image using 32-byte alignment and also
559+
add the padding to fill the image up to the slot size:
560+
`--pad --align 32 --max-align 32`.
529561
530562
Command example:
531563
@@ -536,7 +568,9 @@ imgtool.py sign -k <YOUR_SIGNING_KEY.pem> --pad --pad-sig --align 32 --max-align
536568
### [Device generated key](#device-generated-key)
537569
538570
First ensure that the application image is able to perform encrypted read and write operations to
539-
the SPI Flash. Flash the bootloader and application normally:
571+
the SPI Flash.
572+
573+
Flash the bootloader and application normally:
540574
541575
```bash
542576
esptool.py -p <PORT> -b 2000000 --after no_reset --chip <ESP_CHIP> write_flash --flash_mode dio --flash_size <FLASH_SIZE> --flash_freq 40m <BOOTLOADER_FLASH_OFFSET> <BOOTLOADER_BIN>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
- Add cache flush after write/erase operations to avoid getting invalid
2+
data when these are followed by read operation.
3+
- Fix image wrong state after swap-scratch when hardware flash encryption
4+
is enabled. When hardware flash encryption is enabled, force expected
5+
erased value (0xFF) into flash when erasing a region, and also always
6+
do a real erase before writing data into flash.

0 commit comments

Comments
 (0)