From 90e4b76bd1eb6c4b557a772fc271532d39db6f56 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Fri, 11 Dec 2015 15:06:45 +0000 Subject: firmware-utils mkmerakifw: firmware generator for MR18 This patch adds firmware generation tool for Cisco's MR18 Signed-off-by: Thomas Hebb SVN-Revision: 47856 --- tools/firmware-utils/src/mkmerakifw.c | 263 ++++++++++++++++++++++++++++++++++ 1 file changed, 263 insertions(+) create mode 100644 tools/firmware-utils/src/mkmerakifw.c (limited to 'tools/firmware-utils/src/mkmerakifw.c') diff --git a/tools/firmware-utils/src/mkmerakifw.c b/tools/firmware-utils/src/mkmerakifw.c new file mode 100644 index 0000000000..241bb33400 --- /dev/null +++ b/tools/firmware-utils/src/mkmerakifw.c @@ -0,0 +1,263 @@ +/* + * Copyright (C) 2015 Thomas Hebb + * + * The format of the header this tool generates was first documented by + * Chris Blake in a shell script of the + * same purpose. I have created this reimplementation at his request. The + * original script can be found at: + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sha1.h" + +#define PADDING_BYTE 0xff + +#define HDR_LENGTH 0x00000400 +#define HDR_OFF_MAGIC1 0 +#define HDR_OFF_HDRLEN 4 +#define HDR_OFF_IMAGELEN 8 +#define HDR_OFF_CHECKSUM 12 +#define HDR_OFF_MAGIC2 32 +#define HDR_OFF_FILLER 36 +#define HDR_OFF_STATICHASH 40 + +struct board_info { + uint32_t magic; + uint32_t imagelen; + unsigned char statichash[20]; + char *id; + char *description; +}; + +/* + * Globals + */ +static char *progname; + +static char *board_id; +static const struct board_info *board; + +static const struct board_info boards[] = { + { + .id = "mr18", + .description = "Meraki MR18 Access Point", + .magic = 0x8e73ed8a, + .imagelen = 0x00800000, + .statichash = {0xda, 0x39, 0xa3, 0xee, 0x5e, + 0x6b, 0x4b, 0x0d, 0x32, 0x55, + 0xbf, 0xef, 0x95, 0x60, 0x18, + 0x90, 0xaf, 0xd8, 0x07, 0x09}, + }, { + /* terminating entry */ + } +}; + +/* + * Message macros + */ +#define ERR(fmt, ...) do { \ + fflush(0); \ + fprintf(stderr, "[%s] *** error: " fmt "\n", \ + progname, ## __VA_ARGS__); \ +} while (0) + +#define ERRS(fmt, ...) do { \ + int save = errno; \ + fflush(0); \ + fprintf(stderr, "[%s] *** error: " fmt "\n", \ + progname, ## __VA_ARGS__, strerror(save)); \ +} while (0) + +static const struct board_info *find_board(const char *id) +{ + const struct board_info *ret; + const struct board_info *board; + + ret = NULL; + for (board = boards; board->id != NULL; board++) { + if (strcasecmp(id, board->id) == 0) { + ret = board; + break; + } + } + + return ret; +} + +static void usage(int status) +{ + FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout; + const struct board_info *board; + + fprintf(stream, "Usage: %s [OPTIONS...]\n", progname); + fprintf(stream, +"\n" +"Options:\n" +" -B create image for the board specified with \n" +" -i read kernel image from the file \n" +" -o write output to the file \n" +" -s strip padding from the end of the image\n" +" -h show this screen\n" + ); + + fprintf(stream, "\nBoards:\n"); + for (board = boards; board->id != NULL; board++) + fprintf(stream, " %-16s%s\n", board->id, board->description); + + exit(status); +} + +void writel(unsigned char *buf, size_t offset, uint32_t value) +{ + value = htonl(value); + memcpy(buf + offset, &value, sizeof(uint32_t)); +} + +int main(int argc, char *argv[]) +{ + int ret = EXIT_FAILURE; + long klen; + size_t kspace; + unsigned char *kernel; + size_t buflen; + unsigned char *buf; + bool strip_padding = false; + char *ofname = NULL, *ifname = NULL; + FILE *out, *in; + + progname = basename(argv[0]); + + while (1) { + int c; + + c = getopt(argc, argv, "B:i:o:sh"); + if (c == -1) + break; + + switch (c) { + case 'B': + board_id = optarg; + break; + case 'i': + ifname = optarg; + break; + case 'o': + ofname = optarg; + break; + case 's': + strip_padding = true; + break; + case 'h': + usage(EXIT_SUCCESS); + break; + default: + usage(EXIT_FAILURE); + break; + } + } + + if (board_id == NULL) { + ERR("no board specified"); + goto err; + } + + board = find_board(board_id); + if (board == NULL) { + ERR("unknown board \"%s\"", board_id); + goto err; + } + + if (ifname == NULL) { + ERR("no input file specified"); + goto err; + } + + if (ofname == NULL) { + ERR("no output file specified"); + goto err; + } + + in = fopen(ifname, "r"); + if (in == NULL) { + ERRS("could not open \"%s\" for reading: %s", ifname); + goto err; + } + + buflen = board->imagelen; + kspace = buflen - HDR_LENGTH; + + /* Get kernel length */ + fseek(in, 0, SEEK_END); + klen = ftell(in); + rewind(in); + + if (klen > kspace) { + ERR("file \"%s\" is too big - max size: 0x%08lX\n", + ifname, kspace); + goto err_close_in; + } + + /* If requested, resize buffer to remove padding */ + if (strip_padding) + buflen = klen + HDR_LENGTH; + + /* Allocate and initialize buffer for final image */ + buf = malloc(buflen); + if (buf == NULL) { + ERRS("no memory for buffer: %s\n"); + goto err_close_in; + } + memset(buf, PADDING_BYTE, buflen); + + /* Load kernel */ + kernel = buf + HDR_LENGTH; + fread(kernel, klen, 1, in); + + /* Write magic values and filler */ + writel(buf, HDR_OFF_MAGIC1, board->magic); + writel(buf, HDR_OFF_MAGIC2, board->magic); + writel(buf, HDR_OFF_FILLER, 0); + + /* Write header and image length */ + writel(buf, HDR_OFF_HDRLEN, HDR_LENGTH); + writel(buf, HDR_OFF_IMAGELEN, klen); + + /* Write checksum and static hash */ + sha1_csum(kernel, klen, buf + HDR_OFF_CHECKSUM); + memcpy(buf + HDR_OFF_STATICHASH, board->statichash, 20); + + /* Save finished image */ + out = fopen(ofname, "w"); + if (out == NULL) { + ERRS("could not open \"%s\" for writing: %s", ofname); + goto err_free; + } + fwrite(buf, buflen, 1, out); + + ret = EXIT_SUCCESS; + + fclose(out); + +err_free: + free(buf); + +err_close_in: + fclose(in); + +err: + return ret; +} -- cgit v1.2.3