[firmware-utils v2] asus_qca_fix_checksum: new tool for ASUS QCA/QCN uImage

2021-11-29 Thread daebo01
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

2021-11-29 Thread daebo01
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",
+, 
,
+, ,
+