分类: LINUX
2010-10-13 09:54:16
Linux启动\kernel\arch\arm\boot\compressed\ head.S分析
这段代码是linux boot后执行的第一个程序,完成的主要工作是解压内核,然后跳转到相关执行地址。这部分代码在做驱动开发时不需要改动,但分析其执行流程对是理解android的第一步
开头有一段宏定义这是gnu arm汇编的宏定义。关于GUN的汇编和其他编译器,在指令语法上有很大差别,具体可查询相关GUN汇编语法了解
另外此段代码必须不能包括重定位部分。因为这时一开始必须要立即运行的。所谓重定位,比如当编译时某个文件用到外部符号是用动态链接库的方式,那么该文件生成的目标文件将包含重定位信息,在加载时需要重定位该符号,否则执行时将因找不到地址而出错
#ifdef DEBUG//开始是调试用,主要是一些打印输出函数,不用关心
#if defined(CONFIG_DEBUG_ICEDCC)
……具体代码略
#endif
宏定义结束之后定义了一个段,
.section ".start", #alloc,
#execinstr
这个段的段名是 .start,#alloc表示Section
contains allocated data, #execinstr表示Section
contains executable instructions.
生成最终映像时,这段代码会放在最开头
.align
start:
.type start,#function
/*.type指定start这个符号是函数类型*/
.rept
8
mov
r0, r0 //将此命令重复8次,相当于nop,这里是为中断向量保存空间
.endr
b
.word 0x
.word start @
absolute load/run zImage
//此处保存了内核加载和运行的地址,实质上也是本函数的运行地址
address
.word _edata @ 内核结束地址
//注意这些地址在顶层vmlixu.lds(具体在/kernel文件夹里)里进行了定义,是链接的地址,加载内核后可能会进行重定位
1: mov
r7,
r1 @ 保存architecture ID ,这里是从bootload传递进来的
mov
r8,
r2 @ 保存参数列表 atags 指针
r1和r2中分别存放着由bootloader传递过来的architecture
ID和指向标记列表的指针。这里将这两个参数先保存。
#ifndef __ARM_ARCH_2__
/*
*
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.
*/
读取cpsr并判断是否处理器处于supervisor模式——从bootload进入kernel,系统已经处于SVC32模式;而利用angel进入则处于user模式,还需要额外两条指令。之后是再次确认中断关闭,并完成cpsr写入
Angel 是 ARM 的调试协议,一般用的 是MULTI-ICE 。ANGLE 需要在板子上有 驻留程序,然后通过 串口就可以调试了 。用过的AXD或trace调试环境的话,对此应该比较熟悉。
not_angel: //若不是通过angel调试进入内核
mrs r2,
cpsr @ turn off
interrupts to
orr r2,
r2, #0xc0 @ prevent angel
from running
msr cpsr_c,
r2 //这里将cpsr中I、F位分别置“
#else
teqp
pc, #0x
常用 TEQP
PC,#(新模式编号) 来改变模式
#endif
另外链接器会把一些处理器相关的代码链接到这个位置,也就是arch/arm/boot/compressed/head-xxx.S文件中的代码。在高通平台下,这个文件是head-msm.S连接脚是compress/vmlinux.lds,其中部分内容大致如下,在连接时,连接器根据每个文件中的段名将相同的段合在一起,比如将head.S和head-msm.S的.start段合在一起
SECTIONS
{
. = TEXT_START;
_text = .;
.text : {
_start = .;
*(.start)
*(.text)
*(.text.*)
*(.fixup)
*(.gnu.warning)
*(.rodata)
*(.rodata.*)
*(.glue_7)
*(.glue_7t)
*(.piggydata)
. = ALIGN(4);
}
_etext = .;
}
下面即进入.text段
.text
adr r0, LC0 //当前运行时LC0符号所在地址位置 ,注意,这里用的是adr指令,这个指令会根据目前PC的值,计算符号相对于PC的位置,是个相对地址。之所以这样做,是因为下面指令用到了绝对地址加载ldmia指令,必须要调整确定目前LC0的真实位置,这个位置也就是用adr来计算
ldmia r0,
{r1, r2, r3, r4, r5, r6, ip, sp}
subs r0, r0,
r1 @ //这里获得当前LCD0实际地址与链接地址 差值
//r1即是LC0的连接地址,也即由vmlinux.lds定位的地址
//差值存入r0中。
beq not_relocated
//如果相等不需要重定位,因为已经在正确的//地址运行了。重定位的原因是,MMU单元未使能,不能进行地址映射,必须要手工重定位。
下面举个简单例子说明:
如果连接地址是0xc0000000,那么LC0的连接地址假如连接为0xc0000010,那么LC0相对于连接起始地址的差为0x10,当此段代码是从0xc0000000运行的话,那么执行adr r0,LC0的值实际上按下面公式计算:
R0=PC+0x10,由于PC=连接处的值,可知,此时是在ram中运行, 同理如果是在不是在连接处运行,则假设是在0x00000000处运行,则R0=0x00000000+0x10,可知,此时不是在ram的连接处运行。
上面这几行代码用于判断代码是否已经重定位到内存中,LC0这个符号在head.S中定义如下,实质上相当于c语言的全局数据结构,结构的每个域存储的是一个指针。指针本身的值代表不同的代码段,已经在顶层连接脚本vmlinux.lds里进行了赋值,比如_start是内核开始的地址
.type LC0,
#object
LC0: .word LC0 @
r1 //这个要加载到r1中的LC0是链接时LC0的地址
.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
通过当前运行时LC0的地址与链接器所链接的地址进行比较判断。若相等则是运行在链接的地址上。
如果不是运行在链接的地址上,则下面的代码必须修改相关地址,进行重新运行
/*
* r5
- zImage base address
* r6
- GOT start
* ip
- GOT end
*/
//修正实际运行的位置,否则跳转指令就找不到相关代码
add r5,
r5, r0 //修改内核映像基地址
add r6,
r6, r0
add ip,
ip, r0 //修改got表的起始和结束位置
#ifndef CONFIG_ZBOOT_ROM
/*若没有定义CONFIG_ZBOOT_ROM,此时运行的是完全位置无关代码
位置无关代码,也就是不能有绝对地址寻址。所以为了保持相对地址正确,
需要将bss段以及堆栈的地址都进行调整
* r2
- BSS start
* r3
- BSS end
* sp
- stack pointer
*/
add r2,
r2, r0
add r3,
r3, r0
add sp,
sp, r0
//全局符号表的地址也需要更改,否则,对全局变量引用将会出错
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 //若定义了CONFIG_ZBOOT_ROM,只对got表中在bss段以外的符号进行重定位
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
如果运行当前运行地址和链接地址相等,则不需进行重定位。直接清除bss段
not_relocated: mov r0, #0
1: str r0,
[r2],
#4 @
clear bss
str r0,
[r2], #4
str r0,
[r2], #4
str r0,
[r2], #4
cmp
r2, r3
blo 1b
之后跳转到cache_on处
bl cache_on
cache_on定义
.align 5
cache_on: mov
r3,
#8 @
cache_on function
b call_cache_fn
把r3的值设为8。这是一个偏移量,也就是索引proc_types中的操作函数。
然后跳转到call_cache_fn。这个函数的定义如下:
call_cache_fn:
adr r12,
proc_types //把proc_types的相对地址加载到r12中
#ifdef
CONFIG_CPU_CP15
mrc
p15, 0, r6, c0, c0 @ get processor ID
#else
ldr r6,
=CONFIG_PROCESSOR_ID
#endif
1: ldr r1,
[r12, #0] @ get
value
ldr r2,
[r12, #4] @ get mask
eor r1,
r1, r6 @ (real ^ match)
tst r1,
r2 @是否和CPU ID匹配?
addeq pc,
r12,
r3 @ 用刚才的偏移量,查找//到cache操作函数,找到后就执行相关操作,比如执行b __armv7_mmu_cache_on
//
add r12, r12, #4*5 //如果不相等,则偏移到下个proc_types结构处
b 1b
addeq pc,
r12,
r3 @
call cache function
proc_type的定义如下 ,实质上还是一张数据结构表
.type proc_types,#object
proc_types:
.word 0x41560600 @
ARM6/610
.word 0xffffffe0
b __arm6_mmu_cache_off @
works, but slow
b __arm6_mmu_cache_off
mov
pc, lr
@ b __arm6_mmu_cache_on @
untested
@ b __arm6_mmu_cache_off
@ b __armv3_mmu_cache_flush
.word 0x00000000 @
old ARM ID
.word 0x
mov
pc, lr
mov
pc, lr
mov
pc, lr
.word 0x41007000 @
ARM7/710
.word 0xfff8fe00
b __arm7_mmu_cache_off
b __arm7_mmu_cache_off
mov
pc, lr
.word 0x41807200 @
ARM720T (writethrough)
.word 0xffffff00
b __armv4_mmu_cache_on
b __armv4_mmu_cache_off
mov
pc, lr
.word 0x41007400 @
ARM74x
.word 0xff00ff00
b __armv3_mpu_cache_on
b __armv3_mpu_cache_off
b __armv3_mpu_cache_flush
.word 0x41009400 @
ARM94x
.word 0xff00ff00
b __armv4_mpu_cache_on
b __armv4_mpu_cache_off
b __armv4_mpu_cache_flush
.word 0x00007000 @
ARM7 IDs
.word 0x
mov
pc, lr
mov
pc, lr
mov
pc, lr
@
Everything from here on will be the new ID system.
.word 0x
.word 0xffffffe0
b __armv4_mmu_cache_on
b __armv4_mmu_cache_off
b __armv4_mmu_cache_flush
.word 0x6901b110 @
sa1110
.word 0xfffffff0
b __armv4_mmu_cache_on
b __armv4_mmu_cache_off
b __armv4_mmu_cache_flush
@
These match on the architecture ID
.word 0x00020000 @
.word 0x
b __armv4_mmu_cache_on
b __armv4_mmu_cache_on //指令的地址
b __armv4_mmu_cache_off
b __armv4_mmu_cache_flush
.word 0x00050000 @
ARMv5TE
.word 0x
b __armv4_mmu_cache_on
b __armv4_mmu_cache_off
b __armv4_mmu_cache_flush
.word 0x00060000 @
ARMv5TEJ
.word 0x
b __armv4_mmu_cache_on
b __armv4_mmu_cache_off
b __armv4_mmu_cache_flush
.word 0x0007b000 @
ARMv6
.word 0x
b __armv4_mmu_cache_on
b __armv4_mmu_cache_off
b __armv6_mmu_cache_flush
.word 0 @
unrecognised type .word 0 mov
pc, lr mov
pc, lr mov
pc, lr .size
proc_types, . - proc_types 找到执行的cache函数后,就用上面的 addeq pc,
r12, r3直接跳转,例如执行下面这个处理器结构的cache函数
chinaunix网友2010-10-13 20:16:11
很好的, 收藏了 推荐一个博客,提供很多免费软件编程电子书下载: http://free-ebooks.appspot.com