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
38struct 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 */
49static char *progname;
50
51static char *board_id;
52static const struct board_info *board;
53
54static 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
85static 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
101static 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
124void 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
130int 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
255err_free:
256	free(buf);
257
258err_close_in:
259	fclose(in);
260
261err:
262	return ret;
263}
264