From a0b1746d0d22418533da949a957441355b78cb05 Mon Sep 17 00:00:00 2001 From: Mike Date: Mon, 11 Mar 2024 08:10:08 +0000 Subject: [PATCH] Fix mechanism for OTA upgrade from Sming 4.2 (#2728) * Fix PartitionTable bool() operator * Allow automatic typecasting from Device::Type to Partition::FullType * Update documentation --- Sming/Components/Storage/README.rst | 1 + Sming/Components/Storage/ota-migration.rst | 107 ++++++++++++++++++ .../Storage/src/include/Storage/Device.h | 1 + .../src/include/Storage/PartitionTable.h | 2 +- 4 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 Sming/Components/Storage/ota-migration.rst diff --git a/Sming/Components/Storage/README.rst b/Sming/Components/Storage/README.rst index 6cd6ac162f..f2818c0f13 100644 --- a/Sming/Components/Storage/README.rst +++ b/Sming/Components/Storage/README.rst @@ -91,6 +91,7 @@ When planning OTA updates please check that the displayed partition map correspo 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. +See :doc:`ota-migration`. It is not necessary to update the bootloader. See :component:`rboot` for further information. diff --git a/Sming/Components/Storage/ota-migration.rst b/Sming/Components/Storage/ota-migration.rst new file mode 100644 index 0000000000..bcd3044997 --- /dev/null +++ b/Sming/Components/Storage/ota-migration.rst @@ -0,0 +1,107 @@ +Partition table migration +========================= + +.. highlight:: c++ + +This guidance applies to esp8266 devices only. + +Sming after v4.2 requires a valid partition table. +If existing devices running previous versions of Sming require updating via OTA then +an intermediate firmware should be created which installs this partition table. + +After rBoot hands control to the SDK entrypoint, the ``user_pre_init()`` function is called. +This function is documented in the NON-OS-SDK guide and was introduced in version 3. +It is called by the SDK before user_init() so nothing else in the framework has yet been initialised, +including any C++ static initialisers. + +Sming uses this function to read the partition table into memory. +Applications may override this function to perform any custom upgrade operations. + +Add to application's `component.mk`: + +.. code-block:: make + + EXTRA_LDFLAGS := $(call Wrap,user_pre_init) + USER_CFLAGS += -DPARTITION_TABLE_OFFSET=$(PARTITION_TABLE_OFFSET) + +Add this to your application:: + + // Support updating legacy devices without partition tables (Sming 4.2 and earlier) + #ifdef ARCH_ESP8266 + + namespace + { + // Note: This file won't exist on initial build! + IMPORT_FSTR(partitionTableData, PROJECT_DIR "/out/Esp8266/debug/firmware/partitions.bin") + } + + extern "C" void __wrap_user_pre_init(void) + { + static_assert(PARTITION_TABLE_OFFSET == 0x3fa000, "Bad PTO"); + Storage::initialize(); + auto& flash = *Storage::spiFlash; + if(!flash.partitions()) { + LOAD_FSTR(data, partitionTableData) + flash.erase_range(PARTITION_TABLE_OFFSET, flash.getBlockSize()); + flash.write(PARTITION_TABLE_OFFSET, data, partitionTableData.size()); + flash.loadPartitions(PARTITION_TABLE_OFFSET); + } + + extern void __real_user_pre_init(void); + __real_user_pre_init(); + } + + #endif // ARCH_ESP8266 + +.. note:: + + You will get a 'file not found' error because the partition table gets built *after* compiling the application. + You can run ``make partmap-build`` manually first to get around this. + + +An alternative method is to build the partition table layout in code, so there are no external file dependencies:: + + // Support updating legacy devices without partition tables (Sming 4.2 and earlier) + #ifdef ARCH_ESP8266 + + #include + + extern "C" void __wrap_user_pre_init(void) + { + static_assert(PARTITION_TABLE_OFFSET == 0x3fa000, "Bad PTO"); + + Storage::initialize(); + + auto& flash = *Storage::spiFlash; + if(!flash.partitions()) { + using FullType = Storage::Partition::FullType; + using SubType = Storage::Partition::SubType; + #define PT_ENTRY(name, fulltype, offset, size) \ + { ESP_PARTITION_MAGIC, FullType(fulltype).type, FullType(fulltype).subtype, offset, size, name, 0 } + + static constexpr Storage::esp_partition_info_t partitionTableData[] PROGMEM{ + PT_ENTRY("spiFlash", Storage::Device::Type::flash, 0, 0x400000), + PT_ENTRY("rom0", SubType::App::ota0, 0x2000, 0xf8000), + PT_ENTRY("rom1", SubType::App::ota1, 0x102000, 0xf8000), + PT_ENTRY("spiffs0", SubType::Data::spiffs, 0x200000, 0xc0000), + PT_ENTRY("spiffs1", SubType::Data::spiffs, 0x2c0000, 0xc0000), + PT_ENTRY("rf_cal", SubType::Data::rfCal, 0x3fb000, 0x1000), + PT_ENTRY("phy_init", SubType::Data::phy, 0x3fc000, 0x1000), + PT_ENTRY("sys_param", SubType::Data::sysParam, 0x3fd000, 0x3000), + }; + + uint8_t buffer[sizeof(partitionTableData)]; + memcpy(buffer, partitionTableData, sizeof(partitionTableData)); + flash.erase_range(PARTITION_TABLE_OFFSET, flash.getBlockSize()); + flash.write(PARTITION_TABLE_OFFSET, buffer, sizeof(buffer)); + flash.loadPartitions(PARTITION_TABLE_OFFSET); + } + + extern void __real_user_pre_init(void); + __real_user_pre_init(); + } + + #endif // ARCH_ESP8266 + + +The above examples are provided as templates and should be modified as required and tested thoroughly! diff --git a/Sming/Components/Storage/src/include/Storage/Device.h b/Sming/Components/Storage/src/include/Storage/Device.h index 7ebaa32df8..b05017e069 100644 --- a/Sming/Components/Storage/src/include/Storage/Device.h +++ b/Sming/Components/Storage/src/include/Storage/Device.h @@ -40,6 +40,7 @@ class Device : public LinkedObjectTemplate * @brief Storage type */ enum class Type : uint8_t { + partitionType = uint8_t(Partition::Type::storage), #define XX(type, value, desc) type = value, STORAGE_TYPE_MAP(XX) #undef XX diff --git a/Sming/Components/Storage/src/include/Storage/PartitionTable.h b/Sming/Components/Storage/src/include/Storage/PartitionTable.h index b54fcee3f3..2497178f76 100644 --- a/Sming/Components/Storage/src/include/Storage/PartitionTable.h +++ b/Sming/Components/Storage/src/include/Storage/PartitionTable.h @@ -24,7 +24,7 @@ class PartitionTable explicit operator bool() const { - return mEntries.isEmpty(); + return !mEntries.isEmpty(); } /**