diff --git a/hardware/esp8266com/esp8266/cores/esp8266/Esp.cpp b/hardware/esp8266com/esp8266/cores/esp8266/Esp.cpp index 419cb889a..73367683a 100644 --- a/hardware/esp8266com/esp8266/cores/esp8266/Esp.cpp +++ b/hardware/esp8266com/esp8266/cores/esp8266/Esp.cpp @@ -19,6 +19,9 @@ */ #include "Arduino.h" +#include "flash_utils.h" +#include "eboot_command.h" +#include extern "C" { #include "user_interface.h" @@ -26,6 +29,9 @@ extern "C" { extern struct rst_info resetInfo; } + +// #define DEBUG_SERIAL Serial + //extern "C" void ets_wdt_init(uint32_t val); extern "C" void ets_wdt_enable(void); extern "C" void ets_wdt_disable(void); @@ -315,4 +321,123 @@ bool EspClass::eraseConfig(void) { return ret; } +uint32_t EspClass::getSketchSize() { + static uint32_t result = 0; + if (result) + return result; + + image_header_t image_header; + uint32_t pos = APP_START_OFFSET; + if (spi_flash_read(pos, (uint32_t*) &image_header, sizeof(image_header))) { + return 0; + } + pos += sizeof(image_header); +#ifdef DEBUG_SERIAL + DEBUG_SERIAL.printf("num_segments=%u\r\n", image_header.num_segments); +#endif + for (uint32_t section_index = 0; + section_index < image_header.num_segments; + ++section_index) + { + section_header_t section_header = {0}; + if (spi_flash_read(pos, (uint32_t*) §ion_header, sizeof(section_header))) { + return 0; + } + pos += sizeof(section_header); + pos += section_header.size; +#ifdef DEBUG_SERIAL + DEBUG_SERIAL.printf("section=%u size=%u pos=%u\r\n", section_index, section_header.size, pos); +#endif + } + result = pos; + return result; +} + +extern "C" uint32_t _SPIFFS_start; + +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)&_SPIFFS_start - 0x40200000; + +#ifdef DEBUG_SERIAL + DEBUG_SERIAL.printf("usedSize=%u freeSpaceStart=%u freeSpaceEnd=%u\r\n", usedSize, freeSpaceStart, freeSpaceEnd); +#endif + return freeSpaceEnd - freeSpaceStart; +} + +bool EspClass::updateSketch(Stream& in, uint32_t size) { + + if (size > getFreeSketchSpace()) + return false; + + uint32_t usedSize = getSketchSize(); + uint32_t freeSpaceStart = (usedSize + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1)); + uint32_t roundedSize = (size + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1)); + +#ifdef DEBUG_SERIAL + DEBUG_SERIAL.printf("erase @0x%x size=0x%x\r\n", freeSpaceStart, roundedSize); +#endif + + noInterrupts(); + int rc = SPIEraseAreaEx(freeSpaceStart, roundedSize); + interrupts(); + if (rc) + return false; + +#ifdef DEBUG_SERIAL + DEBUG_SERIAL.println("erase done"); +#endif + + uint32_t addr = freeSpaceStart; + uint32_t left = size; + + const uint32_t bufferSize = FLASH_SECTOR_SIZE; + std::unique_ptr buffer(new uint8_t[bufferSize]); + +#ifdef DEBUG_SERIAL + DEBUG_SERIAL.println("writing"); +#endif + while (left > 0) { + size_t willRead = (left < bufferSize) ? left : bufferSize; + size_t rd = in.readBytes(buffer.get(), willRead); + if (rd != willRead) { +#ifdef DEBUG_SERIAL + DEBUG_SERIAL.println("stream read failed"); +#endif + return false; + } + + noInterrupts(); + rc = SPIWrite(addr, buffer.get(), willRead); + interrupts(); + if (rc) { +#ifdef DEBUG_SERIAL + DEBUG_SERIAL.println("write failed"); +#endif + return false; + } + + addr += willRead; + left -= willRead; +#ifdef DEBUG_SERIAL + DEBUG_SERIAL.print("."); +#endif + } + +#ifdef DEBUG_SERIAL + DEBUG_SERIAL.println("\r\nrestarting"); +#endif + eboot_command ebcmd; + ebcmd.action = ACTION_COPY_RAW; + ebcmd.args[0] = freeSpaceStart; + ebcmd.args[1] = 0x00000; + ebcmd.args[2] = size; + eboot_command_write(&ebcmd); + + ESP.restart(); + return true; // never happens +} diff --git a/hardware/esp8266com/esp8266/cores/esp8266/Esp.h b/hardware/esp8266com/esp8266/cores/esp8266/Esp.h index ecb80da07..2b58b79d1 100644 --- a/hardware/esp8266com/esp8266/cores/esp8266/Esp.h +++ b/hardware/esp8266com/esp8266/cores/esp8266/Esp.h @@ -69,44 +69,48 @@ class EspClass { // note: setting the timeout value is not implemented at the moment void wdtEnable(WDTO_t timeout_ms = WDTO_0MS); - void wdtDisable(void); - void wdtFeed(void); + void wdtDisable(); + void wdtFeed(); void deepSleep(uint32_t time_us, WakeMode mode = WAKE_RF_DEFAULT); - void reset(void); - void restart(void); + void reset(); + void restart(); - uint16_t getVcc(void); - uint32_t getFreeHeap(void); + uint16_t getVcc(); + uint32_t getFreeHeap(); - uint32_t getChipId(void); + uint32_t getChipId(); - const char * getSdkVersion(void); + const char * getSdkVersion(); - uint8_t getBootVersion(void); - uint8_t getBootMode(void); + uint8_t getBootVersion(); + uint8_t getBootMode(); - uint8_t getCpuFreqMHz(void); + uint8_t getCpuFreqMHz(); - uint32_t getFlashChipId(void); + uint32_t getFlashChipId(); //gets the actual chip size based on the flash id - uint32_t getFlashChipRealSize(void); + uint32_t getFlashChipRealSize(); //gets the size of the flash as set by the compiler - uint32_t getFlashChipSize(void); - uint32_t getFlashChipSpeed(void); - FlashMode_t getFlashChipMode(void); - uint32_t getFlashChipSizeByChipId(void); + uint32_t getFlashChipSize(); + uint32_t getFlashChipSpeed(); + FlashMode_t getFlashChipMode(); + uint32_t getFlashChipSizeByChipId(); - String getResetInfo(void); - struct rst_info * getResetInfoPtr(void); + uint32_t getSketchSize(); + uint32_t getFreeSketchSpace(); + bool updateSketch(Stream& in, uint32_t size); - bool eraseConfig(void); + String getResetInfo(); + struct rst_info * getResetInfoPtr(); - inline uint32_t getCycleCount(void); + bool eraseConfig(); + + inline uint32_t getCycleCount(); }; -uint32_t EspClass::getCycleCount(void) +uint32_t EspClass::getCycleCount() { uint32_t ccount; __asm__ __volatile__("rsr %0,ccount":"=a" (ccount)); diff --git a/hardware/esp8266com/esp8266/cores/esp8266/core_esp8266_eboot_command.c b/hardware/esp8266com/esp8266/cores/esp8266/core_esp8266_eboot_command.c new file mode 100644 index 000000000..60c92bf18 --- /dev/null +++ b/hardware/esp8266com/esp8266/cores/esp8266/core_esp8266_eboot_command.c @@ -0,0 +1,88 @@ +/* + core_esp8266_eboot_command.c - interface to the eboot bootloader + + Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include "eboot_command.h" + +uint32_t crc_update(uint32_t crc, const uint8_t *data, size_t length) +{ + uint32_t i; + bool bit; + uint8_t c; + + while (length--) { + c = *data++; + for (i = 0x80; i > 0; i >>= 1) { + bit = crc & 0x80000000; + if (c & i) { + bit = !bit; + } + crc <<= 1; + if (bit) { + crc ^= 0x04c11db7; + } + } + } + return crc; +} + +uint32_t eboot_command_calculate_crc32(const struct eboot_command* cmd) +{ + return crc_update(0xffffffff, (const uint8_t*) cmd, + offsetof(struct eboot_command, crc32)); +} + +int eboot_command_read(struct eboot_command* cmd) +{ + const uint32_t dw_count = sizeof(struct eboot_command) / sizeof(uint32_t); + uint32_t* dst = (uint32_t *) cmd; + for (uint32_t i = 0; i < dw_count; ++i) { + dst[i] = RTC_MEM[i]; + } + + uint32_t crc32 = eboot_command_calculate_crc32(cmd); + if (cmd->magic & EBOOT_MAGIC_MASK != EBOOT_MAGIC || + cmd->crc32 != crc32) { + return 1; + } + + return 0; +} + +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]; + } +} + +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; +} + diff --git a/hardware/esp8266com/esp8266/cores/esp8266/core_esp8266_flash_utils.c b/hardware/esp8266com/esp8266/cores/esp8266/core_esp8266_flash_utils.c new file mode 100644 index 000000000..6f8a34f41 --- /dev/null +++ b/hardware/esp8266com/esp8266/cores/esp8266/core_esp8266_flash_utils.c @@ -0,0 +1,64 @@ +/* + core_esp8266_flash_utils.c - flash and binary image helpers + + Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#include +#include +#include +#include "flash_utils.h" + + +int SPIEraseAreaEx(const uint32_t start, const uint32_t size) +{ + if (start & (FLASH_SECTOR_SIZE - 1) != 0) { + return 1; + } + + const uint32_t sectors_per_block = FLASH_BLOCK_SIZE / FLASH_SECTOR_SIZE; + uint32_t current_sector = start / FLASH_SECTOR_SIZE; + uint32_t sector_count = (size + FLASH_SECTOR_SIZE - 1) / FLASH_SECTOR_SIZE; + const uint32_t end = current_sector + sector_count; + + for (; current_sector < end && (current_sector & (sectors_per_block-1)); + ++current_sector, --sector_count) { + if (SPIEraseSector(current_sector)) { + return 2; + } + } + + for (;current_sector + sectors_per_block <= end; + current_sector += sectors_per_block, + sector_count -= sectors_per_block) { + if (SPIEraseBlock(current_sector / sectors_per_block)) { + return 3; + } + } + + for (; current_sector < end; + ++current_sector, --sector_count) { + if (SPIEraseSector(current_sector)) { + return 4; + } + } + + return 0; +} + diff --git a/hardware/esp8266com/esp8266/cores/esp8266/eboot_command.h b/hardware/esp8266com/esp8266/cores/esp8266/eboot_command.h new file mode 100644 index 000000000..3d854afba --- /dev/null +++ b/hardware/esp8266com/esp8266/cores/esp8266/eboot_command.h @@ -0,0 +1,36 @@ +#ifndef EBOOT_COMMAND_H +#define EBOOT_COMMAND_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define RTC_MEM ((volatile uint32_t*)0x60001200) + +enum action_t { + ACTION_COPY_RAW = 0x00000001, + ACTION_LOAD_APP = 0xffffffff +}; + +#define EBOOT_MAGIC 0xeb001000 +#define EBOOT_MAGIC_MASK 0xfffff000 + +struct eboot_command { + uint32_t magic; + enum action_t action; + uint32_t args[29]; + uint32_t crc32; +}; + + +int eboot_command_read(struct eboot_command* cmd); +void eboot_command_write(struct eboot_command* cmd); +void eboot_command_clear(); + +#ifdef __cplusplus +} +#endif + +#endif //EBOOT_COMMAND_H diff --git a/hardware/esp8266com/esp8266/cores/esp8266/flash_utils.h b/hardware/esp8266com/esp8266/cores/esp8266/flash_utils.h new file mode 100644 index 000000000..4e5f2120b --- /dev/null +++ b/hardware/esp8266com/esp8266/cores/esp8266/flash_utils.h @@ -0,0 +1,63 @@ +/* + flash_utils.h - Flash access function and data structures + Copyright (c) 2015 Ivan Grokhotkov. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + + +#ifndef FLASH_UTILS_H +#define FLASH_UTILS_H + +#ifdef __cplusplus +extern "C" { +#endif + +int SPIEraseBlock(uint32_t block); +int SPIEraseSector(uint32_t sector); +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); + +#define FLASH_SECTOR_SIZE 0x1000 +#define FLASH_BLOCK_SIZE 0x10000 +#define APP_START_OFFSET 0x1000 + +typedef struct { + unsigned char magic; + unsigned char num_segments; + + /* SPI Flash Interface (0 = QIO, 1 = QOUT, 2 = DIO, 0x3 = DOUT) */ + unsigned char flash_mode; + + /* High four bits: 0 = 512K, 1 = 256K, 2 = 1M, 3 = 2M, 4 = 4M, + Low four bits: 0 = 40MHz, 1= 26MHz, 2 = 20MHz, 0xf = 80MHz */ + unsigned char flash_size_freq; + + uint32_t entry; +} image_header_t; + + +typedef struct { + uint32_t address; + uint32_t size; +} section_header_t; + +#ifdef __cplusplus +} +#endif + + +#endif //FLASH_UTILS_H