关于UBI介绍可以参考官方文档
http://www.linux-mtd.infradead.org/doc/ubifs.html
下面是一张简介图,大概的介绍就是UBIFS依赖kernel UBI子系统,运行在MTD设备之上,应用上UBI可做自己的分区管理
SSD202的简介可以参考我另一篇博文
目前国内有几个厂家在推广使用且出了相关的核心板,我目前使用的是启明云端的开发板(启明云端已移植好openwrt系统,我在此之上进行相关分析)
首先看系统分区:
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系统有个特点,Kernel和ROOTFS是一起打包的,且支持overlayfs,对应的分区为UBI,UBI2是备份分区,
其使用地方有两个:在uboot启动失败时使用和sysupgrade升级系统时使用,具体详细的分析在此不展开,后面有空再单独介绍
分析一个系统或者一个模块的执行过程,运行日志是很好的切入点,首先我们看下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
其核心地方有3个
第一,UBI: auto-attach mtd10, auto-attach 名为自动挂载,理解这个之前我们得先看一下cmdline关于rootfs相关的部分“rootfstype=squashfs,ubifs rootwait=1”,启动参数中只指定了类型,没有指定rootdev,所以此处的auto-attach尤其重要
第二,ubi0: user volume: 3, internal volumes: 1, max. volumes count: 128,说明ubi0有3个卷,此处为ubifs的一个特点,也跟openwrt ubi.img制作的方式有关,3个卷分别为kernel,rootfs和rootfs_data(也就是overlay)
第三,ubiblock: device ubiblock0_1 (rootfs) set to be root filesystem, 即ubi0的rootfs卷被挂载为系统的rootfs
从这3处初略可以看出系统是如何挂载哪里的rootfs,大致就是自动找到有roofs的ubi分区,然后自动attach,接着将其挂载为系统rootfs
下面针对这3个地方进行详细分析:
一、自动挂载ubi分区UBI: auto-attach mtd10
其主要机制从一个patch文件即可看出 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);
其执行过程在ubi_init中触发,需要内核开启CONFIG_MTD_ROOTFS_ROOT_DEV支持,自动查找名为ubi或data的分区,然后自动进行attach
二、加载rootfs卷
其过程也是一个patch文件 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.
在函数ubiblock_init中进行ubiblock_create_auto_rootfs,硬编码为rootfs,注释已非常明确 /* auto-attach "rootfs" volume if existing and non-ubifs */
三、设置根文件系统
在分析之前我们先需要了解一下linux的根文件系统加载过程,此处介绍devtmpfs过程,devtmpfs网上介绍很多,大致就是在内核启动过程中自动创建/dev/xxx文件
系统运行过程中关于devtmpfs的日志只有两条:
[ 0.636686] devtmpfs: initialized
...
[ 2.321714] devtmpfs: mounted
devtmpfs: initialized 执行时机为cpu启动之后,具体在driver_init初始化的过程中
driver_init -->devtmpfs_init-->register_filesystem
static struct file_system_type dev_fs_type = {
.name = "devtmpfs",
.mount = dev_mount,
.kill_sb = kill_litter_super,
};
在devtmpfs环境准备好之后,才有VFS初始化过程以及MTD分区检索
linux根文件系统挂载入口为prepare_namespace
具体过程为:
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(".");
}
其中有几个关键的参数:
cmdline中的root,rootwait,rootdelay,rootfstype
root指定挂载分区,在 auto-attach 章节里,如果cmdline中指定了root设备,那么就不用自动探测,当然在openwrt系统中,kernel和rootfs是一体的,设置自动探测与其特有的固件打包方式有关;
一般来说,当rootfs占有一个独立的mtd分区的时候,启动参数才指定root分区
rootfstype,说明挂载类型,在openwrt中传递的是“rootfstype=squashfs,ubifs“
详细日志参照:
[ 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]
主要挂载过程在mount_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
}
由于使能了CONFIG_MTD_ROOTFS_ROOT_DEV,前面有UBI的自动attach,因此在此处直接挂载ubifs
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;
}
至此UBI根文件系统挂载完毕
四、关于overlayfs挂载
主要在openwrt的mount_root中,日志为
[ 4.376589] mount_root: overlay filesystem has not been fully initialized yet
[ 4.383875] mount_root: switching to ubifs overlay
具体overlay机制,openwrt中资料很多,在此不作介绍