[openwrt/openwrt] generic: add mstc-boot mtdsplit parser

LEDE Commits lede-commits at lists.infradead.org
Sun Jul 20 07:14:04 PDT 2025


hauke pushed a commit to openwrt/openwrt.git, branch main:
https://git.openwrt.org/ddf7d63e946a65d510c4ec0338aea49dc45a976c

commit ddf7d63e946a65d510c4ec0338aea49dc45a976c
Author: INAGAKI Hiroshi <musashino.open at gmail.com>
AuthorDate: Mon Apr 28 18:44:29 2025 +0900

    generic: add mstc-boot mtdsplit parser
    
    Add new mtdsplit parser "mstc-boot" for the devices manufactured by MSTC
    (Mitra Star Technology Corp.). This is necessary to handle dual-boot on
    those devices.
    This parser splits kernel+rootfs or only rootfs(or UBI) based on the
    image in the firmware partition or pre-defined partitions in dts, and
    "bootnum" value in the "persist" (or "working") partition.
    
    Note: "bootnum" is used for switching active firmware partitions on the
          devices manufactured by MSTC and '1' or '2' are used on most
          devices. But some devices use '0' or '1'. (example: I-O DATA
          WN-DEAX1800GR)
    
    Sequence:
    
    1. obtain "bootnum" value
    2. child nodes exsist (regardless of bootnum)
       -> fixed partitions
          (active parts  : without bootnum (ex.: "kernel", "rootfs")
           inactive parts: with bootnum (ex.: "kernel2", "rootfs2"))
    3. current partition is active (dt bootnum == mtd bootnum)
       -> image-based partitions
    
    Device Tree:
    
    - common
    
      - mstc,bootnum  : "bootnum" value for the mtd partition (0/1/2)
      - mstc,persist  : phandle of "persist" partition containing "bootnum"
                        value
    
    - fixed partitions
    
      - #address-cells: indicate cell count of address of child nodes (1)
      - #size-cells   : indicate cell count of size of child nodes (1)
      - (child nodes) : define the child partitions
        - reg         : define the offset and size
        - label-base  : define the base name of the partition
          - (example) : base:"kernel"->"kernel"(active)/"kernel2"(inactive)
    
      example:
    
      partition at 3c0000 {
            compatible = "mstc,boot";
            reg = <0x3c0000 0x3240000>;
            label = "firmware1";
            mstc,bootnum = <1>;
            mstc,persist = <&mtd_persist>;
            #address-cells = <1>;
            #size-cells = <1>;
    
            partition at 0 {
                    reg = <0x0 0x800000>;
                    label-base = "kernel";
            };
    
            partition at 800000 {
                    reg = <0x800000 0x2a40000>;
                    label-base = "ubi";
            };
      };
    
    - image-based partitions
    
      (no additional properties)
    
      example:
    
      partition at 5a0000 {
            compatible = "mstc,boot";
            label = "firmware1";
            reg = <0x5a0000 0x3200000>;
            mstc,bootnum = <1>;
            mstc,persist = <&mtd_persist>;
      };
    
    Signed-off-by: INAGAKI Hiroshi <musashino.open at gmail.com>
    Link: https://github.com/openwrt/openwrt/pull/18976
    Signed-off-by: Hauke Mehrtens <hauke at hauke-m.de>
---
 target/linux/generic/config-6.12                   |   1 +
 target/linux/generic/config-6.6                    |   1 +
 .../generic/files/drivers/mtd/mtdsplit/Kconfig     |   5 +
 .../generic/files/drivers/mtd/mtdsplit/Makefile    |   1 +
 .../drivers/mtd/mtdsplit/mtdsplit_mstc_boot.c      | 270 +++++++++++++++++++++
 5 files changed, 278 insertions(+)

diff --git a/target/linux/generic/config-6.12 b/target/linux/generic/config-6.12
index dbf0d5b3aa..53d799bc19 100644
--- a/target/linux/generic/config-6.12
+++ b/target/linux/generic/config-6.12
@@ -3915,6 +3915,7 @@ CONFIG_MTD_SPLIT_FIRMWARE_NAME="firmware"
 # CONFIG_MTD_SPLIT_JIMAGE_FW is not set
 # CONFIG_MTD_SPLIT_LZMA_FW is not set
 # CONFIG_MTD_SPLIT_MINOR_FW is not set
+# CONFIG_MTD_SPLIT_MSTC_BOOT is not set
 # CONFIG_MTD_SPLIT_SEAMA_FW is not set
 # CONFIG_MTD_SPLIT_SEIL_FW is not set
 CONFIG_MTD_SPLIT_SQUASHFS_ROOT=y
diff --git a/target/linux/generic/config-6.6 b/target/linux/generic/config-6.6
index cb1fd5b272..8ede1d244d 100644
--- a/target/linux/generic/config-6.6
+++ b/target/linux/generic/config-6.6
@@ -3803,6 +3803,7 @@ CONFIG_MTD_SPLIT_FIRMWARE_NAME="firmware"
 # CONFIG_MTD_SPLIT_JIMAGE_FW is not set
 # CONFIG_MTD_SPLIT_LZMA_FW is not set
 # CONFIG_MTD_SPLIT_MINOR_FW is not set
+# CONFIG_MTD_SPLIT_MSTC_BOOT is not set
 # CONFIG_MTD_SPLIT_SEAMA_FW is not set
 # CONFIG_MTD_SPLIT_SEIL_FW is not set
 CONFIG_MTD_SPLIT_SQUASHFS_ROOT=y
diff --git a/target/linux/generic/files/drivers/mtd/mtdsplit/Kconfig b/target/linux/generic/files/drivers/mtd/mtdsplit/Kconfig
index 17d590890d..396becf160 100644
--- a/target/linux/generic/files/drivers/mtd/mtdsplit/Kconfig
+++ b/target/linux/generic/files/drivers/mtd/mtdsplit/Kconfig
@@ -110,3 +110,8 @@ config MTD_SPLIT_SEIL_FW
 	bool "IIJ SEIL firmware parser"
 	depends on MTD_SPLIT_SUPPORT
 	select MTD_SPLIT
+
+config MTD_SPLIT_MSTC_BOOT
+	bool "MSTC bootnum-based parser"
+	depends on MTD_SPLIT_SUPPORT
+	select MTD_SPLIT
diff --git a/target/linux/generic/files/drivers/mtd/mtdsplit/Makefile b/target/linux/generic/files/drivers/mtd/mtdsplit/Makefile
index e9d63c8332..b85ce5d21f 100644
--- a/target/linux/generic/files/drivers/mtd/mtdsplit/Makefile
+++ b/target/linux/generic/files/drivers/mtd/mtdsplit/Makefile
@@ -17,3 +17,4 @@ obj-$(CONFIG_MTD_SPLIT_MINOR_FW) += mtdsplit_minor.o
 obj-$(CONFIG_MTD_SPLIT_JIMAGE_FW) += mtdsplit_jimage.o
 obj-$(CONFIG_MTD_SPLIT_ELF_FW) += mtdsplit_elf.o
 obj-$(CONFIG_MTD_SPLIT_H3C_VFS) += mtdsplit_h3c_vfs.o
+obj-$(CONFIG_MTD_SPLIT_MSTC_BOOT) += mtdsplit_mstc_boot.o
diff --git a/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_mstc_boot.c b/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_mstc_boot.c
new file mode 100644
index 0000000000..6d7980792a
--- /dev/null
+++ b/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_mstc_boot.c
@@ -0,0 +1,270 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * a mtdsplit parser using "bootnum" value in the "persist" partition
+ * for the devices manufactured by MSTC (MitraStar Technology Corp.)
+ */
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/types.h>
+#include <linux/byteorder/generic.h>
+#include <linux/slab.h>
+#include <linux/libfdt.h>
+#include <linux/of_fdt.h>
+#include <dt-bindings/mtd/partitions/uimage.h>
+
+#include "mtdsplit.h"
+
+#define PERSIST_BOOTNUM_OFFSET	0x4
+#define NR_PARTS_MAX		2
+
+/*
+ * Legacy format image header,
+ * all data in network byte order (aka natural aka bigendian).
+ */
+ struct uimage_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		*/
+	uint8_t		ih_name[IH_NMLEN];	/* Image Name		*/
+};
+
+/* check whether the current mtd device is active or not */
+static int
+mstcboot_is_active(struct mtd_info *mtd, u32 *bootnum_dt)
+{
+	struct device_node *np = mtd_get_of_node(mtd);
+	struct device_node *persist_np;
+	size_t retlen;
+	u32 persist_offset;
+	u_char bootnum;
+	int ret;
+
+	ret = of_property_read_u32(np, "mstc,bootnum", bootnum_dt);
+	if (ret)
+		return ret;
+
+	persist_np = of_parse_phandle(np, "mstc,persist", 0);
+	if (!persist_np)
+		return -ENODATA;
+	/* is "persist" under the same node? */
+	if (persist_np->parent != np->parent) {
+		of_node_put(persist_np);
+		return -EINVAL;
+	}
+
+	ret = of_property_read_u32(persist_np, "reg", &persist_offset);
+	of_node_put(persist_np);
+	if (ret)
+		return ret;
+	ret = mtd_read(mtd->parent, persist_offset + PERSIST_BOOTNUM_OFFSET,
+		       1, &retlen, &bootnum);
+	if (ret)
+		return ret;
+	if (retlen != 1)
+		return -EIO;
+
+	return (bootnum == *bootnum_dt) ? 1 : 0;
+}
+
+/*
+ * mainly for NOR devices that uses raw kernel and squashfs
+ *
+ * example:
+ *
+ * partition at 5a0000 {
+ * 	compatible = "mstc,boot";
+ * 	label = "firmware1";
+ * 	reg = <0x5a0000 0x3200000>;
+ * 	mstc,bootnum = <1>;
+ * 	mstc,persist = <&mtd_persist>;
+ * };
+ */
+static int
+mstcboot_parse_image_parts(struct mtd_info *mtd,
+			   const struct mtd_partition **pparts)
+{
+	struct mtd_partition *parts;
+	size_t retlen, kern_len = 0;
+	size_t rootfs_offset;
+	enum mtdsplit_part_type type;
+	u_char buf[0x40];
+	int ret, nr_parts = 1, index = 0;
+
+	ret = mtd_read(mtd, 0, sizeof(struct uimage_header), &retlen, buf);
+	if (ret)
+		return ret;
+	if (retlen != sizeof(struct uimage_header))
+		return -EIO;
+
+	if (be32_to_cpu(*(u32 *)buf) == OF_DT_HEADER) {
+		/* Flattened Image Tree (FIT) */
+		struct fdt_header *fdthdr = (void *)buf;
+		kern_len = be32_to_cpu(fdthdr->totalsize);
+	} else if (be32_to_cpu(*(u32 *)buf) == IH_MAGIC) {
+		/* Legacy uImage */
+		struct uimage_header *uimghdr = (void *)buf;
+		kern_len = sizeof(*uimghdr) + be32_to_cpu(uimghdr->ih_size);
+	}
+
+	ret = mtd_find_rootfs_from(mtd, kern_len, mtd->size, &rootfs_offset, &type);
+	if (ret) {
+		pr_debug("no rootfs in \"%s\"\n", mtd->name);
+		return ret;
+	}
+
+	if (kern_len > 0)
+		nr_parts++;
+
+	parts = kcalloc(nr_parts, sizeof(*parts), GFP_KERNEL);
+	if (!parts)
+		return -ENOMEM;
+
+	if (kern_len) {
+		parts[index].name = KERNEL_PART_NAME;
+		parts[index].offset = 0;
+		parts[index++].size = rootfs_offset;
+	}
+
+	parts[index].name = (type == MTDSPLIT_PART_TYPE_UBI)
+				? UBI_PART_NAME : ROOTFS_PART_NAME;
+	parts[index].offset = rootfs_offset;
+	parts[index].size = mtd->size - rootfs_offset;
+
+	*pparts = parts;
+	return nr_parts;
+}
+
+/*
+ * mainly for NAND devices that uses raw-kernel and UBI and needs
+ * splitted kernel/ubi partitions when sysupgrade
+ *
+ * example:
+ *
+ * partition at 3c0000 {
+ * 	compatible = "mstc,boot";
+ * 	reg = <0x3c0000 0x3240000>;
+ * 	label = "firmware1";
+ * 	mstc,bootnum = <1>;
+ * 	mstc,persist = <&mtd_persist>;
+ * 	#address-cells = <1>;
+ * 	#size-cells = <1>;
+ *
+ * 	partition at 0 {
+ * 		reg = <0x0 0x800000>;
+ * 		label-base = "kernel";
+ * 	};
+ *
+ * 	partition at 800000 {
+ * 		reg = <0x800000 0x2a40000>;
+ * 		label-base = "ubi";
+ * 	};
+};
+ */
+static int
+mstcboot_parse_fixed_parts(struct mtd_info *mtd,
+			   const struct mtd_partition **pparts,
+			   int active, u32 bootnum_dt)
+{
+	struct device_node *np = mtd_get_of_node(mtd);
+	struct device_node *child;
+	struct mtd_partition *parts;
+	int ret, nr_parts, index = 0;
+
+	nr_parts = of_get_child_count(np);
+	if (nr_parts > NR_PARTS_MAX) {
+		pr_err("too many partitions found!\n");
+		return -EINVAL;
+	}
+
+	parts = kcalloc(nr_parts, sizeof(*parts), GFP_KERNEL);
+	if (!parts)
+		return -ENOMEM;
+
+	for_each_child_of_node(np, child) {
+		u32 reg[2];
+		if (of_n_addr_cells(child) != 1 ||
+		    of_n_size_cells(child) != 1)
+		{
+			ret = -EINVAL;
+			break;
+		}
+
+		ret = of_property_read_u32_array(child, "reg", reg, 2);
+		if (ret)
+			break;
+		ret = of_property_read_string(child, "label-base",
+					      &parts[index].name);
+		if (ret)
+			break;
+
+		if (!active) {
+			parts[index].name = devm_kasprintf(&mtd->dev, GFP_KERNEL,
+						"%s%u",
+						parts[index].name, bootnum_dt);
+			if (!parts[index].name) {
+				ret = -ENOMEM;
+				break;
+			}
+		}
+		parts[index].offset = reg[0];
+		parts[index].size = reg[1];
+		index++;
+	}
+	of_node_put(child);
+
+	if (ret)
+		kfree(parts);
+	else
+		*pparts = parts;
+	return ret ? ret : nr_parts;
+}
+
+static int
+mtdsplit_mstcboot_parse(struct mtd_info *mtd,
+			const struct mtd_partition **pparts,
+			struct mtd_part_parser_data *data)
+{
+	struct device_node *np = mtd_get_of_node(mtd);
+	u32 bootnum_dt;
+	int ret;
+
+	ret = mstcboot_is_active(mtd, &bootnum_dt);
+	if (ret < 0)
+		goto exit;
+
+	if (of_get_child_count(np))
+		ret = mstcboot_parse_fixed_parts(mtd, pparts, ret, bootnum_dt);
+	else if (ret != 0)
+		ret = mstcboot_parse_image_parts(mtd, pparts);
+
+exit:
+	/*
+	 * return 0 when ret=-ENODEV, to prevent deletion of
+	 * parent mtd partitions on Linux 6.7 and later
+	 */
+	return ret == -ENODEV ? 0 : ret;
+}
+
+static const struct of_device_id mtdsplit_mstcboot_of_match_table[] = {
+	{ .compatible = "mstc,boot" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, mtdsplit_mstcboot_of_match_table);
+
+static struct mtd_part_parser mtdsplit_mstcboot_parser = {
+	.owner = THIS_MODULE,
+	.name = "mstc-boot",
+	.of_match_table = mtdsplit_mstcboot_of_match_table,
+	.parse_fn = mtdsplit_mstcboot_parse,
+	.type = MTD_PARSER_TYPE_FIRMWARE,
+};
+module_mtd_part_parser(mtdsplit_mstcboot_parser)




More information about the lede-commits mailing list