天天看點

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/productramdisk.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 = ;

    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, );
        if (initrd_high == ~)
            initrd_copy_to_ram = ;
    } 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, , initrd_high);
            else
                *initrd_start = (ulong)lmb_alloc(lmb, rd_len,
                                 );

            if (*initrd_start == ) {
                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 = ;
        *initrd_end = ;
    }
    debug("   ramdisk load start = 0x%08lx, ramdisk load end = 0x%08lx\n",
            *initrd_start, *initrd_end);

    return ;

error:
    return -;
}
           

三、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, , &machid) < ) {
            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,
                              , machid, r2);
        } else
#endif
            /*啟動核心時把tag的位址通過參數傳入,核心可以通過該位址解析bootloader傳入的tag,包括ramdisk的addr及size*/
            kernel_entry(, 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 ;

    /* find or create "/chosen" node. */
    nodeoffset = fdt_find_or_add_subnode(fdt, , "chosen");
    if (nodeoffset < )
        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 = ; 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 < ) {
        printf("fdt_initrd: %s\n", fdt_strerror(err));
        return err;
    }

    is_u64 = (fdt_address_cells(fdt, ) == );
    /*往dtb中添加一個"linux,initrd-start"屬性,核心通過解析dtb可以獲知ramdisk的位址*/
    err = fdt_setprop_uxx(fdt, nodeoffset, "linux,initrd-start",
                  (uint64_t)initrd_start, is_u64);

    if (err < ) {
        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 < ) {
        printf("WARNING: could not set linux,initrd-end %s.\n",
               fdt_strerror(err));

        return err;
    }

    return ;
}
           

四、核心解析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/);

    prop = of_get_flat_dt_prop(node, "linux,initrd-end", &len);//讀取ramdisk在記憶體的結束位址
    if (!prop)
        return;
    end = of_read_number(prop, len/);

    initrd_start = (unsigned long)__va(start);
    initrd_end = (unsigned long)__va(end);
    initrd_below_start_ok = ;

    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()) * /, mempages - );
    mempages -= reserve;

    names_cachep = kmem_cache_create("names_cache", PATH_MAX, ,
            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),
            , 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 = ; u < HASH_SIZE; u++)
        INIT_LIST_HEAD(&mount_hashtable[u]);
    for (u = ; 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, , "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 < ), "%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 ?  : );
    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, );
        if (fd >= ) {
            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 ;
}
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[];

    header_buf = kmalloc(, GFP_KERNEL);
    symlink_buf = kmalloc(PATH_MAX + N_ALIGN(PATH_MAX) + , 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 = ;
    message = NULL;
    while (!message && len) {
        loff_t saved_offset = this_header;
        if (*buf == '0' && !(this_header & )) {
            state = Start;
            written = write_buffer(buf, len);
            buf += written;
            len -= written;
            continue;
        }
        if (!*buf) {
            buf++;
            len--;
            this_header++;
            continue;
        }
        this_header = ;
        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;
}
           

繼續閱讀