firmware-utils: mkmerakifw-old: Add le32 support
[openwrt/staging/hauke.git] / tools / firmware-utils / src / mkmerakifw-old.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Copyright (C) 2015 Thomas Hebb <tommyhebb@gmail.com>
4 * Copyright (C) 2016 Christian Lamparter <chunkeey@googlemail.com>
5 *
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>
11 *
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>
16 */
17
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <stdbool.h>
21 #include <string.h>
22 #include <libgen.h>
23 #include <endian.h>
24 #include <getopt.h>
25 #include <errno.h>
26 #include <arpa/inet.h>
27
28 #define PADDING_BYTE 0xff
29
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
39
40 struct board_info {
41 char *id;
42 char *description;
43 uint32_t magic;
44 uint32_t imagelen;
45 uint32_t load_addr;
46 uint32_t entry;
47 bool le32;
48 };
49
50 /*
51 * Globals
52 */
53 static char *progname;
54 static bool strip_padding;
55
56 static char *board_id;
57 static const struct board_info *board;
58
59 static const struct board_info boards[] = {
60 {
61 .id = "z1",
62 .description = "Meraki Z1 Access Point",
63 .magic = 0x4d495053,
64 .imagelen = 0x007e0000,
65 .load_addr = 0x80060000,
66 .entry = 0x80060000,
67 .le32 = 0,
68 }, {
69 .id = "mx64",
70 .description = "Meraki MX64/MX65",
71 .magic = 0x4d495053,
72 .imagelen = 0x00000000,
73 .load_addr = 0x60008000,
74 .entry = 0x60008000,
75 .le32 = 1,
76 }, {
77 /* terminating entry */
78 }
79 };
80
81 /*
82 * Message macros
83 */
84 #define ERR(fmt, ...) do { \
85 fflush(0); \
86 fprintf(stderr, "[%s] *** error: " fmt "\n", \
87 progname, ## __VA_ARGS__); \
88 } while (0)
89
90 #define ERRS(fmt, ...) do { \
91 int save = errno; \
92 fflush(0); \
93 fprintf(stderr, "[%s] *** error: " fmt "\n", \
94 progname, ## __VA_ARGS__, strerror(save)); \
95 } while (0)
96
97 static const struct board_info *find_board(const char *id)
98 {
99 const struct board_info *ret;
100 const struct board_info *board;
101
102 ret = NULL;
103 for (board = boards; board->id != NULL; board++) {
104 if (strcasecmp(id, board->id) == 0) {
105 ret = board;
106 break;
107 }
108 }
109
110 return ret;
111 }
112
113 static void usage(int status)
114 {
115 FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout;
116 const struct board_info *board;
117
118 fprintf(stream, "Usage: %s [OPTIONS...]\n", progname);
119 fprintf(stream,
120 "\n"
121 "Options:\n"
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"
127 );
128
129 fprintf(stream, "\nBoards:\n");
130 for (board = boards; board->id != NULL; board++)
131 fprintf(stream, " %-16s%s\n", board->id, board->description);
132
133 exit(status);
134 }
135
136 static void writel(unsigned char *buf, size_t offset, uint32_t value, bool le32)
137 {
138 if (!le32)
139 value = htonl(value);
140
141 memcpy(buf + offset, &value, sizeof(uint32_t));
142 }
143
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,
209 };
210
211 static inline uint32_t crc32_accumulate_8(const uint32_t crc, const uint8_t ch)
212 {
213 return crc32_table[(crc ^ ch) & 0xff] ^ (crc >> 8);
214 }
215
216 static void crc32_csum(uint8_t *buf, const size_t len, bool le32)
217 {
218 int j, k, l, m;
219 uint32_t crc;
220 size_t i;
221
222 crc = ~0;
223 k = le32 * 3;
224 l = le32 * -2 + 1;
225
226 for (i = 0; i < len; i += 4) {
227 for (j = 3; j >= 0; j -= 1) {
228 m = (j - k) * l;
229 crc = crc32_accumulate_8(crc, buf[i + m]);
230 }
231 }
232 crc = ~crc;
233
234 writel(buf, HDR_OFF_CHECKSUM, crc, le32);
235 }
236
237
238 static int meraki_build_hdr(const struct board_info *board, const size_t klen,
239 FILE *out, FILE *in)
240 {
241 unsigned char *kernel;
242 unsigned char *buf;
243 size_t buflen;
244 size_t kspace;
245
246 size_t rc;
247 buflen = board->imagelen;
248
249 if (buflen > 0) {
250 kspace = buflen - HDR_LENGTH;
251
252 if (klen > kspace) {
253 ERR("kernel file is too big - max size: 0x%08lX\n", kspace);
254 return EXIT_FAILURE;
255 }
256 }
257
258 /* If requested, resize buffer to remove padding */
259 if (strip_padding)
260 buflen = klen + HDR_LENGTH;
261
262 /* Allocate and initialize buffer for final image */
263 buf = malloc(buflen);
264 if (buf == NULL) {
265 ERRS("no memory for buffer: %s\n");
266 return EXIT_FAILURE;
267 }
268 memset(buf, PADDING_BYTE, buflen);
269
270 /* Load kernel */
271 kernel = buf + HDR_LENGTH;
272 fread(kernel, klen, 1, in);
273
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);
279
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);
283
284 /* Write header and image length */
285 writel(buf, HDR_OFF_IMAGELEN, klen, board->le32);
286
287 /* this gets replaced later, after the checksum has been calculated */
288 writel(buf, HDR_OFF_CHECKSUM, 0, board->le32);
289
290 /* Write checksum */
291 crc32_csum(buf, klen + HDR_LENGTH, board->le32);
292
293 rc = fwrite(buf, buflen, 1, out);
294
295 free(buf);
296
297 return rc == 1 ? EXIT_SUCCESS : EXIT_FAILURE;
298 }
299
300 int main(int argc, char *argv[])
301 {
302 int ret = EXIT_FAILURE;
303 char *ofname = NULL, *ifname = NULL;
304 FILE *out, *in;
305 size_t klen;
306
307 progname = basename(argv[0]);
308
309 while (1) {
310 int c;
311
312 c = getopt(argc, argv, "B:i:o:sh");
313 if (c == -1)
314 break;
315
316 switch (c) {
317 case 'B':
318 board_id = optarg;
319 break;
320 case 'i':
321 ifname = optarg;
322 break;
323 case 'o':
324 ofname = optarg;
325 break;
326 case 's':
327 strip_padding = true;
328 break;
329 case 'h':
330 usage(EXIT_SUCCESS);
331 break;
332 default:
333 usage(EXIT_FAILURE);
334 break;
335 }
336 }
337
338 if (board_id == NULL) {
339 ERR("no board specified");
340 goto err;
341 }
342
343 board = find_board(board_id);
344 if (board == NULL) {
345 ERR("unknown board \"%s\"", board_id);
346 goto err;
347 }
348
349 if (ifname == NULL) {
350 ERR("no input file specified");
351 goto err;
352 }
353
354 if (ofname == NULL) {
355 ERR("no output file specified");
356 goto err;
357 }
358
359 in = fopen(ifname, "r");
360 if (in == NULL) {
361 ERRS("could not open \"%s\" for reading: %s", ifname);
362 goto err;
363 }
364
365 /* Get kernel length */
366 fseek(in, 0, SEEK_END);
367 klen = ftell(in);
368 rewind(in);
369
370 out = fopen(ofname, "w");
371 if (out == NULL) {
372 ERRS("could not open \"%s\" for writing: %s", ofname);
373 goto err_close_in;
374 }
375
376 ret = meraki_build_hdr(board, klen, out, in);
377 fclose(out);
378
379 err_close_in:
380 fclose(in);
381
382 err:
383 return ret;
384 }