2012年(8)
分类:
2012-03-17 21:01:59
written by leeming
作为我们实验室的一个学术交流,我顺着fp的linux arm启动汇编部分继续下去。我们可以看到其实linux汇编部分的启动大量的工作是对zimage的解压,重定位等操作,如果是image(也就是zimage解压重定位结束后)来说,其实主要就做了以下这么几件事情:1.建立启动时的一级页表,2.打开mmu,3.保存机器号等参数。
因此对于整个处理器系统来说还需要做大量的工作,对于移植内核来说,只有真正了解了这部分你才会明白在arch/arm/mach-sep4020这个目录中的文件为什么需要这样写。进入正题:
1.进入start_kernel,详解setup_arch(处理器的移植,页表建立都在这里实现的)
asmlinkage void __init start_kernel(void)
{
char * command_line;
extern struct kernel_param __start___param[], __stop___param[];
/*
* Interrupts are still disabled. Do necessary setups, then
* enable them
*/
lock_kernel();
//这里是和高端内存相关的操作,arm中不涉及
page_address_init();
//这个只是printk的等级,但是为什么在控制台不显示,待看
//这时候控制台还没有初始化,因此所有的信息都是在log_buf里
printk(KERN_NOTICE);
printk(linux_banner);
setup_arch(&command_line);
……
……
到这里就碰到了我们详解start kernel的第一道坎,setup_arch(&command_line);别看就一句话,其实这个函数本身是非常庞大的,下面我们来具体看完整的setup_arch函数。
void __init setup_arch(char **cmdline_p)
{
struct tag *tags = (struct tag *)&init_tags;
struct machine_desc *mdesc;
//in the arch/arm/kernel/setup.c
//static char default_command_line[COMMAND_LINE_SIZE] __initdata = CONFIG_CMDLINE;
//在我们的配置中#define CONFIG_CMDLINE "root=/dev/ram0 rw console=ttyS0,115200"
char *from = default_command_line;
//就是查找你是什么版本的处理器架构,最后就是调用
//了lookup_processor_type这个函数,它在汇编部分也提到过
setup_processor();
//machine_arch_type 就是我们的机器号0xc2
mdesc = setup_machine(machine_arch_type);
machine_name = mdesc->name;
//这个变量初始值为"h",如果这里设置成softboot,它会将这个初始值变为"s"
if (mdesc->soft_reboot)
reboot_setup("s");
//boot_params 如果为0则表示bootloader没有传参数
//一般默认为0x30000100位置
if (mdesc->boot_params)
tags = phys_to_virt(mdesc->boot_params);
/*
* If we have the old style parameters, convert them to
* a tag list.
*/
if (tags->hdr.tag != ATAG_CORE)
convert_to_tag_list(tags);
if (tags->hdr.tag != ATAG_CORE)
tags = (struct tag *)&init_tags;
if (mdesc->fixup)
mdesc->fixup(mdesc, tags, &from, &meminfo);
//是通过标签0x544100**来辨别的,因此uboot中有相应的标签字
if (tags->hdr.tag == ATAG_CORE) {
//已经被fixup函数修改,则将atag中的mem段置为none
if (meminfo.nr_banks != 0)
squash_mem_tags(tags);
//继续把atag的参数传递结束,在这里将把uboot传递进来的commandline覆盖
parse_tags(tags);
}
//下面几个参数是由vmlinux.lds文件决定的
init_mm.start_code = (unsigned long) &_text;
init_mm.end_code = (unsigned long) &_etext;
init_mm.end_data = (unsigned long) &_edata;
init_mm.brk = (unsigned long) &_end;
//因此在这里已经是我们uboot的参数了。
memcpy(saved_command_line, from, COMMAND_LINE_SIZE);
saved_command_line[COMMAND_LINE_SIZE-1] = '\0';
//分析cmdline,这里只分析了当中的mem部分。
//内核参数的解析一共有两处,一处是setup_arch()->parse_cmdline()
//用于解析内核参数中关于内存的部分,另外一处是start_kernel()->parse_option()用于解析其余部分
parse_cmdline(cmdline_p, from);
//非常重要的部分,页表建立,根据meminfo,也就是我们在4020.c中的mache_start何mache_end之间的内容,它其实就是一个类型是machine_desc的结构体,这里就是把这个结构体中的内存信息(fixup函数中指定的),寄存器信息(sep4020_map_io指定的)建立相应的一二级页表
/*************************************/
paging_init(&meminfo, mdesc);
/*************************************/
request_standard_resources(&meminfo, mdesc);
#ifdef CONFIG_SMP
smp_init_cpus();
#endif
//设置不同模式的堆栈,包括3种模式,irq,abort,undefine,每种模式的堆栈是12个字节
cpu_init();
{
//gcc内嵌汇编的格式是__asm(汇编代码:输入:输出:破坏描述部分);
//下面的限制字符"r" "I"是把变量放进通用寄存器,立即数的意思
__asm__ (
"msr cpsr_c, %1\n\t"
"add sp, %0, %2\n\t"
"msr cpsr_c, %3\n\t"
"add sp, %0, %4\n\t"
"msr cpsr_c, %5\n\t"
"add sp, %0, %6\n\t"
"msr cpsr_c, %7"
:
: "r" (stk),//堆栈空间起始地址
"I" (PSR_F_BIT | PSR_I_BIT | IRQ_MODE),//切换到irq模式,并禁止irq fiq
"I" (offsetof(struct stack, irq[0])),//irq[0]的偏移,一下同理
"I" (PSR_F_BIT | PSR_I_BIT | ABT_MODE),
"I" (offsetof(struct stack, abt[0])),
"I" (PSR_F_BIT | PSR_I_BIT | UND_MODE),
"I" (offsetof(struct stack, und[0])),
"I" (PSR_F_BIT | PSR_I_BIT | SVC_MODE)
: "r14");//注意切换svc时,告诉系统,r14改变了
}
/*
* Set up various architecture-specific pointers
*/
//继续将020.c中的mache_start何mache_end之间的内容注册到系统中,这里是把系统初始化函数,中断,定时器注册(这几部分也就是在为一个新的处理器移植内核的时候非常重要的地方)
init_arch_irq = mdesc->init_irq;
system_timer = mdesc->timer;
init_machine = mdesc->init_machine;
#ifdef CONFIG_VT
#if defined(CONFIG_VGA_CONSOLE)
conswitchp = &vga_con;
#elif defined(CONFIG_DUMMY_CONSOLE)
conswitchp = &dummy_con;
#endif
#endif
}
至此整个setup_arch函数解释完毕,由于setup_arch中还有一个重要函数paging_init函数也蛮庞大的,因此在第二讲中详细说明这个函数。