转载:?http://www.embedu.org/Column/Column345.htm
一个典型的嵌入式系统中,bootloader代码放在NOR Flash或NAND Flash里面,系统加电或复位后,首先运行这段代码。通常把bootloader代码放在NOR Flash里面,NAND Flash由于硬件原因不能随机访问,需要特殊的硬件支持机制。
bootloader代码除了初始化以外就是搬运程序,即地址重定位(relocate)。我们为什么需要relocate?主要是经济方面和速度方面的原因。经济方面,NOR Flash和NAND Flash每兆价格相差悬殊,bootloader代码一般在几十到几百K大小,而应用程序通常都很大,几M到几十M的大小,所以用价格低廉的NAND Flash存储。速度方面,程序在NOR Flash里执行的速度远远小于在SDRAM中执行的速度,为了追求更高的速度,也需要relocate,让程序在SDRAM里面执行。
relocate涉及到加载域(VMA)和运行域(LMA)两个概念。加载域是程序代码在ROM、FLASH中的排列次序及地址安排,运行域是程序运行时代码在SRAM、SDRAM中地址安排。存储代码时按照加载域存放在FLASH中,运行时再从FLASH中取出代码到RAM运行域运行,一段代码的加载域和存储域可以不同。(可以参考杜春雷的《arm体系结构与编程》一书的有关章节)。
以smdk2410为例,密切相关的就两个文件夹/board/smdk2410和/cpu/arm920t,里面核心文件就u-boot.lds 、config.mk 、start.S。
/cpu/arm920t/u-boot.lds
????????OUTPUT_FORMAT("elf32-littlearm","elf32-littlearm","elf32-littlearm")
????????OUTPUT_ARCH(arm)
????????ENTRY(_start)
????????SECTIONS
????????{
????????????????. = 0x00000000; // 从0地址起始
????????. = ALIGN(4);
????????????????.text :
????????????????{
????????????????????????cpu/arm920t/start.o (.text)
????????????????????????*(.text)
????????????????}
????????. = ALIGN(4);
????????????????.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.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 (NOLOAD) : { *(.bss) . = ALIGN(4); }
????????????????_end = .;
????????}
连接脚本文件lds中没有设置LMA,只是设置了VMA。VMA的设置是通过顶层目录下的config.mk文件中的LDFLAGS实现的,TEXT_BASE在/board/smdk2410/config.mk中定义为0x33F80000(SDRAM地址)。
LDFLAGS += -Bstatic -T $(obj)u-boot.lds $(PLATFORM_LDFLAGS)
????????ifneq ($(TEXT_BASE),)
????????LDFLAGS += -Ttext $(TEXT_BASE)
????????endif
查看u-boot.map文件,代码的连接地址是从0x33F80000开始的。
167 .text???????? 0x33f80000????????0x232c8
????????168????????cpu/arm920t/start.o(.text)
????????169????????.text????????????????0x33f80000????????????????0x4a0 cpu/arm920t/start.o
????????170????????????????????????????????0x33f80048????????????????_bss_start
????????171????????????????????????????????0x33f8004c????????????????_bss_end
????????172????????????????????????????????0x33f80044????????????????_armboot_start
????????173????????????????????????????????0x33f80000????????????????_start
????????174????????board/samsung/fs2410/lowlevel_init.o(.text)
????????175????????.text????????????????0x33f804a0???????? 0x64 board/samsung/fs2410/lowlevel_init.o
????????176????????????????????????????????0x33f804a4????????????????lowlevel_init
????????177????????board/samsung/fs2410/nand_read.o(.text)
????????178????????.text????????????????0x33f80504????????0xe8 board/samsung/fs2410/nand_read.o
????????179????????????????????????????????0x33f80504????????????????wait_idle
????????180????????????????????????????????0x33f80518????????????????nand_read_ll
bootloader代码上电之后之所以能够正确执行,有个很重要的原因,就是最初执行的bootloader代码是地址无关的,即这个映象文件可以被放在内存中的任何一个地址上运行。
对于地址无关的代码, 寻址是基于pc值的, 在pc值上+/-一个偏移值得到运行地址,如跳转指令B。当执行完代码搬运,就需要跳到和地址相关的地方去执行,即RAM中。一般是跳转到一个标号,这时地址相关代码就开始运行了,如:ldr pc,_start_armboot。
因为在bin映象生成的时候,就已经把_start_armboot这个符号和实际地址绑定在一起,当执行ldr pc,_start_armboot 语句时,程序就从在ROM中执行跳入到RAM中了,前提是进行了代码搬移。如果没有代码搬运就执行ldr pc,_start_armboot,因为RAM中没有正确的可执行代码,程序就马上飞掉了,所有在搬运之前不能寻址绝对地址有关代码,必须执行代码地址无关.
下面的代码是从NOR Flash向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 r0,_start,这是一条伪指令,一般被编译器替换为sub r0, pc,#offset ,不要理解为读取符合表中_start符号的地址(0x33F80000)。上电开始执行时,pc从0开始,所以现在r0值为0+offset,不等于_TEXT_BASE(0x33F80000)。接下来要用到链接时确定的符号地址_armboot_start(0x33F80044)了,把_start:0x0 (NOR Flash)里的.text、.data的代码往SDRAM里_TEXT_BASE确定的地址: 0x33f80000搬运。s3c2410的SDRAM基地址是0x3000_0000,由于uboot支持的这个board SDRAM64M(0x3000_0000-0x3400_0000),所以把u-boot.bin搬运到内存的高端地址.然后跳到内存中执行,提高速度。