一、 AT91Bootstrap 介绍
AT91Bootstrap 是针对 Atmel 芯片的第一级引导装载程序,它提供了一整套的算法管理硬件初始化(GPIO,Clock,SDRAM等),从指定的存储介质( Nand Flash,SDCard 等)下载主应用程序( uboot 或 kernel )到主存储器( SDRAM )并启动它。
二、 代码分析
首先浏览一下 Makefile,指定了链接脚本 elf32-littlearm.lds 。简单看一下 elf32-littlearm.lds ,指定了程序入口 ENTRY(reset) 。reset 定义在 crt0_gnu.S ,它即入口文件。
crt0_gnu.S :
reset:
/* 异常向量表 */
_exception_vectors:
b reset_vector /* reset */
b undef_vector /* Undefined Instruction */
b swi_vector /* Software Interrupt */
b pabt_vector /* Prefetch Abort */
b dabt_vector /* Data Abort */
.word _edata /* Size of the binary for ROMCode loading */
b irq_vector /* IRQ : read the AIC */
b fiq_vector /* FIQ */
reset_vector:
/* 设置堆栈指针 SP */
_init_stack:
ldr sp,=TOP_OF_MEMORY /* TOP_OF_MEMORY = “0x310000” */
/* 初始化时钟 */
/* r4 = lowlevel_clock_init 的入口地址 */
ldr r4, = lowlevel_clock_init
/* 保存 PC 指针 */
mov lr, pc
/* 跳转到函数 lowlevel_clock_init 的入口地址去执行 */
bx r4/* 拷贝数据段 */
_init_data:
/*
_lp_data:
.word _edummy
.word _sdata
.word _edata
*/
/* r2 指向 _lp_data */
/* r1 = [r2] , r1 指向 _edummy
r3 = [r2+4] , r3 指向 _sdatar4 = [r2+8] , r4 指向 _edata
*/
ldmia r2, {r1, r3, r4}
1:
/* 比较 r3,r4 */
/* 如 r3<r4 ,则 r2=[r1], r1=r1+4 */
ldrcc r2, [r1], #4
/* 如 r3<r4 ,则 [r3]=r2, r3=r3+4 */
strcc r2, [r3], #4
/* 如 r3<r4 ,则跳到 1 */
/* 清 bss 段 */
_init_bss:
/*
_lp_bss:
.word _sbss
.word _ebss
*/
/* r2 指向 _lp_bss */
adr r2, _lp_bss
r4 = [r2+4] , r3 指向 _ebss
*/
ldmia r2, {r3, r4}
/* r2=0 */
mov r2, #01:
/* 比较 r3,r4 */
cmp r3, r4
/* 如 r3<r4 ,则 [r3]=r2=0, r3=r3+4 */
strcc r2, [r3], #4
/* 如 r3<r4 ,则跳到 1 */
bcc 1b/* 跳转到 c 代码 main 函数*/
_branch_main:
/* r4 指向 main 函数入口地址 */
ldr r4, = main
/* 保存 PC */
mov lr, pc
/* 跳转到 main 函数 */
bx r4/* main 函数返回 JUMP_ADDR 到r0中,该值是 u-boot 或 kernel 在 SDRAM 中的地址 。具体是引导哪个程序还要看是使用的哪个配置,比如使用 board/at91sama5d3xek/at91sama5d3xeknf_android_dt_defconfig,则引导 NandFlash 中的 android 内核,使用 board/at91sama5d3xek/at91sama5d3xeknf_uboot_defconfig ,则引导 NandFlash 中的 u-boot 。
*/
/* 跳转到指定的地址去执行 */
_go:
/* 获取机器 ID */
ldr r1, =MACH_TYPE
/* 保存 PC */
mov lr, pc
/* 跳转到 JUMP_ADDR */
bx r0main 函数 定义在 main.c 文件中
int main(void)
{
/* 镜像的加载地址 */
image.dest = (unsigned char *)JUMP_ADDR;/* 启动方式 ,可以从 DataFlash ,NandFalsh ,SDCard 等启动。这里以 NandFlash 启动为例 */
#ifdef CONFIG_NANDFLASH
media_str = "NAND: ";
/* 镜像地址 */
image.offset = IMG_ADDRESS;
/* 镜像大小 */
image.length = IMG_SIZE;#endif
#ifdef CONFIG_HW_INIT
/* 关闭看门狗,配置 PLLA,主时钟,使能外部复位,初始化 DBGU ,设置协处理器 CP15,初始化 DDRAM 控制器等 */
#endif
#ifdef CONFIG_LOAD_ONE_WIRE
/* Load one wire informaion */
load_1wire_info();
#endif
init_loadfunction();
/* 加载镜像文件 */
ret = (*load_image)(&image);#ifdef CONFIG_SCLK
slowclk_switch_osc32();
#endif
/* 返回跳转地址 */
return JUMP_ADDR;
}init_loadfunction() 定义如下,根据选定的启动方式和引导内容,将函数指针 load_image 指向相关函数的入口地址:
static int init_loadfunction(void)
{
#if defined(CONFIG_LOAD_LINUX) || defined(CONFIG_LOAD_ANDROID)
load_image = &load_kernel;
#else
#if defined (CONFIG_DATAFLASH)
load_image = &load_dataflash;
#elif defined(CONFIG_NANDFLASH)
load_image = &load_nandflash;
#elif defined(CONFIG_SDCARD)
load_image = &load_sdcard;
#else
#error "No booting media_str specified!"
#endif
#endif
return 0;
}
本文是 NandFlash 启动,引导 u-boot ,所以 load_image = &load_nandflash 。
load_nandflash 定义在 driver/nandflash.c :
int load_nandflash(struct image_info *image)
{
struct nand_info nand;
int ret;
/* 初始化 NandFlash */
nandflash_hw_init();if (nandflash_get_type(&nand))
return -1;
#ifdef CONFIG_NANDFLASH_RECOVERY
if (nandflash_recovery(&nand) == 0)
return -2;
#endif
#ifdef CONFIG_USE_PMECC
if (init_pmecc(&nand))
return -1;
#endif
#ifdef CONFIG_ENABLE_SW_ECC
dbg_log(1, "NAND: Using Software ECC\n\r");
#endif
dbg_log(1, "NAND: Image: Copy %d bytes from %d to %d\r\n",image->length, image->offset, image->dest);
/* 将镜像文件从 NandFlash 加载到 SDRAM 的 JUMP_ADDR 地址处 */
ret = nand_loadimage(&nand, image->offset, image->length, image->dest);
if (ret)
return ret;
if (image->of) {
dbg_log(1, "NAND: dt blob: Copy %d bytes from %d to %d\r\n",image->of_length, image->of_offset, image->of_dest);
ret = nand_loadimage(&nand, image->of_offset,image->of_length, image->of_dest);
if (ret)
return ret;
}
return 0;
}
AT91Bootstrap 的启动过程,主要分为两个阶段:
第一阶段:汇编程序 - crt0_gnu.S 主要是建立异常向量表、初始化堆栈、初始化时钟、设置数据段、设置 bss 段,然后跳转到main函数执行,最后跳转到 SDRAM 的 JUMP_ADDR 地址去启动 u-boot 或 kernel 。
第二阶段:C 程序 - main.c ,主要是 SDRAM 初始化,镜像文件的拷贝等,完成程序在 SDRAM中 运行的准备工作 。