Skip to content

Commit c18d50c

Browse files
authored
Use esp_partition_* functions in Updater.cpp (#3898)
Background The current implementation of Update() uses the spi_flash_* api to write and read from flash. These functions ignore the partition->encrypted flag and always write raw data to flash even if the partition is marked as encrypted. Changes in this PR Update() now uses the esp_partition_* api. Wrapper functions for esp_partition_* added to ESP.cpp. This was done to maintain a consistent approach to the way the spi_flash_* functions were used. I note though that not all of the esp-idf functions are used are wrapped, for example esp_ota_get_next_update_partition() so it may be that these should not be added? The current implementation of Update() changes the first (magic) byte of firmware to 0xFF on write, and then when the firmware is completely written changes it back to ESP_IMAGE_HEADER_MAGIC. This works without erasing the sector because flash bits can be changed from 1->0 (but not 0->1). If the flash is encrypted then the actual data written to flash will not be all ones, so this approach will not work. In addition, encrypted flash must be written in 16 byte blocks. So, instead of changing the first byte the changed code stashes the first 16 bytes, and starts writing at the 17th byte, leaving the first 16 bytes as 0xFF. Then, in _enablePartition() the stashed bytes can be successfully written. Benefits Whilst it's not possible to use encrypted flash directly from either the Arduino IDE or PIO it's reasonably straightforward to compile and flash a bootloader with the necessary support from a simple esp-idf project and then use ArduinoOTA for subsequent updates. This PR enables the use of this workflow until such time as encrypted flash is supported, and is a first (small) step toward adding support. Regardless of the above, the esp_partition_* api is recommended over the api_flash_* api. Application code should mostly use these esp_partition_* API functions instead of lower level spi_flash_* API functions. Partition table API functions do bounds checking and calculate correct offsets in flash, based on data stored in a partition table.
1 parent 80418fa commit c18d50c

File tree

4 files changed

+41
-15
lines changed

4 files changed

+41
-15
lines changed

Diff for: cores/esp32/Esp.cpp

+14
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,20 @@ bool EspClass::flashRead(uint32_t offset, uint32_t *data, size_t size)
336336
return spi_flash_read(offset, (uint32_t*) data, size) == ESP_OK;
337337
}
338338

339+
bool EspClass::partitionEraseRange(const esp_partition_t *partition, uint32_t offset, size_t size)
340+
{
341+
return esp_partition_erase_range(partition, offset, size) == ESP_OK;
342+
}
343+
344+
bool EspClass::partitionWrite(const esp_partition_t *partition, uint32_t offset, uint32_t *data, size_t size)
345+
{
346+
return esp_partition_write(partition, offset, data, size) == ESP_OK;
347+
}
348+
349+
bool EspClass::partitionRead(const esp_partition_t *partition, uint32_t offset, uint32_t *data, size_t size)
350+
{
351+
return esp_partition_read(partition, offset, data, size) == ESP_OK;
352+
}
339353

340354
uint64_t EspClass::getEfuseMac(void)
341355
{

Diff for: cores/esp32/Esp.h

+5
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#define ESP_H
2222

2323
#include <Arduino.h>
24+
#include <esp_partition.h>
2425

2526
/**
2627
* AVR macros for WDT managment
@@ -99,6 +100,10 @@ class EspClass
99100
bool flashWrite(uint32_t offset, uint32_t *data, size_t size);
100101
bool flashRead(uint32_t offset, uint32_t *data, size_t size);
101102

103+
bool partitionEraseRange(const esp_partition_t *partition, uint32_t offset, size_t size);
104+
bool partitionWrite(const esp_partition_t *partition, uint32_t offset, uint32_t *data, size_t size);
105+
bool partitionRead(const esp_partition_t *partition, uint32_t offset, uint32_t *data, size_t size);
106+
102107
uint64_t getEfuseMac();
103108

104109
};

Diff for: libraries/Update/src/Update.h

+4
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
#define U_SPIFFS 100
2727
#define U_AUTH 200
2828

29+
#define ENCRYPTED_BLOCK_SIZE 16
30+
2931
class UpdateClass {
3032
public:
3133
typedef std::function<void(size_t, size_t)> THandlerFunction_Progress;
@@ -163,10 +165,12 @@ class UpdateClass {
163165
bool _writeBuffer();
164166
bool _verifyHeader(uint8_t data);
165167
bool _verifyEnd();
168+
bool _enablePartition(const esp_partition_t* partition);
166169

167170

168171
uint8_t _error;
169172
uint8_t *_buffer;
173+
uint8_t *_skipBuffer;
170174
size_t _bufferLen;
171175
size_t _size;
172176
THandlerFunction_Progress _progress_callback;

Diff for: libraries/Update/src/Updater.cpp

+18-15
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,11 @@ static const char * _err2str(uint8_t _error){
3636
}
3737

3838
static bool _partitionIsBootable(const esp_partition_t* partition){
39-
uint8_t buf[4];
39+
uint8_t buf[ENCRYPTED_BLOCK_SIZE];
4040
if(!partition){
4141
return false;
4242
}
43-
if(!ESP.flashRead(partition->address, (uint32_t*)buf, 4)) {
43+
if(!ESP.partitionRead(partition, 0, (uint32_t*)buf, ENCRYPTED_BLOCK_SIZE)) {
4444
return false;
4545
}
4646

@@ -50,17 +50,11 @@ static bool _partitionIsBootable(const esp_partition_t* partition){
5050
return true;
5151
}
5252

53-
static bool _enablePartition(const esp_partition_t* partition){
54-
uint8_t buf[4];
53+
bool UpdateClass::_enablePartition(const esp_partition_t* partition){
5554
if(!partition){
5655
return false;
5756
}
58-
if(!ESP.flashRead(partition->address, (uint32_t*)buf, 4)) {
59-
return false;
60-
}
61-
buf[0] = ESP_IMAGE_HEADER_MAGIC;
62-
63-
return ESP.flashWrite(partition->address, (uint32_t*)buf, 4);
57+
return ESP.partitionWrite(partition, 0, (uint32_t*) _skipBuffer, ENCRYPTED_BLOCK_SIZE);
6458
}
6559

6660
UpdateClass::UpdateClass()
@@ -179,24 +173,33 @@ void UpdateClass::abort(){
179173

180174
bool UpdateClass::_writeBuffer(){
181175
//first bytes of new firmware
176+
uint8_t skip = 0;
182177
if(!_progress && _command == U_FLASH){
183178
//check magic
184179
if(_buffer[0] != ESP_IMAGE_HEADER_MAGIC){
185180
_abort(UPDATE_ERROR_MAGIC_BYTE);
186181
return false;
187182
}
188-
//remove magic byte from the firmware now and write it upon success
189-
//this ensures that partially written firmware will not be bootable
190-
_buffer[0] = 0xFF;
183+
184+
//Stash the first 16 bytes of data and set the offset so they are
185+
//not written at this point so that partially written firmware
186+
//will not be bootable
187+
skip = ENCRYPTED_BLOCK_SIZE;
188+
_skipBuffer = (uint8_t*)malloc(skip);
189+
if(!_skipBuffer){
190+
log_e("malloc failed");
191+
return false;
192+
}
193+
memcpy(_skipBuffer, _buffer, skip);
191194
}
192195
if (!_progress && _progress_callback) {
193196
_progress_callback(0, _size);
194197
}
195-
if(!ESP.flashEraseSector((_partition->address + _progress)/SPI_FLASH_SEC_SIZE)){
198+
if(!ESP.partitionEraseRange(_partition, _progress, SPI_FLASH_SEC_SIZE)){
196199
_abort(UPDATE_ERROR_ERASE);
197200
return false;
198201
}
199-
if (!ESP.flashWrite(_partition->address + _progress, (uint32_t*)_buffer, _bufferLen)) {
202+
if (!ESP.partitionWrite(_partition, _progress + skip, (uint32_t*)_buffer + skip/sizeof(uint32_t), _bufferLen - skip)) {
200203
_abort(UPDATE_ERROR_WRITE);
201204
return false;
202205
}

0 commit comments

Comments
 (0)