android rootfs的挂载流程

一、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到内存

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解析。相关代码如下:

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中的内容的。

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地址

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

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

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


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的函数

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

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中

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;
}

你可能感兴趣的:(linux内核)