UF2-based OTA
LibreTiny's OTA updating is based on Microsoft's UF2 specification. Some aspects of the process, such as OTA1/2 support and target partition selection, have been customized with extension tags.
Note
Just like in UF2, all values in this format are little-endian.
Firmware images
UF2 files may contain multiple firmware images that are to be flashed, i.e. main firmware + bootloader + some config partition.
Some CPUs support dual-OTA schemes: firmware runs from one image, while the other one is reserved for updated firmware. After applying the update, a reboot causes to run the other image instead.
Each firmware image may be either applicable:
- only when flashing OTA1 (
part;file;;
) - only when flashing OTA2 (
;;part;file
) - for both schemes to a single partition (
part;file
) - for both schemes but different partitions (
part1;file;part2;file
) - for both schemes but with a different binary (
part;file1;part;file2
) - for both schemes, with different binaries and target partitions (
part1;file1;part2;file2
)
* part
means partition here
** values in parentheses show the input format to use for uf2ota.py
For easier understanding, these update types will be referred to in this document using the numbers.
Extension tags
Standard tags are used: VERSION
, DEVICE
and DEVICE_ID
.
Additionally, custom tags are defined:
Name | ID | Type | Description |
---|---|---|---|
OTA_VERSION |
0x5D57D0 | int 8-bit | format version (for simple compatibility checks) |
BOARD |
0xCA25C8 | string | board name / code (lowercase) |
FIRMWARE |
0x00DE43 | string | firmware description / name |
BUILD_DATE |
0x822F30 | int 32-bit | build date/time as Unix timestamp |
LT_VERSION |
0x59563D | semver | LT version |
LT_PART_1 |
0x805946 | string | OTA1 partition name |
LT_PART_2 |
0xA1E4D7 | string | OTA2 partition name |
LT_HAS_OTA1 |
0xBBD965 | bool 8-bit | image has any data for OTA1 |
LT_HAS_OTA2 |
0x92280E | bool 8-bit | image has any data for OTA2 |
LT_BINPATCH |
0xB948DE | bytes | binary patch to convert OTA1->OTA2 |
Update types
Single OTA scheme (1, 2)
Image is ignored if the OTA scheme does not match. UF2 has LT_PART_1
or LT_PART_2
set to target partition name. The other partition tag is present, but empty (0 bytes).
Dual-OTA/single-file scheme (3, 4)
One image is used for both OTA schemes. UF2 has LT_PART_1
and LT_PART_2
tags set. For type 3
these two tags contain the same partition name.
Dual-OTA/dual-file scheme (5, 6)
Just like types 3
and 4
, UF2 has two partition tags set. For type 5
they have the same name.
The image stored in UF2 is meant for OTA1 scheme. There is an additional tag LT_BINPATCH
present. In OTA1 flashing scheme, it is ignored.
Binary patching
OTA2 images are not stored directly, as that would needlessly double the UF2 file size. Instead, binary patching instructions, embedded into the extension tags area, allow the CPU to convert the OTA1 image from UF2 into OTA2 image.
There can be at most one binpatch tag in a UF2 block. It has the following format:
- opcode (1 byte) - operation type:
DIFF32
(0xFE) - difference between 32-bit values
- length (1 byte) - data length
- data (
length
bytes)- for
DIFF32
:- difference value (signed int 32-bit)
- offset table (
length-4
bytes)
- for
The presented structure can be repeated in a single binpatch tag.
DIFF32
This method works by adding the difference value to a 32-bit integer. It allows to save the most space in OTA1/2 image scenarios, where the only different values are, for example, flash memory addresses. The offset table contains positions within the 256-byte block, to which the difference value should be mathematically added.
For a block like:
000 72 71 73 61 76 65 00 00 5f 66 72 65 65 72 74 6f |rqsave.._freerto|
010 73 5f 6d 75 74 65 78 5f 67 65 74 5f 74 69 6d 65 |s_mutex_get_time|
020 6f 75 74 00 5d a4 03 08 61 a4 03 08 85 a4 03 08 |out.]...a.......|
030 5d a4 03 08 61 a4 03 08 85 a4 03 08 81 a9 03 08 |]...a...........|
040 6d a9 03 08 7d a4 03 08 d9 a8 03 08 05 a7 03 08 |m...}...........|
050 bd a4 03 08 ad a8 03 08 59 a7 03 08 9d a8 03 08 |........Y.......|
060 01 a7 03 08 51 a8 03 08 21 aa 03 08 b9 a4 03 08 |....Q...!.......|
070 85 a3 03 08 89 a3 03 08 4d a4 03 08 a1 a8 03 08 |........M.......|
080 00 00 00 00 00 00 00 00 19 a8 03 08 c1 a4 03 08 |................|
090 8d a8 03 08 ed a6 03 08 dd a7 03 08 ad a4 03 08 |................|
0a0 9d a7 03 08 95 a4 03 08 81 a7 03 08 09 a7 03 08 |................|
0b0 31 a7 03 08 d1 a6 03 08 dd a5 03 08 61 aa 03 08 |1...........a...|
0c0 c5 a2 03 08 d5 a2 03 08 d9 a2 03 08 b1 a6 03 08 |................|
0d0 65 aa 03 08 ad a6 03 08 a9 a6 03 08 8d a6 03 08 |e...............|
0e0 e5 a2 03 08 e9 a2 03 08 1d a4 03 08 ed a3 03 08 |................|
0f0 35 a4 03 08 05 a4 03 08 bd a3 03 08 8d a3 03 08 |5...............|
a DIFF32 patch containing:
fe 39 00 50 0c 00 24 28 2c 30 34 38 3c 40 44 48 |.9.P..$(,048<@DH|
4c 50 54 58 5c 60 64 68 6c 70 74 78 7c 88 8c 90 |LPTX\`dhlptx|...|
94 98 9c a0 a4 a8 ac b0 b4 b8 bc c0 c4 c8 cc d0 |................|
d4 d8 dc e0 e4 e8 ec f0 f4 f8 fc |........... |
adds 0x000C5000 to 53 values, producing OTA2 output like this:
000 72 71 73 61 76 65 00 00 5f 66 72 65 65 72 74 6f |rqsave.._freerto|
010 73 5f 6d 75 74 65 78 5f 67 65 74 5f 74 69 6d 65 |s_mutex_get_time|
020 6f 75 74 00 5d f4 0f 08 61 f4 0f 08 85 f4 0f 08 |out.]...a.......|
030 5d f4 0f 08 61 f4 0f 08 85 f4 0f 08 81 f9 0f 08 |]...a...........|
040 6d f9 0f 08 7d f4 0f 08 d9 f8 0f 08 05 f7 0f 08 |m...}...........|
050 bd f4 0f 08 ad f8 0f 08 59 f7 0f 08 9d f8 0f 08 |........Y.......|
060 01 f7 0f 08 51 f8 0f 08 21 fa 0f 08 b9 f4 0f 08 |....Q...!.......|
070 85 f3 0f 08 89 f3 0f 08 4d f4 0f 08 a1 f8 0f 08 |........M.......|
080 00 00 00 00 00 00 00 00 19 f8 0f 08 c1 f4 0f 08 |................|
090 8d f8 0f 08 ed f6 0f 08 dd f7 0f 08 ad f4 0f 08 |................|
0a0 9d f7 0f 08 95 f4 0f 08 81 f7 0f 08 09 f7 0f 08 |................|
0b0 31 f7 0f 08 d1 f6 0f 08 dd f5 0f 08 61 fa 0f 08 |1...........a...|
0c0 c5 f2 0f 08 d5 f2 0f 08 d9 f2 0f 08 b1 f6 0f 08 |................|
0d0 65 fa 0f 08 ad f6 0f 08 a9 f6 0f 08 8d f6 0f 08 |e...............|
0e0 e5 f2 0f 08 e9 f2 0f 08 1d f4 0f 08 ed f3 0f 08 |................|
0f0 35 f4 0f 08 05 f4 0f 08 bd f3 0f 08 8d f3 0f 08 |5...............|