uboot学习与移植笔记整理
- 格式:doc
- 大小:107.00 KB
- 文档页数:28
Part1:
remap.
remap比较简单,和MMU的功能可以看做是等价的,只是一般remap地址估定为0x0 ,网上有个帖子叫<>专门讲了它对remap的理解,对remap的作用是这样讲的: 当ARM处理器上电或者Reset之后,处理器从0x0取指。因此,必须保证系统上电时,0x0处有指令可以执行。所以,上电的时候,0x0地址处必定是ROM或者Flash(NOR)。但是,为了加快启动的速度,也方便可以更改异常向量表,加快中断响应速度,往往把异常向量表映射到更快、更宽(32bit/16bit)的RAM中。但是异常向量表的开始地址是由ARM架构决定的,必须位于0x0处,因此,必须把RAM映射到0x0。
文中提到了ARM处理器remap的三种情况,如下
1)如果处理器有专门的寄存器可以完成Remap。那么Remap是通过Remap寄存器的相应bit置1完成的。
如Atmel AT91xx
2)如果处理器没有专门的寄存器,但是memory的bank控制寄存器可以用来配置bank的起始地址,那么只要把RAM的起始地址编程为0x0,也可以完成remap。如samsung s3c4510 .
3)如果上面两种机制都没有,那么Remap就不要做了。因为处理器实现决定了SDRAM对应的bank地址是不能改变的。如Samsung S3c2410.
不过我的看法有点稍微不一样,如果上面两种机制都没有,那么Remap就不要做了,它给的典型例子是Samsung S3c2410 ,2410虽然sdram对应的bank地址不能改变,但它有MMU功能, MMU可以起到remap的作用,常用的最典型的应该是例子Samsung S3c44b0,它既没有mmu,又SDRAM对应地址有没办法改变.顺便补充下除了4510可以改变每个bank的地址,还有华邦的w90P740(arm7),呵呵,我现在用的U就是这款U,可以把bank的地址随意的设置.
2.relocate .
relocate (地址重定位),个人觉得这个是boot里面最麻烦也是最核心的部分,刚开始看boot代码的时候,它简直是我的噩梦,不知道大家分析boot的源码流程是否这样,也可能我大学不是计算机的,没学过编译原理(现在也没看过)对链接和加载一无所知,有两个星期非常痛苦,就是不懂人家boot里面的链接脚本为什么要那样写.网上关于uboot的帖子很多,但对链接加载这块,始终写的不详细,不知道是不是太过于基础了,高手都不愿意讲,最后自己找资料,发现其实一切痛苦的根源都是对链接和加载不太清楚造成的,但个人感觉boot除了初始化以外就是搬运程序,如何搬运?为什么要那样搬运都需要对硬件板的地址分布很清楚?而这些都是链接决定的,所以非弄清楚不可!
1.我们为什么需要relocate ? 经济方面,(nandflash和norflash 每兆价格相差悬殊),把boot代码放在norflash里面(为什么不放在nandflash里面,因为nandflash读需要驱动支持,norflash可以直接访问),boot通常很小,只需要占用几十k的空间,所以只需要很小的norflash芯片,这样很便宜,而把应用程序通常很大,所以用价格低廉nandflash来储存,实际应用,通过执行boot程序,把nandflash里面代码和数据搬运到内存中来执行,这样比程序直接放在norflash里执行,可以.另外还有运行速度方面的差别,程序在norflash里执行的速度远远小于在sdram中执行的速度,为了追求更高的速度,也需要relocate,让程序在sdram里面执行 .
2.关于加载域(VMA)和运行域(LMA)(加载域和运行域,加载域是程序代码在ROM、FLASH中的排列次序及地址安排,运行域是程序运行时代码在SRAM、SDRAM中地址安排;存储代码时按照加载域存放在FLASH中,运行时再从FLASH中取出代码到RAM运行域运行,一段代码的加载域和存储域可以不同),杜春雷在它那本经典的<>一书专门有一章来讲加载域和运行域不一致的情况,但我当初接触了它的这些加载域和运行域后,看uboot的lds ,uboot的lds没有设置LMA,只是设置了VMA,为此我疑惑很久.直到耐心的看了那本链接器和加载器的书才豁然明白( /viewthread.php?tid=817770 ),任何一个链接器和加载器的基本工作都非常简单: 将更抽象的名字与更底层的名字绑定起来,好让程序员使用更抽象的名字编写代码,链接器的就是把源文件进行符号解析,把解析出来的符号和地址的进行绑定,把全局变量,函数,标号等等这些符合和地址绑定起来.
3.boot上电后开始能够正确执行还有个很重要的原因,是要保证boot在系统加电或复位后最初执行的代码是跟地址无关的,(即在代码搬运前所执行的代码是与地址无关),地址无关即地址无关代码生成的这个映象文件可以被放在内存中的任何一个地址上运行。对于地址无关的代码, 寻址是基于pc值的, 在pc值上+/-一个偏移值, 得到运行地址,如跳转指令B.当我们执行完代码搬运,就需要跳到和地址相关的地方去执行,即我们的RAM中,一般是跳转到一个标号, 这时地址相关代码就开始运行了ldr pc,_start_armboot.因为在bin映象生成的时候,就已经把_start_armboot这个符号,和实际地址绑定在一起,当我们执行ldr pc,_start_armboot
程序就从在ROM中执行跳入到RAM中了,但前提是我们进行了代码搬移,如果没有代码搬运ldr pc,_start_armboot,RAM中没有代码程序就马上飞掉了,所有我们在在搬运之前不能寻址绝对地址有关代码,必须执行代码地址无关。
拿u-boot-1.1.4下的smdk2410来做例子,和smdk2410 board密切相关的就两个文件夹\board\smdk2410和\cpu\arm920t,里面核心文件就u-boot.lds ,
config.mk ,start.S .
ENTRY(_start)
SECTIONS
{
. = 0x00000000;//从0地址起始
. = ALIGN(4);
.text :
{
cpu/arm920t/start.o (.text)
*(.text)
}
. = ALIGN(4);
.rodata : { *(.rodata) }
. = ALIGN(4);
.data : { *(.data) }
. = ALIGN(4); .got : { *(.got) }
. = .;
__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;
. = ALIGN(4);
__bss_start = .;//为搬运代码提供的符号,来标明bss段地址,方便relocate
.bss : { *(.bss) }
_end = .; //定义整个image的结束地址
}
u-boot.lds 是链接脚本文件, 我刚开始看这个链接脚本文件时,我疑惑很久,不明白lds中VMA= LMA(资料上很多链接脚本包括我们公司项目里面自己写的lds脚本是通过AT命令设置过LMA,这样看起来地址空间分配更清晰),而且整个image 的VMA按照lds为基址为0x0,而2410芯片不能remap,0x0地址是ROM的区域,不是运行时RAM的地址,我的理解是代码段地址应该是指向该硬件板内存区域,设置 .text=TEXT_BASE 而不是lds中的.text=0x0 ,这个疑点弄的我当时很郁闷,想了很久也没想没有搞清楚u-boot这样链接脚本都能让boot跑起来,当我把编译出来的bin烧到norflash中,uboot居然跑起来了,同时发现了一个问题, u-boot.map 中发现 .text 是从config.mk 定义TEXT_BASE =0x33f80000 ,而不是lds设置的0x0,这又让我吃惊,没清楚是怎么会事,手上有介绍移植uboot的资料,但都对uboot链接这部分,写的不够详细,知道事config.mk文件搞的鬼,但把makefile文件看了几遍都没找不到是怎么回事(还是对makefile不熟啊!),最后把编译uboot的过程看了隐藏了个机关是
arm-linux-ld –Tu-boot-1.1.4\board\smdk2410\u-boot.lds –Ttext
0x33f80000
arm-linux-objcopy --gap-fill =0xff –O binary uboot ubtoot.bin
不知道uboot设计者为什么要在这里加一个–Ttext 而不是在lds就设置?而很多移植uboot的资料对lds文件都有所描述,但这个重要的细节似乎都漏掉了,不知道是不是因为太基础了,所以没有讲. 不过最后生成的bin 从上看arm-linux-objcopy --gap-fill =0xff –O binary
uboot ubtoot.bin没有对链接生成的elf文件进行重定位,因此它的运行地址是config.mk 定义TEXT_BASE为基地址,顺序按照lds的顺序依次增加的,所以整个uboot最初运行的流程是
_start reset cpu_init_crit relocate
这个部分就是完成初始化,设SVC32,关看门狗,关中断,设置时钟,初始化SDRAM(为代码搬运到SDRAM做准备),这些都很简单
relocate:
adr r0, _start
ldr r1, _TEXT_BASE
cmp r0, r1
beq stack_setup
ldr r2, _armboot_start
ldr r3, _bss_start
sub r2, r3, r2
add r2, r0, r2
copy_loop:
ldmia r0!, {r3-r10}
stmia r1!, {r3-r10}
cmp r0, r2
ble copy_loop
看了下网上的帖子,adr指令,网上很多人被这这个指令弄郁闷,我看杜春雷的<>P143讲,这个指令是基于PC或者寄存器的,读到是地址无关的,一般被编译器替换为SUB r0, pc,#offset ,不要理解为读取符合表中_start符号的地址(0x33f80000).在我们上电开始执行时,pc从0开始,所以现在r0值为0 +offset,不等于_TEXT_BASE(0x33f80000).接下来要用到链接时确定的符号地址了,_armboot_start(0x33f80000)., _bss_start(0x33f97954)这些可以在u-boot.map里面的看到, size of armboot =0x33f97954-0x33f80000 ,把_start:0x0 (norflsh)把.text ,.data的代码往SDRAM里_TEXT_BASE确定的地