OpenWrt support for Deco X20

ah ha! Finally got sysupgrade working via UART! https://github.com/dmascord/openwrt/tree/add_tplink_deco_x20

The second uboot looks for this tag_kernel, but luckily ignores the CRC. It only uses the fsLen (filesystem length) to know how big the filesystem is to search for the 3rd uboot and vmlinuz...

Had to build the uboot from the gpl archive, flash it to the second uboot, and hope it didn't brick it, luckily it booted with the extra debug I needed!

Patch needed for firmware-utils:

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6e356fc..4ef57da 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -83,6 +83,7 @@ FW_UTIL(mkrasimage "" --std=gnu99 "")
 FW_UTIL(mkrtn56uimg "" "" "${ZLIB_LIBRARIES}")
 FW_UTIL(mksenaofw src/md5.c --std=gnu99 "")
 FW_UTIL(mksercommfw "" "" "")
+FW_UTIL(mktag_kernel "" "" "")
 FW_UTIL(mktitanimg "" "" "")
 FW_UTIL(mktplinkfw "src/mktplinkfw-lib.c;src/md5.c" -fgnu89-inline "")
 FW_UTIL(mktplinkfw2 "src/mktplinkfw-lib.c;src/md5.c" -fgnu89-inline "")
diff --git a/src/mktag_kernel.c b/src/mktag_kernel.c
new file mode 100644
index 0000000..4d8b860
--- /dev/null
+++ b/src/mktag_kernel.c
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * mktag_kernel.c - utility to write the tag_kernel file for TP Link Deco X20 jffs2 filesystem lookup
+ *
+ * Copyright (C) 2024 Damien Mascord <tusker@tusker.org>
+ */
+
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+int fpread(void *buffer, size_t size, size_t nitems, size_t offset, FILE *fp);
+
+typedef struct __attribute__((scalar_storage_order("little-endian"))) _LINUX_FILE_TAG
+{
+       int32_t rootfsLen;
+       int32_t binCrc32;
+       int32_t reserved[126];
+}LINUX_FILE_TAG;
+
+void usage(void) __attribute__ (( __noreturn__ ));
+
+void usage(void)
+{
+       fprintf(stderr, "Usage: mktag_kernel [-s <rootfsLen>] [-o <outputfile>] [-t (test mode)]\n");
+       exit(EXIT_FAILURE);
+}
+
+int main(int argc, char **argv)
+{
+       char buf[1024]; /* keep this at 1k or adjust garbage calc below */
+       FILE *in = stdin, *out = stdout;
+       char *ofn = NULL, *rootfsLen = NULL;
+       size_t n;
+       int c, first_block = 1;
+       unsigned char tag_buf[sizeof(LINUX_FILE_TAG)];
+       LINUX_FILE_TAG * pKern_tag = (LINUX_FILE_TAG *)tag_buf;
+       int testMode = 0;
+
+       while ((c = getopt(argc, argv, "s:o:h:t")) != -1) {
+               switch (c) {
+                       case 's':
+                               rootfsLen = optarg;
+                               break;
+                       case 'o':
+                               ofn = optarg;
+                               break;
+                       case 't':
+                               testMode = 1;
+                               break;
+                       case 'h':
+                       default:
+                               usage();
+               }
+       }
+       
+       if (ofn && !(out = fopen(ofn, "wb"))) {
+               fprintf(stderr, "can not open \"%s\" for writing\n", ofn);
+               usage();
+       }
+       
+       memset(tag_buf, 0, sizeof(LINUX_FILE_TAG));
+       
+       if (!rootfsLen)
+       {
+               usage();
+       }
+       
+       pKern_tag->rootfsLen = strtol(rootfsLen, NULL, 10);
+       pKern_tag->binCrc32 = 0;
+       
+       if (testMode)
+       {
+               for (int xx = 0; xx < sizeof(LINUX_FILE_TAG); xx++)
+               {
+                        printf("%.2X", ((unsigned char *)pKern_tag)[xx]);
+               }
+               printf("\n");
+       }
+       
+       int flag = 0;
+    flag = fwrite(pKern_tag, sizeof(LINUX_FILE_TAG), 1, out);
+    fflush(out);
+    fclose(out);
+    
+    if (testMode)
+       {
+               if (ofn && !(out = fopen(ofn, "rb"))) {
+                       fprintf(stderr, "can not open \"%s\" for reading\n", ofn);
+                       usage();
+               }
+               //clear it out
+               memset(tag_buf, 0, sizeof(LINUX_FILE_TAG));
+               fpread(tag_buf, 1, sizeof(LINUX_FILE_TAG), 0, out);
+       }
+       
+}
+
+
+int fpread(void *buffer, size_t size, size_t nitems, size_t offset, FILE *fp)
+{
+        printf("fpread %d %d %d \n", size, nitems, offset);
+     int seekPosition = fseek(fp, offset, SEEK_SET);
+     printf("seekPosition: %d\n", seekPosition);
+     if(seekPosition != 0)
+     {
+                printf("unable to seek to %d\n", offset);
+         return 0;
+        }
+     int returnValue = fread(buffer, size, nitems, fp);
+     printf("returnValue %d:\n",returnValue);
+     for (int i = 0; i < nitems; i++)
+     {
+                printf("%.2X", ((unsigned char *)buffer)[i]);
+        }
+        printf(".\n");
+     return returnValue;
+}
+
2 Likes

And now, courtesy of naf, we now have a method to install without opening up the device. Please follow the commit message instructions to test it out.

You might need to solder on the UART in case something fails. I have tested with UART attached, and also fresh devices without UART.

1.0.7 firmware needs to be upgraded to 1.1.8 for the naf bind shell to work.

Let me know how you go.

LEDs mapped in DTS now, so should be fully supported now, so would be good to have a few test installs on other devices before I submit for PR

1 Like

How can I install it on my device and try it out.

You can check out the GitHub repo linked above, and follow the usual OpenWRT build instructions, if that's an issue let me know.

Otherwise you can download from https://1drv.ms/f/s!AsfW32P1XQXagaRzSVeC_ovaFXKqvw?e=DiwIBP

Freshly built with a new checkout (on a different machine from my dev machine)

Can confirm x20 v3 openwrt works via uart install. Using your provided download link.
Have not been able to install via webui, my x20 is stuck on 1.1.7, and is unable to update to 1.1.8 (or any other version) for some reason. I have been able to get a telnet session, tftp'ed the *squashfs-jffs2.bin and write to md3, but the device keeps rebooting to stock firmware (1.1.7)

Hmmm... Looks like it is reverting back to the root1 to boot because of a failure. I think 1.1.7 should be fine if you can get a telnet shell.

I think the problem must be the MTD command must also wipe /dev/mtd3 (ie, add -e /dev/mtd3), so I will update the installation instructions.

On the device that did revert to booting on the second root partition, I overwrote both the partitions (first did mtd3, and didn't use the -r (reboot) in the command, and second wrote over the second root partition and did include the -r) and it booted fine into OpenWRT.

Can confirm that writing to both partitions does indeed boot to openwrt. I now only have one device left that is untainted :). Would writing to both partitions reduce the chance of restoring the original firmware, if ever needed?

If you have anything you want me try with -e /dev/mtd3. Let me know

It has http recovery on uboot, so you can always flash the factory firmware.

You can probably force it back to booting to factory on root0, by flashing the factory firmware on to root1 via uboot http recovery, then dd copy root1 to root0 and wipe root1. It will fail to boot root1 and then boot to factory firmware on root0.

Then start from scratch and try with -e /dev/mtd3

So I'm entering these commands after I succesfully connected to the shell:

tftp -l /tmp/jffs -r openwrt-ramips-mt7621-tplink_deco-x20-v3-squashfs-jffs2.bin -g 192.168.1.76
/usr/sbin/fw_setenv tp_boot_idx 0
mtd -e /dev/mtd3 -r write /tmp/jffs /dev/mtd3;

After a while it says:

Unlocking /dev/mtd3 ...
Erasing /dev/mtd3 ...

Writing from /tmp/jffs to /dev/mtd3 ...     
Rebooting ...

After a few minutes when the light is green again it comes back with stock firmware.
Is that maybe caused by the fact the I'm trying this procedure on a Deco X20 (DFN) Ver 3.0 (DutchFiberNetwork) instead of the regular version? Do you think I have more luck flashing it with the UART method?