diff options
Diffstat (limited to 'tools/firmware-utils/src/jcgimage.c')
-rw-r--r-- | tools/firmware-utils/src/jcgimage.c | 423 |
1 files changed, 0 insertions, 423 deletions
diff --git a/tools/firmware-utils/src/jcgimage.c b/tools/firmware-utils/src/jcgimage.c deleted file mode 100644 index 59a6ac4de5..0000000000 --- a/tools/firmware-utils/src/jcgimage.c +++ /dev/null @@ -1,423 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * jcgimage - Create a JCG firmware image - * - * Copyright (C) 2015 Reinhard Max <reinhard@m4x.de> - * Copyright (C) 2019 Davide Fioravanti <pantanastyle@gmail.com> - */ - -/* - * JCG firmware update images consist of a 512 byte header and a - * modified uImage (details below) as the payload. - * - * The payload is obfuscated by XORing it with a key that is generated - * from parts of the header. Fortunately only non-essential parts of - * the header are used for this and zeroing them results in a zero - * key, effectively disabling the obfuscation and allowing us to use - * clear text payloads. - * - * The mandatory parts of the header are: - * - * - A magic string of "YSZJ" at offset 0. - * - A value of 1 at offset 39 (header format version?) - * - A CRC32 checksum of the payload at offset 504. - * - A CRC32 checksum of the header at offset 508. - * - * An image constructed by these rules will be accepted by JCG's - * U-Boot in resuce mode via TFTP and the payload will be written to - * the flash starting at offset 0x00050000. - * - * JCG's U-Boot does check the content or size of the payload - * image. If it is too large, it wraps around and overwrites U-Boot, - * requiring JTAG to revive the board. To prevent such bricking from - * happening, this tool refuses to build such overlong images. - * - * Using -m is possible to set the maximum size of the payload. - * Otherwise the default MAXSIZE will be used. - * For an 8Mb flash, the corresponding maxsize is: - * 8 * 1024 * 1024 - 5 * 64 * 1024 = 8388608 - 327680 = 8060928 - * - * Two more conditions have to be met for a JCG image to be accepted - * as a valid update by the web interface of the stock firware: - * - * - The bytes at offsets 109 and 111 in the header must be a binary - * representation of the first two components of the firmware - * version as displayed in the update web form, or it will be - * rejected as "incorrect product". - * - * - The payload must start with a valid uImage header whose data - * CRC checksum matches the whole rest of the update file rather - * than just the number of bytes specified in the size field of the - * header. - * - * This last condition is met by JCG's original firmware images, - * because they have both, kernel and rootfs inside the uImage and - * abuse the last four bytes of the name field to record the offset of - * the file system from the start of the uImage header. This tool - * produces such images when called with -k and -r, which are meant to - * repack the original firmware after modifying the file systen, - * e.g. to add debugging tools and enable shell access. - * - * In contrast, OpenWrt sysupgrade images consist of a uImage that - * only contains the kernel and has the rootfs appended to it. Hence, - * the CRC over kernel and file system does not match the one in the - * uImage header. Fixing this by adjusting the uImage header is not - * possible, because it makes the uImage unusable for booting. Instead - * we append four "patch" bytes to the end of the file system, that - * are calculated to force the checksum of kernel+fs to be the same as - * for the kernel alone. - * - */ - -#include <zlib.h> -#include <stdio.h> -#include <string.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <unistd.h> -#include <libgen.h> -#include <stdlib.h> -#include <errno.h> -#include <err.h> -#include <time.h> -#include <sys/mman.h> -#include <arpa/inet.h> -#include <assert.h> -#include <inttypes.h> - -/* - * JCG Firmware image header - */ -#define JH_MAGIC 0x59535a4a /* "YSZJ" */ -struct jcg_header { - uint32_t jh_magic; - uint8_t jh_version[32]; /* Firmware version string. - Fill with zeros to avoid encryption */ - uint32_t jh_type; /* must be 1 */ - uint8_t jh_info[64]; /* Firmware info string. Fill with - zeros to avoid encryption */ - uint32_t jh_time; /* Image creation time in seconds since - * the Epoch. Does not seem to be used - * by the stock firmware. */ - uint16_t jh_major; /* Major fimware version */ - uint16_t jh_minor; /* Minor fimrmware version */ - uint8_t jh_unknown[392]; /* Apparently unused and all zeros */ - uint32_t jh_dcrc; /* CRC checksum of the payload */ - uint32_t jh_hcrc; /* CRC checksum of the header */ -}; - -/* - * JCG uses a modified uImage header that replaces the last four bytes - * of the image name with the length of the kernel in the image. - */ -#define IH_MAGIC 0x27051956 /* Image Magic Number */ -#define IH_NMLEN 28 /* Image Name Length */ - -struct uimage_header { - uint32_t ih_magic; /* Image Header Magic Number */ - uint32_t ih_hcrc; /* Image Header CRC Checksum */ - uint32_t ih_time; /* Image Creation Timestamp */ - uint32_t ih_size; /* Image Data Size */ - uint32_t ih_load; /* Data Load Address */ - uint32_t ih_ep; /* Entry Point Address */ - uint32_t ih_dcrc; /* Image Data CRC Checksum */ - uint8_t ih_os; /* Operating System */ - uint8_t ih_arch; /* CPU architecture */ - uint8_t ih_type; /* Image Type */ - uint8_t ih_comp; /* Compression Type */ - uint8_t ih_name[IH_NMLEN];/* Image Name */ - uint32_t ih_fsoff; /* Offset of the file system - partition from the start of - the header */ -}; - -/* - * Open the named file and return its size and file descriptor. - * Exit in case of errors. - */ -int -opensize(char *name, size_t *size) -{ - struct stat s; - int fd = open(name, O_RDONLY); - if (fd < 0) - err(1, "cannot open \"%s\"", name); - - if (fstat(fd, &s) == -1) - err(1, "cannot stat \"%s\"", name); - - *size = s.st_size; - return fd; -} - -static time_t source_date_epoch = -1; -static void set_source_date_epoch() { - char *env = getenv("SOURCE_DATE_EPOCH"); - char *endptr = env; - errno = 0; - if (env && *env) { - source_date_epoch = strtoull(env, &endptr, 10); - if (errno || (endptr && *endptr != '\0')) { - fprintf(stderr, "Invalid SOURCE_DATE_EPOCH"); - exit(1); - } - } -} - -/* - * Write the JCG header - */ -void -mkjcgheader(struct jcg_header *h, size_t psize, char *version) -{ - uLong crc; - uint16_t major = 0, minor = 0; - void *payload = (void *)h + sizeof(*h); - time_t t; - - if (source_date_epoch != -1) - t = source_date_epoch; - else if ((time(&t) == (time_t)(-1))) - err(1, "time call failed"); - - - if (version != NULL) - if (sscanf(version, "%hu.%hu", &major, &minor) != 2) - err(1, "cannot parse version \"%s\"", version); - - memset(h, 0, sizeof(*h)); - h->jh_magic = htonl(JH_MAGIC); - h->jh_type = htonl(1); - h->jh_time = htonl(t); - h->jh_major = htons(major); - h->jh_minor = htons(minor); - - /* CRC over JCG payload (uImage) */ - crc = crc32(0L, Z_NULL, 0); - crc = crc32(crc, payload, psize); - h->jh_dcrc = htonl(crc); - - /* CRC over JCG header */ - crc = crc32(0L, Z_NULL, 0); - crc = crc32(crc, (void *)h, sizeof(*h)); - h->jh_hcrc = htonl(crc); -} - -/* - * Write the uImage header - */ -void -mkuheader(struct uimage_header *h, size_t ksize, size_t fsize) -{ - uLong crc; - void *payload = (void *)h + sizeof(*h); - - // printf("mkuheader: %p, %zd, %zd\n", h, ksize, fsize); - memset(h, 0, sizeof(*h)); - h->ih_magic = htonl(IH_MAGIC); - h->ih_time = htonl(time(NULL)); - h->ih_size = htonl(ksize + fsize); - h->ih_load = htonl(0x80000000); - h->ih_ep = htonl(0x80292000); - h->ih_os = 0x05; - h->ih_arch = 0x05; - h->ih_type = 0x02; - h->ih_comp = 0x03; - h->ih_fsoff = htonl(sizeof(*h) + ksize); - strcpy((char *)h->ih_name, "Linux Kernel Image"); - - /* CRC over uImage payload (kernel and file system) */ - crc = crc32(0L, Z_NULL, 0); - crc = crc32(crc, payload, ntohl(h->ih_size)); - h->ih_dcrc = htonl(crc); - printf("CRC1: %08lx\n", crc); - - /* CRC over uImage header */ - crc = crc32(0L, Z_NULL, 0); - crc = crc32(crc, (void *)h, sizeof(*h)); - h->ih_hcrc = htonl(crc); - printf("CRC2: %08lx\n", crc); -} - -/* - * Calculate a "patch" value and write it into the last four bytes of - * buf, so that the CRC32 checksum of the whole buffer is dcrc. - * - * Based on: SAR-PR-2006-05: Reversing CRC – Theory and Practice. - * Martin Stigge, Henryk Plötz, Wolf Müller, Jens-Peter Redlich. - * http://sar.informatik.hu-berlin.de/research/publications/#SAR-PR-2006-05 - */ -void -craftcrc(uint32_t dcrc, uint8_t *buf, size_t len) -{ - int i; - uint32_t a; - uint32_t patch = 0; - uint32_t crc = crc32(0L, Z_NULL, 0); - - a = ~dcrc; - for (i = 0; i < 32; i++) { - if (patch & 1) - patch = (patch >> 1) ^ 0xedb88320L; - else - patch >>= 1; - - if (a & 1) - patch ^= 0x5b358fd3L; - - a >>= 1; - } - patch ^= ~crc32(crc, buf, len - 4); - for (i = 0; i < 4; i++) { - buf[len - 4 + i] = patch & 0xff; - patch >>= 8; - } - /* Verify that we actually get the desired result */ - crc = crc32(0L, Z_NULL, 0); - crc = crc32(crc, buf, len); - if (crc != dcrc) - errx(1, "CRC patching is broken: wanted %08x, but got %08x.", - dcrc, crc); - -} - -void -usage() { - fprintf(stderr, "Usage:\n" - "jcgimage -o outfile -u uImage [-m maxsize] [-v version]\n" - "jcgimage -o outfile -k kernel -f rootfs [-m maxsize] [-v version]\n"); - exit(1); -} - -#define MODE_UNKNOWN 0 -#define MODE_UIMAGE 1 -#define MODE_KR 2 - -/* The output image must not be larger than 4MiB - 5*64kiB */ -#define MAXSIZE (size_t)(4 * 1024 * 1024 - 5 * 64 * 1024) - -int -main(int argc, char **argv) -{ - struct jcg_header *jh; - struct uimage_header *uh; - int c; - char *imagefile = NULL; - char *file1 = NULL; - char *file2 = NULL; - char *version = NULL; - size_t maxsize = MAXSIZE; - char *endptr; - int mode = MODE_UNKNOWN; - int fdo, fd1, fd2; - size_t size1, size2, sizeu, sizeo, off1, off2; - void *map; - - /* Make sure the headers have the right size */ - assert(sizeof(struct jcg_header) == 512); - assert(sizeof(struct uimage_header) == 64); - set_source_date_epoch(); - - while ((c = getopt(argc, argv, "o:k:f:u:v:m:h")) != -1) { - switch (c) { - case 'o': - imagefile = optarg; - break; - case 'k': - if (mode == MODE_UIMAGE) - errx(1,"-k cannot be combined with -u"); - - mode = MODE_KR; - file1 = optarg; - break; - case 'f': - if (mode == MODE_UIMAGE) - errx(1,"-f cannot be combined with -u"); - - mode = MODE_KR; - file2 = optarg; - break; - case 'u': - if (mode == MODE_KR) - errx(1,"-u cannot be combined with -k and -r"); - - mode = MODE_UIMAGE; - file1 = optarg; - break; - case 'm': - if (optarg != NULL) - maxsize = strtoimax(optarg, &endptr, 10); - - break; - case 'v': - version = optarg; - break; - case 'h': - default: - usage(); - } - } - if (optind != argc) - errx(1, "illegal arg \"%s\"", argv[optind]); - - if (imagefile == NULL) - errx(1, "no output file specified"); - - if (mode == MODE_UNKNOWN) - errx(1, "specify either -u or -k and -r"); - - if (mode == MODE_KR) { - if (file1 == NULL || file2 == NULL) - errx(1, "need -k and -r"); - - fd2 = opensize(file2, &size2); - } - fd1 = opensize(file1, &size1); - if (mode == MODE_UIMAGE) { - off1 = sizeof(*jh); - sizeu = size1 + 4; - sizeo = sizeof(*jh) + sizeu; - } else { - off1 = sizeof(*jh) + sizeof(*uh); - off2 = sizeof(*jh) + sizeof(*uh) + size1; - sizeu = sizeof(*uh) + size1 + size2; - sizeo = sizeof(*jh) + sizeu; - } - - if (sizeo > maxsize) - errx(1, "payload too large: %zd > %zd\n", sizeo, maxsize); - - - fdo = open(imagefile, O_RDWR | O_CREAT | O_TRUNC, 00644); - if (fdo < 0) - err(1, "cannot open \"%s\"", imagefile); - - - if (ftruncate(fdo, sizeo) == -1) - err(1, "cannot grow \"%s\" to %zd bytes", imagefile, sizeo); - - map = mmap(NULL, sizeo, PROT_READ|PROT_WRITE, MAP_SHARED, fdo, 0); - uh = map + sizeof(*jh); - if (map == MAP_FAILED) - err(1, "cannot mmap \"%s\"", imagefile); - - - if (read(fd1, map + off1, size1) != size1) - err(1, "cannot copy %s", file1); - - - if (mode == MODE_KR) { - if (read(fd2, map+off2, size2) != size2) - err(1, "cannot copy %s", file2); - - mkuheader(uh, size1, size2); - } else if (mode == MODE_UIMAGE) - craftcrc(ntohl(uh->ih_dcrc), (void*)uh + sizeof(*uh), - sizeu - sizeof(*uh)); - - mkjcgheader(map, sizeu, version); - munmap(map, sizeo); - close(fdo); - return 0; -} |