IMx53上电后,进入的是其内部的Memory,从iMx53RM.pdf Chapter 2中可以查看到System Memory Map图如下:
从片内地址的0x0000_0000至0x0000_FFFF这64K是Boot Rom的空间,其固化有Boot Code。IMx53在硬件复位后会强制Arm内核从Boot Rom启动,运行Boot Code。
Boot Code会根据BOOT_MODE[1:0]的寄存器值、eFUSEs状态和GPIO Setting来完成从不同的boot devices启动,Serial Downloader from UART or USB、Device Configuration Data、Digital signature based High Assurance Boot。Boot Devices包括包括NOR FLASH、NAND FLASH、OneNAND FLASH、SD/MMC、Parmllel ATA/Serial ATA HDD、Serial ROM devices(EEPROM等)。
BOOT_MODE[1:0]对应PIN脚为C18、B20,BOOT_MODE的值会在RESET复位的上升沿进行一次采样(仅此一次),并保存在BOOT_MODE寄存器中,之后不论PIN脚电平的变化,均不会改变BOOT_MODE寄存器的值。BOOT_MODE的定义如下图所示:
BOOT_MODE[1:0]为0b00时,选择Internal Boot模式启动,在此模式下,启动设备和参数的选择根据BT_FUSE_SEL、eFUSEs、GPIO Setting来选择启动的设备和参数。当BT_FUSE_SEL为1(fuse is blown),boot参数是由eFUSEs决定的,它是第一次烧录时选择的devices和参数的保存值;当BT_FUSE_SEL为0(fuse is unblown),此时eFUSEs的部分值将被GPIO pin的状态来覆盖,从而选择不同的devices和参数进行启动。GPIO pin的定义及覆盖的eFUSEs值见下图:
由于这些GPIO在产品开发时会被使用,飞思卡尔建议Internal Boot模式启动的参数由eFUSEs来决定,只是保留GPIO这种模式用作开发和测试时使用。
BOOT_MODE[1:0]为0b01,保留!
BOOT_MODE[1:0]为0b10,选择Boot From FUSES模式启动,在此模式下,GPIO pin的状态将被忽略,直接选择eFUSEs的设置来进行启动。当BT_FUSE_SEL为0时,说明boot devices还没有被烧录,将直接跳到Serial Downloader模式。当BT_FUSE_SEL为1时,将按照eFUSEs的设置启动系统。一般选择此模式启动,如果BT_FUSE_SEL为1,但是eFUSEs设置对应的设备又不存在时,系统也会自动跳转到Serial Downloader模式,并在重新抄录成功后,改变eFUSEs的值,从而避免硬件boot devices的更改而芯片已经烧录过无法更新eFUSEs的问题。
BOOT_MODE[1:0]为0b11,选择Serial Downloader模式启动。通过USB或者UART进行系统程序下载。
eFUSEs的说明见下表:
对应于devices及其参数的选择由BOOT_CFG来决定,在此不再赘述,仅列出通过ESDHC boot的参数如下:
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”相对应。
Uboot启动一般认为是由两个阶段来运行,一是汇编start.s运行阶段,二是由其他C语言完成的第二阶段,一下做简要分析。
第一阶段主要是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。
在看链接文件之前,我们先看一下,生成的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 = .;
}
.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启动。
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
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并运行