• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/firmware-utils/src/mksenaofw.c

  1 // SPDX-License-Identifier: GPL-2.0-only
  2 /*
  3  *
  4  *  Copyright (C) 2012 OpenWrt.org
  5  *  Copyright (C) 2012 Mikko Hissa <mikko.hissa@uta.fi>
  6  */
  7 
  8 #include <stdio.h>
  9 #include <stdlib.h>
 10 #include <string.h>
 11 #include <stdarg.h>
 12 #include <libgen.h>
 13 #include <errno.h>
 14 #include <arpa/inet.h>
 15 #include <unistd.h>
 16 #include "md5.h"
 17 
 18 #define HDR_LEN                 0x60
 19 #define BUF_SIZE                0x200
 20 #define VERSION_SIZE            0x10
 21 #define MD5_SIZE                0x10
 22 #define PAD_SIZE                0x20
 23 
 24 #define DEFAULT_BLOCK_SIZE      65535
 25 
 26 #define DEFAULT_HEAD_VALUE      0x0
 27 #define DEFAULT_VERSION         "123"
 28 #define DEFAULT_MAGIC           0x12345678
 29 
 30 typedef struct {
 31         uint32_t head;
 32         uint32_t vendor_id;
 33         uint32_t product_id;
 34         uint8_t  version[VERSION_SIZE];
 35         uint32_t firmware_type;
 36         uint32_t filesize;
 37         uint32_t zero;
 38         uint8_t  md5sum[MD5_SIZE];
 39         uint8_t  pad[PAD_SIZE];
 40         uint32_t chksum;
 41         uint32_t magic;
 42 } img_header;
 43 
 44 typedef struct {
 45         uint8_t id;
 46         char * name;
 47 } firmware_type;
 48 
 49 typedef enum {
 50         NONE, ENCODE, DECODE
 51 } op_mode;
 52 
 53 static firmware_type FIRMWARE_TYPES[] = {
 54         { 0x00, "combo" }, /* Used for new capwap-included style header */
 55         { 0x01, "bootloader" },
 56         { 0x02, "kernel" },
 57         { 0x03, "kernelapp" },
 58         { 0x04, "apps" },
 59         /* The types below this line vary by manufacturer */
 60         { 0x05, "littleapps (D-Link)/factoryapps (EnGenius)" },
 61         { 0x06, "sounds (D-Link)/littleapps (EnGenius)" },
 62         { 0x07, "userconfig (D-Link)/appdata (EnGenius)" },
 63         { 0x08, "userconfig (EnGenius)"},
 64         { 0x09, "odmapps (EnGenius)"},
 65         { 0x0a, "factoryapps (D-Link)" },
 66         { 0x0b, "odmapps (D-Link)" },
 67         { 0x0c, "langpack (D-Link)" }
 68 };
 69 
 70 #define MOD_DEFAULT 0x616C6C00
 71 #define SKU_DEFAULT 0x0
 72 #define DATECODE_NONE 0xFFFFFFFF
 73 #define FIRMWARE_TYPE_NONE 0xFF
 74 
 75 struct capwap_header {
 76         uint32_t mod;
 77         uint32_t sku;
 78         uint32_t firmware_ver[3];
 79         uint32_t datecode;
 80         uint32_t capwap_ver[3];
 81         uint32_t model_size;
 82         uint8_t  model[];
 83 };
 84 
 85 static long get_file_size(const char *filename)
 86 {
 87         FILE *fp_file;
 88         long result;
 89 
 90         fp_file = fopen(filename, "r");
 91         if (!fp_file)
 92                 return -1;
 93         fseek(fp_file, 0, SEEK_END);
 94         result = ftell(fp_file);
 95         fclose(fp_file);
 96         return result;
 97 }
 98 
 99 static int header_checksum(void *data, size_t len)
100 {
101         int sum = 0; /* shouldn't this be unsigned ? */
102         size_t i;
103 
104         if (data != NULL && len > 0) {
105                 for (i = 0; i < len; ++i)
106                         sum += ((unsigned char *)data)[i];
107                 return sum;
108         }
109 
110         return -1;
111 }
112 
113 static int md5_file(const char *filename, uint8_t *dst)
114 {
115         FILE *fp_src;
116         MD5_CTX ctx;
117         char buf[BUF_SIZE];
118         size_t bytes_read;
119 
120         MD5_Init(&ctx);
121 
122         fp_src = fopen(filename, "r+b");
123         if (!fp_src) {
124                 return -1;
125         }
126         while (!feof(fp_src)) {
127                 bytes_read = fread(&buf, 1, BUF_SIZE, fp_src);
128                 MD5_Update(&ctx, &buf, bytes_read);
129         }
130         fclose(fp_src);
131 
132         MD5_Final(dst, &ctx);
133 
134         return 0;
135 }
136 
137 static int encode_image(const char *input_file_name,
138                         const char *output_file_name, img_header *header,
139                         struct capwap_header *cw_header, int block_size)
140 {
141         char buf[BUF_SIZE];
142         size_t pad_len = 0;
143         size_t bytes_avail;
144         size_t bytes_read;
145 
146         FILE *fp_output;
147         FILE *fp_input;
148 
149         int model_size;
150         long magic;
151         size_t i;
152 
153         fp_input = fopen(input_file_name, "r+b");
154         if (!fp_input) {
155                 fprintf(stderr, "Cannot open %s !!\n", input_file_name);
156                 return -1;
157         }
158 
159         fp_output = fopen(output_file_name, "w+b");
160         if (!fp_output) {
161                 fprintf(stderr, "Cannot open %s !!\n", output_file_name);
162                 fclose(fp_input);
163                 return -1;
164         }
165 
166         header->filesize = get_file_size(input_file_name);
167         if (!header->filesize) {
168                 fprintf(stderr, "File %s open/size error!\n", input_file_name);
169                 fclose(fp_input);
170                 fclose(fp_output);
171                 return -1;
172         }
173         /*
174          * Zero padding
175          */
176         if (block_size > 0) {
177                 pad_len = block_size - (header->filesize % block_size);
178         }
179 
180         if (md5_file(input_file_name, (uint8_t *) &header->md5sum) < 0) {
181                 fprintf(stderr, "MD5 failed on file %s\n", input_file_name);
182                 fclose(fp_input);
183                 fclose(fp_output);
184                 return -1;
185         }
186         header->zero = 0;
187         header->chksum = header_checksum(header, HDR_LEN);
188         if (cw_header) {
189                 header->chksum += header_checksum(cw_header,
190                         sizeof(struct capwap_header) + cw_header->model_size);
191         }
192 
193         header->head = htonl(header->head);
194         header->vendor_id = htonl(header->vendor_id);
195         header->product_id = htonl(header->product_id);
196         header->firmware_type = htonl(header->firmware_type);
197         header->filesize = htonl(header->filesize);
198         header->chksum = htonl(header->chksum);
199         magic = header->magic;
200         header->magic = htonl(header->magic);
201 
202         fwrite(header, HDR_LEN, 1, fp_output);
203 
204         if (cw_header) {
205                 model_size = cw_header->model_size;
206                 cw_header->mod = htonl(cw_header->mod);
207                 cw_header->sku = htonl(cw_header->sku);
208                 cw_header->firmware_ver[0] = htonl(cw_header->firmware_ver[0]);
209                 cw_header->firmware_ver[1] = htonl(cw_header->firmware_ver[1]);
210                 cw_header->firmware_ver[2] = htonl(cw_header->firmware_ver[2]);
211                 cw_header->datecode = htonl(cw_header->datecode);
212                 cw_header->capwap_ver[0] = htonl(cw_header->capwap_ver[0]);
213                 cw_header->capwap_ver[1] = htonl(cw_header->capwap_ver[1]);
214                 cw_header->capwap_ver[2] = htonl(cw_header->capwap_ver[2]);
215                 cw_header->model_size = htonl(cw_header->model_size);
216                 fwrite(cw_header, sizeof(struct capwap_header) + model_size, 1,
217                        fp_output);
218         }
219 
220         while (!feof(fp_input) || pad_len > 0) {
221 
222                 if (!feof(fp_input))
223                         bytes_read = fread(&buf, 1, BUF_SIZE, fp_input);
224                 else
225                         bytes_read = 0;
226 
227                 /*
228                  * No more bytes read, start padding
229                  */
230                 if (bytes_read < BUF_SIZE && pad_len > 0) {
231                         bytes_avail = BUF_SIZE - bytes_read;
232                         memset( &buf[bytes_read], 0, bytes_avail);
233                         bytes_read += bytes_avail < pad_len ? bytes_avail : pad_len;
234                         pad_len -= bytes_avail < pad_len ? bytes_avail : pad_len;
235                 }
236 
237                 for (i = 0; i < bytes_read; i++)
238                         buf[i] ^= magic >> (i % 8) & 0xff;
239                 fwrite(&buf, bytes_read, 1, fp_output);
240         }
241 
242         fclose(fp_input);
243         fclose(fp_output);
244         return 1;
245 }
246 
247 int decode_image(const char *input_file_name, const char *output_file_name)
248 {
249         struct capwap_header cw_header;
250         char buf[BUF_SIZE];
251         img_header header;
252 
253         char *pmodel = NULL;
254         FILE *fp_input;
255         FILE *fp_output;
256 
257         size_t bytes_read;
258         size_t bytes_written;
259         unsigned int i;
260 
261         fp_input = fopen(input_file_name, "r+b");
262         if (!fp_input) {
263                 fprintf(stderr, "Cannot open %s !!\n", input_file_name);
264                 return -1;
265         }
266 
267         fp_output = fopen(output_file_name, "w+b");
268         if (!fp_output) {
269                 fprintf(stderr, "Cannot open %s !!\n", output_file_name);
270                 fclose(fp_input);
271                 return -1;
272         }
273 
274         if (fread(&header, 1, HDR_LEN, fp_input) != HDR_LEN) {
275                 fprintf(stderr, "Incorrect header size reading base header!!");
276                 fclose(fp_input);
277                 fclose(fp_output);
278                 return -1;
279         }
280 
281         header.head = ntohl(header.head);
282         header.vendor_id = ntohl(header.vendor_id);
283         header.product_id = ntohl(header.product_id);
284         header.firmware_type = ntohl(header.firmware_type);
285         header.filesize = ntohl(header.filesize);
286         header.chksum = ntohl(header.chksum);
287         header.magic = ntohl(header.magic);
288 
289         /* read capwap header if firmware_type is zero */
290         if (header.firmware_type == 0) {
291                 if (fread(&cw_header, 1, sizeof(struct capwap_header),
292                           fp_input) != sizeof(struct capwap_header)) {
293                         fprintf(stderr, "Incorrect header size reading capwap_header!!");
294                         fclose(fp_input);
295                         fclose(fp_output);
296                         return -1;
297                 }
298                 cw_header.mod = ntohl(cw_header.mod);
299                 cw_header.sku = ntohl(cw_header.sku);
300                 cw_header.firmware_ver[0] = ntohl(cw_header.firmware_ver[0]);
301                 cw_header.firmware_ver[1] = ntohl(cw_header.firmware_ver[1]);
302                 cw_header.firmware_ver[2] = ntohl(cw_header.firmware_ver[2]);
303                 cw_header.datecode = ntohl(cw_header.datecode);
304                 cw_header.capwap_ver[0] = ntohl(cw_header.capwap_ver[0]);
305                 cw_header.capwap_ver[1] = ntohl(cw_header.capwap_ver[1]);
306                 cw_header.capwap_ver[2] = ntohl(cw_header.capwap_ver[2]);
307                 cw_header.model_size = ntohl(cw_header.model_size);
308 
309                 pmodel = malloc(cw_header.model_size + 1);
310                 if (pmodel) {
311                         pmodel[cw_header.model_size] = '\0';
312                         if (fread(pmodel, 1, cw_header.model_size, fp_input) !=
313                                   cw_header.model_size) {
314                                 fprintf(stderr, "Incorrect header size reading model name!!");
315                                 free(pmodel);
316                                 fclose(fp_input);
317                                 fclose(fp_output);
318                                 return -1;
319                         }
320                         free(pmodel);
321                 } else {
322                         fprintf(stderr, "Incorrect header size reading model name!!");
323                         fclose(fp_input);
324                         fclose(fp_output);
325                         return -1;
326                 }
327         }
328 
329         bytes_written = 0;
330         while (!feof(fp_input)) {
331 
332                 bytes_read = fread(&buf, 1, BUF_SIZE, fp_input);
333                 for (i = 0; i < bytes_read; i++)
334                         buf[i] ^= header.magic >> (i % 8) & 0xff;
335 
336                 /*
337                  * Handle padded source file
338                  */
339                 if (bytes_written + bytes_read > header.filesize) {
340                         bytes_read = header.filesize - bytes_written;
341                         if (bytes_read > 0)
342                                 fwrite(&buf, bytes_read, 1, fp_output);
343                         break;
344                 }
345 
346                 fwrite(&buf, bytes_read, 1, fp_output);
347                 bytes_written += bytes_read;
348         }
349 
350         fclose(fp_input);
351         fclose(fp_output);
352 
353         return 1;
354 }
355 
356 static void usage(const char *progname, int status)
357 {
358         FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout;
359         size_t i;
360 
361         fprintf(stream, "Usage: %s [OPTIONS...]\n", progname);
362         fprintf(stream, "\n"
363                         "Options:\n"
364                         "  -e <file>            encode image file <file>\n"
365                         "  -d <file>            decode image file <file>\n"
366                         "  -o <file>            write output to the file <file>\n"
367                         "  -t <type>            set image type to <type>\n"
368                         "                       valid image <type> values:\n");
369         for (i = 0; i < sizeof(FIRMWARE_TYPES) / sizeof(firmware_type); i++) {
370                 fprintf(stream, "                       %-5i= %s\n", FIRMWARE_TYPES[i].id,
371                                 FIRMWARE_TYPES[i].name);
372         }
373         fprintf(stream, "  -v <version>         set image version to <version>\n"
374                         "  -r <vendor>          set image vendor id to <vendor>\n"
375                         "  -p <product>         set image product id to <product>\n"
376                         "  -m <magic>           set encoding magic <magic>\n"
377                         "  -z                   enable image padding to <blocksize>\n"
378                         "  -b <blocksize>       set image <blocksize>, defaults to %u\n"
379                         "  -c <datecode>        add capwap header with <datecode> (e.g. 171101)\n"
380                         "  -w <fw_ver>          firmware version for capwap header (e.g. 3.0.1)\n"
381                         "  -x <cw_ver>          capwap firmware version for capwap header (e.g. 1.8.53)\n"
382                         "  -n <name>            model name for capwap header (e.g. ENS620EXT)\n"
383                         "  -h                   show this screen\n", DEFAULT_BLOCK_SIZE);
384         exit(status);
385 }
386 
387 int main(int argc, char *argv[])
388 {
389         static const char period[2] = ".";
390         struct capwap_header cw_header;
391         img_header header;
392 
393         struct capwap_header *pcw_header = NULL;
394         char *output_file = NULL;
395         char *input_file = NULL;
396         char *progname = NULL;
397         char *mod_name = NULL;
398         char *token;
399 
400         op_mode mode = NONE;
401         int tmp, pad = 0;
402         int block_size;
403         size_t i;
404         int opt;
405 
406         block_size = DEFAULT_BLOCK_SIZE;
407         progname = basename(argv[0]);
408 
409         memset(&header, 0, sizeof(img_header));
410         header.magic = DEFAULT_MAGIC;
411         header.head = DEFAULT_HEAD_VALUE;
412         header.firmware_type = FIRMWARE_TYPE_NONE;
413         memset(&cw_header, 0, sizeof(struct capwap_header));
414         cw_header.mod = MOD_DEFAULT;
415         cw_header.sku = SKU_DEFAULT;
416         cw_header.datecode = DATECODE_NONE;
417         strncpy( (char*)&header.version, DEFAULT_VERSION, VERSION_SIZE - 1);
418 
419         while ((opt = getopt(argc, argv, ":o:e:d:t:v:r:p:m:b:c:w:x:n:h?z")) != -1) {
420                 switch (opt) {
421                 case 'e':
422                         input_file = optarg;
423                         mode = ENCODE;
424                         break;
425                 case 'd':
426                         input_file = optarg;
427                         mode = DECODE;
428                         break;
429                 case 'o':
430                         output_file = optarg;
431                         break;
432                 case 't':
433                         tmp = strtol(optarg, 0, 10);
434                         for (i = 0; i < sizeof(FIRMWARE_TYPES) / sizeof(firmware_type);
435                                         i++) {
436                                 if (FIRMWARE_TYPES[i].id == tmp) {
437                                         header.firmware_type = FIRMWARE_TYPES[i].id;
438                                         break;
439                                 }
440                         }
441                         if (header.firmware_type == FIRMWARE_TYPE_NONE) {
442                                 fprintf(stderr, "Invalid firmware type \"\"!\n");
443                                 usage(progname, EXIT_FAILURE);
444                         }
445                         break;
446                 case 'v':
447                         strncpy( (char*)&header.version, optarg,
448                                         VERSION_SIZE - 1);
449                         break;
450                 case 'r':
451                         header.vendor_id = strtol(optarg, 0, 0);
452                         break;
453                 case 'p':
454                         header.product_id = strtol(optarg, 0, 0);
455                         break;
456                 case 'm':
457                         header.magic = strtoul(optarg, 0, 16);
458                         break;
459                 case 'z':
460                         pad = 1;
461                         break;
462                 case 'b':
463                         block_size = strtol(optarg, 0, 10);
464                         break;
465                 case 'c':
466                         cw_header.datecode = strtoul(optarg, 0, 10);
467                         break;
468                 case 'w':
469                         token = strtok(optarg, period);
470                         i = 0;
471                         while (token && (i < 3)) {
472                                 cw_header.firmware_ver[i++] =
473                                         strtoul(token, 0, 10);
474                                 token = strtok(NULL, period);
475                         }
476                         break;
477                 case 'x':
478                         token = strtok(optarg, period);
479                         i = 0;
480                         while (token && (i < 3)) {
481                                 cw_header.capwap_ver[i++] =
482                                         strtoul(token, 0, 10);
483                                 token = strtok(NULL, period);
484                         }
485                         break;
486                 case 'n':
487                         mod_name = optarg;
488                         cw_header.model_size = strlen(mod_name);
489                         break;
490                 case 'h':
491                         usage(progname, EXIT_SUCCESS);
492                         break;
493                 case ':':
494                         fprintf(stderr, "Option -%c requires an operand\n", optopt);
495                         usage(progname, EXIT_FAILURE);
496                         break;
497                 case '?':
498                         fprintf(stderr, "Unrecognized option: -%c\n", optopt);
499                         usage(progname, EXIT_FAILURE);
500                         break;
501                 default:
502                         usage(progname, EXIT_FAILURE);
503                         break;
504                 }
505         }
506 
507         /* Check required arguments */
508         if (mode == NONE) {
509                 fprintf(stderr, "A mode must be defined\n");
510                 usage(progname, EXIT_FAILURE);
511         }
512 
513         if (input_file == NULL || output_file == NULL) {
514                 fprintf(stderr, "Input and output files must be defined\n");
515                 usage(progname, EXIT_FAILURE);
516         }
517 
518         if (mode == DECODE) {
519                 if (decode_image(input_file, output_file) < 0)
520                                 return EXIT_FAILURE;
521 
522                 return EXIT_SUCCESS;
523         }
524 
525         if ((header.firmware_type == 0) &&
526             (cw_header.datecode == DATECODE_NONE)) {
527                 fprintf(stderr, "Firmware type must be non-zero for non-capwap images\n");
528                 usage(progname, EXIT_FAILURE);
529         }
530 
531         if (header.vendor_id == 0 || header.product_id == 0) {
532                 fprintf(stderr, "Vendor ID and Product ID must be defined and non-zero\n");
533                 usage(progname, EXIT_FAILURE);
534         }
535 
536         /* Check capwap header specific arguments */
537         if (cw_header.datecode != DATECODE_NONE) {
538                 if (!mod_name) {
539                         fprintf(stderr, "Capwap header specified: model name must be specified\n");
540                         usage(progname, EXIT_FAILURE);
541                 }
542                 if (!cw_header.firmware_ver[0] && !cw_header.firmware_ver[1] &&
543                         !cw_header.firmware_ver[2]) {
544                         fprintf(stderr, "Capwap header specified, fw_ver must be non-zero\n");
545                 }
546                 if (!cw_header.capwap_ver[0] && !cw_header.capwap_ver[1] &&
547                         !cw_header.capwap_ver[2]) {
548                         fprintf(stderr, "Capwap header specified, cw_ver must be non-zero\n");
549                 }
550                 pcw_header = malloc(sizeof(struct capwap_header) +
551                                         cw_header.model_size);
552                 if (pcw_header) {
553                         memcpy(pcw_header, &cw_header,
554                                 sizeof(struct capwap_header));
555                         memcpy(&(pcw_header->model), mod_name,
556                                 cw_header.model_size);
557                 } else {
558                         fprintf(stderr, "Failed to allocate memory\n");
559                         return EXIT_FAILURE;
560                 }
561         }
562 
563         if (encode_image(input_file, output_file, &header, pcw_header,
564                                 pad ? block_size : 0) < 0)
565                 return EXIT_FAILURE;
566 
567         return EXIT_SUCCESS;
568 }
569 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt