分类: LINUX
2008-11-30 21:04:55
u-boot的强大就在于它具有代码relocate功能,运行时代码在ram中跑,最明显的一个好处就是u-boot可以自己替换自己固化在flash中的代码。
先看一下这套代码中和relocate有关的几个大名鼎鼎的参数:
board/ar7100/ap83/config.mk
# ROM version
TEXT_BASE = 0xbf000000
# SDRAM version
#TEXT_BASE = 0x8020000
include/configs/ap83.h
#define CFG_MONITOR_BASE TEXT_BASE
board/ar7100/ap83/u-boot.lds
OUTPUT_FORMAT("elf32-tradbigmips", "elf32-tradbigmips", "elf32-tradbigmips")
OUTPUT_ARCH(mips)
ENTRY(_start)
SECTIONS
{
. = 0x00000000;
. = ALIGN(4);
.text :
{
*(.text)
}
. = ALIGN(4);
.rodata : { *(.rodata) }
. = ALIGN(4);
.data : { *(.data) }
. = ALIGN(4);
.sdata : { *(.sdata) }
_gp = ALIGN(16);
__got_start = .;
.got : { *(.got) }
__got_end = .;
.sdata : { *(.sdata) }
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;
uboot_end_data = .;
num_got_entries = (__got_end - __got_start) >> 2;
. = ALIGN(4);
.sbss : { *(.sbss) }
.bss : { *(.bss) }
uboot_end = .;
}
注释:
0xbf000000 是mips 24kc Flash起始的位置,也是reset异常向量的入口。
0x80000000是ar9132平台DDR的起始位置。
链接脚本中的0x00000000会被这个TEXT_BASE替代,可以观察编译时候的输出,你会发现有一个-DTEXT_BASE=0xbf000000的选项。
Relocate的代码在start.S中。
启动的大致顺序如下,假设代码固化于flash起始位置。
CPU 上电
Reset异常(初始化cp0寄存器)
设置GOT
lowlevel_init(初始化SDRAM)
初始化cache
Relocate代码
下面是代码的入口
la t9, board_init_f
j t9
nop
这个board_init_f定义在lib_mips/board.c中。
这个程序为下列元素分配合适的内存空间:
[1] u-boot代码(.text .data .bss)
[2] malloc空间 (相当于heap)
[3] Board Info
[4] Global Data
[5] Stack
这里可以看出malloc的空间位于heap中,即内存的高端,而函数中的局部变量,都在stack中的,在内存低端。
void board_init_f(ulong bootflag)
{
gd_t gd_data, *id;
bd_t *bd;
init_fnc_t **init_fnc_ptr;
ulong addr, addr_sp, len = (ulong)&uboot_end - CFG_MONITOR_BASE;
ulong *s;
/* Pointer is writable since we allocated a register for it.
*/
gd = &gd_data;
/* compiler optimization barrier needed for GCC >= 3.4 */
__asm__ __volatile__("": : :"memory");
memset ((void *)gd, 0, sizeof (gd_t));
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}
/*
* Now that we have DRAM mapped and working, we can
* relocate the code and continue running from DRAM.
*/
addr = CFG_SDRAM_BASE + gd->ram_size;
/* We can reserve some RAM "on top" here.
*/
/* round down to next 4 kB limit.
*/
addr &= ~(4096 - 1);
debug ("Top of RAM usable for U-Boot at: %08lx\n", addr);
/* Reserve memory for U-Boot code, data & bss
* round down to next 16 kB limit
*/
addr -= len;
addr &= ~(16 * 1024 - 1);
debug ("Reserving %ldk for U-Boot at: %08lx\n", len >> 10, addr);
/* Reserve memory for malloc() arena.
*/
addr_sp = addr - TOTAL_MALLOC_LEN;
debug ("Reserving %dk for malloc() at: %08lx\n",
TOTAL_MALLOC_LEN >> 10, addr_sp);
/*
* (permanently) allocate a Board Info struct
* and a permanent copy of the "global" data
*/
addr_sp -= sizeof(bd_t);
bd = (bd_t *)addr_sp;
gd->bd = bd;
debug ("Reserving %d Bytes for Board Info at: %08lx\n",
sizeof(bd_t), addr_sp);
addr_sp -= sizeof(gd_t);
id = (gd_t *)addr_sp;
debug ("Reserving %d Bytes for Global Data at: %08lx\n",
sizeof (gd_t), addr_sp);
/* Reserve memory for boot params.
*/
addr_sp -= CFG_BOOTPARAMS_LEN;
bd->bi_boot_params = addr_sp;
debug ("Reserving %dk for boot params() at: %08lx\n",
CFG_BOOTPARAMS_LEN >> 10, addr_sp);
/*
* Finally, we set up a new (bigger) stack.
*
* Leave some safety gap for SP, force alignment on 16 byte boundary
* Clear initial stack frame
*/
addr_sp -= 16;
addr_sp &= ~0xF;
s = (ulong *)addr_sp;
*s-- = 0;
*s-- = 0;
addr_sp = (ulong)s;
debug ("Stack Pointer at: %08lx\n", addr_sp);
memcpy (id, (void *)gd, sizeof (gd_t));
/* On the purple board we copy the code in a special way
* in order to solve flash problems
*/
#ifdef CONFIG_PURPLE
copy_code(addr);
#endif
relocate_code (addr_sp, id, addr);
/* NOTREACHED - relocate_code() does not return */
}
从代码中可以看出,U-boot被relocate到内存的最高端。Relocate的实际部分在start.S中,通过relocate_code (addr_sp, id, addr)进入汇编,mips体系中函数参数被保存在a0,a1,a2中,所以看start.S中的注释有:
/*
* void relocate_code (addr_sp, gd, addr_moni)
*
* This "function" does not return, instead it continues in RAM
* after relocating the monitor code.
*
* a0 = addr_sp
* a1 = gd
* a2 = destination address
*/
Relocate的核心代码如下,分析一下能体会到mips汇编的delay branch
relocate_code:
move sp, a0 /* Set new stack pointer */
li t0, CFG_MONITOR_BASE
la t3, in_ram
lw t2, -12(t3) /* t2 <-- uboot_end_data */
move t1, a2
/*
* t0 = source address
* t1 = target address
* t2 = source end address
*/
/* On the purple board we copy the code earlier in a special way
* in order to solve flash problems
*/
1:
lw t3, 0(t0)
sw t3, 0(t1)
addu t0, 4
ble t0, t2, 1b
addu t1, 4 /* delay slot */
/* If caches were enabled, we would have to flush them here.
*/
/* Jump to where we've relocated ourselves.
*/
addi t0, a2, in_ram - _start
j t0
nop
上面计算u-boot end地址lw t2, -12(t3)是因为有下面的定义,
.word uboot_end_data
.word uboot_end
.word num_got_entries
in_ram:
一个word四个字节,所以是in_ram的位置-12个字节。拷贝代码的时候,增加target指针位置的代码放在跳转代码之后,就是由于mips的流水线。等拷贝完代码之后,就跳转到ram中执行了。也就是in_ram的地方(此时的in_ram在relocate后的内存高端)。
注意到在u-boot的连接脚本里面专门有一个.u_boot_cmd段,专门设置这样一个段有什么好处呢?可以看一下find_cmd这个函数,就可以理解了,通过把所有cmd结构指针放在一个统一的段里面在查找起来非常方便,要添加新的命令,也不用改变原来的结构。看看它是怎么定义的吧:
原理:
每个命令都有一个命令结构体
struct cmd_tbl_s {
char*name; /* Command Name*/
intmaxargs; /* maximum number of arguments*/
intrepeatable; /* autorepeat allowed?*/
int (*cmd)(struct cmd_tbl_s *, int, int, char *[]); /* Implementation function*/
char*usage;/* Usage message(short)*/
char*help;/* Help message(long)*/
};
去定义它。Cmd为要调用的命令函数!name为该命令名字符串。
在u-boot里面有这样的宏
#define Struct_Section __attribute__ ((unused,section (".u_boot_cmd")))
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}
宏U_BOOT_CMD(name,maxargs,rep,cmd,usage,help)就是将
cmd_tbl_s{
name,
maxargs,
rep,
cmd,
usage,
help
}这样的一个命令结构体放入内存.u_boot_cmd这个区域,.u_boot_cmd这个域在board/smdk2410/u-boot.lds中定义!在U-boot中的shell中,根据用户输入的命令,就会在.u_boot_cmd这个内存区域中查找,当.u_boot_cmd中某一个cmd_tbl_s命令结构体的cmd_tbl_s.name和输入的命令字符串相符时,就调用该命令
结构体的cmd_tbl_s.cmd( ….)函数!
这里有一个要注意的是,cmd是一个函数指针,由于u-boot代码是要relocate的,所以在代码relocate之后,每个这样的指针也要加上相应的偏移才能正常动作,代码在board_init_r函数中。
最后是板子上电启动时的输出信息,看了很有帮助。
U-Boot
AP83 (ar9100 with SPI flash)
DRAM: 16 MB
Top of RAM usable for U-Boot at: 81000000
Reserving 263k for U-Boot at: 80fbc000
Reserving 192k for malloc() at:
Reserving 56 Bytes for Board Info at:
Reserving 36 Bytes for Global Data at:
Reserving 128k for boot params() at:
Stack Pointer at:
Now running in RAM - U-Boot at: 80fbc000
Found MXIC Flash. ID c22018
Flash: 16 MB
Relocate代码中还包含PIC GOT的东西,学懂了也要分析一下。
从代码调试中的一个小地方可以看出GOT的作用,你只要把代码relocate前后的&__u_boot_cmd_start的值打印出来,你会发现它们是不一样的,这就是我们在start.S中修改GOT指针的作用。