Linux学习之源码1:入口流程
有地方看到,启动流程是arch/arm/boot/compressed/head.s ----->调用arch/arm/boot/compressed/misc.c的decompress_kernel()
函数解压内核。---->arch/arm/kernel/head-common.S初始化 ---->init/main.c的asmlinkage void __init start_kernel(void)
注意在arch/arm/kernel/smp.c文件中有一个启动多核处理器的函数 void __init smp_prepare_boot_cpu(void),被init/main.c的asmlinkage void __init start_kernel(void)调用
有人说是ARM Linux的启动代码有两处,一处是经过压缩的,一处是没有经过压缩的,压缩的最终还是会调用没有压缩的,没有压缩的入口在arch/arm/kernel/head.S文件中。看起来是这样的,第二种启动流程是在boot/compressed文件夹中,证实是压缩启动流程.
以32位arm为例,在3.9.7内核版本上分析。
1、 压缩启动流程
/arch/arm/boot/compressed/head.S
1)首先定义了一系列的宏,如writeb、loadsp、kputc、kphex、debug_reloc_start、debug_reloc_end;
2)使用r0、r7、r8分别保存r0、r1、r2的值(分别为0、MachineType、ATAG指针),r9是cpsr的值;
3)r2保存cpsr,判断最后2bit是否0,即判断是否user mode,不是的话则设置为svc模式,并把r9中的cpsr保存到spsr;
4)把最终的kernel文件的地址放在r4中,一种方式是通过pc&0xf8000000 + TEXT_OFFSET;一种方式是zreladdr;TEXT_OFFSET = ??
zreladdr在makefile中定义,但是来龙去脉不清楚。
5)跳转到cache_on
6)adr r0, LC0 //将LC0标签的地址赋给r0,LC0标签处的内容如下图所示
ldmia r0, {r1, r2, r3, r6, r10, r11, r12} //将r0地址开始的7个word数据分别保存到r1, r2, r3, r6, r10, r11, r12中
设置堆栈,是LC0开始第8个word的值
/*
* We might be running at a different address. We need
* to fix up various pointers.
*/
sub r0, r0, r1 @ calculate the delta offset
add r6, r6, r0 @ _edata
add r10, r10, r0 @ inflated kernel size location
计算kernel size存放的地址并保存在r10中
LC0: .word LC0 @ r1
.word __bss_start @ r2
.word _end @ r3
.word _edata @ r6
.word input_data_end - 4 @ r10 (inflated size location)
.word _got_start @ r11
.word _got_end @ ip
.word .L_user_stack_end @ sp
.size LC0, . - LC0
7)从r10开始的地址依次读取4个字节,拼成32bit存放在r9中,即是kernel size(解压缩的大小)的值;
8)分配空间在堆栈之上,堆栈最大64KB(0x10000),将sp + 0x10000的值赋给r10;
9)初始化dtb的大小为0,并保存在r5中;
10)如果有device trees (dtb) appended to zImage,一些处理,暂时先不分析
截止目前,各寄存器的内容如下图所示:
/*
* r0 = delta
* r2 = BSS start
* r3 = BSS end
* r4 = final kernel address
* r5 = appended dtb size (still unknown)
* r6 = _edata
* r7 = architecture ID
* r8 = atags/device tree pointer
* r9 = size of decompressed image
* r10 = end of this image, including bss/stack/malloc space if non XIP
* r11 = GOT start
* r12 = GOT end
* sp = stack pointer
*
* if there are device trees (dtb) appended to zImage, advance r10 so that the
* dtb data will get relocated along with the kernel if necessary.
*/
11)判断是否会地址覆盖,
r10 = r10 + 16KB;
r4 > r10则不会覆盖,否则r10 = r4 + r9,r10 < r9则不会覆盖否则就会覆盖
覆盖时的处理这里先不分析
12)orrs r1, r0, r5 //orr逻辑与,r5为0,当r0及delta也为0,跳转到not_relocated
beq not_relocated //否则就需要对地址进行跳转,跳转部分我们暂时先不看了
13)清零bss段,从r2开始的地址到r3开始的地址
14)解压缩C代码环境准备及解压缩
decompress_kernel(
unsigned long output_start, //(r4中的值,移送到r0)
unsigned long free_mem_ptr_p, //(sp的值,移送到r1)
unsigned long free_mem_ptr_end_p, //(sp值+64KB,移送到r2)
int arch_id //(r7中的值,移送到r3)
);
分别准备4个参数,保存在r0~r3中,然后调用decompress_kernel函数
15)刷cache、关闭cache,分别调用cache_clean_flush、cache_off
16)再把r7、r8中的architecture number、atags pointer恢复到r1、r2中
跳转到__enter_kernel标签,在这里,将r0恢复到0,r4中的解压缩内核地址移送到pc中,跳转到r4中,完成从压缩内核流程向非压缩内核流程转换。
2、非压缩启动流程
代码入口是在arch\arm\kernel\head.s,然后跳转到main.c中的start_kernel。
确保SVC模式和中断关闭
|
获取cpuid
|
判断ATAG,貌似只判断了ATAG_CORE
|
__create_page_tables,创建页表
|
将__mmap_switched地址保存在r13中
|
跳转到__enable_mmu,
在__turn_mmu_on的最后跳转到r13中,即跳转到__mmap_switched
|
在__mmap_switched的最后 b start_kernel
这段GCC汇编看的好痛苦,有些在as文档中都找不到,也不知道是什么意思,所以还只是个大概。
http://linux.chinaunix.net/bbs/thread-1021226-1-1.html中有详细的描述这段汇编。
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。