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