build: add mkqdimg
authorCoia Prant <coiaprant@gmail.com>
Sun, 5 Jan 2025 09:16:54 +0000 (17:16 +0800)
committerHauke Mehrtens <hauke@hauke-m.de>
Sat, 2 Aug 2025 22:54:46 +0000 (00:54 +0200)
The mkqdimg is used for Qding QC202 device.

This tool is a reverse engineering of /lib/upgrade/platform.sh
and deqdimg programs that come with the original firmware.

It has been tested on Qding QC202 U-Boot WebUI.

The mkqdimg is also compatible with other qding devices.

Signed-off-by: Coia Prant <coiaprant@gmail.com>
Tested-by: Coia Prant <coiaprant@gmail.com>
Link: https://github.com/openwrt/firmware-utils/pull/38
[style fixes and init some vars]
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
CMakeLists.txt
src/mkqdimg.c [new file with mode: 0644]

index 71c604cf8ee530a8e28f5fb1ee0a9b7562b9a30b..eed563f163880bd93781763b820a0357de0d1f7d 100644 (file)
@@ -79,6 +79,7 @@ FW_UTIL(mkmerakifw-old "" "" "")
 FW_UTIL(mkmylofw "" "" "")
 FW_UTIL(mkplanexfw src/sha1.c "" "")
 FW_UTIL(mkporayfw "" "" "")
+FW_UTIL(mkqdimg src/sha1.c "" "")
 FW_UTIL(mkrasimage "" --std=gnu99 "")
 FW_UTIL(mkrtn56uimg "" "" "${ZLIB_LIBRARIES}")
 FW_UTIL(mksenaofw src/md5.c --std=gnu99 "")
diff --git a/src/mkqdimg.c b/src/mkqdimg.c
new file mode 100644 (file)
index 0000000..46ecb72
--- /dev/null
@@ -0,0 +1,269 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2025 Coia Prant <coiaprant@gmail.com>
+ *
+ * The golang version can be found at:
+ * <https://gitlab.com/CoiaPrant/mkqdimg>
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <libgen.h>
+#include <getopt.h>
+#include <errno.h>
+#include <endian.h>
+
+#include "sha1.h"
+
+#define HDR_PADDING_BYTE       0x00
+#define PADDING_BYTE           0xff
+
+#define MAX_LENGTH             16647168
+#define BOARD_ID_LENGTH                8
+#define VERSION_LENGTH         8
+#define UBOOT_LENGTH           196608
+
+#define HDR_LENGTH             0x00000400
+#define HDR_OFF_BOARD_ID       0
+#define HDR_OFF_VERSION                8
+#define HDR_OFF_UBOOT          16
+#define HDR_OFF_FIRMWARE       32
+#define HDR_OFF_MAGIC          48
+#define HDR_OFF_CHECKSUM       52
+#define HDR_OFF_UBOOT_LEN      72
+#define HDR_OFF_FIRMWARE_LEN   76
+#define HDR_MAGIC              538248722
+
+/*
+ * Globals
+ */
+static char *progname;
+
+/*
+ * Message macros
+ */
+#define ERR(fmt, ...) do { \
+       fflush(0); \
+       fprintf(stderr, "[%s] *** error: " fmt "\n", \
+                       progname, ## __VA_ARGS__); \
+} while (0)
+
+#define ERRS(fmt, ...) do { \
+       int save = errno; \
+       fflush(0); \
+       fprintf(stderr, "[%s] *** error: " fmt "\n", \
+                       progname, ## __VA_ARGS__, strerror(save)); \
+} while (0)
+
+static void usage(int status)
+{
+       FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout;
+
+       fprintf(stream, "Usage: %s [OPTIONS...]\n", progname);
+       fprintf(stream,
+"\n"
+"Options:\n"
+"  -B <board>      create image for the board specified with <board>\n"
+"  -V <version>    version string\n"
+"  -u <file>       read uboot image from the file <file>\n"
+"  -f <file>       read firmware image from the file <file>\n"
+"  -o <file>       write output to the file <file>\n"
+"  -h              show this screen\n"
+       );
+
+       exit(status);
+}
+
+void writele(unsigned char *buf, size_t offset, uint32_t value)
+{
+       value = htole32(value);
+       memcpy(buf + offset, &value, sizeof(uint32_t));
+}
+
+int main(int argc, char *argv[])
+{
+       int ret = EXIT_FAILURE;
+       long ulen, flen, buflen = HDR_LENGTH, fspace;
+       unsigned char *buf;
+       char *board_id = NULL, *version = NULL, *ufname = NULL, *ffname = NULL, *ofname = NULL;
+       FILE *out, *uboot = NULL, *firmware = NULL;
+
+       progname = basename(argv[0]);
+
+       while (1) {
+               int c;
+
+               c = getopt(argc, argv, "B:V:u:f:o:h");
+               if (c == -1)
+                       break;
+
+               switch (c) {
+               case 'B':
+                       board_id = optarg;
+                       break;
+               case 'V':
+                       version = optarg;
+                       break;
+               case 'u':
+                       ufname = optarg;
+                       break;
+               case 'f':
+                       ffname = optarg;
+                       break;
+               case 'o':
+                       ofname = optarg;
+                       break;
+               case 'h':
+                       usage(EXIT_SUCCESS);
+                       break;
+               default:
+                       usage(EXIT_FAILURE);
+                       break;
+               }
+       }
+
+       if (board_id == NULL) {
+               ERR("no board specified");
+               goto err;
+       }
+
+       if (strlen(board_id) > BOARD_ID_LENGTH) {
+               ERR("board_id \"%s\" is too long - max length: 8\n",
+                   board_id);
+               goto err;
+       }
+
+       if (version != NULL && strlen(version) > VERSION_LENGTH) {
+               ERR("version \"%s\" is too long - max length: 8\n",
+                   version);
+               goto err;
+       }
+
+       if (ofname == NULL) {
+               ERR("no output file specified");
+               goto err;
+       }
+
+       if (ufname != NULL) {
+               uboot = fopen(ufname, "r");
+               if (uboot == NULL) {
+                       ERRS("could not open \"%s\" for reading: %s", ufname);
+                       goto err;
+               }
+
+               /* Get uboot length */
+               fseek(uboot, 0, SEEK_END);
+               ulen = ftell(uboot);
+               rewind(uboot);
+
+               if (ulen > UBOOT_LENGTH) {
+                       fclose(uboot);
+                       ERR("file \"%s\" is too big - max size: 0x%08d\n",
+                         ufname, UBOOT_LENGTH);
+                       goto err;
+               }
+
+               buflen += UBOOT_LENGTH;
+       }
+
+       if (ffname != NULL) {
+               firmware = fopen(ffname, "r");
+               if (firmware == NULL) {
+                       ERRS("could not open \"%s\" for reading: %s", ffname);
+                       goto err;
+               }
+
+               /* Get firmware length */
+               fseek(firmware, 0, SEEK_END);
+               flen = ftell(firmware);
+               rewind(firmware);
+
+               fspace = MAX_LENGTH - buflen;
+               if (flen > fspace) {
+                       ERR("file \"%s\" is too big - max size: 0x%08ld\n",
+                         ffname, fspace);
+                       goto err_close;
+               }
+
+               buflen += flen;
+       }
+
+       /* Allocate and initialize buffer for final image */
+       buf = malloc(buflen);
+       if (buf == NULL) {
+               ERRS("no memory for buffer: %s\n");
+               goto err_close;
+       }
+       memset(buf, HDR_PADDING_BYTE, HDR_LENGTH);
+       memset(buf + HDR_LENGTH, PADDING_BYTE, buflen - HDR_LENGTH);
+
+       /* Write board id */
+       memcpy(buf + HDR_OFF_BOARD_ID, board_id, strlen(board_id));
+
+       /* Write version */
+       if (version != NULL) {
+               memcpy(buf + HDR_OFF_VERSION, version, strlen(version));
+       }
+
+       if (uboot != NULL) {
+               /* Write UBOOT ID */
+               memcpy(buf + HDR_OFF_UBOOT, "UBOOT", 5);
+
+               /* Load U-Boot */
+               fread(buf + HDR_LENGTH, ulen, 1, uboot);
+
+               /* Write U-Boot Length */
+               writele(buf, HDR_OFF_UBOOT_LEN, UBOOT_LENGTH);
+       }
+
+       if (firmware != NULL) {
+               /* Write FIRMWARE ID */
+               memcpy(buf + HDR_OFF_FIRMWARE, "FIRMWARE", 8);
+
+               /* Load Firmware */
+               if (uboot != NULL) {
+                       fread(buf + HDR_LENGTH + UBOOT_LENGTH, flen, 1, firmware);
+               } else {
+                       fread(buf + HDR_LENGTH, flen, 1, firmware);
+               }
+
+               /* Write Firmware Length */
+               writele(buf, HDR_OFF_FIRMWARE_LEN, flen);
+       }
+
+       /* Write magic */
+       writele(buf, HDR_OFF_MAGIC, HDR_MAGIC);
+
+       /* Write checksum and static hash */
+       sha1_csum(buf + HDR_LENGTH, buflen - HDR_LENGTH, buf + HDR_OFF_CHECKSUM);
+
+       /* Save finished image */
+       out = fopen(ofname, "w");
+       if (out == NULL) {
+               ERRS("could not open \"%s\" for writing: %s", ofname);
+               goto err_free;
+       }
+       fwrite(buf, buflen, 1, out);
+
+       ret = EXIT_SUCCESS;
+
+       fclose(out);
+
+err_free:
+       free(buf);
+
+err_close:
+       if (uboot != NULL) {
+               fclose(uboot);
+       }
+
+       if (firmware != NULL) {
+               fclose(firmware);
+       }
+
+err:
+       return ret;
+}