diff options
| author | Coia Prant | 2025-01-05 09:16:54 +0000 |
|---|---|---|
| committer | Hauke Mehrtens | 2025-08-02 22:54:46 +0000 |
| commit | 3346d7711c9aac561beefa2879db9e0b071ccdca (patch) | |
| tree | bb0e8227a0e19096f2c66a3e6c84c43dc2b15873 | |
| parent | f29de74ecd7d952ee1f9a20577fec9f37c0d6f4a (diff) | |
| download | firmware-utils-3346d7711c9aac561beefa2879db9e0b071ccdca.tar.gz | |
build: add mkqdimg
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>
| -rw-r--r-- | CMakeLists.txt | 1 | ||||
| -rw-r--r-- | src/mkqdimg.c | 269 |
2 files changed, 270 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 71c604c..eed563f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 index 0000000..46ecb72 --- /dev/null +++ b/src/mkqdimg.c @@ -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; +} |