1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (C) 2015 Thomas Hebb <tommyhebb@gmail.com>
4 * Copyright (C) 2016 Christian Lamparter <chunkeey@googlemail.com>
6 * The format of the header this tool generates was first documented by
7 * Chris Blake <chrisrblake93 (at) gmail.com> in a shell script of the
8 * same purpose. I have created this reimplementation at his request. The
9 * original script can be found at:
10 * <https://github.com/riptidewave93/meraki-partbuilder>
12 * Support for the old header format, which is used by the Cisco Z1 AP
13 * has been reverse engineered from the nandloader's nand_load_bk function.
14 * The original code is part of Cisco's GPL code and can be found at:
15 * <https://github.com/riptidewave93/meraki-linux>
26 #include <arpa/inet.h>
28 #define PADDING_BYTE 0xff
30 #define HDR_LENGTH 0x00000020
31 #define HDR_OFF_MAGIC1 0
32 #define HDR_OFF_LOAD_ADDR 4
33 #define HDR_OFF_IMAGELEN 8
34 #define HDR_OFF_ENTRY 12
35 #define HDR_OFF_CHECKSUM 16
36 #define HDR_OFF_FILLER0 20
37 #define HDR_OFF_FILLER1 24
38 #define HDR_OFF_FILLER2 28
53 static char *progname
;
54 static bool strip_padding
;
56 static char *board_id
;
57 static const struct board_info
*board
;
59 static const struct board_info boards
[] = {
62 .description
= "Meraki Z1 Access Point",
64 .imagelen
= 0x007e0000,
65 .load_addr
= 0x80060000,
70 .description
= "Meraki MX64/MX65",
72 .imagelen
= 0x00000000,
73 .load_addr
= 0x60008000,
77 /* terminating entry */
84 #define ERR(fmt, ...) do { \
86 fprintf(stderr, "[%s] *** error: " fmt "\n", \
87 progname, ## __VA_ARGS__); \
90 #define ERRS(fmt, ...) do { \
93 fprintf(stderr, "[%s] *** error: " fmt "\n", \
94 progname, ## __VA_ARGS__, strerror(save)); \
97 static const struct board_info
*find_board(const char *id
)
99 const struct board_info
*ret
;
100 const struct board_info
*board
;
103 for (board
= boards
; board
->id
!= NULL
; board
++) {
104 if (strcasecmp(id
, board
->id
) == 0) {
113 static void usage(int status
)
115 FILE *stream
= (status
!= EXIT_SUCCESS
) ? stderr
: stdout
;
116 const struct board_info
*board
;
118 fprintf(stream
, "Usage: %s [OPTIONS...]\n", progname
);
122 " -B <board> create image for the board specified with <board>\n"
123 " -i <file> read kernel image from the file <file>\n"
124 " -o <file> write output to the file <file>\n"
125 " -s strip padding from the end of the image\n"
126 " -h show this screen\n"
129 fprintf(stream
, "\nBoards:\n");
130 for (board
= boards
; board
->id
!= NULL
; board
++)
131 fprintf(stream
, " %-16s%s\n", board
->id
, board
->description
);
136 static void writel(unsigned char *buf
, size_t offset
, uint32_t value
, bool le32
)
139 value
= htonl(value
);
141 memcpy(buf
+ offset
, &value
, sizeof(uint32_t));
144 static const uint32_t crc32_table
[] = {
145 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
146 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
147 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
148 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
149 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
150 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
151 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
152 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
153 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
154 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
155 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
156 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
157 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
158 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
159 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
160 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
161 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
162 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
163 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
164 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
165 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
166 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
167 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
168 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
169 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
170 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
171 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
172 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
173 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
174 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
175 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
176 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
177 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
178 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
179 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
180 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
181 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
182 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
183 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
184 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
185 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
186 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
187 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
188 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
189 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
190 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
191 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
192 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
193 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
194 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
195 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
196 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
197 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
198 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
199 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
200 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
201 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
202 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
203 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
204 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
205 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
206 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
207 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
208 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
211 static inline uint32_t crc32_accumulate_8(const uint32_t crc
, const uint8_t ch
)
213 return crc32_table
[(crc
^ ch
) & 0xff] ^ (crc
>> 8);
216 static void crc32_csum(uint8_t *buf
, const size_t len
, bool le32
)
226 for (i
= 0; i
< len
; i
+= 4) {
227 for (j
= 3; j
>= 0; j
-= 1) {
229 crc
= crc32_accumulate_8(crc
, buf
[i
+ m
]);
234 writel(buf
, HDR_OFF_CHECKSUM
, crc
, le32
);
238 static int meraki_build_hdr(const struct board_info
*board
, const size_t klen
,
241 unsigned char *kernel
;
247 buflen
= board
->imagelen
;
250 kspace
= buflen
- HDR_LENGTH
;
253 ERR("kernel file is too big - max size: 0x%08lX\n", kspace
);
258 /* If requested, resize buffer to remove padding */
260 buflen
= klen
+ HDR_LENGTH
;
262 /* Allocate and initialize buffer for final image */
263 buf
= malloc(buflen
);
265 ERRS("no memory for buffer: %s\n");
268 memset(buf
, PADDING_BYTE
, buflen
);
271 kernel
= buf
+ HDR_LENGTH
;
272 fread(kernel
, klen
, 1, in
);
274 /* Write magic values and filler */
275 writel(buf
, HDR_OFF_MAGIC1
, board
->magic
, board
->le32
);
276 writel(buf
, HDR_OFF_FILLER0
, 0, board
->le32
);
277 writel(buf
, HDR_OFF_FILLER1
, 0, board
->le32
);
278 writel(buf
, HDR_OFF_FILLER2
, 0, board
->le32
);
280 /* Write load and kernel entry point address */
281 writel(buf
, HDR_OFF_LOAD_ADDR
, board
->load_addr
, board
->le32
);
282 writel(buf
, HDR_OFF_ENTRY
, board
->entry
, board
->le32
);
284 /* Write header and image length */
285 writel(buf
, HDR_OFF_IMAGELEN
, klen
, board
->le32
);
287 /* this gets replaced later, after the checksum has been calculated */
288 writel(buf
, HDR_OFF_CHECKSUM
, 0, board
->le32
);
291 crc32_csum(buf
, klen
+ HDR_LENGTH
, board
->le32
);
293 rc
= fwrite(buf
, buflen
, 1, out
);
297 return rc
== 1 ? EXIT_SUCCESS
: EXIT_FAILURE
;
300 int main(int argc
, char *argv
[])
302 int ret
= EXIT_FAILURE
;
303 char *ofname
= NULL
, *ifname
= NULL
;
307 progname
= basename(argv
[0]);
312 c
= getopt(argc
, argv
, "B:i:o:sh");
327 strip_padding
= true;
338 if (board_id
== NULL
) {
339 ERR("no board specified");
343 board
= find_board(board_id
);
345 ERR("unknown board \"%s\"", board_id
);
349 if (ifname
== NULL
) {
350 ERR("no input file specified");
354 if (ofname
== NULL
) {
355 ERR("no output file specified");
359 in
= fopen(ifname
, "r");
361 ERRS("could not open \"%s\" for reading: %s", ifname
);
365 /* Get kernel length */
366 fseek(in
, 0, SEEK_END
);
370 out
= fopen(ofname
, "w");
372 ERRS("could not open \"%s\" for writing: %s", ofname
);
376 ret
= meraki_build_hdr(board
, klen
, out
, in
);