1 Uboot存放地址
Uboot是是运行完固化的Boot Code后,Boot Code会跳转到选定设备的指定地址去运行,以EMMC为例,见下图
起始的前0x200即512B为MBR,接着的是预留的Second Image Table,Boot Image( uboot)应该放在0x400(1024)的地址,即烧写uboot的时候,就应该烧写在emmc主分区的1024位置,这也和烧录uboot的脚本“sudo dd if=./u-boot.bin of=./u-boot-no-padding.bin bs=1024 skip=1; sync”相对应。
2 Uboot启动
Uboot启动一般认为是由两个阶段来运行,一是汇编start.s运行阶段,二是由其他C语言完成的第二阶段,一下做简要分析。
2.1 第一阶段分析
第一阶段主要是start.s的运行,主要完成定义入口地址、设置异常向量、设置CPU的频率、初始化内存控制器、加载Uboot第二阶段代码代码到RAM、初始化堆栈、跳转到RAM运行第二阶段程序。按照规定的0x400存放这段代码,BOOT CODE会加载这段代码到内部RAM运行,根据不同的boot device,具体加载的偏移地址和加载程序范围如下图:
可见到,对于emmc来说,加载的偏移地址为0x400,大小为2Kbyte。
通过make mx53_smd_android_config配置自己的板子,查看uboot-imx下的Makefile可知
mx53_smd_android_config :unconfig
$(MKCONFIG) $(@:_config=) arm arm_cortexa8 mx53_smd freescale mx53。
2.1.1 链接文件u-boot.lds
在看链接文件之前,我们先看一下,生成的map文件如下(仅截取memory map一部分):
Memory Configuration
Name Origin Length Attributes
*default* 0x00000000 0xffffffff
Linker script and memory map
0x00000000 . = 0x0
0x00000000 . = ALIGN (0x4)
.text 0x77800000 0x258a8
board/freescale/mx53_mpvceo/flash_header.o(.text.flasheader)
.text.flasheader
0x77800000 0x5cc board/freescale/mx53_mpvceo/flash_header.o
cpu/arm_cortexa8/start.o()
*fill* 0x778005cc 0x14 00
.text 0x778005e0 0x440 cpu/arm_cortexa8/start.o
0x77800620 _end_vect
0x77800628 _bss_start
0x7780062c _bss_end
0x77800624 _armboot_start
0x778005e0 _start
0x77800960 v7_flush_dcache_all
.data 0x77800a20 0x0 cpu/arm_cortexa8/start.o
.bss 0x77800a20 0x0 cpu/arm_cortexa8/start.o
.ARM.attributes
0x77800a20 0x17 cpu/arm_cortexa8/start.o
.debug_line 0x77800a37 0xbb cpu/arm_cortexa8/start.o
.debug_info 0x77800af2 0x95 cpu/arm_cortexa8/start.o
.debug_abbrev 0x77800b87 0x14 cpu/arm_cortexa8/start.o
*fill* 0x77800b9b 0x5 00
.debug_aranges
0x77800ba0 0x20 cpu/arm_cortexa8/start.o
.glue_7 0x77800bc0 0x0 cpu/arm_cortexa8/start.o
.glue_7t 0x77800bc0 0x0 cpu/arm_cortexa8/start.o
.vfp11_veneer 0x77800bc0 0x0 cpu/arm_cortexa8/start.o
.v4_bx 0x77800bc0 0x0 cpu/arm_cortexa8/start.o
board/freescale/mx53_mpvceo/libmx53_mpvceo.a(.text)
.text 0x77800bc0 0x1a88 board/freescale/mx53_mpvceo/libmx53_mpvceo.a(mx53_mpvceo.o)
0x77800c74 dram_init
0x77800c40 get_board_id_from_fuse
0x77800c1c __iounmap
0x77800c20 get_boot_device
0x778018d4 i2c_failed_handle
0x77802078 board_mmc_init
……(省略部分内容)
.text 0x77802648 0x0 board/freescale/mx53_mpvceo/libmx53_mpvceo.a(lowlevel_init.o)
下面再来看链接文件:
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")设置输出文件是elf格式,32位ARM指令,小端;
OUTPUT_ARCH(arm) 设置输出执行平台Arm;
ENTRY(_start) 设置入口地址,在map中可知,其地址为0x778005e0;
SECTIONS
{
. = 0x00000000;目标代码段的起始地址;
. = ALIGN(4);对齐为4字节;
.text :代码段,会看到在map中,其值为0x77800000,是由于其被config.mk的TEXT_BASE重定向了,即其运行地址在0x77800000;
{
/* WARNING - the following is hand-optimized to fit within */
/* the sector layout of our flash chips! XXX FIXME XXX */
board/freescale/mx53_mpvceo/flash_header.o (.text.flasheader)这个文件就是启动流程1里面,boot code会完成Device Configuration Data(DCD);
cpu/arm_cortexa8/start.o 运行地址在程序段0x778005e0,虽然其和flash_header.o运行地址在SDRAM中,但是其存放地址是在boot devices的起始地址,所以其均会被boot Code加载,并在start.s把代码拷贝到RAM中的0x77800000中,之后跳转到RAM运行,具体看start.s分析;
board/freescale/mx53_mpvceo/libmx53_mpvceo.a (.text)
lib_arm/libarm.a (.text)
net/libnet.a (.text)
drivers/mtd/libmtd.a (.text)
drivers/mmc/libmmc.a (.text)
. = DEFINED(env_offset) ? env_offset : .;
common/env_embedded.o(.text)
*(.text)
}
. = ALIGN(4);
.rodata : { *(.rodata) } 只读data –常量;
. = ALIGN(4);
.data : { *(.data) } 变量初始化过的;
. = ALIGN(4);
.got : { *(.got) }
. = .;
__u_boot_cmd_start = .; Uboot cmd的存放位置;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;
. = ALIGN(4);
_end_of_copy = .; /* end_of ROM copy code here */
__bss_start = .; 未初始化的变量,不用拷贝,未初始化本身就为0
.bss : { *(.bss) }
_end = .;
}
2.1.2 Start.s分析
.globl _start 定义全局变量_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
……
reset:
/*
* set the cpu to SVC32 mode
*/
mrs r0, cpsr
bic r0, r0, #0x1f
orr r0, r0, #0xd3
msr cpsr,r0
……
_TEXT_BASE:
.word TEXT_BASE
.globl _armboot_start
_armboot_start:
.word _start 地址为0x778005e0
/*
* These are defined in the board-specific linker script.
*/
.globl _bss_start
_bss_start:
.word __bss_start 地址为0x778301b4
.globl _bss_end
_bss_end:
.word _end
……
#ifndef CONFIG_SKIP_RELOCATE_UBOOT
relocate: @ relocate U-Boot to RAM
adr r0, _start @ r0 <- current position of code 相对位置
ldr r1, _TEXT_BASE @ test if we run from flash or RAM 绝对地址
cmp r0, r1 @ don't reloc during debug
beq stack_setup
ldr r2, _armboot_start绝对地址地址为0x77800624
ldr r3, _bss_start绝对地址地址为0x778301b4,即拷贝程序大小为_armboot_start到0x778301b4这段空间
sub r2, r3, r2 @ r2 <- size of armboot
add r2, r0, r2 @ r2 <- source end address
copy_loop: @ copy 32 bytes at a time
ldmia r0!, {r3 - r10} @ copy from source address [r0]
stmia r1!, {r3 - r10} @ copy to target address [r1]
cmp r0, r2 @ until source end addreee [r2]
ble copy_loop
#endif /* CONFIG_SKIP_RELOCATE_UBOOT */
/* Set up the stack */
stack_setup:
ldr r0, _TEXT_BASE @ upper 128 KiB: relocated uboot
sub r0, r0, #CONFIG_SYS_MALLOC_LEN @ malloc area
sub r0, r0, #CONFIG_SYS_GBL_DATA_SIZE @ bdinfo
#ifdef CONFIG_USE_IRQ
sub r0, r0, #(CONFIG_STACKSIZE_IRQ + CONFIG_STACKSIZE_FIQ)
#endif
sub sp, r0, #12 @ leave 3 words for abort-stack
and sp, sp, #~7 @ 8 byte alinged for (ldr/str)d
/* Clear BSS (if any). Is below tx (watch load addr - need space) */
clear_bss:
ldr r0, _bss_start @ find start of bss segment
ldr r1, _bss_end @ stop here
mov r2, #0x00000000 @ clear value
clbss_l:
str r2, [r0] @ clear BSS location
cmp r0, r1 @ are we at the end yet
add r0, r0, #4 @ increment clear index pointer
bne clbss_l @ keep clearing till at end
#ifdef CONFIG_ARCH_MMU
bl board_mmu_init
#endif
ldr pc, _start_armboot @ jump to C code
_start_armboot: .word start_armboot 跳转到C start_armboot启动。
2.2 第二阶段分析
lib_arm/board.c中的start_armboot是第二阶段开始的代码,其主要完成系统内核、中断、时钟、接口、设备包括FLASH、DISPLAY、网络等的初始化,并进入命令循环,接收用户命令后完成相应的工作。
/* Pointer is writable since we allocated a register for it */
初始化Global data的数据地址
gd = (gd_t*)(_armboot_start - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t));
/* compiler optimization barrier needed for GCC >= 3.4 */
__asm__ __volatile__("": : :"memory");
memset ((void*)gd, 0, sizeof (gd_t));
gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
memset (gd->bd, 0, sizeof (bd_t));
gd->flags |= GD_FLG_RELOC;
初始化序列
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}
……
/* main_loop() can return to retry autoboot, if so just run it again. */
for (;;) {
main_loop ();
}
在main_loop中,会不断扫描输入,并根据输入做相应的设置,如果超出delay时间(3S),会自动运行bootcmd的evn参数。那么对于从emmc启动的板子,bootcmd的参数为:
"loadaddr=0x70800000\0"
"rd_loadaddr=0x70D00000\0" \
"bootargs=console=ttymxc0 init=/init " \
"androidboot.console=ttymxc0 di1_primary calibration video=mxcdi0fb:720P60\0" \
"bootcmd_SD=mmc read 1 ${loadaddr} 0x800 0x2000;" \
"mmc read 1 ${rd_loadaddr} 0x3000 0x300\0" \
"bootcmd=run bootcmd_SD; bootm ${loadaddr} ${rd_loadaddr}\0"
mmc read <device num> addr blk cnt 意思是将blk开始的cnt个block的内容读取到内存地址的addr处。Bootm addr 启动内存addr出的image文件。由此可以判定,uboot启动之后,将读取设备节点1的0x800个block开始的0x2000个block大小的内容到0x70800000地址。在uboot初始化fsl_esdhc_initialize时,ESDHC的初始化时按下面的数组顺序来进行的
struct fsl_esdhc_cfg esdhc_cfg[2] = {
{MMC_SDHC1_BASE_ADDR, 1, 1},
{MMC_SDHC3_BASE_ADDR, 1, 1},
};
SDHC3的位置放置的板载EMMC,SD卡的位置在SDHC1的位置,所以节点1正好是板载EMMD的节点。而一个block的大小是512,那么0x800正好是1M的大小,也就是烧录kernel的位置。0x3000的位置正好是6M,即是uRamdisk的烧录位置。
$ dd if=$FILE of=/dev/mmcblk0 bs=512 seek=2048
$ dd if=$FILE of=/dev/mmcblk0 bs=6M seek=1
因此,系统将会在加载完kernel运行后,加载uramdisk并运行。