diff --git a/bootloaders/eboot/README.md b/bootloaders/eboot/README.md new file mode 100644 index 0000000000..c9d9403fb4 --- /dev/null +++ b/bootloaders/eboot/README.md @@ -0,0 +1,48 @@ +eboot Bootloader +=================== + + +A simple bootloader which can copy an OTA update to main application address in flash and load the application code to IRAM. + +# Contents +- [Startup sequence](#startup-sequence) + + +### Startup sequence ### + +On startup the ESP ROM will [load and start the firmware image](#load-and-start) from flash location 0x0 - eboot. + +##### eboot will ##### +1. print a version string from the current firmware image +2. print a list of the flags for the first 8 flash-command slots +3. [attempt to read a command](#read-eboot-command) + 1. execute command (if found) + 2. on completion of the command, mark it complete +4. [load and start the application](#load-and-start) + + + +### Read eboot command ### + +Historically eboot commands were stored in RTC RAM. In an effort to survive a power-outage mid-update (which will claer the RTC RAM) there is now provision for storing the commands in Flash. If no valid command is found in the flash storage (or flash storage is disabled) eboot will try RTC RAM. + +Commands are considered valid if the checksum is correct. + +```C +typedef struct eboot_command { + uint32_t magic; + enum action_t action; + uint32_t args[29]; + uint32_t crc32; +} eboot_command_t; +``` + + +### Load and start ### + +The firmware images are expected to have ELF headers indicating what location each section should be loaded into - which is relevant for the RAM locations. + +Both the ESP bootloader and eboot will work through the image sections copying the contents of the appropriate ones into their address. + +Finally execution jumps to the entry-point specified in the image header. + diff --git a/bootloaders/eboot/eboot.c b/bootloaders/eboot/eboot.c index 69bc692e97..6fede5bd99 100644 --- a/bootloaders/eboot/eboot.c +++ b/bootloaders/eboot/eboot.c @@ -12,6 +12,7 @@ #include #include "flash.h" #include "eboot_command.h" +#include "eboot.h" #define SWRST do { (*((volatile uint32_t*) 0x60000700)) |= 0x80000000; } while(0); @@ -21,10 +22,10 @@ extern void ets_wdt_disable(void); int print_version(const uint32_t flash_addr) { uint32_t ver; - if (SPIRead(flash_addr + APP_START_OFFSET + sizeof(image_header_t) + sizeof(section_header_t), &ver, sizeof(ver))) { + if (SPIRead(find_app_start(flash_addr) + sizeof(image_header_t) + sizeof(section_header_t), &ver, sizeof(ver))) { return 1; } - const char* __attribute__ ((aligned (4))) fmtt = "v%08x\n\0\0"; + const char* __attribute__ ((aligned (4))) fmtt = "v%08x+\n\0"; uint32_t fmt[2]; fmt[0] = ((uint32_t*) fmtt)[0]; fmt[1] = ((uint32_t*) fmtt)[1]; @@ -32,16 +33,71 @@ int print_version(const uint32_t flash_addr) return 0; } +#ifdef EBOOT_ENABLE_FLASH_STORAGE + +int print_flags(const uint32_t flash_addr) +{ + const char* __attribute__ ((aligned (4))) fmtt = "#%08x\n\0\0"; + uint32_t ver, i; + uint32_t fmt[2]; + eboot_flash_command_t cmd; + fmt[0] = ((uint32_t*) fmtt)[0]; + fmt[1] = ((uint32_t*) fmtt)[1]; + + ets_putc('<'); + ets_printf((const char*) fmt, ver); + for (i = 0; i < EBOOT_COMMAND_SLOT_COUNT; i++) { + ets_putc('0' + i); + ets_putc(':'); + readBootCommand(i, &cmd); + ets_printf((const char*) fmt, cmd.flags); + } + ets_putc('\n'); + return 0; +} + +#endif // EBOOT_ENABLE_FLASH_STORAGE + +int find_app_start(const uint32_t flash_addr) +{ + image_header_t image_header; + uint32_t pos = flash_addr; + uint8_t i; + + for (i = 0; i < EBOOT_APP_OFFSET_PAGE_LIMIT; i++) { + pos += APP_START_OFFSET; + if (SPIRead(pos, &image_header, sizeof(image_header))) { + return 0; + } + if (image_header.magic == 0xe9) { + return pos; + } + } + return 0; +} + int load_app_from_flash_raw(const uint32_t flash_addr) { image_header_t image_header; - uint32_t pos = flash_addr + APP_START_OFFSET; + uint32_t pos = find_app_start(flash_addr); +#ifdef EBOOT_VERBOSE_STARTUP + const char* __attribute__ ((aligned (4))) fmtt = "l:%08x\n\0"; + uint32_t ver, i; + uint32_t fmt[2]; + fmt[0] = ((uint32_t*) fmtt)[0]; + fmt[1] = ((uint32_t*) fmtt)[1]; - if (SPIRead(pos, &image_header, sizeof(image_header))) { + ets_printf((const char*) fmt, pos); +#endif // EBOOT_VERBOSE_STARTUP + if (SPIRead(pos, &image_header, sizeof(image_header))) { return 1; } pos += sizeof(image_header); + #ifdef EBOOT_VERBOSE_STARTUP + ets_printf((const char*) fmt, pos); + #endif // EBOOT_VERBOSE_STARTUP + for (uint32_t section_index = 0; section_index < image_header.num_segments; @@ -55,6 +111,15 @@ int load_app_from_flash_raw(const uint32_t flash_addr) const uint32_t address = section_header.address; +#ifdef EBOOT_VERBOSE_STARTUP + ets_putc('f'); + ets_printf((const char*) fmt, pos); + ets_putc('t'); + ets_printf((const char*) fmt, address); + ets_putc('s'); + ets_printf((const char*) fmt, section_header.size); +#endif // EBOOT_VERBOSE_STARTUP + bool load = false; if (address < 0x40000000) { @@ -74,8 +139,18 @@ int load_app_from_flash_raw(const uint32_t flash_addr) continue; } +#ifdef EBOOT_VERBOSE_STARTUP + ets_putc('l'); + ets_putc('o'); +#endif // EBOOT_VERBOSE_STARTUP + if (SPIRead(pos, (void*)address, section_header.size)) return 3; +#ifdef EBOOT_VERBOSE_STARTUP + ets_putc('a'); + ets_putc('d'); + ets_putc('\n'); +#endif // EBOOT_VERBOSE_STARTUP pos += section_header.size; } @@ -93,6 +168,21 @@ int copy_raw(const uint32_t src_addr, const uint32_t dst_addr, const uint32_t size) { + uint32_t overwrite_eboot = 0; +#ifdef EBOOT_VERBOSE_STARTUP + const char* __attribute__ ((aligned (4))) fmtt = ":%08x\n\0\0"; + uint32_t fmt[2]; + fmt[0] = ((uint32_t*) fmtt)[0]; + fmt[1] = ((uint32_t*) fmtt)[1]; + + ets_putc('S'); + ets_printf((const char*) fmt, src_addr); + ets_putc('D'); + ets_printf((const char*) fmt, dst_addr); + ets_putc('B'); + ets_printf((const char*) fmt, size); +#endif // EBOOT_VERBOSE_STARTUP + // require regions to be aligned if (src_addr & 0xfff != 0 || dst_addr & 0xfff != 0) { @@ -106,14 +196,16 @@ int copy_raw(const uint32_t src_addr, uint32_t daddr = dst_addr; while (left) { - if (SPIEraseSector(daddr/buffer_size)) { - return 2; - } - if (SPIRead(saddr, buffer, buffer_size)) { - return 3; - } - if (SPIWrite(daddr, buffer, buffer_size)) { - return 4; + if ((daddr >= FLASH_SECTOR_SIZE) || (overwrite_eboot == 1)) { + if (SPIEraseSector(daddr/buffer_size)) { + return 2; + } + if (SPIRead(saddr, buffer, buffer_size)) { + return 3; + } + if (SPIWrite(daddr, buffer, buffer_size)) { + return 4; + } } saddr += buffer_size; daddr += buffer_size; @@ -131,10 +223,12 @@ void main() struct eboot_command cmd; print_version(0); +#ifdef EBOOT_VERBOSE_STARTUP + print_flags(0); +#endif // EBOOT_VERBOSE_STARTUP if (eboot_command_read(&cmd) == 0) { // valid command was passed via RTC_MEM - eboot_command_clear(); ets_putc('@'); } else { // no valid command found @@ -150,6 +244,7 @@ void main() ets_wdt_enable(); ets_putc('0'+res); ets_putc('\n'); if (res == 0) { + eboot_command_clear(); cmd.action = ACTION_LOAD_APP; cmd.args[0] = cmd.args[1]; } @@ -166,5 +261,9 @@ void main() SWRST; } - while(true){} + while(true) { +#ifdef EBOOT_VERBOSE_STARTUP + ets_putc('.'); +#endif // EBOOT_VERBOSE_STARTUP + } } diff --git a/bootloaders/eboot/eboot.elf b/bootloaders/eboot/eboot.elf deleted file mode 100755 index da0f845278..0000000000 Binary files a/bootloaders/eboot/eboot.elf and /dev/null differ diff --git a/bootloaders/eboot/eboot.h b/bootloaders/eboot/eboot.h new file mode 100644 index 0000000000..b0d9b2201c --- /dev/null +++ b/bootloaders/eboot/eboot.h @@ -0,0 +1,10 @@ +#ifndef EBOOT_CONFIG_H +#define EBOOT_CONFIG_H + +#define EBOOT_ENABLE_FLASH_STORAGE +#define EBOOT_COMMAND_SLOT_COUNT 8 +#define EBOOT_APP_OFFSET_PAGE_LIMIT 4 +//#define EBOOT_VERBOSE_STARTUP + + +#endif \ No newline at end of file diff --git a/bootloaders/eboot/eboot.ld b/bootloaders/eboot/eboot.ld index 303ae8a56c..4b90f38037 100644 --- a/bootloaders/eboot/eboot.ld +++ b/bootloaders/eboot/eboot.ld @@ -167,6 +167,7 @@ SECTIONS .irom0.text : ALIGN(4) { _irom0_text_start = ABSOLUTE(.); + LONG(0x31305241) *(.irom0.literal .irom.literal .irom.text.literal .irom0.text .irom.text) _irom0_text_end = ABSOLUTE(.); } >irom0_0_seg :irom0_0_phdr diff --git a/bootloaders/eboot/eboot_command.c b/bootloaders/eboot/eboot_command.c index 648039e48a..7c53fa1548 100644 --- a/bootloaders/eboot/eboot_command.c +++ b/bootloaders/eboot/eboot_command.c @@ -1,4 +1,5 @@ #include "eboot_command.h" +#include "flash.h" uint32_t crc_update(uint32_t crc, const uint8_t *data, size_t length) { @@ -28,7 +29,143 @@ uint32_t eboot_command_calculate_crc32(const struct eboot_command* cmd) offsetof(struct eboot_command, crc32)); } -int eboot_command_read(struct eboot_command* cmd) +#if defined (EBOOT_ENABLE_FLASH_STORAGE) + +extern int find_app_start(const uint32_t flash_addr); + +eboot_flash_command_t *commandAddress(void) { + eboot_index_t eboot_index; + const uint32_t addr = find_app_start(0) + sizeof(image_header_t) + sizeof(section_header_t); + if (SPIRead(addr, (uint32_t *)&eboot_index, sizeof(eboot_index))) { + return NULL; + } + if (eboot_index.magic != EBOOT_INDEX_MAGIC_V1) { + return NULL; + } else { + return eboot_index.commands; + } +} + +bool readBootCommand(int cmd, eboot_flash_command_t *dst) { + eboot_flash_command_t *cmds = commandAddress(); + if (cmd >= EBOOT_COMMAND_MAX_COUNT) { + return 0; + } +#ifdef EBOOT_DEBUG_FLASH + ets_putc('r'); + ets_putc('b'); + ets_putc('0' + cmd); + ets_putc('\n'); +#endif // EBOOT_DEBUG_FLASH + uint32_t uint = (uint32_t) cmds; + uint32_t addr = (uint32_t)cmds - 0x40200000; + addr += cmd * sizeof(*dst); + if (SPIRead(addr, (uint32_t *)dst, sizeof(*dst))) { + return 0; + } +} + +bool writeBootCommand(int cmd, eboot_flash_command_t *dst) { + eboot_flash_command_t *cmds = commandAddress(); + if (cmd >= EBOOT_COMMAND_MAX_COUNT) { + return 0; + } +#ifdef EBOOT_DEBUG_FLASH + ets_putc('w'); + ets_putc('b'); + ets_putc('0' + cmd); + ets_putc('\n'); +#endif // EBOOT_DEBUG_FLASH + uint32_t uint = (uint32_t) cmds; + uint32_t addr = (uint32_t)cmds - 0x40200000; + addr += cmd * sizeof(*dst); + if (SPIWrite(addr, (uint32_t *)dst, sizeof(*dst))) { + return 0; + } +} + +uint32_t eboot_command_read_from_flash(struct eboot_command *cmd) +{ + + eboot_flash_command_t flash_command; + uint32_t i, *src, *dst = (uint32_t *)cmd; + const uint32_t dw_count = sizeof(struct eboot_command) / sizeof(uint32_t); + // ets_printf("Reading command from flash: %p\n", cmd); + + for (i = 0; i < EBOOT_COMMAND_MAX_COUNT; i++) { + // ets_printf("Read bootCommand %d, flags: %x\n", i, flash_command.flags); + readBootCommand(i, &flash_command); + // ets_printf("Read bootCommand %d, flags: %x\n", i, flash_command.flags); + if (((flash_command.flags & EBOOT_CMD_FLAG_SLOT_FREE) == 0) && + ((flash_command.flags & EBOOT_CMD_FLAG_PENDING) == EBOOT_CMD_FLAG_PENDING)) { + // Not free (meaning there's some data) and pending (so it's yet to be completed) +#ifdef EBOOT_DEBUG_FLASH + ets_putc('r'); + ets_putc('c'); + ets_putc('0' + i); + ets_putc('\n'); +#endif // EBOOT_DEBUG_FLASH + src = (uint32_t *)&flash_command.cmd; + for (uint32_t j = 0; j < dw_count; ++j) { + dst[j] = src[j]; + } + // ets_printf("Returning bootCommand %d, flags: %x\n", i, flash_command.flags); + return 1; + } + } + return 0; +} + +uint32_t eboot_command_write_to_flash(struct eboot_command *cmd) +{ + eboot_flash_command_t flash_command; + uint32_t i, *dst, *src = (uint32_t *)cmd; + const uint32_t dw_count = sizeof(struct eboot_command) / sizeof(uint32_t); + + for (i = 0; i < EBOOT_COMMAND_MAX_COUNT; i++) { + readBootCommand(i, &flash_command); + if (((flash_command.flags & EBOOT_CMD_FLAG_SLOT_FREE) == EBOOT_CMD_FLAG_SLOT_FREE) && + ((flash_command.flags & EBOOT_CMD_FLAG_PENDING) == EBOOT_CMD_FLAG_PENDING)) { +#ifdef EBOOT_DEBUG_FLASH + ets_putc('w'); + ets_putc('c'); + ets_putc('0' + i); + ets_putc('\n'); +#endif // EBOOT_DEBUG_FLASH + dst = (uint32_t *)&flash_command.cmd; + for (uint32_t j = 0; j < dw_count; ++j) { + dst[j] = src[j]; + } + flash_command.flags &= ~EBOOT_CMD_FLAG_SLOT_FREE; + writeBootCommand(i, &flash_command); + return 1; + } + } + return 0; +} + +uint32_t eboot_command_clear_flash(void) +{ + eboot_flash_command_t flash_command; + uint32_t i; + + for (i = 0; i < EBOOT_COMMAND_MAX_COUNT; i++) { + readBootCommand(i, &flash_command); + if (((flash_command.flags & EBOOT_CMD_FLAG_SLOT_FREE) == 0) && + ((flash_command.flags & EBOOT_CMD_FLAG_PENDING) == EBOOT_CMD_FLAG_PENDING)) { + flash_command.flags &= ~EBOOT_CMD_FLAG_PENDING; + ets_wdt_disable(); + writeBootCommand(i, &flash_command); + ets_wdt_enable(); + return 1; + } + } + return 0; +} + +#endif // EBOOT_ENABLE_FLASH_STORAGE + +int eboot_command_read_from_rtc(struct eboot_command *cmd) { const uint32_t dw_count = sizeof(struct eboot_command) / sizeof(uint32_t); uint32_t* dst = (uint32_t *) cmd; @@ -36,6 +173,40 @@ int eboot_command_read(struct eboot_command* cmd) dst[i] = RTC_MEM[i]; } + return 0; +} + +int eboot_command_write_to_rtc(struct eboot_command *cmd) +{ + const uint32_t dw_count = sizeof(struct eboot_command) / sizeof(uint32_t); + const uint32_t* src = (const uint32_t *) cmd; + for (uint32_t i = 0; i < dw_count; ++i) { + RTC_MEM[i] = src[i]; + } +} + +void eboot_command_clear_rtc(void) +{ + RTC_MEM[offsetof(struct eboot_command, magic) / sizeof(uint32_t)] = 0; + RTC_MEM[offsetof(struct eboot_command, crc32) / sizeof(uint32_t)] = 0; +} + +int eboot_command_read(struct eboot_command* cmd) +{ + uint32_t have_command = 0; +#if defined (EBOOT_ENABLE_FLASH_STORAGE) + ets_putc('F'); + ets_putc(':'); + have_command = eboot_command_read_from_flash(cmd); + ets_putc('0' + have_command); + ets_putc('\n'); +#endif // EBOOT_ENABLE_FLASH_STORAGE + if (have_command == 0) { + ets_putc('R'); + eboot_command_read_from_rtc(cmd); + ets_putc('\n'); + } + uint32_t crc32 = eboot_command_calculate_crc32(cmd); if (cmd->magic & EBOOT_MAGIC_MASK != EBOOT_MAGIC || cmd->crc32 != crc32) { @@ -49,17 +220,20 @@ void eboot_command_write(struct eboot_command* cmd) { cmd->magic = EBOOT_MAGIC; cmd->crc32 = eboot_command_calculate_crc32(cmd); - - const uint32_t dw_count = sizeof(struct eboot_command) / sizeof(uint32_t); - const uint32_t* src = (const uint32_t *) cmd; - for (uint32_t i = 0; i < dw_count; ++i) { - RTC_MEM[i] = src[i]; - } +#if defined (EBOOT_ENABLE_FLASH_STORAGE) + eboot_command_write_to_flash(cmd); +#endif // EBOOT_ENABLE_FLASH_STORAGE + eboot_command_write_to_rtc(cmd); } void eboot_command_clear() { - RTC_MEM[offsetof(struct eboot_command, magic) / sizeof(uint32_t)] = 0; - RTC_MEM[offsetof(struct eboot_command, crc32) / sizeof(uint32_t)] = 0; + uint32_t cleared = 0; +#if defined (EBOOT_ENABLE_FLASH_STORAGE) + cleared = eboot_command_clear_flash(); +#endif // EBOOT_ENABLE_FLASH_STORAGE + if (cleared > 0) { + eboot_command_clear_rtc(); + } } diff --git a/bootloaders/eboot/eboot_command.h b/bootloaders/eboot/eboot_command.h index ba9c889a2c..46ae3f7e12 100644 --- a/bootloaders/eboot/eboot_command.h +++ b/bootloaders/eboot/eboot_command.h @@ -11,6 +11,7 @@ #include #include #include +#include "eboot.h" #define RTC_MEM ((volatile uint32_t*)0x60001200) @@ -22,13 +23,45 @@ enum action_t { #define EBOOT_MAGIC 0xeb001000 #define EBOOT_MAGIC_MASK 0xfffff000 -struct eboot_command { +typedef struct eboot_command { uint32_t magic; enum action_t action; uint32_t args[29]; uint32_t crc32; -}; +} eboot_command_t; + + +#if defined (EBOOT_ENABLE_FLASH_STORAGE) +// Magic for version 1 corresponds to AR01 +#define EBOOT_INDEX_MAGIC_V1 0x31305241 +#define EBOOT_COMMAND_MAX_COUNT 8 + + +#define FLASH_SECTOR_SIZE 0x1000 + + +/* A command is ready to be actioned if it is both + * Not free (SLOT_FREE == 0) AND + * pending (PENDING == 1) +*/ + +// Is the slot available for new command data (1 == Yes; 0 == It's been used, so No) +#define EBOOT_CMD_FLAG_SLOT_FREE (1 << 0) +// Has the command been actioned (1 == No, needs to be actioned; 0 == It's been completed) +#define EBOOT_CMD_FLAG_PENDING (1 << 1) + +typedef struct eboot_flash_command { + uint32_t flags; + eboot_command_t cmd; +} eboot_flash_command_t; + +typedef struct eboot_index { + uint32_t version; + uint32_t magic; + eboot_flash_command_t *commands; +} eboot_index_t; +#endif // EBOOT_ENABLE_FLASH_STORAGE int eboot_command_read(struct eboot_command* cmd); void eboot_command_write(struct eboot_command* cmd); diff --git a/bootloaders/eboot/flash.h b/bootloaders/eboot/flash.h index 38c528869a..dbdad8c45e 100644 --- a/bootloaders/eboot/flash.h +++ b/bootloaders/eboot/flash.h @@ -21,6 +21,11 @@ int SPIRead(uint32_t addr, void *dest, size_t size); int SPIWrite(uint32_t addr, void *src, size_t size); int SPIEraseAreaEx(const uint32_t start, const uint32_t size); +extern uint32_t _ESP8266_AR_APP_START; + +#define FLASH_SECTOR_SIZE 0x1000 +#define FLASH_BLOCK_SIZE 0x10000 +#define APP_START_OFFSET ((uint32_t) (&_ESP8266_AR_APP_START) - 0x40200000) typedef struct { unsigned char magic; diff --git a/cores/esp8266/Esp.cpp b/cores/esp8266/Esp.cpp index a21dc4bc0f..23e9e5732a 100644 --- a/cores/esp8266/Esp.cpp +++ b/cores/esp8266/Esp.cpp @@ -519,14 +519,14 @@ uint32_t EspClass::getSketchSize() { return result; } -extern "C" uint32_t _FS_start; +extern "C" uint32_t _SKETCH_AREA_end; uint32_t EspClass::getFreeSketchSpace() { uint32_t usedSize = getSketchSize(); // round one sector up uint32_t freeSpaceStart = (usedSize + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1)); - uint32_t freeSpaceEnd = (uint32_t)&_FS_start - 0x40200000; + uint32_t freeSpaceEnd = (uint32_t)&_SKETCH_AREA_end - 0x40200000; #ifdef DEBUG_SERIAL DEBUG_SERIAL.printf("usedSize=%u freeSpaceStart=%u freeSpaceEnd=%u\r\n", usedSize, freeSpaceStart, freeSpaceEnd); diff --git a/cores/esp8266/Updater.cpp b/cores/esp8266/Updater.cpp index d6b6b621f5..58ebaae231 100644 --- a/cores/esp8266/Updater.cpp +++ b/cores/esp8266/Updater.cpp @@ -23,6 +23,7 @@ extern "C" { } extern "C" uint32_t _FS_start; +extern "C" uint32_t _SKETCH_AREA_end; UpdaterClass::UpdaterClass() : _async(false) @@ -110,7 +111,7 @@ bool UpdaterClass::begin(size_t size, int command, int ledPin, uint8_t ledOn) { //size of current sketch rounded to a sector size_t currentSketchSize = (ESP.getSketchSize() + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1)); //address of the end of the space available for sketch and update - uintptr_t updateEndAddress = (uintptr_t)&_FS_start - 0x40200000; + uintptr_t updateEndAddress = (uintptr_t)&_SKETCH_AREA_end - 0x40200000; //size of the update rounded to a sector size_t roundedSize = (size + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1)); //address where we will start writing the update diff --git a/cores/esp8266/core_esp8266_eboot_command.cpp b/cores/esp8266/core_esp8266_eboot_command.cpp index 2ddc65933e..9d2b78d47b 100644 --- a/cores/esp8266/core_esp8266_eboot_command.cpp +++ b/cores/esp8266/core_esp8266_eboot_command.cpp @@ -22,7 +22,11 @@ #include #include #include "coredecls.h" +#include #include "eboot_command.h" +#include "c_types.h" +#include "flash_utils.h" +#include "spi_flash.h" extern "C" { @@ -32,7 +36,157 @@ static uint32_t eboot_command_calculate_crc32(const struct eboot_command* cmd) return crc32((const uint8_t*) cmd, offsetof(struct eboot_command, crc32)); } -int eboot_command_read(struct eboot_command* cmd) +#if defined (EBOOT_ENABLE_FLASH_STORAGE) + +uint32_t eboot_get_flash_block_size(void) { + return 2 * 4 * 1024; +} + +uint32_t eboot_read_flash_index(eboot_index_t *eboot_index) +{ + if (spi_flash_read(0 + APP_START_OFFSET + sizeof(image_header_t) + sizeof(section_header_t), (uint32_t *) &eboot_index, sizeof(eboot_index)) != 0) { + return 0; + } + + if (eboot_index->magic != EBOOT_INDEX_MAGIC_V1) { + return 0; + } + + return 1; +} + +eboot_flash_command_t *commandAddress(void) { + eboot_index_t eboot_index; + const uint32_t addr = 0 + APP_START_OFFSET + sizeof(image_header_t) + sizeof(section_header_t); + ets_printf("commandAddress: %p\n", addr); + if (spi_flash_read(addr, (uint32_t *)&eboot_index, sizeof(eboot_index)) != 0) { + ets_printf("failed to read SPI\n"); + return NULL; + } + if (eboot_index.magic != EBOOT_INDEX_MAGIC_V1) { + ets_printf("failed to find MAGIC value (had %x)\n", eboot_index.magic); + return NULL; + } else { + return eboot_index.commands; + } +} + +bool readBootCommand(int cmd, eboot_flash_command_t *dst) { + eboot_flash_command_t *cmds = commandAddress(); + if (cmds == 0) { + return 0; + } + ets_printf("Reading command %d from flash: %p\n", cmd, cmds); + if (cmd >= EBOOT_COMMAND_MAX_COUNT) { + return 0; + } + uint32_t uint = (uint32_t) cmds; + uint32_t addr = (uint32_t)cmds - 0x40200000; + addr += cmd * sizeof(*dst); + ets_printf("Reading command %d from flash @ %p\n", cmd, addr); + if (spi_flash_read(addr, (uint32_t *)dst, sizeof(*dst))) { + return 0; + } + return 1; +} + +bool writeBootCommand(int cmd, eboot_flash_command_t *dst) { + eboot_flash_command_t *cmds = commandAddress(); + ets_printf("!Writing command %d to flash: %p\n", cmd, cmds); + if (cmd >= EBOOT_COMMAND_MAX_COUNT) { + return 0; + } + uint32_t uint = (uint32_t) cmds; + uint32_t addr = (uint32_t)cmds - 0x40200000; + addr += cmd * sizeof(*dst); + ets_printf("Writing command %d to flash @ %p\n", cmd, addr); + if (spi_flash_write(addr, (uint32_t *)dst, sizeof(*dst))) { + return 0; + } +} + +bool eraseBootCommandBlock(void) { + eboot_flash_command_t *cmds = commandAddress(); + uint32_t addr = (uint32_t)cmds - 0x40200000; + ets_printf("Erasing command block at %p\n", addr); + if (spi_flash_erase_sector(addr / FLASH_SECTOR_SIZE)) { + return 0; + } + return 1; +} + +uint32_t eboot_command_read_from_flash(struct eboot_command *cmd) +{ + eboot_index_t eboot_index; + eboot_flash_command_t *flash_command; + uint32_t i; + const uint32_t dw_count = sizeof(struct eboot_command) / sizeof(uint32_t); + + if (!eboot_read_flash_index(&eboot_index)) { + return 0; + } else { + flash_command = eboot_index.commands; + for (i = 0; i < EBOOT_COMMAND_MAX_COUNT; i++) { + if (((flash_command->flags & EBOOT_CMD_FLAG_SLOT_FREE) == 0) && + ((flash_command->flags & EBOOT_CMD_FLAG_PENDING) == EBOOT_CMD_FLAG_PENDING)) { + // This is a valid command waiting to be actioned, or should be. The CRC check will determine if it's actually valid + uint32_t* dst = (uint32_t *) cmd; + // uint32_t* src = (uint32_t *) flash_command->cmd; + // for (uint32_t i = 0; i < dw_count; ++i) { + // dst[i] = src[i]; + // } + return 1; + } + } + } + return 0; +} + +uint32_t eboot_command_write_to_flash(struct eboot_command *cmd) +{ + eboot_flash_command_t flash_command; + uint32_t i, *dst, *src = (uint32_t *)cmd; + int32_t target_command_slot = -1; + const uint32_t dw_count = sizeof(struct eboot_command) / sizeof(uint32_t); + ets_printf("Writing command to flash: %p\n", cmd); + + for (i = 0; i < EBOOT_COMMAND_MAX_COUNT; i++) { + ets_printf("Read bootCommand %d, flags: %x\n", i, flash_command.flags); + if (readBootCommand(i, &flash_command) == 0) { + ets_printf("Reading bootCommand %d failed!\n", i); + } else { + ets_printf("Read bootCommand %d, flags: %x\n", i, flash_command.flags); + if (((flash_command.flags & EBOOT_CMD_FLAG_SLOT_FREE) == EBOOT_CMD_FLAG_SLOT_FREE) && + ((flash_command.flags & EBOOT_CMD_FLAG_PENDING) == EBOOT_CMD_FLAG_PENDING)) { + target_command_slot = i; + break; + } + } + } + if (target_command_slot == -1) { + // We didn't find a free slot. Assume this is due to the slots being all used, + // so erase the storage page + if (eraseBootCommandBlock() != 1) { + return 0; + } + // Block is now clear, so we can use the first slot + target_command_slot = 0; + // And reinitialise our block to a blank one. + readBootCommand(target_command_slot, &flash_command); + } + dst = (uint32_t *)&flash_command.cmd; + for (uint32_t j = 0; j < dw_count; ++j) { + dst[j] = src[j]; + } + flash_command.flags &= ~EBOOT_CMD_FLAG_SLOT_FREE; + ets_printf("Writing command %d to flash: %p\n", target_command_slot, cmd); + writeBootCommand(target_command_slot, &flash_command); + return 1; +} + +#endif // EBOOT_ENABLE_FLASH_STORAGE + +int eboot_command_read_from_rtc(struct eboot_command *cmd) { const uint32_t dw_count = sizeof(struct eboot_command) / sizeof(uint32_t); uint32_t* dst = (uint32_t *) cmd; @@ -40,8 +194,36 @@ int eboot_command_read(struct eboot_command* cmd) dst[i] = RTC_MEM[i]; } + return 0; +} + +int eboot_command_write_to_rtc(struct eboot_command *cmd) +{ + const uint32_t dw_count = sizeof(struct eboot_command) / sizeof(uint32_t); + const uint32_t* src = (const uint32_t *) cmd; + for (uint32_t i = 0; i < dw_count; ++i) { + RTC_MEM[i] = src[i]; + } +} + +void eboot_command_clear_rtc(void) +{ + RTC_MEM[offsetof(struct eboot_command, magic) / sizeof(uint32_t)] = 0; + RTC_MEM[offsetof(struct eboot_command, crc32) / sizeof(uint32_t)] = 0; +} + +int eboot_command_read(struct eboot_command* cmd) +{ + uint32_t have_command = 0; +#if defined (EBOOT_ENABLE_FLASH_STORAGE) + // have_command = eboot_command_read_from_flash(cmd); +#endif // EBOOT_ENABLE_FLASH_STORAGE + if (have_command == 0) { + eboot_command_read_from_rtc(cmd); + } + uint32_t crc32 = eboot_command_calculate_crc32(cmd); - if ((cmd->magic & EBOOT_MAGIC_MASK) != EBOOT_MAGIC || + if (cmd->magic & EBOOT_MAGIC_MASK != EBOOT_MAGIC || cmd->crc32 != crc32) { return 1; } @@ -51,20 +233,23 @@ int eboot_command_read(struct eboot_command* cmd) void eboot_command_write(struct eboot_command* cmd) { + uint32_t saved = 0; cmd->magic = EBOOT_MAGIC; cmd->crc32 = eboot_command_calculate_crc32(cmd); - - const uint32_t dw_count = sizeof(struct eboot_command) / sizeof(uint32_t); - const uint32_t* src = (const uint32_t *) cmd; - for (uint32_t i = 0; i < dw_count; ++i) { - RTC_MEM[i] = src[i]; + ets_printf("Writing command: %p\n", cmd); +#if defined (EBOOT_ENABLE_FLASH_STORAGE) + saved = eboot_command_write_to_flash(cmd); +#endif // EBOOT_ENABLE_FLASH_STORAGE + if (saved == 0) { + eboot_command_write_to_rtc(cmd); } } void eboot_command_clear() { - RTC_MEM[offsetof(struct eboot_command, magic) / sizeof(uint32_t)] = 0; - RTC_MEM[offsetof(struct eboot_command, crc32) / sizeof(uint32_t)] = 0; +#if defined (EBOOT_ENABLE_FLASH_STORAGE) +#endif // EBOOT_ENABLE_FLASH_STORAGE + eboot_command_clear_rtc(); } }; diff --git a/cores/esp8266/eboot_command.h b/cores/esp8266/eboot_command.h index 3d854afba3..62e20491f2 100644 --- a/cores/esp8266/eboot_command.h +++ b/cores/esp8266/eboot_command.h @@ -17,13 +17,45 @@ enum action_t { #define EBOOT_MAGIC 0xeb001000 #define EBOOT_MAGIC_MASK 0xfffff000 -struct eboot_command { +typedef struct eboot_command { uint32_t magic; enum action_t action; uint32_t args[29]; uint32_t crc32; -}; +} eboot_command_t; + + +#if defined (EBOOT_ENABLE_FLASH_STORAGE) +// Magic for version 1 corresponds to AR01 +#define EBOOT_INDEX_MAGIC_V1 0x31305241 +#define EBOOT_COMMAND_MAX_COUNT 8 + + +#define FLASH_SECTOR_SIZE 0x1000 + + +/* A command is ready to be actioned if it is both + * Not free (SLOT_FREE == 0) AND + * pending (PENDING == 1) +*/ + +// Is the slot available for new command data (1 == Yes; 0 == It's been used, so No) +#define EBOOT_CMD_FLAG_SLOT_FREE (1 << 0) +// Has the command been actioned (1 == No, needs to be actioned; 0 == It's been completed) +#define EBOOT_CMD_FLAG_PENDING (1 << 1) + +typedef struct eboot_flash_command { + uint32_t flags; + eboot_command_t cmd; +} eboot_flash_command_t; + +typedef struct eboot_index { + uint32_t version; + uint32_t magic; + eboot_flash_command_t *commands; +} eboot_index_t; +#endif // EBOOT_ENABLE_FLASH_STORAGE int eboot_command_read(struct eboot_command* cmd); void eboot_command_write(struct eboot_command* cmd); diff --git a/parseBoards.py b/parseBoards.py new file mode 100644 index 0000000000..b24b480f19 --- /dev/null +++ b/parseBoards.py @@ -0,0 +1,230 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# generateBoards.py — generate boards.txt and various make files for ESP Arduino +# +# Copyright © 2017 Julian Davison +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# + +from __future__ import print_function +import sys +import os +import argparse +import subprocess +import tempfile +import shutil +import pprint + +nonDevicesList = {'menu': 'Menu items', + } + +platformSuffixes = ['linux', 'windows', 'macosx'] + +# Pull out the 'menu' subtree (at the top level) to define the custom menus +# 'boardIds' == remaining top-level keys +# iterate over boardIds to pull out board specific config + +# platform suffixes stripped in IDE on load + # key = processPlatformSuffix(key, ".linux", OSUtils.isLinux()); + # key = processPlatformSuffix(key, ".windows", OSUtils.isWindows()); + # key = processPlatformSuffix(key, ".macosx", OSUtils.isMacOS()); + +# FInd top-level custom menus, and then hiff the 'menu' branch of boards.txt + # // Create custom menus for this platform + # PreferencesMap menus = boardsPreferences.get("menu"); + # if (menus != null) + # customMenus = menus.topLevelMap(); + # boardsPreferences.remove("menu"); + + + +devicesList = { + 'generic': {'name': 'Generic ESP8266 Module', + }, + 'esp8285': {'name': 'Generic ESP8285 Module', + }, + 'espduino': {'name': 'ESPDuino (ESP-13 Module)', + }, + 'huzzah': {'name': 'Adafruit HUZZAH ESP8266', + }, + 'espresso_lite_v1': {'name': 'ESPresso Lite 1.0', + }, + 'espresso_lite_v2': {'name': 'ESPresso Lite 2.0', + }, + 'phoenix_v1': {'name': 'Phoenix 1.0', + }, + 'phoenix_v2': {'name': 'Phoenix 2.0', + }, + 'nodemcu': {'name': 'NodeMCU 0.9 (ESP-12 Module)', + }, + 'nodemcuv2': {'name': 'NodeMCU 1.0 (ESP-12E Module)', + }, + 'modwifi': {'name': 'Olimex MOD-WIFI-ESP8266(-DEV)', + }, + 'thing': {'name': 'SparkFun ESP8266 Thing', + }, + 'thingdev': {'name': 'SparkFun ESP8266 Thing Dev', + }, + 'esp210': {'name': 'SweetPea ESP-210', + }, + 'd1_mini': {'name': 'WeMos D1 R2 & mini', + }, + 'd1': {'name': 'WeMos D1(Retired)', + }, + 'espino': {'name': 'ESPino (ESP-12 Module)', + }, + 'espinotee': {'name': 'ThaiEasyElec''s ESPino', + }, + 'wifinfo': {'name': 'WifInfo', + }, + 'coredev': {'name': 'Core Development Module', + }, +} + +menu = {} +devceMenu = {} +customMenus = {} + +settings = {} +genericSettings = {} + +with open('boards.txt') as f: + for line in f: + line = line.rstrip("\n") + # SKip blank lines + if (line == ''): + continue + # Skip comment lines + if (line[0] == '#'): + continue + + (key, value) = line.split('=', 1) + (device, config_line) = key.split('.', 1) + # print(components) + # print(value) + # print('Line: ' + line) + # print('Key: ' + key) + # print('Value: ' + value) + # print('Device: ' + device) + # print('Config: ' + config_line) + + item = menu + # device = '' #None + if (device in nonDevicesList.keys()): + # config_line = device + '.' + config_line + # device = 'None' + item = customMenus + else: + device = "'{device}'".format(device = device) + # print('Device: ' + device) + # print('Config: ' + config_line) + + + + if (config_line not in item.keys()): + item[config_line] = {value: [device]} + if (value not in item[config_line]): + item[config_line][value] = [device] + if (device not in item[config_line][value]): + item[config_line][value].append(device) + if (device == '\'generic\''): + if (config_line not in genericSettings.keys()): + genericSettings[config_line] = value + else: + if ((config_line not in genericSettings.keys()) or + (genericSettings[config_line] != value)): + if (device not in settings.keys()): + settings[device] = {} + settings[device][config_line] = value + + +pp = pprint.PrettyPrinter(indent=1) +# pp.pprint(menu) + +# print(menu) + +def build(data, prefix): + if (type(data) == type([])): + return data + for k in data.keys(): + if (prefix != ''): + devices = build(data[k], prefix + '.' + k) + else: + devices = build(data[k], k) + if (devices is not None): + for device in devices: + print("{device}.{prefix}={k}".format(device = device, prefix = prefix, k = k)) + return None + +# build(menu, '') + +def myPprint(data, prefix_len): + if (type(data) == type([])): + return data + firstIteration = True + for k in sorted(data): + if (firstIteration): + firstIteration = False + else: + print(" " * prefix_len, end='') + # print(prefix_len, end=';') + start = "{{'{k}': ".format(k = k) + print(start, end='') + # print(" " * (len(start) + prefix_len), end='') + # print(len(start), end=':') + returnVal = myPprint(data[k], prefix_len + len(start)) + if (returnVal is not None): + # print(returnVal) + print("['", end='') + print("', '".join(returnVal), end='') + print("']") + # print(prefix_len, end='') + return None + +def myPprint2(data): + indent = 1 + print("{", end='') + firstIteration = True + for config_line in sorted(data.iterkeys()): + if (firstIteration): + firstIteration = False + else: + print(" ", end='') + outputStr = "'{k}': {{".format(k = config_line) + print(outputStr, end='') + indent = len(outputStr) + entries = 0 + for value in data[config_line]: + entries += 1 + if (entries > 1): + print() + print(" " * indent, end='') + print("'{v}': [".format(v = value.replace("'", "\\'")), end='') + print(", ".join(data[config_line][value]), end='') + # for device in data[config_line][value]: + # print("'{d}', ".format(d = device), end='') + print("],", end='') + if (entries > 1): + print(" " * indent, end='') + print("},") + print("}") + +# myPprint(menu, 0) +print("Menu") +myPprint2(menu) +print("customMenus") +myPprint2(customMenus) +print("genericSettings") +pp.pprint(genericSettings) +pp.pprint(settings) diff --git a/platform.txt b/platform.txt index 8bccbe2afc..6eddbee13c 100644 --- a/platform.txt +++ b/platform.txt @@ -53,7 +53,7 @@ compiler.c.flags=-c {compiler.warning_flags} -Os -g -Wpointer-arith -Wno-implici compiler.S.cmd=xtensa-lx106-elf-gcc compiler.S.flags=-c -g -x assembler-with-cpp -MMD -mlongcalls -compiler.c.elf.flags=-g {compiler.warning_flags} -Os -nostdlib -Wl,--no-check-sections -u app_entry {build.float} -Wl,-static "-L{compiler.sdk.path}/lib" "-L{compiler.sdk.path}/lib/{build.sdk}" "-L{compiler.sdk.path}/ld" "-L{compiler.libc.path}/lib" "-T{build.flash_ld}" -Wl,--gc-sections -Wl,-wrap,system_restart_local -Wl,-wrap,spi_flash_read +compiler.c.elf.flags=-g {compiler.warning_flags} -Os -nostdlib -Wl,--no-check-sections -u app_entry {build.float} -Wl,-static "-L{compiler.sdk.path}/lib" "-L{compiler.sdk.path}/lib/{build.sdk}" "-L{compiler.sdk.path}/ld" "-L{compiler.libc.path}/lib" -Wl,--defsym=_FS_start_offset={build.spiffs_start} "-T{build.flash_ld}" -Wl,--gc-sections -Wl,-wrap,system_restart_local -Wl,-wrap,spi_flash_read compiler.c.elf.cmd=xtensa-lx106-elf-gcc compiler.c.elf.libs=-lhal -lphy -lpp -lnet80211 {build.lwip_lib} -lwpa -lcrypto -lmain -lwps -lbearssl -laxtls -lespnow -lsmartconfig -lairkiss -lwpa2 {build.stdcpp_lib} -lm -lc -lgcc @@ -72,7 +72,7 @@ compiler.elf2hex.flags= compiler.size.cmd=xtensa-lx106-elf-size # This can be overriden in boards.txt -build.extra_flags=-DESP8266 +build.extra_flags=-DESP8266 -DEBOOT_ENABLE_FLASH_STORAGE # These can be overridden in platform.local.txt compiler.c.extra_flags= diff --git a/tools/sdk/ld/eagle.app.v6.common.ld.h b/tools/sdk/ld/eagle.app.v6.common.ld.h index 2f5735fff7..364e22148b 100644 --- a/tools/sdk/ld/eagle.app.v6.common.ld.h +++ b/tools/sdk/ld/eagle.app.v6.common.ld.h @@ -138,7 +138,10 @@ SECTIONS .irom0.text : ALIGN(4) { _irom0_text_start = ABSOLUTE(.); + _ESP8266_AR_APP_START = ABSOLUTE(.); *(.ver_number) + LONG(0x31305241) + LONG(_BOOTLOADER_DATA) *.c.o(.literal*, .text*) *.cpp.o(EXCLUDE_FILE (umm_malloc.cpp.o) .literal*, EXCLUDE_FILE (umm_malloc.cpp.o) .text*) *.cc.o(.literal*, .text*) diff --git a/tools/sdk/ld/eagle.flash.4m.ld b/tools/sdk/ld/eagle.flash.4m.ld index f77c95ae1e..aab228d77c 100644 --- a/tools/sdk/ld/eagle.flash.4m.ld +++ b/tools/sdk/ld/eagle.flash.4m.ld @@ -14,9 +14,13 @@ MEMORY irom0_0_seg : org = 0x40201010, len = 0xfeff0 } -PROVIDE ( _FS_start = 0x405FB000 ); +_BOOTLOADER_FS_OFFSET = DEFINED(eboot_get_flash_block_size) ? 0x2000 : 0; + +PROVIDE ( _FS_start = 0x40200000 + _BOOTLOADER_FS_OFFSET + _FS_start_offset ); PROVIDE ( _FS_end = 0x405FB000 ); PROVIDE ( _FS_page = 0x0 ); PROVIDE ( _FS_block = 0x0 ); +PROVIDE ( _BOOTLOADER_DATA = DEFINED(eboot_get_flash_block_size) ? _FS_start - _BOOTLOADER_FS_OFFSET : 0 ); +PROVIDE ( _SKETCH_AREA_end = DEFINED(eboot_get_flash_block_size) ? _BOOTLOADER_DATA : _FS_start ); INCLUDE "local.eagle.app.v6.common.ld" diff --git a/tools/sdk/ld/eagle.flash.4m1m.ld b/tools/sdk/ld/eagle.flash.4m1m.ld index 4d295e3069..6969107cd3 100644 --- a/tools/sdk/ld/eagle.flash.4m1m.ld +++ b/tools/sdk/ld/eagle.flash.4m1m.ld @@ -14,9 +14,13 @@ MEMORY irom0_0_seg : org = 0x40201010, len = 0xfeff0 } -PROVIDE ( _FS_start = 0x40500000 ); -PROVIDE ( _FS_end = 0x405FA000 ); +_BOOTLOADER_FS_OFFSET = DEFINED(eboot_get_flash_block_size) ? 0x2000 : 0; + +PROVIDE ( _FS_start = 0x40200000 + _BOOTLOADER_FS_OFFSET + _FS_start_offset ); +PROVIDE ( _FS_end = 0x405FB000 ); PROVIDE ( _FS_page = 0x100 ); PROVIDE ( _FS_block = 0x2000 ); +PROVIDE ( _BOOTLOADER_DATA = DEFINED(eboot_get_flash_block_size) ? _FS_start - _BOOTLOADER_FS_OFFSET : 0 ); +PROVIDE ( _SKETCH_AREA_end = DEFINED(eboot_get_flash_block_size) ? _BOOTLOADER_DATA : _FS_start ); INCLUDE "local.eagle.app.v6.common.ld"