U-Boot Image wrapper: Python (read-write) parsing library

The legacy U-Boot image format (uImage), as created by mkimage: a 64-byte header followed by the image data (e.g. a Linux kernel, a ramdisk or a script). The header describes the data (OS, CPU architecture, image type, compression, load and entry point addresses, name) and protects both the header and the data with CRC-32 checksums.

U-Boot's newer FIT (Flattened Image Tree) format is not covered by this spec.

Application

U-Boot

File extension

["bin", "img", "scr", "trx", "bix", "uimg"]

KS implementation details

License: CC0-1.0
Minimal Kaitai Struct required: 0.11

References

This page hosts a formal specification of U-Boot Image wrapper using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.

Python (read-write) source code to parse U-Boot Image wrapper

uimage.py

# This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
# type: ignore

import kaitaistruct
from kaitaistruct import ReadWriteKaitaiStruct, KaitaiStream, BytesIO
from enum import IntEnum


if getattr(kaitaistruct, 'API_VERSION', (0, 9)) < (0, 11):
    raise Exception("Incompatible Kaitai Struct Python API: 0.11 or later is required, but you have %s" % (kaitaistruct.__version__))

class Uimage(ReadWriteKaitaiStruct):
    """The legacy U-Boot image format (uImage), as created by `mkimage`: a 64-byte
    header followed by the image data (e.g. a Linux kernel, a ramdisk or a
    script). The header describes the data (OS, CPU architecture, image type,
    compression, load and entry point addresses, name) and protects both the
    header and the data with CRC-32 checksums.
    
    U-Boot's newer FIT (Flattened Image Tree) format is not covered by this spec.
    
    .. seealso::
       Git tag "v2026.07" - https://github.com/u-boot/u-boot/blob/ece349ade2973e220f524ce59e59711cc919263f/include/image.h
    """

    class MagicTypes(IntEnum):
        allnet_all_sg8316m = 5
        allnet_all_sg8208m = 6
        allnet_all_sg8324m = 22
        datto_l8 = 7353344
        allnet_all_wapc0450c = 9453568
        engenius_ews2910p_v3 = 50398464
        engenius_ews1200_28tfp = 50402048
        engenius_ews7926efp = 50402050
        engenius_ews7952p = 50406401
        engenius_ews2908p = 50409728
        engenius_ews7928p_v2 = 51450112
        engenius_ews1200_28tfp_v2 = 51450624
        engenius_ews2910p = 58730768
        engenius_ews5912fp = 58743058
        engenius_ews7928p = 58751272
        engenius_ews1200d_10t = 59187216
        engenius_ews1200_28t = 59187240
        engenius_ews7928fp = 59216527
        engenius_ews1200_52t = 60235858
        engenius_ews7952fp = 60265775
        youku_yk_l1 = 304680960
        realtek_sdk = 305418240
        netgear_gs750e = 391006017
        netgear_gs728tp_v2 = 391006018
        bolt_bl100 = 638656533
        uimage = 654645590
        netgear_wnce4004 = 654645607
        netgear_wnr612_v2_early = 808857906
        netgear_wnr1000_v2_vc = 825241648
        netgear_wnr1000_v2 = 825241649
        netgear_wnr1000_v2_vm = 825241650
        netgear_wnr1000_v4 = 825241652
        netgear_wpn824n = 825307184
        zyxel_uag50 = 827343191
        netgear_wnr2000 = 842018865
        netgear_wnr2000_v3 = 842018867
        netgear_wnr2000_v4 = 842018868
        netgear_wnr612_v2 = 842020401
        netgear_wnr2200 = 842149936
        netgear_wndr3700 = 859254832
        netgear_wndr3700_v2 = 859254833
        netgear_wndr3700_v4 = 859254835
        netgear_r6100 = 909127728
        netgear_wnr612 = 909193778
        mitrastar_comb = 1129270594
        mitrastar_comc = 1129270595
        netgear_gs308t = 1313293109
        netgear_nge = 1313293600
        netgear_ngf = 1313293856
        netgear_ngg = 1313294112
        netgear_ngh = 1313294368
        netgear_ngi = 1313294624
        netgear_ngp = 1313296416
        openwrt_okli = 1330334793
        openwrt_dlink_covr = 1752396147
        openwrt_senao_okli = 1936805707
        engenius_ecs1112fp = 2155872258
        engenius_fitswitch = 2155872259
        engenius_ews1200_28tfp_old_fw = 2197885696
        engenius_ews7926efp_old_fw = 2197885698
        engenius_ews7952p_old_fw = 2197890049
        realtek_rtl838x = 2206203904
        iodata_bsh_g08m = 2206203905
        iodata_bsh_gp08 = 2206203906
        iodata_bsh_g16m = 2206203907
        iodata_bsh_g24m = 2206203908
        iodata_bsh_g08mb = 2206203920
        iodata_bsh_gp08mb = 2206203921
        iodata_bsh_g16mb = 2206203922
        iodata_bsh_g24mb = 2206203923
        trendnet_tpe_3012ls = 2206210573
        trendnet_tpe_3018ls = 2206210574
        engenius_egs2108p_old_fw = 2206212360
        engenius_egs2110p_old_fw = 2206212368
        engenius_ews2910p_old_fw = 2206214416
        engenius_egs5110p_old_fw = 2206224656
        engenius_egs5212fp_old_fw = 2206224914
        engenius_ews5912fp_old_fw = 2206226706
        engenius_ews7928p_old_fw = 2206234920
        engenius_ews1200d_10t_old_fw = 2206670864
        engenius_ews1200_28t_old_fw = 2206670888
        engenius_egs7228fp_old_fw = 2206671503
        engenius_ews7928fp_old_fw = 2206700175
        realtek_rtl839x = 2207252480
        realtek_rtl8396 = 2207645696
        engenius_ews1200_52t_old_fw = 2207719506
        engenius_egs7252fp_old_fw = 2207720751
        engenius_ews7952fp_old_fw = 2207749423
        realtek_rtl930x = 2466250752
        zyxel_xgs1010_12 = 2466254864
        zyxel_xgs1210_12 = 2466255376
        zyxel_xgs1250_12 = 2466255440
        nicgiga_s100_0800s_m = 2466447360
        plasmacloud_psx28 = 2467299328

    class UimageArch(IntEnum):
        invalid = 0
        alpha = 1
        arm = 2
        i386 = 3
        ia64 = 4
        mips = 5
        mips64 = 6
        ppc = 7
        s390 = 8
        sh = 9
        sparc = 10
        sparc64 = 11
        m68k = 12
        nios = 13
        microblaze = 14
        nios2 = 15
        blackfin = 16
        avr32 = 17
        st200 = 18
        sandbox = 19
        nds32 = 20
        openrisc = 21
        arm64 = 22
        arc = 23
        x86_64 = 24
        xtensa = 25
        riscv = 26

    class UimageComp(IntEnum):
        none = 0
        gzip = 1
        bzip2 = 2
        lzma = 3
        lzo = 4
        lz4 = 5
        zstd = 6
        mz = 9
        xip = 10

    class UimageOs(IntEnum):
        invalid = 0
        openbsd = 1
        netbsd = 2
        freebsd = 3
        bsd4_4 = 4
        linux = 5
        svr4 = 6
        esix = 7
        solaris = 8
        irix = 9
        sco = 10
        dell = 11
        ncr = 12
        lynxos = 13
        vxworks = 14
        psos = 15
        qnx = 16
        u_boot = 17
        rtems = 18
        artos = 19
        unity = 20
        integrity = 21
        ose = 22
        plan9 = 23
        openrtos = 24
        arm_trusted_firmware = 25
        tee = 26
        opensbi = 27
        efi = 28
        elf = 29

    class UimageType(IntEnum):
        invalid = 0
        standalone = 1
        kernel = 2
        ramdisk = 3
        multi = 4
        firmware = 5
        script = 6
        filesystem = 7
        flatdt = 8
        kwbimage = 9
        imximage = 10
        ublimage = 11
        omapimage = 12
        aisimage = 13
        kernel_noload = 14
        pblimage = 15
        mxsimage = 16
        gpimage = 17
        atmelimage = 18
        socfpgaimage = 19
        x86_setup = 20
        lpc32xximage = 21
        loadable = 22
        rkimage = 23
        rksd = 24
        rkspi = 25
        zynqimage = 26
        zynqmpimage = 27
        zynqmpbif = 28
        fpga = 29
        vybridimage = 30
        tee = 31
        firmware_ivt = 32
        pmmc = 33
        stm32image = 34
        socfpgaimage_v1 = 35
        mtkimage = 36
        imx8mimage = 37
        imx8image = 38
        copro = 39
        sunxi_egon = 40
        sunxi_toc0 = 41
        fdt_legacy = 42
        renesas_spkg = 43
        starfive_spl = 44
        tfa_bl31 = 45
        stm32image_v2 = 46
        amlimage = 47
    def __init__(self, _io=None, _parent=None, _root=None):
        super(Uimage, self).__init__(_io)
        self._parent = _parent
        self._root = _root or self

    def _read(self):
        self.header = Uimage.Uheader(self._io, self, self._root)
        self.header._read()
        self.data = self._io.read_bytes(self.header.len_image)
        self._dirty = False


    def _fetch_instances(self):
        pass
        self.header._fetch_instances()


    def _write__seq(self, io=None):
        super(Uimage, self)._write__seq(io)
        self.header._write__seq(self._io)
        self._io.write_bytes(self.data)


    def _check(self):
        if self.header._root != self._root:
            raise kaitaistruct.ConsistencyError(u"header", self._root, self.header._root)
        if self.header._parent != self:
            raise kaitaistruct.ConsistencyError(u"header", self, self.header._parent)
        if len(self.data) != self.header.len_image:
            raise kaitaistruct.ConsistencyError(u"data", self.header.len_image, len(self.data))
        self._dirty = False

    class AsusFirmwareInformation(ReadWriteKaitaiStruct):
        """ASUS firmware images [overlay the 32-byte image
        name](https://github.com/drag0njoe/RT-AC55U/blob/cf5874684ab6995ee07e91a52dfdd0562fcc2655/release/src/asustools/mkimage.src/include/image.h#L185-L188)
        with a [`TAIL`
        structure](https://github.com/drag0njoe/RT-AC55U/blob/cf5874684ab6995ee07e91a52dfdd0562fcc2655/release/src/asustools/mkimage.src/include/image.h#L151-L171)
        that contains version information and the product ID (model name).
        
        The first 16 bytes have the same structure in all three known variants of
        the `TAIL` structure. The meaning of the last 16 bytes differs across the
        variants - therefore, for simplicity, this Kaitai Struct implementation
        treats them as an opaque byte array named `extra_info`. For an overview of
        the variants, see the documentation for `extra_info`.
        """
        def __init__(self, _io=None, _parent=None, _root=None):
            super(Uimage.AsusFirmwareInformation, self).__init__(_io)
            self._parent = _parent
            self._root = _root

        def _read(self):
            self.kernel_version = Uimage.Version(self._io, self, self._root)
            self.kernel_version._read()
            self.fs_version = Uimage.Version(self._io, self, self._root)
            self.fs_version._read()
            self.product_id = (KaitaiStream.bytes_terminate(self._io.read_bytes(12), 0, False)).decode(u"UTF-8")
            self.extra_info = self._io.read_bytes(16)
            self._dirty = False


        def _fetch_instances(self):
            pass
            self.kernel_version._fetch_instances()
            self.fs_version._fetch_instances()


        def _write__seq(self, io=None):
            super(Uimage.AsusFirmwareInformation, self)._write__seq(io)
            self.kernel_version._write__seq(self._io)
            self.fs_version._write__seq(self._io)
            self._io.write_bytes_limit((self.product_id).encode(u"UTF-8"), 12, 0, 0)
            self._io.write_bytes(self.extra_info)


        def _check(self):
            if self.kernel_version._root != self._root:
                raise kaitaistruct.ConsistencyError(u"kernel_version", self._root, self.kernel_version._root)
            if self.kernel_version._parent != self:
                raise kaitaistruct.ConsistencyError(u"kernel_version", self, self.kernel_version._parent)
            if self.fs_version._root != self._root:
                raise kaitaistruct.ConsistencyError(u"fs_version", self._root, self.fs_version._root)
            if self.fs_version._parent != self:
                raise kaitaistruct.ConsistencyError(u"fs_version", self, self.fs_version._parent)
            if len((self.product_id).encode(u"UTF-8")) > 12:
                raise kaitaistruct.ConsistencyError(u"product_id", 12, len((self.product_id).encode(u"UTF-8")))
            if KaitaiStream.byte_array_index_of((self.product_id).encode(u"UTF-8"), 0) != -1:
                raise kaitaistruct.ConsistencyError(u"product_id", -1, KaitaiStream.byte_array_index_of((self.product_id).encode(u"UTF-8"), 0))
            if len(self.extra_info) != 16:
                raise kaitaistruct.ConsistencyError(u"extra_info", 16, len(self.extra_info))
            self._dirty = False


    class NameOrAsusInfo(ReadWriteKaitaiStruct):
        def __init__(self, _io=None, _parent=None, _root=None):
            super(Uimage.NameOrAsusInfo, self).__init__(_io)
            self._parent = _parent
            self._root = _root
            self._should_write_byte0 = False
            self.byte0__enabled = True
            self._should_write_byte4 = False
            self.byte4__enabled = True

        def _read(self):
            if (not (self.has_asus_info)):
                pass
                self.name = (self._io.read_bytes_term(0, False, True, False)).decode(u"UTF-8")

            if self.has_asus_info:
                pass
                self.asus_info = Uimage.AsusFirmwareInformation(self._io, self, self._root)
                self.asus_info._read()

            self._dirty = False


        def _fetch_instances(self):
            pass
            if (not (self.has_asus_info)):
                pass

            if self.has_asus_info:
                pass
                self.asus_info._fetch_instances()

            _ = self.byte0
            if hasattr(self, '_m_byte0'):
                pass

            _ = self.byte4
            if hasattr(self, '_m_byte4'):
                pass



        def _write__seq(self, io=None):
            super(Uimage.NameOrAsusInfo, self)._write__seq(io)
            self._should_write_byte0 = self.byte0__enabled
            self._should_write_byte4 = self.byte4__enabled
            if (not (self.has_asus_info)):
                pass
                if KaitaiStream.byte_array_index_of((self.name).encode(u"UTF-8"), 0) != -1:
                    raise kaitaistruct.ConsistencyError(u"name", -1, KaitaiStream.byte_array_index_of((self.name).encode(u"UTF-8"), 0))
                self._io.write_bytes((self.name).encode(u"UTF-8"))
                if not self._io.is_eof():
                    self._io.write_u1(0)


            if self.has_asus_info:
                pass
                if self.asus_info._root != self._root:
                    raise kaitaistruct.ConsistencyError(u"asus_info", self._root, self.asus_info._root)
                if self.asus_info._parent != self:
                    raise kaitaistruct.ConsistencyError(u"asus_info", self, self.asus_info._parent)
                self.asus_info._write__seq(self._io)



        def _check(self):
            if self.byte0__enabled:
                pass

            if self.byte4__enabled:
                pass

            self._dirty = False

        @property
        def byte0(self):
            if self._should_write_byte0:
                self._write_byte0()
            if hasattr(self, '_m_byte0'):
                return self._m_byte0

            if not self.byte0__enabled:
                return None

            _pos = self._io.pos()
            self._io.seek(0)
            self._m_byte0 = self._io.read_u1()
            self._io.seek(_pos)
            return getattr(self, '_m_byte0', None)

        @byte0.setter
        def byte0(self, v):
            self._dirty = True
            self._m_byte0 = v

        def _write_byte0(self):
            self._should_write_byte0 = False
            _pos = self._io.pos()
            self._io.seek(0)
            self._io.write_u1(self._m_byte0)
            self._io.seek(_pos)

        @property
        def byte4(self):
            if self._should_write_byte4:
                self._write_byte4()
            if hasattr(self, '_m_byte4'):
                return self._m_byte4

            if not self.byte4__enabled:
                return None

            _pos = self._io.pos()
            self._io.seek(4)
            self._m_byte4 = self._io.read_u1()
            self._io.seek(_pos)
            return getattr(self, '_m_byte4', None)

        @byte4.setter
        def byte4(self, v):
            self._dirty = True
            self._m_byte4 = v

        def _write_byte4(self):
            self._should_write_byte4 = False
            _pos = self._io.pos()
            self._io.seek(4)
            self._io.write_u1(self._m_byte4)
            self._io.seek(_pos)

        @property
        def has_asus_info(self):
            """Whether this type stores ASUS firmware information (`asus_info`)
            rather than an image name (`name`).
            
            The header doesn't say which one it is, so we infer it using a
            heuristic based on the layout of the `asus_firmware_information` type.
            If ASUS firmware info is present, the first byte (the major kernel
            version number) is a small integer (i.e. not a printable character),
            and byte 4 (first byte of the product ID/model name) is a printable
            character. An empty name is not mistaken for ASUS information, because
            its byte 4 is a null byte.
            
            The highest known major kernel version number is 9 (e.g. in
            `DSL-N55U_9.0.0.4_380_3925-gad8f412_Annex_A.trx` from
            <https://dlcdnets.asus.com/pub/ASUS/wireless/DSL-N55U/FW_DSL_N55U_90043803925.zip>,
            released on 2016-08-04 and listed on
            <https://www.asus.com/supportonly/dsl-n55u/helpdesk_bios/>). ASUS
            seems to use such high major versions (9, sometimes 7) for beta
            releases, whereas regular releases use 3 (or 1 in older firmware).
            
            It can also be 0, in particular due to a bug in the MediaTek MT798X
            build of ASUS's `mkimage` (it reads the `-V` values from fixed `argv`
            positions, even though it parses the options using `getopt()` - see
            <https://github.com/blocktrron/tuf-ax4200-gpl/blob/a4fe37f4d78473f97c6d47cf93d97c3811af90cc/release/src-mtk-MT798X/Uboot-upstream/tools/mkimage.c#L317-L333>).
            For example, this is the case in
            `RT-AX52_3.0.0.4_388_34015-g9488655.trx` from
            <https://dlcdnets.asus.com/pub/ASUS/wireless/RT-AX52/FW_RT-AX52_300438834015.zip>,
            released on 2026-03-20 and listed on
            <https://www.asus.com/supportonly/rt-ax52/helpdesk_bios?model2Name=RT-AX52>.
            """
            if hasattr(self, '_m_has_asus_info'):
                return self._m_has_asus_info

            self._m_has_asus_info =  ((self.byte0 < 32) and (self.byte4 >= 32) and (self.byte4 < 127)) 
            return getattr(self, '_m_has_asus_info', None)

        def _invalidate_has_asus_info(self):
            del self._m_has_asus_info

    class Uheader(ReadWriteKaitaiStruct):
        def __init__(self, _io=None, _parent=None, _root=None):
            super(Uimage.Uheader, self).__init__(_io)
            self._parent = _parent
            self._root = _root

        def _read(self):
            self.magic = KaitaiStream.resolve_enum(Uimage.MagicTypes, self._io.read_u4be())
            if not isinstance(self.magic, Uimage.MagicTypes):
                raise kaitaistruct.ValidationNotInEnumError(self.magic, self._io, u"/types/uheader/seq/0")
            self.header_crc = self._io.read_u4be()
            self.timestamp = self._io.read_u4be()
            self.len_image = self._io.read_u4be()
            self.load_address = self._io.read_u4be()
            self.entry_address = self._io.read_u4be()
            self.data_crc = self._io.read_u4be()
            self.os_type = KaitaiStream.resolve_enum(Uimage.UimageOs, self._io.read_u1())
            if not isinstance(self.os_type, Uimage.UimageOs):
                raise kaitaistruct.ValidationNotInEnumError(self.os_type, self._io, u"/types/uheader/seq/7")
            self.architecture = KaitaiStream.resolve_enum(Uimage.UimageArch, self._io.read_u1())
            if not isinstance(self.architecture, Uimage.UimageArch):
                raise kaitaistruct.ValidationNotInEnumError(self.architecture, self._io, u"/types/uheader/seq/8")
            self.image_type = KaitaiStream.resolve_enum(Uimage.UimageType, self._io.read_u1())
            if not isinstance(self.image_type, Uimage.UimageType):
                raise kaitaistruct.ValidationNotInEnumError(self.image_type, self._io, u"/types/uheader/seq/9")
            self.compression_type = KaitaiStream.resolve_enum(Uimage.UimageComp, self._io.read_u1())
            if not isinstance(self.compression_type, Uimage.UimageComp):
                raise kaitaistruct.ValidationNotInEnumError(self.compression_type, self._io, u"/types/uheader/seq/10")
            self._raw_name_or_asus_info = self._io.read_bytes(32)
            _io__raw_name_or_asus_info = KaitaiStream(BytesIO(self._raw_name_or_asus_info))
            self.name_or_asus_info = Uimage.NameOrAsusInfo(_io__raw_name_or_asus_info, self, self._root)
            self.name_or_asus_info._read()
            self._dirty = False


        def _fetch_instances(self):
            pass
            self.name_or_asus_info._fetch_instances()


        def _write__seq(self, io=None):
            super(Uimage.Uheader, self)._write__seq(io)
            self._io.write_u4be(int(self.magic))
            self._io.write_u4be(self.header_crc)
            self._io.write_u4be(self.timestamp)
            self._io.write_u4be(self.len_image)
            self._io.write_u4be(self.load_address)
            self._io.write_u4be(self.entry_address)
            self._io.write_u4be(self.data_crc)
            self._io.write_u1(int(self.os_type))
            self._io.write_u1(int(self.architecture))
            self._io.write_u1(int(self.image_type))
            self._io.write_u1(int(self.compression_type))
            _io__raw_name_or_asus_info = KaitaiStream(BytesIO(bytearray(32)))
            self._io.add_child_stream(_io__raw_name_or_asus_info)
            _pos2 = self._io.pos()
            self._io.seek(self._io.pos() + (32))
            def handler(parent, _io__raw_name_or_asus_info=_io__raw_name_or_asus_info):
                self._raw_name_or_asus_info = _io__raw_name_or_asus_info.to_byte_array()
                if len(self._raw_name_or_asus_info) != 32:
                    raise kaitaistruct.ConsistencyError(u"raw(name_or_asus_info)", 32, len(self._raw_name_or_asus_info))
                parent.write_bytes(self._raw_name_or_asus_info)
            _io__raw_name_or_asus_info.write_back_handler = KaitaiStream.WriteBackHandler(_pos2, handler)
            self.name_or_asus_info._write__seq(_io__raw_name_or_asus_info)


        def _check(self):
            if not isinstance(self.magic, Uimage.MagicTypes):
                raise kaitaistruct.ValidationNotInEnumError(self.magic, None, u"/types/uheader/seq/0")
            if not isinstance(self.os_type, Uimage.UimageOs):
                raise kaitaistruct.ValidationNotInEnumError(self.os_type, None, u"/types/uheader/seq/7")
            if not isinstance(self.architecture, Uimage.UimageArch):
                raise kaitaistruct.ValidationNotInEnumError(self.architecture, None, u"/types/uheader/seq/8")
            if not isinstance(self.image_type, Uimage.UimageType):
                raise kaitaistruct.ValidationNotInEnumError(self.image_type, None, u"/types/uheader/seq/9")
            if not isinstance(self.compression_type, Uimage.UimageComp):
                raise kaitaistruct.ValidationNotInEnumError(self.compression_type, None, u"/types/uheader/seq/10")
            if self.name_or_asus_info._root != self._root:
                raise kaitaistruct.ConsistencyError(u"name_or_asus_info", self._root, self.name_or_asus_info._root)
            if self.name_or_asus_info._parent != self:
                raise kaitaistruct.ConsistencyError(u"name_or_asus_info", self, self.name_or_asus_info._parent)
            self._dirty = False


    class Version(ReadWriteKaitaiStruct):
        def __init__(self, _io=None, _parent=None, _root=None):
            super(Uimage.Version, self).__init__(_io)
            self._parent = _parent
            self._root = _root

        def _read(self):
            self.major = self._io.read_u1()
            self.minor = self._io.read_u1()
            self._dirty = False


        def _fetch_instances(self):
            pass


        def _write__seq(self, io=None):
            super(Uimage.Version, self)._write__seq(io)
            self._io.write_u1(self.major)
            self._io.write_u1(self.minor)


        def _check(self):
            self._dirty = False