ESPHome 2026.5.0b1
Loading...
Searching...
No Matches
ota_bootloader_esp_idf.cpp
Go to the documentation of this file.
1#ifdef USE_ESP32
3
5
6#ifdef USE_OTA_PARTITIONS
7#include "esphome/core/log.h"
8
9#include <esp_image_format.h>
10#include <esp_ota_ops.h>
11
12namespace esphome::ota {
13
14static const char *const TAG = "ota.idf";
15
17 // Register the bootloader partition
18 esp_err_t err = esp_partition_register_external(nullptr, ESP_PRIMARY_BOOTLOADER_OFFSET, ESP_BOOTLOADER_SIZE,
19 "PrimaryBTLDR", ESP_PARTITION_TYPE_BOOTLOADER,
20 ESP_PARTITION_SUBTYPE_BOOTLOADER_PRIMARY, &this->bootloader_part_);
21 if (err != ESP_OK) {
22 ESP_LOGE(TAG, "esp_partition_register_external failed (bootloader) (err=0x%X)", err);
24 }
25
26 // Verify existing bootloader to make sure ESP_PRIMARY_BOOTLOADER_OFFSET is correct
27 esp_image_metadata_t data = {};
28 const esp_partition_pos_t part_pos = {
29 .offset = this->bootloader_part_->address,
30 .size = this->bootloader_part_->size,
31 };
32 err = esp_image_verify(ESP_IMAGE_VERIFY, &part_pos, &data);
33 if (err != ESP_OK) {
34 ESP_LOGE(TAG, "esp_image_verify failed (existing bootloader) (err=0x%X)", err);
36 }
37 return OTA_RESPONSE_OK;
38}
39
40// Pre-esp_ota_begin: enforce size limit, register/verify the existing bootloader, and validate the
41// partition table to confirm the bootloader region is at the expected offset (and therefore the
42// expected size). The partition table registration is released here; abort() cleans up the
43// bootloader registration if any later step fails.
45 if (image_size > ESP_BOOTLOADER_SIZE) {
46 ESP_LOGE(TAG, "Length of received data exceeds the available bootloader size: expected <=%zu bytes, got %zu",
47 ESP_BOOTLOADER_SIZE, image_size);
49 }
51 if (result != OTA_RESPONSE_OK) {
52 return result;
53 }
55 if (result != OTA_RESPONSE_OK) {
57 }
58 esp_partition_deregister_external(this->partition_table_part_);
59 this->partition_table_part_ = nullptr;
60 return OTA_RESPONSE_OK;
61}
62
63// Post-esp_ota_begin: verify the staging app partition is large enough, erase it, and redirect the
64// final write target to the bootloader partition. esp_ota_set_final_partition is called with
65// `restore_old_data=false` because we erased the staging region in advance.
67 if (this->partition_->size < this->bootloader_part_->size) {
68 ESP_LOGE(TAG, "Staging partition too small");
70 }
71 // Erase full size of the bootloader partition in the staging partition
72 // to avoid copying old data to the bootloader partition later
73 esp_err_t err = esp_partition_erase_range(this->partition_, 0, this->bootloader_part_->size);
74 if (err != ESP_OK) {
75 ESP_LOGW(TAG, "esp_partition_erase_range failed (err=0x%X)", err);
76 // No critical error, don't return
77 }
78 err = esp_ota_set_final_partition(this->update_handle_, this->bootloader_part_, false);
79 if (err != ESP_OK) {
80 esp_ota_abort(this->update_handle_);
81 this->update_handle_ = 0;
82 ESP_LOGE(TAG, "esp_ota_set_final_partition failed (err=0x%X)", err);
84 }
85 return OTA_RESPONSE_OK;
86}
87
88// After esp_ota_end: copy the staged image into the bootloader partition. esp_partition_copy is
89// the only window in which a power loss can render the device unbootable; everything before this
90// point either preserves the existing bootloader or fails harmlessly. After a successful copy the
91// first sector of staging is wiped so the device can't accidentally boot from it, and the
92// bootloader partition is deregistered.
94 if (ota_end_err != ESP_OK) {
96 }
97 esp_bootloader_desc_t bootloader_desc;
98 esp_err_t desc_err = esp_ota_get_bootloader_description(this->partition_, &bootloader_desc);
99#ifdef USE_ESP32_SRAM1_AS_IRAM
100 if (desc_err != ESP_OK) {
101 ESP_LOGE(TAG, "New bootloader does not support SRAM1 as IRAM");
103 }
104#endif
105 ESP_LOGE(TAG, "Starting bootloader update.\n"
106 " DO NOT REMOVE POWER until the update completes successfully.\n"
107 " Loss of power during this operation may render the device\n"
108 " unable to boot until it is recovered via a serial flash.");
109 esp_err_t err = esp_partition_copy(this->bootloader_part_, 0, this->partition_, 0, this->bootloader_part_->size);
110 if (err != ESP_OK) {
111 ESP_LOGE(TAG, "esp_partition_copy failed (err=0x%X)", err);
112 // Only if esp_partition_copy failed there's a chance of the device being unbootable
114 }
115 ESP_LOGI(TAG,
116 "Successfully installed the new bootloader\n"
117 " ESP-IDF %s",
118 (desc_err == ESP_OK) ? bootloader_desc.idf_ver : "version unknown");
119 // Wipe first sector of staging partition to make sure the device can't boot from it
120 err = esp_partition_erase_range(this->partition_, 0, this->partition_->erase_size);
121 if (err != ESP_OK) {
122 ESP_LOGW(TAG, "esp_partition_erase_range failed (err=0x%X)", err);
123 // No critical error, don't return
124 }
125 esp_partition_deregister_external(this->bootloader_part_);
126 this->bootloader_part_ = nullptr;
127 return OTA_RESPONSE_OK;
128}
129
130} // namespace esphome::ota
131
132#endif // USE_OTA_PARTITIONS
133#endif // USE_ESP32
OTAResponseTypes finalize_bootloader_update_(esp_err_t ota_end_err)
OTAResponseTypes prepare_bootloader_update_(size_t image_size)
OTAResponseTypes register_and_validate_bootloader_part_()
OTAResponseTypes register_and_validate_partition_table_part_()
@ OTA_RESPONSE_ERROR_BOOTLOADER_UPDATE
Definition ota_backend.h:48
@ OTA_RESPONSE_ERROR_BOOTLOADER_VERIFY
Definition ota_backend.h:47