From fec464b23ff0b751bb3a100c9dad3bec2543d593 Mon Sep 17 00:00:00 2001 From: Mike Date: Wed, 17 Mar 2021 08:41:34 +0000 Subject: [PATCH] Add rBoot partition support (#2258) This PR adds partition table support to the ESP8266 bootloader (rBoot). See #2254 #2251. The ESP8266 flash layout has been reverted to the original Sming 4.2 layout, with the addition of the partition table in the sector before the RF calibration data at end of flash. Update rBoot Update bootloader to read ROM addresses from partition table on boot. These overwrite whatever values have been set in its own configuration, thus avoiding inconsistencies. rBoot has been forked to simplify management. An additional partition subtype has been added for RF calibration data, so existing applications running off the develop branch will need re-building and re-flashing. The partition table may now be freely relocated for the Esp8266 (+ Host) to allow compatibility with existing devices which must be upgrade OTA. Methods have been added to OTA classes to allow update regions to be set using partitions; this is safer than working with raw flash addresses but the underlying mechanism hasn't changed. --- .gitmodules | 2 +- .../Esp8266/Components/esp8266/startup.cpp | 13 +- Sming/Arch/Esp8266/options.json | 18 + Sming/Arch/Esp8266/spiffs-two-roms.hw | 5 +- Sming/Arch/Esp8266/standard.hw | 30 +- Sming/Arch/Esp8266/two-rom-mode.hw | 5 +- Sming/Components/Storage/README.rst | 25 +- .../Storage/Tools/hwconfig/config.py | 8 +- .../Storage/Tools/hwconfig/hwconfig.py | 4 +- .../Storage/Tools/hwconfig/partition.py | 40 +- Sming/Components/Storage/component.mk | 33 +- .../Storage/src/include/Storage/Partition.h | 1 + .../src/include/Storage/PartitionTable.h | 22 +- Sming/Components/rboot/.patches/rboot.patch | 402 ------------------ .../rboot/.patches/rboot/README.rst | 349 --------------- Sming/Components/rboot/README.rst | 16 +- Sming/Components/rboot/component.mk | 8 +- .../include/Data/Stream/RbootOutputStream.h | 21 +- .../rboot/include/Network/RbootHttpUpdater.h | 150 +++++-- Sming/Components/rboot/rboot | 2 +- .../Components/rboot/src/Arch/Host/rboot.cpp | 40 +- .../Components/rboot/src/RbootHttpUpdater.cpp | 54 +-- Sming/Components/spiffs/component.mk | 10 - .../OtaUpgrade/OtaUpgrade/BasicStream.cpp | 41 +- .../OtaUpgrade/OtaUpgrade/BasicStream.h | 29 +- .../OtaUpgrade/OtaUpgrade/EncryptedStream.cpp | 15 +- .../OtaUpgrade/OtaUpgrade/EncryptedStream.h | 15 +- Sming/component.mk | 3 +- Tools/vscode/setup.py | 22 +- Tools/vscode/template/workspace.json | 4 +- docs/source/upgrading/4.2-4.3.rst | 22 +- samples/Basic_Storage/basic_storage.hw | 9 +- samples/Basic_rBoot/app/application.cpp | 25 +- samples/Basic_rBoot/basic_rboot.hw | 7 +- samples/Basic_rBoot/component.mk | 4 - .../app/application.cpp | 2 +- 36 files changed, 449 insertions(+), 1007 deletions(-) delete mode 100644 Sming/Components/rboot/.patches/rboot.patch delete mode 100644 Sming/Components/rboot/.patches/rboot/README.rst diff --git a/.gitmodules b/.gitmodules index 84a5e304aa..85003018ee 100644 --- a/.gitmodules +++ b/.gitmodules @@ -63,7 +63,7 @@ ignore = dirty [submodule "rboot"] path = Sming/Components/rboot/rboot - url = https://github.com/raburton/rboot.git + url = https://github.com/mikee47/rboot ignore = dirty [submodule "spiffs"] path = Sming/Components/spiffs/spiffs diff --git a/Sming/Arch/Esp8266/Components/esp8266/startup.cpp b/Sming/Arch/Esp8266/Components/esp8266/startup.cpp index ef6d1d07d3..ff62f084d2 100644 --- a/Sming/Arch/Esp8266/Components/esp8266/startup.cpp +++ b/Sming/Arch/Esp8266/Components/esp8266/startup.cpp @@ -55,9 +55,8 @@ extern "C" void WEAK_ATTR user_rf_pre_init(void) extern "C" uint32 ICACHE_FLASH_ATTR WEAK_ATTR user_rf_cal_sector_set(void) { - // RF calibration stored in last sector of sysParam - auto sysParam = *Storage::findPartition(Storage::Partition::SubType::Data::sysParam); - return ((sysParam.address() + sysParam.size()) / SPI_FLASH_SEC_SIZE) - 1; + auto rfCal = *Storage::findPartition(Storage::Partition::SubType::Data::rfCal); + return rfCal.address(); } #ifdef SDK_INTERNAL @@ -71,16 +70,14 @@ extern "C" void ICACHE_FLASH_ATTR WEAK_ATTR user_pre_init(void) Storage::initialize(); auto sysParam = *Storage::findPartition(Storage::Partition::SubType::Data::sysParam); + auto rfCal = *Storage::findPartition(Storage::Partition::SubType::Data::rfCal); auto phy = *Storage::findPartition(Storage::Partition::SubType::Data::phy); - // RF calibration stored in last sector of sysParam - auto sysParamSize = sysParam.size() - SPI_FLASH_SEC_SIZE; - static const partition_item_t partitions[] = { {SYSTEM_PARTITION_BOOTLOADER, 0, SPI_FLASH_SEC_SIZE}, {SYSTEM_PARTITION_PHY_DATA, phy.address(), phy.size()}, - {SYSTEM_PARTITION_SYSTEM_PARAMETER, sysParam.address(), sysParamSize}, - {SYSTEM_PARTITION_RF_CAL, sysParam.address() + sysParamSize, SPI_FLASH_SEC_SIZE}, + {SYSTEM_PARTITION_SYSTEM_PARAMETER, sysParam.address(), sysParam.size()}, + {SYSTEM_PARTITION_RF_CAL, rfCal.address(), rfCal.size()}, }; enum flash_size_map sizeMap = system_get_flash_size_map(); diff --git a/Sming/Arch/Esp8266/options.json b/Sming/Arch/Esp8266/options.json index 7e3d545649..dfa35550eb 100644 --- a/Sming/Arch/Esp8266/options.json +++ b/Sming/Arch/Esp8266/options.json @@ -6,5 +6,23 @@ "filename": "$(FLASH_INIT_DATA_VCC)" } } + }, + "alternate": { + "description": "ESP8266 layout with critical partitions at start of flash", + "partition_table_offset": "0x2000", + "partitions": { + "rf_cal": { + "address": "0x3000" + }, + "phy_init": { + "address": "0x4000" + }, + "sys_param": { + "address": "0x5000" + }, + "rom0": { + "address": "0x8000" + } + } } } \ No newline at end of file diff --git a/Sming/Arch/Esp8266/spiffs-two-roms.hw b/Sming/Arch/Esp8266/spiffs-two-roms.hw index 9503891e2a..93e4605b47 100644 --- a/Sming/Arch/Esp8266/spiffs-two-roms.hw +++ b/Sming/Arch/Esp8266/spiffs-two-roms.hw @@ -2,11 +2,14 @@ "name": "Two ROM slots with single SPIFFS", "base_config": "spiffs", "partitions": { + "rom0": { + "subtype": "ota_0" + }, "rom1": { "address": "0x108000", "size": "992K", "type": "app", - "subtype": "ota_0", + "subtype": "ota_1", "filename": "$(RBOOT_ROM_1_BIN)" } } diff --git a/Sming/Arch/Esp8266/standard.hw b/Sming/Arch/Esp8266/standard.hw index 240e56f679..37d6bd7d68 100644 --- a/Sming/Arch/Esp8266/standard.hw +++ b/Sming/Arch/Esp8266/standard.hw @@ -2,7 +2,7 @@ "name": "Standard config with single ROM", "comment": "Should work with any Esp8266 variant", "arch": "Esp8266", - "partition_table_offset": "0x2000", + "partition_table_offset": "self.devices[0].size - 0x6000", "devices": { "spiFlash": { "type": "flash", @@ -12,25 +12,31 @@ } }, "partitions": { + "rom0": { + "address": "0x002000", + "size": "992K", + "type": "app", + "subtype": "factory", + "filename": "$(RBOOT_ROM_0_BIN)" + }, + "rf_cal": { + "address": "self.device.size - 0x5000", + "size": "4K", + "type": "data", + "subtype": "rfcal" + }, "phy_init": { - "address": "0x003000", + "address": "self.device.size - 0x4000", "size": "4K", "type": "data", "subtype": "phy", "filename": "$(FLASH_INIT_DATA)" }, "sys_param": { - "address": "0x004000", - "size": "16K", + "address": "self.device.size - 0x3000", + "size": "12K", "type": "data", "subtype": "sysparam" - }, - "rom0": { - "address": "0x008000", - "size": "992K", - "type": "app", - "subtype": "factory", - "filename": "$(RBOOT_ROM_0_BIN)" } } -} +} \ No newline at end of file diff --git a/Sming/Arch/Esp8266/two-rom-mode.hw b/Sming/Arch/Esp8266/two-rom-mode.hw index 9d6399179f..2be8a10eca 100644 --- a/Sming/Arch/Esp8266/two-rom-mode.hw +++ b/Sming/Arch/Esp8266/two-rom-mode.hw @@ -3,13 +3,14 @@ "base_config": "standard", "partitions": { "rom0": { + "subtype": "ota_0", "size": "480K" }, "rom1": { "address": "0x80000", - "size": "512K", + "size": "488K", "type": "app", - "subtype": "ota_0", + "subtype": "ota_1", "filename": "$(RBOOT_ROM_1_BIN)" } } diff --git a/Sming/Components/Storage/README.rst b/Sming/Components/Storage/README.rst index 8b609fee02..0ab3666e67 100644 --- a/Sming/Components/Storage/README.rst +++ b/Sming/Components/Storage/README.rst @@ -84,6 +84,16 @@ If using this approach, remember to updated your project's ``component.mk`` with and verify the layout is correct using ``make map``. +OTA updates +----------- + +When planning OTA updates please check that the displayed partition map corresponds to your project. +For example, the partition table requires a free sector so must not overlap other partitions. + +Your OTA update process must include a step to write the partition table to the correct location. + +It is not necessary to update the bootloader. See :component:`rboot` for further information. + Custom configurations --------------------- @@ -173,6 +183,19 @@ To customise the hardware configuration for a project, for example 'my_project': This will flash everything: bootloader, partition table and all defined partitions (those with a ``filename`` entry). +.. note:: + + The build system isn't smart enough to track dependencies for partition build targets. + + To rebuild these manually type:: + + make partbuild + + These will be removed when ``make clean`` is run, but you can also clean them separately thus:: + + make part-clean + + Partition maps -------------- @@ -356,7 +379,7 @@ you can take advantage of the partition API to manage them as follows: - Create an instance of your custom device and make a call to :cpp:func:`Storage::registerDevice` in your ``init()`` function (or elsewhere if more appropriate). - + API --- diff --git a/Sming/Components/Storage/Tools/hwconfig/config.py b/Sming/Components/Storage/Tools/hwconfig/config.py index 19bad09f79..67a465bd0c 100644 --- a/Sming/Components/Storage/Tools/hwconfig/config.py +++ b/Sming/Components/Storage/Tools/hwconfig/config.py @@ -46,6 +46,7 @@ def from_name(cls, name): config.load(name) if options != '': config.parse_options(options.split(',')) + config.resolve_expressions() config.partitions.sort() return config @@ -73,6 +74,9 @@ def parse_options(self, options): temp.pop('description', None) self.parse_dict(temp) + def resolve_expressions(self): + self.partitions.offset = eval(str(self.partitions.offset)) + def parse_dict(self, data): base_config = data.pop('base_config', None) if base_config is not None: @@ -89,7 +93,7 @@ def parse_dict(self, data): elif k == 'arch': self.arch = v elif k == 'partition_table_offset': - self.partitions.offset = parse_int(v) + self.partitions.offset = v elif k == 'devices': self.devices.parse_dict(v) elif k == 'comment': @@ -132,7 +136,7 @@ def buildVars(self): return res def verify(self, secure): - self.partitions.verify(self.arch, secure) + self.partitions.verify(self.arch, self.devices[0], secure) def map(self): return partition.Map(self.partitions, self.devices) diff --git a/Sming/Components/Storage/Tools/hwconfig/hwconfig.py b/Sming/Components/Storage/Tools/hwconfig/hwconfig.py index 7c388ff726..e2b3d90ddb 100644 --- a/Sming/Components/Storage/Tools/hwconfig/hwconfig.py +++ b/Sming/Components/Storage/Tools/hwconfig/hwconfig.py @@ -44,7 +44,7 @@ def handle_flashcheck(args, config, part): for e in list: addr, filename = e.split('=') addr = int(addr, 0) - part = config.partitions.find_by_address(addr) + part = config.partitions.find_by_address(config.devices[0], addr) if part is None: raise InputError("No partition contains address 0x%08x" % addr) if part.address != addr: @@ -113,5 +113,5 @@ def main(): try: main() except InputError as e: - print(e, file=sys.stderr) + print("** ERROR! %s" % e, file=sys.stderr) sys.exit(2) diff --git a/Sming/Components/Storage/Tools/hwconfig/partition.py b/Sming/Components/Storage/Tools/hwconfig/partition.py index d0a0e07f1e..18e78435d5 100644 --- a/Sming/Components/Storage/Tools/hwconfig/partition.py +++ b/Sming/Components/Storage/Tools/hwconfig/partition.py @@ -16,11 +16,12 @@ # See the License for the specific language governing permissions and # limitations under the License. -import struct, hashlib, storage, binascii +import struct, hashlib, storage, binascii, copy from common import * MAX_PARTITION_LENGTH = 0xC00 # 3K for partition data (96 entries) leaves 1K in a 4K sector for signature MD5_PARTITION_BEGIN = b"\xEB\xEB" + b"\xFF" * 14 # The first 2 bytes are like magic numbers for MD5 sum +FLASH_SECTOR_SIZE = 0x1000 PARTITION_TABLE_SIZE = 0x1000 # Size of partition table PARTITION_ENTRY_SIZE = 32 @@ -84,6 +85,7 @@ "nvs_keys": 0x04, "efuse": 0x05, "sysparam": 0x40, + "rfcal": 0x41, "esphttpd": 0x80, "fat": 0x81, "spiffs": 0x82, @@ -154,6 +156,10 @@ def offset_str(self): def buildVars(self): dict = {} dict['PARTITION_NAMES'] = " ".join(p.name for p in self) + buildparts = [p for p in self if p.build is not None] + dict['PARTITIONS_WITH_TARGETS'] = " ".join(p.name for p in buildparts) + dict['PARTITION_BUILD_TARGETS'] = " ".join(p.filename for p in buildparts) + for p in self: dict.update(p.buildVars()) return dict @@ -198,19 +204,28 @@ def find_by_name(self, name): return p return None - def find_by_address(self, addr): + def find_by_address(self, device, addr): for p in self: - if p.contains(addr): + if p.device == device and p.contains(addr): return p return None - def verify(self, arch, secure): + def verify(self, arch, spiFlash, secure): """Verify partition layout """ # verify each partition individually for p in self: p.verify(arch, secure) + if self.offset % FLASH_SECTOR_SIZE != 0: + raise InputError("Partition table offset not aligned to flash sector") + + p = self.find_by_address(spiFlash, self.offset) + if p is None: + p = self.find_by_address(spiFlash, self.offset + PARTITION_TABLE_SIZE - 1) + if not p is None: + raise InputError("Partition table conflict with '%s'" % p.name) + # check on duplicate name names = [p.name for p in self] duplicates = set(n for n in names if names.count(n) > 1) @@ -224,7 +239,10 @@ def verify(self, arch, secure): raise InputError("Partition names must be unique") # check for overlaps - minPartitionAddress = self.offset + PARTITION_TABLE_SIZE + if arch == 'Esp32': + minPartitionAddress = self.offset + PARTITION_TABLE_SIZE + else: + minPartitionAddress = 0x00002000 dev = '' last = None for p in self: @@ -345,7 +363,7 @@ def parse_dict(self, data, devices): if k == 'device': self.device = devices.find_by_name(v) elif k == 'address': - self.address = parse_int(v) + self.address = eval(str(v)) elif k == 'size': self.size = parse_int(v) elif k == 'filename': @@ -523,13 +541,17 @@ def add_unused(address, last_end): if address > last_end + 1: add('(unused)', last_end + 1, address - last_end - 1) + partitions = copy.copy(table) + if table.offset == 0: last = None else: - add("Boot Sector", 0, table.offset) - last = add("Partition Table", table.offset, PARTITION_TABLE_SIZE) + last = add('Boot Sector', 0, min(table.offset, partitions[0].address)) + p = Entry(device, 'Partition Table', table.offset, PARTITION_TABLE_SIZE, 0xff, 0xff) + partitions.append(p) + partitions.sort() - for p in table: + for p in partitions: if last is not None: if p.device != last.device: add_unused(last.device.size, last.end()) diff --git a/Sming/Components/Storage/component.mk b/Sming/Components/Storage/component.mk index a6627c056a..986d7fe0d0 100644 --- a/Sming/Components/Storage/component.mk +++ b/Sming/Components/Storage/component.mk @@ -35,10 +35,11 @@ HWCONFIG_TOOL := \ BUILD_BASE=$(BUILD_BASE) \ $(PYTHON) $(PARTITION_TOOLS)/hwconfig/hwconfig.py -ifeq (,$(MAKE_DOCS)$(MAKE_CLEAN)) - -# Generate build variables from hardware configuration HWCONFIG_MK := $(PROJECT_DIR)/$(OUT_BASE)/hwconfig.mk +ifneq (,$(MAKE_DOCS)$(MAKE_CLEAN)) +-include $(HWCONFIG_MK) +else +# Generate build variables from hardware configuration $(shell $(HWCONFIG_TOOL) --quiet expr $(HWCONFIG) $(HWCONFIG_MK) "config.buildVars()") include $(HWCONFIG_MK) ifeq ($(SMING_ARCH_HW),) @@ -47,6 +48,7 @@ else ifneq ($(SMING_ARCH),$(SMING_ARCH_HW)) $(error Hardware configuration is for arch $(SMING_ARCH_HW), does not match SMING_ARCH ($(SMING_ARCH))) endif COMPONENT_CXXFLAGS := -DPARTITION_TABLE_OFFSET=$(PARTITION_TABLE_OFFSET) +COMPONENT_CPPFLAGS := -DPARTITION_TABLE_OFFSET=$(PARTITION_TABLE_OFFSET) # Function to evaluate expression against config HWEXPR := $(HWCONFIG_TOOL) $(if $(PART),--part "$(PART)") expr $(HWCONFIG) - @@ -109,22 +111,25 @@ $(PARTITIONS_BIN): $(HWCONFIG_DEPENDS) # Create build target for a partition # $1 -> Partition name define PartitionTarget -PTARG := $(shell $(HWCONFIG_TOOL) --part $1 expr $(HWCONFIG) - part.filename) -$$(PTARG): +$(PARTITION_$1_FILENAME): $$(Q) $$(MAKE) --no-print-directory $$(shell $$(HWCONFIG_TOOL) --part $1 expr $$(HWCONFIG) - "part.build['target']") PART=$1 -CUSTOM_TARGETS += $$(PTARG) +CUSTOM_TARGETS += $(PARTITION_$1_FILENAME) endef -# Create build targets for all partitions with 'build' property -DEBUG_VARS += PARTITIONS_WITH_TARGETS -PARTITIONS_WITH_TARGETS := $(call HwExpr,(' '.join([part.name for part in filter(lambda part: part.build is not None, config.partitions)]))) - # Must be invoked from project.mk after all Components have been processed # This allows partition definitions to include variables which may not yet be defined define PartitionCreateTargets $(foreach p,$(PARTITIONS_WITH_TARGETS),$(eval $(call PartitionTarget,$p))) endef +.PHONY: buildpart +buildpart: ##Rebuild all partition targets +ifeq (,$(PARTITION_BUILD_TARGETS)) + @echo "No partitions have build targets" +else + $(Q) rm -f $(PARTITION_BUILD_TARGETS) + $(MAKE) $(PARTITION_BUILD_TARGETS) +endif ##@Flashing @@ -192,3 +197,11 @@ flashmap: $(PARTITIONS_BIN) kill_term ##Write partition table to device endif # MAKE_DOCS + +##@Cleaning + +clean: part-clean + +.PHONY: part-clean +part-clean: ##Clean partition targets + $(Q) rm -f $(PARTITION_BUILD_TARGETS) diff --git a/Sming/Components/Storage/src/include/Storage/Partition.h b/Sming/Components/Storage/src/include/Storage/Partition.h index 0c141abad0..9b5badeee1 100644 --- a/Sming/Components/Storage/src/include/Storage/Partition.h +++ b/Sming/Components/Storage/src/include/Storage/Partition.h @@ -59,6 +59,7 @@ XX(nvsKeys, 0x04, "NVS key information") \ XX(eFuseEm, 0x05, "eFuse emulation") \ XX(sysParam, 0x40, "System Parameters") \ + XX(rfCal, 0x41, "RF Calibration") \ XX(espHttpd, 0x80, "ESPHTTPD") \ XX(fat, 0x81, "FAT") \ XX(spiffs, 0x82, "SPIFFS") \ diff --git a/Sming/Components/Storage/src/include/Storage/PartitionTable.h b/Sming/Components/Storage/src/include/Storage/PartitionTable.h index 1dd4ee7b16..5acfc135bd 100644 --- a/Sming/Components/Storage/src/include/Storage/PartitionTable.h +++ b/Sming/Components/Storage/src/include/Storage/PartitionTable.h @@ -45,7 +45,7 @@ class PartitionTable /** * @brief Find partition by name - * @param Name to search for, case-sensitive + * @param Name Name to search for, case-sensitive * @retval Partition * * Names are unique so at most only one match @@ -55,6 +55,26 @@ class PartitionTable return *std::find(begin(), end(), name); } + /** + * @brief Find partition containing the given address + * @param address Address to search for + * @retval Partition + */ + Partition find(uint32_t address) const + { + return *std::find_if(begin(), end(), [address](Partition part) { return part.contains(address); }); + } + + /** + * @brief Find the n'th OTA partition + */ + Partition findOta(uint8_t index) + { + using App = Partition::SubType::App; + auto subtype = App(uint8_t(App::ota0) + index); + return (subtype >= App::ota_min && subtype <= App::ota_max) ? *find(subtype) : Partition{}; + } + Iterator begin() const { return Iterator(mDevice, 0); diff --git a/Sming/Components/rboot/.patches/rboot.patch b/Sming/Components/rboot/.patches/rboot.patch deleted file mode 100644 index b8cd1ce078..0000000000 --- a/Sming/Components/rboot/.patches/rboot.patch +++ /dev/null @@ -1,402 +0,0 @@ -diff --git a/Makefile b/Makefile -index 638a8f7..5176f8f 100644 ---- a/Makefile -+++ b/Makefile -@@ -58,6 +58,19 @@ endif - ifeq ($(RBOOT_IROM_CHKSUM),1) - CFLAGS += -DBOOT_IROM_CHKSUM - endif -+ifneq ($(RBOOT_ROM0_ADDR),) -+ CFLAGS += -DBOOT_ROM0_ADDR=$(RBOOT_ROM0_ADDR) -+endif -+ifneq ($(RBOOT_ROM1_ADDR),) -+ CFLAGS += -DBOOT_ROM1_ADDR=$(RBOOT_ROM1_ADDR) -+endif -+ifneq ($(RBOOT_ROM2_ADDR),) -+ CFLAGS += -DBOOT_ROM2_ADDR=$(RBOOT_ROM2_ADDR) -+endif -+ifeq ($(RBOOT_SILENT),1) -+ CFLAGS += -DBOOT_SILENT=$(RBOOT_SILENT) -+endif -+ - ifneq ($(RBOOT_EXTRA_INCDIR),) - CFLAGS += $(addprefix -I,$(RBOOT_EXTRA_INCDIR)) - endif - -diff --git a/rboot.c b/rboot.c -index d622f97..5e254fa 100644 ---- a/rboot.c -+++ b/rboot.c -@@ -13,6 +13,12 @@ - #define UART_CLK_FREQ (26000000 * 2) - #endif - -+#ifndef BOOT_SILENT -+#define echof(fmt, ...) ets_printf(fmt, ##__VA_ARGS__) -+#else -+#define echof(fmt, ...) -+#endif -+ - static uint32_t check_image(uint32_t readpos) { - - uint8_t buffer[BUFFER_SIZE]; -@@ -257,8 +263,30 @@ static uint8_t calc_chksum(uint8_t *start, uint8_t *end) { - // created on first boot or in case of corruption - static uint8_t default_config(rboot_config *romconf, uint32_t flashsize) { - romconf->count = 2; -+#ifdef BOOT_ROM0_ADDR -+ romconf->roms[0] = BOOT_ROM0_ADDR; -+#else - romconf->roms[0] = SECTOR_SIZE * (BOOT_CONFIG_SECTOR + 1); -+#endif -+ -+#ifdef BOOT_ROM1_ADDR -+ romconf->roms[1] = BOOT_ROM1_ADDR; -+#else - romconf->roms[1] = (flashsize / 2) + (SECTOR_SIZE * (BOOT_CONFIG_SECTOR + 1)); -+#endif -+ -+#if defined(BOOT_BIG_FLASH) && defined(BOOT_GPIO_ENABLED) -+ if(flashsize > 0x200000) { -+ romconf->count += 1; -+#ifdef BOOT_ROM2_ADDR -+ romconf->roms[2] = BOOT_ROM2_ADDR; -+#else -+ romconf->roms[2] = 0x310000; -+#endif -+ romconf->gpio_rom = 2; -+} -+#endif -+ - #ifdef BOOT_GPIO_ENABLED - romconf->mode = MODE_GPIO_ROM; - #endif -@@ -306,100 +334,100 @@ uint32_t NOINLINE find_image(void) { - ets_delay_us(BOOT_DELAY_MICROS); - #endif - -- ets_printf("\r\nrBoot v1.4.2 - richardaburton@gmail.com\r\n"); -+ echof("\r\nrBoot v1.4.2 - richardaburton@gmail.com\r\n"); - - // read rom header - SPIRead(0, header, sizeof(rom_header)); - - // print and get flash size -- ets_printf("Flash Size: "); -+ echof("Flash Size: "); - flag = header->flags2 >> 4; - if (flag == 0) { -- ets_printf("4 Mbit\r\n"); -+ echof("4 Mbit\r\n"); - flashsize = 0x80000; - } else if (flag == 1) { -- ets_printf("2 Mbit\r\n"); -+ echof("2 Mbit\r\n"); - flashsize = 0x40000; - } else if (flag == 2) { -- ets_printf("8 Mbit\r\n"); -+ echof("8 Mbit\r\n"); - flashsize = 0x100000; - } else if (flag == 3 || flag == 5) { -- ets_printf("16 Mbit\r\n"); -+ echof("16 Mbit\r\n"); - #ifdef BOOT_BIG_FLASH - flashsize = 0x200000; - #else - flashsize = 0x100000; // limit to 8Mbit - #endif - } else if (flag == 4 || flag == 6) { -- ets_printf("32 Mbit\r\n"); -+ echof("32 Mbit\r\n"); - #ifdef BOOT_BIG_FLASH - flashsize = 0x400000; - #else - flashsize = 0x100000; // limit to 8Mbit - #endif - } else if (flag == 8) { -- ets_printf("64 Mbit\r\n"); -+ echof("64 Mbit\r\n"); - #ifdef BOOT_BIG_FLASH - flashsize = 0x800000; - #else - flashsize = 0x100000; // limit to 8Mbit - #endif - } else if (flag == 9) { -- ets_printf("128 Mbit\r\n"); -+ echof("128 Mbit\r\n"); - #ifdef BOOT_BIG_FLASH - flashsize = 0x1000000; - #else - flashsize = 0x100000; // limit to 8Mbit - #endif - } else { -- ets_printf("unknown\r\n"); -+ echof("unknown\r\n"); - // assume at least 4mbit - flashsize = 0x80000; - } - - // print spi mode -- ets_printf("Flash Mode: "); -+ echof("Flash Mode: "); - if (header->flags1 == 0) { -- ets_printf("QIO\r\n"); -+ echof("QIO\r\n"); - } else if (header->flags1 == 1) { -- ets_printf("QOUT\r\n"); -+ echof("QOUT\r\n"); - } else if (header->flags1 == 2) { -- ets_printf("DIO\r\n"); -+ echof("DIO\r\n"); - } else if (header->flags1 == 3) { -- ets_printf("DOUT\r\n"); -+ echof("DOUT\r\n"); - } else { -- ets_printf("unknown\r\n"); -+ echof("unknown\r\n"); - } - - // print spi speed -- ets_printf("Flash Speed: "); -+ echof("Flash Speed: "); - flag = header->flags2 & 0x0f; -- if (flag == 0) ets_printf("40 MHz\r\n"); -- else if (flag == 1) ets_printf("26.7 MHz\r\n"); -- else if (flag == 2) ets_printf("20 MHz\r\n"); -- else if (flag == 0x0f) ets_printf("80 MHz\r\n"); -- else ets_printf("unknown\r\n"); -+ if (flag == 0) echof("40 MHz\r\n"); -+ else if (flag == 1) echof("26.7 MHz\r\n"); -+ else if (flag == 2) echof("20 MHz\r\n"); -+ else if (flag == 0x0f) echof("80 MHz\r\n"); -+ else echof("unknown\r\n"); - - // print enabled options - #ifdef BOOT_BIG_FLASH -- ets_printf("rBoot Option: Big flash\r\n"); -+ echof("rBoot Option: Big flash\r\n"); - #endif - #ifdef BOOT_CONFIG_CHKSUM -- ets_printf("rBoot Option: Config chksum\r\n"); -+ echof("rBoot Option: Config chksum\r\n"); - #endif - #ifdef BOOT_GPIO_ENABLED -- ets_printf("rBoot Option: GPIO rom mode (%d)\r\n", BOOT_GPIO_NUM); -+ echof("rBoot Option: GPIO rom mode (%d)\r\n", BOOT_GPIO_NUM); - #endif - #ifdef BOOT_GPIO_SKIP_ENABLED -- ets_printf("rBoot Option: GPIO skip mode (%d)\r\n", BOOT_GPIO_NUM); -+ echof("rBoot Option: GPIO skip mode (%d)\r\n", BOOT_GPIO_NUM); - #endif - #ifdef BOOT_RTC_ENABLED -- ets_printf("rBoot Option: RTC data\r\n"); -+ echof("rBoot Option: RTC data\r\n"); - #endif - #ifdef BOOT_IROM_CHKSUM -- ets_printf("rBoot Option: irom chksum\r\n"); -+ echof("rBoot Option: irom chksum\r\n"); - #endif -- ets_printf("\r\n"); -+ echof("\r\n"); - - // read boot config - SPIRead(BOOT_CONFIG_SECTOR * SECTOR_SIZE, buffer, SECTOR_SIZE); -@@ -410,7 +438,7 @@ uint32_t NOINLINE find_image(void) { - #endif - ) { - // create a default config for a standard 2 rom setup -- ets_printf("Writing default boot config.\r\n"); -+ echof("Writing default boot config.\r\n"); - ets_memset(romconf, 0x00, sizeof(rboot_config)); - romconf->magic = BOOT_CONFIG_MAGIC; - romconf->version = BOOT_CONFIG_VERSION; -@@ -433,10 +461,10 @@ uint32_t NOINLINE find_image(void) { - - if (rtc.next_mode & MODE_TEMP_ROM) { - if (rtc.temp_rom >= romconf->count) { -- ets_printf("Invalid temp rom selected.\r\n"); -+ echof("Invalid temp rom selected.\r\n"); - return 0; - } -- ets_printf("Booting temp rom.\r\n"); -+ echof("Booting temp rom.\r\n"); - temp_boot = 1; - romToBoot = rtc.temp_rom; - } -@@ -447,10 +475,10 @@ uint32_t NOINLINE find_image(void) { - if (perform_gpio_boot(romconf)) { - #if defined(BOOT_GPIO_ENABLED) - if (romconf->gpio_rom >= romconf->count) { -- ets_printf("Invalid GPIO rom selected.\r\n"); -+ echof("Invalid GPIO rom selected.\r\n"); - return 0; - } -- ets_printf("Booting GPIO-selected rom.\r\n"); -+ echof("Booting GPIO-selected rom.\r\n"); - romToBoot = romconf->gpio_rom; - gpio_boot = 1; - #elif defined(BOOT_GPIO_SKIP_ENABLED) -@@ -462,7 +490,7 @@ uint32_t NOINLINE find_image(void) { - #endif - updateConfig = 1; - if (romconf->mode & MODE_GPIO_ERASES_SDKCONFIG) { -- ets_printf("Erasing SDK config sectors before booting.\r\n"); -+ echof("Erasing SDK config sectors before booting.\r\n"); - for (sec = 1; sec < 5; sec++) { - SPIEraseSector((flashsize / SECTOR_SIZE) - sec); - } -@@ -474,7 +502,7 @@ uint32_t NOINLINE find_image(void) { - // gpio/temp boots will have already validated this - if (romconf->current_rom >= romconf->count) { - // if invalid rom selected try rom 0 -- ets_printf("Invalid rom selected, defaulting to 0.\r\n"); -+ echof("Invalid rom selected, defaulting to 0.\r\n"); - romToBoot = 0; - romconf->current_rom = 0; - updateConfig = 1; -@@ -486,14 +514,14 @@ uint32_t NOINLINE find_image(void) { - #ifdef BOOT_GPIO_ENABLED - if (gpio_boot && loadAddr == 0) { - // don't switch to backup for gpio-selected rom -- ets_printf("GPIO boot rom (%d) is bad.\r\n", romToBoot); -+ echof("GPIO boot rom (%d) is bad.\r\n", romToBoot); - return 0; - } - #endif - #ifdef BOOT_RTC_ENABLED - if (temp_boot && loadAddr == 0) { - // don't switch to backup for temp rom -- ets_printf("Temp boot rom (%d) is bad.\r\n", romToBoot); -+ echof("Temp boot rom (%d) is bad.\r\n", romToBoot); - // make sure rtc temp boot mode doesn't persist - rtc.next_mode = MODE_STANDARD; - rtc.chksum = calc_chksum((uint8_t*)&rtc, (uint8_t*)&rtc.chksum); -@@ -504,7 +532,7 @@ uint32_t NOINLINE find_image(void) { - - // check we have a good rom - while (loadAddr == 0) { -- ets_printf("Rom %d at %x is bad.\r\n", romToBoot, romconf->roms[romToBoot]); -+ echof("Rom %d at %x is bad.\r\n", romToBoot, romconf->roms[romToBoot]); - // for normal mode try each previous rom - // until we find a good one or run out - updateConfig = 1; -@@ -512,7 +540,7 @@ uint32_t NOINLINE find_image(void) { - if (romToBoot < 0) romToBoot = romconf->count - 1; - if (romToBoot == romconf->current_rom) { - // tried them all and all are bad! -- ets_printf("No good rom available.\r\n"); -+ echof("No good rom available.\r\n"); - return 0; - } - loadAddr = check_image(romconf->roms[romToBoot]); -@@ -543,7 +571,7 @@ uint32_t NOINLINE find_image(void) { - system_rtc_mem(RBOOT_RTC_ADDR, &rtc, sizeof(rboot_rtc_data), RBOOT_RTC_WRITE); - #endif - -- ets_printf("Booting rom %d at %x, load addr %x.\r\n", romToBoot, romconf->roms[romToBoot], loadAddr); -+ echof("Booting rom %d at %x, load addr %x.\r\n", romToBoot, romconf->roms[romToBoot], loadAddr); - // copy the loader to top of iram - ets_memcpy((void*)_text_addr, _text_data, _text_len); - // return address to load from -diff --git a/appcode/rboot-api.c b/appcode/rboot-api.c -index eb4d028..bf276a7 100644 ---- a/appcode/rboot-api.c -+++ b/appcode/rboot-api.c -@@ -9,7 +9,7 @@ - #include - // c_types.h needed for spi_flash.h - #include --#include -+#include - - #include "rboot-api.h" - -@@ -33,7 +33,7 @@ static uint8_t calc_chksum(uint8_t *start, uint8_t *end) { - // get the rboot config - rboot_config ICACHE_FLASH_ATTR rboot_get_config(void) { - rboot_config conf; -- spi_flash_read(BOOT_CONFIG_SECTOR * SECTOR_SIZE, (uint32_t*)&conf, sizeof(rboot_config)); -+ flashmem_read(&conf, BOOT_CONFIG_SECTOR * SECTOR_SIZE, sizeof(rboot_config)); - return conf; - } - -@@ -43,7 +43,7 @@ rboot_config ICACHE_FLASH_ATTR rboot_get_config(void) { - // updates checksum automatically (if enabled) - bool ICACHE_FLASH_ATTR rboot_set_config(rboot_config *conf) { - uint8_t *buffer; -- buffer = (uint8_t*)pvPortMalloc(SECTOR_SIZE, 0, 0); -+ buffer = (uint8_t*)os_malloc(SECTOR_SIZE); - if (!buffer) { - //os_printf("No ram!\r\n"); - return false; -@@ -53,12 +53,12 @@ bool ICACHE_FLASH_ATTR rboot_set_config(rboot_config *conf) { - conf->chksum = calc_chksum((uint8_t*)conf, (uint8_t*)&conf->chksum); - #endif - -- spi_flash_read(BOOT_CONFIG_SECTOR * SECTOR_SIZE, (uint32_t*)((void*)buffer), SECTOR_SIZE); -+ flashmem_read(buffer, BOOT_CONFIG_SECTOR * SECTOR_SIZE, SECTOR_SIZE); - memcpy(buffer, conf, sizeof(rboot_config)); -- spi_flash_erase_sector(BOOT_CONFIG_SECTOR); -- spi_flash_write(BOOT_CONFIG_SECTOR * SECTOR_SIZE, (uint32_t*)((void*)buffer), SECTOR_SIZE); -+ flashmem_erase_sector(BOOT_CONFIG_SECTOR); -+ flashmem_write(buffer, BOOT_CONFIG_SECTOR * SECTOR_SIZE, SECTOR_SIZE); - -- vPortFree(buffer, 0, 0); -+ os_free(buffer); - return true; - } - -@@ -103,7 +103,7 @@ bool ICACHE_FLASH_ATTR rboot_write_end(rboot_write_status *status) { - - // function to do the actual writing to flash - // call repeatedly with more data (max len per write is the flash sector size (4k)) --bool ICACHE_FLASH_ATTR rboot_write_flash(rboot_write_status *status, uint8_t *data, uint16_t len) { -+bool ICACHE_FLASH_ATTR rboot_write_flash(rboot_write_status *status, const uint8_t *data, uint16_t len) { - - bool ret = false; - uint8_t *buffer; -@@ -114,7 +114,7 @@ bool ICACHE_FLASH_ATTR rboot_write_flash(rboot_write_status *status, uint8_t *da - } - - // get a buffer -- buffer = (uint8_t *)pvPortMalloc(len + status->extra_count, 0, 0); -+ buffer = (uint8_t *)os_malloc(len + status->extra_count); - if (!buffer) { - //os_printf("No ram!\r\n"); - return false; -@@ -141,18 +141,18 @@ bool ICACHE_FLASH_ATTR rboot_write_flash(rboot_write_status *status, uint8_t *da - lastsect = ((status->start_addr + len) - 1) / SECTOR_SIZE; - while (lastsect > status->last_sector_erased) { - status->last_sector_erased++; -- spi_flash_erase_sector(status->last_sector_erased); -+ flashmem_erase_sector(status->last_sector_erased); - } - - // write current chunk - //os_printf("write addr: 0x%08x, len: 0x%04x\r\n", status->start_addr, len); -- if (spi_flash_write(status->start_addr, (uint32_t *)((void*)buffer), len) == SPI_FLASH_RESULT_OK) { -+ if (flashmem_write(buffer, status->start_addr, len) == len) { - ret = true; - status->start_addr += len; - } - //} - -- vPortFree(buffer, 0, 0); -+ os_free(buffer); - return ret; - } - -diff --git a/appcode/rboot-api.h b/appcode/rboot-api.h -index a98c209..ec4f0e5 100644 ---- a/appcode/rboot-api.h -+++ b/appcode/rboot-api.h -@@ -93,7 +93,7 @@ bool ICACHE_FLASH_ATTR rboot_write_end(rboot_write_status *status); - * of OTA data is received over the network. - * @note Call rboot_write_init before calling this function to get the rboot_write_status structure - */ --bool ICACHE_FLASH_ATTR rboot_write_flash(rboot_write_status *status, uint8_t *data, uint16_t len); -+bool ICACHE_FLASH_ATTR rboot_write_flash(rboot_write_status *status, const uint8_t *data, uint16_t len); - - #ifdef BOOT_RTC_ENABLED - /** @brief Get rBoot status/control data from RTC data area - diff --git a/Sming/Components/rboot/.patches/rboot/README.rst b/Sming/Components/rboot/.patches/rboot/README.rst deleted file mode 100644 index e365cdb1f0..0000000000 --- a/Sming/Components/rboot/.patches/rboot/README.rst +++ /dev/null @@ -1,349 +0,0 @@ -================================================== -rBoot - An open source boot loader for the ESP8266 -================================================== - -by Richard A Burton, richardaburton@gmail.com -http://richard.burtons.org/ - -rBoot is designed to be a flexible open source boot loader, a -replacement for the binary blob supplied with the SDK. It has the -following advantages over the Espressif loader: - -- Open source (written in C). -- Supports up to 256 roms. -- Roms can be variable size. -- Able to test multiple roms to find a valid backup (without - resetting). -- Flash layout can be changed on the fly (with care and appropriately - linked rom images). -- GPIO support for rom selection. -- Wastes no stack space (SDK boot loader uses 144 bytes). -- Documented config structure to allow easy editing from user code. -- Can validate .irom0.text section with checksum. -- Temporary next-boot rom selection. - -Limitations -=========== - -The ESP8266 can only map 8Mbits (1MB) of flash to memory, but which -8Mbits to map is selectable. This allows individual roms to be up to 1MB -in size, so long as they do not straddle an 8Mbit boundary on the flash. -This means you could have four 1MB roms or 8 512KB roms on a 32Mbit -flash (such as on the ESP-12), or a combination. Note, however, that you -could not have, for example, a 512KB rom followed immediately by a 1MB -rom because the 2nd rom would then straddle an 8MBit boundary. By -default support for using more than the first 8Mbit of the flash is -disabled, because it requires several steps to get it working. See below -for instructions. - -Building -======== - -A Makefile is included, which should work with the gcc xtensa cross -compiler. There are two source files, the first is compiled and included -as data in the second. When run this code is copied to memory and -executed (there is a good reason for this, see my blog for an -explanation). The make file will handle this for you, but you’ll need my -esptool2 (see github). - -To use the Makefile set ``SDK_BASE`` to point to the root of the -Espressif SDK and either set ``XTENSA_BINDIR`` to the gcc xtensa bin -directory or include it in your ``PATH``. These can be set as -environment variables or by editing the Makefile. - -Two small assembler stub functions allow the bootloader to launch the -user code without reserving any space on the stack (while the SDK boot -loader uses 144 bytes). This compiles fine with GCC, but if you use -another compiler and it will not compile/work for you then uncomment the -``#define BOOT_NO_ASM`` in ``rboot.h`` to use a C version of these -functions (this uses 32 bytes). - -Tested with SDK v2.2 and GCC v4.8.5. - -Installation -============ - -Simply write rboot.bin to the first sector of the flash. Remember to set -your flash size correctly with your chosen flash tool (e.g. for -esptool.py use the ``-fs`` option). When run rBoot will create it’s own -config at the start of sector two for a simple two rom system. You can -can then write your two roms to flash addresses ``0x2000`` and (half -chip size + ``0x2000``). E.g. for 8Mbit flash: -``esptool.py write_flash -fs 8m 0x0000 rboot.bin 0x2000 user1.bin 0x82000 user2.bin`` - -Note: your device may need other options specified. E.g. The nodemcu -devkit v1.0 (commonly, but incorrectly, sold as v2) also needs the -``-fm dio`` option. - -For more interesting rom layouts you’ll need to write an rBoot config -sector manually, see next step. - -The two testload bin files can be flashed in place of normal user roms -for testing rBoot. You do not need these for normal use. - -rBoot Config -============ - -:: - - typedef struct { - uint8_t magic; // our magic - uint8_t version; // config struct version - uint8_t mode; // boot loader mode - uint8_t current_rom; // currently selected rom - uint8_t gpio_rom; // rom to use for gpio boot - uint8_t count; // number of roms in use - uint8_t unused[2]; // padding - uint32_t roms[MAX_ROMS]; // flash addresses of the roms - #ifdef BOOT_CONFIG_CHKSUM - uint8_t chksum; // boot config chksum - #endif - } rboot_config; - -Write a config structure as above to address ``0x1000`` on the flash. If -you want more than 4 roms (default) just increase MAX_ROMS when you -compile rBoot. Think about how you intend to layout your flash before -you start! Rom addresses must be sector aligned i.e start on a multiple -of 4096. - -- ``magic`` should have value ``0xe1`` (defined as - ``BOOT_CONFIG_MAGIC``). -- ``version`` is used in case the config structure changes after - deployment. It is defined as ``0x01`` (``BOOT_CONFIG_VERSION``). I - don’t intend to increase this, but you should if you choose to - reflash the bootloader after deployment and the config structure has - changed. -- ``mode`` can be ``0x00`` (``MODE_STANDARD``) or ``0x01`` - (``MODE_GPIO_ROM``). See below for an explanation of - ``MODE_GPIO_ROM``. There is also an optional extra mode flag ``0x04`` - (``MODE_GPIO_ERASES_SDKCONFIG``), see below for details. -- ``current_rom`` is the rom to boot, numbered ``0`` to ``count-1``. -- ``gpio_rom`` is the rom to boot when the GPIO is triggered at boot. -- ``count`` is the number of roms available (may be less than - ``MAX_ROMS``, but not more). -- ``unused[2]`` is padding so the ``uint32_t`` rom addresses are 4 - bytes aligned. -- ``roms`` is the array of flash address for the roms. The default - generated config will contain two entries: ``0x00002000`` and - ``0x00082000``. -- ``chksum`` (if enabled, not by deafult) should be the xor of ``0xef`` - followed by each of the bytes of the config structure up to (but - obviously not including) the chksum byte itself. - -Default config -============== - -A default config sector will be created on boot if one does not exists, -or if an existing config is corrupted, and the default rom will be set -to rom 0. If you want to have a very customised config for which the -default would not be suitable, you can override the implementation in -the ``rboot.h`` header file. See the comments and example code in -``rboot.h`` for more information. - -GPIO boot mode -============== - -.. envvar:: RBOOT_GPIO_ENABLED - -If rBoot is compiled with ``BOOT_GPIO_ENABLED`` set in ``rboot.h`` (or -``RBOOT_GPIO_ENABLED`` set in the Makefile), then GPIO boot -functionality will be included in the rBoot binary. The feature can then -be enabled by setting the rboot_config ``mode`` field to -``MODE_GPIO_ROM``. You must also set ``gpio_rom`` in the config to -indicate which rom to boot when the GPIO is activated at boot. - -If the GPIO input pin reads high at boot then rBoot will start the -currently selected normal or temp rom (as appropriate). However if the -GPIO is pulled low then the rom indicated in config option ``gpio_rom`` -is started instead. - -The default GPIO is 16, but this can be overriden in the Makefile -(``RBOOT_GPIO_NUMBER``) or ``rboot.h`` (``BOOT_GPIO_NUM``). If GPIOs -other than 16 are used, the internal pullup resistor is enabled before -the pin is read and disabled immediately afterwards. For pins that -default on reset to configuration other than GPIO input, the pin mode is -changed to input when reading but changed back before rboot continues. - -After a GPIO boot the ``current_rom`` field will be updated in the -config, so the GPIO booted rom should change this again if required. - -GPIO boot skip mode -=================== - -.. envvar:: RBOOT_GPIO_SKIP_ENABLED - -If rBoot is compiled with ``BOOT_GPIO_SKIP_ENABLED`` set in ``rboot.h`` -(or ``RBOOT_GPIO_SKIP_ENABLED`` set in the Makefile), then a GPIO can be -used to skip to the next rom at boot. The feature must then be enabled -by setting the rboot_config ‘mode’ field to ``MODE_GPIO_SKIP``. This -means you do not need to have a dedicated GPIO boot rom. If you have a -rom that is technically good (valid checksum, etc.) but has operational -problems, e.g. wifi doesn’t work or it crashes on boot, rBoot will not -be able to detect that and switch rom automatically. In this scenario -rebooting the device while pulling the GPIO low will force rBoot to skip -this rom and try the next one instead. In a simple two rom setup this -simply toggles booting of the other rom. - -``RBOOT_GPIO_SKIP_ENABLED`` and ``RBOOT_GPIO_ENABLED`` cannot be used at -the same time. ``BOOT_GPIO_NUM`` is used to select the GPIO pin, as with -``RBOOT_GPIO_ENABLED``. - -Erasing SDK configuration on GPIO boot (rom or skip mode) -========================================================= - -If you set the ``MODE_GPIO_ERASES_SDKCONFIG`` flag in the configuration -like this: ``conf.mode = MODE_GPIO_ROM|MODE_GPIO_ERASES_SDKCONFIG``; -then a GPIO boot will also the erase the Espressif SDK persistent -settings store in the final 16KB of flash. This includes removing -calibration constants, saved SSIDs, etc. - -Note that ``MODE_GPIO_ERASES_SDKCONFIG`` is a flag, so it has to be set -as well as ``MODE_GPIO_ROM`` to take effect. - -Linking user code -================= - -Each rom will need to be linked with an appropriate linker file, -specifying where it will reside on the flash. If you are only flashing -one rom to multiple places on the flash it must be linked multiple times -to produce the set of rom images. This is the same as with the SDK -loader. - -Because there are endless possibilities for layout with this loader I -don’t supply sample linker files. Instead I’ll tell you how to make -them. - -For each rom slot on the flash take a copy of the ``eagle.app.v6.ld`` -linker script from the sdk. You then need to modify just one line in it -for each rom: -``irom0_0_seg : org = 0x40240000, len = 0x3C000`` - -Change the org address to be ``0x40200000`` (base memory mapped location -of the flash) + flash address + ``0x10`` (offset of data after the -header). The logical place for your first rom is the third sector, -address ``0x2000``. ``0x40200000 + 0x2000 + 0x10 = 0x40202010`` If you -use the default generated config the loader will expect to find the -second rom at flash address half-chip-size + ``0x2000`` -(e.g. ``0x82000`` on an 8MBit flash) so the ``irom0_0_seg`` should be: -``0x40200000 + 0x82000 + 0x10 = 0x40282010`` Due to the limitation of -mapped flash (max 8MBit) if you use a larger chip and do not have big -flash support enabled the second rom in the default config will still be -placed at ``0x082000``, not truly half-chip-size + ``0x2000``. Ideally -you should also adjust the len to help detect over sized sections at -link time, but more important is the overall size of the rom which you -need to ensure fits in the space you have allocated for it in your flash -layout plan. - -Then simply compile and link as you would normally for OTA updates with -the SDK boot loader, except using the linker scripts you’ve just -prepared rather than the ones supplied with the SDK. Remember when -building roms to create them as ‘new’ type roms (for use with SDK boot -loader v1.2+). Or if using my esptool2 use the ``-boot2`` option. Note: -the test loads included with rBoot are built with ``-boot0`` because -they do not contain a ``.irom0.text`` section (and so the value of -``irom0_0_seg`` in the linker file is irrelevant to them) but ‘normal’ -user apps always do. - -irom checksum -============= - -The SDK boot loader checksum only covers sections loaded into ram (data -and some code). Most of the SDK and user code remains on the flash and -that is not included in the checksum. This means you could attempt to -boot a corrupt rom and, because it looks ok to the boot loader, there -will be no attempt to switch to a backup rom. rBoot improves on this by -allowing the ``.irom0.text`` section to be included in the checksum. To -enable this uncomment ``#define BOOT_IROM_CHKSUM`` in ``rboot.h`` and -build your roms with esptool2 using the ``-iromchksum`` option. - -.. _big_flash_support: - -Big flash support -================= - -This only needs to be enabled if you wish to be able to memory map more -than the first 8MBit of the flash. Note you can still only map 8Mbit at -a time. Use this if you want to have multiple 1MB roms, or more smaller -roms than will fit in 8Mbits. If you have a large flash but only need, -for example, two 512KB roms you do not need to enable this mode. - -Support in rBoot is enabled by uncommenting the -``#define BOOT_BIG_FLASH`` in ``rboot.h``. - -Thinking about your linker files is either simpler or more complicated, -depending on your usage of the flash. If you intend to use multiple 1MB -roms you will only need one linker file and you only need to link once -for OTA updates. Although when you perform an OTA update the rom will be -written to a different position on the flash, each 8Mbit of flash is -mapped (separately) to ``0x40200000``. So when any given rom is run the -code will appear at the same place in memory regardless of where it is -on the flash. Your base address for the linker would be ``0x40202010``. -(Actually all but the first rom could base at ``0x40200010`` (because -they don’t need to leave space for rBoot and config) but then you’re -just making it more complicated again!) - -If you wanted eight 512KB roms you would need two linker files - one for -the first half of any given 8Mbits of flash and another for the second -half. Just remember you are really laying out within a single 8MBit -area, which can then be replicated multiple times on the flash. - -Now the clever bit - rBoot needs to hijack the memory mapping code to -select which 8Mbits gets mapped. There is no API for this, but we can -override the SDK function. First we need to slightly modify the SDK -library ``libmain.a``, like so: - -:: - - xtensa-lx106-elf-objcopy -W Cache_Read_Enable_New libmain.a libmain2.a - -This produces a version of libmain with a ‘weakened’ -``Cache_Read_Enable_New`` function, which we can then override with our -own. Modify your Makefile to link against the library ``main2`` instead -of ``main``. - -Next add ``rboot-bigflash.c`` (from the ``appcode`` directory) & -``rboot.h`` to your project - this adds the replacement -``Cache_Read_Enable_New`` to your code. - -Getting gcc to apply the override correctly can be slightly tricky (I’m -not sure why, it shouldn’t be). One option is to add -``-u Cache_Read_Enable_New`` to your ``LD_FLAGS`` and change the order -of objects on the LD command so your ``objects/.a`` file is before the -libraries. Another way that seems easier was to -``#include rboot-bigflash.c`` into the main .c file, rather than -compiling it to a separate object file. I can’t make any sense of that, -but I suggest you uncomment the message in the ``Cache_Read_Enable_New`` -function when you first build with it, to make sure you are getting your -version into the rom. - -Now when rBoot starts your rom, the SDK code linked in it that normally -performs the memory mapping will delegate part of that task to rBoot -code (linked in your rom, not in rBoot itself) to choose which part of -the flash to map. - -Temporary boot option and rBoot<–>app communication -=================================================== - -.. envvar:: RBOOT_RTC_ENABLED - -To enable communication between rBoot and your app you should enable the -``BOOT_RTC_ENABLED`` option in ``rboot.h``. rBoot will then use the RTC -data area to pass a structure with boot information which can be read by -the app. This will allow the app to determine the boot mode (normal, -temporary or GPIO) and the booted rom (even if it is a tempoary boot). -Your app can also update this structure to communicate with rBoot when -the device is next rebooted, e.g. to instruct it to temporarily boot a -different rom to the one saved in the config. See the api documentation -and/or the rBoot sample project for more details. Note: the message -“don’t use rtc mem data”, commonly seen on startup, comes from the sdk -and is not related to this rBoot feature. - -Integration into other frameworks -================================= - -If you wish to integrate rBoot into a development framework (e.g. Sming) -you can set the define ``RBOOT_INTEGRATION`` and at compile time the -file ``rboot-integration.h`` will be included into the source. This -should allow you to set some platform specific options without having to -modify the source of rBoot which makes it easier to integrate and -maintain. diff --git a/Sming/Components/rboot/README.rst b/Sming/Components/rboot/README.rst index 50cd239c2a..95ddeb0203 100644 --- a/Sming/Components/rboot/README.rst +++ b/Sming/Components/rboot/README.rst @@ -10,10 +10,24 @@ pre-configured flash memory addresses, called "slots". Sming supports up to thre .. note:: With Sming 4.3 partitions are used to manage flash storage. - A "slot" refers to a specific application partition, namely ``rom0``, ``rom1`` or ``rom2``. + A "slot" refers to a specific application partition, typically ``rom0``, ``rom1`` or ``rom2``. The location or size of these partitions is determined by the :ref:`hardware_config`. + The bootloader has been modified to use the partition table as reference, identifying slots + by the partition sub-type. + + Where systems are to be updated Over the Air (OTA) at least two application partitions are required. + The bootloader identifies these by their partition subtype: ``slot #0`` -> ``App/Ota_0``, ``slot #1`` -> ``App/Ota_1``, etc. + + Fixed applications without OTA capability use a single application image. + This must be the ``App/Factory`` partition type, and corresponds to ``slot #0``. + + At startup, the bootloader will use the partition table to locate the application image. + It will also ensure that the ROM slot information in the boot configuration is consistent, + and update it if necessary. + + .. attention:: Make sure that your slots do not extend beyond a 1MB boundary and diff --git a/Sming/Components/rboot/component.mk b/Sming/Components/rboot/component.mk index 814296d7d5..96f9441e53 100644 --- a/Sming/Components/rboot/component.mk +++ b/Sming/Components/rboot/component.mk @@ -125,7 +125,11 @@ endif COMPONENT_CXXFLAGS += \ -DRBOOT_ROM0_ADDR=$(RBOOT_ROM0_ADDR) \ - -DRBOOT_ROM1_ADDR=$(RBOOT_ROM1_ADDR) + -DRBOOT_ROM1_ADDR=$(RBOOT_ROM1_ADDR) \ + -DPARTITION_TABLE_OFFSET=$(PARTITION_TABLE_OFFSET) + +COMPONENT_CFLAGS += \ + -DPARTITION_TABLE_OFFSET=$(PARTITION_TABLE_OFFSET) ifdef RBOOT_EMULATION FLASH_BOOT_CHUNKS = 0x00000=$(BLANK_BIN) @@ -136,7 +140,7 @@ export RBOOT_ROM1_ADDR RBOOT_BIN := $(FW_BASE)/rboot.bin CUSTOM_TARGETS += $(RBOOT_BIN) $(RBOOT_BIN): - $(Q) $(MAKE) -C $(RBOOT_DIR)/rboot $(RBOOT_CFLAGS) + $(Q) $(MAKE) -C $(RBOOT_DIR)/rboot PARTITION_TABLE_OFFSET=$(PARTITION_TABLE_OFFSET) $(RBOOT_CFLAGS) EXTRA_LDFLAGS := -u Cache_Read_Enable_New diff --git a/Sming/Components/rboot/include/Data/Stream/RbootOutputStream.h b/Sming/Components/rboot/include/Data/Stream/RbootOutputStream.h index 30c8b85db8..1873d01885 100644 --- a/Sming/Components/rboot/include/Data/Stream/RbootOutputStream.h +++ b/Sming/Components/rboot/include/Data/Stream/RbootOutputStream.h @@ -17,6 +17,7 @@ #include #include +#include /** * @brief Write-only stream type used during rBoot firmware updates @@ -25,13 +26,23 @@ class RbootOutputStream : public ReadWriteStream { public: /** + * @brief Construct a stream using raw flash address/size * @param startAddress the start address on the storage media * @param maxLength the maximum allowed length of the rom. Use 0 if unlimited. + * @note This should be avoided, use partition where possible */ RbootOutputStream(uint32_t startAddress, size_t maxLength = 0) : startAddress(startAddress), maxLength(maxLength) { } + /** + * @brief Construct a stream for the given partition + * @param partition + */ + RbootOutputStream(Storage::Partition partition) : startAddress(partition.address()), maxLength(partition.size()) + { + } + virtual ~RbootOutputStream() { close(); @@ -77,11 +88,11 @@ class RbootOutputStream : public ReadWriteStream } protected: - bool initialized = false; - rboot_write_status rBootWriteStatus = {}; - size_t written = 0; // << the number of written bytes - uint32_t startAddress = 0; // << the start address on the storage - size_t maxLength = 0; // << maximum allowed length + bool initialized{false}; + rboot_write_status rBootWriteStatus{}; + size_t written{0}; // << the number of written bytes + uint32_t startAddress{0}; // << the start address on the storage + size_t maxLength{0}; // << maximum allowed length protected: virtual bool init(); diff --git a/Sming/Components/rboot/include/Network/RbootHttpUpdater.h b/Sming/Components/rboot/include/Network/RbootHttpUpdater.h index c7ed16a43c..f8328ed764 100644 --- a/Sming/Components/rboot/include/Network/RbootHttpUpdater.h +++ b/Sming/Components/rboot/include/Network/RbootHttpUpdater.h @@ -18,32 +18,101 @@ #include #include "../Data/Stream/RbootOutputStream.h" -#define NO_ROM_SWITCH 0xff +/** + * @brief Magic value for ROM slot indicating slot won't change after successful OTA + */ +constexpr uint8_t NO_ROM_SWITCH{0xff}; class RbootHttpUpdater; -typedef Delegate OtaUpdateDelegate; - -struct RbootHttpUpdaterItem { - String url; - uint32_t targetOffset; - size_t size; // << max allowed size - RbootOutputStream* stream = nullptr; // (optional) output stream to use. -}; +using OtaUpdateDelegate = Delegate; class RbootHttpUpdater : protected HttpClient { public: - virtual ~RbootHttpUpdater() + struct Item { + String url; + uint32_t targetOffset; + size_t size; // << max allowed size + RbootOutputStream* stream{nullptr}; // (optional) output stream to use. + + Item(String url, uint32_t targetOffset, size_t size, RbootOutputStream* stream) + : url(url), targetOffset(targetOffset), size(size), stream(stream) + { + } + + ~Item() + { + delete stream; + } + + RbootOutputStream* getStream() + { + if(stream == nullptr) { + stream = new RbootOutputStream(targetOffset, size); + } + return stream; + } + }; + + class ItemList : public Vector { - cleanup(); + public: + bool addNew(Item* it) + { + if(addElement(it)) { + return true; + } + delete it; + return false; + } + }; + + /** + * @brief Add an item to update + * @param offset + * @param firmwareFileUrl + * @param maxSize + * @retval bool + * @note Use the `Partition` overload where possible + */ + bool addItem(uint32_t offset, const String& firmwareFileUrl, size_t maxSize = 0) + { + return items.addNew(new Item{firmwareFileUrl, offset, maxSize, nullptr}); + } + + /** + * @brief Add an item to update + * @param firmwareFileUrl + * @param partition Target partition to write + * @retval bool + */ + bool addItem(const String& firmwareFileUrl, Storage::Partition partition) + { + return addItem(partition.address(), firmwareFileUrl, partition.size()); } - bool addItem(uint32_t offset, const String& firmwareFileUrl, size_t maxSize = 0); - bool addItem(const String& firmwareFileUrl, RbootOutputStream* stream = nullptr); + /** + * @brief Add an item to update use a pre-constructed stream + * @param firmwareFileUrl + * @param stream + * @retval bool + */ + bool addItem(const String& firmwareFileUrl, RbootOutputStream* stream) + { + if(stream == nullptr) { + return false; + } + + return items.addNew(new Item{firmwareFileUrl, stream->getStartAddress(), stream->getMaxLength(), stream}); + } void start(); + /** + * @brief On completion, switch to the given ROM slot + * @param romSlot specify NO_ROM_SWITCH (the default) to cancel any previously set switch + */ void switchToRom(uint8_t romSlot) { this->romSlot = romSlot; @@ -59,11 +128,13 @@ class RbootHttpUpdater : protected HttpClient this->updateDelegate = reqUpdateDelegate; } - /* Sets the base request that can be used to pass - * - default request parameters, like request headers... - * - default SSL options - * - default SSL fingeprints - * - default SSL client certificates + /** + * @brief Sets the base request that can be used to pass + * + * - default request parameters, like request headers... + * - default SSL options + * - default SSL fingeprints + * - default SSL client certificates * * @param request */ @@ -72,10 +143,21 @@ class RbootHttpUpdater : protected HttpClient baseRequest = request; } - // Allow reading items - RbootHttpUpdaterItem getItem(unsigned int index) + /** + * @brief Allow reading items + * @deprecated Access list directly using `getItems()` + */ + const Item& getItem(unsigned int index) const SMING_DEPRECATED + { + return items[index]; + } + + /** + * @brief Allow read access to item list + */ + const ItemList& getItems() const { - return items.elementAt(index); + return items; } protected: @@ -86,24 +168,16 @@ class RbootHttpUpdater : protected HttpClient virtual int updateComplete(HttpConnection& client, bool success); protected: - Vector items; - int currentItem = 0; - rboot_write_status rbootWriteStatus; - uint8_t romSlot = NO_ROM_SWITCH; - OtaUpdateDelegate updateDelegate = nullptr; - - HttpRequest* baseRequest = nullptr; - -private: - void cleanup() - { - for(unsigned i = 0; i < items.count(); i++) { - delete items[i].stream; - items[i].stream = nullptr; - } - items.clear(); - } + ItemList items; + OtaUpdateDelegate updateDelegate; + HttpRequest* baseRequest{nullptr}; + uint8_t romSlot{NO_ROM_SWITCH}; + uint8_t currentItem{0}; + rboot_write_status rbootWriteStatus{}; }; /** @deprecated Use `RbootHttpUpdater` */ typedef RbootHttpUpdater rBootHttpUpdate SMING_DEPRECATED; + +/** @deprecated Use 'auto' in expressions or `RbootHttpUpdater::Item` */ +typedef RbootHttpUpdater::Item RBootHttpUpdaterItem SMING_DEPRECATED; diff --git a/Sming/Components/rboot/rboot b/Sming/Components/rboot/rboot index 614f33685d..4ad3ba2a8f 160000 --- a/Sming/Components/rboot/rboot +++ b/Sming/Components/rboot/rboot @@ -1 +1 @@ -Subproject commit 614f33685d0dd990fc4202f2409b0d2365eeaef3 +Subproject commit 4ad3ba2a8f6d48d8ab90e0e7c67d42cac49f4da3 diff --git a/Sming/Components/rboot/src/Arch/Host/rboot.cpp b/Sming/Components/rboot/src/Arch/Host/rboot.cpp index 96c5719d88..3eec026938 100644 --- a/Sming/Components/rboot/src/Arch/Host/rboot.cpp +++ b/Sming/Components/rboot/src/Arch/Host/rboot.cpp @@ -13,6 +13,16 @@ #include #include +static uint32_t SPIRead(uint32_t addr, void* outptr, uint32_t len) +{ + return (flashmem_read(outptr, addr, len) == len) ? 0 : 1; +} + +#define SPIEraseSector(sector) flashmem_erase_sector(sector) +#define echof(fmt, ...) host_printf(fmt, ##__VA_ARGS__) + +#include + /* * Called from emulator startup code after flash has been initialised. */ @@ -20,18 +30,14 @@ void host_init_bootloader() { rboot_config romconf = rboot_get_config(); - bool init; - // fresh install or old version? + bool init = false; if(romconf.magic != BOOT_CONFIG_MAGIC) { hostmsg("MAGIC mismatch"); init = true; } else if(romconf.version != BOOT_CONFIG_VERSION) { hostmsg("VERSION mismatch: %u found, current %u", romconf.version, BOOT_CONFIG_VERSION); init = true; - } else { - // OK - init = false; } if(init) { @@ -39,16 +45,22 @@ void host_init_bootloader() memset(&romconf, 0, sizeof(romconf)); romconf.magic = BOOT_CONFIG_MAGIC; romconf.version = BOOT_CONFIG_VERSION; - romconf.count = 2; - romconf.roms[0] = RBOOT_ROM0_ADDR; -#ifdef BOOT_ROM1_ADDR - romconf.roms[1] = RBOOT_ROM1_ADDR; -#else - size_t flashsize = flashmem_get_size_bytes(); - romconf.roms[1] = RBOOT_ROM0_ADDR + (flashsize / 2); -#endif + } + + // Read ROM locations from partition table + bool config_changed = false; + if(scan_partitions(&romconf)) { + config_changed = true; + } + + if(romconf.count == 0) { + hostmsg("ERROR! No App partitions found\r\n"); + return; + } + + if(init || config_changed) { bool ok = rboot_set_config(&romconf); - hostmsg("Write default config: %s", ok ? "OK" : "FAIL"); + hostmsg("Update rBoot config: %s", ok ? "OK" : "FAIL"); } String addr; diff --git a/Sming/Components/rboot/src/RbootHttpUpdater.cpp b/Sming/Components/rboot/src/RbootHttpUpdater.cpp index 07d28bf31d..78ed79e27a 100644 --- a/Sming/Components/rboot/src/RbootHttpUpdater.cpp +++ b/Sming/Components/rboot/src/RbootHttpUpdater.cpp @@ -15,36 +15,13 @@ #include -bool RbootHttpUpdater::addItem(uint32_t offset, const String& firmwareFileUrl, size_t maxSize) -{ - RbootHttpUpdaterItem add; - add.targetOffset = offset; - add.url = firmwareFileUrl; - add.size = maxSize; - add.stream = nullptr; - return items.add(add); -} - -bool RbootHttpUpdater::addItem(const String& firmwareFileUrl, RbootOutputStream* stream) -{ - if(stream == nullptr) { - return false; - } - - RbootHttpUpdaterItem add; - add.targetOffset = stream->getStartAddress(); - add.url = firmwareFileUrl; - add.size = stream->getMaxLength(); - add.stream = stream; - - return items.add(add); -} - void RbootHttpUpdater::start() { for(unsigned i = 0; i < items.count(); i++) { - RbootHttpUpdaterItem& it = items[i]; - debug_d("Download file:\r\n (%d) %s -> %X", currentItem, it.url.c_str(), it.targetOffset); + auto& it = items[i]; + debug_d("Download file:\r\n" + " (%u) %s -> %X", + currentItem, it.url.c_str(), it.targetOffset); HttpRequest* request; if(baseRequest != nullptr) { @@ -55,12 +32,7 @@ void RbootHttpUpdater::start() } request->setMethod(HTTP_GET); - - if(it.stream == nullptr) { - it.stream = new RbootOutputStream(it.targetOffset, it.size); - } - - request->setResponseStream(it.stream); + request->setResponseStream(it.getStream()); if(i == items.count() - 1) { request->onRequestComplete(RequestCompletedDelegate(&RbootHttpUpdater::updateComplete, this)); @@ -82,8 +54,8 @@ int RbootHttpUpdater::itemComplete(HttpConnection& client, bool success) return -1; } - RbootHttpUpdaterItem& it = items[currentItem]; - debug_d("Finished: URL: %s, Offset: %d, Length: %d", it.url.c_str(), it.stream->getStartAddress(), + auto& it = items[currentItem]; + debug_d("Finished: URL: %s, Offset: 0x%X, Length: %u", it.url.c_str(), it.stream->getStartAddress(), it.stream->available()); it.stream = nullptr; // the actual deletion will happen outside of this class @@ -94,14 +66,14 @@ int RbootHttpUpdater::itemComplete(HttpConnection& client, bool success) int RbootHttpUpdater::updateComplete(HttpConnection& client, bool success) { - auto hasError = itemComplete(client, success); - if(hasError) { + int hasError = itemComplete(client, success); + if(hasError != 0) { return hasError; } debug_d("\r\nFirmware download finished!"); for(unsigned i = 0; i < items.count(); i++) { - debug_d(" - item: %d, addr: %X, url: %s", i, items[i].targetOffset, items[i].url.c_str()); + debug_d(" - item: %u, addr: 0x%X, url: %s", i, items[i].targetOffset, items[i].url.c_str()); } if(!success) { @@ -124,19 +96,19 @@ void RbootHttpUpdater::updateFailed() if(updateDelegate) { updateDelegate(*this, false); } - cleanup(); + items.clear(); } void RbootHttpUpdater::applyUpdate() { - cleanup(); + items.clear(); if(romSlot == NO_ROM_SWITCH) { debug_d("Firmware updated."); return; } // set to boot new rom and then reboot - debug_d("Firmware updated, rebooting to rom %d...\r\n", romSlot); + debug_d("Firmware updated, rebooting to rom %u...\r\n", romSlot); rboot_set_current_rom(romSlot); System.restart(); } diff --git a/Sming/Components/spiffs/component.mk b/Sming/Components/spiffs/component.mk index e82a68d993..77064eb4a3 100644 --- a/Sming/Components/spiffs/component.mk +++ b/Sming/Components/spiffs/component.mk @@ -25,22 +25,12 @@ COMPONENT_RELINK_VARS += SPIFFS_OBJ_META_LEN SPIFFS_OBJ_META_LEN ?= 16 COMPONENT_CFLAGS += -DSPIFFS_OBJ_META_LEN=$(SPIFFS_OBJ_META_LEN) -##@Cleaning - -.PHONY: spiffs-image-clean -spiffs-image-clean: ##Remove SPIFFS image file - $(info Cleaning $(SPIFF_BIN_OUT)) - $(Q) rm -f $(SPIFF_BIN_OUT) - ##@Building # Spiffs image generation tool SPIFFSGEN := $(PYTHON) $(COMPONENT_PATH)/spiffsgen.py SPIFFSGEN_SMING = $(SPIFFSGEN) --meta-len=$(SPIFFS_OBJ_META_LEN) --block-size=8192 -.PHONY: spiffs-image-update -spiffs-image-update: spiffs-image-clean $(SPIFF_BIN_OUT) ##Rebuild the SPIFFS filesystem image - # Target invoked via partition table ifneq (,$(filter spiffsgen,$(MAKECMDGOALS))) PART_TARGET := $(PARTITION_$(PART)_FILENAME) diff --git a/Sming/Libraries/OtaUpgrade/OtaUpgrade/BasicStream.cpp b/Sming/Libraries/OtaUpgrade/OtaUpgrade/BasicStream.cpp index c1ac6be9e4..af28c04518 100644 --- a/Sming/Libraries/OtaUpgrade/OtaUpgrade/BasicStream.cpp +++ b/Sming/Libraries/OtaUpgrade/OtaUpgrade/BasicStream.cpp @@ -13,6 +13,7 @@ #include #include #include +#include extern "C" uint32 user_rf_cal_sector_set(void); @@ -27,29 +28,13 @@ DECLARE_FSTR_ARRAY(AppFlashRegionOffsets, uint32_t); BasicStream::Slot::Slot() { // Get parameters of the slot where the firmware image should be stored. - const rboot_config bootConfig = rboot_get_config(); - uint8_t currentSlot = bootConfig.current_rom; - index = (currentSlot == 0 ? 1 : 0); - address = bootConfig.roms[index]; - size = 0x100000 - (address & 0xFFFFF); + uint8_t currentSlot = rboot_get_current_rom(); + index = (currentSlot == 0) ? 1 : 0; - const auto limitSize = [&](uint32_t otherAddress) { - if(otherAddress > address) { - size = std::min(size, otherAddress - address); - } - }; - - limitSize(flashmem_get_size_bytes()); - limitSize(user_rf_cal_sector_set() * INTERNAL_FLASH_SECTOR_SIZE); - for(uint8_t i = 0; i < bootConfig.count; ++i) { - if(i != currentSlot) { - limitSize(bootConfig.roms[i]); - } - } - - for(auto offset : AppFlashRegionOffsets) { - limitSize(offset); - } + // Lookup slot details from partition table + auto part = Storage::spiFlash->partitions().findOta(index); + address = part.address(); + size = part.size(); } BasicStream::BasicStream() @@ -83,7 +68,7 @@ bool BasicStream::consume(const uint8_t*& data, size_t& size) void BasicStream::setError(Error code) { assert(code != Error::None); - debug_e("Error: %s", errorToString(code).c_str()); + debug_e("Error: %s", toString(code).c_str()); errorCode = code; state = State::Error; } @@ -225,6 +210,14 @@ size_t BasicStream::write(const uint8_t* data, size_t size) String BasicStream::errorToString(Error code) { + return toString(code); +} + +} // namespace OtaUpgrade + +String toString(OtaUpgrade::BasicStream::Error code) +{ + using Error = OtaUpgrade::BasicStream::Error; switch(code) { case Error::None: return nullptr; @@ -254,5 +247,3 @@ String BasicStream::errorToString(Error code) return F(""); } } - -} // namespace OtaUpgrade diff --git a/Sming/Libraries/OtaUpgrade/OtaUpgrade/BasicStream.h b/Sming/Libraries/OtaUpgrade/OtaUpgrade/BasicStream.h index 995c57034d..303431caae 100644 --- a/Sming/Libraries/OtaUpgrade/OtaUpgrade/BasicStream.h +++ b/Sming/Libraries/OtaUpgrade/OtaUpgrade/BasicStream.h @@ -48,7 +48,7 @@ class BasicStream : public ReadWriteStream /** * @brief Error code values */ - enum Error { + enum class Error { None, ///< No error occured thus far (default value of \c #errorCode if `hasError()` returns false) InvalidFormat, ///< Invalid/unsupported upgrade file format UnsupportedData, ///< Some content of the upgrade file is not supported by this version of OtaUpgradeStream. @@ -67,8 +67,9 @@ class BasicStream : public ReadWriteStream /** @brief Convert error code to string. * @see #errorCode + * @deprecated Use `toString()` global function */ - static String errorToString(Error code); + static String errorToString(Error code) SMING_DEPRECATED; /** @brief Process chunk of upgrade file. * @param data Pointer to chunk of data. @@ -113,11 +114,12 @@ class BasicStream : public ReadWriteStream uint32_t address; uint32_t size; uint8_t index; - bool updated = false; - } slot; + bool updated{false}; + }; + Slot slot; // Instead of RbootOutputStream, the rboot write API is used directly because in a future extension the OTA file may contain data for multiple FLASH regions. - rboot_write_status rbootWriteStatus = {}; + rboot_write_status rbootWriteStatus{}; enum class State { Error, @@ -128,14 +130,14 @@ class BasicStream : public ReadWriteStream VerifyRoms, RomsComplete, }; - State state = State::Header; + State state{State::Header}; #ifdef ENABLE_OTA_SIGNING using Verifier = SignatureVerifier; - static const uint32_t expectedHeaderMagic = OTA_HEADER_MAGIC_SIGNED; + static const uint32_t expectedHeaderMagic{OTA_HEADER_MAGIC_SIGNED}; #else using Verifier = ChecksumVerifier; - static const uint32_t expectedHeaderMagic = OTA_HEADER_MAGIC_NOT_SIGNED; + static const uint32_t expectedHeaderMagic{OTA_HEADER_MAGIC_NOT_SIGNED}; #endif Verifier verifier; @@ -144,9 +146,9 @@ class BasicStream : public ReadWriteStream Verifier::VerificationData verificationData; - size_t remainingBytes = 0; - uint8_t* destinationPtr = nullptr; - uint8_t romIndex = 0; + size_t remainingBytes{0}; + uint8_t* destinationPtr{nullptr}; + uint8_t romIndex{0}; void setupChunk(State nextState, size_t size, void* destination = nullptr) { @@ -183,3 +185,8 @@ class BasicStream : public ReadWriteStream }; } // namespace OtaUpgrade + +/** @brief Convert error code to string. + * @see #errorCode + */ +String toString(OtaUpgrade::BasicStream::Error code); diff --git a/Sming/Libraries/OtaUpgrade/OtaUpgrade/EncryptedStream.cpp b/Sming/Libraries/OtaUpgrade/OtaUpgrade/EncryptedStream.cpp index 80a88457af..fa1c09a0f8 100644 --- a/Sming/Libraries/OtaUpgrade/OtaUpgrade/EncryptedStream.cpp +++ b/Sming/Libraries/OtaUpgrade/OtaUpgrade/EncryptedStream.cpp @@ -46,16 +46,15 @@ size_t EncryptedStream::write(const uint8_t* data, size_t size) case Fragment::ChunkSize: remainingBytes = 1 + chunkSizeMinusOne; - if(buffer == nullptr || bufferSize < remainingBytes) { - free(buffer); - buffer = (uint8_t*)malloc(remainingBytes); - if(buffer == nullptr) { + if(!buffer || bufferSize < remainingBytes) { + buffer.reset(new uint8_t[remainingBytes]); + if(!buffer) { setError(Error::OutOfMemory); break; } bufferSize = remainingBytes; } - fragmentPtr = buffer; + fragmentPtr = buffer.get(); fragment = Fragment::Chunk; break; @@ -63,8 +62,8 @@ size_t EncryptedStream::write(const uint8_t* data, size_t size) unsigned char tag; size_t chiperTextLength = 1 + chunkSizeMinusOne; unsigned long long messageLength = 0; - bool ok = (crypto_secretstream_xchacha20poly1305_pull(&state, buffer, &messageLength, &tag, buffer, - chiperTextLength, nullptr, 0) == 0); + bool ok = (crypto_secretstream_xchacha20poly1305_pull(&state, buffer.get(), &messageLength, &tag, + buffer.get(), chiperTextLength, nullptr, 0) == 0); if(!ok || messageLength > bufferSize) { setError(Error::DecryptionFailed); break; @@ -77,7 +76,7 @@ size_t EncryptedStream::write(const uint8_t* data, size_t size) fragment = Fragment::None; } - BasicStream::write(buffer, static_cast(messageLength)); + BasicStream::write(buffer.get(), size_t(messageLength)); } break; case Fragment::None: diff --git a/Sming/Libraries/OtaUpgrade/OtaUpgrade/EncryptedStream.h b/Sming/Libraries/OtaUpgrade/OtaUpgrade/EncryptedStream.h index 3557c56f82..7b34375a33 100644 --- a/Sming/Libraries/OtaUpgrade/OtaUpgrade/EncryptedStream.h +++ b/Sming/Libraries/OtaUpgrade/OtaUpgrade/EncryptedStream.h @@ -28,11 +28,6 @@ class EncryptedStream : public BasicStream public: EncryptedStream() = default; - ~EncryptedStream() - { - free(buffer); - } - /** @brief Process an arbitrarily sized chunk of an encrypted OTA upgrade file. * @param data Pointer to chunk of data. * @param size Size of chunk pointed to by \a data in bytes. @@ -55,12 +50,12 @@ class EncryptedStream : public BasicStream Chunk, None, }; - Fragment fragment = Fragment::Header; - size_t remainingBytes = sizeof(header); - uint8_t* fragmentPtr = header; - uint8_t* buffer = nullptr; - size_t bufferSize = 0; + Fragment fragment{Fragment::Header}; + size_t remainingBytes{sizeof header}; + uint8_t* fragmentPtr{header}; + std::unique_ptr buffer; + size_t bufferSize{0}; }; } // namespace OtaUpgrade diff --git a/Sming/component.mk b/Sming/component.mk index 8f3471a83c..4d3ba173fa 100644 --- a/Sming/component.mk +++ b/Sming/component.mk @@ -149,10 +149,9 @@ GLOBAL_CFLAGS += -DSTRING_OBJECT_SIZE=$(STRING_OBJECT_SIZE) ##@Flashing .PHONY: flashinit -flashinit: $(ESPTOOL) $(FLASH_INIT_DATA) | $(FW_BASE) ##Erase your device's flash memory +flashinit: $(ESPTOOL) | $(FW_BASE) ##Erase your device's flash memory $(info Flash init data default and blank data) $(Q) $(EraseFlash) - $(call WriteFlash,$(FLASH_INIT_CHUNKS)) .PHONY: flashboot flashboot: $(FLASH_BOOT_LOADER) kill_term ##Write just the Bootloader diff --git a/Tools/vscode/setup.py b/Tools/vscode/setup.py index 4e59b0a0ab..8363406a23 100644 --- a/Tools/vscode/setup.py +++ b/Tools/vscode/setup.py @@ -32,6 +32,11 @@ def replace(self, path, name, prefix): return '${%s%s}%s' % (prefix, name, s[len(value):]) return path + def resolve(self, path): + """Convert any embedded environment variables into real paths + """ + return os.path.expandvars(path) + def subst_path(self, path, prefix=''): path = self.replace(path, 'SMING_HOME', prefix) path = self.replace(path, 'ESP_HOME', prefix) @@ -113,9 +118,10 @@ def get_property(data, name, default): return data[name] def update_intellisense(): - dirs = os.environ['COMPONENTS_EXTRA_INCDIR'].split() - for i, d in enumerate(dirs): - dirs[i] = check_path(env.subst_path(d)) + dirs = [] + for d in os.environ['COMPONENTS_EXTRA_INCDIR'].split(): + if os.path.exists(d): + dirs += [check_path(env.subst_path(d))] propertiesFile = '.vscode/c_cpp_properties.json' if os.path.exists(propertiesFile): @@ -185,11 +191,15 @@ def update_launch(): def update_workspace(): filename = 'sming.code-workspace' ws = load_json(filename, False) + template = load_template('workspace.json') if ws is None: - template = load_template('workspace.json') ws = template.copy() - # TODO: Make any required changes to generated - save_json(ws, filename) + schemas = ws['settings']['json.schemas'] = [] + # ws['settings']['json.schemas'] + for schema in template['settings']['json.schemas']: + schema['url'] = env.resolve(schema['url']) + schemas += [schema] + save_json(ws, filename) def main(): if not env.SMING_HOME or not env.SMING_ARCH: diff --git a/Tools/vscode/template/workspace.json b/Tools/vscode/template/workspace.json index f5d5823978..a79cc17307 100644 --- a/Tools/vscode/template/workspace.json +++ b/Tools/vscode/template/workspace.json @@ -15,13 +15,13 @@ "fileMatch": [ "*.hw" ], - "url": "{$SMING_HOME}/Components/Storage/schema.json" + "url": "file://${SMING_HOME}/Components/Storage/schema.json" }, { "fileMatch": [ "*.fwfs" ], - "url": "${SMING_HOME}/Components/IFS/fsbuild/schema.json" + "url": "file://${SMING_HOME}/Components/IFS/fsbuild/schema.json" } ] } diff --git a/docs/source/upgrading/4.2-4.3.rst b/docs/source/upgrading/4.2-4.3.rst index 69fb0498e4..6698e12634 100644 --- a/docs/source/upgrading/4.2-4.3.rst +++ b/docs/source/upgrading/4.2-4.3.rst @@ -17,6 +17,12 @@ the :envvar:`HWCONFIG` setting. You can find full details in the :component:`Storage` library. See also background on :doc:`/information/flash`. +Removed build targets + spiffs-image-update + Use the new `buildpart` target instead + spiffs-image-clean + Use the new `part-clean` target instead + New and updated build targets hwconfig Displays the current configuration in JSON format @@ -36,22 +42,18 @@ New and updated build targets ``make flashinit`` to write system parameter information. Failure to do this was a common problem and should now be a thing of the past. flashinit - This now just erases the flash memory, and is no longer a pre-requisite for ``make flash``. - - The ESP8266 system parameter information has been moved into registered - partitions (phy_init and sys_param) near the beginning of flash memory. - It now gets written when running ``make flash``. - - Previously, this information was kept right at the end of the flash memory, - so the location would vary depending on the setting of ``SPI_SIZE``. - This was a frequent cause of problems as the system would fail to start if this - was set incorrectly. + This now just erases the flash memory. + The ESP8266 initialisation data gets written when running ``make flash``. flashmap Flash just the partition map flashpart Flash a single partition, e.g. ``make flashpart PART=spiffs0`` erasepart Erase a partition, e.g. ``make erasepart PART=spiffs0`` + buildpart + Re-builds images associated with partitions, such as SPIFFS or other filesystem images. + part-clean + Removes any partition images with build information. This is done as part of a normal project `clean`. Configuration variables A number of configuration variables have been removed or made read-only, as these are now diff --git a/samples/Basic_Storage/basic_storage.hw b/samples/Basic_Storage/basic_storage.hw index 90da02eb6d..44caa91626 100644 --- a/samples/Basic_Storage/basic_storage.hw +++ b/samples/Basic_Storage/basic_storage.hw @@ -2,8 +2,15 @@ "name": "Basic Storage sample", "base_config": "spiffs", "devices": { - // Override default (conservative) flash settings for maximum performance + // If required, override default (conservative) flash settings "spiFlash": { + /* + * The default mode (dio) should work with all flash devices. + * Make sure to set the mode according to your flash chip. + * Allowed SPI modes are listed here: + * https://sming.readthedocs.io/en/latest/_inc/Sming/Components/esptool/index.html#envvar-SPI_MODE. + */ + // "mode": "qio", "speed": 80 } }, diff --git a/samples/Basic_rBoot/app/application.cpp b/samples/Basic_rBoot/app/application.cpp index b8e7397a30..cfb3f85655 100644 --- a/samples/Basic_rBoot/app/application.cpp +++ b/samples/Basic_rBoot/app/application.cpp @@ -96,17 +96,16 @@ void OtaUpdate() void Switch() { - uint8 before, after; - before = rboot_get_current_rom(); - if(before == 0) { - after = 1; + uint8_t before = rboot_get_current_rom(); + uint8_t after = (before == 0) ? 1 : 0; + + Serial.printf(_F("Swapping from rom %u to rom %u.\r\n"), before, after); + if(rboot_set_current_rom(after)) { + Serial.println(F("Restarting...\r\n")); + System.restart(); } else { - after = 0; + Serial.println(F("Switch failed.")); } - Serial.printf("Swapping from rom %d to rom %d.\r\n", before, after); - rboot_set_current_rom(after); - Serial.println("Restarting...\r\n"); - System.restart(); } void ShowInfo() @@ -116,15 +115,15 @@ void ShowInfo() Serial.printf("CPU Frequency: %d MHz\r\n", system_get_cpu_freq()); Serial.printf("System Chip ID: %x\r\n", system_get_chip_id()); Serial.printf("SPI Flash ID: %x\r\n", Storage::spiFlash->getId()); - //Serial.printf("SPI Flash Size: %d\r\n", (1 << ((spi_flash_get_id() >> 16) & 0xff))); + Serial.printf("SPI Flash Size: %x\r\n", Storage::spiFlash->getSize()); rboot_config conf; conf = rboot_get_config(); debugf("Count: %d", conf.count); - debugf("ROM 0: 0x%08x", conf.roms[0]); - debugf("ROM 1: 0x%08x", conf.roms[1]); - debugf("ROM 2: 0x%08x", conf.roms[2]); + for(unsigned i = 0; i < MAX_ROMS; ++i) { + debugf("ROM %u: 0x%08x", i, conf.roms[i]); + } debugf("GPIO ROM: %d", conf.gpio_rom); } diff --git a/samples/Basic_rBoot/basic_rboot.hw b/samples/Basic_rBoot/basic_rboot.hw index 64465e1b3a..27332cb161 100644 --- a/samples/Basic_rBoot/basic_rboot.hw +++ b/samples/Basic_rBoot/basic_rboot.hw @@ -2,11 +2,14 @@ "name": "Two ROM slots, two SPIFFS", "base_config": "spiffs", "partitions": { + "rom0": { + "subtype": "ota_0" + }, "rom1": { "address": "0x108000", "size": "992K", "type": "app", - "subtype": "ota_0", + "subtype": "ota_1", "filename": "$(RBOOT_ROM_1_BIN)" }, "spiffs1": { @@ -16,4 +19,4 @@ "subtype": "spiffs" } } -} +} \ No newline at end of file diff --git a/samples/Basic_rBoot/component.mk b/samples/Basic_rBoot/component.mk index df007c2dfe..9555e50099 100644 --- a/samples/Basic_rBoot/component.mk +++ b/samples/Basic_rBoot/component.mk @@ -4,8 +4,4 @@ RBOOT_ENABLED := 1 ## Use standard hardware config with two ROM slots and two SPIFFS partitions -ifeq ($(SMING_ARCH),Esp8266) HWCONFIG := basic_rboot -else -HWCONFIG := spiffs -endif diff --git a/samples/HttpServer_FirmwareUpload/app/application.cpp b/samples/HttpServer_FirmwareUpload/app/application.cpp index 6c97838a30..2dbfe83325 100644 --- a/samples/HttpServer_FirmwareUpload/app/application.cpp +++ b/samples/HttpServer_FirmwareUpload/app/application.cpp @@ -38,7 +38,7 @@ int onUpload(HttpServerConnection& connection, HttpRequest& request, HttpRespons response.code = HTTP_STATUS_BAD_REQUEST; response.setContentType(MIME_HTML); - String html = "

" + OtaUpgradeStream::errorToString(otaStream->errorCode) + "

"; + String html = "

" + toString(otaStream->errorCode) + "

"; response.headers[HTTP_HEADER_CONTENT_LENGTH] = html.length(); response.sendString(html);