购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

4.4 U-Boot的启动过程分析

U-Boot的启动过程分为两个阶段:处理器初始化阶段和板级初始化阶段。U-Boot启动完成后根据用户的选择进入不同的处理状态,如果在循环等待阶段按任意键则进入命令行状态,接受用户输入的命令,可以执行环境变量设置、下载烧写程序、运行程序等操作。如果在循环等待阶段没有用户干预,则进入操作系统启动模式,复制操作系统并跳转到操作系统入口处执行µClinux内核。

4.4.1 处理器初始化

目标板外部存储空间包括SDRAM和Flash,地址范围如表4.9所示。

表4.9 目标板外部存储空间

U-Boot程序的代码段、数据段等各部分内容在存储器中的位置由/board/bf537-stamp/u-boot.lds文件决定。u-boot.lds文件是链接脚本文件,链接器bfin-uclinux-ld根据该文件决定各部分的存储和执行位置。打开u-boot.lds文件,在SECTIONS中可以清楚地看到代码段的定义:

. = ((32 * 1024*1024) - (256 << 10));

.text :

{

cpu/bf537/start.o (.text)

cpu/bf537/start1.o (.text)

cpu/bf537/traps.o (.text)

cpu/bf537/interrupt.o (.text) cpu/bf537/serial.o (.text)

common/dlmalloc.o (.text)

lib_generic/crc32.o (.text)

. = DEFINED(env_offset) ? env_offset : .;

common/environment.o (.text)

*(EXCLUDE_FILE (board/bf537-stamp/post-memory.o) .text)

*(.fixup)

*(.got1)

} > ram

首先定义代码段的起始地址是((32 * 1024*1024) - (256 << 10)),即0x1fc0000,这个地址就是配置文件中定义的CFG_MONITOR_BASE的地址,也就是U-Boot的起始地址。存放的第一个文件是/cpu/bf537/start.o,该文件是U-Boot的入口文件,即最早执行的文件,其次是cpu/bf537/start1.o文件。看上去U-Boot是从0x1fc0000开始执行的,事实上并非如此,因为U-Boot是烧写在Flash中的,而目标板的启动模式是从Flash直接启动,也就是说是从Flash的起始地址0x20000000开始执行的。关于启动模式的问题将在下节讨论,这里仅研究U-Boot的启动过程。实际上U-Boot的确是从0x20000000开始执行的,本阶段的启动过程如图4.12所示。

图4.12 处理器初始化阶段的启动过程

启动代码在/cpu/bf537/start.S文件中,代码复制与跳转方法设计得很巧妙,具体参看下面的程序注释。

call get_pc; //获得当前的执行地址,应该是指向Flash空间的地址

offset:

r2.l = offset;

r2.h = offset;

r3.l = start;

r3.h = start;

r1 = r2 - r3;

r0 = r0 - r1;

p1 = r0; //计算U-Boot在Flash中的起始地址,结果存放在p1中

p2.l = (CFG_MONITOR_BASE & 0xffff);

p2.h = (CFG_MONITOR_BASE >> 16); //获得U-Boot在SDRAM空间的地址

p3 = 0x04;

p4.l = ((CFG_MONITOR_BASE + CFG_MONITOR_LEN) & 0xffff);

p4.h = ((CFG_MONITOR_BASE + CFG_MONITOR_LEN) >> 16);

loop1:

r1 = [p1 ++ p3];

[p2 ++ p3] = r1;

cc=p2==p4;

if !cc jump loop1; //把Flash中的代码循环复制到SDRAM中

p0.l = (EVT_IVG15_ADDR & 0xFFFF);

p0.h = (EVT_IVG15_ADDR >> 16);

p1.l = _real_start;

p1.h = _real_start;

[p0] = p1; //把_real_start地址设置到软件中断向量表p0.l = (IMASK & 0xFFFF);

p0.h = (IMASK >> 16);

r0.l = LO(IVG15_POS);

r0.h = HI(IVG15_POS); //使能软件中断

[p0] = r0;

raise 15; //触发软件中断,从而跳转_real_start处执行,_real_start指向SDRAM地址

4.4.2 板级初始化

_real_start是第二阶段启动的开始,此时已经跳转到SDRAM空间。本阶段的启动过程如图4.13所示。

图4.13 板级初始化阶段启动过程

4.4.3 µClinux的启动过程

Main_loop函数在/common/main.c中实现,进入该函数后U-Boot等待用户输入,如果有任意键按下就会进入命令行状态,如果在设置的延时内没有输入则执行bootcmd命令。bootcmd是U-Boot的环境变量,为了引导µClinux通常使用以下命令设置bootcmd :

setenv bootcmd bootm 0x20020000

其中0x20020000是µClinux内核的存放地址。由于bootcmd的内容是bootm,所以程序会跳转到do_bootm()执行bootm命令。do_bootm函数在/common/cmd_bootm.c中实现,下面重点分析该函数的启动过程。在分析该函数之前,首先了解一个数据结构image_header_t,该结构存放µClinux映像的头信息,在bootm过程中用到。

typedef struct image_header {

uint32_t ih_magic;

uint32_t ih_hcrc;

uint32_t ih_time;

uint32_t ih_size; uint32_t ih_load;

uint32_t ih_ep; uint32_t ih_dcrc;

uint8_t ih_os;

uint8_t ih_arch;

uint8_t ih_type;

uint8_t ih_comp;

uint8_t ih_name[IH_NMLEN];

} image_header_t;

image_header_t在include/image.h文件中定义,宏IH_NMLEN定义为32,因此整个头信息的长度为64字节,这个64字节的头信息是U-Boot启动特有的要求。µClinux内核映像必须加上64字节的头信息才能被U-Boot引导,否则不能被识别,各成员变量的含义如表4.10所示。

表4.10 µClinux映像头信息的含义

(续表)

o_bootm()的启动流程如图4.14所示。

图4.14 do_boom()启动过程

do_bootm()中的解压函数实现了把µClinux映像从Flash到SDRAM的数据搬移,这样保证了以后µClinux的运行是在SDRAM空间完成的。加载的地址不是随便确定的,而是要和µClinux映像生成的参数保持一致。do_bootm()函数执行到最后会调用do_bootm_linux()进入µClinux的启动过程,do_bootm_linux()在/lib_blackfin/bf533_linux.c中定义,该函数的实现如下所示:

void do_bootm_linux(cmd_tbl_t * cmdtp, int flag, int argc, char

*argv[],

ulong addr, ulong * len_ptr, int verify)

{

int (*appl)(char *cmdline);

char *cmdline;

appl = (int (*)(char *))ntohl(header.ih_ep); printf("Starting Kernel at = %x\n", appl);

cmdline = make_command_line();

if (icache_status()) {

flush_instruction_cache();

icache_disable();

}

if (dcache_status()) {

flush_data_cache();

dcache_disable();

}

(*appl)(cmdline);

}

do_linux_bootm()的实现比较简单,首先把映像头信息中的入口地址ih_ep转换为一个函数指针,然后根据配置文件中定义的BOOTARGS变量生成传递给µClinux内核的命令行参数,最后以该命令行为参量调用入口函数,完成µClinux的启动工作。实际上ih_ep指向的地址是µClinux的_start()函数,即µClinux运行的起始地址,上述调用实现了到µClinux内核的跳转工作,程序控制不再返回U-Boot,而是µClinux内核接管了对处理器的控制。命令行的定义如下:

#define CONFIG_BOOTARGS "root=/dev/mtdblock0 rw console=ttyBF0,115200" vnIoOC6oxsot4UhFF7Hgh6zONMVxHwXMrBfQlbovOi658Frw6o1F+sU1Fo0IFYTp

点击中间区域
呼出菜单
上一章
目录
下一章
×