QQ登录

只需一步,快速开始

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 1855|回复: 0

uClinux cmdline分析以及rootfs挂载

[复制链接]
发表于 2006-8-14 16:00:54 | 显示全部楼层 |阅读模式
因为在做sigma designs em8620的方案,通过em8620的开发包了解一下有关uClinux的一些东西,有误的地方希望大虾们指出来

主要参考了www-128.bim.com
《linux启动过程综述》
《解析linux VFS文件系统机制》

由start_kernel函数开始
在arch\armnommu\vmlinux-armv.lds.in里面有如下定义:
SECTIONS
{
        . = TEXTADDR;
        .init : {                        /* Init code and data                */
                _stext = .;
                __init_begin = .;        //init段
                        *(.text.init)
                __proc_info_begin = .;   //proc_info段
                        *(.proc.info)
                __proc_info_end = .;
                __arch_info_begin = .;   //.arch.info段
                        *(.arch.info)
                __arch_info_end = .;
                        *(.data.init)
                . = ALIGN(16);
                __setup_start = .;        //_setup_init段
                        *(.setup.init)
                __setup_end = .;
                __initcall_start = .;      
                        *(.initcall.init)
                __initcall_end = .;
                __ramdisk_data = .;
                        INITRDIMAGE
                __ramdisk_data_end = .;
                . = ALIGN(4096);
                __init_end = .;
        }

1、init/main.c
asmlinkage void __init start_kernel(void)
{
        char * command_line;
        extern char saved_command_line[]; //在arch/armnommu/kernel/setup.c里面定义的,//setup_arch()使用
/*
* Interrupts are still disabled. Do necessary setups, then
* enable them
*/
        lock_kernel();
        printk(linux_banner);
        setup_arch(&command_line); //setup_arch调用parse_cmline生成commandline串,见1.1
        printk("Kernel command line: %s\n", saved_command_line);
        parse_options(command_line);//parse setup_arch生成的commandlie,见1.2
        trap_init();       //接下来是一系列的init
        init_IRQ();
        sched_init();
        softirq_init();
        time_init();

        /*
         * HACK ALERT! This is early. We're enabling the console before
         * we've done PCI setups etc, and console_init() must be aware of
         * this. But we do want output early, in case something goes wrong.
         */
        console_init();
#ifdef CONFIG_MODULES
        init_modules();
#endif
   。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
           mem_init();
        kmem_cache_sizes_init();
        pgtable_cache_init();

        /*
         * For architectures that have highmem, num_mappedpages represents
         * the amount of memory the kernel can use.  For other architectures
         * it's the same as the total pages.  We need both numbers because
         * some subsystems need to initialize based on how much memory the
         * kernel can use.
         */
        if (num_mappedpages == 0)
                num_mappedpages = num_physpages;
  
        fork_init(num_mappedpages);
        proc_caches_init();
        vfs_caches_init(num_physpages);
        buffer_init(num_physpages);
        page_cache_init(num_physpages);
#if defined(CONFIG_ARCH_S390)
        ccwcache_init();
#endif
        signals_init();
#ifdef CONFIG_PROC_FS
        proc_root_init();
#endif
#if defined(CONFIG_SYSVIPC)
        ipc_init();
#endif
        check_bugs();
        printk("POSIX conformance testing by UNIFIX\n");

        /*
         *        We count on the initial thread going ok
         *        Like idlers init is an unlocked kernel thread, which will
         *        make syscalls (and thus be locked).
         */
        smp_init();
        rest_init();  //init调用,见2
}//end start_kernel

1.1在arch\armnommu\kernel\setup.c中

setup_arch函数由linux\init\main.c中的start_kernel()调用
void __init setup_arch(char **cmdline_p)
{
        struct param_struct *params = NULL;
        struct machine_desc *mdesc;
        char *from = default_command_line;
//static char default_command_line[COMMAND_LINE_SIZE] __initdata = //CONFIG_CMDLINE;
        int bootmap_size;
…………………………….
#ifdef CONFIG_ARCH_EM86XX
#ifdef CONFIG_SD_USE_BOOTLOADER_MEMCFG
        /* Use the smaller one as size: specified in kernel config, or
           what we detected in the bootloader. */
        if (is_valid_memcfg(em86xx_memcfg_ptr)) {//loader的memcfg
                unsigned long max_size;

                printk("Found bootloader memory map at 0x%08lx.\n",
                        (unsigned long)em86xx_memcfg_ptr);
                max_size = em86xx_memcfg_ptr->dram0_size -
                        (em86xx_memcfg_ptr->dram0_fixed_topreserved +
                        em86xx_memcfg_ptr->dram0_removable_topreserved);
                max_size &= 0xfff00000; /* Round it to MB boundary */

                em86xx_kmemsize = ((max_size > em86xx_kmemsize) ?
                                 em86xx_kmemsize : max_size);
        } else
                printk("Cannot find valid bootloader signature.\n");
#else
        /* Use the default value set by kernel configuration */
#endif
#endif

        ROOT_DEV = MKDEV(0, 255); //ROOT_DEV的第一次设定

        setup_processor();//
        mdesc = setup_architecture(machine_arch_type);
//setup_architecture查找并返回mdesc结构,见1.1.1
        machine_name = mdesc->name;

        if (mdesc->soft_reboot)
                reboot_setup("s");//补丁函数

        if (mdesc->param_offset)//如果param_offset有效,执行如下语句
                params = (struct param_struct*) //params指针,这里因为有效,且为BOOT_PARAM
                        (phys_to_virt(mdesc->param_offset));

        /*
         * Do the machine-specific fixups before we parse the
         * parameters or tags.
         */
        if (mdesc->fixup)
                mdesc->fixup(mdesc, params, &from, &meminfo);

        if (params) {
                struct tag *tag = (struct tag *)params;//把前面的到的params指针转换位tag指针,用于解析

                /*
                 * Is the first tag the CORE tag?  This differentiates
                 * between the tag list and the parameter table.
                 */
#ifdef CONFIG_SD //如果定义CONFIG_SD则用parse_tag分析,否则用param_param分析
                if (tag->hdr.tag == ATAG_CORE || tag->hdr.tag == ATAG_CMDLINE || tag->hdr.tag == ATAG_NONE)
#else
                if (tag->hdr.tag == ATAG_CORE)
#endif
                        parse_tags(mdesc->tagtable, mdesc->tagsize, tag);
                else
                        parse_params(params);
        }

        if (meminfo.nr_banks == 0) {
                meminfo.nr_banks      = 1;
                meminfo.bank[0].start = PAGE_OFFSET;//PHYS_OFFSET;
                meminfo.bank[0].size  = MEM_SIZE;
#ifdef CONFIG_NUMA_EM86XX
                if ((is_valid_memcfg(em86xx_memcfg_ptr)) &&
                        (em86xx_memcfg_ptr->dram1_size > 0)) {
                        ++meminfo.nr_banks;
                        ++numnodes;
                        meminfo.bank[1].node = 1;
                        meminfo.bank[1].start = DRAM_BASE_2;
                        meminfo.bank[1].size  = em86xx_memcfg_ptr->dram1_size;
                }
#endif
        }

        init_mm.start_code = (unsigned long) &_text;
        init_mm.end_code   = (unsigned long) &_etext;
#ifndef CONFIG_RAM_ATTACHED_ROMFS  
        init_mm.end_data   = (unsigned long) &_edata;
        init_mm.brk           = (unsigned long) &_end;
#else
        init_mm.end_data   = (unsigned long) _ramstart;
        init_mm.brk           = (unsigned long) _ramstart;
#endif

        memcpy(saved_command_line, from, COMMAND_LINE_SIZE);//copy ”from” to save_commland_line
        saved_command_line[COMMAND_LINE_SIZE-1] = '\0';
        parse_cmdline(&meminfo, cmdline_p, from);  
//解析”from”设置meminfo和cmdline_p. “from”为CONFIG_CMDLINE, cmdline_p为指向
//全局数组char command_line[512]首地址的指针,见1.1.2
#ifdef CONFIG_ARCH_EM86XX
        /* Record the memory size used by kernel */
        if (is_valid_memcfg(em86xx_memcfg_ptr)) {
                em86xx_memcfg_ptr->kernel_end += em86xx_kmemsize;
                gen_memcfg_checksum(em86xx_memcfg_ptr); /* Update checksum */
        }
#endif

#if 1
        bootmem_init(&meminfo);
#else
        bootmap_size= init_bootmem_node(
                          NODE_DATA(0),
                          memory_start >> PAGE_SHIFT,
                          PAGE_OFFSET >> PAGE_SHIFT,
                          END_MEM >> PAGE_SHIFT);

        free_bootmem(memory_start, END_MEM - memory_start);
        reserve_bootmem(memory_start, bootmap_size);
#endif
        paging_init(&meminfo, mdesc);                // mem_map is set up here!
        request_standard_resources(&meminfo, mdesc);

        /*
         * Set up various architecture-specific pointers
         */
        init_arch_irq = mdesc->init_irq;

#ifdef CONFIG_BLK_DEV_INITRD
        // let the kernel know where the initrd exists
#ifdef CONFIG_SD_INITRD_EMBED
        {
                extern int __ramdisk_data, __ramdisk_data_end;
                initrd_start = (unsigned long) &__ramdisk_data;
                initrd_end = (unsigned long) &__ramdisk_data_end - (unsigned long) &__ramdisk_data;
        }
#else
        initrd_start = CONFIG_SD_INITRD_START;
        initrd_end = CONFIG_SD_INITRD_START + CONFIG_SD_INITRD_SIZE;
#endif
#endif

#ifdef CONFIG_VT
#if defined(CONFIG_VGA_CONSOLE)
        conswitchp = &vga_con;
#elif defined(CONFIG_DUMMY_CONSOLE)
        conswitchp = &dummy_con;
#endif
#endif
}//end setup_arch()

1.1.1 setup_architecture会返回一个machine_desc
static struct machine_desc * __init setup_architecture(unsigned int nr)
{//根据nr值来查找相应得machine结构
        extern struct machine_desc __arch_info_begin, __arch_info_end;
    //在arm_info段查找
        ……………………….
        return list;
}

在arm\armnommu\mach-em86xx\arch.c里面有如下的machine_desc
在arch\armnommu\mach-em86xx\arch.c中
        MACHINE_START(EM86XX, "EM86XX")
    MAINTAINER("Ho Lee - Sigma Designs, Inc")
    BOOT_MEM(PHYS_OFFSET, PHYS_OFFSET, PHYS_OFFSET)
// phys_ram, phys_io, virt_io
            // phys_io, virt_io have no meaning
            // but if virt_io is less than 0x00040000, kernel doesn't boot
            // (refer __lookup_architecture_type() at head-armv.S
            // r7 = (virt_io >> 1
    INITIRQ(em86xx_init_irq)
    FIXUP(em86xx_fixup)
    BOOT_PARAMS(DRAM_BASE + 0x200)
MACHINE_END

该MACHINE_START的原形为:
/asm-arm/mach/arch.h中的:

#define MACHINE_START(_type,_name)                \
const struct machine_desc __mach_desc_##_type        \ //MACHINE_START生成一个machine_desc结构
__attribute__((__section__(".arch.info"))) = {        \//这里是vmlinux.lds.in中arch.info段内容
        nr:                MACH_TYPE_##_type,        \
        name:                _name,
#define MAINTAINER(n)
#define BOOT_MEM(_pram,_pio,_vio)                \
        phys_ram:        _pram,                        \
        phys_io:        _pio,                        \
        virt_io:        _vio,
#define BOOT_PARAMS(_params)                        \   //BOOT_PARAMS地址,为param_offset
        param_offset:        _params,
#define VIDEO(_start,_end)                        \
        video_start:        _start,                        \
        video_end:        _end,
#define DISABLE_PARPORT(_n)                        \
        reserve_lp##_n:        1,
#define BROKEN_HLT /* unused */
#define SOFT_REBOOT                                \
        soft_reboot:        1,
#define FIXUP(_func)                                \
        fixup:                _func,
#define MAPIO(_func)                                \
        map_io:                _func,
#define INITIRQ(_func)                                \
        init_irq:        _func,
#define MACHINE_END                                \
};
#endif


1.1.2 Setup_arch()函数中的parse_tag()函数中,有一个tag header与相应函数的关系表
static const struct tagtable core_tagtable[] __init = {
        { ATAG_CORE,      parse_tag_core      },
        { ATAG_MEM,       parse_tag_mem32     },
        { ATAG_VIDEOTEXT, parse_tag_videotext },
        { ATAG_RAMDISK,   parse_tag_ramdisk   },
        { ATAG_INITRD,    parse_tag_initrd    },
        { ATAG_SERIAL,    parse_tag_serialnr  },
        { ATAG_REVISION,  parse_tag_revision  },
        { ATAG_CMDLINE,   parse_tag_cmdline   }
};

static int __init
parse_tag(const struct tagtable *tbl, int size, const struct tag *t)
{
        int i;

        for (i = 0; i < size; i++, tbl++)
                if (t->hdr.tag == tbl->tag) {//根据hdr.tag的类型调用相应的parse函数
                        tbl->parse(t);
                        break;
                }

        return i < size;
}
因为在bootloader里面设定为ATAG_CMDLINE,所以会调用相应得parse_tag_cmdline函数
   static int __init parse_tag_cmdline(const struct tag *tag)
{
        strncpy(default_command_line, tag->u.cmdline.cmdline, COMMAND_LINE_SIZE);
//把在setup_arch里面找到tag结构的cmdline复制给全局的default_cmd_line
        default_command_line[COMMAND_LINE_SIZE - 1] = '\0';
        return 0;
}


1.2 parse_options函数
static void __init parse_options(char *line) //(init/main.c)
{
…………………… //先“解析”一些特殊字符串,比如带有”init=……”格式的字符串;

                if (checksetup(line)) //根据解析line(这里可以理解为parse_tag_cmdline生成的//default_cmd_line)里面的信息,见1.2.1

………………………………       
}

1.2.1
static int __init checksetup(char *line)  // (init/main.c)checksetup(char *line)会根据函数parse_options分////析的字符串情况分别调用相应的由宏__setup建立的函数
{
        struct kernel_param *p;

        p = &__setup_start;  //所有由__setup宏定义的函数起始地址 见1.2.1.1
        do {
                int n = strlen(p->str);
                if (!strncmp(line,p->str,n)) {  
                        if (p->setup_func(line+n)) //调用__setup建立的相应函数
                                return 1;
        …………………………
}
1.2.1.1
__setup宏(include\linux\init.h)定义如下
#define __setup(str, fn) \
static char __setup_str_##fn[] __initdata = str; \
static struct kernel_param __setup_##fn __attribute__((unused)) __initsetup = { __setup_str_##fn, fn }
#define __initsetup __attribute__ ((unused,__section__ (".setup.init")))//setup.init段
实例:如果cmdline为”root=……”,则将执行__setup(“root=”,root_dev_setup);// (init/do_mounts.c里面)
static int __init root_dev_setup(char *line)
{
        int i;
        char ch;

        ROOT_DEV = name_to_kdev_t(line);//根据字符串解析ROOT_DEV
        memset (root_device_name, 0, sizeof root_device_name);
        if (strncmp (line, "/dev/", 5) == 0) line += 5;
        for (i = 0; i < sizeof root_device_name - 1; ++i)
        {
            ch = line;
            if ( isspace (ch) || (ch == ',') || (ch == '\0') ) break;
            root_device_name = ch;
        }
        return 1;
}

__setup("root=", root_dev_setup); //如果line的起始字符为”root=”则调用root_dev_setup

2、在start_kernel最后会启动init,调用相关的init段
Inculde\linux\init.h中相关段的定义
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
#ifndef NO_TEXT_SECTIONS
#define __init                __attribute__ ((__section__ (".text.init")))  
#define __exit                __attribute__ ((unused, __section__(".text.exit")))
#else
#define __init
#define __exit                __attribute__ ((unused))
#endif
#define __initdata        __attribute__ ((__section__ (".data.init")))
#define __exitdata        __attribute__ ((unused, __section__ (".data.exit")))
#define __initsetup        __attribute__ ((unused,__section__ (".setup.init")))  
#define __init_call        __attribute__ ((unused,__section__ (".initcall.init")))
#define __exit_call        __attribute__ ((unused,__section__ (".exitcall.exit")))

/* For assembly routines */
#define __INIT                .section        ".text.init","ax"
#define __FINIT                .previous
#define __INITDATA        .section        ".data.init","aw"
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
如static int __init root_dev_setup(char *line)make时会连接到text.init段

3、init段中的rootfs初始化
static void __init mount_root(void)
{
#ifdef CONFIG_ROOT_NFS
       if (MAJOR(ROOT_DEV) == NFS_MAJOR
           && MINOR(ROOT_DEV) == NFS_MINOR) {
                if (mount_nfs_root()) {
                        sys_chdir("/root");
                        ROOT_DEV = current->fs->pwdmnt->mnt_sb->s_dev;
                        printk("VFS: Mounted root (nfs filesystem).\n");
                        return;
                }
                printk(KERN_ERR "VFS: Unable to mount root fs via NFS, trying floppy.\n");
                ROOT_DEV = MKDEV(FLOPPY_MAJOR, 0);
        }
#endif
        devfs_make_root(root_device_name);
        create_dev("/dev/root", ROOT_DEV, root_device_name);//建立目录“dev/root”,将在root_dev_setup()中建立/的ROOTDEV“挂载”到目录/dev/root下面
#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 = MKDEV(RAMDISK_MAJOR, 1);
                                create_dev("/dev/root", ROOT_DEV, NULL);
                        }
                } else
                        change_floppy("root floppy");
     mount_block_root("/dev/root", root_mountflags);//将目录”dev/root”挂载为根文件系统
}
根据《解析 Linux 中的 VFS 文件系统机制》,在执行static void __init mount_root(void)之前,目录”/dev”,”/root”已经建立好了,create_dev是vfs文件系统操作的封装


static void __init mount_block_root(char *name, int flags)
{
        char *fs_names = __getname();
        char *p;

        get_fs_names(fs_names);
retry:
        for (p = fs_names; *p; p += strlen(p)+1) {
                int err = sys_mount(name, "/root", p, flags, root_mount_data); //将name mount到”/root”下面
                switch (err) {
                        case 0:
                                goto out;
                        case -EACCES:
                                flags |= MS_RDONLY;
                                goto retry;
                        case -EINVAL:
#ifdef CONFIG_SD
                        // by Ho Lee 01/20/2003
                        // newly introducted mount_block_root() function has some problem
                        // when this code tries to mount ISO9660 filesystem and it fails,
                        // it return -ENOENT. without this code, kernel panics
                        case -ENOENT:
#endif
                                continue;
                }
                /*
                 * Allow the user to distinguish between failed open
                 * and bad superblock on root device.
                 */
                printk ("VFS: Cannot open root device \"%s\" or %s\n",
                        root_device_name, kdevname (ROOT_DEV));
                printk ("Please append a correct \"root=\" boot option\n");
                panic("VFS: Unable to mount root fs on %s",
                        kdevname(ROOT_DEV));
        }
        panic("VFS: Unable to mount root fs on %s", kdevname(ROOT_DEV));
out:
        putname(fs_names);
************************************************************************在 Linux 下,设定一个进程的当前工作目录是通过系统调用 sys_chdir() 进行的。初始化期间,Linux 在将 hda1 上的 ext2 文件系统安装到了 "/root" 上后,通过调用 sys_chdir("/root") 将当前进程,也就是 init_task 进程的当前工作目录(pwd)设定为 ext2 文件系统的根目录。因为以后 Linux 世界中的所有进程都由这个 init_task 进程派生出来,无一例外地要继承该进程的根目录,如果是这样,意味着用户进程从根目录搜索某一目录时,实际上是从 VFS 的根目录开始的,而事实上却是从 ext2 的根文件开始搜索的。这个矛盾的解决是靠了在调用完 mount_root() 函数后,系统调用的下面函数:
        sys_chdir("/root");
        ROOT_DEV = current->fs->pwdmnt->mnt_sb->s_dev;
        printk("VFS: Mounted root (%s filesystem)%s.\n",
                current->fs->pwdmnt->mnt_sb->s_type->name,
                (current->fs->pwdmnt->mnt_sb->s_flags & MS_RDONLY) ? " readonly" : "");
}

4、rootfs挂载实例
Rootfs挂载实例:挂载硬盘第二分区(romfs)
在make linux-config里面
1)        System setup 里面定义kernel_cmdlin root=/dev/hda;
2)        在do-mounts.c里面的,
   static struct dev_name_struct {
        const char *name;
        const int num;
} root_dev_names[] __initdata = {
        { "nfs",     MKDEV(NFS_MAJOR, NFS_MINOR) },
        //{ "hda",     0x0300 },
        //modified by yangm
        { "hda",     0x0302 },//partition 0 change to partition 2
        { "hdb",     0x0340 },
        { "loop",    0x0700 },
        { "hdc",     0x1600 },
        { "hdd",     0x1640 },
        { "hde",     0x2100 },
        { "hdf",     0x2140 },
        { "hdg",     0x2200 },
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
}
上面的表项将用于 name_to_kdev_t解析,这里root=/dev/had解析得到的ROOTDEV=0x0302
static int __init root_dev_setup(char *line)
{
  //add by yangm printk
。。。。。。。。。。。。。。。。。。。。。。。。。。。
  printk("root_dev_setup() executed command line is %s\n",line);
        ROOT_DEV = name_to_kdev_t(line);
。。。。。。。。。。。。。。。。。。。。。。。。。。。。
        return 1;
}

kdev_t __init name_to_kdev_t(char *line)
{
        int base = 0, offs;
        char *end;

        if (strncmp(line,"/dev/",5) == 0) {
                struct dev_name_struct *dev = root_dev_names;
                line += 5;
                do {
                        int len = strlen(dev->name); //这里为“hda”
                        if (strncmp(line,dev->name,len) == 0) {
                                line += len;
                                base = dev->num;
                                break;
                        }
                        dev++;
                } while (dev->name);
        }
        offs = simple_strtoul(line, &end, base?10:16);
        if (*end)
                offs = 0;
        return to_kdev_t(base + offs);//返回”hda”对应得”0x0302”

}

因为是先执行setup.init段,在后面的blkmem.c编译好的blkmem.o驱动程序里面blk_init()函数设定的ROOT_DEV将覆盖上面建立的ROO_DEV值,注掉里面有关ROOT_DEV的设定

然后make linux,将其
Boot>ide romfs 0 0
然后
Boot>ide
能够挂载第二个分区的romfs文件系统
您需要登录后才可以回帖 登录 | 注册

本版积分规则

GMT+8, 2024-11-26 18:53 , Processed in 0.049563 second(s), 15 queries .

© 2021 Powered by Discuz! X3.5.

快速回复 返回顶部 返回列表