From 27fbf299922a586a674cc3780d7824556b9fa053 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Mon, 4 Apr 2016 14:53:22 +0000 Subject: [PATCH] firmware-utils: add oseama tool for creating Seama entities MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit It has an important feature (compared to seama) of using multiple input files, aligning them and padding zeroes until reaching a specified absolute offset. This is needed for a proper flash layout on NAND. We want kernel partition to be big enough to handle future updates without a need to resize it and wipe whole "ubi" partition. It's important as we don't want to lose block counters. Signed-off-by: Rafał Miłecki Backport of r48601 git-svn-id: svn://svn.openwrt.org/openwrt/branches/chaos_calmer@49116 3c298f89-4303-0410-b956-a3cf2f4a3e73 --- tools/firmware-utils/Makefile | 1 + tools/firmware-utils/src/oseama.c | 422 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 423 insertions(+) create mode 100644 tools/firmware-utils/src/oseama.c diff --git a/tools/firmware-utils/Makefile b/tools/firmware-utils/Makefile index 6d88845ef6..0397845663 100644 --- a/tools/firmware-utils/Makefile +++ b/tools/firmware-utils/Makefile @@ -64,6 +64,7 @@ define Host/Compile $(call cc,mkdapimg) $(call cc, mkcameofw, -Wall) $(call cc,seama md5) + $(call cc,oseama md5, -Wall) $(call cc,fix-u-media-header cyg_crc32,-Wall) $(call cc,hcsmakeimage bcmalgo) $(call cc,mkporayfw, -Wall) diff --git a/tools/firmware-utils/src/oseama.c b/tools/firmware-utils/src/oseama.c new file mode 100644 index 0000000000..37a2171574 --- /dev/null +++ b/tools/firmware-utils/src/oseama.c @@ -0,0 +1,422 @@ +/* + * oseama + * + * Copyright (C) 2016 Rafał Miłecki + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "md5.h" + +#if !defined(__BYTE_ORDER) +#error "Unknown byte order" +#endif + +#if __BYTE_ORDER == __BIG_ENDIAN +#define cpu_to_be32(x) (x) +#define be32_to_cpu(x) (x) +#define cpu_to_be16(x) (x) +#define be16_to_cpu(x) (x) +#elif __BYTE_ORDER == __LITTLE_ENDIAN +#define cpu_to_be32(x) bswap_32(x) +#define be32_to_cpu(x) bswap_32(x) +#define cpu_to_be16(x) bswap_16(x) +#define be16_to_cpu(x) bswap_16(x) +#else +#error "Unsupported endianness" +#endif + +#define SEAMA_MAGIC 0x5ea3a417 + +struct seama_seal_header { + uint32_t magic; + uint16_t reserved; + uint16_t metasize; + uint32_t imagesize; +} __attribute__ ((packed)); + +struct seama_entity_header { + uint32_t magic; + uint16_t reserved; + uint16_t metasize; + uint32_t imagesize; + uint8_t md5[16]; +} __attribute__ ((packed)); + +char *seama_path; +int entity_idx = -1; + +static inline size_t oseama_min(size_t x, size_t y) { + return x < y ? x : y; +} + +/************************************************** + * Info + **************************************************/ + +static void oseama_info_parse_options(int argc, char **argv) { + int c; + + while ((c = getopt(argc, argv, "e:")) != -1) { + switch (c) { + case 'e': + entity_idx = atoi(optarg); + break; + } + } +} + +static int oseama_info_entities(FILE *seama) { + struct seama_entity_header hdr; + size_t bytes, metasize, imagesize; + uint8_t buf[1024]; + char *end, *tmp; + int i = 0; + int err = 0; + + while ((bytes = fread(&hdr, 1, sizeof(hdr), seama)) == sizeof(hdr)) { + if (be32_to_cpu(hdr.magic) != SEAMA_MAGIC) { + fprintf(stderr, "Invalid Seama magic: 0x%08x\n", be32_to_cpu(hdr.magic)); + err = -EINVAL; + goto err_out; + } + metasize = be16_to_cpu(hdr.metasize); + imagesize = be32_to_cpu(hdr.imagesize); + + if (entity_idx >= 0 && i != entity_idx) { + fseek(seama, metasize + imagesize, SEEK_CUR); + i++; + continue; + } + + if (metasize >= sizeof(buf)) { + fprintf(stderr, "Too small buffer (%zu B) to read all meta info (%zd B)\n", sizeof(buf), metasize); + err = -EINVAL; + goto err_out; + } + + if (entity_idx < 0) + printf("\n"); + printf("Entity offset:\t%ld\n", ftell(seama) - sizeof(hdr)); + printf("Entity size:\t%zd\n", sizeof(hdr) + metasize + imagesize); + printf("Meta size:\t%zd\n", metasize); + printf("Image size:\t%zd\n", imagesize); + + bytes = fread(buf, 1, metasize, seama); + if (bytes != metasize) { + fprintf(stderr, "Couldn't read %zd B of meta\n", metasize); + err = -EIO; + goto err_out; + } + + end = (char *)&buf[metasize - 1]; + *end = '\0'; + for (tmp = (char *)buf; tmp < end && strlen(tmp); tmp += strlen(tmp) + 1) { + printf("Meta entry:\t%s\n", tmp); + } + + fseek(seama, imagesize, SEEK_CUR); + i++; + } + +err_out: + return err; +} + +static int oseama_info(int argc, char **argv) { + FILE *seama; + struct seama_seal_header hdr; + size_t bytes; + uint16_t metasize; + uint32_t imagesize; + uint8_t buf[1024]; + int err = 0; + + if (argc < 3) { + fprintf(stderr, "No Seama file passed\n"); + err = -EINVAL; + goto out; + } + seama_path = argv[2]; + + optind = 3; + oseama_info_parse_options(argc, argv); + + seama = fopen(seama_path, "r"); + if (!seama) { + fprintf(stderr, "Couldn't open %s\n", seama_path); + err = -EACCES; + goto out; + } + + bytes = fread(&hdr, 1, sizeof(hdr), seama); + if (bytes != sizeof(hdr)) { + fprintf(stderr, "Couldn't read %s header\n", seama_path); + err = -EIO; + goto err_close; + } + metasize = be16_to_cpu(hdr.metasize); + imagesize = be32_to_cpu(hdr.imagesize); + + if (be32_to_cpu(hdr.magic) != SEAMA_MAGIC) { + fprintf(stderr, "Invalid Seama magic: 0x%08x\n", be32_to_cpu(hdr.magic)); + err = -EINVAL; + goto err_close; + } + + if (metasize >= sizeof(buf)) { + fprintf(stderr, "Too small buffer (%zu B) to read all meta info (%d B)\n", sizeof(buf), metasize); + err = -EINVAL; + goto err_close; + } + + if (imagesize) { + fprintf(stderr, "Invalid Seama image size: 0x%08x (should be 0)\n", imagesize); + err = -EINVAL; + goto err_close; + } + + bytes = fread(buf, 1, metasize, seama); + if (bytes != metasize) { + fprintf(stderr, "Couldn't read %d B of meta\n", metasize); + err = -EIO; + goto err_close; + } + + if (entity_idx < 0) { + char *end, *tmp; + + printf("Meta size:\t%d\n", metasize); + printf("Image size:\t%d\n", imagesize); + + end = (char *)&buf[metasize - 1]; + *end = '\0'; + for (tmp = (char *)buf; tmp < end && strlen(tmp); tmp += strlen(tmp) + 1) { + printf("Meta entry:\t%s\n", tmp); + } + } + + oseama_info_entities(seama); + +err_close: + fclose(seama); +out: + return err; +} + +/************************************************** + * Create + **************************************************/ + +static ssize_t oseama_entity_append_file(FILE *seama, const char *in_path) { + FILE *in; + size_t bytes; + ssize_t length = 0; + uint8_t buf[128]; + + in = fopen(in_path, "r"); + if (!in) { + fprintf(stderr, "Couldn't open %s\n", in_path); + return -EACCES; + } + + while ((bytes = fread(buf, 1, sizeof(buf), in)) > 0) { + if (fwrite(buf, 1, bytes, seama) != bytes) { + fprintf(stderr, "Couldn't write %zu B to %s\n", bytes, seama_path); + length = -EIO; + break; + } + length += bytes; + } + + fclose(in); + + return length; +} + +static ssize_t oseama_entity_append_zeros(FILE *seama, size_t length) { + uint8_t *buf; + + buf = malloc(length); + if (!buf) + return -ENOMEM; + memset(buf, 0, length); + + if (fwrite(buf, 1, length, seama) != length) { + fprintf(stderr, "Couldn't write %zu B to %s\n", length, seama_path); + return -EIO; + } + + return length; +} + +static ssize_t oseama_entity_align(FILE *seama, size_t curr_offset, size_t alignment) { + if (curr_offset & (alignment - 1)) { + size_t length = alignment - (curr_offset % alignment); + + return oseama_entity_append_zeros(seama, length); + } + + return 0; +} + +static int oseama_entity_write_hdr(FILE *seama, size_t metasize, size_t imagesize) { + struct seama_entity_header hdr = {}; + uint8_t buf[128]; + size_t length = imagesize; + size_t bytes; + MD5_CTX ctx; + + fseek(seama, sizeof(hdr) + metasize, SEEK_SET); + MD5_Init(&ctx); + while ((bytes = fread(buf, 1, oseama_min(sizeof(buf), length), seama)) > 0) { + MD5_Update(&ctx, buf, bytes); + length -= bytes; + } + MD5_Final(hdr.md5, &ctx); + + hdr.magic = cpu_to_be32(SEAMA_MAGIC); + hdr.metasize = cpu_to_be16(metasize); + hdr.imagesize = cpu_to_be32(imagesize); + + fseek(seama, 0, SEEK_SET); + bytes = fwrite(&hdr, 1, sizeof(hdr), seama); + if (bytes != sizeof(hdr)) { + fprintf(stderr, "Couldn't write Seama entity header to %s\n", seama_path); + return -EIO; + } + + return 0; +} + +static int oseama_entity(int argc, char **argv) { + FILE *seama; + ssize_t sbytes; + size_t curr_offset = sizeof(struct seama_entity_header); + size_t metasize = 0, imagesize = 0; + int c; + int err = 0; + + if (argc < 3) { + fprintf(stderr, "No Seama file passed\n"); + err = -EINVAL; + goto out; + } + seama_path = argv[2]; + + seama = fopen(seama_path, "w+"); + if (!seama) { + fprintf(stderr, "Couldn't open %s\n", seama_path); + err = -EACCES; + goto out; + } + fseek(seama, curr_offset, SEEK_SET); + + optind = 3; + while ((c = getopt(argc, argv, "m:f:b:")) != -1) { + switch (c) { + case 'm': + sbytes = fwrite(optarg, 1, strlen(optarg) + 1, seama); + if (sbytes < 0) { + fprintf(stderr, "Failed to write meta %s\n", optarg); + } else { + curr_offset += sbytes; + metasize += sbytes; + } + + sbytes = oseama_entity_align(seama, curr_offset, 4); + if (sbytes < 0) { + fprintf(stderr, "Failed to append zeros\n"); + } else { + curr_offset += sbytes; + metasize += sbytes; + } + + break; + case 'f': + case 'b': + break; + } + } + + optind = 3; + while ((c = getopt(argc, argv, "m:f:b:")) != -1) { + switch (c) { + case 'm': + break; + case 'f': + sbytes = oseama_entity_append_file(seama, optarg); + if (sbytes < 0) { + fprintf(stderr, "Failed to append file %s\n", optarg); + } else { + curr_offset += sbytes; + imagesize += sbytes; + } + break; + case 'b': + sbytes = strtol(optarg, NULL, 0) - curr_offset; + if (sbytes < 0) { + fprintf(stderr, "Current Seama entity length is 0x%zx, can't pad it with zeros to 0x%lx\n", curr_offset, strtol(optarg, NULL, 0)); + } else { + sbytes = oseama_entity_append_zeros(seama, sbytes); + if (sbytes < 0) { + fprintf(stderr, "Failed to append zeros\n"); + } else { + curr_offset += sbytes; + imagesize += sbytes; + } + } + break; + } + if (err) + break; + } + + oseama_entity_write_hdr(seama, metasize, imagesize); + + fclose(seama); +out: + return err; +} + +/************************************************** + * Start + **************************************************/ + +static void usage() { + printf("Usage:\n"); + printf("\n"); + printf("Info about Seama seal (container):\n"); + printf("\toseama info [options]\n"); + printf("\t-e\t\t\t\tprint info about specified entity only\n"); + printf("\n"); + printf("Create Seama entity:\n"); + printf("\toseama entity [options]\n"); + printf("\t-m meta\t\t\t\tmeta into to put in header\n"); + printf("\t-f file\t\t\t\tappend content from file\n"); + printf("\t-b offset\t\t\tappend zeros till reaching absolute offset\n"); +} + +int main(int argc, char **argv) { + if (argc > 1) { + if (!strcmp(argv[1], "info")) + return oseama_info(argc, argv); + else if (!strcmp(argv[1], "entity")) + return oseama_entity(argc, argv); + } + + usage(); + return 0; +} -- 2.11.0