Skip to content

Commit 4302112

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 <[email protected]>
1 parent db995ee commit 4302112

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
@@ -237,26 +237,38 @@ int flash_area_read(const struct flash_area *fa, uint32_t off, void *dst,
237237
return 0;
238238
}
239239

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

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

258+
if (flash_encryption_enabled && erase) {
259+
if (bootloader_flash_erase_range(dest_addr, size) != ESP_OK) {
260+
return false;
261+
}
262+
}
252263
return bootloader_flash_write(dest_addr, (void *)src, size, flash_encryption_enabled) == ESP_OK;
253264
}
254-
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);
265+
BOOT_LOG_DBG("%s: forcing unaligned write dest_addr: 0x%08x src: 0x%08x size: 0x%x erase: %c",
266+
__func__, (uint32_t)dest_addr, (uint32_t)src, size, erase ? 't' : 'f');
255267

256268
const uint32_t aligned_addr = ALIGN_DOWN(dest_addr, alignment);
257269
const uint32_t addr_offset = ALIGN_OFFSET(dest_addr, alignment);
258270
uint32_t bytes_remaining = size;
259-
uint8_t write_data[FLASH_BUFFER_SIZE] __attribute__((aligned(32))) = {0};
271+
uint8_t write_data[FLASH_SECTOR_SIZE] __attribute__((aligned(32))) = {0};
260272

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

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

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

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

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

289312
if (bootloader_flash_write(aligned_addr + offset, write_data, ALIGN_UP(bytes, alignment), flash_encryption_enabled) != ESP_OK) {
@@ -301,7 +324,7 @@ static bool aligned_flash_write(size_t dest_addr, const void *src, size_t size)
301324
static bool aligned_flash_erase(size_t addr, size_t size)
302325
{
303326
if (IS_ALIGNED(addr, FLASH_SECTOR_SIZE) && IS_ALIGNED(size, FLASH_SECTOR_SIZE)) {
304-
/* A single write operation is enough when all parameters are aligned */
327+
/* A single erase operation is enough when all parameters are aligned */
305328

306329
return bootloader_flash_erase_range(addr, size) == ESP_OK;
307330
}
@@ -329,13 +352,13 @@ static bool aligned_flash_erase(size_t addr, size_t size)
329352

330353
/* Write first part of non-erased data */
331354
if(addr_offset > 0) {
332-
if (!aligned_flash_write(aligned_addr, write_data, addr_offset)) {
355+
if (!aligned_flash_write(aligned_addr, write_data, addr_offset, false)) {
333356
return false;
334357
}
335358
}
336359

337360
if(bytes < sizeof(write_data)) {
338-
if (!aligned_flash_write(aligned_addr + bytes, write_data + bytes, sizeof(write_data) - bytes)) {
361+
if (!aligned_flash_write(aligned_addr + bytes, write_data + bytes, sizeof(write_data) - bytes, false)) {
339362
return false;
340363
}
341364
}
@@ -357,7 +380,7 @@ static bool aligned_flash_erase(size_t addr, size_t size)
357380
}
358381

359382
if(bytes < sizeof(write_data)) {
360-
if (!aligned_flash_write(aligned_addr + offset + bytes, write_data + bytes, sizeof(write_data) - bytes)) {
383+
if (!aligned_flash_write(aligned_addr + offset + bytes, write_data + bytes, sizeof(write_data) - bytes, false)) {
361384
return false;
362385
}
363386
}
@@ -384,9 +407,19 @@ int flash_area_write(const struct flash_area *fa, uint32_t off, const void *src,
384407
}
385408

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

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

405438
const uint32_t start_addr = fa->fa_off + off;
406-
BOOT_LOG_DBG("%s: Addr: 0x%08x Length: %d", __func__, (int)start_addr, (int)len);
439+
BOOT_LOG_DBG("%s: Addr: 0x%08x Length: %d (0x%x)", __func__, (int)start_addr, (int)len, (int)len);
407440

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

413446
flush_cache(start_addr, len);
414447

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

533549
---
534550

551+
---
552+
***Note***
553+
554+
As recommended, ensure that the secondary slot and scratch area are **erased** prior to
555+
the first time boot. Hardware flash encryption is transparent to MCUboot and the first boot
556+
encryption process will encrypt the whole slots and scratch including their trailer regions,
557+
and as said before, erased value read from trailer registers is also an expected state of
558+
MCUboot update checking process. Erase flash command:
559+
560+
```bash
561+
esptool.py -p <PORT> erase_flash
562+
```
563+
564+
---
565+
535566
### [Signing the image when working with Flash Encryption](#signing-the-image-when-working-with-flash-encryption)
536567

537-
When enabling flash encryption, it is required to signed the image using 32-byte alignment:
538-
`--align 32 --max-align 32`.
568+
When enabling flash encryption, it is required to sign the image using 32-byte alignment and also
569+
add the padding to fill the image up to the slot size:
570+
`--pad --align 32 --max-align 32`.
539571

540572
Command example:
541573

@@ -546,7 +578,9 @@ imgtool.py sign -k <YOUR_SIGNING_KEY.pem> --pad --pad-sig --align 32 --max-align
546578
### [Device generated key](#device-generated-key)
547579

548580
First ensure that the application image is able to perform encrypted read and write operations to
549-
the SPI Flash. Flash the bootloader and application normally:
581+
the SPI Flash.
582+
583+
Flash the bootloader and application normally:
550584

551585
```bash
552586
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)