您的位置:首页 > 运维架构 > Linux

arm-linux 启动流程之-- 进入内核(转)

2012-07-05 21:26 405 查看
见:http://blog.csdn.net/dansen_xu/archive/2007/08/13/1741576.aspx
还是从编译链接生成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中

这个程序不是特别复杂,细心看看还是能大概看懂,我也不能去一一注释

这里有一个流程图



到了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

完了

另一分析见:http://www.eetop.cn/blog/html/45/11145.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: