uClinux cmdline分析以及rootfs挂载
因为在做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 __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.start = PAGE_OFFSET;//PHYS_OFFSET;
meminfo.bank.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.node = 1;
meminfo.bank.start = DRAM_BASE_2;
meminfo.bank.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 = '\0';
parse_cmdline(&meminfo, cmdline_p, from);
//解析”from”设置meminfo和cmdline_p. “from”为CONFIG_CMDLINE, cmdline_p为指向
//全局数组char command_line首地址的指针,见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 >> 18)
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 = '\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文件系统
页:
[1]