Mounting process of ubi root file system under SigmaStar SSD202 openwrt system

Posted by Nymphetamine on Tue, 08 Feb 2022 19:13:41 +0100

For the introduction of UBI, please refer to the official documents

http://www.linux-mtd.infradead.org/doc/ubifs.html

The following is a brief introduction. UBIFS relies on the kernel UBI subsystem and runs on MTD devices. UBI can manage its own partitions in applications

For an introduction to SSD202, please refer to another blog post

At present, several domestic manufacturers are promoting the use of relevant core boards, and I currently use the development board of Qiming cloud (the openwrt system has been transplanted to Qiming cloud, on which I will conduct relevant analysis)

First look at the system partition:

root@wireless-tag:/# cat /proc/mtd 
dev:    size   erasesize  name
mtd0: 00060000 00020000 "IPL0"
mtd1: 00060000 00020000 "IPL1"
mtd2: 00060000 00020000 "IPL_CUST0"
mtd3: 00060000 00020000 "IPL_CUST1"
mtd4: 000c0000 00020000 "UBOOT0"
mtd5: 000c0000 00020000 "UBOOT1"
mtd6: 00060000 00020000 "ENV0"
mtd7: 00020000 00020000 "KEY_CUST"
mtd8: 00060000 00020000 "LOGO"
mtd9: 00060000 00020000 "wtinfo"
mtd10: 03000000 00020000 "ubi"
mtd11: 03000000 00020000 "ubi2"
mtd12: 09a80000 00020000 "opt"

Openwrt system has a feature that Kernel and ROOTFS are packaged together and support overlayfs. The corresponding partition is UBI, and UBI2 is the backup partition,

It can be used in two places: when uboot fails to start and when sysupgrade upgrades the system. The specific and detailed analysis will not be expanded here, and will be introduced separately later when free

To analyze the execution process of a system or a module, the operation log is a good entry point. First, let's take a look at the operation log related to UBI

[    1.971721] UBI: auto-attach mtd10
[    1.974967] ubi0: attaching mtd10
[    2.021832] UBI: EOF marker found, PEBs from 65 will be erased
[    2.097352] ubi0: scanning is finished
[    2.125345] ubi0: volume 2 ("rootfs_data") re-sized from 9 to 277 LEBs
[    2.132520] ubi0: attached mtd10 (name "ubi", size 48 MiB)
[    2.137870] ubi0: PEB size: 131072 bytes (128 KiB), LEB size: 126976 bytes
[    2.144726] ubi0: min./max. I/O unit sizes: 2048/2048, sub-page size 2048
[    2.151529] ubi0: VID header offset: 2048 (aligned 2048), data offset: 4096
[    2.158487] ubi0: good PEBs: 384, bad PEBs: 0, corrupted PEBs: 0
[    2.164483] ubi0: user volume: 3, internal volumes: 1, max. volumes count: 128
[    2.171724] ubi0: max/mean erase counter: 1/0, WL threshold: 4096, image sequence number: 263505868
[    2.180769] ubi0: available PEBs: 0, total reserved PEBs: 384, PEBs reserved for bad PEB handling: 40
[    2.190013] ubi0: background thread "ubi_bgt0d" started, PID 504
[    2.205930] block ubiblock0_1: created from ubi0:1(rootfs)
[    2.211260] ubiblock: device ubiblock0_1 (rootfs) set to be root filesystem

There are three core areas

First, UBI: Auto attach mtd10. Auto attach is called auto mount. Before understanding this, we have to take a look at the "rootfstype=squashfs,ubifs rootwait=1" related to rootfs in cmdline. Only the type is specified in the startup parameter, and rootdev is not specified, so auto attach here is particularly important

Second, ubi0: user volume: 3, internal volumes: 1, max. volumes count: 128, indicating that ubi0 has three volumes. This is a feature of ubifs, which is also similar to openwrt UBI The three volumes are kernel, rootfs and rootfs_ Data (i.e. overlay)

Third, ubiblock: device ubiblock0_1 (rootfs) set to be root filesystem, that is, the rootfs volume of ubi0 is mounted as the rootfs of the system

It can be seen from the initial rootfs partition that the system will be automatically mounted. Then, it can be seen from the initial rootfs partition that the system will be automatically mounted

The following is a detailed analysis of these three places:

1, Auto mount ubi partition ubi: Auto attach mtd10

The main mechanism can be seen from a patch file 490 UBI auto attach MTD device named UBI or data on boot patch

From: Daniel Golle <daniel@makrotopia.org>
Subject: ubi: auto-attach mtd device named "ubi" or "data" on boot

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
 drivers/mtd/ubi/build.c | 36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)

--- a/drivers/mtd/ubi/build.c
+++ b/drivers/mtd/ubi/build.c
@@ -1226,6 +1226,73 @@ static struct mtd_info * __init open_mtd
 	return mtd;
 }
 
+/*
+ * This function tries attaching mtd partitions named either "ubi" or "data"
+ * during boot.
+ */
+static void __init ubi_auto_attach(void)
+{
+	int err;
+	struct mtd_info *mtd;
+	loff_t offset = 0;
+	size_t len;
+	char magic[4];
+
+	/* try attaching mtd device named "ubi" or "data" */
+	mtd = open_mtd_device("ubi");
+	if (IS_ERR(mtd))
+		mtd = open_mtd_device("data");
+
+	if (IS_ERR(mtd))
+		return;
+
+	/* get the first not bad block */
+	if (mtd_can_have_bb(mtd))
+		while (mtd_block_isbad(mtd, offset)) {
+			offset += mtd->erasesize;
+
+			if (offset > mtd->size) {
+				pr_err("UBI error: Failed to find a non-bad "
+				       "block on mtd%d\n", mtd->index);
+				goto cleanup;
+			}
+		}
+
+	/* check if the read from flash was successful */
+	err = mtd_read(mtd, offset, 4, &len, (void *) magic);
+	if ((err && !mtd_is_bitflip(err)) || len != 4) {
+		pr_err("UBI error: unable to read from mtd%d\n", mtd->index);
+		goto cleanup;
+	}
+
+	/* check for a valid ubi magic */
+	if (strncmp(magic, "UBI#", 4)) {
+		pr_err("UBI error: no valid UBI magic found inside mtd%d\n", mtd->index);
+		goto cleanup;
+	}
+
+	/* don't auto-add media types where UBI doesn't makes sense */
+	if (mtd->type != MTD_NANDFLASH &&
+	    mtd->type != MTD_NORFLASH &&
+	    mtd->type != MTD_DATAFLASH &&
+	    mtd->type != MTD_MLCNANDFLASH)
+		goto cleanup;
+
+	mutex_lock(&ubi_devices_mutex);
+	pr_notice("UBI: auto-attach mtd%d\n", mtd->index);
+	err = ubi_attach_mtd_dev(mtd, UBI_DEV_NUM_AUTO, 0, 0);
+	mutex_unlock(&ubi_devices_mutex);
+	if (err < 0) {
+		pr_err("UBI error: cannot attach mtd%d\n", mtd->index);
+		goto cleanup;
+	}
+
+	return;
+
+cleanup:
+	put_mtd_device(mtd);
+}
+
 static int __init ubi_init(void)
 {
 	int err, i, k;
@@ -1309,6 +1376,12 @@ static int __init ubi_init(void)
 		}
 	}
 
+	/* auto-attach mtd devices only if built-in to the kernel and no ubi.mtd
+	 * parameter was given */
+	if (IS_ENABLED(CONFIG_MTD_ROOTFS_ROOT_DEV) &&
+	    !ubi_is_module() && !mtd_devs)
+		ubi_auto_attach();
+
 	err = ubiblock_init();
 	if (err) {
 		pr_err("UBI error: block: cannot initialize, error %d", err);

Its execution process is in ubi_ Triggered in init, the kernel needs to open CONFIG_MTD_ROOTFS_ROOT_DEV supports automatic search of partitions named ubi or data, and then automatic attach

2, Load rootfs volume

The process is also a patch file 491 UBI auto create ubiblock device for rootfs patch

From: Daniel Golle <daniel@makrotopia.org>
Subject: ubi: auto-create ubiblock device for rootfs

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
 drivers/mtd/ubi/block.c | 42 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)

--- a/drivers/mtd/ubi/block.c
+++ b/drivers/mtd/ubi/block.c
@@ -635,6 +635,44 @@ static void __init ubiblock_create_from_
 	}
 }
 
+#define UBIFS_NODE_MAGIC  0x06101831
+static inline int ubi_vol_is_ubifs(struct ubi_volume_desc *desc)
+{
+	int ret;
+	uint32_t magic_of, magic;
+	ret = ubi_read(desc, 0, (char *)&magic_of, 0, 4);
+	if (ret)
+		return 0;
+	magic = le32_to_cpu(magic_of);
+	return magic == UBIFS_NODE_MAGIC;
+}
+
+static void __init ubiblock_create_auto_rootfs(void)
+{
+	int ubi_num, ret, is_ubifs;
+	struct ubi_volume_desc *desc;
+	struct ubi_volume_info vi;
+
+	for (ubi_num = 0; ubi_num < UBI_MAX_DEVICES; ubi_num++) {
+		desc = ubi_open_volume_nm(ubi_num, "rootfs", UBI_READONLY);
+		if (IS_ERR(desc))
+			continue;
+
+		ubi_get_volume_info(desc, &vi);
+		is_ubifs = ubi_vol_is_ubifs(desc);
+		ubi_close_volume(desc);
+		if (is_ubifs)
+			break;
+
+		ret = ubiblock_create(&vi);
+		if (ret)
+			pr_err("UBI error: block: can't add '%s' volume, err=%d\n",
+				vi.name, ret);
+		/* always break if we get here */
+		break;
+	}
+}
+
 static void ubiblock_remove_all(void)
 {
 	struct ubiblock *next;
@@ -667,6 +705,10 @@ int __init ubiblock_init(void)
 	 */
 	ubiblock_create_from_param();
 
+	/* auto-attach "rootfs" volume if existing and non-ubifs */
+	if (IS_ENABLED(CONFIG_MTD_ROOTFS_ROOT_DEV))
+		ubiblock_create_auto_rootfs();
+
 	/*
 	 * Block devices are only created upon user requests, so we ignore
 	 * existing volumes.

In the function ubiblock_ Ubiblock in init_ create_ auto_ Rootfs, hard coded as rootfs, with clear comments / * Auto attach "rootfs" volume if existing and non UBIFS*/

3, Set root file system

Before analyzing, we need to understand the root file system loading process of linux. devtmpfs process is introduced here. devtmpfs is introduced on the Internet, which is roughly to automatically create / dev/xxx file during kernel startup

There are only two logs about devtmpfs during system operation:

[    0.636686] devtmpfs: initialized
...
[    2.321714] devtmpfs: mounted

Devtmpfs: the initialized execution time is after the cpu is started, specifically in the driver_ During init initialization

driver_init -->devtmpfs_init-->register_filesystem

static struct file_system_type dev_fs_type = {
	.name = "devtmpfs",
	.mount = dev_mount,
	.kill_sb = kill_litter_super,
};

After the devtmpfs environment is ready, the VFS initialization process and MTD partition retrieval are available

The mount entry of linux root file system is prepare_namespace

The specific process is as follows:

kernel_init --> kernel_init_freeable --> prepare_namespace

/*
 * Prepare the namespace - decide what/where to mount, load ramdisks, etc.
 */
void __init prepare_namespace(void)
{
	int is_floppy;

	printk(KERN_INFO "prepare_namespace root_delay=%d, cmd_root_dev [%s]\n", 
		root_delay, saved_root_name);

	if (root_delay) {
		printk(KERN_INFO "Waiting %d sec before mounting root device...\n",
		       root_delay);
		ssleep(root_delay);
	}

	/*
	 * wait for the known devices to complete their probing
	 *
	 * Note: this is a potential source of long boot delays.
	 * For example, it is not atypical to wait 5 seconds here
	 * for the touchpad of a laptop to initialize.
	 */
	wait_for_device_probe();

	md_run_setup();

	if (saved_root_name[0]) {
		root_device_name = saved_root_name;
		if (!strncmp(root_device_name, "mtd", 3) ||
		    !strncmp(root_device_name, "ubi", 3)) {
			mount_block_root(root_device_name, root_mountflags);
			goto out;
		}
		ROOT_DEV = name_to_dev_t(root_device_name);
		if (strncmp(root_device_name, "/dev/", 5) == 0)
			root_device_name += 5;
	}

	if (initrd_load())
		goto out;

	/* wait for any asynchronous scanning to complete */
	if ((ROOT_DEV == 0) && root_wait) {
		printk(KERN_INFO "Waiting for root device %s...\n",
			saved_root_name);
		while (driver_probe_done() != 0 ||
			(ROOT_DEV = name_to_dev_t(saved_root_name)) == 0)
			msleep(100);
		async_synchronize_full();
	}

	is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR;

	if (is_floppy && rd_doload && rd_load_disk(0))
		ROOT_DEV = Root_RAM0;

	mount_root();
out:
	devtmpfs_mount("dev");
	sys_mount(".", "/", NULL, MS_MOVE, NULL);
	sys_chroot(".");
}

There are several key parameters:

root, rootwait, rootdelay, rootfstype in cmdline

Root specifies the mount partition. In the auto attach chapter, if the root device is specified in the cmdline, there is no need for automatic detection. Of course, in the openwrt system, kernel and rootfs are integrated, and the setting of automatic detection is related to its unique firmware packaging method;

Generally speaking, when rootfs occupies an independent mtd partition, the boot parameter specifies the root partition

Rootfstype, which indicates the mount type. In openwrt, "rootfstype=squashfs,ubifs" is passed“

Detailed log reference:

[    2.269712] prepare_namespace root_delay=0, cmd_root_dev []
[    2.275130] VFS: under CONFIG_MTD_ROOTFS_ROOT_DEV to mount_ubi_rootfs
[    2.287905] VFS: to do_mount_root [squashfs] from cmdlines rootfstype
[    2.304145] VFS: Mounted root (squashfs filesystem) readonly on device 254:0.
[    2.321714] devtmpfs: mounted to [dev]

The main mounting process is in mount_ In root

void __init mount_root(void)
{
#ifdef CONFIG_ROOT_NFS
	if (ROOT_DEV == Root_NFS) {
		if (mount_nfs_root())
			return;

		printk(KERN_ERR "VFS: Unable to mount root fs via NFS, trying floppy.\n");
		ROOT_DEV = Root_FD0;
	}
#endif
#ifdef CONFIG_BLK_DEV_FD
	if (MAJOR(ROOT_DEV) == FLOPPY_MAJOR) {
		/* rd_doload is 2 for a dual initrd/ramload setup */
		if (rd_doload==2) {
			if (rd_load_disk(1)) {
				ROOT_DEV = Root_RAM1;
				root_device_name = NULL;
			}
		} else
			change_floppy("root floppy");
	}
#endif
#ifdef CONFIG_MTD_ROOTFS_ROOT_DEV
	printk(KERN_NOTICE "VFS: under CONFIG_MTD_ROOTFS_ROOT_DEV to mount_ubi_rootfs\n");
	if (!mount_ubi_rootfs())
		return;
#endif
#ifdef CONFIG_BLOCK
	{
		int err = create_dev("/dev/root", ROOT_DEV);

		if (err < 0)
			pr_emerg("Failed to create /dev/root: %d\n", err);
		mount_block_root("/dev/root", root_mountflags);
	}
#endif
}

Because config is enabled_ MTD_ ROOTFS_ ROOT_ Dev is preceded by the automatic attach of UBI, so you can directly mount ubifs here

static int __init mount_ubi_rootfs(void)
{
	int flags = MS_SILENT;
	int err, tried = 0;

	while (tried < 2) {
		err = do_mount_root("ubi0:rootfs", "ubifs", flags, \
					root_mount_data);
		switch (err) {
			case -EACCES:
				flags |= MS_RDONLY;
				tried++;
				break;
			default:
				return err;
		}
	}

	return -EINVAL;
}

At this point, the UBI root file system is mounted

4, About overlayfs mount

Mainly in the mount of openwrt_ In root, the log is

[    4.376589] mount_root: overlay filesystem has not been fully initialized yet
[    4.383875] mount_root: switching to ubifs overlay

There are a lot of information about the specific overlay mechanism in openwrt, which will not be introduced here

 

 

Topics: Linux ARM openwrt