[firmware-utils v2] asus_qca_fix_checksum: new tool for ASUS QCA/QCN uImage
From: Alisha Kim Fix checksum of Asus QCA/QCN devices. Tested on ac59u v1 Signed-off-by: Alisha Kim --- CMakeLists.txt | 1 + src/asus_qca_fix_checksum.c | 205 2 files changed, 206 insertions(+) create mode 100644 src/asus_qca_fix_checksum.c diff --git a/CMakeLists.txt b/CMakeLists.txt index f406520..f551b88 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,7 @@ ENDMACRO(FW_UTIL) FW_UTIL(add_header "" "" "") FW_UTIL(addpattern "" "" "") +FW_UTIL(asus_qca_fix_checksum "" "" "${ZLIB_LIBRARIES}") FW_UTIL(asustrx "" "" "") FW_UTIL(bcm4908asus "" "" "") FW_UTIL(bcm4908kernel "" "" "") diff --git a/src/asus_qca_fix_checksum.c b/src/asus_qca_fix_checksum.c new file mode 100644 index 000..511f101 --- /dev/null +++ b/src/asus_qca_fix_checksum.c @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * asus_qca_fix_checksum.c : checksum fix for ASUS QCA/QCN SoC uImage + * + * Copyright (C) 2021 Alisha Kim + * + * Based on: + * uimage_padhdr.c : add zero paddings after the tail of uimage header + * Copyright (C) 2019 NOGUCHI Hiroshi + * + */ + +#include +#include +#include +#include +#include +#include +#include + +/* from asuswrt opensource */ +#define MAX_STRING 12 +#define MAX_VER 5 + +typedef struct +{ + uint8_t major; + uint8_t minor; +} version_t; + +/* + * ASUS QCA/QCN Custom Header + */ +typedef struct +{ + version_t kernel; + version_t fs; + char productid[MAX_STRING]; + uint16_t sn; + uint16_t en; + uint8_t pkey; + uint8_t key; + version_t hw[MAX_VER]; +} TAIL; + +/* from u-boot/include/image.h */ +#define IH_MAGIC 0x27051956 /* Image Magic Number */ +#define IH_NMLEN 32/* Image Name Length*/ + +/* + * Legacy format image header, + * all data in network byte order (aka natural aka bigendian). + */ +typedef struct image_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 */ + union + { + uint8_t ih_name[IH_NMLEN]; /* Image Name */ + TAIL tail; /* Asuswrt Custom Tail */ + } u; +} image_header_t; + +void fix_checksum(uint8_t *image, off_t image_len, TAIL *tail) +{ + image_header_t *header = (image_header_t *)image; + + uint32_t checksum_a_offset = 0; // image first byte + uint32_t checksum_b_offset = (ntohl(header->ih_size) + sizeof(image_header_t)) >> 1; + + uint8_t checksum_a = image[checksum_a_offset]; + uint8_t checksum_b; + + uint32_t recalc_crc; + + if (image_len < checksum_b_offset) + { + fprintf(stderr, "too small uImage size\n"); + exit(1); + } + + checksum_b = image[checksum_b_offset]; + + tail->key = checksum_a + ~checksum_b; + + // copy an existing image name + memcpy(>productid, >u.ih_name, sizeof(tail->productid) - 1); + + // overwrite asus custom header to image name field + header->u.tail = *tail; + + header->ih_hcrc = 0; + recalc_crc = crc32(0, image, sizeof(image_header_t)); + header->ih_hcrc = htonl(recalc_crc); +} + +void usage(char *prog) +{ + fprintf(stderr, "%s -i -o \n", prog); + fprintf(stderr, " -v "); +} + +int main(int argc, char *argv[]) +{ + struct stat statbuf; + uint8_t *filebuf; + int ifd; + int ofd; + ssize_t rsz; + int opt; + char *infname = NULL; + char *outfname = NULL; + char *version = NULL; + TAIL tail = {}; + + while ((opt = getopt(argc, argv, "i:o:v:")) != -1) + { + switch (opt) + { + case 'i': + infname = optarg; + break; + case 'o': + outfname = optarg; + break; + case 'v': + version = optarg; + if (6 != sscanf( +version, "%hhu.%hhu.%hhu.%hhu.%hu.%hu", +, , +, , +
[firmware-utils v2] asus_qca_fix_checksum: new tool for ASUS QCA/QCN uImage
From: daebo01 Fix checksum of Asus QCA/QCN devices. Tested on ac59u v1 Signed-off-by: Alisha Kim --- CMakeLists.txt | 1 + src/asus_qca_fix_checksum.c | 205 2 files changed, 206 insertions(+) create mode 100644 src/asus_qca_fix_checksum.c diff --git a/CMakeLists.txt b/CMakeLists.txt index f406520..f551b88 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,7 @@ ENDMACRO(FW_UTIL) FW_UTIL(add_header "" "" "") FW_UTIL(addpattern "" "" "") +FW_UTIL(asus_qca_fix_checksum "" "" "${ZLIB_LIBRARIES}") FW_UTIL(asustrx "" "" "") FW_UTIL(bcm4908asus "" "" "") FW_UTIL(bcm4908kernel "" "" "") diff --git a/src/asus_qca_fix_checksum.c b/src/asus_qca_fix_checksum.c new file mode 100644 index 000..511f101 --- /dev/null +++ b/src/asus_qca_fix_checksum.c @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * asus_qca_fix_checksum.c : checksum fix for ASUS QCA/QCN SoC uImage + * + * Copyright (C) 2021 Alisha Kim + * + * Based on: + * uimage_padhdr.c : add zero paddings after the tail of uimage header + * Copyright (C) 2019 NOGUCHI Hiroshi + * + */ + +#include +#include +#include +#include +#include +#include +#include + +/* from asuswrt opensource */ +#define MAX_STRING 12 +#define MAX_VER 5 + +typedef struct +{ + uint8_t major; + uint8_t minor; +} version_t; + +/* + * ASUS QCA/QCN Custom Header + */ +typedef struct +{ + version_t kernel; + version_t fs; + char productid[MAX_STRING]; + uint16_t sn; + uint16_t en; + uint8_t pkey; + uint8_t key; + version_t hw[MAX_VER]; +} TAIL; + +/* from u-boot/include/image.h */ +#define IH_MAGIC 0x27051956 /* Image Magic Number */ +#define IH_NMLEN 32/* Image Name Length*/ + +/* + * Legacy format image header, + * all data in network byte order (aka natural aka bigendian). + */ +typedef struct image_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 */ + union + { + uint8_t ih_name[IH_NMLEN]; /* Image Name */ + TAIL tail; /* Asuswrt Custom Tail */ + } u; +} image_header_t; + +void fix_checksum(uint8_t *image, off_t image_len, TAIL *tail) +{ + image_header_t *header = (image_header_t *)image; + + uint32_t checksum_a_offset = 0; // image first byte + uint32_t checksum_b_offset = (ntohl(header->ih_size) + sizeof(image_header_t)) >> 1; + + uint8_t checksum_a = image[checksum_a_offset]; + uint8_t checksum_b; + + uint32_t recalc_crc; + + if (image_len < checksum_b_offset) + { + fprintf(stderr, "too small uImage size\n"); + exit(1); + } + + checksum_b = image[checksum_b_offset]; + + tail->key = checksum_a + ~checksum_b; + + // copy an existing image name + memcpy(>productid, >u.ih_name, sizeof(tail->productid) - 1); + + // overwrite asus custom header to image name field + header->u.tail = *tail; + + header->ih_hcrc = 0; + recalc_crc = crc32(0, image, sizeof(image_header_t)); + header->ih_hcrc = htonl(recalc_crc); +} + +void usage(char *prog) +{ + fprintf(stderr, "%s -i -o \n", prog); + fprintf(stderr, " -v "); +} + +int main(int argc, char *argv[]) +{ + struct stat statbuf; + uint8_t *filebuf; + int ifd; + int ofd; + ssize_t rsz; + int opt; + char *infname = NULL; + char *outfname = NULL; + char *version = NULL; + TAIL tail = {}; + + while ((opt = getopt(argc, argv, "i:o:v:")) != -1) + { + switch (opt) + { + case 'i': + infname = optarg; + break; + case 'o': + outfname = optarg; + break; + case 'v': + version = optarg; + if (6 != sscanf( +version, "%hhu.%hhu.%hhu.%hhu.%hu.%hu", +, , +, , +