对u-boot重定向(relocate)的理解
(2012-10-04 12:10:14)
序言:
一周前开始学习u-boot并尝试在S3C2440开发板上的移植。根据韦东山编写的《嵌入式Linux应用开发完全手册》中介绍u-boot的移植过程(NorFlash启动),我编译出适合S3C2440开发板u-boot二进制文件。但是,由于没有理解移植原理,我首先将二进制文件下载到飞凌2440核心板中的NandFlash中去运行,结果Nand启动后串口终端没有任何反映。然后,我通过J-Link下载器将二进制文件烧写到核心板的NorFlash中【1】,在终端看到了u-boot的启动信息。为什么NorFlash能够启动u-boot而NandFlash不能?u-boot的启动过程是怎样的?如何进行u-boot移植?这些问题使我开始想要去研究u-boot的源代码。
源码中一个比较重要的概念是“重定向(relocate)”,简单来说就是将u-boot的运行环境转移到SDRAM中去。但是仔细研究相关代码,却发现还有很多值得思考的问题。以下内容基于u-boot
1.1.6源码以及飞凌S3C2440核心板。
1. 重定向的意义和目标
以S3C2440为例,启动方式有两种,一种是NorFlash启动另外一种是NandFlash启动【2】。
NorFlash有自己的地址线和数据线,可以采用类似于Memory的随机访问方式,在NorFlash上可以直接运行程序,所以NorFlash可以直接用来启动u-boot。但是,就运行速度而言SDRAM比NorFlash更能满足一些中断事件响应的需求。
NandFlash是IO设备,数据、地址、控制线都是共用的,需要软件区控制读取时序,所以不能像NorFlash、内存一样随机访问,不能EIP(片上运行),因此不能直接作为boot。但是S3C2440在选择NandFlash启动时,会首先将NandFlash中的前4K代码拷贝到其内部的SRAM中去运行,而超过4K的u-boot文件则无法完全通过内部SRAM执行,这就需要借助SDRAM来完成。
所以,将u-boot转移到SDRAM中去运行就成为一种可行的方案,这一过程也就是“重定向”。所以,重定向的目标就是将u-boot的运行环境转移到SDRAM中去。那么转移就要涉及两种地址,一种是转移前代码存放的地址,即加载地址,另一种转移后代码运行的地址,即运行地址。
2. u-boot的加载地址与运行地址
这两种地址是由链接脚本(.lds文件)定义的,在u-boot源码的根目录下Makefile中定义了u-boot的链接选项:
$(obj)u-boot:
depend
version $(SUBDIRS)
$(OBJS) $(LIBS) $(LDSCRIPT)
UNDEF_SYM=`$(OBJDUMP) -x $(LIBS) |sed -n -e
's/.*\(__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\
cd $(LNDIR) &&
$(LD) $(LDFLAGS)
$$UNDEF_SYM $(__OBJS)
\
--start-group $(__LIBS)
--end-group $(PLATFORM_LIBS)
\
-Map u-boot.map -o u-boot
执行make之后,会打印出对应的链接指令:
arm-linux-ld -Bstatic -T
/home/chengbo/uboot/u-boot-1.1.6/board/FL2440/u-boot.lds -Ttext
0x33F80000 $UNDEF_SYM cpu/arm920t/start.o ... ...
--start-group ... ...
\
-Map u-boot.map -o
u-boot
其中,链接选项-T指定了链接脚本u-boot.lds,它定义了gcc编译出目标文件的排列顺序、运行、加载地址以及程序入口等,部分源码如下:
OUTPUT_FORMAT("elf32-littlearm",
"elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
. = 0x00000000;
. = ALIGN(4);
.text
:
{
cpu/arm920t/start.o
(.text)
*(.text)
}
... ...
链接脚本中没有定义加载地址(由AT属性来定义),所以默认运行地址与加载地址相同。运行地址从0x00000000开始,但是被链接指令中的另一个选项-Ttext
0x33F80000偏移到了0x33F80000,这个地址属于SDRAM,也就是说运行地址是从0x33F80000开始的(此时加载地址是否也是0x33F80000开始的,我不确定,杜春雷的《
ARM体系结构与编程
》解释的比较详细,以后要认真读一读)。将u-boot二进制文件反编译后,也可以清楚的看出运行地址的起始地址:
u-boot:
file format elf32-littlearm
Disassembly of section .text:
33f80000
<_start>:
33f80000:
ea000012
b 33f80050
<reset>
33f80004:
e59ff014
ldr pc, [pc,
#20] ;
33f80020
<_undefined_instruction>
33f80008:
e59ff014
ldr pc, [pc,
#20] ;
33f80024 <_software_interrupt>
33f8000c:
e59ff014
ldr pc, [pc,
#20] ;
33f80028 <_prefetch_abort>
33f80010:
e59ff014
ldr pc, [pc,
#20] ;
33f8002c <_data_abort>
33f80014:
e59ff014
ldr pc, [pc,
#20] ;
33f80030 <_not_used>
33f80018:
e59ff014
ldr pc, [pc,
#20] ;
33f80034 <_irq>
33f8001c:
e59ff014
ldr pc, [pc,
#20] ;
33f80038 <_fiq>
33f80020
<_undefined_instruction>:
33f80020:
33f80140
.word
0x33f80140
33f80024
<_software_interrupt>:
33f80024:
33f801a0
.word
0x33f801a0
33f80028
<_prefetch_abort>:
33f80028:
33f80200
.word
0x33f80200
33f8002c
<_data_abort>:
33f8002c:
33f80260
.word
0x33f80260
33f80030
<_not_used>:
33f80030:
33f802c0
.word
0x33f802c0
33f80034
<_irq>:
33f80034:
33f80320
.word
0x33f80320
33f80038
<_fiq>:
33f80038:
33f80380
.word
0x33f80380
这段代码对应的是整个u-boot的启动代码/cpu/arm920t/start.S的中断入口代码,对应的源码如下:
.globl
_start
_start:
b
reset
ldr
pc, _undefined_instruction
ldr pc,
_software_interrupt
ldr pc,
_prefetch_abort
ldr pc,
_data_abort
ldr pc,
_not_used
ldr pc,
_irq
ldr pc,
_fiq
_undefined_instruction:
.word
undefined_instruction
_software_interrupt:
.word
software_interrupt
_prefetch_abort:
.word
prefetch_abort
_data_abort:
.word
data_abort
_not_used:
.word
not_used
_irq:
.word
irq
_fiq:
.word
fiq
.balignl 16,0xdeadbeef
设想,如果u-boot运行在NorFlash中(物理地址的起始地址为0x00000000),而且SDRAM中没有任何u-boot代码,那么程序是无法正常运行的。也就是说,通过Makefile引导的编译、链接,最终我们得到了只有在SDRAM中才能够正常运行的代码。所以,u-boot要将自己搬运到SDRAM中才可以正常运行。
但是,在搬运代码之前,u-boot为什么能在NorFlash中运行起来呢?这就需要借助两种跳转指令,相对跳转(汇编指令b
XXXX)以及“绝对跳转(非术语)”(汇编指令ldr pc, XXXX)。
3.
从NorFlash向SDRAM的跳转
u-boot执行的第一条指令位于/cpu/arm920t/start.S,上述源代码包含了这条指令,即:
b
reset
其反汇编指令为:
b
0x33f80050
但是,0x33f80050这个地址只是反汇编的结果,并不是跳转到这个绝对位置。相对跳转指令b是以当前pc指针为基址,加上一个偏移量来计算实际要跳转的地址,这个偏移量信息是保存在指令本身(机器码:0xea000012)的,与要跳转目的地址无关。所以,相对跳转指令在任意位置都可以正确运行。
我们可以发现在start.S中,除了最初的几个中断入口跳转指令,在重定向(relocate)之前,跳转指令都采用相对跳转,这一点也保证了u-boot在重定向之前等够在NorFlash中运行。
与相对跳转不同,“绝对跳转”(ldr
pc,
XXXX)的目的是要跳转到某个绝对地址。它的实现过程是把文字池中某一位置存储的数据赋值给pc实现跳转,文字池中的这个位置与当前指令的偏移量是保存在指令本身中的(这一点与相对跳转指令b相似),也就是说无论程序运行在什么环境中,ldr都可以准确找到文字池中的这个位置,而这个位置存储的数据就是要跳转的绝对位置。以上述代码中:
ldr pc, _undefined_instruction
为例,其反汇编代码为:
ldr
pc,[pc,
#20]
[pc,#20]对应的相对位置是文字池中_undefined_instruction这个位置,无论代码运行在什么位置,该指令都可以找到_undefined_instruction的地址,因为这个偏移量就保存在机器码0xe59ff014中。而_undefined_instruction中保存的值0x33f80140则是实际要跳转的绝对位置。
因此,在u-boot运行之初,通过与运行环境无关的代码将整个代码转移到SDRAM之后,通过指令:
ldr pc,
_start_armboot
跳转到SDRAM中去运行。这也就实现了u-boot的重定向。
【1】烧写u-boot到NorFlash的方法:http://blog.sina.com.cn/s/blog_4bd13d48010189yu.html
【2】NorFlash与NandFlash启动u-boot的区别:http://blog.sina.com.cn/s/blog_4bd13d48010189e1.html
喜欢
0
赠金笔
加载中,请稍候......