Skip to content

Commit 786c9fb

Browse files
almir-okatocfriedt
authored andcommitted
flash: espressif: erase region before writing if encryption enabled
Ensuring flash region has been erased before writing to avoid inconsistences and force expected erased value (0xFF) into flash when erasing a region when Hardware Flash Encryption is enabled 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 f6a2821 commit 786c9fb

File tree

2 files changed

+297
-8
lines changed

2 files changed

+297
-8
lines changed

drivers/flash/flash_esp32.c

Lines changed: 285 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
#include <zephyr/kernel.h>
2525
#include <zephyr/device.h>
26+
#include <zephyr/devicetree.h>
2627
#include <stddef.h>
2728
#include <string.h>
2829
#include <errno.h>
@@ -34,6 +35,16 @@ LOG_MODULE_REGISTER(flash_esp32, CONFIG_FLASH_LOG_LEVEL);
3435

3536
#define FLASH_SEM_TIMEOUT (k_is_in_isr() ? K_NO_WAIT : K_FOREVER)
3637

38+
#ifdef CONFIG_ESP32_EFUSE_VIRTUAL_KEEP_IN_FLASH
39+
#define ENCRYPTION_IS_VIRTUAL (!efuse_hal_flash_encryption_enabled())
40+
#else
41+
#define ENCRYPTION_IS_VIRTUAL 0
42+
#endif
43+
44+
#ifndef ALIGN_OFFSET
45+
#define ALIGN_OFFSET(num, align) ((num) & ((align) - 1))
46+
#endif
47+
3748
struct flash_esp32_dev_config {
3849
spi_dev_t *controller;
3950
};
@@ -76,6 +87,227 @@ static inline void flash_esp32_sem_give(const struct device *dev)
7687
#include <stdint.h>
7788
#include <string.h>
7889

90+
#ifndef CONFIG_MCUBOOT
91+
static int flash_esp32_read_check_enc(off_t address, void *buffer, size_t length)
92+
{
93+
int ret = 0;
94+
95+
if (esp_flash_encryption_enabled()) {
96+
LOG_DBG("Flash read ENCRYPTED - address 0x%lx size 0x%x", address, length);
97+
ret = esp_flash_read_encrypted(NULL, address, buffer, length);
98+
} else {
99+
LOG_DBG("Flash read RAW - address 0x%lx size 0x%x", address, length);
100+
ret = esp_flash_read(NULL, buffer, address, length);
101+
}
102+
103+
if (ret != 0) {
104+
LOG_ERR("Flash read error: %d", ret);
105+
return -EIO;
106+
}
107+
108+
return 0;
109+
}
110+
111+
static int flash_esp32_write_check_enc(off_t address, const void *buffer, size_t length)
112+
{
113+
int ret = 0;
114+
115+
if (esp_flash_encryption_enabled() && !ENCRYPTION_IS_VIRTUAL) {
116+
LOG_DBG("Flash write ENCRYPTED - address 0x%lx size 0x%x", address, length);
117+
ret = esp_flash_write_encrypted(NULL, address, buffer, length);
118+
} else {
119+
LOG_DBG("Flash write RAW - address 0x%lx size 0x%x", address, length);
120+
ret = esp_flash_write(NULL, buffer, address, length);
121+
}
122+
123+
if (ret != 0) {
124+
LOG_ERR("Flash write error: %d", ret);
125+
return -EIO;
126+
}
127+
128+
return 0;
129+
}
130+
131+
#ifdef CONFIG_ESP_FLASH_ENCRYPTION
132+
#define FLASH_BUFFER_SIZE 32
133+
134+
static bool aligned_flash_write(size_t dest_addr, const void *src, size_t size, bool erase);
135+
static bool aligned_flash_erase(size_t addr, size_t size);
136+
137+
/* Auxiliar buffer to store the sector that will be partially written */
138+
static uint8_t write_aux_buf[FLASH_SECTOR_SIZE] = {0};
139+
140+
/* Auxiliar buffer to store the sector that will be partially erased */
141+
static uint8_t erase_aux_buf[FLASH_SECTOR_SIZE] = {0};
142+
143+
static bool aligned_flash_write(size_t dest_addr, const void *src, size_t size, bool erase)
144+
{
145+
bool flash_encryption_enabled = esp_flash_encryption_enabled();
146+
147+
/* When flash encryption is enabled, write alignment is 32 bytes, however to avoid
148+
* inconsistences the region may be erased right before writing, thus the alignment
149+
* is set to the erase required alignment (FLASH_SECTOR_SIZE).
150+
* When flash encryption is not enabled, regular write alignment is 4 bytes.
151+
*/
152+
size_t alignment = flash_encryption_enabled ? (erase ? FLASH_SECTOR_SIZE : 32) : 4;
153+
154+
if (IS_ALIGNED(dest_addr, alignment) && IS_ALIGNED((uintptr_t)src, 4) &&
155+
IS_ALIGNED(size, alignment)) {
156+
/* A single write operation is enough when all parameters are aligned */
157+
158+
if (flash_encryption_enabled && erase) {
159+
if (esp_flash_erase_region(NULL, dest_addr, size) != ESP_OK) {
160+
LOG_ERR("%s: Flash erase failed at 0x%08lx", __func__,
161+
(uintptr_t)dest_addr);
162+
return false;
163+
}
164+
}
165+
return flash_esp32_write_check_enc(dest_addr, (void *)src, size) == ESP_OK;
166+
}
167+
168+
LOG_DBG("%s: forcing unaligned write dest_addr: 0x%08lx src: 0x%08lx size: 0x%x erase: %c",
169+
__func__, (uintptr_t)dest_addr, (uintptr_t)src, size, erase ? 't' : 'f');
170+
171+
size_t write_addr = dest_addr;
172+
size_t bytes_remaining = size;
173+
size_t src_offset = 0;
174+
175+
while (bytes_remaining > 0) {
176+
size_t aligned_curr_addr = ROUND_DOWN(write_addr, alignment);
177+
size_t curr_buf_off = write_addr - aligned_curr_addr;
178+
size_t chunk_len = MIN(bytes_remaining, FLASH_SECTOR_SIZE - curr_buf_off);
179+
180+
/* Read data before modifying */
181+
if (flash_esp32_read_check_enc(aligned_curr_addr, write_aux_buf,
182+
ROUND_UP(chunk_len, alignment)) != ESP_OK) {
183+
LOG_ERR("%s: Flash read failed at 0x%08lx", __func__,
184+
(uintptr_t)aligned_curr_addr);
185+
return false;
186+
}
187+
188+
/* Erase if needed */
189+
if (flash_encryption_enabled && erase) {
190+
if (esp_flash_erase_region(NULL, aligned_curr_addr,
191+
ROUND_UP(chunk_len, FLASH_SECTOR_SIZE)) !=
192+
ESP_OK) {
193+
LOG_ERR("%s: Flash erase failed at 0x%08lx", __func__,
194+
(uintptr_t)aligned_curr_addr);
195+
return false;
196+
}
197+
}
198+
199+
/* Merge data into buffer */
200+
memcpy(&write_aux_buf[curr_buf_off], &((const uint8_t *)src)[src_offset],
201+
chunk_len);
202+
203+
/* Write back aligned chunk */
204+
if (flash_esp32_write_check_enc(aligned_curr_addr, write_aux_buf,
205+
ROUND_UP(chunk_len, alignment)) != ESP_OK) {
206+
LOG_ERR("%s: Flash write failed at 0x%08lx", __func__,
207+
(uintptr_t)aligned_curr_addr);
208+
return false;
209+
}
210+
211+
write_addr += chunk_len;
212+
src_offset += chunk_len;
213+
bytes_remaining -= chunk_len;
214+
}
215+
216+
return true;
217+
}
218+
219+
static bool erase_partial_sector(size_t addr, size_t sector_size, size_t erase_start,
220+
size_t erase_end)
221+
{
222+
/* Read full sector before erasing */
223+
if (flash_esp32_read_check_enc(addr, erase_aux_buf, sector_size) != ESP_OK) {
224+
LOG_ERR("%s: Flash read failed at 0x%08lx", __func__, (uintptr_t)addr);
225+
return false;
226+
}
227+
/* Erase full sector */
228+
if (esp_flash_erase_region(NULL, addr, sector_size) != ESP_OK) {
229+
LOG_ERR("%s: Flash erase failed at 0x%08lx", __func__, (uintptr_t)addr);
230+
return false;
231+
}
232+
/* Write back preserved head data up to erase_start */
233+
if (erase_start > 0) {
234+
if (!aligned_flash_write(addr, erase_aux_buf, erase_start, false)) {
235+
LOG_ERR("%s: Flash write failed at 0x%08lx", __func__, (uintptr_t)addr);
236+
return false;
237+
}
238+
}
239+
/* Write back preserved tail data from erase_end up to sector end */
240+
if (erase_end < sector_size) {
241+
if (!aligned_flash_write(addr + erase_end, &erase_aux_buf[erase_end],
242+
sector_size - erase_end, false)) {
243+
LOG_ERR("%s: Flash write failed at 0x%08lx", __func__,
244+
(uintptr_t)(addr + erase_end));
245+
return false;
246+
}
247+
}
248+
return true;
249+
}
250+
251+
static bool aligned_flash_erase(size_t addr, size_t size)
252+
{
253+
if (IS_ALIGNED(addr, FLASH_SECTOR_SIZE) && IS_ALIGNED(size, FLASH_SECTOR_SIZE)) {
254+
/* A single erase operation is enough when all parameters are aligned */
255+
return esp_flash_erase_region(NULL, addr, size) == ESP_OK;
256+
}
257+
258+
const size_t sector_size = FLASH_SECTOR_SIZE;
259+
const size_t start_addr = ROUND_DOWN(addr, sector_size);
260+
const size_t end_addr = ROUND_UP(addr + size, sector_size);
261+
const size_t total_len = end_addr - start_addr;
262+
263+
LOG_DBG("%s: forcing unaligned erase on sector Offset: "
264+
"0x%08lx Length: 0x%x total_len: 0x%x",
265+
__func__, (uintptr_t)addr, (int)size, total_len);
266+
267+
size_t current_addr = start_addr;
268+
269+
while (current_addr < end_addr) {
270+
bool preserve_head = (addr > current_addr);
271+
bool preserve_tail = ((addr + size) < (current_addr + sector_size));
272+
273+
if (preserve_head || preserve_tail) {
274+
size_t erase_start = preserve_head ? (addr - current_addr) : 0;
275+
size_t erase_end =
276+
MIN(current_addr + sector_size, addr + size) - current_addr;
277+
278+
LOG_DBG("%s: partial sector erase from: 0x%08lx to: 0x%08lx Length: 0x%x",
279+
__func__, (uintptr_t)(current_addr + erase_start),
280+
(uintptr_t)(current_addr + erase_end), erase_end - erase_start);
281+
282+
if (!erase_partial_sector(current_addr, sector_size, erase_start,
283+
erase_end)) {
284+
return false;
285+
}
286+
287+
current_addr += sector_size;
288+
} else {
289+
/* Full sector erase is safe, erase the next consecutive full sectors */
290+
size_t contiguous_size =
291+
ROUND_DOWN(addr + size, sector_size) - current_addr;
292+
293+
LOG_DBG("%s: sectors erased from: 0x%08lx length: 0x%x", __func__,
294+
(uintptr_t)current_addr, contiguous_size);
295+
296+
if (esp_flash_erase_region(NULL, current_addr, contiguous_size) != ESP_OK) {
297+
LOG_ERR("%s: Flash erase failed at 0x%08lx", __func__,
298+
(uintptr_t)current_addr);
299+
return false;
300+
}
301+
302+
current_addr += contiguous_size;
303+
}
304+
}
305+
306+
return true;
307+
}
308+
#endif /* CONFIG_ESP_FLASH_ENCRYPTION */
309+
#endif /* !CONFIG_MCUBOOT */
310+
79311
#ifdef CONFIG_MCUBOOT
80312
#define READ_BUFFER_SIZE 32
81313
static bool flash_esp32_is_aligned(off_t address, void *buffer, size_t length)
@@ -143,11 +375,7 @@ static int flash_esp32_read(const struct device *dev, off_t address, void *buffe
143375
#else
144376
flash_esp32_sem_take(dev);
145377

146-
if (esp_flash_encryption_enabled()) {
147-
ret = esp_flash_read_encrypted(NULL, address, buffer, length);
148-
} else {
149-
ret = esp_flash_read(NULL, buffer, address, length);
150-
}
378+
ret = flash_esp32_read_check_enc(address, buffer, length);
151379

152380
flash_esp32_sem_give(dev);
153381
#endif
@@ -179,12 +407,24 @@ static int flash_esp32_write(const struct device *dev,
179407
#else
180408
flash_esp32_sem_take(dev);
181409

410+
#ifdef CONFIG_ESP_FLASH_ENCRYPTION
411+
bool erase = false;
412+
182413
if (esp_flash_encryption_enabled()) {
183-
ret = esp_flash_write_encrypted(NULL, address, buffer, length);
184-
} else {
185-
ret = esp_flash_write(NULL, buffer, address, length);
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;
186418
}
187419

420+
if (!aligned_flash_write(address, buffer, length, erase)) {
421+
LOG_ERR("%s: Flash erase before write failed", __func__);
422+
ret = -1;
423+
}
424+
#else
425+
ret = flash_esp32_write_check_enc(address, buffer, length);
426+
#endif
427+
188428
flash_esp32_sem_give(dev);
189429
#endif
190430

@@ -204,7 +444,44 @@ static int flash_esp32_erase(const struct device *dev, off_t start, size_t len)
204444
ret = esp_rom_flash_erase_range(start, len);
205445
#else
206446
flash_esp32_sem_take(dev);
447+
448+
#ifdef CONFIG_ESP_FLASH_ENCRYPTION
449+
if (!aligned_flash_erase(start, len)) {
450+
ret = -EIO;
451+
}
452+
453+
if (esp_flash_encryption_enabled()) {
454+
uint8_t erased_val_buf[FLASH_BUFFER_SIZE];
455+
uint32_t bytes_remaining = len;
456+
uint32_t offset = start;
457+
uint32_t bytes_written = MIN(sizeof(erased_val_buf), len);
458+
459+
memset(erased_val_buf,
460+
flash_get_parameters(dev)->erase_value, sizeof(erased_val_buf));
461+
462+
/* When hardware flash encryption is enabled, force expected erased
463+
* value (0xFF) into flash when erasing a region.
464+
*
465+
* This is handled on this implementation because MCUboot's state
466+
* machine relies on erased valued data (0xFF) readed from a
467+
* previously erased region that was not written yet, however when
468+
* hardware flash encryption is enabled, the flash read always
469+
* decrypts whats being read from flash, thus a region that was
470+
* erased would not be read as what MCUboot expected (0xFF).
471+
*/
472+
while (bytes_remaining != 0) {
473+
if (!aligned_flash_write(offset, erased_val_buf, bytes_written, false)) {
474+
LOG_ERR("%s: Flash erase failed", __func__);
475+
return -1;
476+
}
477+
offset += bytes_written;
478+
bytes_remaining -= bytes_written;
479+
}
480+
}
481+
#else
207482
ret = esp_flash_erase_region(NULL, start, len);
483+
#endif
484+
208485
flash_esp32_sem_give(dev);
209486
#endif
210487
if (ret != 0) {

soc/espressif/common/Kconfig.flash

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,4 +126,16 @@ config SPI_FLASH_HPM_ENABLE
126126
This option is invisible, and will be selected automatically
127127
when ``ESPTOOLPY_FLASHFREQ_120M`` is selected.
128128

129+
DT_CHOSEN_Z_FLASH := zephyr,flash
130+
DT_CHOSEN_FLASH_NODE := $(dt_chosen_path,$(DT_CHOSEN_Z_FLASH))
131+
DT_FLASH_WRITE_BLOCK_SIZE := $(dt_node_int_prop_int,$(DT_CHOSEN_FLASH_NODE),write-block-size)
132+
config ESP_FLASH_ENCRYPTION
133+
bool "Hardware flash encryption compatibility"
134+
depends on !ESP_SIMPLE_BOOT && !MCUBOOT && "$(DT_FLASH_WRITE_BLOCK_SIZE)" = 32
135+
help
136+
Enable Espressif's hardware flash encryption compatibility with MCUboot
137+
Espressif Port.
138+
The flash encryption configuration must be enabled on the bootloader and
139+
the flash write block size must be set to 32 bytes on Zephyr's device tree.
140+
129141
endif # SOC_FAMILY_ESPRESSIF_ESP32

0 commit comments

Comments
 (0)