Skip to content

Commit

Permalink
Fix mechanism for OTA upgrade from Sming 4.2 (#2728)
Browse files Browse the repository at this point in the history
* Fix PartitionTable bool() operator

* Allow automatic typecasting from Device::Type to Partition::FullType

* Update documentation
  • Loading branch information
mikee47 authored Mar 11, 2024
1 parent 8ba44cb commit a0b1746
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 1 deletion.
1 change: 1 addition & 0 deletions Sming/Components/Storage/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
107 changes: 107 additions & 0 deletions Sming/Components/Storage/ota-migration.rst
Original file line number Diff line number Diff line change
@@ -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 <Storage/partition_info.h>

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!
1 change: 1 addition & 0 deletions Sming/Components/Storage/src/include/Storage/Device.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class Device : public LinkedObjectTemplate<Device>
* @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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class PartitionTable

explicit operator bool() const
{
return mEntries.isEmpty();
return !mEntries.isEmpty();
}

/**
Expand Down

0 comments on commit a0b1746

Please sign in to comment.