内核启动流程
内核启动分为两个阶段,内核引导阶段和内核启动阶段。内核引导阶段又可细分为,自解压搬运,内核启动前预处理。首先介绍自解压搬运流程,再介绍内核启动过程。
(一)内核引导阶段(自解压搬运)
前面章节已经介绍过bootloadert跳转到内核运行的过程,本节主要介绍内核自解压启动流程。
上一节介绍了zImage文件的生成过程,zImage是可执行文件格式,是由arch/arm/boot/compressed/vmlinux文件通过objcopy转化的。而arch/arm/boot/compressed/vmlinux则是由arch/arm/boot/compressed/vmlinux.lds , arch/arm/boot/compressed/head.o ,
arch/arm/boot/compressed/piggy.o,arch/arm/boot/compressed/misc.o这三个文件生成的。这三个文件是我们研究的重点。
zImage文件的起始地址由arch/arm/boot/compressed/vmlinux.lds决定,而vmlinux.lds是由同目录下的vmlinux.lds.in文件生成。
阅读这个文件之前需要了解下lds文件。Lds文件称为链接脚本,每个链接过程都由lds控制。主要作用是规定如何把输入文件中的section放到输出文件内,并控制输出文件内各部分在程序地址空间内的布局。目标文件的每个section至少包含两个信息: 名字和大小。
链接脚本由一系列命令组成, 每个命令由一个关键字(一般在其后紧跟相关参数)或一条对符号的赋值语句组成. 命令由分号‘;’分隔开。
文件名或格式名内如果包含分号’;'或其他分隔符, 则要用引号‘”’将名字全称引用起来. 无法处理含引号的文件名。/* */之间的是注释。
文件vmlinux.lds中Line10
OUTPUT_ARCH(arm)
命令OUTPUT_ARCH指定一个特定的输出机器架构。可以使用带-f选项的objdump程序查看一个目标文件的架构。
文件vmlinux.lds中Line11
ENTRY(_start)
程序第一个被执行的指令称之为入口点。命令ENTRY设置入口点,参数是一个符号名。
文件vmlinux.lds中Line19
. = 0;
把定位器符号置为0。
Line20、23符号赋值,此处忽略不看。
Line24 *(.start)即对应arch/arm/boot/compressed/head.S中Line115
.section ".start", #alloc, #execinstr
Line33 *(.piggydata)对应arch/arm/boot/compressed/piggy.S中Line1
.section .piggydata,#alloc
从这里我们可以知道zImage是从head.S中开始运行,内核压缩镜像放到了head.s和misc.c程序最后。
我们开始看arch/arm/boot/compressed/head.S文件。
Line121~124起延时功能。
Line130~131 保存bootloader传入的其中两个参数,硬件ID和参数指针
Line164~171
adr r0, LC0
ARM( ldmia r0, {r1, r2, r3, r4, r5, r6, ip, sp} )
THUMB( ldmia r0, {r1, r2, r3, r4, r5, r6, ip} )
THUMB( ldr sp, [r0, #28] )
subs r0, r0, r1 @ calculate the delta offset
将LCO当前运行处的地址传入r0,即程序开始于0x30008000的地址。
接着将LCO符号处的数据保存到r1, r2, r3, r4, r5, r6, ip, sp这些寄存器中。值为链接时确定的地址,即开始于0的地址。
r0保存的是运行实时地址与编译地址的偏差。
Line302~309
LC0: .word LC0 @ r1
.word __bss_start @ r2
.word _end @ r3
.word zreladdr @ r4
.word _start @ r5
.word _got_start @ r6
.word _got_end @ ip
.word user_stack+4096 @ sp
Line181~183
add r5, r5, r0
add r6, r6, r0
add ip, ip, r0
Line193~195
add r2, r2, r0
add r3, r3, r0
add sp, sp, r0
此处修正了r2,r3,r5,r6,ip,sp的值
Line200~204
1: ldr r1, [r6, #0] @ relocate entries in the GOT
add r1, r1, r0 @ table. This fixes up the
str r1, [r6], #4 @ C references.
cmp r6, ip
blo 1b
此处修正GOT表。GOT(Global Offset Table)表中每一项都是本运行模块要引用的一个全局变量或函数的地址。可以用GOT表来间接引用全局变量、函数,也可以把GOT表的首地址作为一个基准,用相对于该基准的偏移量来引用静态变量、静态函数。
Line220~226
not_relocated: mov r0, #0
1: str r0, [r2], #4 @ clear bss
str r0, [r2], #4
str r0, [r2], #4