内核启动流程分析:
arch/arm/boot/compressed/head .S 文件分析:正真启动内核前的工作
120行开始:
start函数定义
start:
.type start,#function@声明start为函数类型
.rept 8
mov r0, r0 @相当于八条空操作
.endr
b 1f
.word 0x016f2818@ Magic numbers to help the loader用于uboot确认kernel存在
.word start@ absolute load/run zImage address 由bootp.lds文件定义
.word _edata@ zImage end address
1: mov r7, r1 @ save architecture ID 此处r1和r2的值来自于uboot传递
mov r8, r2 @ save atags pointer
#ifndef __ARM_ARCH_2__ @因为没有定义ARM的第二个架构,所以要执行下面
/*
* Booting from Angel - need to enter SVC mode and disable
* FIQs/IRQs (numeric definitions from angel arm.h source).
* We only do this if we were in user mode on entry.
*/
mrs r2, cpsr@ get current mode 在进入svc管理模式(也就是保护模式)前拷贝当前状态寄存器
tst r2, #3@ not user?
@判断当前所处的模式是否为普通用户模式,因为只有user mode下cpsr低两位为0
@uboot进入kernel已经是svc32 mode,如果是从angle进入则是user mode
bne not_angel @如果不是user mode跳到下面not_angel
mov r0, #0x17@ angel_SWIreason_EnterSVC 如果是user mode,软件中断设置成svc模式M[4:0]:10011:supervisor mode
swi 0x123456@ angel_SWI_ARM 为什么是0x123456,只要后面的的合法的24为立即数就可以
@执行时,程序进入SWI异常处理子程序SoftwareInterrupt,然后根据R0的内容跳转到相应的子分支,这里即进入svc模式
not_angel:
mrs r2, cpsr@ 拷贝当前状态寄存器
orr r2, r2, #0xc0@ 将屏蔽中断的值合并到r2中
msr cpsr_c, r2 @ turn off interrupts(IRQ & FIQ) to prevent angel from running
#else @无用分支
teqp pc, #0x0c000003@ turn off interrupts
#endif
.text
adr r0, LC0 @保存标号LC0地址,LC0在后面定义,adr指令和当前程序运行的位置有关(连接地址都是0x0)
ldmia r0, {r1, r2, r3, r4, r5, r6, ip, sp} @此时r0指向LC0的开始地址,根据下面LC0的定义可知,r1将被赋予LC0的地址
subs r0, r0, r1@ calculate the delta offset subs:影响cpsr的z位
@这里获得当前运行地址与链接地址的偏移量,存入r0中为0x30008000
@ if delta is zero, we are
beq not_relocated@ running at the address we were linked at. r0=r1:跳转,not_relocata不重新定向加载
@这中间就存在一个固定的地址偏移,在s3c2440中是0x30008000
/*
* We're running at a different address. We need to fix
* up various pointers:
* r5 - zImage base address 是zImage的基地址
* r6 - GOT start 是全局偏移表的开始地址
* ip - GOT end
*
* 要重定向的情况
*/
add r5, r5, r0
add r6, r6, r0
add ip, ip, r0
#ifndef CONFIG_ZBOOT_ROM @这个也没有定义,要执行下面的东东
/*
* If we're running fully PIC === CONFIG_ZBOOT_ROM = n,
* we need to fix up pointers into the BSS region.
* r2 - BSS start
* r3 - BSS end
* sp - stack pointer
*/
add r2, r2, r0
add r3, r3, r0
add sp, sp, r0
/*
* Relocate all entries in the GOT table.
*/
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
#else
/*
* Relocate entries in the GOT table. We only relocate
* the entries that are outside the (relocated) BSS region.
*/
1: ldr r1, [r6, #0] @ relocate entries in the GOT
cmp r1, r2@ entry < bss_start ||
cmphs r3, r1@ _end < entry
addlo r1, r1, r0@ table. This fixes up the
str r1, [r6], #4@ C references.
cmp r6, ip
blo 1b
#endif
############################################################################
not_relocated: movr0, #0
1: str r0, [r2], #4 @ clear bss(Init BSS section) 看上面就明白
str r0, [r2], #4
str r0, [r2], #4
str r0, [r2], #4
cmp r2, r3
blo 1b @条件跳转:小于跳转
/*
* The C runtime environment should now be setup
* sufficiently. Turn the cache on, set up some
* pointers, and start decompressing.
*设置进入c函数前的准备工作,打开cache,设置sp指针,开始进行解压
*/
bl cache_on @这个标号在后面有定义,过程比较复杂,涉及的相关的流程如下:
@cache_on --> b call_cache_fn --> 通过查表调用了 __armv4_cache_on --> b __setup_mmu
mov r1, sp @ malloc space above stack 由于此时sp指向4k栈的顶端,注意是“顶端”,这个很关键
add r2, sp, #0x10000 @ 64k max:r2保存的是解压函数需要的缓冲内存的结束地址
/*
* 检查是否会覆盖压缩内核映像本身
* Check to see if we will overwrite ourselves.
* r4 = final kernel address
* r5 = start of this image
* r2 = end of malloc space (and therefore this image)
* We basically want:
* r4 >= r2 -> OK
* r4 + image length <= r5 -> OK
*/
cmp r4, r2 @第一个判断
bhs wont_overwrite @大于等于跳转
@r4为内核执行地址,此时为0X30008000,r2此时为用户栈定,即解压函数所需内存缓冲的开始处,
@显然r4 < r2所以不会跳转。
sub r3, sp, r5 @ > compressed kernel size压缩的内核文件大小
add r0, r4, r3, lsl #2@ allow for 4x expansion
cmp r0, r5 @第二个判断
bls wont_overwrite @小于等于
@r5是内核映像的开始地址0X30008000,r6为内核映像大小,r4为解压后内核开始地址,
@此时为0X30008000,r5为解压前映存放的开始位置,此时也为0X30008000。下面的判
@断是,看解压后的内核是不是会覆盖未解压的映像。显然是覆盖的,所以是不会跳转的。
@注意:内核映像解压后不会超过解压前的4倍大小。
@就是说nand启动到这里,执行完bls wont_overwrite就完成了自引导过程。
mov r5, r2@ decompress after malloc space
mov r0, r5@r5做中介获得r2的一份拷贝
mov r3, r7
bl decompress_kernel @调用解压内核c函数 :需要4个参数
@函数原型decompress_kernel(ulg output_start, ulg free_mem_ptr_p, ulg free_mem_ptr_end_p,int arch_id)
@r0->output_start:指解压后内核输出的起始位置
@r1->free_mem_ptr_p:解压缓冲区起始地址
@r2->free_mem_ptr_end_p:解压缓冲区结束地址
@r3->arch_id:机器ID
@调用完解压函数,它的返回值“kernel size”放在r0中
add r0, r0, #127 + 128@ alignment + stack
bic r0, r0, #127@ align the kernel length
/*
* r0 = decompressed kernel length
* r1-r3 = unused
* r4 = kernel execution address
* r5 = decompressed kernel start
* r6 = processor ID
* r7 = architecture ID
* r8 = atags pointer
* r9-r14 = corrupted
*/
add r1, r5, r0@ end of decompressed kernel
adr r2, reloc_start
ldr r3, LC1
add r3, r2, r3
1: ldmia r2!, {r9 - r14} @ copy relocation code
stmia r1!, {r9 - r14}
ldmia r2!, {r9 - r14}
stmia r1!, {r9 - r14}
cmp r2, r3
blo 1b
add sp, r1, #128@ relocate the stack
bl cache_clean_flush
add pc, r5, r0@ call relocation code
/*
* We're not in danger of overwriting ourselves. Do this the simple way.
*
* r4 = kernel execution address
* r7 = architecture ID
*/
@不会覆盖时调用
wont_overwrite: movr0, r4
mov r3, r7
bl decompress_kernel
b call_kernel
.type LC0, #object @定义一个数据对象,类似于C语言中的结构体
LC0: .word LC0 @ r1
.word __bss_start @ r2
.word _end @ r3
.word zreladdr @ r4 zImage的运行地址0x30008000
.word _start @ r5 压缩内核映像的起始地址0x30008000
.word _got_start @ r6
.word _got_end @ ip
.word user_stack+4096 @ sp
LC1: .word reloc_end - reloc_start
.size LC0, . - LC0
/*
* All code following this line is relocatable. It is relocated by
* the above code to the end of the decompressed kernel image and
* executed there. During this time, we have no stacks.
*
* r0 = decompressed kernel length
* r1-r3 = unused
* r4 = kernel execution address
* r5 = decompressed kernel start
* r6 = processor ID
* r7 = architecture ID
* r8 = atags pointer
* r9-r14 = corrupted
*/
@由reloc_start至reloc_end的代码复制解压后的内核代码到0x30008000处,并调用call_kernel跳转到0x30008000处执行
reloc_start: addr9, r5, r0
sub r9, r9, #128@ do not copy the stack
debug_reloc_start
mov r1, r4
1:
.rept 4
ldmia r5!, {r0, r2, r3, r10 - r14}@ relocate kernel
stmia r1!, {r0, r2, r3, r10 - r14}
.endr
cmp r5, r9
blo 1b
add sp, r1, #128@ relocate the stack
debug_reloc_end
call_kernel: blcache_clean_flush
bl cache_off
mov r0, #0@ must be zero
mov r1, r7@ restore architecture number
mov r2, r8@ restore atags pointer
arch/arm/boot/compressed/head .S 文件分析:正真启动内核前的工作
120行开始:
start函数定义
start:
.type start,#function@声明start为函数类型
.rept 8
mov r0, r0 @相当于八条空操作
.endr
b 1f
.word 0x016f2818@ Magic numbers to help the loader用于uboot确认kernel存在
.word start@ absolute load/run zImage address 由bootp.lds文件定义
.word _edata@ zImage end address
1: mov r7, r1 @ save architecture ID 此处r1和r2的值来自于uboot传递
mov r8, r2 @ save atags pointer
#ifndef __ARM_ARCH_2__ @因为没有定义ARM的第二个架构,所以要执行下面
/*
* Booting from Angel - need to enter SVC mode and disable
* FIQs/IRQs (numeric definitions from angel arm.h source).
* We only do this if we were in user mode on entry.
*/
mrs r2, cpsr@ get current mode 在进入svc管理模式(也就是保护模式)前拷贝当前状态寄存器
tst r2, #3@ not user?
@判断当前所处的模式是否为普通用户模式,因为只有user mode下cpsr低两位为0
@uboot进入kernel已经是svc32 mode,如果是从angle进入则是user mode
bne not_angel @如果不是user mode跳到下面not_angel
mov r0, #0x17@ angel_SWIreason_EnterSVC 如果是user mode,软件中断设置成svc模式M[4:0]:10011:supervisor mode
swi 0x123456@ angel_SWI_ARM 为什么是0x123456,只要后面的的合法的24为立即数就可以
@执行时,程序进入SWI异常处理子程序SoftwareInterrupt,然后根据R0的内容跳转到相应的子分支,这里即进入svc模式
not_angel:
mrs r2, cpsr@ 拷贝当前状态寄存器
orr r2, r2, #0xc0@ 将屏蔽中断的值合并到r2中
msr cpsr_c, r2 @ turn off interrupts(IRQ & FIQ) to prevent angel from running
#else @无用分支
teqp pc, #0x0c000003@ turn off interrupts
#endif
.text
adr r0, LC0 @保存标号LC0地址,LC0在后面定义,adr指令和当前程序运行的位置有关(连接地址都是0x0)
ldmia r0, {r1, r2, r3, r4, r5, r6, ip, sp} @此时r0指向LC0的开始地址,根据下面LC0的定义可知,r1将被赋予LC0的地址
subs r0, r0, r1@ calculate the delta offset subs:影响cpsr的z位
@这里获得当前运行地址与链接地址的偏移量,存入r0中为0x30008000
@ if delta is zero, we are
beq not_relocated@ running at the address we were linked at. r0=r1:跳转,not_relocata不重新定向加载
##############################################################################
@从nand启动时,由于内核已被搬移,标号LC0的地址不是以地址0为偏移的,@这中间就存在一个固定的地址偏移,在s3c2440中是0x30008000
/*
* We're running at a different address. We need to fix
* up various pointers:
* r5 - zImage base address 是zImage的基地址
* r6 - GOT start 是全局偏移表的开始地址
* ip - GOT end
*
* 要重定向的情况
*/
add r5, r5, r0
add r6, r6, r0
add ip, ip, r0
#ifndef CONFIG_ZBOOT_ROM @这个也没有定义,要执行下面的东东
/*
* If we're running fully PIC === CONFIG_ZBOOT_ROM = n,
* we need to fix up pointers into the BSS region.
* r2 - BSS start
* r3 - BSS end
* sp - stack pointer
*/
add r2, r2, r0
add r3, r3, r0
add sp, sp, r0
/*
* Relocate all entries in the GOT table.
*/
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
#else
/*
* Relocate entries in the GOT table. We only relocate
* the entries that are outside the (relocated) BSS region.
*/
1: ldr r1, [r6, #0] @ relocate entries in the GOT
cmp r1, r2@ entry < bss_start ||
cmphs r3, r1@ _end < entry
addlo r1, r1, r0@ table. This fixes up the
str r1, [r6], #4@ C references.
cmp r6, ip
blo 1b
#endif
############################################################################
not_relocated: movr0, #0
1: str r0, [r2], #4 @ clear bss(Init BSS section) 看上面就明白
str r0, [r2], #4
str r0, [r2], #4
str r0, [r2], #4
cmp r2, r3
blo 1b @条件跳转:小于跳转
/*
* The C runtime environment should now be setup
* sufficiently. Turn the cache on, set up some
* pointers, and start decompressing.
*设置进入c函数前的准备工作,打开cache,设置sp指针,开始进行解压
*/
bl cache_on @这个标号在后面有定义,过程比较复杂,涉及的相关的流程如下:
@cache_on --> b call_cache_fn --> 通过查表调用了 __armv4_cache_on --> b __setup_mmu
mov r1, sp @ malloc space above stack 由于此时sp指向4k栈的顶端,注意是“顶端”,这个很关键
add r2, sp, #0x10000 @ 64k max:r2保存的是解压函数需要的缓冲内存的结束地址
/*
* 检查是否会覆盖压缩内核映像本身
* Check to see if we will overwrite ourselves.
* r4 = final kernel address
* r5 = start of this image
* r2 = end of malloc space (and therefore this image)
* We basically want:
* r4 >= r2 -> OK
* r4 + image length <= r5 -> OK
*/
cmp r4, r2 @第一个判断
bhs wont_overwrite @大于等于跳转
@r4为内核执行地址,此时为0X30008000,r2此时为用户栈定,即解压函数所需内存缓冲的开始处,
@显然r4 < r2所以不会跳转。
sub r3, sp, r5 @ > compressed kernel size压缩的内核文件大小
add r0, r4, r3, lsl #2@ allow for 4x expansion
cmp r0, r5 @第二个判断
bls wont_overwrite @小于等于
@r5是内核映像的开始地址0X30008000,r6为内核映像大小,r4为解压后内核开始地址,
@此时为0X30008000,r5为解压前映存放的开始位置,此时也为0X30008000。下面的判
@断是,看解压后的内核是不是会覆盖未解压的映像。显然是覆盖的,所以是不会跳转的。
@注意:内核映像解压后不会超过解压前的4倍大小。
@就是说nand启动到这里,执行完bls wont_overwrite就完成了自引导过程。
mov r5, r2@ decompress after malloc space
mov r0, r5@r5做中介获得r2的一份拷贝
mov r3, r7
bl decompress_kernel @调用解压内核c函数 :需要4个参数
@函数原型decompress_kernel(ulg output_start, ulg free_mem_ptr_p, ulg free_mem_ptr_end_p,int arch_id)
@r0->output_start:指解压后内核输出的起始位置
@r1->free_mem_ptr_p:解压缓冲区起始地址
@r2->free_mem_ptr_end_p:解压缓冲区结束地址
@r3->arch_id:机器ID
@调用完解压函数,它的返回值“kernel size”放在r0中
add r0, r0, #127 + 128@ alignment + stack
bic r0, r0, #127@ align the kernel length
/*
* r0 = decompressed kernel length
* r1-r3 = unused
* r4 = kernel execution address
* r5 = decompressed kernel start
* r6 = processor ID
* r7 = architecture ID
* r8 = atags pointer
* r9-r14 = corrupted
*/
add r1, r5, r0@ end of decompressed kernel
adr r2, reloc_start
ldr r3, LC1
add r3, r2, r3
1: ldmia r2!, {r9 - r14} @ copy relocation code
stmia r1!, {r9 - r14}
ldmia r2!, {r9 - r14}
stmia r1!, {r9 - r14}
cmp r2, r3
blo 1b
add sp, r1, #128@ relocate the stack
bl cache_clean_flush
add pc, r5, r0@ call relocation code
/*
* We're not in danger of overwriting ourselves. Do this the simple way.
*
* r4 = kernel execution address
* r7 = architecture ID
*/
@不会覆盖时调用
wont_overwrite: movr0, r4
mov r3, r7
bl decompress_kernel
b call_kernel
.type LC0, #object @定义一个数据对象,类似于C语言中的结构体
LC0: .word LC0 @ r1
.word __bss_start @ r2
.word _end @ r3
.word zreladdr @ r4 zImage的运行地址0x30008000
.word _start @ r5 压缩内核映像的起始地址0x30008000
.word _got_start @ r6
.word _got_end @ ip
.word user_stack+4096 @ sp
LC1: .word reloc_end - reloc_start
.size LC0, . - LC0
/*
* All code following this line is relocatable. It is relocated by
* the above code to the end of the decompressed kernel image and
* executed there. During this time, we have no stacks.
*
* r0 = decompressed kernel length
* r1-r3 = unused
* r4 = kernel execution address
* r5 = decompressed kernel start
* r6 = processor ID
* r7 = architecture ID
* r8 = atags pointer
* r9-r14 = corrupted
*/
@由reloc_start至reloc_end的代码复制解压后的内核代码到0x30008000处,并调用call_kernel跳转到0x30008000处执行
reloc_start: addr9, r5, r0
sub r9, r9, #128@ do not copy the stack
debug_reloc_start
mov r1, r4
1:
.rept 4
ldmia r5!, {r0, r2, r3, r10 - r14}@ relocate kernel
stmia r1!, {r0, r2, r3, r10 - r14}
.endr
cmp r5, r9
blo 1b
add sp, r1, #128@ relocate the stack
debug_reloc_end
call_kernel: blcache_clean_flush
bl cache_off
mov r0, #0@ must be zero
mov r1, r7@ restore architecture number
mov r2, r8@ restore atags pointer
movpc, r4 @ call kernel前面已经说明:r4是真正内核的开始执行的地址