9#include <esp_ota_ops.h>
11#include <spi_flash_mmap.h>
12#ifdef USE_OTA_DOWNGRADE_PROTECTION
13#include <esp_app_desc.h>
20std::unique_ptr<IDFOTABackend>
make_ota_backend() {
return make_unique<IDFOTABackend>(); }
25#ifdef USE_OTA_PARTITIONS
26 this->ota_type_ = ota_type;
29 if (image_size != ESP_PARTITION_TABLE_MAX_LEN) {
30 ESP_LOGE(TAG,
"Wrong partition table size: expected %u bytes, got %zu", ESP_PARTITION_TABLE_MAX_LEN, image_size);
33 memset(this->buf_, 0xFF,
sizeof this->buf_);
34 this->buf_written_ = 0;
35 this->image_size_ = image_size;
53#ifdef USE_OTA_ROLLBACK
57 esp_ota_mark_app_valid_cancel_rollback();
60 this->partition_ = esp_ota_get_next_update_partition(
nullptr);
61 if (this->partition_ ==
nullptr) {
68 if (image_size != 0 && image_size > this->partition_->size) {
73#ifdef USE_OTA_BLOCK_ERASE_AHEAD
74 this->erased_end_ = 0;
79 err = esp_ota_resume(this->partition_, 0, 0, &this->update_handle_);
80#if defined(CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE) && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0)
85 esp_ota_invalidate_inactive_ota_data_slot();
89 err = esp_ota_begin(this->partition_, OTA_WITH_SEQUENTIAL_WRITES, &this->update_handle_);
93 ESP_LOGE(TAG,
"OTA begin failed (err=0x%X)", err);
94 esp_ota_abort(this->update_handle_);
95 this->update_handle_ = 0;
96 if (err == ESP_ERR_FLASH_OP_TIMEOUT || err == ESP_ERR_FLASH_OP_FAIL) {
98 }
else if (err == ESP_ERR_OTA_PARTITION_CONFLICT) {
104#ifdef USE_OTA_PARTITIONS
117 memcpy(this->expected_bin_md5_, expected_md5, 32);
118 this->md5_set_ =
true;
122#ifdef USE_OTA_PARTITIONS
124 if (
len > PARTITION_TABLE_BUFFER_SIZE - this->buf_written_) {
125 ESP_LOGE(TAG,
"Wrong partition table size");
128 memcpy(this->buf_ + this->buf_written_, data,
len);
129 this->buf_written_ +=
len;
130 this->md5_.
add(data,
len);
139 if (this->written_ +
len > this->partition_->size) {
142#ifdef USE_OTA_BLOCK_ERASE_AHEAD
148 esp_err_t err = esp_ota_write(this->update_handle_, data,
len);
149 this->md5_.
add(data,
len);
151 ESP_LOGE(TAG,
"esp_ota_write failed (err=0x%X)", err);
152 if (err == ESP_ERR_OTA_VALIDATE_FAILED) {
154 }
else if (err == ESP_ERR_INVALID_SIZE) {
157 }
else if (err == ESP_ERR_FLASH_OP_TIMEOUT || err == ESP_ERR_FLASH_OP_FAIL) {
162 this->written_ +=
len;
166#ifdef USE_OTA_BLOCK_ERASE_AHEAD
168 const size_t end = this->written_ +
len;
169 if (this->erased_end_ >=
end) {
178 esp_err_t err = esp_partition_erase_range(this->partition_, this->erased_end_, erase_to - this->erased_end_);
180 ESP_LOGE(TAG,
"esp_partition_erase_range failed (err=0x%X)", err);
183 this->erased_end_ = erase_to;
189 if (this->md5_set_) {
191 if (!this->md5_.
equals_hex(this->expected_bin_md5_)) {
196#ifdef USE_OTA_PARTITIONS
207 esp_err_t err = esp_ota_end(this->update_handle_);
208 this->update_handle_ = 0;
210 ESP_LOGE(TAG,
"esp_ota_end failed (err=0x%X)", err);
212#ifdef USE_OTA_PARTITIONS
218#ifdef USE_OTA_SIGNED_VERIFICATION_MULTI_KEY
224 if (!this->verify_signed_image_(this->partition_)) {
228#ifdef USE_OTA_DOWNGRADE_PROTECTION
233 esp_app_desc_t incoming;
234 esp_err_t desc_err = esp_ota_get_partition_description(this->partition_, &incoming);
235 if (desc_err != ESP_OK) {
238 ESP_LOGW(TAG,
"Downgrade protection: could not read image version (err=0x%X); allowing update", desc_err);
240 ESP_LOGE(TAG,
"Rejecting downgrade: image version '%s' is older than running version '%s'", incoming.version,
241 ESPHOME_PROJECT_VERSION);
245 err = esp_ota_set_boot_partition(this->partition_);
250 if (err == ESP_ERR_OTA_VALIDATE_FAILED) {
251#ifdef USE_OTA_SIGNED_VERIFICATION
252 ESP_LOGE(TAG,
"OTA validation failed (err=0x%X) - possible signature verification failure", err);
258 if (err == ESP_ERR_FLASH_OP_TIMEOUT || err == ESP_ERR_FLASH_OP_FAIL) {
267#ifdef USE_OTA_PARTITIONS
268 if (this->partition_table_part_ !=
nullptr) {
269 esp_partition_deregister_external(this->partition_table_part_);
270 this->partition_table_part_ =
nullptr;
272 if (this->bootloader_part_ !=
nullptr) {
273 esp_partition_deregister_external(this->bootloader_part_);
274 this->bootloader_part_ =
nullptr;
279 esp_ota_abort(this->update_handle_);
280 this->update_handle_ = 0;
282#ifdef USE_OTA_BLOCK_ERASE_AHEAD
283 this->erased_end_ = 0;
bool equals_hex(const char *expected)
Compare the hash against a provided hex-encoded hash of get_size() * 2 characters,...
void calculate() override
Compute the digest, based on the provided data.
void add(const uint8_t *data, size_t len) override
Add bytes of data for the digest.
void init() override
Initialize a new MD5 digest computation.
OTAResponseTypes setup_bootloader_staging_()
OTAResponseTypes update_partition_table_()
void set_update_md5(const char *md5)
OTAResponseTypes finalize_bootloader_update_(esp_err_t ota_end_err)
OTAResponseTypes begin(size_t image_size, ota::OTAType ota_type=ota::OTA_TYPE_UPDATE_APP)
OTAResponseTypes prepare_bootloader_update_(size_t image_size)
OTAResponseTypes write(uint8_t *data, size_t len)
bool is_app_or_bootloader_update_() const
OTAResponseTypes hold_lock_if_ok_(OTAResponseTypes result)
bool try_acquire()
Returns false if held elsewhere.
@ OTA_TYPE_UPDATE_BOOTLOADER
@ OTA_TYPE_UPDATE_PARTITION_TABLE
ESPHOME_LOG_TAG(TAG, "ota")
bool version_is_older(const char *candidate, const char *reference)
Compare two dotted-numeric version strings (such as "1.2.3").
constexpr size_t next_erase_end(size_t write_end, size_t partition_size)
Target erased watermark for lazy block erase-ahead.
@ OTA_RESPONSE_ERROR_MD5_MISMATCH
@ OTA_RESPONSE_ERROR_VERSION_DOWNGRADE
@ OTA_RESPONSE_ERROR_ESP32_NOT_ENOUGH_SPACE
@ OTA_RESPONSE_ERROR_WRITING_FLASH
@ OTA_RESPONSE_ERROR_UNSUPPORTED_OTA_TYPE
@ OTA_RESPONSE_ERROR_UPDATE_END
@ OTA_RESPONSE_ERROR_SIGNATURE_INVALID
@ OTA_RESPONSE_ERROR_UPDATE_BUSY
@ OTA_RESPONSE_ERROR_UNKNOWN
@ OTA_RESPONSE_ERROR_NO_UPDATE_PARTITION
@ OTA_RESPONSE_ERROR_MAGIC
@ OTA_RESPONSE_ERROR_PARTITION_TABLE_VERIFY
std::unique_ptr< ArduinoLibreTinyOTABackend > make_ota_backend()