[][openwrt-24][common][bsp][Add initial support for openwrt master filogic target]

[Description]
Add initial support for filogic target from openwrt master

[Release-log]
N/A

Change-Id: Ib9d44bdf74b05dc445679001e988b2f1fb8d05b6
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/9273335
diff --git a/master/files/package/mtk/reset-boot-count/Makefile b/master/files/package/mtk/reset-boot-count/Makefile
new file mode 100644
index 0000000..83fbf13
--- /dev/null
+++ b/master/files/package/mtk/reset-boot-count/Makefile
@@ -0,0 +1,38 @@
+#
+# Copyright (C) 2008-2012 OpenWrt.org
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+include $(TOPDIR)/rules.mk
+include $(INCLUDE_DIR)/kernel.mk
+
+PKG_NAME:=reset-boot-count
+PKG_RELEASE:=1
+PKG_LICENSE:=BSD-3-Clause
+
+include $(INCLUDE_DIR)/package.mk
+
+define KernelPackage/reset-boot-count
+  SUBMENU:=Other modules
+  TITLE:=Reset dual-boot boot counter for MediaTek platform
+  FILES:=$(PKG_BUILD_DIR)/reset-boot-count.ko
+  KCONFIG:=
+endef
+
+define KernelPackage/reset-boot-count/description
+  This is used to reset dual-boot boot retry counter to indicate that the
+  system has booted up successfully
+endef
+
+define Build/Compile
+	$(KERNEL_MAKE) M="$(PKG_BUILD_DIR)" modules
+endef
+
+define KernelPackage/reset-boot-count/install
+	$(INSTALL_DIR) $(1)/etc/init.d
+	$(INSTALL_BIN) ./files/reset-boot-count.init $(1)/etc/init.d/reset-boot-count
+endef
+
+$(eval $(call KernelPackage,reset-boot-count))
diff --git a/master/files/package/mtk/reset-boot-count/files/reset-boot-count.init b/master/files/package/mtk/reset-boot-count/files/reset-boot-count.init
new file mode 100644
index 0000000..257f122
--- /dev/null
+++ b/master/files/package/mtk/reset-boot-count/files/reset-boot-count.init
@@ -0,0 +1,13 @@
+#!/bin/sh /etc/rc.common
+# Copyright (C) 2024 All Rights Reserved.
+# Author: Weijie Gao <weijie.gao@mediatek.com>
+
+START=99
+boot() {
+	local enabled=$([ -f /sys/firmware/devicetree/base/mediatek,reset-boot-count ] && echo Y)
+
+	if [ x"${enabled}" = xY ]; then
+		modprobe reset-boot-count
+		echo 1 > /proc/reset_boot_count
+	fi
+}
diff --git a/master/files/package/mtk/reset-boot-count/src/Makefile b/master/files/package/mtk/reset-boot-count/src/Makefile
new file mode 100644
index 0000000..4aeb180
--- /dev/null
+++ b/master/files/package/mtk/reset-boot-count/src/Makefile
@@ -0,0 +1,2 @@
+

+obj-m      := reset-boot-count.o

diff --git a/master/files/package/mtk/reset-boot-count/src/reset-boot-count.c b/master/files/package/mtk/reset-boot-count/src/reset-boot-count.c
new file mode 100644
index 0000000..6a7f220
--- /dev/null
+++ b/master/files/package/mtk/reset-boot-count/src/reset-boot-count.c
@@ -0,0 +1,142 @@
+// SPDX-License-Identifier: GPL-2.0

+/*

+ * Copyright (C) 2024 MediaTek Inc. All rights reserved.

+ *

+ * Helper for resetting boot count of A/B boot systems

+ *

+ * Author: Weijie Gao <weijie.gao@mediatek.com>

+ */

+

+#include <linux/kernel.h>

+#include <linux/module.h>

+#include <linux/delay.h>

+#include <linux/init.h>

+#include <linux/proc_fs.h>

+#include <linux/seq_file.h>

+#include <linux/arm-smccc.h>

+

+#define RBC "reset_boot_count"

+

+#define MTK_SIP_READ_NONRST_REG			0xC2000550

+#define MTK_SIP_WRITE_NONRST_REG		0xC2000551

+

+static struct proc_dir_entry *rbc_entry;

+

+static bool dual_boot_get_boot_count(u32 *retslot, u32 *retcnt)

+{

+	struct arm_smccc_res res = {0};

+	u32 val, slot;

+	s8 neg, pos;

+

+	arm_smccc_smc(MTK_SIP_READ_NONRST_REG, 0, 0, 0, 0, 0, 0, 0, &res);

+

+	val = (u32)res.a0;

+

+	/* slot: val[31..24] = -slot, val[23..16] = slot */

+	pos = (val >> 16) & 0xff;

+	neg = (val >> 24) & 0xff;

+

+	if (!(pos >= 0 && neg <= 0 && pos + neg == 0)) {

+		pr_debug("slot of boot count is invalid\n");

+		goto err;

+	}

+

+	slot = pos;

+

+	/* count: val[15..8] = -count, val[7..0] = count */

+	pos = val & 0xff;

+	neg = (val >> 8) & 0xff;

+

+	if (!(pos >= 0 && neg <= 0 && pos + neg == 0)) {

+		pr_debug("count of boot count is invalid\n");

+		goto err;

+	}

+

+	if (retslot)

+		*retslot = slot;

+

+	if (retcnt)

+		*retcnt = pos;

+

+	return true;

+

+err:

+	if (retslot)

+		*retslot = 0;

+

+	if (retcnt)

+		*retcnt = 0;

+

+	return false;

+}

+

+static void dual_boot_set_boot_count(u32 slot, u32 count)

+{

+	struct arm_smccc_res res = {0};

+	u32 val;

+	s32 neg;

+

+	if (slot > 127 || count > 127)

+		return;

+

+	neg = -count;

+	val = count | ((neg << 8) & 0xff00);

+

+	neg = -slot;

+	val = val | ((uint32_t)slot << 16) | ((neg << 24) & 0xff000000);

+

+	arm_smccc_smc(MTK_SIP_WRITE_NONRST_REG, 0, val, 0, 0, 0, 0, 0, &res);

+}

+

+static int rbc_display(struct seq_file *seq, void *v)

+{

+	return 0;

+}

+

+static int rbc_open(struct inode *inode, struct file *file)

+{

+	return single_open(file, rbc_display, inode->i_private);

+}

+

+static ssize_t rbc_write(struct file *file, const char __user *buffer,

+			 size_t count, loff_t *pos)

+{

+	u32 slot;

+

+	dual_boot_get_boot_count(&slot, NULL);

+	dual_boot_set_boot_count(slot, 0);

+

+	pr_info("Boot count reset\n");

+

+	return count;

+}

+

+static const struct proc_ops rbc_fops = {

+	.proc_open  = rbc_open,

+	.proc_read = seq_read,

+	.proc_write = rbc_write,

+	.proc_lseek  = seq_lseek,

+	.proc_release = single_release,

+};

+

+static int __init rbc_init(void)

+{

+	rbc_entry = proc_create(RBC, 0200, NULL, &rbc_fops);

+

+	if (!rbc_entry)

+		pr_err("failed to create proc entry " RBC);

+

+	return 0;

+}

+

+static void __exit rbc_exit(void)

+{

+	remove_proc_entry(RBC, NULL);

+}

+

+module_init(rbc_init);

+module_exit(rbc_exit);

+

+MODULE_AUTHOR("Weijie Gao <weijie.gao@mediatek.com>");

+MODULE_DESCRIPTION("Kernel module for resetting boot count of A/B boot systems");

+MODULE_LICENSE("GPL");

diff --git a/master/files/package/system/fstools/patches/0001-add-support-for-dual-boot.patch b/master/files/package/system/fstools/patches/0001-add-support-for-dual-boot.patch
new file mode 100644
index 0000000..53d68b6
--- /dev/null
+++ b/master/files/package/system/fstools/patches/0001-add-support-for-dual-boot.patch
@@ -0,0 +1,570 @@
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -5,6 +5,10 @@ ADD_DEFINITIONS(-Os -ggdb -Wall -Werror
+ 
+ SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
+ 
++ADD_LIBRARY(fstools-bootparam SHARED
++		boot_param.c)
++INSTALL(TARGETS fstools-bootparam LIBRARY DESTINATION lib)
++
+ ADD_LIBRARY(fstools SHARED
+ 		libfstools/common.c
+ 		libfstools/snapshot.c
+@@ -18,7 +22,7 @@ ADD_LIBRARY(fstools SHARED
+ 		libfstools/rootdisk.c
+ 		libfstools/partname.c
+ 		libfstools/find.c)
+-TARGET_LINK_LIBRARIES(fstools ubox)
++TARGET_LINK_LIBRARIES(fstools ubox fstools-bootparam)
+ INSTALL(TARGETS fstools LIBRARY DESTINATION lib)
+ 
+ ADD_LIBRARY(blkid-tiny SHARED
+@@ -80,9 +84,9 @@ INSTALL(TARGETS blockd RUNTIME DESTINATI
+ ADD_EXECUTABLE(block block.c probe.c probe-libblkid.c)
+ IF(DEFINED CMAKE_UBIFS_EXTROOT)
+ 	ADD_DEFINITIONS(-DUBIFS_EXTROOT)
+-	TARGET_LINK_LIBRARIES(block blkid-tiny dl uci ubox ubus blobmsg_json ubi-utils ${json})
++	TARGET_LINK_LIBRARIES(block blkid-tiny fstools-bootparam dl uci ubox ubus blobmsg_json ubi-utils ${json})
+ ELSE(DEFINED CMAKE_UBIFS_EXTROOT)
+-	TARGET_LINK_LIBRARIES(block blkid-tiny dl uci ubox ubus blobmsg_json ${json})
++	TARGET_LINK_LIBRARIES(block blkid-tiny fstools-bootparam dl uci ubox ubus blobmsg_json ${json})
+ ENDIF(DEFINED CMAKE_UBIFS_EXTROOT)
+ INSTALL(TARGETS block RUNTIME DESTINATION sbin)
+ 
+--- a/block.c
++++ b/block.c
+@@ -45,6 +45,7 @@
+ #include <libubus.h>
+ 
+ #include "probe.h"
++#include "boot_param.h"
+ 
+ #define AUTOFS_MOUNT_PATH       "/tmp/run/blockd/"
+ 
+@@ -87,6 +88,9 @@ static LIST_HEAD(devices);
+ static int anon_mount, anon_swap, auto_mount, auto_swap, check_fs;
+ static unsigned int delay_root;
+ 
++static char *hide_block_devs[3];
++static uint32_t num_hide_block_devs;
++
+ enum {
+ 	CFG_ANON_MOUNT,
+ 	CFG_ANON_SWAP,
+@@ -557,9 +561,12 @@ static struct probe_info* _probe_path(ch
+ 	return pr;
+ }
+ 
++static char* find_mount_point(char *block);
++
+ static int _cache_load(const char *path)
+ {
+ 	int gl_flags = GLOB_NOESCAPE | GLOB_MARK;
++	uint32_t i;
+ 	int j;
+ 	glob_t gl;
+ 
+@@ -568,8 +575,30 @@ static int _cache_load(const char *path)
+ 
+ 	for (j = 0; j < gl.gl_pathc; j++) {
+ 		struct probe_info *pr = _probe_path(gl.gl_pathv[j]);
+-		if (pr)
++		bool skip_curr = false;
++
++		if (pr) {
++			char *mp = find_mount_point(pr->dev);
++			if (mp) {
++				/* Skip blocks mounted as root or overlay */
++				if (!strcmp(mp, "/rom") ||
++				    !strcmp(mp, "/overlay"))
++					continue;
++			}
++
++			for (i = 0; i < num_hide_block_devs; i++) {
++				/* Skip blocks used for dual boot */
++				if (!strcmp(hide_block_devs[i], pr->dev)) {
++					skip_curr = true;
++					break;
++				}
++			}
++
++			if (skip_curr)
++				continue;
++
+ 			list_add_tail(&pr->list, &devices);
++		}
+ 	}
+ 
+ 	globfree(&gl);
+@@ -1376,6 +1405,15 @@ static int find_block_ubi(libubi_t libub
+ 	int dev_num;
+ 	int vol_id;
+ 	int err = -1;
++	char rootfs_data_dev[127];
++
++	if (!strcmp(name, "rootfs_data")) {
++		read_boot_param_string("boot-rootfs_data-part", rootfs_data_dev,
++				       sizeof(rootfs_data_dev));
++
++		if (rootfs_data_dev[0])
++			name = rootfs_data_dev;
++	}
+ 
+ 	err = find_ubi_vol(libubi, name, &dev_num, &vol_id);
+ 	if (!err)
+@@ -1936,6 +1974,26 @@ static int main_swapoff(int argc, char *
+ 	return 0;
+ }
+ 
++static bool add_hide_block_dev(char *path)
++{
++	if (num_hide_block_devs >= ARRAY_SIZE(hide_block_devs))
++		return false;
++
++	hide_block_devs[num_hide_block_devs++] = path;
++	return true;
++}
++
++static void hide_boot_param_dev(const char *name)
++{
++	char *path;
++
++	path = boot_param_get_dev(name);
++	if (path) {
++		if (!add_hide_block_dev(path))
++			free(path);
++	}
++}
++
+ int main(int argc, char **argv)
+ {
+ 	char *base = basename(*argv);
+@@ -1945,6 +2003,8 @@ int main(int argc, char **argv)
+ 	ulog_open(-1, -1, "block");
+ 	ulog_threshold(LOG_NOTICE);
+ 
++	hide_boot_param_dev("rootfs_data-part");
++
+ 	if (!strcmp(base, "swapon"))
+ 		return main_swapon(argc, argv);
+ 
+--- a/boot_param.c
++++ b/boot_param.c
+@@ -0,0 +1,244 @@
++/* SPDX-License-Identifier: BSD-3-Clause */
++/*
++ * Copyright (C) 2024 MediaTek Inc. All rights reserved.
++ *
++ * Author: Weijie Gao <weijie.gao@mediatek.com>
++ */
++
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <stdio.h>
++#include <string.h>
++#include <dirent.h>
++#include <fcntl.h>
++#include <unistd.h>
++#include <glob.h>
++#include <dlfcn.h>
++
++#include <blkid/blkid.h>
++#include <libubox/ulog.h>
++#include "boot_param.h"
++
++#define ARRAY_SIZE(a)	(sizeof(a) / sizeof(a[0]))
++
++#define BOOT_PARAM_STR_MAX_LEN			256
++
++static struct {
++	bool loaded;
++	blkid_probe (*new_probe_from_filename)(const char *);
++	int (*do_safeprobe)(blkid_probe);
++	int (*probe_lookup_value)(blkid_probe, const char *, const char **, size_t *);
++	void (*free_probe)(blkid_probe);
++	int (*probe_enable_partitions)(blkid_probe, int);
++	int (*probe_set_partitions_flags)(blkid_probe, int);
++} libblkid = {};
++
++bool read_boot_param_bool(const char *name)
++{
++	char path[BOOT_PARAM_STR_MAX_LEN];
++	FILE *f;
++
++	snprintf(path, sizeof(path),
++		 "/sys/firmware/devicetree/base/mediatek,%s", name);
++
++	f = fopen(path, "rb");
++	if (!f)
++		return false;
++
++	fclose(f);
++
++	return true;
++}
++
++int read_boot_param_string(const char *name, char *val, size_t maxsize)
++{
++	char path[BOOT_PARAM_STR_MAX_LEN];
++	size_t len;
++	FILE *f;
++
++	snprintf(path, sizeof(path),
++		 "/sys/firmware/devicetree/base/mediatek,%s", name);
++
++	f = fopen(path, "rb");
++	if (!f) {
++		val[0] = 0;
++		return -1;
++	}
++
++	len = fread(val, 1, maxsize, f);
++	fclose(f);
++
++	while (len > 0) {
++		if (val[len - 1] != '\n' && val[len - 1] != '\r')
++			break;
++
++		len--;
++	}
++
++	if (len < maxsize)
++		val[len] = 0;
++
++	return len;
++}
++
++static bool load_libblkid(void)
++{
++	void *lib;
++
++	if (libblkid.loaded)
++		return true;
++
++	lib = dlopen("libblkid.so", RTLD_GLOBAL);
++
++	if (!lib)
++		lib = dlopen("libblkid.so.1", RTLD_GLOBAL);
++
++	if (!lib)
++		return false;
++
++	libblkid.new_probe_from_filename = dlsym(lib, "blkid_new_probe_from_filename");
++	if (!libblkid.new_probe_from_filename)
++		return false;
++
++	libblkid.do_safeprobe = dlsym(lib, "blkid_do_safeprobe");
++	if (!libblkid.do_safeprobe)
++		return false;
++
++	libblkid.probe_lookup_value = dlsym(lib, "blkid_probe_lookup_value");
++	if (!libblkid.probe_lookup_value)
++		return false;
++
++	libblkid.free_probe = dlsym(lib, "blkid_free_probe");
++	if (!libblkid.free_probe)
++		return false;
++
++	libblkid.probe_enable_partitions = dlsym(lib, "blkid_probe_enable_partitions");
++	if (!libblkid.probe_enable_partitions)
++		return false;
++
++	libblkid.probe_set_partitions_flags = dlsym(lib, "blkid_probe_set_partitions_flags");
++	if (!libblkid.probe_set_partitions_flags)
++		return false;
++
++	libblkid.loaded = true;
++	return true;
++}
++
++static char *lookup_block_dev(const char *path, const char *key, bool is_uuid)
++{
++	int gl_flags = GLOB_NOESCAPE | GLOB_MARK;
++	const char *type, *value;
++	char *result = NULL;
++	size_t len;
++	glob_t gl;
++	int i;
++
++	if (glob(path, gl_flags, NULL, &gl) < 0)
++		return NULL;
++
++	type = is_uuid ? "PART_ENTRY_UUID" : "PART_ENTRY_NAME";
++
++	for (i = 0; i < gl.gl_pathc; i++) {
++		blkid_probe pr = libblkid.new_probe_from_filename(gl.gl_pathv[i]);
++		if (!pr)
++			continue;
++
++		libblkid.probe_enable_partitions(pr, 1);
++		libblkid.probe_set_partitions_flags(pr, BLKID_PARTS_ENTRY_DETAILS);
++
++		if (libblkid.do_safeprobe(pr))
++			goto free_pr;
++
++		if (!libblkid.probe_lookup_value(pr, type, &value, &len)) {
++			if (!strcmp(value, key))
++				result = strdup(gl.gl_pathv[i]);
++		}
++
++	free_pr:
++		libblkid.free_probe(pr);
++
++		if (result)
++			break;
++	}
++
++	globfree(&gl);
++
++	return result;
++}
++
++static char *find_block_dev(const char *key, bool is_uuid)
++{
++	char *devpath = NULL;
++	int i;
++
++	static const char *block_pats[] = {
++		"/dev/loop*",
++		"/dev/mmcblk*",
++		"/dev/sd*",
++		"/dev/hd*",
++		"/dev/md*",
++		"/dev/nvme*",
++		"/dev/vd*",
++		"/dev/xvd*",
++		"/dev/dm-*",
++		"/dev/fit*",
++	};
++
++	if (!load_libblkid())
++		return NULL;
++
++	for (i = 0; i < ARRAY_SIZE(block_pats); i++) {
++		devpath = lookup_block_dev(block_pats[i], key, is_uuid);
++		if (devpath)
++			break;
++	}
++
++	return devpath;
++}
++
++char *blockdev_parse(const char *name)
++{
++	char *e, *part_dev_path;
++	struct stat st;
++
++	if (!name)
++		return NULL;
++
++	e = strchr(name, '=');
++	if (e) {
++		*e = 0;
++		e++;
++	}
++
++	if (!e) {
++		if (stat(name, &st))
++			return NULL;
++
++		if (!S_ISBLK(st.st_mode))
++			return NULL;
++
++		part_dev_path = strdup(name);
++	} else if (!strcmp(name, "PARTLABEL")) {
++		part_dev_path = find_block_dev(e, false);
++	} else if (!strcmp(name, "PARTUUID")) {
++		if (strlen(e) != 36)
++			return NULL;
++		part_dev_path = find_block_dev(e, true);
++	} else {
++		return NULL;
++	}
++
++	return part_dev_path;
++}
++
++char *boot_param_get_dev(const char *name)
++{
++	char partkey[BOOT_PARAM_STR_MAX_LEN];
++
++	read_boot_param_string(name, partkey, sizeof(partkey));
++
++	if (!partkey[0])
++		return NULL;
++
++	return blockdev_parse(partkey);
++}
+--- a/boot_param.h
++++ b/boot_param.h
+@@ -0,0 +1,20 @@
++// SPDX-License-Identifier: BSD-3-Clause
++/*
++ * Copyright (C) 2024 MediaTek Inc. All rights reserved.
++ *
++ * Author: Weijie Gao <weijie.gao@mediatek.com>
++ */
++
++#ifndef _BOOT_PARAM_H_
++#define _BOOT_PARAM_H_
++
++#include <stddef.h>
++#include <stdbool.h>
++
++bool read_boot_param_bool(const char *name);
++int read_boot_param_string(const char *name, char *val, size_t maxsize);
++
++char *blockdev_parse(const char *name);
++char *boot_param_get_dev(const char *name);
++
++#endif /* _BOOT_PARAM_H_ */
+--- a/libfstools/rootdisk.c
++++ b/libfstools/rootdisk.c
+@@ -12,6 +12,7 @@
+  */
+ 
+ #include "common.h"
++#include "../boot_param.h"
+ 
+ #include <linux/loop.h>
+ 
+@@ -28,6 +29,7 @@ struct rootdev_volume {
+ 	struct volume v;
+ 	uint64_t offset;
+ 	char loop_name[32];
++	char *dev_path;
+ };
+ 
+ static const char *rootdev;
+@@ -93,6 +95,28 @@ static int get_squashfs(struct squashfs_
+ 	return 0;
+ }
+ 
++static struct volume *find_existed_rootfs_data(void)
++{
++	struct rootdev_volume *p;
++	char *rootfs_data_dev;
++
++	rootfs_data_dev = boot_param_get_dev("rootfs_data-part");
++
++	if (!rootfs_data_dev)
++		return NULL;
++
++	ULOG_NOTE("Using existed rootfs_data device %s\n", rootfs_data_dev);
++
++	p = calloc(1, sizeof(*p));
++	p->v.drv = &rootdisk_driver;
++	p->v.name = "rootfs_data";
++
++	p->offset = 0;
++	p->dev_path = rootfs_data_dev;
++
++	return &p->v;
++}
++
+ static struct volume *rootdisk_volume_find(char *name)
+ {
+ 	struct squashfs_super_block sb;
+@@ -101,6 +125,9 @@ static struct volume *rootdisk_volume_fi
+ 	if (strcmp(name, "rootfs_data") != 0)
+ 		return NULL;
+ 
++	if (read_boot_param_bool("no-split-fitrw"))
++		return find_existed_rootfs_data();
++
+ 	if (!rootdev)
+ 		rootdev = get_rootdev("/");
+ 	if (!rootdev)
+@@ -128,9 +155,14 @@ static struct volume *rootdisk_volume_fi
+ static int rootdisk_volume_identify(struct volume *v)
+ {
+ 	struct rootdev_volume *p = container_of(v, struct rootdev_volume, v);
++	const char *dev = rootdev;
+ 	FILE *f;
+ 	int ret = FS_NONE;
+-	f = fopen(rootdev, "r");
++
++	if (p->dev_path)
++		dev = p->dev_path;
++
++	f = fopen(dev, "r");
+ 	if (!f)
+ 		return ret;
+ 
+@@ -213,6 +245,15 @@ static int rootdisk_create_loop(struct r
+ static int rootdisk_volume_init(struct volume *v)
+ {
+ 	struct rootdev_volume *p = container_of(v, struct rootdev_volume, v);
++	const char *dev = rootdev;
++
++	if (p->dev_path) {
++		/* Do not create loop device with no-split-fitrw set */
++		v->type = BLOCKDEV;
++		v->blk = p->dev_path;
++		dev = p->dev_path;
++		goto do_format;
++	}
+ 
+ 	if (!p->loop_name[0] && rootdisk_create_loop(p) != 0) {
+ 		ULOG_ERR("unable to create loop device\n");
+@@ -222,7 +263,8 @@ static int rootdisk_volume_init(struct v
+ 	v->type = BLOCKDEV;
+ 	v->blk = p->loop_name;
+ 
+-	return block_volume_format(v, p->offset, rootdev);
++do_format:
++	return block_volume_format(v, p->offset, dev);
+ }
+ 
+ static struct driver rootdisk_driver = {
+--- a/libfstools/ubi.c
++++ b/libfstools/ubi.c
+@@ -12,6 +12,7 @@
+  */
+ 
+ #include "common.h"
++#include "../boot_param.h"
+ 
+ /* fit for UBI_MAX_VOLUME_NAME and sysfs path lengths */
+ #define BUFLEN		128
+@@ -140,6 +141,15 @@ static struct volume *ubi_volume_find(ch
+ 	DIR *ubi_dir;
+ 	struct dirent *ubi_dirent;
+ 	unsigned int ubi_num;
++	char rootfs_data_dev[127];
++
++	if (!strcmp(name, "rootfs_data")) {
++		read_boot_param_string("boot-rootfs_data-part", rootfs_data_dev,
++				       sizeof(rootfs_data_dev));
++
++		if (rootfs_data_dev[0])
++			name = rootfs_data_dev;
++	}
+ 
+ 	if (find_filesystem("ubifs"))
+ 		return ret;
+--- a/mount_root.c
++++ b/mount_root.c
+@@ -23,6 +23,8 @@
+ #include "libfstools/libfstools.h"
+ #include "libfstools/volume.h"
+ 
++#include "boot_param.h"
++
+ /*
+  * Called in the early (PREINIT) stage, when we immediately need some writable
+  * filesystem.
+@@ -62,6 +64,12 @@ start(int argc, char *argv[3])
+ 	/* There isn't extroot, so just try to mount "rootfs_data" */
+ 	volume_init(data);
+ 	switch (volume_identify(data)) {
++	case -1:
++		/* Use ramoverlay if no "rootfs_data" device found with no-split-fitrw set */
++		if (!read_boot_param_bool("no-split-fitrw"))
++			break;
++
++		/* fall through */
+ 	case FS_NONE:
+ 		ULOG_WARN("no usable overlay filesystem found, using tmpfs overlay\n");
+ 		return ramoverlay();
diff --git a/master/files/target/linux/mediatek/files-6.6/arch/arm64/boot/dts/mediatek/mt7981-rfb-spim-nor.dtso b/master/files/target/linux/mediatek/files-6.6/arch/arm64/boot/dts/mediatek/mt7981-rfb-spim-nor.dtso
new file mode 100644
index 0000000..d93cbbc
--- /dev/null
+++ b/master/files/target/linux/mediatek/files-6.6/arch/arm64/boot/dts/mediatek/mt7981-rfb-spim-nor.dtso
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/*
+ * Copyright (C) 2022 MediaTek Inc.
+ * Author: Sam.Shih <sam.shih@mediatek.com>
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "mediatek,mt7981-rfb", "mediatek,mt7981";
+
+	fragment@0 {
+		target = <&spi2>;
+		__overlay__ {
+			pinctrl-names = "default";
+			pinctrl-0 = <&spi2_flash_pins>;
+			status = "okay";
+
+			flash@0 {
+				#address-cells = <1>;
+				#size-cells = <1>;
+				compatible = "jedec,spi-nor";
+				reg = <0>;
+				spi-max-frequency = <52000000>;
+				spi-tx-bus-width = <4>;
+				spi-rx-bus-width = <4>;
+
+				partition@00000 {
+					label = "BL2";
+					reg = <0x00000 0x0040000>;
+				};
+				partition@40000 {
+					label = "u-boot-env";
+					reg = <0x40000 0x0010000>;
+				};
+				partition@50000 {
+					label = "Factory";
+					reg = <0x50000 0x00b0000>;
+				};
+				partition@100000 {
+					label = "FIP";
+					reg = <0x100000 0x0080000>;
+				};
+				nor_rootfs: partition@180000 {
+					label = "firmware";
+					reg = <0x180000 0xe80000>;
+				};
+			};
+		};
+	};
+
+	fragment@1 {
+		target-path = "/chosen";
+		__overlay__ {
+			rootdisk-nor = <&nor_rootfs>;
+		};
+	};
+};
diff --git a/master/files/target/linux/mediatek/files-6.6/arch/arm64/boot/dts/mediatek/mt7988a-rfb-spim-nand-nmbm.dtso b/master/files/target/linux/mediatek/files-6.6/arch/arm64/boot/dts/mediatek/mt7988a-rfb-spim-nand-nmbm.dtso
new file mode 100644
index 0000000..62d4e86
--- /dev/null
+++ b/master/files/target/linux/mediatek/files-6.6/arch/arm64/boot/dts/mediatek/mt7988a-rfb-spim-nand-nmbm.dtso
@@ -0,0 +1,78 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/*
+ * Copyright (C) 2022 MediaTek Inc.
+ * Author: Sam.Shih <sam.shih@mediatek.com>
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "mediatek,mt7988a-rfb", "mediatek,mt7988a";
+
+	fragment@0 {
+		target = <&spi0>;
+		__overlay__ {
+			pinctrl-names = "default";
+			pinctrl-0 = <&spi0_flash_pins>;
+			status = "okay";
+
+			flash@0 {
+				compatible = "spi-nand";
+				reg = <0>;
+				spi-max-frequency = <52000000>;
+				spi-tx-bus-width = <4>;
+				spi-rx-bus-width = <4>;
+				mediatek,nmbm;
+				mediatek,bmt-max-ratio = <1>;
+				mediatek,bmt-max-reserved-blocks = <64>;
+
+				partitions {
+					compatible = "fixed-partitions";
+					#address-cells = <1>;
+					#size-cells = <1>;
+
+					partition@0 {
+						label = "BL2";
+						reg = <0x00000 0x0100000>;
+						read-only;
+					};
+
+					partition@100000 {
+						label = "u-boot-env";
+						reg = <0x0100000 0x0080000>;
+					};
+
+					partition@180000 {
+						label = "Factory";
+						reg = <0x180000 0x0400000>;
+					};
+
+					partition@580000 {
+						label = "FIP";
+						reg = <0x580000 0x0200000>;
+					};
+
+					partition@780000 {
+						label = "ubi";
+						reg = <0x780000 0x7080000>;
+						compatible = "linux,ubi";
+
+						volumes {
+							ubi_rootfs: ubi-volume-fit {
+								volname = "firmware";
+							};
+						};
+					};
+				};
+			};
+		};
+	};
+
+	fragment@2 {
+		target-path = "/chosen";
+		__overlay__ {
+			rootdisk-spim-nand = <&ubi_rootfs>;
+		};
+	};
+};
diff --git a/master/files/target/linux/mediatek/filogic/base-files/lib/upgrade/mtk_mmc.sh b/master/files/target/linux/mediatek/filogic/base-files/lib/upgrade/mtk_mmc.sh
new file mode 100644
index 0000000..db99960
--- /dev/null
+++ b/master/files/target/linux/mediatek/filogic/base-files/lib/upgrade/mtk_mmc.sh
@@ -0,0 +1,111 @@
+# Script for dual image A/B system boots
+# Copyright (C) 2024 MediaTek Inc.
+# Author: Weijie Gao <weijie.gao@mediatek.com>
+
+block_dev_path() {
+	local dev_path
+
+	case "$1" in
+	/dev/mmcblk*)
+		dev_path="$1"
+		;;
+	PARTLABEL=* | PARTUUID=*)
+		dev_path=$(blkid -t "$1" -o device)
+		[ -z "${dev_path}" -o $? -ne 0 ] && return 1
+		;;
+	*)
+		return 1;
+		;;
+	esac
+
+	echo "${dev_path}"
+	return 0
+}
+
+mmc_dual_boot_upgrade_itb() {
+	local fit_file="$1"
+	local reserve_rootfs_data=$([ -f /sys/firmware/devicetree/base/mediatek,reserve-rootfs_data ] && echo Y)
+
+	local firmware_part=$(cat /sys/firmware/devicetree/base/mediatek,upgrade-firmware-part 2>/dev/null)
+	[ -z "${firmware_part}" -o $? -ne 0 ] && return 1
+
+	EMMC_KERN_DEV=$(block_dev_path "${firmware_part}")
+	[ -z "${EMMC_KERN_DEV}" -o $? -ne 0 ] && return 1
+
+	export EMMC_KERNEL_BLOCKS=$(($(get_image "$fit_file" | fwtool -i /dev/null -T - | dd of="$EMMC_KERN_DEV" bs=512 2>&1 | grep "records out" | cut -d' ' -f1)))
+
+	local upgrade_image_slot=$(cat /sys/firmware/devicetree/base/mediatek,upgrade-image-slot 2>/dev/null)
+	[ -n "${upgrade_image_slot}" ] && {
+		v "Set new boot image slot to ${upgrade_image_slot}"
+		# Force the creation of fw_printenv.lock
+		mkdir -p /var/lock
+		touch /var/lock/fw_printenv.lock
+		fw_setenv "dual_boot.current_slot" "${upgrade_image_slot}"
+		fw_setenv "dual_boot.slot_${upgrade_image_slot}_invalid" "0"
+	}
+
+	local rootfs_data_dev=$(cat /sys/firmware/devicetree/base/mediatek,rootfs_data-part 2>/dev/null)
+	if [ -z "${rootfs_data_dev}" -o $? -ne 0 ]; then
+		# Individual rootfs_data for each slot
+		[ -z "$UPGRADE_BACKUP" ] && dd if=/dev/zero of="$EMMC_KERN_DEV" bs=512 seek=$EMMC_KERNEL_BLOCKS count=8
+		return
+	fi
+
+	if [ x"${reserve_rootfs_data}" = xY ]; then
+		# Do not touch rootfs_data
+		return
+	fi
+
+	rootfs_data_dev=$(block_dev_path "${rootfs_data_dev}")
+	[ -z "${rootfs_data_dev}" -o $? -ne 0 ] && return 1
+
+	[ -z "$UPGRADE_BACKUP" ] && dd if=/dev/zero of="${rootfs_data_dev}" bs=512 count=8
+}
+
+mmc_do_upgrade() {
+	local file=$1
+	local dual_boot=$([ -f /sys/firmware/devicetree/base/mediatek,dual-boot ] && echo Y)
+
+	local file_type=$(identify_magic_long "$(get_magic_long "$file")")
+	case "$file_type" in
+		"fit")
+			if [ x"${dual_boot}" != xY ]; then
+				emmc_upgrade_fit "$file"
+				sync
+				return
+			fi
+
+			mmc_dual_boot_upgrade_itb "$file"
+			sync
+			;;
+		*)
+			v "Unsupported firmware type: $file_type"
+			;;
+	esac
+}
+
+mmc_copy_config() {
+	local dual_boot=$([ -f /sys/firmware/devicetree/base/mediatek,dual-boot ] && echo Y)
+
+	if [ x"${dual_boot}" != xY ]; then
+		emmc_copy_config
+		return
+	fi
+
+	local reserve_rootfs_data=$([ -f /sys/firmware/devicetree/base/mediatek,reserve-rootfs_data ] && echo Y)
+	if [ x"${reserve_rootfs_data}" = xY ]; then
+		# Do not touch rootfs_data
+		return
+	fi
+
+	local rootfs_data_dev=$(cat /sys/firmware/devicetree/base/mediatek,rootfs_data-part 2>/dev/null)
+	if [ -z "${rootfs_data_dev}" -o $? -ne 0 ]; then
+		emmc_copy_config
+		return
+	fi
+
+	rootfs_data_dev=$(block_dev_path "${rootfs_data_dev}")
+	[ -z "${rootfs_data_dev}" -o $? -ne 0 ] && return 1
+
+	dd if="$UPGRADE_BACKUP" of="${rootfs_data_dev}" bs=512
+}
diff --git a/master/files/target/linux/mediatek/filogic/base-files/lib/upgrade/mtk_nand.sh b/master/files/target/linux/mediatek/filogic/base-files/lib/upgrade/mtk_nand.sh
new file mode 100644
index 0000000..1b944dc
--- /dev/null
+++ b/master/files/target/linux/mediatek/filogic/base-files/lib/upgrade/mtk_nand.sh
@@ -0,0 +1,218 @@
+# Script for dual image A/B system boots
+# Copyright (C) 2024 MediaTek Inc.
+# Author: Weijie Gao <weijie.gao@mediatek.com>
+
+ubi_prepare_u_boot_env() {
+	local ubidev="$1"
+
+	local env_size=$(cat /sys/firmware/devicetree/base/mediatek,env-size 2>/dev/null)
+	[ -z "${env_size}" ] && return
+
+	local env_vol=$(cat /sys/firmware/devicetree/base/mediatek,env-ubi-volume 2>/dev/null)
+	if [ -n "${env_vol}" ]; then
+		local env_ubivol="$( nand_find_volume $ubidev $env_vol )"
+		[ -z "$env_ubivol" ] && ubimkvol /dev/$ubidev -N ${env_vol} -s ${env_size} 2>/dev/null || :
+	fi
+
+	local env2_vol=$(cat /sys/firmware/devicetree/base/mediatek,env-ubi-volume-redund 2>/dev/null)
+	if [ -n "${env2_vol}" ]; then
+		local env2_ubivol="$( nand_find_volume $ubidev $env2_vol )"
+		[ -z "$env2_ubivol" ] && ubimkvol /dev/$ubidev -N ${env2_vol} -s ${env_size} 2>/dev/null || :
+	fi
+}
+
+ubi_dual_boot_restore_config() {
+	local rootfs_data_vol=$(cat /sys/firmware/devicetree/base/mediatek,upgrade-rootfs_data-part 2>/dev/null)
+	local ubidev=$( nand_find_ubi "${CI_ROOT_UBIPART:-$CI_UBIPART}" )
+	local ubivol="$( nand_find_volume $ubidev $rootfs_data_vol )"
+	if [ ! "$ubivol" ]; then
+		ubivol="$( nand_find_volume $ubidev "$CI_ROOTPART" )"
+		if [ ! "$ubivol" ]; then
+			echo "cannot find ubifs data volume"
+			return 1
+		fi
+	fi
+	mkdir /tmp/new_root
+	if ! mount -t ubifs /dev/$ubivol /tmp/new_root; then
+		echo "cannot mount ubifs volume $ubivol"
+		rmdir /tmp/new_root
+		return 1
+	fi
+	if mv "$1" "/tmp/new_root/$BACKUP_FILE"; then
+		if umount /tmp/new_root; then
+			echo "configuration saved"
+			rmdir /tmp/new_root
+			return 0
+		fi
+	else
+		umount /tmp/new_root
+	fi
+	echo "could not save configuration to ubifs volume $ubivol"
+	rmdir /tmp/new_root
+	return 1
+}
+
+ubi_dual_boot_do_restore_config() {
+	local conf_tar="/tmp/sysupgrade.tgz"
+	[ ! -f "$conf_tar" ] || ubi_dual_boot_restore_config "$conf_tar"
+}
+
+ubi_dual_boot_do_upgrade_success() {
+	if ubi_dual_boot_do_restore_config && sync; then
+		echo "sysupgrade successful"
+		umount -a
+		reboot -f
+	fi
+	nand_do_upgrade_failed
+}
+
+dual_boot_upgrade_prepare_ubi() {
+	local boot_firmware_vol_name="$1"
+	local firmware_vol_name="$2"
+	local firmware_length="$3"
+	local reserve_rootfs_data="$4"
+
+	local ubidev="$( nand_attach_ubi "$CI_UBIPART" 0 )"
+
+	local boot_fw_ubivol="$( nand_find_volume $ubidev $boot_firmware_vol_name )"
+	local fw_ubivol="$( nand_find_volume $ubidev $firmware_vol_name )"
+	local data_ubivol="$( nand_find_volume $ubidev rootfs_data )"
+
+	# remove ubiblocks
+	[ "$boot_fw_ubivol" ] && { nand_remove_ubiblock $boot_fw_ubivol || return 1; }
+	[ "$fw_ubivol" ] && { nand_remove_ubiblock $fw_ubivol || return 1; }
+	[ "$data_ubivol" ] && { nand_remove_ubiblock $data_ubivol || return 1; }
+
+	# kill firmware volume
+	[ "$fw_ubivol" ] && ubirmvol /dev/$ubidev -N "$firmware_vol_name" || :
+
+	local rootfs_data_vol=$(cat /sys/firmware/devicetree/base/mediatek,upgrade-rootfs_data-part 2>/dev/null)
+	if [ x"${reserve_rootfs_data}" != xY ]; then
+		# kill rootfs_data volume
+		[ "$data_ubivol" ] && ubirmvol /dev/$ubidev -N "$rootfs_data_vol" || :
+	fi
+
+	# create firmware vol
+	if ! ubimkvol /dev/$ubidev -N "$firmware_vol_name" -s "$firmware_length"; then
+		echo "cannot create firmware volume"
+		return 1;
+	fi
+
+	# create u-boot environment volume
+	ubi_prepare_u_boot_env $ubidev
+
+	if [ x"${reserve_rootfs_data}" = xY ]; then
+		# Do not touch rootfs_data
+		sync
+		return 0
+	fi
+
+	# create rootfs_data vol
+	local rootfs_data_size=$(cat /sys/firmware/devicetree/base/mediatek,rootfs_data-size-limit 2>/dev/null)
+
+	if [ -n "${rootfs_data_size}" ]; then
+		rootfs_data_length="-s $rootfs_data_size"
+	else
+		rootfs_data_length="-m"
+	fi
+
+	if ! ubimkvol /dev/$ubidev -N "$rootfs_data_vol" "$rootfs_data_length"; then
+		if [ -n "${rootfs_data_size}" ]; then
+			if ! ubimkvol /dev/$root_ubidev -N "$rootfs_data_vol" -m; then
+				echo "cannot initialize $rootfs_data_vol volume"
+				return 1
+			fi
+		else
+			echo "cannot initialize $rootfs_data_vol volume"
+			return 1
+		fi
+	fi
+
+	sync
+	return 0
+}
+
+ubi_dual_boot_upgrade_itb() {
+	local fit_file="$1"
+	local gz="$2"
+
+	local boot_firmware_vol_name=$(cat /sys/firmware/devicetree/base/mediatek,boot-firmware-part 2>/dev/null)
+	[ -z "${boot_firmware_vol_name}" -o $? -ne 0 ] && return 1
+
+	local firmware_vol_name=$(cat /sys/firmware/devicetree/base/mediatek,upgrade-firmware-part 2>/dev/null)
+	[ -z "${firmware_vol_name}" -o $? -ne 0 ] && return 1
+
+	local fit_length=$( (${gz}cat "$fit_file" | wc -c) 2> /dev/null)
+
+	local reserve_rootfs_data=$([ -f /sys/firmware/devicetree/base/mediatek,reserve-rootfs_data ] && echo Y)
+	dual_boot_upgrade_prepare_ubi "${boot_firmware_vol_name}" "${firmware_vol_name}" "${fit_length}" "${reserve_rootfs_data}" || return 1
+
+	local fit_ubidev="$(nand_find_ubi "$CI_UBIPART")"
+	local fit_ubivol="$(nand_find_volume $fit_ubidev "${firmware_vol_name}")"
+
+	${gz}cat "$fit_file" | ubiupdatevol /dev/$fit_ubivol -s "$fit_length" - || return 1
+
+	local upgrade_image_slot=$(cat /sys/firmware/devicetree/base/mediatek,upgrade-image-slot 2>/dev/null)
+	if [ -n "${upgrade_image_slot}" ]; then
+		v "Set new boot image slot to ${upgrade_image_slot}"
+		# Force the creation of fw_printenv.lock
+		mkdir -p /var/lock
+		touch /var/lock/fw_printenv.lock
+		fw_setenv "dual_boot.current_slot" "${upgrade_image_slot}"
+		fw_setenv "dual_boot.slot_${upgrade_image_slot}_invalid" "0"
+	fi
+
+	if [ x"${reserve_rootfs_data}" != xY ]; then
+		# do normal upgrade flow
+		ubi_dual_boot_do_upgrade_success
+	fi
+
+	# Do not touch rootfs_data
+	sync
+
+	echo "sysupgrade successful"
+	umount -a
+	reboot -f
+}
+
+# Write the FIT image to UBI kernel volume
+nand_upgrade_itb() {
+	local fit_file="$1"
+	local gz="$2"
+
+	local fit_length=$( (${gz}cat "$fit_file" | wc -c) 2> /dev/null)
+
+	nand_upgrade_prepare_ubi "" "" "$fit_length" "" || return 1
+
+	local fit_ubidev="$(nand_find_ubi "$CI_UBIPART")"
+	local fit_ubivol="$(nand_find_volume $fit_ubidev "$CI_KERNPART")"
+	${gz}cat "$fit_file" | ubiupdatevol /dev/$fit_ubivol -s "$fit_length" -
+
+	# create u-boot environment volume
+	ubi_prepare_u_boot_env $fit_ubidev
+}
+
+ubi_do_upgrade() {
+	local file=$1
+	local dual_boot=$([ -f /sys/firmware/devicetree/base/mediatek,dual-boot ] && echo Y)
+
+	local gz="$(identify_if_gzip "$file")"
+	local file_type="$(identify "$file" "" "$gz")"
+
+	case "$file_type" in
+		"fit")
+			sync
+			nand_verify_if_gzip_file "$file" "$gz" || return 1
+
+			if [ x"${dual_boot}" != xY ]; then
+				nand_upgrade_itb "$file" "$gz" && nand_do_upgrade_success
+				nand_do_upgrade_failed
+			else
+				ubi_dual_boot_upgrade_itb "$file" "$gz"
+			fi
+			;;
+		*)
+			v "Unsupported firmware type: $file_type"
+			;;
+	esac
+}
diff --git a/master/files/target/linux/mediatek/patches-6.6/999-dual-boot-01-do-not-auto-mount-ubi-rootfs.patch b/master/files/target/linux/mediatek/patches-6.6/999-dual-boot-01-do-not-auto-mount-ubi-rootfs.patch
new file mode 100644
index 0000000..3af2c5c
--- /dev/null
+++ b/master/files/target/linux/mediatek/patches-6.6/999-dual-boot-01-do-not-auto-mount-ubi-rootfs.patch
@@ -0,0 +1,33 @@
+From: Weijie Gao <weijie.gao@mediatek.com>
+Subject: [PATCH] init: do not mount ubi rootfs if dual boot is enabled
+
+Do not mount ubi rootfs if dual boot is enabled.
+UBIFS-type rootfs volume will be specified by U-Boot.
+
+--- a/init/do_mounts.c
++++ b/init/do_mounts.c
+@@ -19,6 +19,7 @@
+ #include <linux/ramfs.h>
+ #include <linux/shmem_fs.h>
+ #include <linux/ktime.h>
++#include <linux/of.h>
+ 
+ #include <linux/nfs_fs.h>
+ #include <linux/nfs_fs_sb.h>
+@@ -252,9 +253,16 @@ out:
+ #ifdef CONFIG_MTD_ROOTFS_ROOT_DEV
+ static int __init mount_ubi_rootfs(void)
+ {
++	struct device_node *np;
+ 	int flags = MS_SILENT;
+ 	int err, tried = 0;
+ 
++	np = of_find_node_by_path("/");
++	if (np) {
++		if (of_property_read_bool(np, "mediatek,dual-boot"))
++			return -EINVAL;
++	}
++
+ 	while (tried < 2) {
+ 		err = do_mount_root("ubi0:rootfs", "ubifs", flags, \
+ 					root_mount_data);
diff --git a/master/files/target/linux/mediatek/patches-6.6/999-dual-boot-02-ubi-allow-no-default-rootdev.patch b/master/files/target/linux/mediatek/patches-6.6/999-dual-boot-02-ubi-allow-no-default-rootdev.patch
new file mode 100644
index 0000000..8c2522b
--- /dev/null
+++ b/master/files/target/linux/mediatek/patches-6.6/999-dual-boot-02-ubi-allow-no-default-rootdev.patch
@@ -0,0 +1,25 @@
+From: Weijie Gao <weijie.gao@mediatek.com>
+Subject: [PATCH] mtd: ubi: allow bypassing setting default rootdev
+
+Add control to bypass setting rootdev to ubi volume
+
+--- a/drivers/mtd/ubi/block.c
++++ b/drivers/mtd/ubi/block.c
+@@ -94,6 +94,9 @@ static DEFINE_IDR(ubiblock_minor_idr);
+ static DEFINE_MUTEX(devices_mutex);
+ static int ubiblock_major;
+ 
++static bool no_default_rootdev;
++module_param(no_default_rootdev, bool, 0444);
++
+ static int __init ubiblock_set_param(const char *val,
+ 				     const struct kernel_param *kp)
+ {
+@@ -435,6 +438,7 @@ int ubiblock_create(struct ubi_volume_in
+ 
+ 	if (!strcmp(vi->name, "rootfs") &&
+ 	    IS_ENABLED(CONFIG_MTD_ROOTFS_ROOT_DEV) &&
++	    !no_default_rootdev &&
+ 	    ROOT_DEV == 0) {
+ 		pr_notice("ubiblock: device ubiblock%d_%d (%s) set to be root filesystem\n",
+ 			  dev->ubi_num, dev->vol_id, vi->name);
diff --git a/master/files/target/linux/mediatek/patches-6.6/999-dual-boot-03-fitblk-do-not-split-rootfs_data-if-required.patch b/master/files/target/linux/mediatek/patches-6.6/999-dual-boot-03-fitblk-do-not-split-rootfs_data-if-required.patch
new file mode 100644
index 0000000..9a4ca14
--- /dev/null
+++ b/master/files/target/linux/mediatek/patches-6.6/999-dual-boot-03-fitblk-do-not-split-rootfs_data-if-required.patch
@@ -0,0 +1,20 @@
+From: Weijie Gao <weijie.gao@mediatek.com>
+Subject: [PATCH] block: fitblk: allow bypassing creation of fitrw
+
+Add control to bypass creating fitrw
+
+--- a/drivers/block/fitblk.c
++++ b/drivers/block/fitblk.c
+@@ -579,6 +579,12 @@ static int parse_fit_on_dev(struct devic
+ 		 slot, (slot > 1)?"s":"", (slot > 1)?"[0...":"", slot - 1,
+ 		 (slot > 1)?"]":"");
+ 
++	np = of_find_node_by_path("/");
++	if (np) {
++		if (of_property_read_bool(np, "mediatek,no-split-fitrw"))
++			goto out_bootconf;
++	}
++
+ 	/* in case uImage.FIT is stored in a partition, map the remaining space */
+ 	if (!bdev->bd_read_only && bdev_is_partition(bdev) &&
+ 	    (imgmaxsect + MIN_FREE_SECT) < dsectors) {