firmware-utils: mkmerakifw-old: replace GPL-2.0-only boilerplate with SPDX
[openwrt/staging/mkresin.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 };
48
49 /*
50 * Globals
51 */
52 static char *progname;
53 static bool strip_padding;
54
55 static char *board_id;
56 static const struct board_info *board;
57
58 static const struct board_info boards[] = {
59 {
60 .id = "z1",
61 .description = "Meraki Z1 Access Point",
62 .magic = 0x4d495053,
63 .imagelen = 0x007e0000,
64 .load_addr = 0x80060000,
65 .entry = 0x80060000
66 }, {
67 /* terminating entry */
68 }
69 };
70
71 /*
72 * Message macros
73 */
74 #define ERR(fmt, ...) do { \
75 fflush(0); \
76 fprintf(stderr, "[%s] *** error: " fmt "\n", \
77 progname, ## __VA_ARGS__); \
78 } while (0)
79
80 #define ERRS(fmt, ...) do { \
81 int save = errno; \
82 fflush(0); \
83 fprintf(stderr, "[%s] *** error: " fmt "\n", \
84 progname, ## __VA_ARGS__, strerror(save)); \
85 } while (0)
86
87 static const struct board_info *find_board(const char *id)
88 {
89 const struct board_info *ret;
90 const struct board_info *board;
91
92 ret = NULL;
93 for (board = boards; board->id != NULL; board++) {
94 if (strcasecmp(id, board->id) == 0) {
95 ret = board;
96 break;
97 }
98 }
99
100 return ret;
101 }
102
103 static void usage(int status)
104 {
105 FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout;
106 const struct board_info *board;
107
108 fprintf(stream, "Usage: %s [OPTIONS...]\n", progname);
109 fprintf(stream,
110 "\n"
111 "Options:\n"
112 " -B <board> create image for the board specified with <board>\n"
113 " -i <file> read kernel image from the file <file>\n"
114 " -o <file> write output to the file <file>\n"
115 " -s strip padding from the end of the image\n"
116 " -h show this screen\n"
117 );
118
119 fprintf(stream, "\nBoards:\n");
120 for (board = boards; board->id != NULL; board++)
121 fprintf(stream, " %-16s%s\n", board->id, board->description);
122
123 exit(status);
124 }
125
126 static void writel(unsigned char *buf, size_t offset, uint32_t value)
127 {
128 value = htonl(value);
129 memcpy(buf + offset, &value, sizeof(uint32_t));
130 }
131
132 static const uint32_t crc32_table[] = {
133 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
134 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
135 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
136 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
137 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
138 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
139 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
140 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
141 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
142 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
143 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
144 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
145 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
146 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
147 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
148 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
149 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
150 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
151 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
152 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
153 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
154 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
155 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
156 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
157 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
158 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
159 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
160 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
161 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
162 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
163 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
164 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
165 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
166 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
167 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
168 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
169 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
170 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
171 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
172 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
173 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
174 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
175 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
176 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
177 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
178 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
179 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
180 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
181 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
182 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
183 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
184 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
185 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
186 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
187 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
188 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
189 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
190 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
191 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
192 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
193 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
194 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
195 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
196 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
197 };
198
199 static inline uint32_t crc32_accumulate_8(const uint32_t crc, const uint8_t ch)
200 {
201 return crc32_table[(crc ^ ch) & 0xff] ^ (crc >> 8);
202 }
203
204 static void crc32_csum(uint8_t *buf, const size_t len)
205 {
206 uint32_t crc;
207 size_t i;
208
209 crc = ~0;
210 for (i = 0; i < len; i += 4) {
211 crc = crc32_accumulate_8(crc, buf[i + 3]);
212 crc = crc32_accumulate_8(crc, buf[i + 2]);
213 crc = crc32_accumulate_8(crc, buf[i + 1]);
214 crc = crc32_accumulate_8(crc, buf[i]);
215 }
216 crc = ~crc;
217
218 writel(buf, HDR_OFF_CHECKSUM, crc);
219 }
220
221
222 static int meraki_build_hdr(const struct board_info *board, const size_t klen,
223 FILE *out, FILE *in)
224 {
225 unsigned char *kernel;
226 unsigned char *buf;
227 size_t buflen;
228 size_t kspace;
229
230 size_t rc;
231 buflen = board->imagelen;
232 kspace = buflen - HDR_LENGTH;
233
234 if (klen > kspace) {
235 ERR("kernel file is too big - max size: 0x%08lX\n", kspace);
236 return EXIT_FAILURE;
237 }
238
239 /* If requested, resize buffer to remove padding */
240 if (strip_padding)
241 buflen = klen + HDR_LENGTH;
242
243 /* Allocate and initialize buffer for final image */
244 buf = malloc(buflen);
245 if (buf == NULL) {
246 ERRS("no memory for buffer: %s\n");
247 return EXIT_FAILURE;
248 }
249 memset(buf, PADDING_BYTE, buflen);
250
251 /* Load kernel */
252 kernel = buf + HDR_LENGTH;
253 fread(kernel, klen, 1, in);
254
255 /* Write magic values and filler */
256 writel(buf, HDR_OFF_MAGIC1, board->magic);
257 writel(buf, HDR_OFF_FILLER0, 0);
258 writel(buf, HDR_OFF_FILLER1, 0);
259 writel(buf, HDR_OFF_FILLER2, 0);
260
261 /* Write load and kernel entry point address */
262 writel(buf, HDR_OFF_LOAD_ADDR, board->load_addr);
263 writel(buf, HDR_OFF_ENTRY, board->entry);
264
265 /* Write header and image length */
266 writel(buf, HDR_OFF_IMAGELEN, klen);
267
268 /* this gets replaced later, after the checksum has been calculated */
269 writel(buf, HDR_OFF_CHECKSUM, 0);
270
271 /* Write checksum */
272 crc32_csum(buf, klen + HDR_LENGTH);
273
274 rc = fwrite(buf, buflen, 1, out);
275
276 free(buf);
277
278 return rc == 1 ? EXIT_SUCCESS : EXIT_FAILURE;
279 }
280
281 int main(int argc, char *argv[])
282 {
283 int ret = EXIT_FAILURE;
284 char *ofname = NULL, *ifname = NULL;
285 FILE *out, *in;
286 size_t klen;
287
288 progname = basename(argv[0]);
289
290 while (1) {
291 int c;
292
293 c = getopt(argc, argv, "B:i:o:sh");
294 if (c == -1)
295 break;
296
297 switch (c) {
298 case 'B':
299 board_id = optarg;
300 break;
301 case 'i':
302 ifname = optarg;
303 break;
304 case 'o':
305 ofname = optarg;
306 break;
307 case 's':
308 strip_padding = true;
309 break;
310 case 'h':
311 usage(EXIT_SUCCESS);
312 break;
313 default:
314 usage(EXIT_FAILURE);
315 break;
316 }
317 }
318
319 if (board_id == NULL) {
320 ERR("no board specified");
321 goto err;
322 }
323
324 board = find_board(board_id);
325 if (board == NULL) {
326 ERR("unknown board \"%s\"", board_id);
327 goto err;
328 }
329
330 if (ifname == NULL) {
331 ERR("no input file specified");
332 goto err;
333 }
334
335 if (ofname == NULL) {
336 ERR("no output file specified");
337 goto err;
338 }
339
340 in = fopen(ifname, "r");
341 if (in == NULL) {
342 ERRS("could not open \"%s\" for reading: %s", ifname);
343 goto err;
344 }
345
346 /* Get kernel length */
347 fseek(in, 0, SEEK_END);
348 klen = ftell(in);
349 rewind(in);
350
351 out = fopen(ofname, "w");
352 if (out == NULL) {
353 ERRS("could not open \"%s\" for writing: %s", ofname);
354 goto err_close_in;
355 }
356
357 ret = meraki_build_hdr(board, klen, out, in);
358 fclose(out);
359
360 err_close_in:
361 fclose(in);
362
363 err:
364 return ret;
365 }