Chinaunix首页 | 论坛 | 博客
  • 博客访问: 15849
  • 博文数量: 4
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 30
  • 用 户 组: 普通用户
  • 注册时间: 2013-02-27 14:59
文章分类
文章存档

2013年(4)

我的朋友

分类: 嵌入式

2013-03-22 08:27:15

       

以下是本文档的部分产生背景。

产生原因:最近频繁遇见u-boot的下载地址及烧写地址的问题,对项目的进展产生影响,另外如果不将这 个坎迈过去,对以后的工作也会产生影响,所以决定将u-boot的启动流程详细分析下。

目的:通过的u-boot启动流程的分析,尽量了解熟悉u-boot的设计理念,u-boot的启动逻辑,能在以 后的工作中,用好u-boot这个工具。

分析时间:1

效果:基本掌握了u-boot第一阶段的工作流程,但仍有许多不懂之处,比如fix rel.dyn段。

总结:在对一件事物进行分析的时候,不可能一次将其掌握,需要先了解框架,再有机会逐步深入。


以下即为at91(atmel公司)架构的u-boot启动流程分析:

首先在进行u-boot启动流程分析之前需要理清一个关系,那就是bootstrapu-boot的关系。

at91架构的u-boot不像其他的u-boot,系统一上电之后就由u-boot来接管目标板,而是先由bootstrap来接管板子,然后再来加载u-boot,目前据我所知,bootstrap做了一部分的u-boot需要完成的任务,具体做了什么,我们暂时不管,但有一个细节需要注意,那就是bootstrapu-boot的加载地址,因为之前它让我遇到一些阻碍,所以无论如何也要将这里搞清楚。假设bootstrapu-bootflash中加载到地址21f00000这个地址处,那么宏CONFIG_SYS_TEXT_BASE也需要设置成21f00000CONFIG_SYS_TEXT_BASE这个地址目前的信息我知道,它是u-boot正文段的起始地址,即代码从这里开始运行,这解释了他们为什么必须相等,先把这个作为得到这个结论的基础,后面再来分析这个基础是怎么产生,以及时候真的合理。


First:

u-boot应该运行的第一段代码在哪里?从bootstrap来看这个地址应该是CONFIG_SYS_TEXT_BASE。那么这个地址放的是什么东西拉?但它在u-boot里面找不到根据,先把这个放在一边,现在要给出一个条件,那就是u-boot会通过/arch/arm/cpu/u-boot.lds来给出u-boot放到内存中去的结构,以及代码从哪里开始运行,下面看u-boot.lds中给出的一句话:

/开始运行的地方*/

ENTRY(_start)


虽然猜得出它的含义是从这个地方开始运行,但当中是怎样一个过程,仍不清楚,但肯定和链接有关,只能循循渐渐的,提升自己的认识,把握对整个系统理解,现在知道从这个地方开始运行并跳到这个地方去。_startarch/arm/cpu/start.S中的一个地址,看start.S,这就是程序开始的地方。

Arch/arm/cpu/arm926ejs/start.S:


.globl _start

_start:

b reset


reset:

/*

* set the cpu to SVC32 mode

*/

mrs r0,cpsr

bic r0,r0,#0x1f

orr r0,r0,#0xd3

msr cpsr,r0

/*上面的指令是对cpsr的操作,arm有几种工作模式,只有将这个寄存器给予某个值才能对硬件有操作权限,cpu才能让你的指令通过*/


/*

* we do sys-critical inits only at reboot,

* not when booting from ram!

*/


#ifndef CONFIG_SKIP_LOWLEVEL_INIT

bl cpu_init_crit

#endif


/*cpu_init_crit的功能是设置重要的寄存器和memory,但现在u-boot已经有bootstrap启动起来了,所以肯定不会运行这里,不然memory又被搞死了。*/


call_board_init_f:

ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)

bic sp, sp, #7 /* 8-byte alignment for ABI compliance */

ldr r0,=0x00000000 //第一个参数

bl board_init_f

/*call_board_init_f设置好堆栈之后跳入到了board_init_f函数当中。CONFIG_SYS_INIT_SP_ADDRinclude/configs/at91sam9260当中定义

CONFIG_SYS_INIT_SP_ADDR = ATMEL_BASE_SRAM + 0x1000 -

GENERATED_GBL_DATA_SIZE当中。

ATMEL_BASE_SRAM = 0x300000.

可见这个时候的堆栈地址还在sram当中,大小为4k - GENERATED_GBL_DATA_SIZE,后面一个值是什么,现在不管。继续分析board_init_f,见后面*/


relocate_code:

.globl relocate_code

relocate_code:

/*ro参数1r1参数2r2参数3*/

mov r4, r0 /* save addr_sp */

mov r5, r1 /* save addr of gd */

mov r6, r2 /* save addr of destination */


stack_setup:

mov sp, r4 //在这里设置C语言的栈空间,从高到低的延伸地址

adr r0, _start

/*_startu-boot在内存中的起始地址,r6=r2=addr=uboot的从新加载地址。在我看来,这种重新加载机制是为了方便系统管理*/

sub r9, r6, r0 /* r9 <- relocation offset */

cmp r0, r6

/*r0 != r6*/

beq clear_bss /* skip relocation */

mov r1, r6 /* r1 <- scratch for copy loop */

ldr r3, _bss_start_ofs

add r2, r0, r3 /* r2 <- source end address */

/*_start_bss_start的数据全部复制到addr出,即在board_init_f中指明的u-boot的重新加载地址处,,产生了问题,为什么bss处的数据不再转移了*/


copy_loop:

ldmia r0!, {r9-r10} /* copy from source address [r0] */

stmia r1!, {r9-r10} /* copy to target address [r1] */

cmp r0, r2 /* until source end address [r2] */

blo copy_loop


/*这段代码是将bss段清0bss(block started by symbol),在运行不占空间,它的清零也许会和malloc的实现相关*/

clear_bss:

#ifdef CONFIG_SPL_BUILD

/* No relocation for SPL */

ldr r0, =__bss_start

ldr r1, =__bss_end__

#else

ldr r0, _bss_start_ofs

ldr r1, _bss_end_ofs

mov r4, r6 /* reloc addr */

add r0, r0, r4

add r1, r1, r4

#endif

mov r2, #0x00000000 /* clear */




/*清零之后跳转*/

clbss_l:cmp r0, r1 /* clear loop... */

bhs clbss_e /* if reached end of bss, exit */

str r2, [r0]

add r0, r0, #4

b clbss_l

clbss_e:

#ifndef CONFIG_SPL_BUILD

bl coloured_LED_init

bl red_led_on

#endif



u-boot的第一阶段到此为止,然后跳转到C语言入口,进行第二阶段。这个阶段主要做了3件事情,硬件初始化并设置gd_t数据结构,然后为C语言设置栈,重加载u-boot到内存。从细节上来看,先汇编设置片内的sdram栈,然后跳到C语言部分初始化硬件,设置好内存布局,然后返回汇编设置外部SDRAM栈,接着搬运u-bootsdram.然后跳到C语言入口处。有一点需要注意的是u-bootbootstrap搬运到内存当中的。



board_init_f位于arch/arm/lib/board.c当中:

/*bootflag = 0*/

void board_init_f(ulong bootflag)

{

bd_t *bd;

init_fnc_t **init_fnc_ptr;

gd_t *id;

ulong addr, addr_sp;

#ifdef CONFIG_PRAM

ulong reg;

#endif


/*BOOTSTAGE_ID_START_UBOOT_F定义于include/bootstage.h,其作用是如果u-boot失败了,它能知道自己处于一个什么样的状态,在什么地方挂掉了,把握对全局的控制*/

bootstage_mark_name(BOOTSTAGE_ID_START_UBOOT_F, "board_init_f");



/* Pointer is writable since we allocated a register for it */

gd = (gd_t *) ((CONFIG_SYS_INIT_SP_ADDR) & ~0x07);

/ *gd_t是一个全局数据结构,能够表示所有的资源信息,比如串口,资源主频,和u-boot相关的各种地址,gd_t包括bd_t结构体,它只和cpu,board,ram有关*/


/* compiler optimization barrier needed for GCC >= 3.4 */

/*内存屏蔽,是同步的一种机制,保证上面这段代码不被优化,按照顺序运行*/

__asm__ __volatile__("": : :"memory");


memset((void *)gd, 0, sizeof(gd_t));


/*_bss_end_ofs = __bss_end__ - _start (__bss_end_是在u-boot.lds当中定义的u-boot在内存中存放的最后位置),即整个u-boot的长度*/

gd->mon_len = _bss_end_ofs;


/*设备树,现在有什么用还不知道,暂时不管*/

#ifdef CONFIG_OF_EMBED

/* Get a pointer to the FDT */

gd->fdt_blob = _binary_dt_dtb_start;

#elif defined CONFIG_OF_SEPARATE

/* FDT is at end of image */

gd->fdt_blob = (void *)(_end_ofs + _TEXT_BASE);

#endif

/* Allow the early environment to override the fdt address */

gd->fdt_blob = (void *)getenv_ulong("fdtcontroladdr", 16,

(uintptr_t)gd->fdt_blob);


/*这是一个函数指针,指向各个需要做的函数,包括一些硬件初始化,比如dram_init,sdam分成bank*/

for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {

if ((*init_fnc_ptr)() != 0) {

hang ();

}

}



/*CONFIG_SYS_SDRAM_BASE = 0x20000000,gd->ram_size = 0x4000000(32M),这个工作就是在dram_init当中完成的,可见addr=sdram的最高物理地址*/

addr = CONFIG_SYS_SDRAM_BASE + gd->ram_size;


/*接下来是保留各种内存,保留内存,lcdmalloc,u-boot.以下是保留u-boot区域*/


addr -= gd->mon_len;

addr &= ~(4096 – 1);


/*从上面看,我们得到一个比较重要的地址addr = SDARM的最高地址(物理地址) - 一系列保存地址(其中包括u-boot)这里不禁想我们不是在运行u-boot吗,为什么还要保留一份拉?这是一个大问题*/


/*这里就是我们传给linux的机器ID,如果没有定义,我们需要自己定义,否则是跑不起来的*/

gd->bd->bi_arch_number = CONFIG_MACH_TYPE; /* board id for Linux */


addr_sp = addr – TOTAL_MALLOC_LEN;

addr_sp -= sizeof (bd_t);

bd = (bd_t *) addr_sp;

gd->bd = bd;


irq_sp = addr_sp;

addr_sp -= 12;

/*以上这段代码给出了addr_sp指向哪里,irq_sp指向哪里,很明显可以看出addr_sp是栈指针,irq_sp是中断异常栈指针,现在需要明确一个观念地址是从高地址到低地址延伸的,另外现在还没有用addr_sp来设置CPU寄存器,所以现在我们用的栈还是在start.S当中设置的SOC地址,现在有一种感觉,那就是整体上有了一点想法,上电这个时候SDRAM还没有像以后有BANK,用的是内部SDRAM,现在变开始设置用外部SDRAM。从代码上来看是汇编设置栈,跳到C语言获取板子信息,初始化硬件(不包括CPU和内部SDRAM),然后即将设置栈,接着....*/



gd->bd->bi_baudrate = gd->baudrate; //获取波特率

dram_init_banksize();

/*设置物理地址 bank,这里只设置了SDRAMbank

gd->bd->bi_dram[0].start = CONFIG_SYS_SDRAM_BASE;

gd->bd->bi_dram[0].size = gd->ram_size;

可见这些信息都保存在gd当中的,然后会保存到bd当中*/


/*这些信息关系u-boot会在哪里*/

gd->relocaddr = addr;

gd->start_addr_sp = addr_sp;

gd->reloc_off = addr - _TEXT_BASE;


memcpy(id,&gd,sizeof(gd_t));

relocate_code(addr_sp,id,addr);


/*board_init_f分析完了,可见它做了几件事情初始化部分硬件,设置堆栈指针,划分外部SDRAM区域,现在跳到relocate_code中去,位于start.S当中*/

}


阅读(1860) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~