arm 启动流程 2016.08.11
Posted liushuhe1990
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了arm 启动流程 2016.08.11相关的知识,希望对你有一定的参考价值。
arm 启动流程
刘术河 2016.08.11
linux-2.6.39-at91archarmkernelhead.S
//获取机器ID,与uboot传进来的比较,返回匹配的ID值
bl __lookup_processor_type @ r5=procinfo r9=cpuid
ldr r13, =__mmap_switched @ address to jump to after mmu has been enabled
b start_kernel
linux-2.6.39-at91initMain.c
start_kernel.c
setup_arch(&command_line);
struct machine_desc *mdesc;
//在这里确定是哪个特定的开发板
mdesc = setup_machine(machine_arch_type);
for (p = __arch_info_begin; p < __arch_info_end; p++)
{
if (nr == p->nr) {
printk("Machine: %s ", p->name);
return p;
}
}
//__arch_info_begin在vmlinux.lds.S->SECTIONS->do_initcalls里面定义
//__arch_info_begin = .;
//*(.arch.info.init)
//__arch_info_end = .;
rest_init();
kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
kernel_init
do_basic_setup();
do_initcalls();
{
initcall_t *fn;
for (fn = __early_initcall_end; fn < __initcall_end; fn++)
do_one_initcall(*fn);
//这里会调用customize_machine
}
/*************************************************************************************/
//分析内核识别单板过程
linux-2.6.39-at91archarmkernelhead.S
//获取机器ID,与uboot传进来的比较,返回匹配的ID值
bl __lookup_processor_type @ r5=procinfo r9=cpuid
ldr r13, =__mmap_switched @ address to jump to after mmu has been enabled
b start_kernel
linux-2.6.39-at91initMain.c
start_kernel.c
setup_arch(&command_line);
struct machine_desc *mdesc;
//在这里确定是哪个特定的开发板
mdesc = setup_machine(machine_arch_type);
for (p = __arch_info_begin; p < __arch_info_end; p++)
{
if (nr == p->nr) {
printk("Machine: %s ", p->name);
return p;
}
}
//__arch_info_begin在vmlinux.lds.S->SECTIONS->do_initcalls里面定义
//__arch_info_begin = .;
//*(.arch.info.init)
//__arch_info_end = .;
//这是9g20的单板结构体
MACHINE_START(STAMP9G20, "taskit Stamp9G20")
/* Maintainer: taskit GmbH */
.boot_params = AT91_SDRAM_BASE + 0x100,
.timer = &at91sam926x_timer,
.map_io = stamp9g20evb_map_io,
.init_irq = init_irq,
.init_machine = stamp9g20evb_board_init,
MACHINE_END
//分析MACHINE_START
linux-2.6.39-at91archarmincludeasmmachArch.h
#define MACHINE_START(_type,_name)
static const struct machine_desc __mach_desc_##_type
__used
__attribute__((__section__(".arch.info.init"))) = {
.nr = MACH_TYPE_##_type,
.name = _name,
#define MACHINE_END
};
//可见MACHINE_START(STAMP9G20, "taskit Stamp9G20")被替换为
const struct machine_desc __mach_desc_STAMP9G20
__used
__attribute__((__section__(".arch.info.init"))) = {
.nr = MACH_TYPE_STAMP9G20,
.name = "taskit Stamp9G20",
//这个MACH_TYPE_STAMP9G20 = 1824,如果要用9g20,uboot传入的机器号就必须是1824
当匹配时,就会返回这个machine_desc结构体指针
//注意MACHINE_START是在单板描述符上,强制加了.nr 和 .name 最终是
/* Maintainer: taskit GmbH */
.nr = MACH_TYPE_STAMP9G20,
.name = "taskit Stamp9G20",
.boot_params = AT91_SDRAM_BASE + 0x100,
.timer = &at91sam926x_timer,
.map_io = stamp9g20evb_map_io,
.init_irq = init_irq,
.init_machine = stamp9g20evb_board_init,
/*************************************************************************************/
//分析内核调用单板初始化函数过程
static int __init customize_machine(void)
{
/* customizes platform devices, or adds new ones */
if (machine_desc->init_machine)
machine_desc->init_machine();
return 0;
}
arch_initcall(customize_machine);
//搜索arch_initcall定义
#define arch_initcall(fn) __define_initcall("3",fn,3)
__define_initcall:
#define __define_initcall(level,fn,id)
static initcall_t __initcall_##fn##id __used
__attribute__((__section__(".initcall" level ".init"))) = fn
结果为:__initcall_customize_machine3;
被放在段 .initcall3.init里
搜索 .initcall3.init 查看在哪里定义
linux-2.6.39-at91includeasm-genericVmlinux.lds.h
#define INITCALLS
*(.initcallearly.init)
VMLINUX_SYMBOL(__early_initcall_end) = .;
*(.initcall0.init)
*(.initcall0s.init)
*(.initcall1.init)
*(.initcall1s.init)
*(.initcall2.init)
*(.initcall2s.init)
*(.initcall3.init)
*(.initcall3s.init)
*(.initcall4.init)
*(.initcall4s.init)
*(.initcall5.init)
*(.initcall5s.init)
*(.initcallrootfs.init)
*(.initcall6.init)
*(.initcall6s.init)
*(.initcall7.init)
*(.initcall7s.init)
*(.initcall8.init)
*(.initcall8s.init)
//设置被包含在INITCALLS宏里
//搜索INITCALLS
#define INIT_CALLS
VMLINUX_SYMBOL(__initcall_start) = .;
INITCALLS
VMLINUX_SYMBOL(__initcall_end) = .;
//搜索INIT_CALLS
linux-2.6.39-at91archarmkernelvmlinux.lds.S
SECTIONS
{
_stext = .;
_sinittext = .;
INIT_CALLS
__init_end = .;
}
//到这里我们就分析清楚了 machine的初始化函数是存放在
INIT_CALLS->*(.initcall3.init)中,
//INIT_CALLS被包含在vmlinux.lds.S文件
SECTIONS->_stext = .; 和 __init_end = .;之间
//回到do_initcalls函数,这里调用所有__early_initcall_end---__initcall_end之间的函数指针
do_initcalls();
{
initcall_t *fn;
for (fn = __early_initcall_end; fn < __initcall_end; fn++)
do_one_initcall(*fn);
customize_machine;
}
//__early_initcall_end在INITCALL定义
VMLINUX_SYMBOL(__early_initcall_end) = .;
//__initcall_end在vmlinux.lds.S的SECTIONS定义
__init_end = .;
//do_initcalls会调用customize_machine函数初始化单板初始化函数
static int __init customize_machine(void)
{
/* customizes platform devices, or adds new ones */
if (machine_desc->init_machine)
machine_desc->init_machine();
return 0;
}
MACHINE_START(STAMP9G20, "taskit Stamp9G20")
/* Maintainer: taskit GmbH */
.boot_params = AT91_SDRAM_BASE + 0x100,
.timer = &at91sam926x_timer,
.map_io = stamp9g20evb_map_io,
.init_irq = init_irq,
.init_machine = stamp9g20evb_board_init,
MACHINE_END
//就是这样调用
machine_desc->init_machine();
stamp9g20evb_board_init();
//分析机器号的生成
linux-2.6.39-at91archarm ools
目录下有3个文件
gen-mach-types
mach-types
Makefile
//查看makefile
include/generated/mach-types.h: $(src)/gen-mach-types $(src)/mach-types
@echo ‘ Generating [email protected]‘
@mkdir -p $(dir [email protected])
$(Q)$(AWK) -f $^ > [email protected] || { rm -f [email protected]; /bin/false; }
可见会生成一个文件include/generated/mach-types.h
它的材料是 gen-mach-types 和 mach-types
//查看mach-types
stamp9g20 MACH_STAMP9G20 STAMP9G20 1824
//查看gen-mach-types
BEGIN { nr = 0 }
/^#/ { next }
/^[ ]*$/ { next }
NF == 4 {
machine_is[nr] = "machine_is_"$1;
config[nr] = "CONFIG_"$2;
mach_type[nr] = "MACH_TYPE_"$3;
num[nr] = $4; nr++
}
NF == 3 {
machine_is[nr] = "machine_is_"$1;
config[nr] = "CONFIG_"$2;
mach_type[nr] = "MACH_TYPE_"$3;
num[nr] = ""; nr++
}
printf("/* see arch/arm/kernel/arch.c for a description of these */ ");
for (i = 0; i < nr; i++)
if (num[i] ~ /..*/)
printf("#define %-30s %d ", mach_type[i], num[i]);
//可见include/generated/mach-types.h内容是在
printf("#define %-30s %d ", mach_type[i], num[i]); 打印出来的
例如:stamp9g20 MACH_STAMP9G20 STAMP9G20 1824
会被打印为
#define MACH_TYPE_STAMP9G20 1824
//MACH_TYPE_STAMP9G20 这个值就是MACHINE_START(STAMP9G20, "taskit Stamp9G20")要用到的
//内核就是这样启动特定的开发板的
5.总结:启动流程
satrt_kernel
setup_arch() //确定启动那个开发板,返回一个machine_desc结构体指针
rest_init() //这里才是真正的调用机器的初始化,1.注册设备 2.注册驱动
kernel_init()
do_basic_setup()
driver_init() //创建设备节点,注册各种总线
do_initcalls() //1.这里真正调用开发板的init_machine函数,里面会 注册设备
//2.这里会调用驱动注册函数
for(fn = _early_initcall_end;fn < _initcall_end;fn++)
{
do_one_initcall(*fn);
}
以上是关于arm 启动流程 2016.08.11的主要内容,如果未能解决你的问题,请参考以下文章