Optimize Update class

- add roll back API to switch the running partition
- do not write the partition magic until the end to prevent booting
into partially written update
This commit is contained in:
me-no-dev 2017-06-02 18:24:26 +03:00
parent dcdf8132d6
commit 063d608e18
2 changed files with 70 additions and 7 deletions

View File

@ -137,6 +137,15 @@ class UpdateClass {
return written; return written;
} }
/*
check if there is a firmware on the other OTA partition that you can bootinto
*/
bool canRollBack();
/*
set the other OTA partition as bootable (reboot to enable)
*/
bool rollBack();
private: private:
void _reset(); void _reset();
void _abort(uint8_t err); void _abort(uint8_t err);

View File

@ -35,6 +35,34 @@ static const char * _err2str(uint8_t _error){
return ("UNKNOWN"); return ("UNKNOWN");
} }
static bool _partitionIsBootable(const esp_partition_t* partition){
uint8_t buf[4];
if(!partition){
return false;
}
if(!ESP.flashRead(partition->address, (uint32_t*)buf, 4)) {
return false;
}
if(buf[0] != ESP_IMAGE_HEADER_MAGIC) {
return false;
}
return true;
}
static bool _enablePartition(const esp_partition_t* partition){
uint8_t buf[4];
if(!partition){
return false;
}
if(!ESP.flashRead(partition->address, (uint32_t*)buf, 4)) {
return false;
}
buf[0] = ESP_IMAGE_HEADER_MAGIC;
return ESP.flashWrite(partition->address, (uint32_t*)buf, 4);
}
UpdateClass::UpdateClass() UpdateClass::UpdateClass()
: _error(0) : _error(0)
, _buffer(0) , _buffer(0)
@ -56,6 +84,22 @@ void UpdateClass::_reset() {
_command = U_FLASH; _command = U_FLASH;
} }
bool UpdateClass::canRollBack(){
if(_buffer){ //Update is running
return false;
}
const esp_partition_t* partition = esp_ota_get_next_update_partition(NULL);
return _partitionIsBootable(partition);
}
bool UpdateClass::rollBack(){
if(_buffer){ //Update is running
return false;
}
const esp_partition_t* partition = esp_ota_get_next_update_partition(NULL);
return _partitionIsBootable(partition) && !esp_ota_set_boot_partition(partition);
}
bool UpdateClass::begin(size_t size, int command) { bool UpdateClass::begin(size_t size, int command) {
if(_size > 0){ if(_size > 0){
log_w("already running"); log_w("already running");
@ -121,6 +165,17 @@ void UpdateClass::abort(){
} }
bool UpdateClass::_writeBuffer(){ bool UpdateClass::_writeBuffer(){
//first bytes of new firmware
if(!_progress && _command == U_FLASH){
//check magic
if(_buffer[0] != ESP_IMAGE_HEADER_MAGIC){
_abort(UPDATE_ERROR_MAGIC_BYTE);
return false;
}
//remove magic byte from the firmware now and write it upon success
//this ensures that partially written firmware will not be bootable
_buffer[0] = 0xFF;
}
if(!ESP.flashEraseSector((_partition->address + _progress)/SPI_FLASH_SEC_SIZE)){ if(!ESP.flashEraseSector((_partition->address + _progress)/SPI_FLASH_SEC_SIZE)){
_abort(UPDATE_ERROR_ERASE); _abort(UPDATE_ERROR_ERASE);
return false; return false;
@ -129,6 +184,10 @@ bool UpdateClass::_writeBuffer(){
_abort(UPDATE_ERROR_WRITE); _abort(UPDATE_ERROR_WRITE);
return false; return false;
} }
//restore magic or md5 will fail
if(!_progress && _command == U_FLASH){
_buffer[0] = ESP_IMAGE_HEADER_MAGIC;
}
_md5.add(_buffer, _bufferLen); _md5.add(_buffer, _bufferLen);
_progress += _bufferLen; _progress += _bufferLen;
_bufferLen = 0; _bufferLen = 0;
@ -150,17 +209,11 @@ bool UpdateClass::_verifyHeader(uint8_t data) {
bool UpdateClass::_verifyEnd() { bool UpdateClass::_verifyEnd() {
if(_command == U_FLASH) { if(_command == U_FLASH) {
uint8_t buf[4]; if(!_partitionIsBootable(_partition)) {
if(!ESP.flashRead(_partition->address, (uint32_t*)buf, 4)) {
_abort(UPDATE_ERROR_READ); _abort(UPDATE_ERROR_READ);
return false; return false;
} }
if(buf[0] != ESP_IMAGE_HEADER_MAGIC) {
_abort(UPDATE_ERROR_MAGIC_BYTE);
return false;
}
if(esp_ota_set_boot_partition(_partition)){ if(esp_ota_set_boot_partition(_partition)){
_abort(UPDATE_ERROR_ACTIVATE); _abort(UPDATE_ERROR_ACTIVATE);
return false; return false;
@ -168,6 +221,7 @@ bool UpdateClass::_verifyEnd() {
_reset(); _reset();
return true; return true;
} else if(_command == U_SPIFFS) { } else if(_command == U_SPIFFS) {
_reset();
return true; return true;
} }
return false; return false;