mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
Merge branch 'feature/refactoring_ota_part' into 'master'
bootloader/app_update: Refactoring otadata part See merge request idf/esp-idf!3544
This commit is contained in:
commit
e04fd42176
@ -36,10 +36,10 @@
|
|||||||
#include "rom/crc.h"
|
#include "rom/crc.h"
|
||||||
#include "soc/dport_reg.h"
|
#include "soc/dport_reg.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
|
#include "esp_flash_data_types.h"
|
||||||
|
#include "bootloader_common.h"
|
||||||
|
#include "sys/param.h"
|
||||||
|
|
||||||
|
|
||||||
#define OTA_MAX(a,b) ((a) >= (b) ? (a) : (b))
|
|
||||||
#define OTA_MIN(a,b) ((a) <= (b) ? (a) : (b))
|
|
||||||
#define SUB_TYPE_ID(i) (i & 0x0F)
|
#define SUB_TYPE_ID(i) (i & 0x0F)
|
||||||
|
|
||||||
typedef struct ota_ops_entry_ {
|
typedef struct ota_ops_entry_ {
|
||||||
@ -52,19 +52,10 @@ typedef struct ota_ops_entry_ {
|
|||||||
LIST_ENTRY(ota_ops_entry_) entries;
|
LIST_ENTRY(ota_ops_entry_) entries;
|
||||||
} ota_ops_entry_t;
|
} ota_ops_entry_t;
|
||||||
|
|
||||||
/* OTA selection structure (two copies in the OTA data partition.)
|
|
||||||
Size of 32 bytes is friendly to flash encryption */
|
|
||||||
typedef struct {
|
|
||||||
uint32_t ota_seq;
|
|
||||||
uint8_t seq_label[24];
|
|
||||||
uint32_t crc; /* CRC32 of ota_seq field only */
|
|
||||||
} ota_select;
|
|
||||||
|
|
||||||
static LIST_HEAD(ota_ops_entries_head, ota_ops_entry_) s_ota_ops_entries_head =
|
static LIST_HEAD(ota_ops_entries_head, ota_ops_entry_) s_ota_ops_entries_head =
|
||||||
LIST_HEAD_INITIALIZER(s_ota_ops_entries_head);
|
LIST_HEAD_INITIALIZER(s_ota_ops_entries_head);
|
||||||
|
|
||||||
static uint32_t s_ota_ops_last_handle = 0;
|
static uint32_t s_ota_ops_last_handle = 0;
|
||||||
static ota_select s_ota_select[2];
|
|
||||||
|
|
||||||
const static char *TAG = "esp_ota_ops";
|
const static char *TAG = "esp_ota_ops";
|
||||||
|
|
||||||
@ -77,6 +68,53 @@ static bool is_ota_partition(const esp_partition_t *p)
|
|||||||
&& p->subtype < ESP_PARTITION_SUBTYPE_APP_OTA_MAX);
|
&& p->subtype < ESP_PARTITION_SUBTYPE_APP_OTA_MAX);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read otadata partition and fill array from two otadata structures.
|
||||||
|
// Also return pointer to otadata info partition.
|
||||||
|
static const esp_partition_t *read_otadata(esp_ota_select_entry_t *two_otadata)
|
||||||
|
{
|
||||||
|
const esp_partition_t *otadata_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_OTA, NULL);
|
||||||
|
|
||||||
|
if (otadata_partition == NULL) {
|
||||||
|
ESP_LOGE(TAG, "not found otadata");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
spi_flash_mmap_handle_t ota_data_map;
|
||||||
|
const void *result = NULL;
|
||||||
|
esp_err_t err = esp_partition_mmap(otadata_partition, 0, otadata_partition->size, SPI_FLASH_MMAP_DATA, &result, &ota_data_map);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "mmap otadata filed. Err=0x%8x", err);
|
||||||
|
return NULL;
|
||||||
|
} else {
|
||||||
|
memcpy(&two_otadata[0], result, sizeof(esp_ota_select_entry_t));
|
||||||
|
memcpy(&two_otadata[1], result + SPI_FLASH_SEC_SIZE, sizeof(esp_ota_select_entry_t));
|
||||||
|
spi_flash_munmap(ota_data_map);
|
||||||
|
}
|
||||||
|
return otadata_partition;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t image_validate(const esp_partition_t *partition, esp_image_load_mode_t load_mode)
|
||||||
|
{
|
||||||
|
esp_image_metadata_t data;
|
||||||
|
const esp_partition_pos_t part_pos = {
|
||||||
|
.offset = partition->address,
|
||||||
|
.size = partition->size,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (esp_image_verify(load_mode, &part_pos, &data) != ESP_OK) {
|
||||||
|
return ESP_ERR_OTA_VALIDATE_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_SECURE_SIGNED_ON_UPDATE
|
||||||
|
esp_err_t ret = esp_secure_boot_verify_signature(partition->address, data.image_len);
|
||||||
|
if (ret != ESP_OK) {
|
||||||
|
return ESP_ERR_OTA_VALIDATE_FAILED;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
esp_err_t esp_ota_begin(const esp_partition_t *partition, size_t image_size, esp_ota_handle_t *out_handle)
|
esp_err_t esp_ota_begin(const esp_partition_t *partition, size_t image_size, esp_ota_handle_t *out_handle)
|
||||||
{
|
{
|
||||||
ota_ops_entry_t *new_entry;
|
ota_ops_entry_t *new_entry;
|
||||||
@ -157,7 +195,7 @@ esp_err_t esp_ota_write(esp_ota_handle_t handle, const void *data, size_t size)
|
|||||||
|
|
||||||
/* check if we have partially written data from earlier */
|
/* check if we have partially written data from earlier */
|
||||||
if (it->partial_bytes != 0) {
|
if (it->partial_bytes != 0) {
|
||||||
copy_len = OTA_MIN(16 - it->partial_bytes, size);
|
copy_len = MIN(16 - it->partial_bytes, size);
|
||||||
memcpy(it->partial_data + it->partial_bytes, data_bytes, copy_len);
|
memcpy(it->partial_data + it->partial_bytes, data_bytes, copy_len);
|
||||||
it->partial_bytes += copy_len;
|
it->partial_bytes += copy_len;
|
||||||
if (it->partial_bytes != 16) {
|
if (it->partial_bytes != 16) {
|
||||||
@ -247,31 +285,19 @@ esp_err_t esp_ota_end(esp_ota_handle_t handle)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t ota_select_crc(const ota_select *s)
|
static esp_err_t rewrite_ota_seq(esp_ota_select_entry_t *two_otadata, uint32_t seq, uint8_t sec_id, const esp_partition_t *ota_data_partition)
|
||||||
{
|
{
|
||||||
return crc32_le(UINT32_MAX, (uint8_t *)&s->ota_seq, 4);
|
if (two_otadata == NULL || sec_id > 1) {
|
||||||
}
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
static bool ota_select_valid(const ota_select *s)
|
two_otadata[sec_id].ota_seq = seq;
|
||||||
{
|
two_otadata[sec_id].crc = bootloader_common_ota_select_crc(&two_otadata[sec_id]);
|
||||||
return s->ota_seq != UINT32_MAX && s->crc == ota_select_crc(s);
|
esp_err_t ret = esp_partition_erase_range(ota_data_partition, sec_id * SPI_FLASH_SEC_SIZE, SPI_FLASH_SEC_SIZE);
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t rewrite_ota_seq(uint32_t seq, uint8_t sec_id, const esp_partition_t *ota_data_partition)
|
|
||||||
{
|
|
||||||
esp_err_t ret;
|
|
||||||
|
|
||||||
if (sec_id == 0 || sec_id == 1) {
|
|
||||||
s_ota_select[sec_id].ota_seq = seq;
|
|
||||||
s_ota_select[sec_id].crc = ota_select_crc(&s_ota_select[sec_id]);
|
|
||||||
ret = esp_partition_erase_range(ota_data_partition, sec_id * SPI_FLASH_SEC_SIZE, SPI_FLASH_SEC_SIZE);
|
|
||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
return ret;
|
return ret;
|
||||||
} else {
|
} else {
|
||||||
return esp_partition_write(ota_data_partition, SPI_FLASH_SEC_SIZE * sec_id, &s_ota_select[sec_id].ota_seq, sizeof(ota_select));
|
return esp_partition_write(ota_data_partition, SPI_FLASH_SEC_SIZE * sec_id, &two_otadata[sec_id], sizeof(esp_ota_select_entry_t));
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return ESP_ERR_INVALID_ARG;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -287,119 +313,70 @@ static uint8_t get_ota_partition_count(void)
|
|||||||
|
|
||||||
static esp_err_t esp_rewrite_ota_data(esp_partition_subtype_t subtype)
|
static esp_err_t esp_rewrite_ota_data(esp_partition_subtype_t subtype)
|
||||||
{
|
{
|
||||||
esp_err_t ret;
|
esp_ota_select_entry_t otadata[2];
|
||||||
const esp_partition_t *find_partition = NULL;
|
const esp_partition_t *otadata_partition = read_otadata(otadata);
|
||||||
uint16_t ota_app_count = 0;
|
if (otadata_partition == NULL) {
|
||||||
uint32_t i = 0;
|
return ESP_ERR_NOT_FOUND;
|
||||||
uint32_t seq;
|
}
|
||||||
spi_flash_mmap_handle_t ota_data_map;
|
|
||||||
const void *result = NULL;
|
|
||||||
|
|
||||||
find_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_OTA, NULL);
|
int ota_app_count = get_ota_partition_count();
|
||||||
if (find_partition != NULL) {
|
|
||||||
ota_app_count = get_ota_partition_count();
|
|
||||||
//esp32_idf use two sector for store information about which partition is running
|
|
||||||
//it defined the two sector as ota data partition,two structure ota_select is saved in the two sector
|
|
||||||
//named data in first sector as s_ota_select[0], second sector data as s_ota_select[1]
|
|
||||||
//e.g.
|
|
||||||
//if s_ota_select[0].ota_seq == s_ota_select[1].ota_seq == 0xFFFFFFFF,means ota info partition is in init status
|
|
||||||
//so it will boot factory application(if there is),if there's no factory application,it will boot ota[0] application
|
|
||||||
//if s_ota_select[0].ota_seq != 0 and s_ota_select[1].ota_seq != 0,it will choose a max seq ,and get value of max_seq%max_ota_app_number
|
|
||||||
//and boot a subtype (mask 0x0F) value is (max_seq - 1)%max_ota_app_number,so if want switch to run ota[x],can use next formulas.
|
|
||||||
//for example, if s_ota_select[0].ota_seq = 4, s_ota_select[1].ota_seq = 5, and there are 8 ota application,
|
|
||||||
//current running is (5-1)%8 = 4,running ota[4],so if we want to switch to run ota[7],
|
|
||||||
//we should add s_ota_select[0].ota_seq (is 4) to 4 ,(8-1)%8=7,then it will boot ota[7]
|
|
||||||
//if A=(B - C)%D
|
|
||||||
//then B=(A + C)%D + D*n ,n= (0,1,2...)
|
|
||||||
//so current ota app sub type id is x , dest bin subtype is y,total ota app count is n
|
|
||||||
//seq will add (x + n*1 + 1 - seq)%n
|
|
||||||
if (SUB_TYPE_ID(subtype) >= ota_app_count) {
|
if (SUB_TYPE_ID(subtype) >= ota_app_count) {
|
||||||
return ESP_ERR_INVALID_ARG;
|
return ESP_ERR_INVALID_ARG;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = esp_partition_mmap(find_partition, 0, find_partition->size, SPI_FLASH_MMAP_DATA, &result, &ota_data_map);
|
//esp32_idf use two sector for store information about which partition is running
|
||||||
if (ret != ESP_OK) {
|
//it defined the two sector as ota data partition,two structure esp_ota_select_entry_t is saved in the two sector
|
||||||
result = NULL;
|
//named data in first sector as otadata[0], second sector data as otadata[1]
|
||||||
return ret;
|
//e.g.
|
||||||
} else {
|
//if otadata[0].ota_seq == otadata[1].ota_seq == 0xFFFFFFFF,means ota info partition is in init status
|
||||||
memcpy(&s_ota_select[0], result, sizeof(ota_select));
|
//so it will boot factory application(if there is),if there's no factory application,it will boot ota[0] application
|
||||||
memcpy(&s_ota_select[1], result + SPI_FLASH_SEC_SIZE, sizeof(ota_select));
|
//if otadata[0].ota_seq != 0 and otadata[1].ota_seq != 0,it will choose a max seq ,and get value of max_seq%max_ota_app_number
|
||||||
spi_flash_munmap(ota_data_map);
|
//and boot a subtype (mask 0x0F) value is (max_seq - 1)%max_ota_app_number,so if want switch to run ota[x],can use next formulas.
|
||||||
}
|
//for example, if otadata[0].ota_seq = 4, otadata[1].ota_seq = 5, and there are 8 ota application,
|
||||||
|
//current running is (5-1)%8 = 4,running ota[4],so if we want to switch to run ota[7],
|
||||||
|
//we should add otadata[0].ota_seq (is 4) to 4 ,(8-1)%8=7,then it will boot ota[7]
|
||||||
|
//if A=(B - C)%D
|
||||||
|
//then B=(A + C)%D + D*n ,n= (0,1,2...)
|
||||||
|
//so current ota app sub type id is x , dest bin subtype is y,total ota app count is n
|
||||||
|
//seq will add (x + n*1 + 1 - seq)%n
|
||||||
|
|
||||||
if (ota_select_valid(&s_ota_select[0]) && ota_select_valid(&s_ota_select[1])) {
|
int active_otadata = bootloader_common_get_active_otadata(otadata);
|
||||||
seq = OTA_MAX(s_ota_select[0].ota_seq, s_ota_select[1].ota_seq);
|
if (active_otadata != -1) {
|
||||||
|
uint32_t seq = otadata[active_otadata].ota_seq;
|
||||||
|
uint32_t i = 0;
|
||||||
while (seq > (SUB_TYPE_ID(subtype) + 1) % ota_app_count + i * ota_app_count) {
|
while (seq > (SUB_TYPE_ID(subtype) + 1) % ota_app_count + i * ota_app_count) {
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
int next_otadata = (~active_otadata)&1; // if 0 -> will be next 1. and if 1 -> will be next 0.
|
||||||
if (s_ota_select[0].ota_seq >= s_ota_select[1].ota_seq) {
|
return rewrite_ota_seq(otadata, (SUB_TYPE_ID(subtype) + 1) % ota_app_count + i * ota_app_count, next_otadata, otadata_partition);
|
||||||
return rewrite_ota_seq((SUB_TYPE_ID(subtype) + 1) % ota_app_count + i * ota_app_count, 1, find_partition);
|
|
||||||
} else {
|
|
||||||
return rewrite_ota_seq((SUB_TYPE_ID(subtype) + 1) % ota_app_count + i * ota_app_count, 0, find_partition);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (ota_select_valid(&s_ota_select[0])) {
|
|
||||||
while (s_ota_select[0].ota_seq > (SUB_TYPE_ID(subtype) + 1) % ota_app_count + i * ota_app_count) {
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
return rewrite_ota_seq((SUB_TYPE_ID(subtype) + 1) % ota_app_count + i * ota_app_count, 1, find_partition);
|
|
||||||
|
|
||||||
} else if (ota_select_valid(&s_ota_select[1])) {
|
|
||||||
while (s_ota_select[1].ota_seq > (SUB_TYPE_ID(subtype) + 1) % ota_app_count + i * ota_app_count) {
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
return rewrite_ota_seq((SUB_TYPE_ID(subtype) + 1) % ota_app_count + i * ota_app_count, 0, find_partition);
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
/* Both OTA slots are invalid, probably because unformatted... */
|
/* Both OTA slots are invalid, probably because unformatted... */
|
||||||
return rewrite_ota_seq(SUB_TYPE_ID(subtype) + 1, 0, find_partition);
|
int next_otadata = 0;
|
||||||
}
|
return rewrite_ota_seq(otadata, SUB_TYPE_ID(subtype) + 1, next_otadata, otadata_partition);
|
||||||
|
|
||||||
} else {
|
|
||||||
return ESP_ERR_NOT_FOUND;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t esp_ota_set_boot_partition(const esp_partition_t *partition)
|
esp_err_t esp_ota_set_boot_partition(const esp_partition_t *partition)
|
||||||
{
|
{
|
||||||
const esp_partition_t *find_partition = NULL;
|
|
||||||
if (partition == NULL) {
|
if (partition == NULL) {
|
||||||
return ESP_ERR_INVALID_ARG;
|
return ESP_ERR_INVALID_ARG;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_image_metadata_t data;
|
if (image_validate(partition, ESP_IMAGE_VERIFY) != ESP_OK) {
|
||||||
const esp_partition_pos_t part_pos = {
|
|
||||||
.offset = partition->address,
|
|
||||||
.size = partition->size,
|
|
||||||
};
|
|
||||||
if (esp_image_verify(ESP_IMAGE_VERIFY, &part_pos, &data) != ESP_OK) {
|
|
||||||
return ESP_ERR_OTA_VALIDATE_FAILED;
|
return ESP_ERR_OTA_VALIDATE_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_SECURE_SIGNED_ON_UPDATE
|
|
||||||
esp_err_t ret = esp_secure_boot_verify_signature(partition->address, data.image_len);
|
|
||||||
if (ret != ESP_OK) {
|
|
||||||
return ESP_ERR_OTA_VALIDATE_FAILED;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
// if set boot partition to factory bin ,just format ota info partition
|
// if set boot partition to factory bin ,just format ota info partition
|
||||||
if (partition->type == ESP_PARTITION_TYPE_APP) {
|
if (partition->type == ESP_PARTITION_TYPE_APP) {
|
||||||
if (partition->subtype == ESP_PARTITION_SUBTYPE_APP_FACTORY) {
|
if (partition->subtype == ESP_PARTITION_SUBTYPE_APP_FACTORY) {
|
||||||
find_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_OTA, NULL);
|
const esp_partition_t *find_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_OTA, NULL);
|
||||||
if (find_partition != NULL) {
|
if (find_partition != NULL) {
|
||||||
return esp_partition_erase_range(find_partition, 0, find_partition->size);
|
return esp_partition_erase_range(find_partition, 0, find_partition->size);
|
||||||
} else {
|
} else {
|
||||||
return ESP_ERR_NOT_FOUND;
|
return ESP_ERR_NOT_FOUND;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// try to find this partition in flash,if not find it ,return error
|
|
||||||
find_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_OTA, NULL);
|
|
||||||
if (find_partition != NULL) {
|
|
||||||
return esp_rewrite_ota_data(partition->subtype);
|
return esp_rewrite_ota_data(partition->subtype);
|
||||||
} else {
|
|
||||||
return ESP_ERR_NOT_FOUND;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return ESP_ERR_INVALID_ARG;
|
return ESP_ERR_INVALID_ARG;
|
||||||
@ -436,59 +413,31 @@ static const esp_partition_t *find_default_boot_partition(void)
|
|||||||
|
|
||||||
const esp_partition_t *esp_ota_get_boot_partition(void)
|
const esp_partition_t *esp_ota_get_boot_partition(void)
|
||||||
{
|
{
|
||||||
esp_err_t ret;
|
esp_ota_select_entry_t otadata[2];
|
||||||
const esp_partition_t *find_partition = NULL;
|
const esp_partition_t *otadata_partition = read_otadata(otadata);
|
||||||
spi_flash_mmap_handle_t ota_data_map;
|
if (otadata_partition == NULL) {
|
||||||
const void *result = NULL;
|
|
||||||
uint16_t ota_app_count = 0;
|
|
||||||
find_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_OTA, NULL);
|
|
||||||
|
|
||||||
if (find_partition == NULL) {
|
|
||||||
ESP_LOGE(TAG, "not found ota data");
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = esp_partition_mmap(find_partition, 0, find_partition->size, SPI_FLASH_MMAP_DATA, &result, &ota_data_map);
|
int ota_app_count = get_ota_partition_count();
|
||||||
if (ret != ESP_OK) {
|
|
||||||
spi_flash_munmap(ota_data_map);
|
|
||||||
ESP_LOGE(TAG, "mmap ota data filed");
|
|
||||||
return NULL;
|
|
||||||
} else {
|
|
||||||
memcpy(&s_ota_select[0], result, sizeof(ota_select));
|
|
||||||
memcpy(&s_ota_select[1], result + 0x1000, sizeof(ota_select));
|
|
||||||
spi_flash_munmap(ota_data_map);
|
|
||||||
}
|
|
||||||
ota_app_count = get_ota_partition_count();
|
|
||||||
|
|
||||||
ESP_LOGD(TAG, "found ota app max = %d", ota_app_count);
|
ESP_LOGD(TAG, "found ota app max = %d", ota_app_count);
|
||||||
|
|
||||||
if (s_ota_select[0].ota_seq == 0xFFFFFFFF && s_ota_select[1].ota_seq == 0xFFFFFFFF) {
|
if ((bootloader_common_ota_select_invalid(&otadata[0]) &&
|
||||||
ESP_LOGD(TAG, "finding factory app......");
|
bootloader_common_ota_select_invalid(&otadata[1])) ||
|
||||||
|
ota_app_count == 0) {
|
||||||
|
ESP_LOGD(TAG, "finding factory app...");
|
||||||
return find_default_boot_partition();
|
return find_default_boot_partition();
|
||||||
} else if (ota_select_valid(&s_ota_select[0]) && ota_select_valid(&s_ota_select[1])) {
|
} else {
|
||||||
ESP_LOGD(TAG, "finding ota_%d app......", \
|
int active_otadata = bootloader_common_get_active_otadata(otadata);
|
||||||
ESP_PARTITION_SUBTYPE_APP_OTA_MIN + ((OTA_MAX(s_ota_select[0].ota_seq, s_ota_select[1].ota_seq) - 1) % ota_app_count));
|
if (active_otadata != -1) {
|
||||||
|
int ota_slot = (otadata[active_otadata].ota_seq - 1) % ota_app_count; // Actual OTA partition selection
|
||||||
return esp_partition_find_first(ESP_PARTITION_TYPE_APP, \
|
ESP_LOGD(TAG, "finding ota_%d app...", ESP_PARTITION_SUBTYPE_APP_OTA_MIN + ota_slot);
|
||||||
ESP_PARTITION_SUBTYPE_APP_OTA_MIN + ((OTA_MAX(s_ota_select[0].ota_seq, s_ota_select[1].ota_seq) - 1) % ota_app_count), NULL);
|
return esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_OTA_MIN + ota_slot, NULL);
|
||||||
} else if (ota_select_valid(&s_ota_select[0])) {
|
|
||||||
ESP_LOGD(TAG, "finding ota_%d app......", \
|
|
||||||
ESP_PARTITION_SUBTYPE_APP_OTA_MIN + (s_ota_select[0].ota_seq - 1) % ota_app_count);
|
|
||||||
|
|
||||||
return esp_partition_find_first(ESP_PARTITION_TYPE_APP, \
|
|
||||||
ESP_PARTITION_SUBTYPE_APP_OTA_MIN + (s_ota_select[0].ota_seq - 1) % ota_app_count, NULL);
|
|
||||||
|
|
||||||
} else if (ota_select_valid(&s_ota_select[1])) {
|
|
||||||
ESP_LOGD(TAG, "finding ota_%d app......", \
|
|
||||||
ESP_PARTITION_SUBTYPE_APP_OTA_MIN + (s_ota_select[1].ota_seq - 1) % ota_app_count);
|
|
||||||
|
|
||||||
return esp_partition_find_first(ESP_PARTITION_TYPE_APP, \
|
|
||||||
ESP_PARTITION_SUBTYPE_APP_OTA_MIN + (s_ota_select[1].ota_seq - 1) % ota_app_count, NULL);
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGE(TAG, "ota data invalid, no current app. Assuming factory");
|
ESP_LOGE(TAG, "ota data invalid, no current app. Assuming factory");
|
||||||
return find_default_boot_partition();
|
return find_default_boot_partition();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -24,21 +24,29 @@ typedef enum {
|
|||||||
} esp_comm_gpio_hold_t;
|
} esp_comm_gpio_hold_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Calculate crc for the OTA data partition.
|
* @brief Calculate crc for the OTA data select.
|
||||||
*
|
*
|
||||||
* @param[in] ota_data The OTA data partition.
|
* @param[in] s The OTA data select.
|
||||||
* @return Returns crc value.
|
* @return Returns crc value.
|
||||||
*/
|
*/
|
||||||
uint32_t bootloader_common_ota_select_crc(const esp_ota_select_entry_t *s);
|
uint32_t bootloader_common_ota_select_crc(const esp_ota_select_entry_t *s);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Verifies the validity of the OTA data partition
|
* @brief Verifies the validity of the OTA data select
|
||||||
*
|
*
|
||||||
* @param[in] ota_data The OTA data partition.
|
* @param[in] s The OTA data select.
|
||||||
* @return Returns true on valid, false otherwise.
|
* @return Returns true on valid, false otherwise.
|
||||||
*/
|
*/
|
||||||
bool bootloader_common_ota_select_valid(const esp_ota_select_entry_t *s);
|
bool bootloader_common_ota_select_valid(const esp_ota_select_entry_t *s);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns true if OTADATA is not marked as bootable partition.
|
||||||
|
*
|
||||||
|
* @param[in] s The OTA data select.
|
||||||
|
* @return Returns true if OTADATA invalid, false otherwise.
|
||||||
|
*/
|
||||||
|
bool bootloader_common_ota_select_invalid(const esp_ota_select_entry_t *s);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Check if the GPIO input is a long hold or a short hold.
|
* @brief Check if the GPIO input is a long hold or a short hold.
|
||||||
*
|
*
|
||||||
@ -93,6 +101,16 @@ bool bootloader_common_label_search(const char *list, char *label);
|
|||||||
*/
|
*/
|
||||||
esp_err_t bootloader_common_get_sha256_of_partition(uint32_t address, uint32_t size, int type, uint8_t *out_sha_256);
|
esp_err_t bootloader_common_get_sha256_of_partition(uint32_t address, uint32_t size, int type, uint8_t *out_sha_256);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the number of active otadata.
|
||||||
|
*
|
||||||
|
* @param[in] two_otadata Pointer on array from two otadata structures.
|
||||||
|
*
|
||||||
|
* @return The number of active otadata (0 or 1).
|
||||||
|
* - -1: If it does not have active otadata.
|
||||||
|
*/
|
||||||
|
int bootloader_common_get_active_otadata(esp_ota_select_entry_t *two_otadata);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns esp_app_desc structure for app partition. This structure includes app version.
|
* @brief Returns esp_app_desc structure for app partition. This structure includes app version.
|
||||||
*
|
*
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#include "soc/gpio_periph.h"
|
#include "soc/gpio_periph.h"
|
||||||
#include "esp_image_format.h"
|
#include "esp_image_format.h"
|
||||||
#include "bootloader_sha.h"
|
#include "bootloader_sha.h"
|
||||||
|
#include "sys/param.h"
|
||||||
|
|
||||||
#define ESP_PARTITION_HASH_LEN 32 /* SHA-256 digest length */
|
#define ESP_PARTITION_HASH_LEN 32 /* SHA-256 digest length */
|
||||||
|
|
||||||
@ -39,9 +40,14 @@ uint32_t bootloader_common_ota_select_crc(const esp_ota_select_entry_t *s)
|
|||||||
return crc32_le(UINT32_MAX, (uint8_t*)&s->ota_seq, 4);
|
return crc32_le(UINT32_MAX, (uint8_t*)&s->ota_seq, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool bootloader_common_ota_select_invalid(const esp_ota_select_entry_t *s)
|
||||||
|
{
|
||||||
|
return s->ota_seq == UINT32_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
bool bootloader_common_ota_select_valid(const esp_ota_select_entry_t *s)
|
bool bootloader_common_ota_select_valid(const esp_ota_select_entry_t *s)
|
||||||
{
|
{
|
||||||
return s->ota_seq != UINT32_MAX && s->crc == bootloader_common_ota_select_crc(s);
|
return bootloader_common_ota_select_invalid(s) == false && s->crc == bootloader_common_ota_select_crc(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_comm_gpio_hold_t bootloader_common_check_long_hold_gpio(uint32_t num_pin, uint32_t delay_sec)
|
esp_comm_gpio_hold_t bootloader_common_check_long_hold_gpio(uint32_t num_pin, uint32_t delay_sec)
|
||||||
@ -193,6 +199,32 @@ esp_err_t bootloader_common_get_sha256_of_partition (uint32_t address, uint32_t
|
|||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int bootloader_common_get_active_otadata(esp_ota_select_entry_t *two_otadata)
|
||||||
|
{
|
||||||
|
int active_otadata = -1;
|
||||||
|
|
||||||
|
bool valid_otadata[2];
|
||||||
|
valid_otadata[0] = bootloader_common_ota_select_valid(&two_otadata[0]);
|
||||||
|
valid_otadata[1] = bootloader_common_ota_select_valid(&two_otadata[1]);
|
||||||
|
if (valid_otadata[0] && valid_otadata[1]) {
|
||||||
|
if (MAX(two_otadata[0].ota_seq, two_otadata[1].ota_seq) == two_otadata[0].ota_seq) {
|
||||||
|
active_otadata = 0;
|
||||||
|
} else {
|
||||||
|
active_otadata = 1;
|
||||||
|
}
|
||||||
|
ESP_LOGD(TAG, "Both OTA copies are valid");
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < 2; ++i) {
|
||||||
|
if (valid_otadata[i]) {
|
||||||
|
active_otadata = i;
|
||||||
|
ESP_LOGD(TAG, "Only otadata[%d] is valid", i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return active_otadata;
|
||||||
|
}
|
||||||
|
|
||||||
esp_err_t bootloader_common_get_partition_description(const esp_partition_pos_t *partition, esp_app_desc_t *app_desc)
|
esp_err_t bootloader_common_get_partition_description(const esp_partition_pos_t *partition, esp_app_desc_t *app_desc)
|
||||||
{
|
{
|
||||||
if (partition == NULL || app_desc == NULL || partition->offset == 0) {
|
if (partition == NULL || app_desc == NULL || partition->offset == 0) {
|
||||||
|
@ -67,6 +67,34 @@ static void set_cache_and_start_app(uint32_t drom_addr,
|
|||||||
uint32_t irom_size,
|
uint32_t irom_size,
|
||||||
uint32_t entry_addr);
|
uint32_t entry_addr);
|
||||||
|
|
||||||
|
// Read ota_info partition and fill array from two otadata structures.
|
||||||
|
static esp_err_t read_otadata(const esp_partition_pos_t *ota_info, esp_ota_select_entry_t *two_otadata)
|
||||||
|
{
|
||||||
|
const esp_ota_select_entry_t *ota_select_map;
|
||||||
|
if (ota_info->offset == 0) {
|
||||||
|
return ESP_ERR_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
// partition table has OTA data partition
|
||||||
|
if (ota_info->size < 2 * SPI_SEC_SIZE) {
|
||||||
|
ESP_LOGE(TAG, "ota_info partition size %d is too small (minimum %d bytes)", ota_info->size, sizeof(esp_ota_select_entry_t));
|
||||||
|
return ESP_FAIL; // can't proceed
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "OTA data offset 0x%x", ota_info->offset);
|
||||||
|
ota_select_map = bootloader_mmap(ota_info->offset, ota_info->size);
|
||||||
|
if (!ota_select_map) {
|
||||||
|
ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", ota_info->offset, ota_info->size);
|
||||||
|
return ESP_FAIL; // can't proceed
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(&two_otadata[0], ota_select_map, sizeof(esp_ota_select_entry_t));
|
||||||
|
memcpy(&two_otadata[1], (uint8_t *)ota_select_map + SPI_SEC_SIZE, sizeof(esp_ota_select_entry_t));
|
||||||
|
bootloader_munmap(ota_select_map);
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
bool bootloader_utility_load_partition_table(bootloader_state_t* bs)
|
bool bootloader_utility_load_partition_table(bootloader_state_t* bs)
|
||||||
{
|
{
|
||||||
const esp_partition_info_t *partitions;
|
const esp_partition_info_t *partitions;
|
||||||
@ -194,71 +222,48 @@ static void log_invalid_app_partition(int index)
|
|||||||
|
|
||||||
int bootloader_utility_get_selected_boot_partition(const bootloader_state_t *bs)
|
int bootloader_utility_get_selected_boot_partition(const bootloader_state_t *bs)
|
||||||
{
|
{
|
||||||
esp_ota_select_entry_t sa,sb;
|
esp_ota_select_entry_t otadata[2];
|
||||||
const esp_ota_select_entry_t *ota_select_map;
|
int boot_index = FACTORY_INDEX;
|
||||||
|
|
||||||
if (bs->ota_info.offset != 0) {
|
if (bs->ota_info.offset == 0) {
|
||||||
// partition table has OTA data partition
|
return FACTORY_INDEX;
|
||||||
if (bs->ota_info.size < 2 * SPI_SEC_SIZE) {
|
|
||||||
ESP_LOGE(TAG, "ota_info partition size %d is too small (minimum %d bytes)", bs->ota_info.size, sizeof(esp_ota_select_entry_t));
|
|
||||||
return INVALID_INDEX; // can't proceed
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ESP_LOGD(TAG, "OTA data offset 0x%x", bs->ota_info.offset);
|
if (read_otadata(&bs->ota_info, otadata) != ESP_OK) {
|
||||||
ota_select_map = bootloader_mmap(bs->ota_info.offset, bs->ota_info.size);
|
return INVALID_INDEX;
|
||||||
if (!ota_select_map) {
|
|
||||||
ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", bs->ota_info.offset, bs->ota_info.size);
|
|
||||||
return INVALID_INDEX; // can't proceed
|
|
||||||
}
|
}
|
||||||
memcpy(&sa, ota_select_map, sizeof(esp_ota_select_entry_t));
|
|
||||||
memcpy(&sb, (uint8_t *)ota_select_map + SPI_SEC_SIZE, sizeof(esp_ota_select_entry_t));
|
|
||||||
bootloader_munmap(ota_select_map);
|
|
||||||
|
|
||||||
ESP_LOGD(TAG, "OTA sequence values A 0x%08x B 0x%08x", sa.ota_seq, sb.ota_seq);
|
ESP_LOGD(TAG, "otadata[0]: sequence values 0x%08x", otadata[0].ota_seq);
|
||||||
if ((sa.ota_seq == UINT32_MAX && sb.ota_seq == UINT32_MAX) || (bs->app_count == 0)) {
|
ESP_LOGD(TAG, "otadata[1]: sequence values 0x%08x", otadata[1].ota_seq);
|
||||||
|
|
||||||
|
if ((bootloader_common_ota_select_invalid(&otadata[0]) &&
|
||||||
|
bootloader_common_ota_select_invalid(&otadata[1])) ||
|
||||||
|
bs->app_count == 0) {
|
||||||
ESP_LOGD(TAG, "OTA sequence numbers both empty (all-0xFF) or partition table does not have bootable ota_apps (app_count=%d)", bs->app_count);
|
ESP_LOGD(TAG, "OTA sequence numbers both empty (all-0xFF) or partition table does not have bootable ota_apps (app_count=%d)", bs->app_count);
|
||||||
if (bs->factory.offset != 0) {
|
if (bs->factory.offset != 0) {
|
||||||
ESP_LOGI(TAG, "Defaulting to factory image");
|
ESP_LOGI(TAG, "Defaulting to factory image");
|
||||||
return FACTORY_INDEX;
|
boot_index = FACTORY_INDEX;
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGI(TAG, "No factory image, trying OTA 0");
|
ESP_LOGI(TAG, "No factory image, trying OTA 0");
|
||||||
return 0;
|
boot_index = 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
bool ota_valid = false;
|
int active_otadata = bootloader_common_get_active_otadata(otadata);
|
||||||
const char *ota_msg;
|
if (active_otadata != -1) {
|
||||||
int ota_seq; // Raw OTA sequence number. May be more than # of OTA slots
|
ESP_LOGD(TAG, "Active OTADATA copy is #%d", active_otadata);
|
||||||
if(bootloader_common_ota_select_valid(&sa) && bootloader_common_ota_select_valid(&sb)) {
|
uint32_t ota_seq = otadata[active_otadata].ota_seq - 1; // Raw OTA sequence number. May be more than # of OTA slots
|
||||||
ota_valid = true;
|
boot_index = ota_seq % bs->app_count; // Actual OTA partition selection
|
||||||
ota_msg = "Both OTA values";
|
ESP_LOGD(TAG, "Mapping seq %d -> OTA slot %d", ota_seq, boot_index);
|
||||||
ota_seq = MAX(sa.ota_seq, sb.ota_seq) - 1;
|
|
||||||
} else if(bootloader_common_ota_select_valid(&sa)) {
|
|
||||||
ota_valid = true;
|
|
||||||
ota_msg = "Only OTA sequence A is";
|
|
||||||
ota_seq = sa.ota_seq - 1;
|
|
||||||
} else if(bootloader_common_ota_select_valid(&sb)) {
|
|
||||||
ota_valid = true;
|
|
||||||
ota_msg = "Only OTA sequence B is";
|
|
||||||
ota_seq = sb.ota_seq - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ota_valid) {
|
|
||||||
int ota_slot = ota_seq % bs->app_count; // Actual OTA partition selection
|
|
||||||
ESP_LOGD(TAG, "%s valid. Mapping seq %d -> OTA slot %d", ota_msg, ota_seq, ota_slot);
|
|
||||||
return ota_slot;
|
|
||||||
} else if (bs->factory.offset != 0) {
|
} else if (bs->factory.offset != 0) {
|
||||||
ESP_LOGE(TAG, "ota data partition invalid, falling back to factory");
|
ESP_LOGE(TAG, "ota data partition invalid, falling back to factory");
|
||||||
return FACTORY_INDEX;
|
boot_index = FACTORY_INDEX;
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGE(TAG, "ota data partition invalid and no factory, will try all partitions");
|
ESP_LOGE(TAG, "ota data partition invalid and no factory, will try all partitions");
|
||||||
return FACTORY_INDEX;
|
boot_index = FACTORY_INDEX;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// otherwise, start from factory app partition and let the search logic
|
return boot_index;
|
||||||
// proceed from there
|
|
||||||
return FACTORY_INDEX;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return true if a partition has a valid app image that was successfully loaded */
|
/* Return true if a partition has a valid app image that was successfully loaded */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user