// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild using System.Collections.Generic; namespace Kaitai { /// /// 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). /// /// /// Reference: Source /// public partial class Uf2 : KaitaiStruct { public static Uf2 FromFile(string fileName) { return new Uf2(new KaitaiStream(fileName)); } public enum ExtensionTagType { End = 0, PageSize = 780791, Description = 6622621, Rp2IgnoreBlock = 10049507, Version = 10471356, Sha2Checksum = 11824560, DeviceTypeId = 13149993, } public enum FamilyId { Stm32l4 = 16738585, Stm32l5 = 69471199, Stm32f411xc = 114362747, M0sense = 299792458, Atmega32 = 374814231, Saml21 = 407992330, Nrf52 = 458716255, Esp32 = 475996592, Stm32l1 = 505365293, Stm32l0 = 539900561, Stm32wl = 558239728, Rtl8710b = 585160444, Lpc55 = 716994540, Esp32c2 = 730387100, Stm32f411xe = 767756741, Stm32g0 = 806311475, Esp32s31 = 822212545, Gd32f350 = 835856582, Esp32h2 = 858203894, Rtl8720d = 863621090, Esp32p4 = 1026592404, MaixplayU4 = 1265126769, Stm32g4 = 1282483210, Stm32h5 = 1318001757, Csk4 = 1332399698, Mimxrt10xx = 1337120189, Xr809 = 1374225320, Stm32f7 = 1404571392, Esp32c6 = 1410195298, Samd51 = 1427194976, Stm32f4 = 1467308631, Fx2 = 1511523995, Stm32f2 = 1561987630, Stm32f1 = 1591873650, Nrf52833 = 1646171002, Stm32f0 = 1685595318, Bk7231u = 1733968048, Samd21 = 1760373640, Ch32v = 1771791084, Bk7251 = 1786956866, Stm32f3 = 1803837832, Stm32f407 = 1829315322, Stm32h7 = 1840668802, Csk6 = 1853049000, Nrf52832xxab = 1869948536, Stm32wb = 1892771411, Nrf52832xxaa = 1920081230, Max32690 = 1947226634, Esp32c61 = 2010665156, Bk7231n = 2067722800, Ra4m1 = 2078840685, Py32f071Uvk5V3 = 2105173743, Esp8266 = 2125160941, Kl32l2 = 2139350931, Nrf52820 = 2181929567, Stm32f407vg = 2410701054, Max78002 = 2446589208, Rza1lu = 2501329455, Gd32vf103 = 2599435827, Esp32h4 = 2651564682, Rtl8710a = 2684343619, At32f415 = 2697558926, Nrf52840 = 2913282112, Esp32h21 = 3067936943, Esp32s2 = 3218951918, Esp32s3 = 3296614247, Esp32c3 = 3559628908, Max32650 = 3594487346, Bl602 = 3725750455, Rtl8720c = 3767498084, Rp2040 = 3834380118, Rp2xxxAbsolute = 3834380119, Rp2xxxData = 3834380120, Rp2350ArmS = 3834380121, Rp2350Riscv = 3834380122, Rp2350ArmNs = 3834380123, Max32666 = 4039314801, Esp32c5 = 4145808195, } public Uf2(KaitaiStream p__io, KaitaiStruct p__parent = null, Uf2 p__root = null) : base(p__io) { m_parent = p__parent; m_root = p__root ?? this; _read(); } private void _read() { _firstBlock = new Block(m_io, this, m_root); _blocks = new List(); for (var i = 0; i < FirstBlock.NumBlocks - 1; i++) { _blocks.Add(new Block(m_io, this, m_root)); } } public partial class Block : KaitaiStruct { public static Block FromFile(string fileName) { return new Block(new KaitaiStream(fileName)); } public Block(KaitaiStream p__io, Uf2 p__parent = null, Uf2 p__root = null) : base(p__io) { m_parent = p__parent; m_root = p__root; f_isRp2350E10Block = false; f_numBlocks = false; _read(); } private void _read() { _magic = m_io.ReadBytes(4); if (!((KaitaiStream.ByteArrayCompare(_magic, new byte[] { 85, 70, 50, 10 }) == 0))) { throw new ValidationNotEqualError(new byte[] { 85, 70, 50, 10 }, _magic, m_io, "/types/block/seq/0"); } _secondMagic = m_io.ReadBytes(4); if (!((KaitaiStream.ByteArrayCompare(_secondMagic, new byte[] { 87, 81, 93, 158 }) == 0))) { throw new ValidationNotEqualError(new byte[] { 87, 81, 93, 158 }, _secondMagic, m_io, "/types/block/seq/1"); } _flags = new Flags(m_io, this, m_root); _targetAddress = m_io.ReadU4le(); { uint M_ = _targetAddress; if (!(KaitaiStream.Mod(M_, 4) == 0)) { throw new ValidationExprError(_targetAddress, m_io, "/types/block/seq/3"); } } _lenPayload = m_io.ReadU4le(); { uint M_ = _lenPayload; if (!(KaitaiStream.Mod(M_, 4) == 0)) { throw new ValidationExprError(_lenPayload, m_io, "/types/block/seq/4"); } } _blockNumber = m_io.ReadU4le(); _numBlocksRaw = m_io.ReadU4le(); if (!(_numBlocksRaw >= BlockNumber + 1)) { throw new ValidationLessThanError(BlockNumber + 1, _numBlocksRaw, m_io, "/types/block/seq/6"); } if (!(Flags.HasFamilyId)) { _fileSize = m_io.ReadU4le(); } if (Flags.HasFamilyId) { _familyId = ((Uf2.FamilyId) m_io.ReadU4le()); } __raw_data = m_io.ReadBytes(476); var io___raw_data = new KaitaiStream(__raw_data); _data = new BlockData(io___raw_data, this, m_root); _finalMagic = m_io.ReadBytes(4); if (!((KaitaiStream.ByteArrayCompare(_finalMagic, new byte[] { 48, 111, 177, 10 }) == 0))) { throw new ValidationNotEqualError(new byte[] { 48, 111, 177, 10 }, _finalMagic, m_io, "/types/block/seq/10"); } } private bool f_isRp2350E10Block; private bool _isRp2350E10Block; /// /// 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. /// /// /// Reference: Git tag "2.3.0" /// /// /// Reference: Source /// public bool IsRp2350E10Block { get { if (f_isRp2350E10Block) return _isRp2350E10Block; f_isRp2350E10Block = true; _isRp2350E10Block = (bool) ( (( ((Flags.Value == 8192) || (Flags.Value == 40960)) ) && (FamilyId == Uf2.FamilyId.Rp2xxxAbsolute) && (NumBlocksRaw == 2) && (BlockNumber == 0) && (LenPayload == 256) && (Data.Payload[0] == 239) && (Data.Payload[Data.Payload.Length - 1] == 239) && ( ((!(Flags.HasExtensionTags)) || (Data.ExtensionTags[0].LenTag == 0) || ( ((Data.ExtensionTags[0].LenTag == 4) && (Data.ExtensionTags[0].TagType == Uf2.ExtensionTagType.Rp2IgnoreBlock)) )) )) ); return _isRp2350E10Block; } } private bool f_numBlocks; private uint _numBlocks; public uint NumBlocks { get { if (f_numBlocks) return _numBlocks; f_numBlocks = true; _numBlocks = (uint) ((IsRp2350E10Block ? 1 : NumBlocksRaw)); return _numBlocks; } } private byte[] _magic; private byte[] _secondMagic; private Flags _flags; private uint _targetAddress; private uint _lenPayload; private uint _blockNumber; private uint _numBlocksRaw; private uint? _fileSize; private FamilyId _familyId; private BlockData _data; private byte[] _finalMagic; private Uf2 m_root; private Uf2 m_parent; private byte[] __raw_data; public byte[] Magic { get { return _magic; } } public byte[] SecondMagic { get { return _secondMagic; } } public Flags Flags { get { return _flags; } } /// /// 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. /// public uint TargetAddress { get { return _targetAddress; } } /// /// 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. /// public uint LenPayload { get { return _lenPayload; } } public uint BlockNumber { get { return _blockNumber; } } /// /// 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). /// public uint NumBlocksRaw { get { return _numBlocksRaw; } } /// /// 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. /// public uint? FileSize { get { return _fileSize; } } public FamilyId FamilyId { get { return _familyId; } } public BlockData Data { get { return _data; } } public byte[] FinalMagic { get { return _finalMagic; } } public Uf2 M_Root { get { return m_root; } } public Uf2 M_Parent { get { return m_parent; } } public byte[] M_RawData { get { return __raw_data; } } } public partial class BlockData : KaitaiStruct { public static BlockData FromFile(string fileName) { return new BlockData(new KaitaiStream(fileName)); } public BlockData(KaitaiStream p__io, Uf2.Block p__parent = null, Uf2 p__root = null) : base(p__io) { m_parent = p__parent; m_root = p__root; f_md5Checksum = false; _read(); } private void _read() { _payload = m_io.ReadBytes(M_Parent.LenPayload); if (M_Parent.Flags.IsFileContainer) { _fileName = System.Text.Encoding.GetEncoding("UTF-8").GetString(m_io.ReadBytesTerm(0, false, true, true)); } if (M_Parent.Flags.HasExtensionTags) { _extensionTags = new List(); { var i = 0; ExtensionTag M_; do { M_ = new ExtensionTag(m_io, this, m_root); _extensionTags.Add(M_); i++; } while (!(M_.LenTag == 0)); } } } private bool f_md5Checksum; private Md5Checksum _md5Checksum; /// /// 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>. /// public Md5Checksum Md5Checksum { get { if (f_md5Checksum) return _md5Checksum; f_md5Checksum = true; if (M_Parent.Flags.HasMd5Checksum) { long _pos = m_io.Pos; m_io.Seek(M_Io.Size - 24); _md5Checksum = new Md5Checksum(m_io, this, m_root); m_io.Seek(_pos); } return _md5Checksum; } } private byte[] _payload; private string _fileName; private List _extensionTags; private Uf2 m_root; private Uf2.Block m_parent; /// /// 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. /// public byte[] Payload { get { return _payload; } } public string FileName { get { return _fileName; } } public List ExtensionTags { get { return _extensionTags; } } public Uf2 M_Root { get { return m_root; } } public Uf2.Block M_Parent { get { return m_parent; } } } /// /// Reference: Source /// public partial class ExtensionTag : KaitaiStruct { public static ExtensionTag FromFile(string fileName) { return new ExtensionTag(new KaitaiStream(fileName)); } public ExtensionTag(KaitaiStream p__io, Uf2.BlockData p__parent = null, Uf2 p__root = null) : base(p__io) { m_parent = p__parent; m_root = p__root; f_lenValue = false; f_minLenTag = false; _read(); } private void _read() { _lenTag = m_io.ReadU1(); { byte M_ = _lenTag; if (!( ((M_ == 0) || (M_ >= MinLenTag)) )) { throw new ValidationExprError(_lenTag, m_io, "/types/extension_tag/seq/0"); } } _tagType = ((Uf2.ExtensionTagType) m_io.ReadBitsIntLe(24)); m_io.AlignToByte(); if (LenTag != 0) { _value = m_io.ReadBytes(LenValue); } _padding = m_io.ReadBytes(KaitaiStream.Mod(-(LenTag), 4)); } private bool f_lenValue; private int _lenValue; public int LenValue { get { if (f_lenValue) return _lenValue; f_lenValue = true; _lenValue = (int) ((LenTag >= MinLenTag ? LenTag - MinLenTag : 0)); return _lenValue; } } private bool f_minLenTag; private int _minLenTag; public int MinLenTag { get { if (f_minLenTag) return _minLenTag; f_minLenTag = true; _minLenTag = (int) (1 + 3); return _minLenTag; } } private byte _lenTag; private ExtensionTagType _tagType; private byte[] _value; private byte[] _padding; private Uf2 m_root; private Uf2.BlockData m_parent; /// /// 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. /// public byte LenTag { get { return _lenTag; } } public ExtensionTagType TagType { get { return _tagType; } } public byte[] Value { get { return _value; } } /// /// Tags are 4-byte aligned, so a tag whose size is not a multiple /// of 4 is followed by padding. /// public byte[] Padding { get { return _padding; } } public Uf2 M_Root { get { return m_root; } } public Uf2.BlockData M_Parent { get { return m_parent; } } } /// /// Reference: Source /// /// /// Reference: Git tag "2.3.0" /// public partial class Flags : KaitaiStruct { public static Flags FromFile(string fileName) { return new Flags(new KaitaiStream(fileName)); } public Flags(KaitaiStream p__io, Uf2.Block p__parent = null, Uf2 p__root = null) : base(p__io) { m_parent = p__parent; m_root = p__root; f_hasExtensionTags = false; f_hasFamilyId = false; f_hasMd5Checksum = false; f_isFileContainer = false; f_notMainFlash = false; _read(); } private void _read() { _value = m_io.ReadU4le(); { uint M_ = _value; if (!( (((M_ & ~61441) == 0) && (!( ((IsFileContainer) && (HasExtensionTags)) ))) )) { throw new ValidationExprError(_value, m_io, "/types/flags/seq/0"); } } } private bool f_hasExtensionTags; private bool _hasExtensionTags; /// /// Indicates whether extension tags are present after the payload. /// public bool HasExtensionTags { get { if (f_hasExtensionTags) return _hasExtensionTags; f_hasExtensionTags = true; _hasExtensionTags = (bool) ((Value & 32768) != 0); return _hasExtensionTags; } } private bool f_hasFamilyId; private bool _hasFamilyId; /// /// The field at offset 28 in the block is `family_id` instead of /// `file_size`. /// public bool HasFamilyId { get { if (f_hasFamilyId) return _hasFamilyId; f_hasFamilyId = true; _hasFamilyId = (bool) ((Value & 8192) != 0); return _hasFamilyId; } } private bool f_hasMd5Checksum; private bool _hasMd5Checksum; /// /// Indicates whether `md5_checksum` is present at the end of `data`. /// public bool HasMd5Checksum { get { if (f_hasMd5Checksum) return _hasMd5Checksum; f_hasMd5Checksum = true; _hasMd5Checksum = (bool) ((Value & 16384) != 0); return _hasMd5Checksum; } } private bool f_isFileContainer; private bool _isFileContainer; /// /// 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`. /// public bool IsFileContainer { get { if (f_isFileContainer) return _isFileContainer; f_isFileContainer = true; _isFileContainer = (bool) ((Value & 4096) != 0); return _isFileContainer; } } private bool f_notMainFlash; private bool _notMainFlash; /// /// 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. /// public bool NotMainFlash { get { if (f_notMainFlash) return _notMainFlash; f_notMainFlash = true; _notMainFlash = (bool) ((Value & 1) != 0); return _notMainFlash; } } private uint _value; private Uf2 m_root; private Uf2.Block m_parent; /// /// 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. /// public uint Value { get { return _value; } } public Uf2 M_Root { get { return m_root; } } public Uf2.Block M_Parent { get { return m_parent; } } } /// /// Reference: Source /// public partial class Md5Checksum : KaitaiStruct { public static Md5Checksum FromFile(string fileName) { return new Md5Checksum(new KaitaiStream(fileName)); } public Md5Checksum(KaitaiStream p__io, Uf2.BlockData p__parent = null, Uf2 p__root = null) : base(p__io) { m_parent = p__parent; m_root = p__root; _read(); } private void _read() { _startAddress = m_io.ReadU4le(); _lenRegion = m_io.ReadU4le(); _md5 = m_io.ReadBytes(16); } private uint _startAddress; private uint _lenRegion; private byte[] _md5; private Uf2 m_root; private Uf2.BlockData m_parent; public uint StartAddress { get { return _startAddress; } } public uint LenRegion { get { return _lenRegion; } } public byte[] Md5 { get { return _md5; } } public Uf2 M_Root { get { return m_root; } } public Uf2.BlockData M_Parent { get { return m_parent; } } } private Block _firstBlock; private List _blocks; private Uf2 m_root; private KaitaiStruct m_parent; public Block FirstBlock { get { return _firstBlock; } } public List Blocks { get { return _blocks; } } public Uf2 M_Root { get { return m_root; } } public KaitaiStruct M_Parent { get { return m_parent; } } } }