Arm linux 内核启动流程

Arm linux 内核启动流程

还是从编译链接生成vmlinux的过程来看吧,由一大堆.o文件链接而成,第一个就是
kernel/arch/arm/kernel/head-armv.o ,而且我们还看到了
lds链接文件kernel/arch/arm/vmlinux.lds,先把它分析一下
ENTRY(stext) //入口点是stext 应该就在head-armv.s中了
SECTIONS
{
. = 0xC0008000;  //基址,是内核开始的虚拟地址
.init : {   /* Init code and data  */
  _stext = .;
  __init_begin = .;
   *(.text.init)
  __proc_info_begin = .;
   *(.proc.info)
  __proc_info_end = .;
  __arch_info_begin = .;
   *(.arch.info)
  __arch_info_end = .;
  __tagtable_begin = .;
   *(.taglist)
  __tagtable_end = .;
   *(.data.init)
  . = ALIGN(16);
  __setup_start = .;
   *(.setup.init)
  __setup_end = .;
  __initcall_start = .;
   *(.initcall.init)
  __initcall_end = .;
  . = ALIGN(4096);
  __init_end = .;
}
关于虚拟地址和物理地址的:使用MMU后,系统就会使用虚拟地址,通过MMU来指向
实际物理地址而在这里我们的0xC0008000实际物理地址就是0x30008000,
具体关于MMU的介绍参考《ARM体系结构与编程》。
到head-armv.s找到程序的入口
  .section ".text.init",#alloc,#execinstr
  .type stext, #function
ENTRY(stext)
  mov r12, r0
  mov r0, #F_BIT | I_BIT | MODE_SVC @ make sure svc mode
  msr cpsr_c, r0   @ and all irqs disabled
  bl __lookup_processor_type
  teq r10, #0    @ invalid processor?
  moveq r0, #'p'   @ yes, error 'p'
  beq __error
  bl __lookup_architecture_type
  teq r7, #0    @ invalid architecture?
  moveq r0, #'a'   @ yes, error 'a'
  beq __error
  bl __create_page_tables
  adr lr, __ret   @ return address
  add pc, r10, #12   @ initialise processor
来看看上一句跳到哪里去了
去追寻r10的值,是在__lookup_processor_type子函数中赋的
__lookup_processor_type:
  adr r5, 2f   //r5 标号2的地址 基址是0x30008000
  ldmia r5, {r7, r9, r10} //r7=__proc_info_end  r9=__proc_info_begin
  sub r5, r5, r10  //r10 标号2的链接地址 基址是0xc0008000
  add r7, r7, r5   @ to our address space
  add r10, r9, r5  //r10 变换为基址是0x30008000的__proc_info_begin
2:  .long __proc_info_end
  .long __proc_info_begin
  .long 2b
这样r10中存放的是__proc_info_begin的地址,因为现在我们还没有打开MMU
所以还是需要把基址变换到0x30008000,接着我们就去找__proc_info_begin吧
注意到在上面的vmlinux.lds中有这个标号,下来链接的是.proc.info段,
在kernel/arch/arm/mm/proc-arm920.s的最后找到了这个段
.section ".proc.info", #alloc, #execinstr

.type __arm920_proc_info,#object
__arm920_proc_info:
.long 0x41009200
.long 0xff00fff0
.long 0x00000c1e   @ mmuflags
b __arm920_setup
ok,这样我们就知道add pc, r10, #12跳到哪里去了,因为这个地址刚好放了条跳转语句
注意了b语句用的都是相对地址,所以不需要变换地址,反正是跳到__arm920_setup,而且
上一条语句是adr lr, __ret,设定了__arm920_setup的返回地址是__ret,所以执行完
__arm920_setup后回到head-armv.s的__ret标号继续执行.
__ret:  ldr lr, __switch_data
  mcr p15, 0, r0, c1, c0 //注意这里了,在这里打开了MMU
  mov r0, r0
  mov r0, r0
  mov r0, r0
  mov pc, lr //跳到__mmap_switched,这里已经用了虚拟地址了吧
// 这条指令ldr lr, __switch_data加载的__mmap_switched地址就是虚拟地址啊
__switch_data: .long __mmap_switched
从__mmap_switched一路执行下来,就要调到C语言代码中去了
  b SYMBOL_NAME(start_kernel) //在kernel/init/main.c中
这个程序不是特别复杂,细心看看还是能大概看懂,我也不能去一一注释
这里有一个流程图

clip_image001
到了C语言中就不是很难理解了
lock_kernel();
printk(linux_banner);
setup_arch(&command_line);
printk("Kernel command line: %s/n", saved_command_line);
parse_options(command_line);
trap_init();
init_IRQ();
sched_init();
softirq_init();
time_init();
就是一大堆初始化工作,追着每个函数去看好了

start_kernel最后调用的一个函数
static void rest_init(void)
{
kernel_thread(init, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGNAL);
unlock_kernel();
current->need_resched = 1;
  cpu_idle();
}
用kernel_thread建立了一个init进程,执行的是main.c中的init函数
lock_kernel();
do_basic_setup();
在do_basic_setup中调用了do_initcalls函数
各种驱动都是在do_initcalls(void)中完成的
static void __init do_initcalls(void)
{
initcall_t *call;

call = &__initcall_start;
do {
  (*call)();
  call++;
} while (call < &__initcall_end);

flush_scheduled_tasks();
}
__initcall_start也是在vmlinux.lds中赋值的,那就需要找到.initcall.ini这个段
在kernel/include/linux/init.h中可以找到
#define __init_call __attribute__ ((unused,__section__ (".initcall.init")))
typedef int (*initcall_t)(void);
#define __initcall(fn)        /
static initcall_t __initcall_##fn __init_call = fn
仔细研究下就发现这是把初始化函数的地址放到了.initcall.init段中
这样就可以不断调用驱动的初始化函数了
如果没有定义MODULE,那么#define module_init(x) __initcall(x);
所以如果要把驱动的编译进内核就很简单了吧
init的最后
if (execute_command)
  execve(execute_command,argv_init,envp_init);
execute_command与ppcboot传的命令行参数是有关的哦,就是init=/linuxrc
这样就要去执行根目录下的linuxrc脚本,这个脚本会去执行busybox
而busybox又去执行/etc/init.d/rcS脚本,这个脚本又去执行/usr/etc/rc.local
完了

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章