UF2 is a file format, developed by Microsoft for PXT (also known as Microsoft MakeCode), that is particularly suitable for flashing microcontrollers over MSC (Mass Storage Class; aka removable flash drive).
The list of family IDs is stored in a separate JSON file:
https://github.com/microsoft/uf2/blob/master/utils/uf2families.json
This JSON file is regularly updated. The family_id enum should be kept in
sync with it. For simplicity and consistency, it's strongly recommended to
auto-generate this enum from uf2families.json directly. This was last done
using the following commands:
$ curl -fsSLO https://github.com/microsoft/uf2/raw/90e9741f217f5a40c98ba74d663e408041037578/utils/uf2families.json
$ jq -r '
.[]
| " \(.id | ascii_downcase):\n"
+ " id: \(.short_name | ascii_downcase | gsub("-"; "_"))\n"
+ " doc: \(.description | tojson)"
' uf2families.json
Test files, picked to cover as many different shapes as possible:
num_blocks.family_id::rp2xxx_absolute block that picotool prepends (see the
is_rp2350_e10_block value instance in the block type).file_size rather
than family_id.family_id::rp2040) is moved to the end of the second file, which was
intended for family_id::rp2xxx_absolute (see
https://github.com/raspberrypi/pico-examples/blob/c81c855ffdedc825975a40ba357723a71358ddf0/universal/CMakeLists.txt#L159-L169).flags.is_file_container flag set (for
example
https://github.com/microsoft/pxt-microsoft-boot-sequence/releases/download/v0.0.4/arcade-p0.uf2
is a pure file container for
file_name == "Projects/microsoft-boot-sequence.elf"). Generated by
MakeCode, the only known producer of file container UF2s.This page hosts a formal specification of UF2 (USB Flashing Format) using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.
// Code generated by kaitai-struct-compiler from a .ksy source file. DO NOT EDIT.
import (
"github.com/kaitai-io/kaitai_struct_go_runtime/kaitai"
"bytes"
"io"
)
/**
* UF2 is a file format, developed by Microsoft for PXT (also known as
* Microsoft MakeCode), that is particularly suitable for flashing
* microcontrollers over MSC (Mass Storage Class; aka removable flash drive).
*
* The list of family IDs is stored in a separate JSON file:
*
* <https://github.com/microsoft/uf2/blob/master/utils/uf2families.json>
*
* This JSON file is regularly updated. The `family_id` enum should be kept in
* sync with it. For simplicity and consistency, it's strongly recommended to
* auto-generate this enum from `uf2families.json` directly. This was last done
* using the following commands:
*
* ```bash
* $ curl -fsSLO https://github.com/microsoft/uf2/raw/90e9741f217f5a40c98ba74d663e408041037578/utils/uf2families.json
* $ jq -r '
* .[]
* | " \(.id | ascii_downcase):\n"
* + " id: \(.short_name | ascii_downcase | gsub("-"; "_"))\n"
* + " doc: \(.description | tojson)"
* ' uf2families.json
* ```
*
* Test files, picked to cover as many different shapes as possible:
*
* * <https://micropython.org/download/RPI_PICO/> - a typical case: all blocks
* have the same family ID and `num_blocks`.
* * <https://micropython.org/download/RPI_PICO2/> - these .uf2 files are
* actually two UF2 files concatenated. The first UF2 file is the standalone
* `family_id::rp2xxx_absolute` block that `picotool` prepends (see the
* `is_rp2350_e10_block` value instance in the `block` type).
* * <https://circuitpython.org/downloads> - the builds for SAMD boards (for
* example
* [Feather M0 Express](https://circuitpython.org/board/feather_m0_express/))
* set no flags at all, so the field at offset 28 is read as `file_size` rather
* than `family_id`.
* * <https://github.com/raspberrypi/pico-sdk-prebuilts/releases> -
* [Universal UF2](https://github.com/raspberrypi/pico-examples/blob/c81c855ffdedc825975a40ba357723a71358ddf0/universal/README.md#universal-binary-vs-universal-uf2)
* files, which are again two UF2 files concatenated. What's interesting about
* these is that the last block of the first file (with the family ID
* `family_id::rp2040`) is moved to the end of the second file, which was
* intended for `family_id::rp2xxx_absolute` (see
* <https://github.com/raspberrypi/pico-examples/blob/c81c855ffdedc825975a40ba357723a71358ddf0/universal/CMakeLists.txt#L159-L169>).
* * <https://github.com/microsoft/pxt-microsoft-boot-sequence/releases> - some
* files contain blocks with the `flags.is_file_container` flag set (for
* example
* <https://github.com/microsoft/pxt-microsoft-boot-sequence/releases/download/v0.0.4/arcade-p0.uf2>
* is a pure file container for
* `file_name == "Projects/microsoft-boot-sequence.elf"`). Generated by
* MakeCode, the only known producer of file container UF2s.
* * <https://github.com/umi-eng/uftwo/tree/35bccf75b4f81c43f088696a8c4a9912f1f4104e/uftwo/tests> -
* synthetic test files for features that real firmware doesn't seem to use
* (e.g. MD5 checksums).
* @see <a href="https://github.com/microsoft/uf2/blob/90e9741f217f5a40c98ba74d663e408041037578/README.md">Source</a>
*/
type Uf2_ExtensionTagType int
const (
Uf2_ExtensionTagType__End Uf2_ExtensionTagType = 0
Uf2_ExtensionTagType__PageSize Uf2_ExtensionTagType = 780791
Uf2_ExtensionTagType__Description Uf2_ExtensionTagType = 6622621
Uf2_ExtensionTagType__Rp2IgnoreBlock Uf2_ExtensionTagType = 10049507
Uf2_ExtensionTagType__Version Uf2_ExtensionTagType = 10471356
Uf2_ExtensionTagType__Sha2Checksum Uf2_ExtensionTagType = 11824560
Uf2_ExtensionTagType__DeviceTypeId Uf2_ExtensionTagType = 13149993
)
var values_Uf2_ExtensionTagType = map[Uf2_ExtensionTagType]struct{}{0: {}, 780791: {}, 6622621: {}, 10049507: {}, 10471356: {}, 11824560: {}, 13149993: {}}
func (v Uf2_ExtensionTagType) isDefined() bool {
_, ok := values_Uf2_ExtensionTagType[v]
return ok
}
type Uf2_FamilyId int
const (
Uf2_FamilyId__Stm32l4 Uf2_FamilyId = 16738585
Uf2_FamilyId__Stm32l5 Uf2_FamilyId = 69471199
Uf2_FamilyId__Stm32f411xc Uf2_FamilyId = 114362747
Uf2_FamilyId__M0sense Uf2_FamilyId = 299792458
Uf2_FamilyId__Atmega32 Uf2_FamilyId = 374814231
Uf2_FamilyId__Saml21 Uf2_FamilyId = 407992330
Uf2_FamilyId__Nrf52 Uf2_FamilyId = 458716255
Uf2_FamilyId__Esp32 Uf2_FamilyId = 475996592
Uf2_FamilyId__Stm32l1 Uf2_FamilyId = 505365293
Uf2_FamilyId__Stm32l0 Uf2_FamilyId = 539900561
Uf2_FamilyId__Stm32wl Uf2_FamilyId = 558239728
Uf2_FamilyId__Rtl8710b Uf2_FamilyId = 585160444
Uf2_FamilyId__Lpc55 Uf2_FamilyId = 716994540
Uf2_FamilyId__Esp32c2 Uf2_FamilyId = 730387100
Uf2_FamilyId__Stm32f411xe Uf2_FamilyId = 767756741
Uf2_FamilyId__Stm32g0 Uf2_FamilyId = 806311475
Uf2_FamilyId__Esp32s31 Uf2_FamilyId = 822212545
Uf2_FamilyId__Gd32f350 Uf2_FamilyId = 835856582
Uf2_FamilyId__Esp32h2 Uf2_FamilyId = 858203894
Uf2_FamilyId__Rtl8720d Uf2_FamilyId = 863621090
Uf2_FamilyId__Esp32p4 Uf2_FamilyId = 1026592404
Uf2_FamilyId__MaixplayU4 Uf2_FamilyId = 1265126769
Uf2_FamilyId__Stm32g4 Uf2_FamilyId = 1282483210
Uf2_FamilyId__Stm32h5 Uf2_FamilyId = 1318001757
Uf2_FamilyId__Csk4 Uf2_FamilyId = 1332399698
Uf2_FamilyId__Mimxrt10xx Uf2_FamilyId = 1337120189
Uf2_FamilyId__Xr809 Uf2_FamilyId = 1374225320
Uf2_FamilyId__Stm32f7 Uf2_FamilyId = 1404571392
Uf2_FamilyId__Esp32c6 Uf2_FamilyId = 1410195298
Uf2_FamilyId__Samd51 Uf2_FamilyId = 1427194976
Uf2_FamilyId__Stm32f4 Uf2_FamilyId = 1467308631
Uf2_FamilyId__Fx2 Uf2_FamilyId = 1511523995
Uf2_FamilyId__Stm32f2 Uf2_FamilyId = 1561987630
Uf2_FamilyId__Stm32f1 Uf2_FamilyId = 1591873650
Uf2_FamilyId__Nrf52833 Uf2_FamilyId = 1646171002
Uf2_FamilyId__Stm32f0 Uf2_FamilyId = 1685595318
Uf2_FamilyId__Bk7231u Uf2_FamilyId = 1733968048
Uf2_FamilyId__Samd21 Uf2_FamilyId = 1760373640
Uf2_FamilyId__Ch32v Uf2_FamilyId = 1771791084
Uf2_FamilyId__Bk7251 Uf2_FamilyId = 1786956866
Uf2_FamilyId__Stm32f3 Uf2_FamilyId = 1803837832
Uf2_FamilyId__Stm32f407 Uf2_FamilyId = 1829315322
Uf2_FamilyId__Stm32h7 Uf2_FamilyId = 1840668802
Uf2_FamilyId__Csk6 Uf2_FamilyId = 1853049000
Uf2_FamilyId__Nrf52832xxab Uf2_FamilyId = 1869948536
Uf2_FamilyId__Stm32wb Uf2_FamilyId = 1892771411
Uf2_FamilyId__Nrf52832xxaa Uf2_FamilyId = 1920081230
Uf2_FamilyId__Max32690 Uf2_FamilyId = 1947226634
Uf2_FamilyId__Esp32c61 Uf2_FamilyId = 2010665156
Uf2_FamilyId__Bk7231n Uf2_FamilyId = 2067722800
Uf2_FamilyId__Ra4m1 Uf2_FamilyId = 2078840685
Uf2_FamilyId__Py32f071Uvk5V3 Uf2_FamilyId = 2105173743
Uf2_FamilyId__Esp8266 Uf2_FamilyId = 2125160941
Uf2_FamilyId__Kl32l2 Uf2_FamilyId = 2139350931
Uf2_FamilyId__Nrf52820 Uf2_FamilyId = 2181929567
Uf2_FamilyId__Stm32f407vg Uf2_FamilyId = 2410701054
Uf2_FamilyId__Max78002 Uf2_FamilyId = 2446589208
Uf2_FamilyId__Rza1lu Uf2_FamilyId = 2501329455
Uf2_FamilyId__Gd32vf103 Uf2_FamilyId = 2599435827
Uf2_FamilyId__Esp32h4 Uf2_FamilyId = 2651564682
Uf2_FamilyId__Rtl8710a Uf2_FamilyId = 2684343619
Uf2_FamilyId__At32f415 Uf2_FamilyId = 2697558926
Uf2_FamilyId__Nrf52840 Uf2_FamilyId = 2913282112
Uf2_FamilyId__Esp32h21 Uf2_FamilyId = 3067936943
Uf2_FamilyId__Esp32s2 Uf2_FamilyId = 3218951918
Uf2_FamilyId__Esp32s3 Uf2_FamilyId = 3296614247
Uf2_FamilyId__Esp32c3 Uf2_FamilyId = 3559628908
Uf2_FamilyId__Max32650 Uf2_FamilyId = 3594487346
Uf2_FamilyId__Bl602 Uf2_FamilyId = 3725750455
Uf2_FamilyId__Rtl8720c Uf2_FamilyId = 3767498084
Uf2_FamilyId__Rp2040 Uf2_FamilyId = 3834380118
Uf2_FamilyId__Rp2xxxAbsolute Uf2_FamilyId = 3834380119
Uf2_FamilyId__Rp2xxxData Uf2_FamilyId = 3834380120
Uf2_FamilyId__Rp2350ArmS Uf2_FamilyId = 3834380121
Uf2_FamilyId__Rp2350Riscv Uf2_FamilyId = 3834380122
Uf2_FamilyId__Rp2350ArmNs Uf2_FamilyId = 3834380123
Uf2_FamilyId__Max32666 Uf2_FamilyId = 4039314801
Uf2_FamilyId__Esp32c5 Uf2_FamilyId = 4145808195
)
var values_Uf2_FamilyId = map[Uf2_FamilyId]struct{}{16738585: {}, 69471199: {}, 114362747: {}, 299792458: {}, 374814231: {}, 407992330: {}, 458716255: {}, 475996592: {}, 505365293: {}, 539900561: {}, 558239728: {}, 585160444: {}, 716994540: {}, 730387100: {}, 767756741: {}, 806311475: {}, 822212545: {}, 835856582: {}, 858203894: {}, 863621090: {}, 1026592404: {}, 1265126769: {}, 1282483210: {}, 1318001757: {}, 1332399698: {}, 1337120189: {}, 1374225320: {}, 1404571392: {}, 1410195298: {}, 1427194976: {}, 1467308631: {}, 1511523995: {}, 1561987630: {}, 1591873650: {}, 1646171002: {}, 1685595318: {}, 1733968048: {}, 1760373640: {}, 1771791084: {}, 1786956866: {}, 1803837832: {}, 1829315322: {}, 1840668802: {}, 1853049000: {}, 1869948536: {}, 1892771411: {}, 1920081230: {}, 1947226634: {}, 2010665156: {}, 2067722800: {}, 2078840685: {}, 2105173743: {}, 2125160941: {}, 2139350931: {}, 2181929567: {}, 2410701054: {}, 2446589208: {}, 2501329455: {}, 2599435827: {}, 2651564682: {}, 2684343619: {}, 2697558926: {}, 2913282112: {}, 3067936943: {}, 3218951918: {}, 3296614247: {}, 3559628908: {}, 3594487346: {}, 3725750455: {}, 3767498084: {}, 3834380118: {}, 3834380119: {}, 3834380120: {}, 3834380121: {}, 3834380122: {}, 3834380123: {}, 4039314801: {}, 4145808195: {}}
func (v Uf2_FamilyId) isDefined() bool {
_, ok := values_Uf2_FamilyId[v]
return ok
}
type Uf2 struct {
FirstBlock *Uf2_Block
Blocks []*Uf2_Block
_io *kaitai.Stream
_root *Uf2
_parent kaitai.Struct
}
func NewUf2() *Uf2 {
return &Uf2{
}
}
func (this Uf2) IO_() *kaitai.Stream {
return this._io
}
func (this *Uf2) Read(io *kaitai.Stream, parent kaitai.Struct, root *Uf2) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp1 := NewUf2_Block()
err = tmp1.Read(this._io, this, this._root)
if err != nil {
return err
}
this.FirstBlock = tmp1
tmp2, err := this.FirstBlock.NumBlocks()
if err != nil {
return err
}
for i := 0; i < int(tmp2 - 1); i++ {
_ = i
tmp3 := NewUf2_Block()
err = tmp3.Read(this._io, this, this._root)
if err != nil {
return err
}
this.Blocks = append(this.Blocks, tmp3)
}
return err
}
type Uf2_Block struct {
Magic []byte
SecondMagic []byte
Flags *Uf2_Flags
TargetAddress uint32
LenPayload uint32
BlockNumber uint32
NumBlocksRaw uint32
FileSize uint32
FamilyId Uf2_FamilyId
Data *Uf2_BlockData
FinalMagic []byte
_io *kaitai.Stream
_root *Uf2
_parent *Uf2
_raw_Data []byte
_f_isRp2350E10Block bool
isRp2350E10Block bool
_f_numBlocks bool
numBlocks uint32
}
func NewUf2_Block() *Uf2_Block {
return &Uf2_Block{
}
}
func (this Uf2_Block) IO_() *kaitai.Stream {
return this._io
}
func (this *Uf2_Block) Read(io *kaitai.Stream, parent *Uf2, root *Uf2) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp4, err := this._io.ReadBytes(int(4))
if err != nil {
return err
}
tmp4 = tmp4
this.Magic = tmp4
if !(bytes.Equal(this.Magic, []uint8{85, 70, 50, 10})) {
return kaitai.NewValidationNotEqualError([]uint8{85, 70, 50, 10}, this.Magic, this._io, "/types/block/seq/0")
}
tmp5, err := this._io.ReadBytes(int(4))
if err != nil {
return err
}
tmp5 = tmp5
this.SecondMagic = tmp5
if !(bytes.Equal(this.SecondMagic, []uint8{87, 81, 93, 158})) {
return kaitai.NewValidationNotEqualError([]uint8{87, 81, 93, 158}, this.SecondMagic, this._io, "/types/block/seq/1")
}
tmp6 := NewUf2_Flags()
err = tmp6.Read(this._io, this, this._root)
if err != nil {
return err
}
this.Flags = tmp6
tmp7, err := this._io.ReadU4le()
if err != nil {
return err
}
this.TargetAddress = uint32(tmp7)
{
_it := this.TargetAddress
tmp8 := _it % 4
if tmp8 < 0 {
tmp8 += 4
}
if !(tmp8 == 0) {
return kaitai.NewValidationExprError(this.TargetAddress, this._io, "/types/block/seq/3")
}
}
tmp9, err := this._io.ReadU4le()
if err != nil {
return err
}
this.LenPayload = uint32(tmp9)
{
_it := this.LenPayload
tmp10 := _it % 4
if tmp10 < 0 {
tmp10 += 4
}
if !(tmp10 == 0) {
return kaitai.NewValidationExprError(this.LenPayload, this._io, "/types/block/seq/4")
}
}
tmp11, err := this._io.ReadU4le()
if err != nil {
return err
}
this.BlockNumber = uint32(tmp11)
tmp12, err := this._io.ReadU4le()
if err != nil {
return err
}
this.NumBlocksRaw = uint32(tmp12)
if !(this.NumBlocksRaw >= this.BlockNumber + 1) {
return kaitai.NewValidationLessThanError(this.BlockNumber + 1, this.NumBlocksRaw, this._io, "/types/block/seq/6")
}
tmp13, err := this.Flags.HasFamilyId()
if err != nil {
return err
}
if (!(tmp13)) {
tmp14, err := this._io.ReadU4le()
if err != nil {
return err
}
this.FileSize = uint32(tmp14)
}
tmp15, err := this.Flags.HasFamilyId()
if err != nil {
return err
}
if (tmp15) {
tmp16, err := this._io.ReadU4le()
if err != nil {
return err
}
this.FamilyId = Uf2_FamilyId(tmp16)
}
tmp17, err := this._io.ReadBytes(int(476))
if err != nil {
return err
}
tmp17 = tmp17
this._raw_Data = tmp17
_io__raw_Data := kaitai.NewStream(bytes.NewReader(this._raw_Data))
tmp18 := NewUf2_BlockData()
err = tmp18.Read(_io__raw_Data, this, this._root)
if err != nil {
return err
}
this.Data = tmp18
tmp19, err := this._io.ReadBytes(int(4))
if err != nil {
return err
}
tmp19 = tmp19
this.FinalMagic = tmp19
if !(bytes.Equal(this.FinalMagic, []uint8{48, 111, 177, 10})) {
return kaitai.NewValidationNotEqualError([]uint8{48, 111, 177, 10}, this.FinalMagic, this._io, "/types/block/seq/10")
}
return err
}
/**
* Determines whether this is a block that `picotool` prepends to RP2350
* flash images as a workaround for erratum RP2350-E10 (i.e. a hardware
* bug in the A2 version of the RP2350 boot ROM).
*
* Such a block is always written on its own, but its `num_blocks_raw` is
* set to 2. If we trusted this value, we would attempt to read one block
* too many (which would most likely fail). Therefore, we must correct it
* to 1 before using it.
*
* The conditions for detecting this block come from the
* [`check_abs_block()`](https://github.com/raspberrypi/picotool/blob/6f6458d792b93685a11423b244a585eaa99eafcf/elf2uf2/elf2uf2.cpp#L147)
* function in `picotool`. However, there are some differences:
*
* 1. In Kaitai Struct, we cannot easily check whether all 256 payload
* bytes are set to `0xef`, so we only check the first and last bytes.
* 2. There are .uf2 files in the wild where `flags.has_extension_tags`
* is true, but there are actually no extension tags (the first and
* only tag has a size of 0, which is just a terminator), so our
* condition allows for this case. You can download an example of such
* a .uf2 file here:
* <https://github.com/neednotapply/DC32-cfw/releases/tag/1.69.13.37>
*
* It's worth noting that we cannot require the presence of the
* `extension_tag_type::rp2_ignore_block`
* (`UF2_EXTENSION_RP2_IGNORE_BLOCK`) tag because the UF2 files generated
* by `picotool` prior to
* <https://github.com/raspberrypi/picotool/commit/78c9bd121b09399823b67ee7ea89003ca0d3315f>
* don't have it. Therefore, we check whether this tag is present only if
* `flags.has_extension_tags` is set.
*
* Test .uf2 files with this special block can be downloaded from
* <https://micropython.org/download/RPI_PICO2/>. Note that all the .uf2
* files there are actually two UF2 (sub)files concatenated, and this
* Kaitai Struct implementation parses only one at a time (so in order to
* parse both, you need something like the helper spec `uf2_files.ksy`
* from
* <https://github.com/kaitai-io/kaitai_struct_formats/pull/542#discussion_r3906386820>).
* v1.24.x releases predate the `extension_tag_type::rp2_ignore_block`
* tag, while releases v1.25.0 and later include it.
* @see <a href="https://github.com/raspberrypi/picotool/blob/6f6458d792b93685a11423b244a585eaa99eafcf/elf2uf2/elf2uf2.cpp#L147">Git tag "2.3.0"</a>
* @see <a href="https://github.com/raspberrypi/picotool/commit/78c9bd121b09399823b67ee7ea89003ca0d3315f">Source</a>
*/
func (this *Uf2_Block) IsRp2350E10Block() (v bool, err error) {
if (this._f_isRp2350E10Block) {
return this.isRp2350E10Block, nil
}
this._f_isRp2350E10Block = true
tmp20 := this.Data.Payload
tmp21, err := this.Flags.HasExtensionTags()
if err != nil {
return false, err
}
this.isRp2350E10Block = bool( (( ((this.Flags.Value == 8192) || (this.Flags.Value == 40960)) ) && (this.FamilyId == Uf2_FamilyId__Rp2xxxAbsolute) && (this.NumBlocksRaw == 2) && (this.BlockNumber == 0) && (this.LenPayload == 256) && (this.Data.Payload[0] == 239) && (tmp20[len(tmp20) - 1] == 239) && ( ((!(tmp21)) || (this.Data.ExtensionTags[0].LenTag == 0) || ( ((this.Data.ExtensionTags[0].LenTag == 4) && (this.Data.ExtensionTags[0].TagType == Uf2_ExtensionTagType__Rp2IgnoreBlock)) )) )) )
return this.isRp2350E10Block, nil
}
func (this *Uf2_Block) NumBlocks() (v uint32, err error) {
if (this._f_numBlocks) {
return this.numBlocks, nil
}
this._f_numBlocks = true
var tmp22 int8;
tmp23, err := this.IsRp2350E10Block()
if err != nil {
return 0, err
}
if (tmp23) {
tmp22 = 1
} else {
tmp22 = this.NumBlocksRaw
}
this.numBlocks = uint32(tmp22)
return this.numBlocks, nil
}
/**
* Address in flash where `data.payload` should be written, or an offset
* in the file specified by `data.file_name` if `flags.is_file_container`
* is set.
*
* The [official
* spec](https://github.com/microsoft/uf2/blob/90e9741f217f5a40c98ba74d663e408041037578/README.md#payload-sizes)
* says:
*
* > In any event, payload size and target address should always be
* > 4-byte aligned.
*/
/**
* The [official
* spec](https://github.com/microsoft/uf2/blob/90e9741f217f5a40c98ba74d663e408041037578/README.md#payload-sizes)
* says:
*
* > In any event, payload size and target address should always be
* > 4-byte aligned.
*/
/**
* Number of blocks that make up the UF2 file to which this block
* belongs. Every block of a file has the same value. It is at least 1
* and every `block_number` in the file must be less than this value
* (which is validated by this Kaitai Struct implementation).
*/
/**
* Size of the file this block belongs to, but only if
* `flags.is_file_container` is true. Otherwise, the official spec allows
* this field to be set to anything - though in practice, it's always
* zero.
*/
type Uf2_BlockData struct {
Payload []byte
FileName string
ExtensionTags []*Uf2_ExtensionTag
_io *kaitai.Stream
_root *Uf2
_parent *Uf2_Block
_f_md5Checksum bool
md5Checksum *Uf2_Md5Checksum
}
func NewUf2_BlockData() *Uf2_BlockData {
return &Uf2_BlockData{
}
}
func (this Uf2_BlockData) IO_() *kaitai.Stream {
return this._io
}
func (this *Uf2_BlockData) Read(io *kaitai.Stream, parent *Uf2_Block, root *Uf2) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp24, err := this._io.ReadBytes(int(this._parent.LenPayload))
if err != nil {
return err
}
tmp24 = tmp24
this.Payload = tmp24
tmp25, err := this._parent.Flags.IsFileContainer()
if err != nil {
return err
}
if (tmp25) {
tmp26, err := this._io.ReadBytesTerm(0, false, true, true)
if err != nil {
return err
}
this.FileName = string(tmp26)
}
tmp27, err := this._parent.Flags.HasExtensionTags()
if err != nil {
return err
}
if (tmp27) {
for i := 1;; i++ {
tmp28 := NewUf2_ExtensionTag()
err = tmp28.Read(this._io, this, this._root)
if err != nil {
return err
}
_it := tmp28
this.ExtensionTags = append(this.ExtensionTags, _it)
if _it.LenTag == 0 {
break
}
}
}
return err
}
/**
* Describes a region that doesn't need to be flashed again if the
* checksum matches.
*
* The [official
* spec](https://github.com/microsoft/uf2/blob/90e9741f217f5a40c98ba74d663e408041037578/README.md#md5-checksum)
* says that "This is currently only used on ESP32", but no real-world
* firmware sample has been found (at least none of the .uf2 files from
* <https://github.com/adafruit/tinyuf2/releases>,
* [MicroPython](https://micropython.org/download/) or
* [CircuitPython](https://circuitpython.org/downloads) contain it). It
* was found only in some synthetic test files, e.g.
* <https://github.com/umi-eng/uftwo/blob/35bccf75b4f81c43f088696a8c4a9912f1f4104e/uftwo/tests/checksum_256.uf2>.
*/
func (this *Uf2_BlockData) Md5Checksum() (v *Uf2_Md5Checksum, err error) {
if (this._f_md5Checksum) {
return this.md5Checksum, nil
}
this._f_md5Checksum = true
tmp29, err := this._parent.Flags.HasMd5Checksum()
if err != nil {
return nil, err
}
if (tmp29) {
_pos, err := this._io.Pos()
if err != nil {
return nil, err
}
tmp30, err := this._io.Size()
if err != nil {
return nil, err
}
_, err = this._io.Seek(int64(tmp30 - 24), io.SeekStart)
if err != nil {
return nil, err
}
tmp31 := NewUf2_Md5Checksum()
err = tmp31.Read(this._io, this, this._root)
if err != nil {
return nil, err
}
this.md5Checksum = tmp31
_, err = this._io.Seek(_pos, io.SeekStart)
if err != nil {
return nil, err
}
}
return this.md5Checksum, nil
}
/**
* The bytes to be written to `_parent.target_address`, which is either
* an address in flash, or an offset in the file specified by `file_name`
* if `_parent.flags.is_file_container` is set.
*/
/**
* @see <a href="https://github.com/microsoft/uf2/blob/90e9741f217f5a40c98ba74d663e408041037578/README.md#extension-tags">Source</a>
*/
type Uf2_ExtensionTag struct {
LenTag uint8
TagType Uf2_ExtensionTagType
Value []byte
Padding []byte
_io *kaitai.Stream
_root *Uf2
_parent *Uf2_BlockData
_f_lenValue bool
lenValue int
_f_minLenTag bool
minLenTag int
}
func NewUf2_ExtensionTag() *Uf2_ExtensionTag {
return &Uf2_ExtensionTag{
}
}
func (this Uf2_ExtensionTag) IO_() *kaitai.Stream {
return this._io
}
func (this *Uf2_ExtensionTag) Read(io *kaitai.Stream, parent *Uf2_BlockData, root *Uf2) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp32, err := this._io.ReadU1()
if err != nil {
return err
}
this.LenTag = tmp32
{
_it := this.LenTag
tmp33, err := this.MinLenTag()
if err != nil {
return err
}
if !( ((_it == 0) || (_it >= tmp33)) ) {
return kaitai.NewValidationExprError(this.LenTag, this._io, "/types/extension_tag/seq/0")
}
}
tmp34, err := this._io.ReadBitsIntLe(24)
if err != nil {
return err
}
this.TagType = Uf2_ExtensionTagType(tmp34)
this._io.AlignToByte()
if (this.LenTag != 0) {
tmp35, err := this.LenValue()
if err != nil {
return err
}
tmp36, err := this._io.ReadBytes(int(tmp35))
if err != nil {
return err
}
tmp36 = tmp36
this.Value = tmp36
}
tmp37 := -(this.LenTag) % 4
if tmp37 < 0 {
tmp37 += 4
}
tmp38, err := this._io.ReadBytes(int(tmp37))
if err != nil {
return err
}
tmp38 = tmp38
this.Padding = tmp38
return err
}
func (this *Uf2_ExtensionTag) LenValue() (v int, err error) {
if (this._f_lenValue) {
return this.lenValue, nil
}
this._f_lenValue = true
var tmp39 int;
tmp40, err := this.MinLenTag()
if err != nil {
return 0, err
}
if (this.LenTag >= tmp40) {
tmp41, err := this.MinLenTag()
if err != nil {
return 0, err
}
tmp39 = this.LenTag - tmp41
} else {
tmp39 = 0
}
this.lenValue = int(tmp39)
return this.lenValue, nil
}
func (this *Uf2_ExtensionTag) MinLenTag() (v int, err error) {
if (this._f_minLenTag) {
return this.minLenTag, nil
}
this._f_minLenTag = true
this.minLenTag = int(1 + 3)
return this.minLenTag, nil
}
/**
* Total size of the tag in bytes, including this byte and `tag_type`, so
* at least 4. The exception is the last tag which terminates the list -
* it specifies a total size of 0.
*/
/**
* Tags are 4-byte aligned, so a tag whose size is not a multiple
* of 4 is followed by padding.
*/
/**
* @see <a href="https://github.com/microsoft/uf2/blob/90e9741f217f5a40c98ba74d663e408041037578/uf2.h#L43-L47">Source</a>
* @see <a href="https://github.com/raspberrypi/pico-sdk/blob/98a542c1a62fb549ffb5d66a3e5892b06276b670/src/common/boot_uf2_headers/include/boot/uf2.h#L23-L27">Git tag "2.3.0"</a>
*/
type Uf2_Flags struct {
Value uint32
_io *kaitai.Stream
_root *Uf2
_parent *Uf2_Block
_f_hasExtensionTags bool
hasExtensionTags bool
_f_hasFamilyId bool
hasFamilyId bool
_f_hasMd5Checksum bool
hasMd5Checksum bool
_f_isFileContainer bool
isFileContainer bool
_f_notMainFlash bool
notMainFlash bool
}
func NewUf2_Flags() *Uf2_Flags {
return &Uf2_Flags{
}
}
func (this Uf2_Flags) IO_() *kaitai.Stream {
return this._io
}
func (this *Uf2_Flags) Read(io *kaitai.Stream, parent *Uf2_Block, root *Uf2) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp42, err := this._io.ReadU4le()
if err != nil {
return err
}
this.Value = uint32(tmp42)
{
_it := this.Value
tmp43, err := this.IsFileContainer()
if err != nil {
return err
}
tmp44, err := this.HasExtensionTags()
if err != nil {
return err
}
if !( ((_it & ^61441 == 0) && (!( ((tmp43) && (tmp44)) ))) ) {
return kaitai.NewValidationExprError(this.Value, this._io, "/types/flags/seq/0")
}
}
return err
}
/**
* Indicates whether extension tags are present after the payload.
*/
func (this *Uf2_Flags) HasExtensionTags() (v bool, err error) {
if (this._f_hasExtensionTags) {
return this.hasExtensionTags, nil
}
this._f_hasExtensionTags = true
this.hasExtensionTags = bool(this.Value & 32768 != 0)
return this.hasExtensionTags, nil
}
/**
* The field at offset 28 in the block is `family_id` instead of
* `file_size`.
*/
func (this *Uf2_Flags) HasFamilyId() (v bool, err error) {
if (this._f_hasFamilyId) {
return this.hasFamilyId, nil
}
this._f_hasFamilyId = true
this.hasFamilyId = bool(this.Value & 8192 != 0)
return this.hasFamilyId, nil
}
/**
* Indicates whether `md5_checksum` is present at the end of `data`.
*/
func (this *Uf2_Flags) HasMd5Checksum() (v bool, err error) {
if (this._f_hasMd5Checksum) {
return this.hasMd5Checksum, nil
}
this._f_hasMd5Checksum = true
this.hasMd5Checksum = bool(this.Value & 16384 != 0)
return this.hasMd5Checksum, nil
}
/**
* When set, the UF2 format is used as a container for regular files
* (akin to a TAR file, or ZIP archive without compression).
*
* `target_address` is the offset in the file where the payload is to be
* written, and `file_size` is the size of that file. The name of the
* destination file is stored in `data.file_name`.
*/
func (this *Uf2_Flags) IsFileContainer() (v bool, err error) {
if (this._f_isFileContainer) {
return this.isFileContainer, nil
}
this._f_isFileContainer = true
this.isFileContainer = bool(this.Value & 4096 != 0)
return this.isFileContainer, nil
}
/**
* Indicates that this block should be skipped when writing the device
* flash. It can be used to store data that does not fit on the device,
* typically embedded source code or debug info.
*/
func (this *Uf2_Flags) NotMainFlash() (v bool, err error) {
if (this._f_notMainFlash) {
return this.notMainFlash, nil
}
this._f_notMainFlash = true
this.notMainFlash = bool(this.Value & 1 != 0)
return this.notMainFlash, nil
}
/**
* Only the five bits that we cover in value instances below are defined,
* and no other bit may be set. The [official
* spec](https://github.com/microsoft/uf2/blob/90e9741f217f5a40c98ba74d663e408041037578/README.md#flags)
* says "Currently, there are five flags defined". If any other flags are
* added in the future, this .ksy spec will need to be updated.
*
* The `is_file_container` and `has_extension_tags` flags disagree about
* what follows the payload in `data` (a file name or a list of extension
* tags), so this Kaitai Struct implementation treats them as mutually
* exclusive and will reject a block that sets both. The official spec
* does not specify how this should be handled, but logically there's a
* conflict, so we've decided to strictly treat it as an error.
*/
/**
* @see <a href="https://github.com/microsoft/uf2/blob/90e9741f217f5a40c98ba74d663e408041037578/README.md#md5-checksum">Source</a>
*/
type Uf2_Md5Checksum struct {
StartAddress uint32
LenRegion uint32
Md5 []byte
_io *kaitai.Stream
_root *Uf2
_parent *Uf2_BlockData
}
func NewUf2_Md5Checksum() *Uf2_Md5Checksum {
return &Uf2_Md5Checksum{
}
}
func (this Uf2_Md5Checksum) IO_() *kaitai.Stream {
return this._io
}
func (this *Uf2_Md5Checksum) Read(io *kaitai.Stream, parent *Uf2_BlockData, root *Uf2) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp45, err := this._io.ReadU4le()
if err != nil {
return err
}
this.StartAddress = uint32(tmp45)
tmp46, err := this._io.ReadU4le()
if err != nil {
return err
}
this.LenRegion = uint32(tmp46)
tmp47, err := this._io.ReadBytes(int(16))
if err != nil {
return err
}
tmp47 = tmp47
this.Md5 = tmp47
return err
}