前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >android rootfs的挂载流程[通俗易懂]

android rootfs的挂载流程[通俗易懂]

作者头像
全栈程序员站长
发布2022-11-08 17:48:53
2.4K0
发布2022-11-08 17:48:53
举报
文章被收录于专栏:全栈程序员必看

一、ramdisk的制作

out/host/linux-x86/bin/mkbootfs out/target/product//root | out/host/linux-x86/bin/minigzip > out/target/product//ramdisk.img 上述命令分两步进行: 1.out/host/linux-x86/bin/mkbootfs out/target/product/*/root 生成一个cpio文件,利用cpio 可将文件或目录从文件库获取出来或将散列文件拷贝到文件库。 2.out/host/linux-x86/bin/minigzip 将生成的cpio文件压缩成一个gzip格式的文件“out/target/product/*/ramdisk.img“

二、uboot加载ramdisk到内存

代码语言:javascript
复制
int boot_ramdisk_high(struct lmb *lmb, ulong rd_data, ulong rd_len,
ulong *initrd_start, ulong *initrd_end)
{
char    *s;
ulong   initrd_high;
int initrd_copy_to_ram = 1;
if ((s = getenv("initrd_high")) != NULL) {
/* a value of "no" or a similar string will act like 0, * turning the "load high" feature off. This is intentional. */
initrd_high = simple_strtoul(s, NULL, 16);
if (initrd_high == ~0)
initrd_copy_to_ram = 0;
} else {
initrd_high = getenv_bootm_mapsize() + getenv_bootm_low();
}
#ifdef CONFIG_LOGBUFFER
/* Prevent initrd from overwriting logbuffer */
lmb_reserve(lmb, logbuffer_base() - LOGBUFF_OVERHEAD, LOGBUFF_RESERVE);
#endif
debug("## initrd_high = 0x%08lx, copy_to_ram = %d\n",
initrd_high, initrd_copy_to_ram);
if (rd_data) {
if (!initrd_copy_to_ram) {  /* zero-copy ramdisk support */
debug(" in-place initrd\n");
*initrd_start = rd_data;
*initrd_end = rd_data + rd_len;
lmb_reserve(lmb, rd_data, rd_len);
} else {
if (initrd_high)
*initrd_start = (ulong)lmb_alloc_base(lmb,
rd_len, 0x1000, initrd_high);
else
*initrd_start = (ulong)lmb_alloc(lmb, rd_len,
0x1000);
if (*initrd_start == 0) {
puts("ramdisk - allocation error\n");
goto error;
}
bootstage_mark(BOOTSTAGE_ID_COPY_RAMDISK);
*initrd_end = *initrd_start + rd_len;
printf(" Loading Ramdisk to %08lx, end %08lx ... ",
*initrd_start, *initrd_end);
/*把ramdisk拷贝到指定的地址*/
memmove_wd((void *)*initrd_start,
(void *)rd_data, rd_len, CHUNKSZ);
#ifdef CONFIG_MP
/*
* Ensure the image is flushed to memory to handle
* AMP boot scenarios in which we might not be
* HW cache coherent
*/
flush_cache((unsigned long)*initrd_start, rd_len);
#endif
puts("OK\n");
}
} else {
*initrd_start = 0;
*initrd_end = 0;
}
debug(" ramdisk load start = 0x%08lx, ramdisk load end = 0x%08lx\n",
*initrd_start, *initrd_end);
return 0;
error:
return -1;
}

三、uboot通知内核ramdisk在内存中的地址

内核挂载rootfs之后需要将ramdisk中的解压到rootfs中,因此内核必须知道ramdisk在内存上的地址。已知有三种方式可以通知内核ramdisk的位置。 1.通过cmdline传入 2.通过setup_initrd_tag函数把initrd_start设置到内核 tag中,内核通过parse_tag解析。相关代码如下:

代码语言:javascript
复制
static void setup_initrd_tag(bd_t *bd, ulong initrd_start, ulong initrd_end)
{
/* an ATAG_INITRD node tells the kernel where the compressed * ramdisk can be found. ATAG_RDIMG is a better name, actually. */
params->hdr.tag = ATAG_INITRD2;
params->hdr.size = tag_size (tag_initrd);
params->u.initrd.start = initrd_start;
params->u.initrd.size = initrd_end - initrd_start;
params = tag_next (params);
}
static void boot_jump_linux(bootm_headers_t *images, int flag)
{
#ifdef CONFIG_ARM64
void (*kernel_entry)(void *fdt_addr, void *res0, void *res1,
void *res2);
int fake = (flag & BOOTM_STATE_OS_FAKE_GO);
kernel_entry = (void (*)(void *fdt_addr, void *res0, void *res1,
void *res2))images->ep;
debug("## Transferring control to Linux (at address %lx)...\n",
(ulong) kernel_entry);
bootstage_mark(BOOTSTAGE_ID_RUN_OS);
announce_and_cleanup(fake);
if (!fake) {
do_nonsec_virt_switch();
kernel_entry(images->ft_addr, NULL, NULL, NULL);
}
#else
unsigned long machid = gd->bd->bi_arch_number;
char *s;
void (*kernel_entry)(int zero, int arch, uint params);
unsigned long r2;
int fake = (flag & BOOTM_STATE_OS_FAKE_GO);
kernel_entry = (void (*)(int, int, uint))images->ep;
s = getenv("machid");
if (s) {
if (strict_strtoul(s, 16, &machid) < 0) {
debug("strict_strtoul failed!\n");
return;
}
printf("Using machid 0x%lx from environment\n", machid);
}
debug("## Transferring control to Linux (at address %08lx)" \
"...\n", (ulong) kernel_entry);
bootstage_mark(BOOTSTAGE_ID_RUN_OS);
announce_and_cleanup(fake);
if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len)
/*使用dtb*/
r2 = (unsigned long)images->ft_addr;
else
/*不使用dtb*/
r2 = gd->bd->bi_boot_params;
if (!fake) {
#ifdef CONFIG_ARMV7_NONSEC
if (armv7_boot_nonsec()) {
armv7_init_nonsec();
secure_ram_addr(_do_nonsec_entry)(kernel_entry,
0, machid, r2);
} else
#endif
/*启动内核时把tag的地址通过参数传入,内核可以通过该地址解析bootloader传入的tag,包括ramdisk的addr及size*/
kernel_entry(0, machid, r2);
}
#endif
}

3.通过dtb传入,通过fdt_initrd函数设置一个“linux,initrd-start”和“linux,initrd-end”的chose uboot设置ramdisk地址到dtb中。这里需要知道,bootloader是可以修改dtb中的内容的。

代码语言:javascript
复制
int fdt_initrd(void *fdt, ulong initrd_start, ulong initrd_end)
{
int   nodeoffset;
int   err, j, total;
int is_u64;
uint64_t addr, size;
/* just return if the size of initrd is zero */
if (initrd_start == initrd_end)
return 0;
/* find or create "/chosen" node. */
nodeoffset = fdt_find_or_add_subnode(fdt, 0, "chosen");
if (nodeoffset < 0)
return nodeoffset;
total = fdt_num_mem_rsv(fdt);
/*
* Look for an existing entry and update it.  If we don't find
* the entry, we will j be the next available slot.
*/
for (j = 0; j < total; j++) {
err = fdt_get_mem_rsv(fdt, j, &addr, &size);
if (addr == initrd_start) {
fdt_del_mem_rsv(fdt, j);
break;
}
}
err = fdt_add_mem_rsv(fdt, initrd_start, initrd_end - initrd_start);
if (err < 0) {
printf("fdt_initrd: %s\n", fdt_strerror(err));
return err;
}
is_u64 = (fdt_address_cells(fdt, 0) == 2);
/*往dtb中添加一个"linux,initrd-start"属性,内核通过解析dtb可以获知ramdisk的地址*/
err = fdt_setprop_uxx(fdt, nodeoffset, "linux,initrd-start",
(uint64_t)initrd_start, is_u64);
if (err < 0) {
printf("WARNING: could not set linux,initrd-start %s.\n",
fdt_strerror(err));
return err;
}
/*往dtb中添加一个"linux,initrd-end"属性,内核通过解析dtb,结合"linux,initrd-start"可以获知dtb的size*/
err = fdt_setprop_uxx(fdt, nodeoffset, "linux,initrd-end",
(uint64_t)initrd_end, is_u64);
if (err < 0) {
printf("WARNING: could not set linux,initrd-end %s.\n",
fdt_strerror(err));
return err;
}
return 0;
}

四、内核解析ramdisk地址

代码语言:javascript
复制
static void __init early_init_dt_check_for_initrd(unsigned long node)
{
u64 start, end;
unsigned long len;
__be32 *prop;
pr_debug("Looking for initrd properties... ");
prop = of_get_flat_dt_prop(node, "linux,initrd-start", &len);//读取ramdisk在内存的起始地址
if (!prop)
return;
start = of_read_number(prop, len/4);
prop = of_get_flat_dt_prop(node, "linux,initrd-end", &len);//读取ramdisk在内存的结束地址
if (!prop)
return;
end = of_read_number(prop, len/4);
initrd_start = (unsigned long)__va(start);
initrd_end = (unsigned long)__va(end);
initrd_below_start_ok = 1;
printk("initrd_start=0x%llx initrd_end=0x%llx\n",
(unsigned long long)start, (unsigned long long)end);
}

五、内核挂载rootfs

代码语言:javascript
复制
void __init vfs_caches_init(unsigned long mempages)
{
unsigned long reserve;
/* Base hash sizes on available memory, with a reserve equal to 150% of current kernel size */
reserve = min((mempages - nr_free_pages()) * 3/2, mempages - 1);
mempages -= reserve;
names_cachep = kmem_cache_create("names_cache", PATH_MAX, 0,
SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);
dcache_init();
inode_init();
files_init(mempages);
mnt_init();
bdev_cache_init();
chrdev_init();
}
void __init mnt_init(void)
{
unsigned u;
int err;
init_rwsem(&namespace_sem);
mnt_cache = kmem_cache_create("mnt_cache", sizeof(struct mount),
0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL);
mount_hashtable = (struct list_head *)__get_free_page(GFP_ATOMIC);
mountpoint_hashtable = (struct list_head *)__get_free_page(GFP_ATOMIC);
if (!mount_hashtable || !mountpoint_hashtable)
panic("Failed to allocate mount hash table\n");
printk(KERN_INFO "Mount-cache hash table entries: %lu\n", HASH_SIZE);
for (u = 0; u < HASH_SIZE; u++)
INIT_LIST_HEAD(&mount_hashtable[u]);
for (u = 0; u < HASH_SIZE; u++)
INIT_LIST_HEAD(&mountpoint_hashtable[u]);
br_lock_init(&vfsmount_lock);
err = sysfs_init();
if (err)
printk(KERN_WARNING "%s: sysfs_init error: %d\n",
__func__, err);
fs_kobj = kobject_create_and_add("fs", NULL);
if (!fs_kobj)
printk(KERN_WARNING "%s: kobj create error\n", __func__);
init_rootfs();
init_mount_tree();
}

初始化rootfs

代码语言:javascript
复制
static struct file_system_type rootfs_fs_type = {
.name       = "rootfs",
.mount      = rootfs_mount,
.kill_sb    = kill_litter_super,
};
int __init init_rootfs(void)
{
int err;
err = bdi_init(&ramfs_backing_dev_info);
if (err)
return err;
err = register_filesystem(&rootfs_fs_type);   //注册rootfs文件系统
if (err)
bdi_destroy(&ramfs_backing_dev_info);
return err;
}

挂载rootfs

代码语言:javascript
复制
static void __init init_mount_tree(void)
{
struct vfsmount *mnt;
struct mnt_namespace *ns;
struct path root;
struct file_system_type *type;
type = get_fs_type("rootfs");
if (!type)
panic("Can't find rootfs type");
mnt = vfs_kern_mount(type, 0, "rootfs", NULL);
put_filesystem(type);
if (IS_ERR(mnt))
panic("Can't create rootfs");
ns = create_mnt_ns(mnt);
if (IS_ERR(ns))
panic("Can't allocate initial namespace");
init_task.nsproxy->mnt_ns = ns;
get_mnt_ns(ns);
root.mnt = mnt;
root.dentry = mnt->mnt_root;
set_fs_pwd(current->fs, &root);
set_fs_root(current->fs, &root);
}
struct vfsmount *
vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data)
{
struct mount *mnt;
struct dentry *root;
if (!type)
return ERR_PTR(-ENODEV);
mnt = alloc_vfsmnt(name);
if (!mnt)
return ERR_PTR(-ENOMEM);
if (flags & MS_KERNMOUNT)
mnt->mnt.mnt_flags = MNT_INTERNAL;
root = mount_fs(type, flags, name, data);
if (IS_ERR(root)) {
free_vfsmnt(mnt);
return ERR_CAST(root);
}
mnt->mnt.mnt_root = root;
mnt->mnt.mnt_sb = root->d_sb;
mnt->mnt_mountpoint = mnt->mnt.mnt_root;
mnt->mnt_parent = mnt;
br_write_lock(&vfsmount_lock);
list_add_tail(&mnt->mnt_instance, &root->d_sb->s_mounts);
br_write_unlock(&vfsmount_lock);
return &mnt->mnt;
}
struct dentry *
mount_fs(struct file_system_type *type, int flags, const char *name, void *data)
{
struct dentry *root;
struct super_block *sb;
char *secdata = NULL;
int error = -ENOMEM;
if (data && !(type->fs_flags & FS_BINARY_MOUNTDATA)) {
secdata = alloc_secdata();
if (!secdata)
goto out;
error = security_sb_copy_data(data, secdata);
if (error)
goto out_free_secdata;
}
root = type->mount(type, flags, name, data);
if (IS_ERR(root)) {
error = PTR_ERR(root);
goto out_free_secdata;
}
sb = root->d_sb;
BUG_ON(!sb);
WARN_ON(!sb->s_bdi);
WARN_ON(sb->s_bdi == &default_backing_dev_info);
sb->s_flags |= MS_BORN;
error = security_sb_kern_mount(sb, flags, secdata);
if (error)
goto out_sb;
/* * filesystems should never set s_maxbytes larger than MAX_LFS_FILESIZE * but s_maxbytes was an unsigned long long for many releases. Throw * this warning for a little while to try and catch filesystems that * violate this rule. */
WARN((sb->s_maxbytes < 0), "%s set sb->s_maxbytes to "
"negative value (%lld)\n", type->name, sb->s_maxbytes);
up_write(&sb->s_umount);
free_secdata(secdata);
return root;
out_sb:
dput(root);
deactivate_locked_super(sb);
out_free_secdata:
free_secdata(secdata);
out:
return ERR_PTR(error);
}

真正挂载rootfs的函数

代码语言:javascript
复制
static struct dentry *rootfs_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data)
{
return mount_nodev(fs_type, flags|MS_NOUSER, data, ramfs_fill_super);
}
struct dentry *mount_nodev(struct file_system_type *fs_type,
int flags, void *data,
int (*fill_super)(struct super_block *, void *, int))
{
int error;
struct super_block *s = sget(fs_type, NULL, set_anon_super, flags, NULL);
if (IS_ERR(s))
return ERR_CAST(s);
error = fill_super(s, data, flags & MS_SILENT ? 1 : 0);
if (error) {
deactivate_locked_super(s);
return ERR_PTR(error);
}
s->s_flags |= MS_ACTIVE;
return dget(s->s_root);
}

通过上面的代码,内核就把rootfs挂载起来了,此时rootfs还是个空目录,并且只有一个根目录‘/’。

六、解压ramdisk中的文件到rootfs

代码语言:javascript
复制
static int __init populate_rootfs(void)
{
char *err = unpack_to_rootfs(__initramfs_start, __initramfs_size);    //Android内核中没有initramfs,这个函数什么也不做
if (err)
panic(err); /* Failed to decompress INTERNAL initramfs */
if (initrd_start) {
#ifdef CONFIG_BLK_DEV_RAM
int fd;
printk(KERN_INFO "Trying to unpack rootfs image as initramfs...\n");
err = unpack_to_rootfs((char *)initrd_start,
initrd_end - initrd_start);
if (!err) {
free_initrd();
goto done;
} else {
clean_rootfs();
unpack_to_rootfs(__initramfs_start, __initramfs_size);
}
printk(KERN_INFO "rootfs image is not initramfs (%s)"
"; looks like an initrd\n", err);
fd = sys_open("/initrd.image",
O_WRONLY|O_CREAT, 0700);
if (fd >= 0) {
sys_write(fd, (char *)initrd_start,
initrd_end - initrd_start);
sys_close(fd);
free_initrd();
}
done:
#else
printk(KERN_INFO "Unpacking initramfs...\n");
err = unpack_to_rootfs((char *)initrd_start,
initrd_end - initrd_start);
if (err)
printk(KERN_EMERG "Initramfs unpacking failed: %s\n", err);
free_initrd();
#endif
/* * Try loading default modules from initramfs. This gives * us a chance to load before device_initcalls. */
load_default_modules();
}
return 0;
}
rootfs_initcall(populate_rootfs);

unpack_to_rootfs会先解压ramdisk成一个cpio文件,然后解析解析cpio文件中所有文件,并生成对应的文件到rootfs中

代码语言:javascript
复制
static char * __init unpack_to_rootfs(char *buf, unsigned len)
{
int written, res;
decompress_fn decompress;
const char *compress_name;
static __initdata char msg_buf[64];
header_buf = kmalloc(110, GFP_KERNEL);
symlink_buf = kmalloc(PATH_MAX + N_ALIGN(PATH_MAX) + 1, GFP_KERNEL);
name_buf = kmalloc(N_ALIGN(PATH_MAX), GFP_KERNEL);
if (!header_buf || !symlink_buf || !name_buf)
panic("can't allocate buffers");
state = Start;
this_header = 0;
message = NULL;
while (!message && len) {
loff_t saved_offset = this_header;
if (*buf == '0' && !(this_header & 3)) {
state = Start;
written = write_buffer(buf, len);
buf += written;
len -= written;
continue;
}
if (!*buf) {
buf++;
len--;
this_header++;
continue;
}
this_header = 0;
decompress = decompress_method(buf, len, &compress_name);
if (decompress) {
res = decompress(buf, len, NULL, flush_buffer, NULL,
&my_inptr, error);
if (res)
error("decompressor failed");
} else if (compress_name) {
if (!message) {
snprintf(msg_buf, sizeof msg_buf,
"compression method %s not configured",
compress_name);
message = msg_buf;
}
} else
error("junk in compressed archive");
if (state != Reset)
error("junk in compressed archive");
this_header = saved_offset + my_inptr;
buf += my_inptr;
len -= my_inptr;
}
dir_utime();
kfree(name_buf);
kfree(symlink_buf);
kfree(header_buf);
return message;
}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/185128.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022年10月6日 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、ramdisk的制作
  • 二、uboot加载ramdisk到内存
  • 三、uboot通知内核ramdisk在内存中的地址
  • 四、内核解析ramdisk地址
  • 五、内核挂载rootfs
  • 六、解压ramdisk中的文件到rootfs
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档