rpcd: iwinfo plugin fixes
[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 }