mktplinkfw2: add support to pack bootloader
authorAlexander Couzens <lynxis@fe80.eu>
Wed, 25 Apr 2018 09:42:43 +0000 (11:42 +0200)
committerAlexander Couzens <lynxis@fe80.eu>
Tue, 19 Sep 2023 18:01:06 +0000 (20:01 +0200)
The 841v13 and 841v14 can not flash any image without bootloader over
the WebUI due a bug in the oem firmware upgrade software.

Allow mktplinkfw2 to integrate bootloaders.
An image with the bootloader looks:

|------------|
|image header|
|------------|
|bootloader  |
|------------|
|optional pad|
|------------|
|image header|
|------------|
|kernel image|
|------------|
|rootfs image|
|------------|

The padding depends on the bootloader size. The 2nd image header must
start on a 64k boundary.

Signed-off-by: Alexander Couzens <lynxis@fe80.eu>
src/mktplinkfw2.c

index d51e9ca00d6ac56a6463df581a6105464cc41699..e5981ef6fccdaf4047dba968cb9163b09d37b809 100644 (file)
@@ -100,7 +100,7 @@ uint32_t kernel_len = 0;
 struct file_info rootfs_info;
 uint32_t rootfs_ofs = 0;
 uint32_t rootfs_align;
-static struct file_info boot_info;
+static struct file_info boot_info = { 0 };
 int combined;
 int strip_padding;
 int add_jffs2_eof;
@@ -196,6 +196,7 @@ static void usage(int status)
 "  -F <id>         use flash layout specified with <id>\n"
 "  -k <file>       read kernel image from the file <file>\n"
 "  -r <file>       read rootfs image from the file <file>\n"
+"  -b <file>       read bootloader image from the file <file>\n"
 "  -a <align>      align the rootfs start on an <align> bytes boundary\n"
 "  -R <offset>     overwrite rootfs offset with <offset> (hexval prefixed with 0x)\n"
 "  -o <file>       write output to the file <file>\n"
@@ -264,11 +265,23 @@ static int check_options(void)
        if (!rootfs_ofs)
                rootfs_ofs = layout->rootfs_ofs;
 
+       /* check bootloader */
+       ret = get_file_stat(&boot_info);
+       if (ret) {
+               ERR("Can not load bootloader image.");
+               return ret;
+       }
+
+       if (boot_info.file_size > 2 * 64 * 1024) {
+               /* the offset in fill_header is hardcoded to 128k */
+               ERR("Bootloader image is bigger than 128k.");
+               return -1;
+       }
+
        if (kernel_info.file_name == NULL) {
                ERR("no kernel image specified");
                return -1;
        }
-
        ret = get_file_stat(&kernel_info);
        if (ret)
                return ret;
@@ -338,10 +351,20 @@ static int check_options(void)
        return 0;
 }
 
-void fill_header(char *buf, int len)
+void fill_header_bootloader(char *buf, int len, int with_bootloader)
 {
        struct fw_header *hdr = (struct fw_header *)buf;
        unsigned ver_len;
+       unsigned int offset = 0;
+
+       if (with_bootloader) {
+               /* ensure the bootloader is padded to 64k */
+               offset = boot_info.file_size & (64 * 1024);
+               if (offset < boot_info.file_size)
+                       offset += 64 * 1024;
+
+               offset += sizeof(struct fw_header);
+       }
 
        memset(hdr, '\xff', sizeof(struct fw_header));
 
@@ -357,29 +380,29 @@ void fill_header(char *buf, int len)
        hdr->hw_rev = htonl(board->hw_rev);
        hdr->hw_ver_add = htonl(board->hw_ver_add);
 
-       if (boot_info.file_size == 0) {
+       if (boot_info.file_size == 0 || !with_bootloader) {
                memcpy(hdr->md5sum1, md5salt_normal, sizeof(hdr->md5sum1));
                hdr->boot_ofs = htonl(0);
                hdr->boot_len = htonl(0);
        } else {
                memcpy(hdr->md5sum1, md5salt_boot, sizeof(hdr->md5sum1));
-               hdr->boot_ofs = htonl(rootfs_ofs + rootfs_info.file_size);
-               hdr->boot_len = htonl(rootfs_info.file_size);
+               hdr->boot_ofs = htonl(0);
+               hdr->boot_len = htonl(boot_info.file_size);
        }
 
        hdr->kernel_la = htonl(kernel_la);
        hdr->kernel_ep = htonl(kernel_ep);
-       hdr->fw_length = htonl(layout->fw_max_len);
-       hdr->kernel_ofs = htonl(sizeof(struct fw_header));
+       hdr->fw_length = htonl(layout->fw_max_len + offset);
+       hdr->kernel_ofs = htonl(sizeof(struct fw_header) + offset);
        hdr->kernel_len = htonl(kernel_len);
        if (!combined) {
+               /* even the bootloader doesnt increased the root_ofs. Unsure if this parser
+                * always ignores the rootfs or only in the 841v13
+                */
                hdr->rootfs_ofs = htonl(rootfs_ofs);
                hdr->rootfs_len = htonl(rootfs_info.file_size);
        }
 
-       hdr->boot_ofs = htonl(0);
-       hdr->boot_len = htonl(boot_info.file_size);
-
        hdr->unk2 = htonl(0);
        hdr->unk3 = htonl(0xffffffff);
        hdr->unk4 = htons(0x55aa);
@@ -400,6 +423,11 @@ void fill_header(char *buf, int len)
        get_md5(buf, len, hdr->md5sum1);
 }
 
+/* fill_header get called by mktplinkfw_lib to fill the header in front of the kernel. */
+void fill_header(char *buf, int len) {
+       fill_header_bootloader(buf, len, 0);
+}
+
 static int inspect_fw(void)
 {
        char *buf;
@@ -561,6 +589,75 @@ static int inspect_fw(void)
        return ret;
 }
 
+/* prepend a second image header and the bootloader.
+ *
+ * |------------|
+ * |image header|
+ * |------------|
+ * |bootloader  |
+ * |------------|
+ * |optional pad|
+ * |------------|
+ * |image header|
+ * |------------|
+ * |kernel image|
+ * |------------|
+ * |rootfs image|
+ * |------------|
+ *
+ * The padding is optional. The second image header should begin a 64k boundary.
+ */
+int prepend_bootloader() {
+       unsigned int buflen = 0;
+       int ret = 0, bootloader_padded = 0;
+       char *buf = 0, *p = 0;
+       struct file_info image;
+
+       /* calculate blocks to ensure padding */
+       bootloader_padded = boot_info.file_size & (64 * 1024);
+       if (bootloader_padded < boot_info.file_size)
+               bootloader_padded += 64 * 1024;
+
+       image.file_name = ofname;
+       ret = get_file_stat(&image);
+       if (ret) {
+               ERR("Can not load the output image");
+               return ret;
+       }
+
+       buflen = image.file_size + bootloader_padded + sizeof(struct fw_header);
+       buf = malloc(buflen);
+       if (buf == NULL) {
+               ERR("Can not allocate buffer %d bytes", buflen);
+               return -1;
+       }
+       memset(buf, 0xff, buflen);
+
+       /* load old image */
+       p = buf + bootloader_padded + sizeof(struct fw_header);
+       ret = read_to_buf(&image, p);
+       if (ret) {
+               ERR("Can not read output image");
+               goto out_free_buf;
+       }
+
+       p = buf + sizeof(struct fw_header);
+       ret = read_to_buf(&boot_info, p);
+
+       fill_header_bootloader(buf, buflen, 1);
+
+       ret = write_fw(ofname, buf, buflen);
+       if (ret)
+               goto out_free_buf;
+
+       ret = EXIT_SUCCESS;
+
+out_free_buf:
+       free(buf);
+
+       return ret;
+}
+
 int main(int argc, char *argv[])
 {
        int ret = EXIT_FAILURE;
@@ -570,7 +667,7 @@ int main(int argc, char *argv[])
        while ( 1 ) {
                int c;
 
-               c = getopt(argc, argv, "a:H:E:F:L:V:N:W:w:ci:k:r:R:o:xhsjv:y:T:e");
+               c = getopt(argc, argv, "a:b:H:E:F:L:V:N:W:w:ci:k:r:R:o:xhsjv:y:T:e");
                if (c == -1)
                        break;
 
@@ -578,6 +675,9 @@ int main(int argc, char *argv[])
                case 'a':
                        sscanf(optarg, "0x%x", &rootfs_align);
                        break;
+               case 'b':
+                       boot_info.file_name = optarg;
+                       break;
                case 'H':
                        opt_hw_id = optarg;
                        break;
@@ -654,12 +754,14 @@ int main(int argc, char *argv[])
        if (ret)
                goto out;
 
-       if (!inspect_info.file_name)
+       if (!inspect_info.file_name) {
                ret = build_fw(sizeof(struct fw_header));
+               if (ret == 0 && boot_info.file_size > 0)
+                       ret = prepend_bootloader();
+       }
        else
                ret = inspect_fw();
 
  out:
        return ret;
 }
-