[firmware-utils] asus_qca_fix_checksum: new tool for ASUS QCA/QCN uImage
webmaster at playmp3.kr
webmaster at playmp3.kr
Mon Nov 29 02:56:09 PST 2021
From: daebo01 <daebo01 at playmp3.kr>
Fix checksum of Asus QCA/QCN devices.
Tested on ac59u v1
Signed-off-by: daebo01 <daebo01 at playmp3.kr>
---
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 0000000..8bb4420
--- /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 daebo01 <daebo01 at playmp3.kr>
+ *
+ * Based on:
+ * uimage_padhdr.c : add zero paddings after the tail of uimage header
+ * Copyright (C) 2019 NOGUCHI Hiroshi <drvlabo at gmail.com>
+ *
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <arpa/inet.h>
+#include <zlib.h>
+
+/* 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(&tail->productid, &header->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 <input_uimage_file> -o <output_file>\n", prog);
+ fprintf(stderr, " -v <asuswrt version (ex. 3.0.0.4.382.52482)>");
+}
+
+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",
+ &tail.kernel.major, &tail.kernel.minor,
+ &tail.fs.major, &tail.fs.minor,
+ &tail.sn, &tail.en))
+ fprintf(stderr, "Version %s doesn't match suppored 6-digits format\n", version);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (!infname || !outfname || !version)
+ {
+ usage(argv[0]);
+ exit(1);
+ }
+
+ ifd = open(infname, O_RDONLY);
+ if (ifd < 0)
+ {
+ fprintf(stderr,
+ "could not open input file. (errno = %d)\n", errno);
+ exit(1);
+ }
+
+ ofd = open(outfname, O_WRONLY | O_CREAT, 0644);
+ if (ofd < 0)
+ {
+ fprintf(stderr,
+ "could not open output file. (errno = %d)\n", errno);
+ exit(1);
+ }
+
+ if (fstat(ifd, &statbuf) < 0)
+ {
+ fprintf(stderr,
+ "could not fstat input file. (errno = %d)\n", errno);
+ exit(1);
+ }
+
+ filebuf = malloc(statbuf.st_size);
+ if (!filebuf)
+ {
+ fprintf(stderr, "buffer allocation failed\n");
+ exit(1);
+ }
+
+ rsz = read(ifd, filebuf, statbuf.st_size);
+ if (rsz != statbuf.st_size)
+ {
+ fprintf(stderr,
+ "could not read input file (errno = %d).\n", errno);
+ exit(1);
+ }
+
+ fix_checksum(filebuf, statbuf.st_size, &tail);
+
+ rsz = write(ofd, filebuf, statbuf.st_size);
+ if (rsz != statbuf.st_size)
+ {
+ fprintf(stderr,
+ "could not write output file (errnor = %d).\n", errno);
+ exit(1);
+ }
+
+ return 0;
+}
--
2.20.1
More information about the openwrt-devel
mailing list