2 # SPDX-License-Identifier: GPL-2.0-or-later
4 # Copyright (C) 2022 OpenWrt.org
6 # ./cameo-tag.py <uImageFileName> <OffsetOfRootFS>
8 # CAMEO tag generator used for the D-Link DGS-1210 switches. Their U-Boot
9 # loader checks for the string CAMEOTAG and a checksum in the kernel and
10 # rootfs partitions. If not found it complains about the boot image.
11 # Nevertheless it will boot if the tags are available in the secondary
12 # boot partitions. If some day we want to overwrite the original vendor
13 # partition we must have the tags in place. To solve this we insert the
14 # tag two times into the kernel image.
16 # To understand what we do here it is helpful to explain how the original
17 # CAMEO tag generation/checking works. The firmware consists of two parts.
18 # A kernel uImage (<1.5MB) and a rootfs image (<12MB) that are written to
19 # their respective mtd partitions. The default generator simply checksums
20 # both parts and appends 16 bytes [<CAMEOTAG><0001><checksum>] to each part.
21 # The checksum is only an addition of all preceding bytes (b0+b1+b2+...).
22 # A tag does not interfere with any data in the images itself. During boot
23 # the loader will scan all primary/secondary partitions (2*kernel, 2*rootfs)
24 # until it finds the CAMEO tag. If checksums match everything is fine.
25 # If all 4 fail we are lost. Luckily the loader does not care about where
26 # the tags are located and ignores any data beyond a tag.
28 # The OpenWrt image consists of a kernel (>1.5MB) and a rootfs. There is
29 # no chance to add CAMEO tags at the default locations, since the kernel spans
30 # both the original kernel partition and the start of the rootfs partition.
31 # This would leave the kernel partition without a tag. So we must find suitable
34 # Location for original kernel partition is at the end of the uImage header.
35 # We will reuse the last bytes of the IH_NAME field. This is the tricky part
36 # because we have the header CRC and the CAMEO checksum that must match the
37 # whole header. uImage header CRC checksums all data except the CRC itself. The
38 # for CAMEO checksum in turn, checksums all preceding data except itself.
39 # Changing one of both results in a change of the other, but data trailing the
40 # CAMEO checksum only influences the CRC.
42 # Location for original rootfs partition is very simple. It is behind the
43 # OpenWrt compressed kernel image file that spans into the rootfs. So
44 # the tag will be written somewhere to the following rootfs partition and
45 # can be found by U-Boot. The CAMEO checksum calculation must start at the
46 # offset of the original rootfs partition and includes the "second" half of the
47 # "split" kernel uImage.
54 UIMAGE_HEADER_SIZE
= 64
63 CAMEO_TAG
= bytes([0x43, 0x41, 0x4d, 0x45, 0x4f, 0x54, 0x41, 0x47, 0x00, 0x00, 0x00, 0x01])
64 IMAGE_NAME
= bytes([0x4f, 0x70, 0x65, 0x6e, 0x57, 0x72, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00])
65 CRC_00
= bytes([0x00] * 4)
66 CRC_FF
= bytes([0xff] * 4)
68 def read_buffer(offset
, count
):
69 args
.uimage_file
.seek(offset
)
70 return bytearray(args
.uimage_file
.read(count
))
72 def write_buffer(whence
, buf
):
73 args
.uimage_file
.seek(0, whence
)
74 args
.uimage_file
.write(buf
)
77 return (sum(buf
) & 0xffffffff).to_bytes(4, 'big')
80 return (zlib
.crc32(buf
) ^
0xffffffff).to_bytes(4, 'little')
82 def checksum_header(buf
):
83 # To efficently get a combination, we will make use of the following fact:
84 # crc32(data + littleendian(crc32(data) ^ 0xffffffff)) = 0xffffffff
86 # After manipulation the uImage header looks like this:
87 # [...<ffffffff>...<OpenWrt><000000><CAMEOTAG><0001><checksum><InvCRC>]
88 buf
[UIMAGE_NAME_OFF
:UIMAGE_NAME_END
] = IMAGE_NAME
+ CAMEO_TAG
89 buf
[UIMAGE_CRC_OFF
:UIMAGE_CRC_END
] = CRC_FF
90 buf
[UIMAGE_SUM_OFF
:UIMAGE_SUM_END
] = cameosum(buf
[0:UIMAGE_NAME_END
])
91 buf
[UIMAGE_CRC_OFF
:UIMAGE_CRC_END
] = CRC_00
92 buf
[UIMAGE_INV_OFF
:UIMAGE_INV_END
] = invertcrc(buf
[0:UIMAGE_SUM_END
])
93 buf
[UIMAGE_CRC_OFF
:UIMAGE_CRC_END
] = CRC_FF
96 parser
= argparse
.ArgumentParser(description
='Insert CAMEO firmware tags.')
97 parser
.add_argument('uimage_file', type=argparse
.FileType('r+b'))
98 parser
.add_argument('rootfs_start', type=int)
99 args
= parser
.parse_args()
101 args
.uimage_file
.seek(0, os
.SEEK_END
)
102 if args
.uimage_file
.tell() <= args
.rootfs_start
:
103 raise ValueError(f
"uImage must be larger than {args.rootfs_start} bytes")
105 # tag for the uImage Header of 64 bytes inside the kernel
106 # partition. Read and mangle it so it contains a valid CAMEO tag
107 # and checksum that matches perfectly to the uImage header CRC.
109 buf
= checksum_header(read_buffer(0, UIMAGE_HEADER_SIZE
))
110 write_buffer(os
.SEEK_SET
, buf
)
112 # tag for the second part of the kernel that resides in the
113 # vendor rootfs partition. For this we will add the CAMEO tag
114 # and the checksum to the end of the image.
116 buf
= read_buffer(args
.rootfs_start
, READ_UNTIL_EOF
)
117 write_buffer(os
.SEEK_END
, CAMEO_TAG
+ cameosum(buf
+ CAMEO_TAG
))