firmware-utils mkmerakifw: firmware generator for MR18
[openwrt/openwrt.git] / tools / firmware-utils / src / mkmerakifw.c
1 /*
2 * Copyright (C) 2015 Thomas Hebb <tommyhebb@gmail.com>
3 *
4 * The format of the header this tool generates was first documented by
5 * Chris Blake <chrisrblake93 (at) gmail.com> in a shell script of the
6 * same purpose. I have created this reimplementation at his request. The
7 * original script can be found at:
8 * <https://github.com/riptidewave93/meraki-partbuilder>
9 *
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License version 2 as published
12 * by the Free Software Foundation.
13 *
14 */
15
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <stdbool.h>
19 #include <string.h>
20 #include <libgen.h>
21 #include <getopt.h>
22 #include <errno.h>
23 #include <arpa/inet.h>
24
25 #include "sha1.h"
26
27 #define PADDING_BYTE 0xff
28
29 #define HDR_LENGTH 0x00000400
30 #define HDR_OFF_MAGIC1 0
31 #define HDR_OFF_HDRLEN 4
32 #define HDR_OFF_IMAGELEN 8
33 #define HDR_OFF_CHECKSUM 12
34 #define HDR_OFF_MAGIC2 32
35 #define HDR_OFF_FILLER 36
36 #define HDR_OFF_STATICHASH 40
37
38 struct board_info {
39 uint32_t magic;
40 uint32_t imagelen;
41 unsigned char statichash[20];
42 char *id;
43 char *description;
44 };
45
46 /*
47 * Globals
48 */
49 static char *progname;
50
51 static char *board_id;
52 static const struct board_info *board;
53
54 static const struct board_info boards[] = {
55 {
56 .id = "mr18",
57 .description = "Meraki MR18 Access Point",
58 .magic = 0x8e73ed8a,
59 .imagelen = 0x00800000,
60 .statichash = {0xda, 0x39, 0xa3, 0xee, 0x5e,
61 0x6b, 0x4b, 0x0d, 0x32, 0x55,
62 0xbf, 0xef, 0x95, 0x60, 0x18,
63 0x90, 0xaf, 0xd8, 0x07, 0x09},
64 }, {
65 /* terminating entry */
66 }
67 };
68
69 /*
70 * Message macros
71 */
72 #define ERR(fmt, ...) do { \
73 fflush(0); \
74 fprintf(stderr, "[%s] *** error: " fmt "\n", \
75 progname, ## __VA_ARGS__); \
76 } while (0)
77
78 #define ERRS(fmt, ...) do { \
79 int save = errno; \
80 fflush(0); \
81 fprintf(stderr, "[%s] *** error: " fmt "\n", \
82 progname, ## __VA_ARGS__, strerror(save)); \
83 } while (0)
84
85 static const struct board_info *find_board(const char *id)
86 {
87 const struct board_info *ret;
88 const struct board_info *board;
89
90 ret = NULL;
91 for (board = boards; board->id != NULL; board++) {
92 if (strcasecmp(id, board->id) == 0) {
93 ret = board;
94 break;
95 }
96 }
97
98 return ret;
99 }
100
101 static void usage(int status)
102 {
103 FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout;
104 const struct board_info *board;
105
106 fprintf(stream, "Usage: %s [OPTIONS...]\n", progname);
107 fprintf(stream,
108 "\n"
109 "Options:\n"
110 " -B <board> create image for the board specified with <board>\n"
111 " -i <file> read kernel image from the file <file>\n"
112 " -o <file> write output to the file <file>\n"
113 " -s strip padding from the end of the image\n"
114 " -h show this screen\n"
115 );
116
117 fprintf(stream, "\nBoards:\n");
118 for (board = boards; board->id != NULL; board++)
119 fprintf(stream, " %-16s%s\n", board->id, board->description);
120
121 exit(status);
122 }
123
124 void writel(unsigned char *buf, size_t offset, uint32_t value)
125 {
126 value = htonl(value);
127 memcpy(buf + offset, &value, sizeof(uint32_t));
128 }
129
130 int main(int argc, char *argv[])
131 {
132 int ret = EXIT_FAILURE;
133 long klen;
134 size_t kspace;
135 unsigned char *kernel;
136 size_t buflen;
137 unsigned char *buf;
138 bool strip_padding = false;
139 char *ofname = NULL, *ifname = NULL;
140 FILE *out, *in;
141
142 progname = basename(argv[0]);
143
144 while (1) {
145 int c;
146
147 c = getopt(argc, argv, "B:i:o:sh");
148 if (c == -1)
149 break;
150
151 switch (c) {
152 case 'B':
153 board_id = optarg;
154 break;
155 case 'i':
156 ifname = optarg;
157 break;
158 case 'o':
159 ofname = optarg;
160 break;
161 case 's':
162 strip_padding = true;
163 break;
164 case 'h':
165 usage(EXIT_SUCCESS);
166 break;
167 default:
168 usage(EXIT_FAILURE);
169 break;
170 }
171 }
172
173 if (board_id == NULL) {
174 ERR("no board specified");
175 goto err;
176 }
177
178 board = find_board(board_id);
179 if (board == NULL) {
180 ERR("unknown board \"%s\"", board_id);
181 goto err;
182 }
183
184 if (ifname == NULL) {
185 ERR("no input file specified");
186 goto err;
187 }
188
189 if (ofname == NULL) {
190 ERR("no output file specified");
191 goto err;
192 }
193
194 in = fopen(ifname, "r");
195 if (in == NULL) {
196 ERRS("could not open \"%s\" for reading: %s", ifname);
197 goto err;
198 }
199
200 buflen = board->imagelen;
201 kspace = buflen - HDR_LENGTH;
202
203 /* Get kernel length */
204 fseek(in, 0, SEEK_END);
205 klen = ftell(in);
206 rewind(in);
207
208 if (klen > kspace) {
209 ERR("file \"%s\" is too big - max size: 0x%08lX\n",
210 ifname, kspace);
211 goto err_close_in;
212 }
213
214 /* If requested, resize buffer to remove padding */
215 if (strip_padding)
216 buflen = klen + HDR_LENGTH;
217
218 /* Allocate and initialize buffer for final image */
219 buf = malloc(buflen);
220 if (buf == NULL) {
221 ERRS("no memory for buffer: %s\n");
222 goto err_close_in;
223 }
224 memset(buf, PADDING_BYTE, buflen);
225
226 /* Load kernel */
227 kernel = buf + HDR_LENGTH;
228 fread(kernel, klen, 1, in);
229
230 /* Write magic values and filler */
231 writel(buf, HDR_OFF_MAGIC1, board->magic);
232 writel(buf, HDR_OFF_MAGIC2, board->magic);
233 writel(buf, HDR_OFF_FILLER, 0);
234
235 /* Write header and image length */
236 writel(buf, HDR_OFF_HDRLEN, HDR_LENGTH);
237 writel(buf, HDR_OFF_IMAGELEN, klen);
238
239 /* Write checksum and static hash */
240 sha1_csum(kernel, klen, buf + HDR_OFF_CHECKSUM);
241 memcpy(buf + HDR_OFF_STATICHASH, board->statichash, 20);
242
243 /* Save finished image */
244 out = fopen(ofname, "w");
245 if (out == NULL) {
246 ERRS("could not open \"%s\" for writing: %s", ofname);
247 goto err_free;
248 }
249 fwrite(buf, buflen, 1, out);
250
251 ret = EXIT_SUCCESS;
252
253 fclose(out);
254
255 err_free:
256 free(buf);
257
258 err_close_in:
259 fclose(in);
260
261 err:
262 return ret;
263 }