From 84c1b786b9830c17034e5de915ce11965666febd Mon Sep 17 00:00:00 2001 From: Pawel Dembicki Date: Sat, 3 Feb 2018 12:59:50 +0100 Subject: [PATCH] firmware-utils: add JBOOT bootloader image support Tested on D-Link DWR-116. Based on mktplinkfw. Signed-off-by: Pawel Dembicki --- tools/firmware-utils/Makefile | 1 + tools/firmware-utils/src/mkdlinkfw-lib.c | 172 ++++++ tools/firmware-utils/src/mkdlinkfw-lib.h | 83 +++ tools/firmware-utils/src/mkdlinkfw.c | 665 +++++++++++++++++++++++ 4 files changed, 921 insertions(+) create mode 100644 tools/firmware-utils/src/mkdlinkfw-lib.c create mode 100644 tools/firmware-utils/src/mkdlinkfw-lib.h create mode 100644 tools/firmware-utils/src/mkdlinkfw.c diff --git a/tools/firmware-utils/Makefile b/tools/firmware-utils/Makefile index f3fefa329a..7fa6f71e4a 100644 --- a/tools/firmware-utils/Makefile +++ b/tools/firmware-utils/Makefile @@ -81,6 +81,7 @@ define Host/Compile $(call cc,mkbuffaloimg, -Wall) $(call cc,zyimage, -Wall) $(call cc,mkdhpimg buffalo-lib, -Wall) + $(call cc,mkdlinkfw mkdlinkfw-lib, -lz -Wall) endef define Host/Install diff --git a/tools/firmware-utils/src/mkdlinkfw-lib.c b/tools/firmware-utils/src/mkdlinkfw-lib.c new file mode 100644 index 0000000000..fcab856231 --- /dev/null +++ b/tools/firmware-utils/src/mkdlinkfw-lib.c @@ -0,0 +1,172 @@ +/* + * mkdlinkfw + * + * Copyright (C) 2018 Paweł Dembicki + * + * This tool is based on mktplinkfw. + * Copyright (C) 2009 Gabor Juhos + * Copyright (C) 2008,2009 Wang Jian + * + * 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 /* for unlink() */ +#include +#include /* for getopt() */ +#include +#include +#include +#include +#include +#include +#include /*for crc32 */ + +#include "mkdlinkfw-lib.h" + +extern char *progname; + +static unsigned char jffs2_eof_mark[4] = { 0xde, 0xad, 0xc0, 0xde }; + +uint32_t jboot_timestamp(void) +{ + time_t rawtime; + time(&rawtime); + return (((uint32_t) rawtime) - TIMESTAMP_MAGIC) >> 2; +} + +uint16_t jboot_checksum(uint16_t start_val, uint16_t *data, int size) +{ + uint32_t counter = start_val; + uint16_t *ptr = data; + + while (size > 1) { + counter += *ptr; + ++ptr; + while (counter >> 16) + counter = (uint16_t) counter + (counter >> 16); + size -= 2; + } + if (size > 0) { + counter += *(uint8_t *) ptr; + counter -= 0xFF; + } + while (counter >> 16) + counter = (uint16_t) counter + (counter >> 16); + return counter; +} + +int get_file_stat(struct file_info *fdata) +{ + struct stat st; + int res; + + if (fdata->file_name == NULL) + return 0; + + res = stat(fdata->file_name, &st); + if (res) { + ERRS("stat failed on %s", fdata->file_name); + return res; + } + + fdata->file_size = st.st_size; + return 0; +} + +int read_to_buf(const struct file_info *fdata, char *buf) +{ + FILE *f; + int ret = EXIT_FAILURE; + + f = fopen(fdata->file_name, "r"); + if (f == NULL) { + ERRS("could not open \"%s\" for reading", fdata->file_name); + goto out; + } + + errno = 0; + fread(buf, fdata->file_size, 1, f); + if (errno != 0) { + ERRS("unable to read from file \"%s\"", fdata->file_name); + goto out_close; + } + + ret = EXIT_SUCCESS; + + out_close: + fclose(f); + out: + return ret; +} + +int pad_jffs2(char *buf, int currlen, int maxlen) +{ + int len; + uint32_t pad_mask; + + len = currlen; + pad_mask = (4 * 1024) | (64 * 1024); /* EOF at 4KB and at 64KB */ + while ((len < maxlen) && (pad_mask != 0)) { + uint32_t mask; + int i; + + for (i = 10; i < 32; i++) { + mask = 1 << i; + if (pad_mask & mask) + break; + } + + len = ALIGN(len, mask); + + for (i = 10; i < 32; i++) { + mask = 1 << i; + if ((len & (mask - 1)) == 0) + pad_mask &= ~mask; + } + + for (i = 0; i < sizeof(jffs2_eof_mark); i++) + buf[len + i] = jffs2_eof_mark[i]; + + len += sizeof(jffs2_eof_mark); + } + + return len; +} + +int write_fw(const char *ofname, const char *data, int len) +{ + FILE *f; + int ret = EXIT_FAILURE; + + f = fopen(ofname, "w"); + if (f == NULL) { + ERRS("could not open \"%s\" for writing", ofname); + goto out; + } + + errno = 0; + fwrite(data, len, 1, f); + if (errno) { + ERRS("unable to write output file"); + goto out_flush; + } + + DBG("firmware file \"%s\" completed", ofname); + + ret = EXIT_SUCCESS; + + out_flush: + fflush(f); + fclose(f); + if (ret != EXIT_SUCCESS) + unlink(ofname); + out: + return ret; +} diff --git a/tools/firmware-utils/src/mkdlinkfw-lib.h b/tools/firmware-utils/src/mkdlinkfw-lib.h new file mode 100644 index 0000000000..d61124cb63 --- /dev/null +++ b/tools/firmware-utils/src/mkdlinkfw-lib.h @@ -0,0 +1,83 @@ +/* + * mkdlinkfw + * + * Copyright (C) 2018 Paweł Dembicki + * + * This tool is based on mktplinkfw. + * Copyright (C) 2009 Gabor Juhos + * Copyright (C) 2008,2009 Wang Jian + * + * 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. + */ + +#ifndef mkdlinkfw_lib_h +#define mkdlinkfw_lib_h + +#define AUH_MAGIC "DLK" +#define AUH_SIZE 80 +#define AUH_LVPS 0x01 +#define AUH_HDR_ID 0x4842 +#define AUH_HDR_VER 0x02 +#define AUH_SEC_ID 0x04 +#define AUH_INFO_TYPE 0x04 + +#define STAG_SIZE 16 +#define STAG_ID 0x04 +#define STAG_MAGIC 0x2B24 +#define STAG_CMARK_FACTORY 0xFF + +#define SCH2_SIZE 40 +#define SCH2_MAGIC 0x2124 +#define SCH2_VER 0x02 + +#define FLAT 0 +#define JZ 1 +#define GZIP 2 +#define LZMA 3 + +#define RAM_ENTRY_ADDR 0x80000000 +#define RAM_LOAD_ADDR 0x80000000 +#define JBOOT_SIZE 0x10000 + +#define ALL_HEADERS_SIZE (AUH_SIZE + STAG_SIZE + SCH2_SIZE) +#define MAX_HEADER_COUNTER 10 +#define TIMESTAMP_MAGIC 0x35016f00L + +#define FACTORY 0 +#define SYSUPGRADE 1 + +#define ALIGN(x, a) ({ typeof(a) __a = (a); (((x) + __a - 1) & ~(__a - 1)); }) + +#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 ": %s\n", \ + progname, ## __VA_ARGS__, strerror(save)); \ +} while (0) + +#define DBG(fmt, ...) do { \ + fprintf(stderr, "[%s] " fmt "\n", progname, ## __VA_ARGS__); \ +} while (0) + +struct file_info { + char *file_name; /* name of the file */ + uint32_t file_size; /* length of the file */ +}; + +uint32_t jboot_timestamp(void); +uint16_t jboot_checksum(uint16_t start_val, uint16_t *data, int size); +int get_file_stat(struct file_info *fdata); +int read_to_buf(const struct file_info *fdata, char *buf); +int pad_jffs2(char *buf, int currlen, int maxlen); +int write_fw(const char *ofname, const char *data, int len); + +#endif /* mkdlinkfw_lib_h */ diff --git a/tools/firmware-utils/src/mkdlinkfw.c b/tools/firmware-utils/src/mkdlinkfw.c new file mode 100644 index 0000000000..87605004fe --- /dev/null +++ b/tools/firmware-utils/src/mkdlinkfw.c @@ -0,0 +1,665 @@ +/* + * mkdlinkfw + * + * Copyright (C) 2018 Paweł Dembicki + * + * This tool is based on mktplinkfw. + * Copyright (C) 2009 Gabor Juhos + * Copyright (C) 2008,2009 Wang Jian + * + * 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 /* for unlink() */ +#include +#include /* for getopt() */ +#include +#include +#include +#include +#include +#include /*for crc32 */ + +#include "mkdlinkfw-lib.h" + +/* ARM update header 2.0 + * used only in factory images to erase and flash selected area + */ +struct auh_header { + uint8_t rom_id[12]; /* 12-bit rom-id unique per router type */ + uint16_t derange; /* used for scramble header */ + uint16_t image_checksum; /* jboot_checksum of flashed data */ + + uint32_t space1; /* zeros */ + uint32_t space2; /* zeros */ + uint16_t space3; /* zerosu */ + uint8_t lpvs; /* must be 0x01 */ + uint8_t mbz; /* bust be 0 */ + uint32_t time_stamp; /* timestamp calculated in jboot way */ + + uint32_t erase_start; /* erase start address */ + uint32_t erase_length; /* erase length address */ + uint32_t data_offset; /* data start address */ + uint32_t data_length; /* data length address */ + + uint32_t space4; /* zeros */ + uint32_t space5; /* zeros */ + uint32_t space6; /* zeros */ + uint32_t space7; /* zeros */ + + uint16_t header_id; /* magic 0x4842 */ + uint16_t header_version; /* 0x02 for 2.0 */ + uint16_t space8; /* zeros */ + uint8_t section_id; /* section id */ + uint8_t image_info_type; /* (?) 0x04 in factory images */ + uint32_t image_info_offset; /* (?) zeros in factory images */ + uint16_t family_member; /* unique per router type */ + uint16_t header_checksum; /* negated jboot_checksum of header data */ +}; + +struct stag_header { /* used only of sch2 wrapped kernel data */ + uint8_t cmark; /* in factory 0xFF ,in sysuograde must be the same as id */ + uint8_t id; /* 0x04 */ + uint16_t magic; /* magic 0x2B24 */ + uint32_t time_stamp; /* timestamp calculated in jboot way */ + uint32_t image_length; /* lentgh of kernel + sch2 header */ + uint16_t image_checksum; /* negated jboot_checksum of sch2 + kernel */ + uint16_t tag_checksum; /* negated jboot_checksum of stag header data */ +}; + +struct sch2_header { /* used only in kernel partitions */ + uint16_t magic; /* magic 0x2124 */ + uint8_t cp_type; /* 0x00 for flat, 0x01 for jz, 0x02 for gzip, 0x03 for lzma */ + uint8_t version; /* 0x02 for sch2 */ + uint32_t ram_addr; /* ram entry address */ + uint32_t image_len; /* kernel image length */ + uint32_t image_crc32; /* kernel image crc */ + uint32_t start_addr; /* ram start address */ + uint32_t rootfs_addr; /* rootfs flash address */ + uint32_t rootfs_len; /* rootfls length */ + uint32_t rootfs_crc32; /* rootfs crc32 */ + uint32_t header_crc32; /* sch2 header crc32, durring calculation this area is replaced by zero */ + uint16_t header_length; /* sch2 header length: 0x28 */ + uint16_t cmd_line_length; /* cmd line length, known zeros */ +}; + +/* globals */ +static struct file_info inspect_info; +struct file_info kernel_info; +struct file_info rootfs_info; +struct file_info image_info; + +char *ofname; +char *progname; +uint32_t firmware_size; +uint16_t family_member; +char *rom_id[12] = { 0 }; + +char image_type; +int add_jffs2_eof; + +static void usage(int status) +{ + fprintf(stderr, "Usage: %s [OPTIONS...]\n", progname); + fprintf(stderr, + "\n" + "Options:\n" + " -i inspect given firmware file \n" + " -f set family member id (hexval prefixed with 0x)\n" + " -F read image and convert it to FACTORY\n" + " -k read kernel image from the file \n" + " -r read rootfs image from the file \n" + " -o write output to the file \n" + " -s set firmware partition size\n" + " -m set rom id to (12-bit string val: \"DLK*********\")\n" + " -h show this screen\n"); + + exit(status); +} + +void print_auh_header(struct auh_header *printed_header) +{ + printf("\trom_id: %s\n" + "\tderange: 0x%04X\n" + "\timage_checksum: 0x%04X\n" + "\tspace1: 0x%08X\n" + "\tspace2: 0x%08X\n" + "\tspace3: 0x%04X\n" + "\tlpvs: 0x%02X\n" + "\tmbz: 0x%02X\n" + "\ttime_stamp: 0x%08X\n" + "\terase_start: 0x%08X\n" + "\terase_length: 0x%08X\n" + "\tdata_offset: 0x%08X\n" + "\tdata_length: 0x%08X\n" + "\tspace4: 0x%08X\n" + "\tspace5: 0x%08X\n" + "\tspace6: 0x%08X\n" + "\tspace7: 0x%08X\n" + "\theader_id: 0x%04X\n" + "\theader_version: 0x%02X\n" + "\tspace8: 0x%04X\n" + "\tsection_id: 0x%02X\n" + "\timage_info_type: 0x%02X\n" + "\timage_info_offset 0x%08X\n" + "\tfamily_member: 0x%04X\n" + "\theader_checksum: 0x%04X\n", + printed_header->rom_id, + printed_header->derange, + printed_header->image_checksum, + printed_header->space1, + printed_header->space2, + printed_header->space3, + printed_header->lpvs, + printed_header->mbz, + printed_header->time_stamp, + printed_header->erase_start, + printed_header->erase_length, + printed_header->data_offset, + printed_header->data_length, + printed_header->space4, + printed_header->space5, + printed_header->space6, + printed_header->space7, + printed_header->header_id, + printed_header->header_version, + printed_header->space8, + printed_header->section_id, + printed_header->image_info_type, + printed_header->image_info_offset, + printed_header->family_member, printed_header->header_checksum); +} + +void print_stag_header(struct stag_header *printed_header) +{ + printf("\tcmark: 0x%02X\n" + "\tid: 0x%02X\n" + "\tmagic: 0x%04X\n" + "\ttime_stamp: 0x%08X\n" + "\timage_length: 0x%04X\n" + "\timage_checksum: 0x%04X\n" + "\ttag_checksum: 0x%04X\n", + printed_header->cmark, + printed_header->id, + printed_header->magic, + printed_header->time_stamp, + printed_header->image_length, + printed_header->image_checksum, printed_header->tag_checksum); +} + +void print_sch2_header(struct sch2_header *printed_header) +{ + printf("\tmagic: 0x%04X\n" + "\tcp_type: 0x%02X\n" + "\tversion: 0x%02X\n" + "\tram_addr: 0x%08X\n" + "\timage_len: 0x%08X\n" + "\timage_crc32: 0x%08X\n" + "\tstart_addr: 0x%08X\n" + "\trootfs_addr: 0x%08X\n" + "\trootfs_len: 0x%08X\n" + "\trootfs_crc32: 0x%08X\n" + "\theader_crc32: 0x%08X\n" + "\theader_length: 0x%04X\n" + "\tcmd_line_length: 0x%04X\n", + printed_header->magic, + printed_header->cp_type, + printed_header->version, + printed_header->ram_addr, + printed_header->image_len, + printed_header->image_crc32, + printed_header->start_addr, + printed_header->rootfs_addr, + printed_header->rootfs_len, + printed_header->rootfs_crc32, + printed_header->header_crc32, + printed_header->header_length, printed_header->cmd_line_length); +} + +static int find_auh_headers(char *buf) +{ + char *tmp_buf = buf; + struct auh_header *tmp_header[MAX_HEADER_COUNTER]; + int header_counter = 0; + + int ret = EXIT_FAILURE; + + while (tmp_buf - buf <= inspect_info.file_size - AUH_SIZE) { + if (!memcmp(tmp_buf, AUH_MAGIC, 3)) { + if (((struct auh_header *)tmp_buf)->header_checksum == + (uint16_t) ~jboot_checksum(0, (uint16_t *) tmp_buf, + AUH_SIZE - 2)) { + uint16_t checksum = 0; + printf("Find proper AUH header at: 0x%lX!\n", + tmp_buf - buf); + tmp_header[header_counter] = + (struct auh_header *)tmp_buf; + checksum = + jboot_checksum(0, (uint16_t *) ((char *) + tmp_header + [header_counter] + + AUH_SIZE), + tmp_header + [header_counter]->data_length); + if (tmp_header[header_counter]->image_checksum + == checksum) + printf("Image checksum ok.\n"); + else + ERR("Image checksum incorrect! Stored: 0x%X Calculated: 0x%X\n", tmp_header[header_counter]->image_checksum, checksum); + header_counter++; + if (header_counter > MAX_HEADER_COUNTER) + break; + } + } + tmp_buf++; + } + + if (header_counter == 0) + ERR("Can't find proper AUH header!\n"); + else if (header_counter > MAX_HEADER_COUNTER) + ERR("To many AUH headers!\n"); + else { + for (int i = 0; i < header_counter; i++) { + printf("AUH %d:\n", i); + print_auh_header(tmp_header[i]); + } + + ret = EXIT_SUCCESS; + } + + return ret; +} + +static int check_stag_header(char *buf, struct stag_header *header) +{ + + int ret = EXIT_FAILURE; + + uint8_t cmark_tmp = header->cmark; + header->cmark = header->id; + + if (header->tag_checksum == + (uint16_t) ~jboot_checksum(0, (uint16_t *) header, + STAG_SIZE - 2)) { + uint16_t checksum = 0; + printf("Find proper STAG header at: 0x%lX!\n", + (char *)header - buf); + checksum = + jboot_checksum(0, (uint16_t *) ((char *)header + STAG_SIZE), + header->image_length); + if (header->image_checksum == checksum) { + printf("Image checksum ok.\n"); + header->cmark = cmark_tmp; + print_stag_header(header); + ret = EXIT_SUCCESS; + } else + ERR("Image checksum incorrect! Stored: 0x%X Calculated: 0x%X\n", header->image_checksum, checksum); + } else + ERR("STAG header checksum incorrect!"); + + header->cmark = cmark_tmp; + return ret; +} + +static int check_sch2_header(char *buf, struct sch2_header *header) +{ + + int ret = EXIT_FAILURE; + + uint32_t crc32_tmp = header->header_crc32; + header->header_crc32 = 0; + + if (crc32_tmp == crc32(0, (uint8_t *) header, header->header_length)) { + uint32_t crc32_val; + printf("Find proper SCH2 header at: 0x%lX!\n", + (char *)header - buf); + + crc32_val = + crc32(0, (uint8_t *) header + header->header_length, + header->image_len); + if (header->image_crc32 == crc32_val) { + printf("Kernel checksum ok.\n"); + + header->header_crc32 = crc32_tmp; + print_sch2_header(header); + ret = EXIT_SUCCESS; + } else + ERR("Kernel checksum incorrect! Stored: 0x%X Calculated: 0x%X\n", header->image_crc32, crc32_val); + + } else + ERR("SCH2 header checksum incorrect!"); + + header->header_crc32 = crc32_tmp; + return ret; +} + +static int inspect_fw(void) +{ + char *buf; + struct stag_header *stag_header_kernel; + struct sch2_header *sch2_header_kernel; + int ret = EXIT_FAILURE; + + buf = malloc(inspect_info.file_size); + if (!buf) { + ERR("no memory for buffer!\n"); + goto out; + } + + ret = read_to_buf(&inspect_info, buf); + if (ret) + goto out_free_buf; + + ret = find_auh_headers(buf); + if (ret) + goto out_free_buf; + + stag_header_kernel = (struct stag_header *)(buf + AUH_SIZE); + + ret = check_stag_header(buf, stag_header_kernel); + if (ret) + goto out_free_buf; + + sch2_header_kernel = (struct sch2_header *)(buf + AUH_SIZE + STAG_SIZE); + + ret = check_sch2_header(buf, sch2_header_kernel); + if (ret) + goto out_free_buf; + + out_free_buf: + free(buf); + out: + return ret; +} + +static int check_options(void) +{ + int ret; + + if (inspect_info.file_name) { + ret = get_file_stat(&inspect_info); + if (ret) + return ret; + + return 0; + } + + return 0; +} + +int fill_sch2(struct sch2_header *header, char *kernel_ptr, char *rootfs_ptr) +{ + + header->magic = SCH2_MAGIC; + header->cp_type = LZMA; + header->version = SCH2_VER; + header->ram_addr = RAM_LOAD_ADDR; + header->image_len = kernel_info.file_size; + header->image_crc32 = crc32(0, (uint8_t *) kernel_ptr, kernel_info.file_size); + header->start_addr = RAM_ENTRY_ADDR; + header->rootfs_addr = + JBOOT_SIZE + STAG_SIZE + SCH2_SIZE + kernel_info.file_size; + header->rootfs_len = rootfs_info.file_size; + header->rootfs_crc32 = crc32(0, (uint8_t *) rootfs_ptr, rootfs_info.file_size); + header->header_crc32 = 0; + header->header_length = SCH2_SIZE; + header->cmd_line_length = 0; + + header->header_crc32 = crc32(0, (uint8_t *) header, header->header_length); + + return EXIT_SUCCESS; +} + +int fill_stag(struct stag_header *header, uint32_t length) +{ + header->cmark = STAG_ID; + header->id = STAG_ID; + header->magic = STAG_MAGIC; + header->time_stamp = jboot_timestamp(); + header->image_length = length + SCH2_SIZE; + header->image_checksum = + jboot_checksum(0, (uint16_t *) ((char *)header + STAG_SIZE), + header->image_length); + header->tag_checksum = + ~jboot_checksum(0, (uint16_t *) header, STAG_SIZE - 2); + + if (image_type == FACTORY) + header->cmark = STAG_CMARK_FACTORY; + + return EXIT_SUCCESS; +}; + +int fill_auh(struct auh_header *header, uint32_t length) +{ + memcpy(header->rom_id, rom_id, 12); + header->derange = 0; + header->image_checksum = + jboot_checksum(0, (uint16_t *) ((char *)header + AUH_SIZE), length); + header->space1 = 0; + header->space2 = 0; + header->space3 = 0; + header->lpvs = AUH_LVPS; + header->mbz = 0; + header->time_stamp = jboot_timestamp(); + header->erase_start = JBOOT_SIZE; + header->erase_length = firmware_size; + header->data_offset = JBOOT_SIZE; + header->data_length = length; + header->space4 = 0; + header->space5 = 0; + header->space6 = 0; + header->space7 = 0; + header->header_id = AUH_HDR_ID; + header->header_version = AUH_HDR_VER; + header->space8 = 0; + header->section_id = AUH_SEC_ID; + header->image_info_type = AUH_INFO_TYPE; + header->image_info_offset = 0; + header->family_member = family_member; + header->header_checksum = + ~jboot_checksum(0, (uint16_t *) header, AUH_SIZE - 2); + + return EXIT_SUCCESS; +} + +int build_fw(void) +{ + char *buf; + char *kernel_ptr; + char *rootfs_ptr; + int ret = EXIT_FAILURE; + int writelen; + + struct stag_header *stag_header_kernel; + struct sch2_header *sch2_header_kernel; + + if (!kernel_info.file_name | !rootfs_info.file_name) + goto out; + + ret = get_file_stat(&kernel_info); + if (ret) + goto out; + ret = get_file_stat(&rootfs_info); + if (ret) + goto out; + + buf = malloc(firmware_size); + if (!buf) { + ERR("no memory for buffer\n"); + goto out; + } + + if (rootfs_info.file_size + kernel_info.file_size + ALL_HEADERS_SIZE > + firmware_size) { + ERR("data is bigger than firmware_size!\n"); + goto out; + } + + memset(buf, 0xff, firmware_size); + + stag_header_kernel = (struct stag_header *)buf; + + sch2_header_kernel = + (struct sch2_header *)((char *)stag_header_kernel + STAG_SIZE); + kernel_ptr = (char *)sch2_header_kernel + SCH2_SIZE; + + ret = read_to_buf(&kernel_info, kernel_ptr); + if (ret) + goto out_free_buf; + + rootfs_ptr = kernel_ptr + kernel_info.file_size; + + ret = read_to_buf(&rootfs_info, rootfs_ptr); + if (ret) + goto out_free_buf; + + writelen = rootfs_ptr + rootfs_info.file_size - buf; + + fill_sch2(sch2_header_kernel, kernel_ptr, rootfs_ptr); + fill_stag(stag_header_kernel, kernel_info.file_size); + + ret = write_fw(ofname, buf, writelen); + if (ret) + goto out_free_buf; + + ret = EXIT_SUCCESS; + + out_free_buf: + free(buf); + out: + return ret; +} + +int wrap_fw(void) +{ + char *buf; + char *image_ptr; + int ret = EXIT_FAILURE; + int writelen; + + struct auh_header *auh_header_kernel; + + if (!image_info.file_name) + goto out; + + ret = get_file_stat(&image_info); + if (ret) + goto out; + + buf = malloc(firmware_size); + if (!buf) { + ERR("no memory for buffer\n"); + goto out; + } + + if (image_info.file_size + AUH_SIZE > + firmware_size) { + ERR("data is bigger than firmware_size!\n"); + goto out; + } + if (!family_member) { + ERR("No family_member!\n"); + goto out; + } + if (!(rom_id[0])) { + ERR("No rom_id!\n"); + goto out; + } + memset(buf, 0xff, firmware_size); + + image_ptr = (char *)(buf + AUH_SIZE); + + ret = read_to_buf(&image_info, image_ptr); + if (ret) + goto out_free_buf; + + writelen = image_ptr + image_info.file_size - buf; + + auh_header_kernel = (struct auh_header *)buf; + fill_auh(auh_header_kernel, writelen - AUH_SIZE); + + ret = write_fw(ofname, buf, writelen); + if (ret) + goto out_free_buf; + + ret = EXIT_SUCCESS; + + out_free_buf: + free(buf); + out: + return ret; +} + +int main(int argc, char *argv[]) +{ + int ret = EXIT_FAILURE; + + progname = basename(argv[0]); + image_type = SYSUPGRADE; + family_member = 0; + firmware_size = 0; + + while (1) { + int c; + + c = getopt(argc, argv, "f:F:i:hk:m:o:r:s:"); + if (c == -1) + break; + + switch (c) { + case 'f': + sscanf(optarg, "0x%hx", &family_member); + break; + case 'F': + image_info.file_name = optarg; + image_type = FACTORY; + break; + case 'i': + inspect_info.file_name = optarg; + break; + case 'k': + kernel_info.file_name = optarg; + break; + case 'm': + if (strlen(optarg) == 12) + memcpy(rom_id, optarg, 12); + break; + case 'r': + rootfs_info.file_name = optarg; + break; + case 'o': + ofname = optarg; + break; + case 's': + sscanf(optarg, "0x%x", &firmware_size); + break; + default: + usage(EXIT_FAILURE); + break; + } + } + + ret = check_options(); + if (ret) + goto out; + + if (!inspect_info.file_name) { + if (image_type == FACTORY) + ret = wrap_fw(); + else + ret = build_fw(); + } + else + ret = inspect_fw(); + + out: + return ret; + +}