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