From 3d09357fc7b5cd102f83bb88563ba51054ec3c26 Mon Sep 17 00:00:00 2001 From: Oleg S Date: Tue, 20 Feb 2024 10:40:29 +0300 Subject: [PATCH] asusuimage: new tool for creating TRX-images compatible with AsusWRT This tool allows you to create `trx`-images for many Asus routers. Supported v2 and v3 format of `trx`-images. This format and tool use the mkimage generated uImage or multi images and add Asus specific tail to pass AsusWRT validation. Tested on Asus RT-AX89X which uses the v2 format. Signed-off-by: Oleg S Signed-off-by: Christian Marangi [Major code cleanup, use generic endian conversion instead of defines, use switches, move info printing to a separate function, use zlib CRC32] [Remove VSC compatibility, fix warnings, rename to asusimage, move variables to top of functions] Link: https://github.com/openwrt/firmware-utils/pull/31 Signed-off-by: Robert Marko --- CMakeLists.txt | 1 + src/asusuimage.c | 763 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 764 insertions(+) create mode 100644 src/asusuimage.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 078c49a..6e356fc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,6 +29,7 @@ ENDMACRO(FW_UTIL) FW_UTIL(add_header "" "" "") FW_UTIL(addpattern "" "" "") FW_UTIL(asustrx "" "" "") +FW_UTIL(asusuimage "" "" "${ZLIB_LIBRARIES}") FW_UTIL(avm-wasp-checksum "" --std=gnu99 "") FW_UTIL(bcm4908asus "" "" "") FW_UTIL(bcm4908kernel "" "" "") diff --git a/src/asusuimage.c b/src/asusuimage.c new file mode 100644 index 0000000..e6f3190 --- /dev/null +++ b/src/asusuimage.c @@ -0,0 +1,763 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * + * Copyright (C) 2024 OpenWrt.org + * Copyright (C) 2024 Oleg S + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* crc32 */ + +/* Various defines picked from U-boot */ + +#define FDT_MAGIC 0xD00DFEED + +#define IH_MAGIC 0x27051956 + +#define IH_OS_LINUX 5 + +#define IH_ARCH_ARM64 22 + +#define IH_TYPE_KERNEL 2 +#define IH_TYPE_MULTI 4 + +#define IH_COMP_NONE 0 + +enum volume_t { + VOL_KERNEL = 0, + VOL_RAMDISK, + VOL_FLATDT, + + VOL_COUNT, +}; + +typedef struct { + uint8_t major; + uint8_t minor; +} __attribute__ ((packed)) version_t; + +#define FS_OFFSET_PREFIX (0xA9) + +typedef struct { + char prod_name[23]; + uint8_t unk0; // version of rootfs ??? + uint32_t fs_offset; // 24 bit BE (first byte = 0xA9) +} __attribute__ ((packed)) trx1_t; + +typedef struct { + char prod_name[12]; + uint16_t sn; // fw build no (example: 388) + uint16_t en; // fw extended build no (example: 51234) + uint8_t dummy; // likely random byte + uint8_t key; // hash value from kernel and fs + uint8_t unk[6]; // likely random bytes + uint32_t fs_offset; // 24 bit BE (first byte = 0xA9) +} __attribute__ ((packed)) trx2_t; // hdr2 + +typedef struct { + char prod_name[23]; + uint8_t unk0; + uint32_t unk1; // ??? usualy: 0x003000 +} __attribute__ ((packed)) trx3_t; + +typedef struct image_header { + uint32_t ih_magic; + uint32_t ih_hcrc; + uint32_t ih_time; + uint32_t ih_size; // content size + uint32_t ih_load; // load addr + uint32_t ih_ep; // entry point + uint32_t ih_dcrc; // content hash + uint8_t ih_os; // os type + uint8_t ih_arch; // kernel arch + uint8_t ih_type; // image type + uint8_t ih_comp; // compression + version_t kernel_ver; // usualy: 3.0 + version_t fs_ver; // usualy: 0.4 + union { + trx1_t trx1; + trx2_t trx2; + trx3_t trx3; + } tail; +} __attribute__ ((packed)) image_header_t; + +typedef struct { + uint32_t extendno; // fw extended build no (example: 51234) + uint16_t buildno; // fw build no (example: 388) + uint16_t r16; // always 0 ??? + uint32_t r32; // always 0 ??? +} __attribute__ ((packed)) tail_content_t; + +#define DEF_ASUS_TAIL_MAGIC 0x2AFED414 + +typedef struct { + uint8_t flags: 4, // always 0 ??? + type : 4; // always 1 ??? + uint8_t clen[3]; // content len (24bit BE) + uint16_t fcrc; // crc for footer + uint16_t checksum; // content hash + uint32_t magic; +} __attribute__ ((packed)) tail_footer_t; + +typedef struct { + int show_info; + char * imagefn; + char * outfn; + char prod_name[128]; + int trx_ver; + version_t kernel_ver; + version_t fs_ver; + uint32_t magic; + uint32_t type; + uint32_t flags; + uint32_t extendno; + uint32_t buildno; + uint32_t r16; + uint32_t r32; +} trx_opt_t; + +trx_opt_t g_def = {0}; +trx_opt_t g_opt = {0}; + +// ========================================================= + +#define ROUNDUP(x, n) (((x) + (n - 1)) & ~(n - 1)) + +// ========================================================= + +char * g_progname = ""; +int g_debug = 0; + +#define DBG(...) do { if (g_debug) printf(__VA_ARGS__); } while(0) +#define _attr_fmt_err_ __attribute__ ((format (printf, 1, 2))) + +static _attr_fmt_err_ +void fatal_error(const char * fmtstr, ...) +{ + va_list ap; + fflush(0); + fprintf(stderr, "%s: ERROR: ", g_progname); + va_start(ap, fmtstr); + vfprintf(stderr, fmtstr, ap); + va_end (ap); + fprintf(stderr, "\n"); + exit(EXIT_FAILURE); +} + +#define ERR(fmtstr, ...) fatal_error(fmtstr, ## __VA_ARGS__) + +static +uint16_t asus_hash16(const void * data, size_t length) +{ + uint16_t * current = (uint16_t *) data; + length = length / sizeof(uint16_t); + uint16_t hash = 0; + + while (length--) { + hash ^= *current++; + } + return ~hash; // same as hash ^ 0xFFFF +} + +void update_iheader_crc(image_header_t * hdr, const void * data, size_t data_size) +{ + if (data == NULL) + data = (const void *)((char *)hdr + sizeof(image_header_t)); + + // Calculate payload checksum + hdr->ih_dcrc = htobe32(crc32(0, data, data_size)); + hdr->ih_size = htobe32(data_size); + + // Calculate header checksum + hdr->ih_hcrc = 0; + hdr->ih_hcrc = htobe32(crc32(0, (const void *)hdr, sizeof(image_header_t))); +} + +static +void init_opt(void) +{ + memset(&g_def, 0, sizeof(g_def)); + g_def.show_info = 0; + g_def.trx_ver = 3; + g_def.magic = DEF_ASUS_TAIL_MAGIC; + g_def.type = 1; + g_def.flags = 0; + memcpy(&g_opt, &g_def, sizeof(g_def)); +} + +static +void usage(int status) +{ + FILE * fp = (status != EXIT_SUCCESS) ? stderr : stdout; + + fprintf(fp, "Usage: %s -i [OPTIONS...]\n", g_progname); + fprintf(fp, "\n"); + fprintf(fp, "Options:\n"); + fprintf(fp, " -i input image filename \n"); + fprintf(fp, " -o output image filename \n"); + fprintf(fp, " -x show only image info \n"); + fprintf(fp, " -n product name \n"); + fprintf(fp, " -v TRX version: 2 or 3 (def: %d) \n", g_def.trx_ver); + fprintf(fp, " -K <#>.<#> kernel version (def: \"%d.%d\") \n", g_def.kernel_ver.major, g_def.kernel_ver.minor); + fprintf(fp, " -F <#>.<#> filesys version (def: \"%d.%d\") \n", g_def.fs_ver.major, g_def.fs_ver.minor); + fprintf(fp, " -m tail HEX signature (def: %08X) \n", g_def.magic); + fprintf(fp, " -t tail type (def: %X) \n", g_def.type); + fprintf(fp, " -f tail flags (def: %X) \n", g_def.flags); + fprintf(fp, " -e tail ext no (def: %u) \n", g_def.extendno); + fprintf(fp, " -b tail build no (def: %u) \n", g_def.buildno); + fprintf(fp, " -h show this screen \n"); + exit(status); +} + +static +int parse_args(int argc, char ** argv) +{ + char *str, *end; + int opt; + + while ((opt = getopt(argc, argv, "Dxi:o:n:K:F:v:m:t:f:e:b:h?")) != -1) { + switch (opt) { + case 'i': + g_opt.imagefn = optarg; + break; + case 'o': + g_opt.outfn = optarg; + break; + case 'x': + g_opt.show_info = 1; + g_debug = 1; + break; + case 'D': + g_debug = 1; + break; + case 'n': + strncpy(g_opt.prod_name, optarg, sizeof(g_opt.prod_name) - 1); + break; + case 'v': + g_opt.trx_ver = strtoul(optarg, &end, 0); + if (end == optarg) + ERR("Incorrect -v argument!"); + break; + case 'K': + g_opt.kernel_ver.major = (uint8_t) strtoul(optarg, &end, 10); + if (end == optarg || end[0] != '.') + ERR("Incorrect -K argument!"); + + str = end + 1; + g_opt.kernel_ver.minor = (uint8_t) strtoul(str, &end, 10); + if (end == str) + ERR("Incorrect -K argument!"); + break; + case 'F': + g_opt.fs_ver.major = (uint8_t) strtoul(optarg, &end, 10); + if (end == optarg || end[0] != '.') + ERR("Incorrect -F argument!"); + + str = end + 1; + g_opt.fs_ver.minor = (uint8_t) strtoul(str, &end, 10); + if (end == str) + ERR("Incorrect -F argument!"); + break; + case 'm': + g_opt.magic = strtoul(optarg, &end, 16); + if (end == optarg) + ERR("Incorrect -m argument!"); + break; + case 't': + g_opt.type = strtoul(optarg, &end, 0); + if (end == optarg) + ERR("Incorrect -t argument!"); + break; + case 'f': + g_opt.flags = strtoul(optarg, &end, 0); + if (end == optarg) + ERR("Incorrect -f argument!"); + break; + case 'e': + g_opt.extendno = strtoul(optarg, &end, 0); + if (end == optarg) + ERR("Incorrect -e argument!"); + break; + case 'b': + g_opt.buildno = strtoul(optarg, &end, 0); + if (end == optarg) + ERR("Incorrect -b argument!"); + break; + case 'h': + default: + usage(EXIT_FAILURE); + } + } + if (g_opt.imagefn == NULL || g_opt.imagefn[0] == 0) + usage(EXIT_FAILURE); // Required input image filename! + + if (g_opt.show_info == 0) + if (g_opt.outfn == NULL || g_opt.outfn[0] == 0) + usage(EXIT_FAILURE); // Required output image filename! + + if (g_opt.trx_ver < 2 || g_opt.trx_ver > 3) + usage(EXIT_FAILURE); + + return 0; +} + +static +char * load_image(size_t pad_size, size_t * psize) +{ + uint32_t file_sz; + size_t readed; + void * buf; + FILE *fp; + + fp = fopen(g_opt.imagefn, "rb"); + if (!fp) + ERR("Can't open %s: %s", g_opt.imagefn, strerror(errno)); + + rewind(fp); + fseek(fp, 0, SEEK_END); + file_sz = ftell(fp); + rewind(fp); + + if ((int32_t)file_sz <= 0) { + fclose(fp); + ERR("Error getting filesize: %s", g_opt.imagefn); + } + + if (file_sz <= sizeof(image_header_t)) { + fclose(fp); + ERR("Bad size: \"%s\" is no valid image", g_opt.imagefn); + } + + buf = malloc(file_sz + pad_size); + if (!buf) { + fclose(fp); + ERR("Out of memory!"); + } + memset(buf, 0, file_sz + pad_size); + + readed = fread(buf, 1, file_sz, fp); + fclose(fp); + if (readed != (size_t)file_sz) + ERR("Error reading file %s", g_opt.imagefn); + + *psize = file_sz; + + return (char *)buf; +} + +static +uint32_t get_timestamp(void) +{ + char * env = getenv("SOURCE_DATE_EPOCH"); + time_t fixed_timestamp = -1; + char * endptr = env; + + if (env && *env) { + errno = 0; + fixed_timestamp = (time_t) strtoull(env, &endptr, 10); + + if (errno || (endptr && *endptr != '\0')) { + fprintf(stderr, "ERROR: Invalid SOURCE_DATE_EPOCH \n"); + fixed_timestamp = -1; + } + } + + if (fixed_timestamp == -1) + time(&fixed_timestamp); + + DBG("timestamp: %u \n", (uint32_t)fixed_timestamp); + return (uint32_t)fixed_timestamp; +} + +static int show_info(char *img, size_t img_size) +{ + tail_footer_t * foot; + image_header_t *hdr; + int i; + + /* Assume valid, already validated early in process_image */ + hdr = (image_header_t *)img; + foot = (tail_footer_t *)(img + img_size - sizeof(tail_footer_t)); + + if (be32toh(hdr->ih_magic) != IH_MAGIC) { + free(img); + ERR("Incorrect image: \"%s\" magic must be %08X", g_opt.imagefn, IH_MAGIC); + } + + g_opt.trx_ver = 0; + if (be32toh(foot->magic) == g_opt.magic) + g_opt.trx_ver = 3; /* tail with magic = DEF_ASUS_TAIL_MAGIC */ + + if (be32toh(hdr->tail.trx2.fs_offset) >> 24 == FS_OFFSET_PREFIX) { + g_opt.trx_ver = 1; /* hdr1 */ + + for (i = 0; i < sizeof(hdr->tail.trx1.prod_name); i++) { + if (hdr->tail.trx1.prod_name[i] >= 0x7F) + g_opt.trx_ver = 2; /* hdr2 */ + } + + if (hdr->tail.trx2.sn >= 380 && hdr->tail.trx2.sn <= 490) + g_opt.trx_ver = 2; /* hdr2 */ + } + + DBG("detect trx version = %d \n", g_opt.trx_ver); + switch(g_opt.trx_ver) { + case 1: + free(img); + ERR("Formart HDR1 currently not supported"); + break; + case 2: + uint32_t data_size, fdt_size, fs_size, fs_offset = 0; + const uint32_t hsz = sizeof(image_header_t); + trx2_t *trx = &hdr->tail.trx2; + uint8_t fs_key, kernel_key, key; + uint32_t sn, en, xx = 0; + size_t buf_size = 12; + uint32_t *fs_data; + uint8_t *buf; + + data_size = (uint32_t)be32toh(hdr->ih_size); + sn = htole16(trx->sn); + en = htole16(trx->en); + + if (en < 20000 && sn >= 386) + en += 0x10000; + + DBG("hdr2.sn: %u (0x%04X) \n", sn, sn); + DBG("hdr2.en: %u (0x%04X) \n", en, htole16(trx->en)); + DBG("hdr2.key: 0x%02X \n", trx->key); + buf = trx->unk; + for (size_t i = 0; i < buf_size; i += 2) { + if (buf[0] == FS_OFFSET_PREFIX && (buf[3] & 3) == 0) { + xx = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | (xx && 0xFF); + fs_offset = be32toh(xx); + buf += 2; + } + buf += 2; + } + DBG("fs_offset: 0x%08X \n", fs_offset); + if (fs_offset + 128 > img_size) + ERR("Incorrect fs_offset!"); + + fs_data = (uint32_t *)(img + fs_offset); + DBG("fs_data: %08X %08X \n", be32toh(fs_data[0]), be32toh(fs_data[1])); + fs_size = hsz + data_size - fs_offset; + DBG("fs_size: 0x%X bytes \n", fs_size); + fs_key = img[fs_offset + fs_size / 2]; + kernel_key = img[fs_offset / 2]; + DBG("fs_key: 0x%02X kernel_key: 0x%02X \n", fs_key, kernel_key); + if (fs_key) { + key = kernel_key + ~fs_key; + } else { + key = (kernel_key % 3) - 3; + } + DBG("key = 0x%02X \n", key); + if (be32toh(fs_data[0]) == FDT_MAGIC) { + DBG("fdt_offset: 0x%08X \n", fs_offset); + fdt_size = be32toh(fs_data[1]); + DBG("fdt_size: 0x%X bytes \n", fdt_size); + fs_offset += ROUNDUP(fdt_size, 64); + DBG("fs_offset: 0x%08X \n", fs_offset); + fs_size -= ROUNDUP(fdt_size, 64); + DBG("fs_size: 0x%X bytes \n", fs_size); + } + break; + case 3: + uint16_t fcrc, fcrc_c, checksum_c; + tail_content_t *cont; + uint32_t cont_len; + + DBG("tail: footer size = 0x%lX (%lu) \n", sizeof(tail_footer_t), sizeof(tail_footer_t)); + DBG("tail: footer magic: 0x%X \n", be32toh(foot->magic)); + + cont_len = foot->clen[0] << 24 | foot->clen[1] << 16 | foot->clen[2]; + DBG("tail: type = %X, flags = %X, content len = 0x%06X \n", foot->type, foot->flags, cont_len); + + fcrc = foot->fcrc; + foot->fcrc = 0; + fcrc_c = asus_hash16(foot, sizeof(*foot)); + DBG("tail: fcrc = %04X (%04X) \n", be16toh(fcrc), fcrc_c); + + cont = (tail_content_t *)((char *)foot - cont_len); + checksum_c = asus_hash16(cont, sizeof(*cont)); + DBG("cont: checksum = %04X (%04X) \n", be16toh(foot->checksum), checksum_c); + + DBG("cont: buildno: %u, extendno: %u \n", be16toh(cont->buildno), be32toh(cont->extendno)); + DBG("cont: r16: 0x%08X, r32: 0x%08X \n", be16toh(cont->r16), be32toh(cont->r32)); + break; + default: + free(img); + ERR("Input image is not compatible with AsusWRT"); + } + + free(img); + return 0; +} + +static +int process_image(void) +{ + const uint32_t hsz = sizeof(image_header_t); + uint32_t vol_offset[VOL_COUNT + 1] = { 0 }; + uint32_t i, data_size, data_crc_c, fs_offset = 0, vol_count = 0, + *fs_data, fs_size, fdt_size, *vol_size, xoffset, *fdt, + cur_fdt_offset, new_fdt_offset, new_fs_offset, pad, + hsqs_offset, hsqs_size, *hsqs_data, cont_len; + uint32_t __attribute__ ((unused)) fdt_offset; + size_t img_size = 0, max_prod_len, new_img_size, + new_data_size, wlen; + char *img, *img_end, *prod_name; + uint8_t fs_key, kernel_key, key; + const char *prod_name_str; + tail_content_t *cont; + image_header_t *hdr; + tail_footer_t *foot; + trx2_t *trx; + FILE *fp; + + img = load_image(1024, &img_size); + if (!img) + ERR("Can't load file %s", g_opt.imagefn); + + if (g_opt.show_info) + return show_info(img, img_size); + + hdr = (image_header_t *)img; + if (be32toh(hdr->ih_magic) != IH_MAGIC) { + memmove(img + hsz, img, img_size); + memset(hdr, 0, hsz); + hdr->ih_magic = htobe32(IH_MAGIC); + hdr->ih_time = htobe32(get_timestamp()); + hdr->ih_size = htobe32(img_size); + hdr->ih_load = 0; + hdr->ih_ep = 0; + hdr->ih_os = IH_OS_LINUX; + hdr->ih_arch = IH_ARCH_ARM64; + hdr->ih_type = IH_TYPE_KERNEL; + hdr->ih_comp = IH_COMP_NONE; + img_size += hsz; + } + data_size = (uint32_t)be32toh(hdr->ih_size); + DBG("data: size = 0x%08X (%u bytes) \n", data_size, data_size); + if (data_size + hsz > img_size) + ERR("Bad size: \"%s\" is no valid content size", g_opt.imagefn); + + data_crc_c = crc32(0, (const unsigned char *)(img + hsz), data_size); + DBG("data: crc = %08X (%08X) \n", be32toh(hdr->ih_dcrc), data_crc_c); + + DBG("image type: %d \n", (int)hdr->ih_type); + + img_end = img + img_size; + + memset(&hdr->tail.trx1, 0, sizeof(hdr->tail.trx1)); + switch(g_opt.trx_ver) { + case 2: + prod_name = hdr->tail.trx2.prod_name; + max_prod_len = sizeof(hdr->tail.trx2.prod_name); + break; + case 3: + prod_name = hdr->tail.trx3.prod_name; + max_prod_len = sizeof(hdr->tail.trx3.prod_name); + } + + prod_name_str = (const char *)&hdr->kernel_ver; + if (g_opt.prod_name[0]) + prod_name_str = g_opt.prod_name; + + strncpy(prod_name, prod_name_str, max_prod_len); + hdr->kernel_ver = g_opt.kernel_ver; + hdr->fs_ver = g_opt.fs_ver; + + switch(g_opt.trx_ver) { + case 2: + trx = &hdr->tail.trx2; + + if (hdr->ih_type == IH_TYPE_MULTI) { + DBG("detect image with type: IH_TYPE_MULTI \n"); + vol_size = (uint32_t *)(img + hsz); + if (vol_size[0] == 0) { + free(img); + ERR("Multi image does not contain volumes"); + } + + for (uint32_t i = 0; i <= VOL_COUNT; i++) { + if (vol_size[i] == 0) + break; + vol_count++; + } + DBG("Multi image: volumes count = %u \n", vol_count); + + if (vol_count > VOL_COUNT) { + free(img); + ERR("Multi image contains too many volumes"); + } + + xoffset = hsz + sizeof(uint32_t) * (vol_count + 1); + for (i = 0; i < vol_count; i++) { + xoffset = ROUNDUP(xoffset, 4); + vol_offset[i] = xoffset; + DBG("Multi image: volume %u has offset = 0x%08X \n", i, xoffset); + if (be32toh(vol_size[i]) > 0x4FFFFFF) { + free(img); + ERR("Multi image contain volume %u with huge size", i); + } + + xoffset += be32toh(vol_size[i]); + } + if (xoffset > img_size) { + free(img); + ERR("Multi image contain incorrect img-size header"); + } + + fdt = (uint32_t *)(img + vol_offset[VOL_FLATDT]); + if (vol_offset[VOL_FLATDT] && be32toh(fdt[0]) == FDT_MAGIC) { + if (hdr->ih_arch == IH_ARCH_ARM64 && (vol_offset[VOL_FLATDT] & 7) != 0) { + // for ARM64 offset of FlatDT must be 8-bytes align + cur_fdt_offset = vol_offset[VOL_FLATDT]; + new_fdt_offset = vol_offset[VOL_FLATDT] + 4; + memmove(img + new_fdt_offset, img + cur_fdt_offset, img_size - cur_fdt_offset); + memset(img + cur_fdt_offset, 0, 4); + img_size += 4; + data_size += 4; + vol_offset[VOL_FLATDT] = new_fdt_offset; + vol_size[VOL_RAMDISK] = htobe32( be32toh(vol_size[VOL_RAMDISK]) + 4 ); + DBG("Multi image: volume %u size increased by 4 bytes \n", VOL_RAMDISK); + DBG("Multi image: volume %u has offset = 0x%08X (patched) \n", VOL_FLATDT, new_fdt_offset); + } + } + fs_offset = vol_offset[VOL_RAMDISK]; + if (fs_offset == 0) { + //ERR("Multi image does not contain rootfs volume"); + fs_offset = hsz + data_size; + } + } else { + fs_offset = hsz + data_size; + if (fs_offset & 3) { + //ERR("kernel size must be align to 4 bytes"); + new_fs_offset = ROUNDUP(fs_offset, 4); + memmove(img + new_fs_offset, img + fs_offset, img_size - fs_offset); + pad = new_fs_offset - fs_offset; + memset(img + fs_offset, 0, pad); + img_size += pad; + data_size += pad; + fs_offset = new_fs_offset; + } + } + DBG("fs_offset: 0x%08X \n", fs_offset); + fs_data = (uint32_t *)(img + fs_offset); + DBG("fs_data: %08X %08X \n", be32toh(fs_data[0]), be32toh(fs_data[1])); + fs_size = img_size - fs_offset; + fdt_offset = 0; + fdt_size = 0; + hsqs_offset = fs_offset; + hsqs_size = fs_size; + if (be32toh(fs_data[0]) == FDT_MAGIC && !vol_count) { + fdt_offset = fs_offset; + DBG("fdt_offset: 0x%08X \n", fs_offset); + fdt_size = be32toh(fs_data[1]); + DBG("fdt_size: 0x%X bytes \n", fdt_size); + hsqs_offset += ROUNDUP(fdt_size, 64); + hsqs_size -= ROUNDUP(fdt_size, 64); + } + DBG("hsqs_offset: 0x%08X \n", hsqs_offset); + DBG("hsqs_size: 0x%X bytes \n", hsqs_size); + hsqs_data = (uint32_t *)(img + hsqs_offset); + DBG("hsqs_data: %08X %08X \n", be32toh(hsqs_data[0]), be32toh(hsqs_data[1])); + kernel_key = img[fs_offset / 2]; + fs_key = img[fs_offset + fs_size / 2]; + DBG("fs_key: 0x%02X kernel_key: 0x%02X \n", fs_key, kernel_key); + key = fs_key ? kernel_key + ~fs_key : (kernel_key % 3) - 3; + DBG("key = 0x%02X \n", key); + trx->sn = htole16((uint16_t)g_opt.buildno); + trx->en = htole16((uint16_t)g_opt.extendno); + trx->key = key; + if (fs_offset >= 0xFFFFFF) { + free(img); + ERR("kernel image size is too big (max size: 16MiB)"); + } + + trx->fs_offset = htobe32((FS_OFFSET_PREFIX << 24) + fs_offset); + update_iheader_crc(hdr, NULL, img_size - hsz); + break; + case 3: + cont_len = 0; + cont = NULL; + foot = NULL; + + hdr->tail.trx3.unk1 = htobe32(0x3000); // unknown value + + cont_len = img_size - hsz - data_size + sizeof(tail_content_t); + cont = (tail_content_t *)img_end; + cont->extendno = htobe32(g_opt.extendno); + cont->buildno = htobe16(g_opt.buildno); + cont->r16 = htobe16(g_opt.r16); + cont->r32 = htobe32(g_opt.r32); + + foot = (tail_footer_t *)(img_end + sizeof(tail_content_t)); + char * cont_ptr = img + hsz + data_size; + foot->checksum = htobe16(asus_hash16(cont_ptr, cont_len)); + + if (cont_len >= (1UL << 24)) { + free(img); + ERR("Content length is too long (more than 0x%lX bytes)", 1UL << 24); + } + + foot->clen[0] = (cont_len >> 16) & 0xFF; // 24bit BigEndian + foot->clen[1] = (cont_len >> 8) & 0xFF; + foot->clen[2] = cont_len & 0xFF; + + foot->magic = htobe32(g_opt.magic); + foot->type = g_opt.type; + foot->flags = g_opt.flags; + foot->fcrc = 0; + foot->fcrc = htobe16(asus_hash16(foot, sizeof(*foot))); + + new_img_size = (size_t)((char *)foot + sizeof(tail_footer_t) - img); + new_data_size = new_img_size - hsz; + update_iheader_crc(hdr, NULL, new_data_size); + + img_size = hsz + data_size + cont_len + sizeof(tail_footer_t); + } + + fp = fopen(g_opt.outfn, "wb"); + if (!fp) { + free(img); + ERR("Can't open %s for writing: %s", g_opt.outfn, strerror(errno)); + } + + wlen = fwrite(img, img_size, 1, fp); + fclose(fp); + if (wlen != 1) { + free(img); + ERR("Failed to write: %s", g_opt.outfn); + } + + DBG("New TRX-image file created: \"%s\" \n", g_opt.outfn); + free(img); + + return 0; // OK +} + +int main(int argc, char ** argv) +{ + g_progname = argv[0]; + + init_opt(); + parse_args(argc, argv); + + int rc = process_image(); + + return rc; +} -- 2.30.2