feat(bootloader): Support v2 firmware updates to v3 by OTA

This commit is contained in:
Dong Heng
2018-11-09 19:54:17 +08:00
parent ef79175caf
commit 11db1b0daf
18 changed files with 752 additions and 15 deletions

View File

@ -539,13 +539,10 @@ const esp_partition_t* esp_ota_get_running_partition(void)
/* Find the flash address of this exact function. By definition that is part
of the currently running firmware. Then find the enclosing partition. */
#ifdef CONFIG_TARGET_PLATFORM_ESP32
size_t phys_offs = spi_flash_cache2phys(esp_ota_get_running_partition);
assert (phys_offs != SPI_FLASH_CACHE2PHYS_FAIL); /* indicates cache2phys lookup is buggy */
#endif
#if CONFIG_TARGET_PLATFORM_ESP32
esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_APP,
ESP_PARTITION_SUBTYPE_ANY,
NULL);
@ -561,9 +558,6 @@ const esp_partition_t* esp_ota_get_running_partition(void)
}
abort(); /* Partition table is invalid or corrupt */
#else
return esp_ota_get_boot_partition();
#endif
}

View File

@ -21,6 +21,7 @@
#include "bootloader_common.h"
#include "esp_image_format.h"
#include "esp_log.h"
#include "esp_spi_flash.h"
static const char* TAG = "boot";
@ -112,6 +113,18 @@ static int selected_boot_partition(const bootloader_state_t *bs)
}
#endif
}
#endif
#ifdef CONFIG_ESP8266_BOOT_COPY_APP
if (boot_index == 1) {
ESP_LOGI(TAG, "Copy application from OAT1 to OTA0, please wait ...");
int ret = esp_patition_copy_ota1_to_ota0(bs);
if (ret) {
ESP_LOGE(TAG, "Fail to initialize OTA0");
return INVALID_INDEX;
}
boot_index = 0;
}
#endif
// Customer implementation.
// if (gpio_pin_1 == true && ...){

View File

@ -12,8 +12,8 @@ MEMORY
{
dport0_seg : org = 0x3FF00000, len = 0x10
/* All .data/.bss/heap are in this segment. */
dram_seg : org = 0x3FFE8000, len = 0x18000
/* All .data/.bss/heap are in this segment. Reserve 1KB for old boot or ROM boot */
dram_seg : org = 0x3FFE8000, len = 0x18000 - 0x400
/* Functions which are critical should be put in this segment. */
iram_seg : org = 0x40100000, len = 0x8000

View File

@ -1,6 +1,8 @@
PROVIDE ( ets_memcpy = 0x400018b4 );
PROVIDE ( SPIRead = 0x40004b1c );
PROVIDE ( SPIWrite = 0x40004a4c );
PROVIDE ( SPIEraseSector = 0x40004a00 );
PROVIDE ( gpio_input_get = 0x40004cf0 );

View File

@ -648,6 +648,8 @@ static void update_flash_config(const esp_image_header_t* pfhdr)
ESP_LOGD(TAG, "bootloader initialize SPI flash clock and I/O");
#endif /* CONFIG_BOOTLOADER_INIT_SPI_FLASH */
Cache_Read_Disable();
}
static void print_flash_info(const esp_image_header_t* phdr)

View File

@ -512,6 +512,13 @@ bool bootloader_utility_load_partition_table(bootloader_state_t* bs)
esp_err_t err;
int num_partitions;
#ifdef CONFIG_ESP8266_OTA_FROM_OLD
if (esp_patition_table_init_location()) {
ESP_LOGE(TAG, "Failed to update partition table location");
return false;
}
#endif
#ifdef CONFIG_SECURE_BOOT_ENABLED
if(esp_secure_boot_enabled()) {
ESP_LOGI(TAG, "Verifying partition table signature...");
@ -605,6 +612,14 @@ bool bootloader_utility_load_partition_table(bootloader_state_t* bs)
bootloader_munmap(partitions);
#ifdef CONFIG_ESP8266_OTA_FROM_OLD
ESP_LOGI(TAG, "Copy firmware ...");
if (esp_patition_table_init_data(bs)) {
ESP_LOGE(TAG,"Failed to update partition data");
return false;
}
#endif
ESP_LOGI(TAG,"End of partition table");
return true;
}

View File

@ -213,6 +213,28 @@ config CRYSTAL_USED_40MHZ
bool "40MHz"
endchoice
config ESP8266_OTA_FROM_OLD
bool "(**Expected**)ESP8266 update from old SDK by OTA"
default n
depends on TARGET_PLATFORM_ESP8266
select ESP8266_BOOT_COPY_APP
help
The function is not released.
Enable this option, script will generate the complete firmware for both old RTOS SDK(before V3.0)
and NonOS SDK to update to v3 by OTA.
The old RTOS SDK(before V3.0) or NonOS SDK can download the firmware to its partition and run it as it self's application.
config ESP8266_BOOT_COPY_APP
bool "(**Expected**)Boot copy app"
default n
help
The function is not released.
Enable this option, when it is that "OTA1" application is to run after update by OTA,
bootloader will copy "OTA1" application to "OTA0" partition and run "OTA0".
endmenu
menu WIFI

View File

@ -89,10 +89,22 @@ OTA_BIN := ./build/$(PROJECT_NAME).ota.bin
OTA1_BIN := ./build/$(PROJECT_NAME).app1.bin
OTA2_BIN := ./build/$(PROJECT_NAME).app2.bin
OTA_V2_TO_V3_BIN := ./build/$(PROJECT_NAME).v2_to_v3.ota.bin
CONFIG_APP2_OFFSET ?= $(CONFIG_APP1_OFFSET)
CONFIG_APP2_SIZE ?= $(CONFIG_APP1_SIZE)
OTA1_OFFSET := CONFIG_APP1_OFFSET
ifdef CONFIG_ESP8266_BOOT_COPY_APP
OTA2_LINK_OFFSET := $(CONFIG_APP1_OFFSET)
else
OTA2_LINK_OFFSET := $(CONFIG_APP2_OFFSET)
endif
$(OTA2_BIN): all_binaries
ifeq ($(CONFIG_ESPTOOLPY_FLASHSIZE), "1MB")
@rm -f ./build/esp8266/esp8266_out.ld
@make APP_OFFSET=$(CONFIG_APP2_OFFSET) APP_SIZE=$(CONFIG_APP2_SIZE) CFLAGS= CXXFLAGS=
@make APP_OFFSET=$(OTA2_LINK_OFFSET) APP_SIZE=$(CONFIG_APP2_SIZE) CFLAGS= CXXFLAGS=
endif
@cp $(RAW_BIN) $(OTA2_BIN)
@echo [GEN] $(OTA2_BIN)
@ -113,9 +125,22 @@ endif
@cp $(OTA1_BIN) $(RAW_BIN)
@echo [GEN] $(OTA_BIN)
ifdef CONFIG_ESP8266_OTA_FROM_OLD
$(OTA_V2_TO_V3_BIN): $(OTA_BIN)
@cp $(RAW_BIN) $(RAW_BIN).tmp.bak
@cp $(OTA1_BIN) $(RAW_BIN)
@python $(IDF_PATH)/tools/pack_fw.py --output $(OTA_V2_TO_V3_BIN) pack3 $(ESPTOOL_ALL_FLASH_ARGS)
@cp $(RAW_BIN).tmp.bak $(RAW_BIN)
@echo [GEN] $(OTA_V2_TO_V3_BIN)
endif
ifdef CONFIG_ESP8266_OTA_FROM_OLD
ota: $(OTA_V2_TO_V3_BIN)
else
ota: $(OTA_BIN)
endif
ota-clean:
@rm -f $(OTA_BIN) $(OTA1_BIN) $(OTA2_BIN)
@rm -f $(OTA_BIN) $(OTA1_BIN) $(OTA2_BIN) $(OTA_V2_TO_V3_BIN)
clean: ota-clean

View File

@ -140,6 +140,12 @@
#define WDT_CTL_EN_LSB 0
#define WDT_FEED_VALUE 0x73
#define WDT_REG_READ(_reg) REG_READ(PERIPHS_WDT_BASEADDR + _reg)
#define WDT_REG_WRITE(_reg, _val) REG_WRITE(PERIPHS_WDT_BASEADDR + _reg, _val)
#define CLEAR_WDT_REG_MASK(_reg, _mask) WDT_REG_WRITE(_reg, WDT_REG_READ(_reg) & (~_mask))
#define WDT_FEED() WDT_REG_WRITE(WDT_RST_ADDRESS, WDT_FEED_VALUE)
//}}
//RTC reg {{

View File

@ -33,6 +33,10 @@ int SPI_write_status(esp_spi_flash_chip_t *chip, uint32_t status);
int SPI_read_status(esp_spi_flash_chip_t *chip, uint32_t *status);
int Enable_QMode(esp_spi_flash_chip_t *chip);
int SPIWrite(uint32_t addr, const uint8_t *src, uint32_t size);
int SPIRead(uint32_t addr, void *dst, uint32_t size);
int SPIEraseSector(uint32_t sector_num);
void Cache_Read_Disable();
void Cache_Read_Enable(uint8_t map, uint8_t p, uint8_t v);

View File

@ -263,6 +263,14 @@ extern "C" {
#define PERIPHS_SPI_FLASH_USRREG (0x60000200 + 0x1c)
#define CACHE_MAP_1M_HIGH BIT25
#define CACHE_MAP_2M BIT24
#define CACHE_MAP_SEGMENT_S 16
#define CACHE_MAP_SEGMENT_MASK 0x3
#define CACHE_BASE_ADDR 0x40200000
#define CACHE_2M_SIZE 0x00200000
#define CACHE_1M_SIZE 0x00100000
#ifdef __cplusplus
}
#endif

View File

@ -21,11 +21,6 @@
#include "portmacro.h"
#include "esp8266/eagle_soc.h"
#define WDT_REG_READ(_reg) REG_READ(PERIPHS_WDT_BASEADDR + _reg)
#define WDT_REG_WRITE(_reg, _val) REG_WRITE(PERIPHS_WDT_BASEADDR + _reg, _val)
#define CLEAR_WDT_REG_MASK(_reg, _mask) WDT_REG_WRITE(_reg, WDT_REG_READ(_reg) & (~_mask))
#define WDT_FEED() WDT_REG_WRITE(WDT_RST_ADDRESS, WDT_FEED_VALUE)
static const char *TAG = "wdt";
#ifdef CONFIG_TASK_WDT_PANIC

View File

@ -9,3 +9,10 @@ COMPONENT_OBJS := src/spi_flash.o src/spi_flash_raw.o
endif
CFLAGS += -DPARTITION_QUEUE_HEADER=\"sys/queue.h\"
ifdef CONFIG_ESP8266_OTA_FROM_OLD
ifdef IS_BOOTLOADER_BUILD
COMPONENT_SRCDIRS += port
COMPONENT_OBJS += port/port.o
endif
endif

View File

@ -32,6 +32,8 @@ extern "C" {
#define SPI_READ_BUF_MAX 64
#define SPI_FLASH_CACHE2PHYS_FAIL UINT32_MAX /*<! Result from spi_flash_cache2phys() if flash cache address is invalid */
#ifdef CONFIG_ENABLE_FLASH_MMAP
/**
* @brief Enumeration which specifies memory space requested in an mmap call
@ -149,6 +151,50 @@ void spi_flash_munmap(spi_flash_mmap_handle_t handle);
#endif /* CONFIG_ENABLE_FLASH_MMAP */
/**
* @brief Given a memory address where flash is mapped, return the corresponding physical flash offset.
*
* Cache address does not have have been assigned via spi_flash_mmap(), any address in memory mapped flash space can be looked up.
*
* @param cached Pointer to flashed cached memory.
*
* @return
* - SPI_FLASH_CACHE2PHYS_FAIL If cache address is outside flash cache region, or the address is not mapped.
* - Otherwise, returns physical offset in flash
*/
uintptr_t spi_flash_cache2phys(const void *cached);
#ifdef CONFIG_ESP8266_OTA_FROM_OLD
/**
* @brief Check if current firmware updates from V2 firmware and its location is at "APP2", if so, then V3 bootloader
* will copy patition table from "APP2" location to "APP1" location of V2 partition map.
*
* @return 0 if success or others if failed
*/
int esp_patition_table_init_location(void);
/**
* @brief Check if current firmware updates from V2 firmware and its location is at "APP2", if so, then V3 bootloader
* will copy firmware from "APP2" location to "APP1" location.
*
* @note All data which is copied is "ota0" application and all data whose location is before "ota0".
*
* @return 0 if success or others if failed
*/
int esp_patition_table_init_data(void *partition_info);
#endif
#ifdef CONFIG_ESP8266_BOOT_COPY_APP
/**
* @brief Check if current application which is to run is at "ota1" location, if so, bootloader will copy it to "ota0" location,
* and clear OTA data partition.
*
* @return 0 if success or others if failed
*/
int esp_patition_copy_ota1_to_ota0(const void *partition_info);
#endif
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,385 @@
// Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "sdkconfig.h"
#if defined(CONFIG_ESP8266_OTA_FROM_OLD) && defined(BOOTLOADER_BUILD)
#include <string.h>
#include <stdint.h>
#include "esp_flash_data_types.h"
#include "esp_spi_flash.h"
#include "esp_log.h"
#include "esp_image_format.h"
#include "bootloader_config.h"
#include "esp_libc.h"
#include "esp8266/rom_functions.h"
#include "esp8266/eagle_soc.h"
#define PARTITION_DATA_OFFSET (CONFIG_SPI_FLASH_SIZE / 2)
typedef struct s_sys_param {
uint8_t flag;
uint8_t reserved1[3];
uint32_t reserved2[7];
} sys_param_t;
typedef union s_boot_param {
struct {
uint8_t usr_bin : 4;
uint8_t flag : 4;
} boot_1;
struct {
uint8_t usr_bin : 4;
uint8_t flag : 4;
uint8_t version;
} boot_2;
struct {
uint8_t usr_bin : 2;
uint8_t boot_statue : 1;
uint8_t to_qio : 1;
uint8_t reserved1 : 4;
uint8_t version : 5;
uint8_t test_pass_flag : 1;
uint8_t test_start_flag : 1;
uint8_t enhance_boot_flag : 1;
} boot_base;
uint8_t data[4096];
} boot_param_t;
static const char *TAG = "partition_port";
static uint32_t s_partition_offset;
static uint8_t s_cache_buf[SPI_FLASH_SEC_SIZE];
static sys_param_t s_sys_param;
static boot_param_t s_boot_param;
static esp_spi_flash_chip_t s_flash_chip = {
0x1640ef,
CONFIG_SPI_FLASH_SIZE,
64 * 1024,
4 * 1024,
256,
0xffff
};
static inline void esp_hw_reset(void)
{
CLEAR_WDT_REG_MASK(WDT_CTL_ADDRESS, BIT0);
WDT_REG_WRITE(WDT_OP_ADDRESS, 8);
WDT_REG_WRITE(WDT_OP_ND_ADDRESS, 8);
SET_PERI_REG_BITS(PERIPHS_WDT_BASEADDR + WDT_CTL_ADDRESS, WDT_CTL_RSTLEN_MASK, 7 << WDT_CTL_RSTLEN_LSB, 0);
// interrupt then reset
SET_PERI_REG_BITS(PERIPHS_WDT_BASEADDR + WDT_CTL_ADDRESS, WDT_CTL_RSPMOD_MASK, 0 << WDT_CTL_RSPMOD_LSB, 0);
// start task watch dog1
SET_PERI_REG_BITS(PERIPHS_WDT_BASEADDR + WDT_CTL_ADDRESS, WDT_CTL_EN_MASK, 1 << WDT_CTL_EN_LSB, 0);
while (1);
}
static inline int spi_flash_read_data(uint32_t addr, void *buf, size_t n)
{
int ret;
ESP_LOGD(TAG, "read buffer %p total %d from 0x%x", buf, n ,addr);
ret = SPI_read_data(&s_flash_chip, addr, buf, n);
return ret;
}
static inline int spi_flash_write_data(uint32_t addr, const void *buf, uint32_t n)
{
int ret;
ESP_LOGD(TAG, "write buffer %p total %d to 0x%x", buf, n ,addr);
ret = SPIWrite(addr, (void *)buf, n);
return ret;
}
static inline int spi_flash_erase(uint32_t addr)
{
int ret;
ESP_LOGD(TAG, "erase addr is 0x%x", addr);
ret = SPIEraseSector(addr / SPI_FLASH_SEC_SIZE);
return ret;
}
static inline uint32_t esp_get_updated_partition_table_addr(void)
{
int ret;
size_t offset;
uint8_t user_bin;
const uint32_t sect = CONFIG_SPI_FLASH_SIZE / SPI_FLASH_SEC_SIZE - 3;
if (s_partition_offset)
return s_partition_offset;
ret = spi_flash_read_data((sect + 2) * SPI_FLASH_SEC_SIZE, &s_sys_param, sizeof(sys_param_t));
if (ret) {
ESP_LOGE(TAG, "read V2 system param error %d", ret);
return -1UL;
}
ESP_LOGD(TAG, "V2 system flag is %x", s_sys_param.flag);
offset = s_sys_param.flag ? 1 : 0;
ret = spi_flash_read_data((sect + offset) * SPI_FLASH_SEC_SIZE, &s_boot_param, sizeof(boot_param_t));
if (ret) {
ESP_LOGE(TAG, "read V2 boot param error %d", ret);
return -1UL;
}
if (s_boot_param.boot_base.usr_bin == 1) {
if (s_boot_param.boot_base.boot_statue == 1)
user_bin = 1;
else
user_bin = 0;
} else {
if (s_boot_param.boot_base.boot_statue == 1)
user_bin = 0;
else {
if (s_boot_param.boot_base.version == 4)
user_bin = 0;
else
user_bin = 1;
}
}
if (user_bin)
s_partition_offset = CONFIG_PARTITION_TABLE_OFFSET + PARTITION_DATA_OFFSET;
else
s_partition_offset = CONFIG_PARTITION_TABLE_OFFSET;
ESP_LOGD(TAG, "Boot info %x %x %x %x %x", s_boot_param.boot_base.usr_bin, s_boot_param.boot_base.boot_statue,
s_boot_param.boot_base.version, user_bin, s_partition_offset);
return s_partition_offset;
}
static inline int spi_flash_write_data_safe(uint32_t addr, const void *buf, size_t n)
{
int ret;
static uint8_t check_buf[SPI_FLASH_SEC_SIZE];
ret = spi_flash_erase(addr);
if (ret) {
ESP_LOGE(TAG, "erase flash 0x%x error", addr);
return -1;
}
ret = spi_flash_write_data(addr, buf, n);
if (ret) {
ESP_LOGE(TAG, "write flash %d bytes to 0x%x error", n, addr);
return 0;
}
ret = spi_flash_read_data(addr, check_buf, n);
if (ret) {
ESP_LOGE(TAG, "read flash %d bytes from 0x%x error", n, addr);
return -1;
}
if (memcmp(buf, check_buf, n)) {
ESP_LOGE(TAG, "check write flash %d bytes to 0x%x error", n, addr);
return -1;
}
return 0;
}
static int esp_flash_sector_copy(uint32_t dest, uint32_t src, uint32_t total_size)
{
ESP_LOGD(TAG, "Start to copy data from 0x%x to 0x%x total %d", src, dest, total_size);
for (uint32_t offset = 0; offset < total_size; offset += SPI_FLASH_SEC_SIZE) {
int ret;
ret = spi_flash_read_data(src + offset, s_cache_buf, SPI_FLASH_SEC_SIZE);
if (ret) {
ESP_LOGE(TAG, "read flash %d bytes from 0x%x error", SPI_FLASH_SEC_SIZE, src + offset);
return -1;
}
ret = spi_flash_write_data_safe(dest + offset, s_cache_buf, SPI_FLASH_SEC_SIZE);
if (ret) {
ESP_LOGE(TAG, "write flash %d bytes to 0x%x error", SPI_FLASH_SEC_SIZE, dest + offset);
return -1;
}
}
return 0;
}
static inline int esp_set_v2boot_app1(void)
{
int ret;
size_t offset = s_sys_param.flag ? 1 : 0;
const uint32_t base_addr = CONFIG_SPI_FLASH_SIZE / SPI_FLASH_SEC_SIZE - 3;
const uint32_t sys_addr = (base_addr + 2) * SPI_FLASH_SEC_SIZE;
const uint32_t to_addr = (base_addr + 1 - offset) * SPI_FLASH_SEC_SIZE;
if (s_boot_param.boot_base.version == 0x2
|| s_boot_param.boot_base.version == 0x1f) {
if (s_boot_param.boot_base.usr_bin == 1)
s_boot_param.boot_base.usr_bin = 0;
else
s_boot_param.boot_base.usr_bin = 1;
} else {
s_boot_param.boot_base.enhance_boot_flag = 1;
if (s_boot_param.boot_base.boot_statue != 0) {
if (s_boot_param.boot_base.usr_bin == 1)
s_boot_param.boot_base.usr_bin = 0;
else
s_boot_param.boot_base.usr_bin = 1;
}
s_boot_param.boot_base.boot_statue = 1;
}
ESP_LOGD(TAG, "Boot info %x %x %x", s_boot_param.boot_base.usr_bin, s_boot_param.boot_base.boot_statue, s_boot_param.boot_base.version);
ret = spi_flash_write_data_safe(to_addr, &s_boot_param, sizeof(boot_param_t));
if (ret) {
ESP_LOGE(TAG, "write flash %d bytes to 0x%x error", sizeof(boot_param_t), to_addr);
return -1;
}
if (s_sys_param.flag)
s_sys_param.flag = 0;
else
s_sys_param.flag = 1;
ret = spi_flash_write_data_safe(sys_addr, &s_sys_param, sizeof(sys_param_t));
if (ret) {
ESP_LOGE(TAG, "write flash %d bytes to 0x%x error", SPI_FLASH_SEC_SIZE, sys_addr);
return -1;
}
return 0;
}
static inline int esp_sdk_update_from_v2(void)
{
const int segment_cnt = 3;
const size_t v2_max_size = 4096;
uint32_t segment_base = sizeof(esp_image_header_t);
uint32_t segment_size = 0;
if (s_partition_offset)
return 1;
for (int i = 0 ; i < segment_cnt; i++) {
int ret;
esp_image_segment_header_t segment;
ret = spi_flash_read_data(segment_base, &segment, sizeof(esp_image_segment_header_t));
if (ret) {
ESP_LOGE(TAG, "%d read segment @0x%x is %d", i, segment_base, ret);
return -1UL;
}
ESP_LOGD(TAG, "data is %x len is %d", segment.load_addr, segment.data_len);
segment_size += segment.data_len;
segment_base += sizeof(esp_image_segment_header_t) + segment.data_len;
}
ESP_LOGD(TAG, "boot total segment size is %u", segment_size);
return segment_size <= v2_max_size;
}
int esp_patition_table_init_location(void)
{
uint32_t addr;
if (!esp_sdk_update_from_v2())
return 0;
addr = esp_get_updated_partition_table_addr();
if (addr == CONFIG_PARTITION_TABLE_OFFSET)
return 0;
return esp_flash_sector_copy(CONFIG_PARTITION_TABLE_OFFSET, addr, SPI_FLASH_SEC_SIZE);
}
int esp_patition_copy_ota1_to_ota0(const void *partition_info)
{
int ret;
bootloader_state_t *bs = (bootloader_state_t *)partition_info;
ret = esp_flash_sector_copy(bs->ota[0].offset, bs->ota[1].offset, bs->ota[1].size);
if (ret) {
ESP_LOGE(TAG, "Fail to copy OTA from 0x%x to 0x%x total %d", bs->ota[1].offset,
bs->ota[0].offset, bs->ota[1].size);
return -1;
}
for (uint32_t offset = 0; offset < bs->ota_info.size; offset += SPI_FLASH_SEC_SIZE) {
ret = spi_flash_erase(bs->ota_info.offset + offset);
if (ret) {
ESP_LOGE(TAG, "Fail to erase OTA data from 0x%x", bs->ota_info.offset + offset);
return -1;
}
}
return 0;
}
int esp_patition_table_init_data(void *partition_info)
{
int ret;
const uint32_t boot_base = 0x1000;
const uint32_t boot_size = CONFIG_SPI_FLASH_SIZE / 2 - boot_base - 4 * SPI_FLASH_SEC_SIZE;
if (!esp_sdk_update_from_v2())
return 0;
if (esp_get_updated_partition_table_addr() == CONFIG_PARTITION_TABLE_OFFSET)
return 0;
ESP_LOGD(TAG, "Copy firmware1 from %d total %d", boot_base + PARTITION_DATA_OFFSET, boot_size);
ret = esp_flash_sector_copy(boot_base, boot_base + PARTITION_DATA_OFFSET, boot_size);
if (ret) {
ESP_LOGE(TAG, "Fail to copy V3 bootloader from 0x%x to 0x%x total %d", boot_base + PARTITION_DATA_OFFSET,
boot_base, boot_size);
return -1;
}
ret = esp_set_v2boot_app1();
if (ret) {
ESP_LOGE(TAG, "Fail to update V2 bootloader data");
return -1;
}
esp_hw_reset();
return 0;
}
#endif /* CONFIG_ESP8266_OTA_FROM_OLD */

View File

@ -751,3 +751,29 @@ void esp_spi_flash_init(uint32_t spi_speed, uint32_t spi_mode)
ESP_EARLY_LOGI("qio_mode", "Enabling default flash chip QIO");
}
}
uintptr_t spi_flash_cache2phys(const void *cached)
{
uint32_t map_size;
uintptr_t addr_offset;
uintptr_t addr = (uintptr_t)cached;
const uint32_t reg = REG_READ(CACHE_FLASH_CTRL_REG);
const uint32_t segment = (reg >> CACHE_MAP_SEGMENT_S) & CACHE_MAP_SEGMENT_MASK;
if (reg & CACHE_MAP_2M) {
map_size = CACHE_2M_SIZE;
addr_offset = 0;
} else {
map_size = CACHE_1M_SIZE;
if (reg & CACHE_MAP_1M_HIGH)
addr_offset = CACHE_1M_SIZE;
else
addr_offset = 0;
}
if (addr <= CACHE_BASE_ADDR || addr >= CACHE_BASE_ADDR + map_size)
return SPI_FLASH_CACHE2PHYS_FAIL;
return segment * CACHE_2M_SIZE + (addr + addr_offset - CACHE_BASE_ADDR);
}

View File

@ -0,0 +1,11 @@
# Name, Type, SubType, Offset, Size, Flags
# Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild
# Bootloader is at 0x1000 - 0x8000, total 28KB
# Partition table is at 0x8000 - 0x9000, total 4KB
# Reserve 4 sectors at the end flash address for V2 updating to V3 and system can read V2 parameters.
# If you don't need read v2 parameters, don't reserve the 4 sectors.
nvs, data, nvs, 0x9000, 0x4000
otadata, data, ota, 0xd000, 0x2000
phy_init, data, phy, 0xf000, 0x1000
ota_0, 0, ota_0, 0x10000, 0x6C000
ota_1, 0, ota_1, 0x90000, 0x6C000
1 # Name, Type, SubType, Offset, Size, Flags
2 # Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild
3 # Bootloader is at 0x1000 - 0x8000, total 28KB
4 # Partition table is at 0x8000 - 0x9000, total 4KB
5 # Reserve 4 sectors at the end flash address for V2 updating to V3 and system can read V2 parameters.
6 # If you don't need read v2 parameters, don't reserve the 4 sectors.
7 nvs, data, nvs, 0x9000, 0x4000
8 otadata, data, ota, 0xd000, 0x2000
9 phy_init, data, phy, 0xf000, 0x1000
10 ota_0, 0, ota_0, 0x10000, 0x6C000
11 ota_1, 0, ota_1, 0x90000, 0x6C000

176
tools/pack_fw.py Normal file
View File

@ -0,0 +1,176 @@
#!/usr/bin/env python2
#
# Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import argparse
import inspect
import sys
import binascii
import struct
__version__ = "1.0.1"
FLASH_SECTOR_SIZE = 0x1000
PYTHON2 = sys.version_info[0] < 3
def esp8266_crc32(data):
"""
CRC32 algorithm used by 8266 SDK bootloader (and gen_appbin.py).
"""
crc = binascii.crc32(data, 0) & 0xFFFFFFFF
if crc & 0x80000000:
return crc ^ 0xFFFFFFFF
else:
return crc + 1
def version(args):
print(__version__)
# python pack_fw.py addr1 bin1 addr2 bin2 ......
# The address must increase.
class proc_addr_file(argparse.Action):
""" Custom parser class for the address/filename pairs passed as arguments """
def __init__(self, option_strings, dest, nargs='+', **kwargs):
super(proc_addr_file, self).__init__(option_strings, dest, nargs, **kwargs)
def __call__(self, parser, namespace, values, option_string=None):
pairs = []
for i in range(0, len(values) ,2):
try:
address = int(values[i], 0)
except ValueError:
raise argparse.ArgumentError(self, 'Address "%s" must be a number' % values[i])
try:
argfile = open(values[i + 1], 'rb')
except IOError as e:
raise argparse.ArgumentError(self, e)
pairs.append((address, argfile))
end = 0
pairs = sorted(pairs)
for address, argfile in pairs:
argfile.seek(0,2) # seek to end
size = argfile.tell()
argfile.seek(0)
sector_start = address & ~(FLASH_SECTOR_SIZE - 1)
sector_end = ((address + size + FLASH_SECTOR_SIZE - 1) & ~(FLASH_SECTOR_SIZE - 1)) - 1
if sector_start < end:
message = 'Detected overlap at address: 0x%x for file: %s' % (address, argfile.name)
raise argparse.ArgumentError(self, message)
end = sector_end
setattr(namespace, self.dest, pairs)
def pack3(args):
fw_data = ''
try:
output_file = open(args.output, "w+")
except IOError as e:
print(e)
end_addr = None
for address, argfile in args.addr_filename:
if address == 0:
address = 4096
if end_addr is not None and address > end_addr:
data = (address - end_addr) * ['ff']
filled = binascii.a2b_hex(''.join(data))
fw_data += filled
try:
argfile.seek(0, 0)
data = argfile.read()
fw_data += data
argfile.seek(0, 2)
end_addr = address + argfile.tell()
except IOError as e:
raise e
crc32 = esp8266_crc32(fw_data)
fw_data += struct.pack('<I', crc32)
try:
output_file.write(fw_data)
output_file.close()
except IOError as e:
raise e
def main():
parser = argparse.ArgumentParser(description='pack_fw v%s - ESP8266 ROM Bootloader Utility' % __version__, prog='pack_fw')
parser.add_argument(
'--output', '-o',
help='Output file name with full path',
default=None)
subparsers = parser.add_subparsers(
dest='operation',
help='Run pack_fw {command} -h for additional help')
parser_pack_fw = subparsers.add_parser(
'pack3',
help='Pack the V3 firmware')
parser_pack_fw.add_argument('addr_filename', metavar='<address> <filename>', help='Address followed by binary filename, separated by space',
action=proc_addr_file)
print('pack_fw.py v%s' % __version__)
args = parser.parse_args()
if args.operation is None:
parser.print_help()
sys.exit(1)
operation_func = globals()[args.operation]
if PYTHON2:
# This function is depreciated in Python3
operation_args = inspect.getargspec(operation_func).args
else:
operation_args = inspect.getfullargspec(operation_func).args
operation_func(args)
class FatalError(RuntimeError):
"""
Wrapper class for runtime errors that aren't caused by internal bugs, but by
ESP8266 responses or input content.
"""
def __init__(self, message):
RuntimeError.__init__(self, message)
@staticmethod
def WithResult(message, result):
"""
Return a fatal error object that appends the hex values of
'result' as a string formatted argument.
"""
message += " (result was %s)" % hexify(result)
return FatalError(message)
def _main():
try:
main()
except FatalError as e:
print('\nA fatal error occurred: %s' % e)
sys.exit(2)
if __name__ == '__main__':
_main()