U-boot在AT91RM9200上的全线移植分析
[email protected] 转载请注明
http://blog.csdn.net/sailor_8318/archive/2007/10/11/1820904.aspx?P_AVPASS=PHDGBITAVPASSP
**********************************************************************
摘要:本文介绍了bootloader的基础知识,分析了U-boot的源代码结构、常见命令及其启动流程,对于AT91RM9200的片内片外启动方式进行了详尽分析,分析启动所需的引导文件的详细流程。针对具体开发板的内存分配情况,介绍了U-boot在AT91RM9200上移植的详细过程。分析了使用go或bootm启动linux内核的方法,对于bootm方式的内核是否压缩、-a、-e、运行地址等16种组合情况,给出了详细的测试过程,提出了6种可用方法种的三种最优解。
关键字:U-boot;AT91RM9200;bootm;mkimage;-a;-e;-c
后记:
“纸上得来终觉浅”,以前由于课题任务较紧张,只是对U-boot的移植进行了一些了解,并没有仔细分析其详细的移植过程,尤其是对于一些地址设置方面的问题不太清楚。
上次给小布丁的参考资料有点零散,她也没看太明白,让我下定决心把U-boot的移植过程详细整理下。很多东西心里明白,但要写出来,确要花费更多的时间,但同时也会更注重于细节,对所做的项目也是一个总结完善的过程。求职在即,总结整理此文,也算是为求职做了些准备吧。
本文参考了众多网友的帖子,在此表示感谢!还有一些不太明白的地方,可能还有些错误,欢迎大家讨论指正。
谨以此文献给小布丁,希望她永远开心,工作学习顺利!
Sailor_forever
2007-10-10
**********************************************************************
目录
一 bootloader介绍
1.1 Bootloader移植的必要性
1.2 BootLoader所支持的CPU和嵌入式系统板
1.3. Boot Loader的安装媒介
1.4 Boot Loader的操作模式(Operation Mode)
15. Bootloader与主机之间进行文件传输所用的通信设备及协议
二 U-boot基础
2.1 U-boot源代码目录结构
2.2 U-Boot支持的主要功能
2.3 U-boot命令介绍及环境变量
2.4 U-Boot的启动流程分析
三 U-BOOT在AT91RM9200上的移植
3.1 at91rm9200的启动方式
3.1.1 片内引导
3.1.2 片外引导
3.2 loader.bin, boot.bin, u-boot.bin代码执行流分析.
3.2.1 loader.bin
3.2.2 boot.bin执行流程
3.2.1 uboot.bin执行流程
3.3 AT91RM9200开发板的存储器情况
3.3.1 内部存储器映射
3.3.2 外部存储器映射
3.4 U-BOOT在at91rm9200上移植修改的文件
3.5 移植的具体步骤
四 U-boot如何引导Linux内核启动?
4.1 GO命令引导未用mkimage生成的内核
4.1.1 非压缩内核Image
4.1.2 压缩内核zImage
4.2 Mkimage参数意义解析
4.3 Bootm的流程分析
4.4 如何用mkimage生成uImage
4.5 Bootm命令引导mkimage生成的内核全程解析
4.5.1 非压缩的Image内核
4.5.2 压缩的zImage内核
4.5.3 关于压缩及非压缩内核bootm启动的全面总结
参考鸣谢:
介绍
移植的必要性
Bootloader是与系统硬件高度相关的初始化软件,它担负着初始化硬件和引导操作系统的双重责任。一些ARM平台可以共用同一种Bootloader,但是总的说来,每一个特定系统的Bootloader都会有所不同。Bootloader广泛用于有操作系统的手持终端设备,智能家电,机顶盒等嵌入式设备上,它负责完成硬件初始化,操作系统引导,系统配制等。Bootloader移植是在特定硬件平台上操作系统移植至关重要的一步。
所支持的CPU和嵌入式系统板
每种不同的CPU体系结构都有不同的BootLoader。有些BootLoader也支持多种体系结构的CPU,比如U-BOOT就同时支持ARM, MIPS, POWERPC等体系结构。除了依赖于CPU的体系结构外,BootLoader实际上也依赖于具体的嵌入式板级设备的配置。也就是说,对于两块不同的嵌入式板而言,即使它们是基于同一种CPU而构建的,要想让运行在一块板子上的BootLoader程序也能运行在另一块板子上,通常也都需要修改BootLoader的源程序。
的安装媒介
系统加电或复位后,所有的CPU通常都从某个由CPU制造商预先安排的地址上取指令。比如,at91rm9200的CPU在复位时通常都从地址0x00000000取它的第一条指令。而基于CPU构建的嵌入式系统通常都有某种类型的固态存储设备(EEPROM或FLASH等,at91rm9200是0x10000000)被映射到这个预先安排的地址上。因此在系统加电后,CPU将首先执行Boot Loader程序。
上图是一个同时装有Boot Loader、内核的启动参数、内核映像和根文件系统映像的固态存储设备的典型空间分配结构图。
的操作模式(Operation Mode)
主机和目标机之间一般通过串口建立连接,BootLoader软件在执行时通常会通过串口来进行数据传输,如输出打印信息到串口,从串口读取用户控制字符。
大多数Boot Loader都包含两种不同的操作模式:启动加载模式和下载模式,这种区别仅对于开发人员才有意义。从最终用户的角度看,BootLoader的作用就是用来加载操作系统,而并不存在所谓的启动加载模式与下载工作模式的区别。
启动加载(Boot loading)模式:这种模式也称为自主模式bootstrap。也即Boot Loader将存储在目标板Flash中的内核和文件系统的镜像装载到SDRAM中,整个过程无需用户的介入。这种模式是BootLoader的正常工作模式,因此在嵌入式产品发布的时候,BootLoader显然必须工作在这种模式下。
下载Downloading模式:在这种模式下,目标机上的BootLoader将通过串口连接或网络连接等通信手段从宿主机Host下载文件,比如:下载内核映像和根文件系统映像等。从主机下载的文件通常首先被BootLoader保存到目标机的RAM中,然后再被BootLoader写到目标机上的FLASH类固态存储设备中。BootLoader的这种模式通常在第一次安装内核与根文件系统时被使用;此外,以后的系统更新也会使用Boot Loader的这种工作模式。工作于这种模式下的BootLoader通常都会向它的终端用户提供一些简单的命令行接口。
像U-BOOT等这样功能强大的BootLoader通常同时支持这两种工作模式,而且允许用户在这两种工作模式之间进行切换。比如,U-BOOT在启动时处于正常的启动加载模式,但是它会延时几秒(在配置文件中可以设定)等待终端用户按下任意键而将其切换到下载模式(相当于bios下按del键可进入系统配置界面一样,设置从光盘启动可进行重装),如果在给定时间内没有用户按键,则U-BOOT继续启动,进行正常的启动加载。
与主机之间进行文件传输所用的通信设备及协议
最常见的情况就是,目标机上的BootLoader通过串口与主机之间进行文件传输,传输协议通常是kermit / xmodem / ymodem协议中的一种。但是,串口传输的速度是有限的,因此如果该Bootloader对目标板的网卡支持良好,还可以通过以太网连接并借助TFTP (Trivial File Transfer Protocol)协议来下载文件是个更好的选择。此时,主机方所用的软件也要考虑,比如,在通过以太网连接和TFTP协议来下载文件时,主机方必须有一个软件用来提供TFTP服务,如Windows平台上的tftpd.exe等或Linux下面的tftp服务器。
1.6 Bootloader的通用执行流程
从操作系统的角度看,Bootloader的总目标就是正确地调用内核来执行。另外,由于Bootloader的实现依赖于 CPU的体系结构,因此大多数Bootloader都分为stagel和stage2两大部分。依赖于CPU体系结构的代码,比如设备初始化代码等,通常都放在stagel中,而且通常都用汇编语言来实现,以达到短小精悍和高效的目的。而stage2则通常用C语言来实现,这样可以实现更复杂的功能,而且代码会具有更好的可读性和可移植性。
Bootloader的stagel通常包括以下步骤(以执行的先后顺序):
l 硬件设备初始化(配置SDRAM存储控制器及IO),中断初始化
l 为加载Bootloader的stage2准备RAM空间(测试内存空间是否有效)
l 拷贝Bootloader的stage2到RAM空间中
l 设置好堆栈
l 跳转到stage2的C入口点
Bootloader的stage2通常包括以下步骤(以执行的先后顺序):
l 初始化本阶段要使用到的硬件设备
l 检测系统内存映射(memory map)
l 没有用户干预时将kernel映像和根文件系统映像从flash读到RAM空间中
l 为内核设置启动参数
l 调用内核
基础
现在为Linux开放源代码Bootloader有很多,blob, redboot, U-BOOT等,其中U-BOOT是目前用来开发嵌入式系统引导代码使用最为广泛的Bootloader。它支持POWERPC, ARM,MIPS, X86等处理器,支持嵌入式操作系统有Linux, Vxworks, NetBSD等。
源代码目录结构
|-- board 平台依赖,存放电路板相关的目录文件
|-- common 通用多功能函数的实现
|-- cpu 平台依赖,存放cpu相关的目录文件
|-- disk 通用。硬盘接口程序
|-- doc 文档
|-- drivers 通用的设备驱动程序,如以太网接口驱动
|-- dtt
|-- examples 应用例子
|-- fs 通用存放文件系统的程序
|-- include 头文件和开发板配置文件,所有开发板配置文件放在其configs里
|-- lib_arm 平台依赖,存放arm架构通用文件
|-- lib_generic 通用的库函数
|-- lib_i386 平台依赖,存放x86架构通用文件
|-- lib_m68k 平台依赖
|-- lib_microblaze 平台依赖
|-- lib_mips 平台依赖
|-- lib_nios 平台依赖
|-- lib_ppc平台依赖,存放ppc架构通用文件
|-- net 存放网络的程序
|-- post 存放上电自检程序
|-- rtc rtc的驱动程序
`-- tools 工具
详细实例:
board:开发板相关的源码,不同的板子对应一个子目录,内部放着主板相关代码。
Board/at91rm9200dk/at91rm9200.c, config.mk, Makefile, flash.c ,u-boot.lds等都和具体开发板的硬件和地址分配有关。
common:与体系结构无关的代码文件,实现了u-boot所有命令,其中内置了一个shell脚本解释器(hush.c, a prototype Bourne shell grammar parser), busybox中也使用了它.
cpu:与cpu相关代码文件,其中的所有子目录都是以u-boot所支持的cpu命名.
cpu/at91rm9200/at45.c, at91rm9200_ether.c, cpu.c, interrupts.c serial.c, start.S, config.mk, Makefile等. 其中cpu.c负责初始化CPU、设置指令Cache和数据Cache等;
interrupt.c负责设置系统的各种中断和异常,比如快速中断、开关中断、时钟中断、软件中断、预取中止和未定义指令等;
start.S负责u-boot启动时执行的第一个文件,它主要是设置系统堆栈和工作方式,为跳转到C程序入口点.
disk:设备分区处理代码。
doc:u-boot相关文档。
drivers:u-boot所支持的设备驱动代码, 网卡、支持CFI的Flash、串口和USB总线等。
fs: u-boot所支持支持文件系统访问存取代码, 如jffs2.
include:u-boot head文件,主要是与各种硬件平台相关的头文件,如include/asm-arm/arch-at91rm9200/ AT91RM9200.h(硬件寄存器名称及地址的定义), hardware.h (内存及flash地址以及IO物理地址和虚拟地址的定义),include/asm-arm/proc-armv (与具体的CPU无关,无需移植)
net:与网络有关的代码,BOOTP协议、TFTP协议、RARP协议代码实现.
lib_arm:与arm体系相关的代码。
tools:编译后会生成mkimage工具,用来对生成的raw bin文件加入u-boot特定的image_header.
支持的主要功能
主要功能如下:
系统引导 支持NFS挂载、RAMDISK(压缩或非压缩)形式的根文件系统
支持NFS挂载、从FLASH中引导压缩或非压缩系统内核;
基本辅助功能 强大的操作系统接口功能;可灵活设置、传递多个关键参数给操作系统,适合系统在不同开发阶段的调试要求与产品发布,尤对Linux支持最为强劲;
支持目标板环境参数多种存储方式,如FLASH、NVRAM、EEPROM;
CRC32校验,可校验FLASH中内核、RAMDISK镜像文件是否完好;
设备驱动 串口、SDRAM、FLASH、以太网、LCD、NVRAM、EEPROM、键盘、USB、PCMCIA、PCI、RTC等驱动支持;
上电自检功能 SDRAM、FLASH大小自动检测;SDRAM故障检测;CPU型号;
特殊功能 XIP内核引导;
命令介绍及环境变量
1. ?得到所有命令列表
2. help: help usb, 列出USB功能的使用说明
3. ping:注:只能开发板PING别的机器(AT91RM9200不支持)
4. setenv: 设置环境变量:
5. setenv serverip 192.168.0.1
6. setenv ipaddr 192.168.0.56
7. setenv bootcmd ‘tftp 32000000 vmlinux; kgo 32000000’
8. saveenv: 保存环境变量 在设置好环境变量以后, 保存变量值
10. tftp: tftp 32000000 vmlinux, 把server(IP=环境变量中设置的serverip)中/tftpboot/ 下的vmlinux通过TFTP读入到物理内存32000000处。
11. kgo: 起动没有压缩的linux内核,kgo 32000000(AT91RM9200不支持)
12. bootm:起动UBOOT TOOLS制作的压缩LINUX内核, bootm 3200000
13. protect: 对FLASH进行写保护或取消写保护, protect on 1:0-3(就是对第一块FLASH的0-3扇区进行保护),protect off 1:0-3取消写保护
14. erase: 删除FLASH的扇区, erase 1:0-2(就是对每一块FLASH的0-2扇区进行删除)
15. cp: 在内存中复制内容, cp 32000000 0 40000(把内存中0x32000000开始的0x40000字节复制到0x0处)
16. mw: 对RAM中的内容写操作, mw 32000000 ff 10000(把内存0x32000000开始的0x10000字节设为0xFF)
17. md: 修改RAM中的内容, md 32000000(内存的起始地址)
18. usb:
usb start: 起动usb 功能
usb info: 列出设备
usb scan: 扫描usb storage(u 盘)设备
19. fatls:列出DOS FAT文件系统, 如:fatls usb 0列出第一块U盘中的文件
20. fatload: 读入FAT中的一个文件,如:fatload usb 0:0 32000000 aa.txt
21. 把USB中的aa.txt 读到物理内存0x32000000处!
22. flinfo: 列出flash的信息
23. loadb: 准备用KERMIT协议接收来自kermit或超级终端传送的文件。
24. nfs: nfs 32000000 192.168.0.2:aa.txt , 把192.168.0.2(LINUX 的NFS文件系统)中的NFS文件系统中的aa.txt 读入内存0x32000000处。
最常用的几个命令
go- 在地址 'addr' 处开始程序执行
run- 运行命令
bootm- 从内存中进行应用程序影象运行
bootp- 通过网络用 BootP/TFTP 协议来启动影象
tftpboot- 通过网络用 TFTP 协议、设置服务器和客户机的 IP 地址进行影象文件传送
loadb- 通过串口线(kermit mode) 来装载二进制文件
printenv- 打印环境变量
setenv- 设置环境变量
saveenv保存环境变量到内存
下面是 U-BOOT 中的简单环境变量
baudrate波特率
bootdelay boot 延迟
bootcmd Boot 命令
bootargs Boot 参数
bootfile
ipaddr 客户机 IP 地址
serverip 服务器地址
loadaddr 装载地址
ethaddr 网卡 MAC 地址
的启动流程分析
和大多数的Bootloader一样,U-BOOT的启动分为两个阶段两个部分,依赖于CPU体系结构的代码主要放在stage1,且用汇编来实现,而stage2则通常用C语言来实现,这样可以实现复杂的功能,而且具有更好的可读性和可移植性。
下面分别分析一下这两个阶段的启动流程:
第一阶段:基本的硬件初始化,为第二阶段程序运行建立环境(cpu/ at91rm9200/start.s文件的代码部分):
××××××××××××××××××××××××××××
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start) // 程序的入口在/cpu/××××/start.s中定义
SECTIONS
{
. = 0x00000000; // 程序存储域和运行域的首地址,链接脚本决定了各个段在印象中的位置,默认情况下VMA=LMA
. = ALIGN(4);
.text :
{
cpu/at91rm9200/start.o (.text) //明确start.o为链接的第一个文件
*(.text)
}
. = ALIGN(4);
.rodata : { *(.rodata) }
. = ALIGN(4);
.data : { *(.data) }
. = ALIGN(4);
.got : { *(.got) }
__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;
armboot_end_data = .; //代码段结束地址
. = ALIGN(4);
.bss : { *(.bss) }
armboot_end = .; //整个U-boot印象的结束地址
}
××××××××××××××××××××××××××××
在此需要定义程序入口,由于一个可执行的Image必须要有一个入口点,并且只能有一个全局入口,通常这个入口就在ROM (flash)的0x0地址,因此,必须通知编译器以使其知道这个入口,该工作可通过修改连接器脚本u-boot.lds来完成,该阶段需要依次完成的工作一般包括:
1. CPU自身的初始化,它包括:CPU运行模式的设置(管理模式)、设置异常的入口地址和异常处理函数、运行时钟频率的设置等工作。
2.初始化GPIO和内存控制器。
3.为拷贝Stage2准备RAM空间。
4.进行自拷贝,将U-BOOT的Stage2拷贝到RAM中。//如何确定stage2的起始位置?
5.设置好堆栈。
6.跳转到Stage2的入口,从而转到RAM中执行,该工作是调用指令ldr pc, start_armboot来完成的。
××××××××××××××××××××××××××
从1.1.2开始,u-boot有初始化SDRAM并拷贝自己到SDRAM运行的代码,而之前的版本就没有这个功能(详细查看下代码??的确如此,因此对于以前的版本TEXT_BASE没有起作用?实际测试下??)。board/at91rm9200dk中config.mk文件(TEXT_BASE = 0x21f00000,32M RAM的设置,为第二阶段程序在RAM中的运行地址)用于设置程序编译连接的起始地址,在程序中要特别注意与地址相关指令的使用。
Board/at91rm9200dk/config.mk
TEXT_BASE = 0x21f00000(u-boot将被载入SDRAM的高端部分)
注意,对于不同的系统RAM大小可能不一样,要根据实际情况调整。
在/config.mk中
ifdef BOARD
sinclude $(TOPDIR)/board/$(BOARDDIR)/config.mk # include board specific rules
endif
CPPFLAGS := $(DBGFLAGS) $(OPTFLAGS) $(RELFLAGS) /
-D__KERNEL__ -DTEXT_BASE=$(TEXT_BASE)
LDFLAGS += -Bstatic -T $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS)
export TEXT_BASE PLATFORM_CPPFLAGS PLATFORM_RELFLAGS CPPFLAGS CFLAGS AFLAGS
对于U-Boot 1.1.2以前的版本,并没有自拷贝的部分,若flash中首地址存放的是非压缩的u-boot.bin的,则启动部分是一直运行在flash中的;但对于AT91RM9200来说,他通常有三个文件loader.bin, boot.bin, u-boot.bin,flash中首先运行的是boot.bin,其将压缩的u-boot.bin.gz解压拷贝到TEXT_BASE处运行,此时已经在RAM中了,因此也无需实现自拷贝了。
对于U-Boot 1.1.2以后的版本,若像AT91RM9200有boot.bin这样的过渡程序,则将u-boot.bin.gz解压到RAM中,此时运行地址和链接地址相同,无需拷贝;若没有,则u-boot.bin将在flash中执行初始化部分,然后将自身拷贝到RAM中执行。
// 比较运行地址和链接地址,如果当前已经在RAM中运行了,则无需拷贝到RAM中
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
ldr r3, _bss_start
sub r2, r3, r2 /* r2 <- size of armboot */
add r2, r0, r2 /* r2 <- source end address */
copy_loop:
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
当程序在Flash中运行时,执行程序跳转时必须要使用相对跳转指令,而不能使用绝对地址的跳转(即直接对PC操作)。如果使用绝对地址,那么,程序的取指是相对于当前PC位置向前或者向后的32MB空间内,而不会跳入SDRAM中。
×××××××××××××××××××××××××××××
在上述操作运行完成后,就进入到/lib_arm/board.c 中的start armboot()函数运行,并建立起了一个基本的环境,此时的物理内存空间的分布就变成了如图所示的情况。
转入boatloader stage2的系统内存布局
第二阶段:运行U-BOOT的主体部分
该阶段以程序跳转到lib arm/board.c中的start armboot函数为标志,该函数同时也是C语言的开始函数,是整个启动代码的主体函数,同时还是整个U-BOOT的主体函数,该函数主要完成以下工作:
I.调用一系列的初始化函数初始化本阶段使用到的硬件,如:
×××××××××××××××××××
init_fnc_t *init_sequence[] = {
cpu_init, /* basic cpu dependent setup */
board_init, /* basic board dependent setup */
interrupt_init, /* set up exceptions */
env_init, /* initialize environment */
init_baudrate, /* initialze baudrate settings */
serial_init, /* serial communications setup */
console_init_f, /* stage 1 init of console */
display_banner, /* say that we are here */
dram_init, /* configure available RAM banks */
display_dram_config,
#if defined(CONFIG_VCMA9)
checkboard,
#endif
NULL,
};
×××××××××××××××××××××
env_init:设置环境变量,初始化环境;
init_baudrate:设置串口的波特率;
serial_init:设置串口的工作方式;
dram_init:设置SDRAM的起始地址和大小;
2检查存储器分配和使用情况:获取flash的bank分区情况、是否擦除、是否上锁等信息为以后flash相关命令使用;初始化系统内存分配函数,供后面的代码使用malloc等函数,U-BOOT没有使用其他现成的库,所有函数的实现均在文件中),如果系统有液晶等显示设备,一并在此分配显示内存。
4打印内存,flash、环境变量设置等信息。
5.等待几秒时间,如果有键盘输入,则进入命令模式,接收用户输入的命令并解释执行(如启动操作系统,更新flash内容)。
6如果在给定的时间内没有用户输入或者在执行命令操作时收到了用户要求启动内核的命令(boot),则将把操作系统内核和根文件系统映像文件拷贝到RAM中相应位置,并在设置内核启动参数后跳转到内核映像的首地址处执行。
×××××××××××××××××××××××××××××
U-BOOT调用 Linux 内核的方法是直接跳转到内核的第一条指令处,也即直接跳转到 MEM_START+0x8000地址处。在跳转时,要满足下列条件:
a) CPU寄存器的设置:R0=0;R1=机器类型 ID,本系统的机器类型ID=193。R2=启动参数标记列表在RAM中的起始基地址;
b) CPU模式:必须禁止中断(IRQs和FIQs);CPU必须工作在SVC模式;
c) Cache和MMU的设置:MMU 必须关闭;指令Cache可以打开也可以关闭;数据Cache必须关闭。
系统采用下列代码来进入内核函数:
void (*theKernel)(int zero, int arch);
theKernel = (void (*)(int, int))ntohl(hdr->ih_ep);
theKernel(0, bd->bi_arch_number);其中,hdr是image_header_t类型的结构体,hdr->ih_ep为entry point,指向内核的第一条指令地址,即Linux操作系统下的/kernel/arch/arm/boot/compressed/head.S汇编程序。theKernel()函数调用应该不会返回,如果该调用返回,则说明出错。
×××××××××××××××××××××××××××××
U-BOOT整体启动流程
在AT91RM9200上的移植
的启动方式
在这里我主要介绍通过片内引导和片外引导, 片内引导通常主要采用串口下载并引导u-boot,并完成程序被烧写到 Flash上,然后就可以通过跳线的方式从片外引导执行已经烧写到片外Flash上的引导程序(bootloader).
3.1.1 片内引导
片内引导的基本原理
系统上电,检测BMS,选择系统的启动方式,如果BMS为高电平,则系统从片内ROM启动。AT91RM9200的内部ROM上电后被映射到了0x0和0x100000处,在这两个地址处都可以访问到ROM。由于9200的ROM中固化了一个BOOTLOAER程序。所以PC从0X0处开始执行这个BOOTLOAER(准确的说应该是一级BOOTLOADER)。这个BOOTLOER依次完成以下步骤:
1. PLL SETUP
设置PLLB产生48M时钟频率提供给USB DEVICE。同时DEBUG USART也被初始化为48M的时钟频率。
2. 相应模式下的堆栈设置
3. 检测主时钟源(Main oscillator)
4. 中断控制器(AIC)的设置
5. C 变量的初始化
6. 跳到主函数
完成以上步骤后,我们可以认为BOOT过程结束,接下来的就是LOADER的过程,或者也可以认为是装载二级BOOTLOER。AT91RM9200按照DATAFLASH、EEPROM、连接在外部总线上的8位并行FLASH的顺序依次来找合法的BOOT程序。所谓合法的指的是在这些存储设备的开始地址处连续的存放的32个字节,也就是8条指令必须是跳转指令或者装载PC的指令,其实这样规定就是把这8条指令当作是异常向量表来处理。必须注意的是第6条指令要包含将要装载的映像的大小。关于如何计算和写这条指令可以参考用户手册。一旦合法的映像找到之后,则BOOT程序会把找到的映像搬到SRAM中去,所以映像的大小是非常有限的,不能超过16K的大小。当BOOT程序完成了把合法的映像搬到SRAM的任务以后,接下来就进行存储器的REMAP,经过REMAP之后,SRAM从映设前的0X200000地址处被映设到了0X0地址并且程序从0X0处开始执行。而ROM这时只能在0X100000这个地址处看到了。至此9200就算完成了一种形式的启动过程。
如果BOOT程序在以上所列的几种存储设备中未找到合法的映像,则自动初始化DEBUG USART口和USB DEVICE口以准备从外部载入映像。对DEBUG口的初始化包括设置参数115200 8 N 1以及运行XMODEM协议。对USB DEVICE进行初始化以及运行DFU协议。现在用户可以从外部(假定为PC平台)载入你的映像了。在PC平台下,以WIN2000为例,你可以用超级终端来完成这个功能,但是还是要注意你的映像的大小不能超过13K。一旦正确从外部装载了映像,接下来的过程就是和前面一样重映设然后执行映像了。
注意:通常所说的片内引导是指没有烧写合法的印象的情况下,但并不意外着烧些了合法印象的情况下不能采用片内引导的方式。通常第一次下载了启动印象后就会选择片外启动的方式了。并且烧些的印象通常第6条指令都不含将要装载的映像的大小,所以片内启动时一般不能运行这些印象。
关于片内引导的详细过程可以参看at91rm9200的芯片说明书――引导程序一章。
at91rm9200片内引导流程图
Boot program Flow Diagram
Device Setup
|
Boot SPI DataFlash Boot --> Download from DataFlash --> run
|
TWI EEPROM Boot --> Download from EEPROM --> run
|
Parallel Boot --> Download from 8-bit Device -->
|
| Xmodem protocol
| |---DBGU Serial Download ---------------------> run
|____|
| DFU protocol
|-----USB download -----------------------> run
at91rm9200片内引导u-boot的实现过程
at91rm9200内部本身有128k的片内rom,其固化了一个bootloader和uploader,其他存储设备上没有合法的映象时,片内引导将启动uploader,uploader开启xmodem协议,等待用户上传程序,上传的程序将载入片内SRAM,重映射,然后pc跳转到片内SRAM执行上传的用户程序,即loader.bin。
注:片内SRAM只有16k,除去3-4k片内启动程序的占用的部分数据空间,因此下载的程序大小限制在12k内。
裸板只能用片内引导方式,载入一个12k以内的小程序loader.bin到内部SRAM运行,而这个小程序初始化SDRAM后,再把u-boot.bin下载到SDRAM的高端运行(u-boot大于12k,不能直接下载的原因就在于此),pc跳到SDRAM的u-boot位置运行u-boot,u-boot启动后再用u-boot自己的命令把boot.bin 及u-boot.gz下载到SDRAM的低端,再用flash烧写命令烧到flash去,以后就可以片外flash启动了。
3.1.2 片外引导
如果BMS为低电平,则AT91RM9200会从片外的FLASH启动,这时片外的FLASH的起始地址就是0X0了,要求已经在此地址烧些了启动映象了,接下来的过程和片内启动的过程是一样的,只不过这时就需要自己写启动代码了,至于怎么写,大致的内容和ROM的BOOT差不多,不同的硬件设计可能有不一样的地方,但基本的都是一样的。由于片外FLASH可以设计的大,所以这里编写的BOOTLOADER可以一步到位,也就是说不用像片内启动可能需要BOOT好几级了。
对于AT91RM9200,通常选择在flash的首地址处放的是boot.bin,由其将u-boot.bin.gz解压到高端RAM中,再运行真正的u-boot.bin,也就是实际的启动映象。
代码执行流分析.
以上三个文件是at91rm9200启动所需要的三个bin,他们的实现代码并不难。
3.2.1 loader.bin
执行流程,这个文件主要在片内启动从串口下载U-boot.bin代码时会用到
loader/entry.S init cpu
b main ---> crt0.S
--> copydata --> clearbss --> b boot
main.c --> boot -->
/*Get internel rom service address*/
/* Init of ROM services structure */
pAT91 = AT91C_ROM_BOOT_ADDRESS;
/* Xmodem Initialization */
--> pAT91->OpenSBuffer
--> pAT91->OpenSvcXmodem
/* System Timer initialization */
---> AT91F_AIC_ConfigureIt
/* Enable ST interrupt */
AT91F_AIC_EnableIt
AT91F_DBGU_Printk("XMODEM: Download U-BOOT ");
Jump.S
// Jump to Uboot BaseAddr exec
Jump((unsigned int)AT91C_UBOOT_BASE_ADDRESS) 跳到下载的U-boot.bin执行
××××××××××××××××××××××××××××××××××
lader.bin主要有3个功能,初始化SDRAM,启动xmodem接收u-boot并写到SDRAM中,pc跳转到SDRAM运行。
xmodem的实现
只需要接收部分,发送部分用win下的”超级终端”等工具就可。
先找来协议文档,熟悉协议,看看现有的xmodem协议源码。协议本身并不复杂,只是它的握手部分实现有点技巧。
接收端要不停的发送字符“C”到串口,发送端收到“C”后发送数据SOH和第一个数据包。
接收端检测到SOH后停止发送“C”并开始处理数据。官方的loader启动了一个时间服务,每隔1s发送一个“C”,在这个我使用了偷懒的算法。
while(Getchar()!=AT91C_XMODEM_SOH)
{
if (0xFFFF==++n )
{
SendChar(AT91C_XMODEM_CRCCHR);
n=0;
}
}
握手解决了,后面的处理都没什么问题。
写SDRAM
unsigned char *pSdram = (unsigned char *)AT91C_UBOOT_BASE_ADDRESS;
for ( n = 0; n<128 ; n ++ )
{
*pSdram++=data[n];
}
PC跳转
添加一个文件jump.S到工程
AREA reset, CODE, READONLY
EXPORT Jump
Jump
mov pc, r0
END
;---------------------------------------------------------------------------------
在main中使用下面的函数跳转
Jump((unsigned int)AT91C_UBOOT_BASE_ADDRESS);
loader的调试过程
xmodem部分可以传一个调试文件,传进去后全部send回串口,看返回的信息就可以判断是否正常工作。
写SDRAM,依然是写入后再读出来看看是否一致,在这里卡了很久,发现每隔2个地址就不能使用,后来发现是SDRAM没有初始化,重写后正常。
Jump测试,得传入一个可以运行的程序到内存才能判断,用先前编译好的u-boot-1.0.0试一试,出现u-boot的提示符了, 也就是说jump没问题。
×××××××××××××××××××××××××××××××××××
3.2.2 boot.bin执行流程
该文件会在从片内启动时由U-boot.bin下载到板子上,以后还会被烧写到片外Flash中,以便在片外启动时用它来引导并解压u-boot.gz,并跳转到解压后的u-boot来执行。
boot/entry.S
b main --> crt0.S --> copydata --> clearbss --> b boot
T91F_DBGU_Printk(" ");
AT91F_DBGU_Printk("************************************** ");
AT91F_DBGU_Printk("** Welcome to at91rm9200 ** ");
AT91F_DBGU_Printk("************************************** ");
boot/misc.s /* unzip uboot.bin.gz */
----> decompress_image(SRC,DST,LEN) --> gunzip
//jump to ubootBaseAddr exec 这里跳转到解压后的u-boot地址处直接开始执行u-boot
asm("mov pc,%0" : : "r" (DST));
修改main.c中下面2项
#define SRC 0x10010000 (u-boot.gz将烧入flash的位置)
#define DST 0x21f00000 (u-boot.gz被解压后载入SDRAM的位置,和loader中保持一致)
3.2.1 uboot.bin执行流程
u-boot/cpu/at91rm9200/start.S
start --->reset
---> copyex ---> cpu_init_crit
---> /* set up the stack */ --> start_armboot
u-boot/lib_arm/board.c
init_fnc_t *init_sequence[] = {
cpu_init, /* basic cpu dependent setup */
board_init, /* basic board dependent setup */
interrupt_init, /* set up exceptions */
env_init, /* initialize environment */
init_baudrate, /* initialze baudrate settings */
serial_init, /* serial communications setup */
console_init_f, /* stage 1 init of console */
display_banner, /* say that we are here */
dram_init, /* configure available RAM banks */
display_dram_config,
checkboard,
NULL,
};
---> start_armboot ---> call init_sequence
---> flash_init --> display_flash_config
---> nand_init ---> AT91F_DataflashInit
---> dataflash_print_info --> env_relocate
---> drv_vfd_init --> devices_init --> jumptable_init
---> console_init_r --> misc_init_r --> enable_interrupts
---> cs8900_get_enetaddr --> board_post_init -->
u-boot/common/main.c
for (;;)
{ /* shell parser */
main_loop () --> u_boot_hush_start --> readline
--> abortboot
-->printf("Hit any key to stop autoboot: %2d ", bootdelay);
}
以上是at91rm9200启动并进入u-boot的执行流分析。后面u-boot还会将uImage解压到特定的位置并开始执行内核代码。
开发板的存储器情况
第一级地址译码由存储控制器执行,即由具有附加功能的高级系统总线(ASB) 执行。译码将32位地址总线决定的4G的地址空间分为16 个256M字节的区域。区域1 ~ 8 对应EBI,和外部片选NC0 ~NCS7相联系。区域0为内部存储器地址,第二级译码提供1M字节内部存储空间。区域15为外设地址,且提供对高级外设总线(APB) 的访问。其它区域未使用,使用它们进行访问时将向发出访问请求的主机发出异常中断。注意,地址的转换都是按照字节为单位的。
3.3.1 内部存储器映射
内部ROM:AT91RM9200集成了一个128-K字节的内部ROM。任何时候,ROM均被映射到地址0x10 0000。若复位时BMS 为高,即片内启动时,则在复位后到重新映射命令执行前,ROM有两个地址,可访问地址0x0。重映射之后SRAM将变为0地址,内部ROM地址就只为0x10 0000。
ROM容量为128KB,即对应0x20000。所以范围为0x100000-0x120000。
内部RAM:AT91RM9200集成了高速,16-K 字节的内部SRAM。复位后到重新映射命令执行前,只可访问SRAM 中0x20 0000的地址空间。重新映射后, SRAM 在地址0x0有效。
RAM容量为16KB,即对应0x4000,所以范围为0x200000-0x204000。
USB 主机端口:AT91RM9200集成了一个USB主机端口开放主机控制器接口(OHCI)。ASB可直接访问该接口寄存器,且同标准内部存储器一样映射到地址0x30 0000。
内部存储器映射
3.3.2 外部存储器映射
嵌入式存储设备通常主要是RAM 和作为永久存储媒质的Flash。
现在所用的AT91RM9200开发板所用的SDRAM是K4S281632F,其容量为4banks×2Mbits×16,即128Mbits=16Mbytes。SDRAM共有两片K4S281632F,数据总线位宽16,两片组成32位位宽,所以SDRAM容量为32MB。
现在所用的Flash芯片为Intel的28F128J3A,容量为16M,地址映射从0x10000000到0x10ffffff。现在将Flash分为128个扇区,每个扇区为128KB=0x20000,每个扇区分为两个擦除块,为64KB=0x10000。
-------------------------------------------------------------------
Chip Select 0――Flash(0x1000 0000-0x10FF FFFF)
0x1000 0000(第0扇区)
boot.bin Flash
0x1001 0000(第0扇区)
u-boot.bin.gz Flash
0x1002 0000(第1扇区)
uImage Flash
.
0x1012 0000(第9扇区)
ramdisk Flash
.
0x107E 0000(第127扇区)
u-boot环境变量 Flash
-------------------------------------------------------------------
Chip Select 1――SDRAM(0x2000 0000-0x2200 0000)
0x2000 0000
SDRAM
.
0x2100 0000
uImage SDRAM
0x2110 0000
ramdisk SDRAM
.
-------------------------------------------------------------------
0x0000 0000 ROM |
0x1000 0000 boot.bin FLASH |
0x1001 0000 uboot.gz FLASH |
0x1002 0000 ulmage FLASH |
0x1012 0000 ramdisk FLASH |
U-BOOT环境变量 8M_FLASH 63 扇区 FLASH 16M_FLASH 127扇区 |
0x2000 0000 SDRAM |
0x2100 0000 ulmage SDRAM |
0x2110 0000 ramdisk SDRAM |
在at91rm9200上移植修改的文件
为了使u-boot-1.0.0支持新的开发板,一种简便的做法是在u-boot已经支持的开发板中参考选择一种较接近板的进行修改, 幸运的是在u-boot-1.0.0中已经有了at91rm9200的支持。
下面将详细阐述对其进行移植所需要关注的几个方面:
l 修改原因:
硬件平台不同部分:由于目标板对GPIO、串口等硬件的使用不同或选择的RAM; flash等芯片的不同,都需要对相应的控制寄存器和硬件设备进行不同的初始化。
向bootloader增加新的功能:比如,有时想在目标平台上增加USB或Ethernet下载功能等,同样需要在源码中加入相应的代码。
l 移植相关内容:
在include/configs/at91rm9200dk.h 它包括开发板的CPU、系统时钟、RAM、Flash系统及其它相关的配置信息。与具体的板子相关,是移植的最重要文件。
在include/asm-arm/AT91RM9200.h, 该文件描述了9200寄存器的结构及若干宏定义。具体内容要参考相关处理器手册。相同CPU的此文件相同,拷贝一份即可,无需修改。
在cpu/at91rm9200/目录下别为cpu.c、interrupts.c和serial.c等文件.
cpu.c 无需改动,缓存,中断堆栈初始化,MMU映射等
interrupts.c无需修改。各种中断的处理函数,U-boot运行无需中断,同时实现为重启;定时中断实现为查询方式,主要用于Xmodem协议传输文件。
serial.c 无需修改,串口初始化,接收发送等。
上述文件都是与CPU相关的,与板子本身的配置无关。通常选择一个相同的CPU下的相关文件即可,无需修改。
在board/at91rm9200dk/目录下分别为flash.c、at91rm9200dk.c, config.mk, Makefile,u-boot.lds
flash.c : u-boot读、写和删除Flash设备的源代码文件。由于不同开发板中Flash存储器的种类各不相同(是移植的重点,可以从其他CPU目录下看是否有相同的flash),所以,修改flash.c时需参考相应的Flash芯片手册。
at91rm9200dk.c
板级初始化,DRAM地址初始化。修改文件
/* arch number of AT91RM9200DK-Board */
gd->bd->bi_arch_number = 251;
/* adress of boot parameters */
gd->bd->bi_boot_params = PHYS_SDRAM + 0x100;
体系结构号,CPU相关,同CPU此值相同。
bi_boot_params内核启动参数的首地址,通常为PHYS_SDRAM + 0x100;即SDRAM的100处,很重要,Linux内核移植时,此值需要匹配。
config.mk
TEXT_BASE = 0x21f00000(u-boot将被载入SDRAM的高端部分)
注意,对于不同的系统RAM大小可能不一样,要根据实际情况调整。
Makefile 无需修改,除非改动了at91rm9200dk.c的名字,目标文件要改动。
OBJS := at91rm9200dk.o flash.o
u-boot.lds 链接脚本, 设置u-boot中各个目标文件的连接地址。无需修改
Makefile
在u-boot-1.0.0/Makefile中
at91rm9200dk_config : unconfig
./mkconfig $(@:_config=) arm at91rm9200 at91rm9200dk(三个参数的意义解释??)
其中ARM是CPU的种类, at91rm9200是ARM CPU对应的代码目录,at91rm9200dk是自已主板对应的目录。
移植的具体步骤
关于u-boot的移植如下,由于u-boot的软件设计体系非常清晰,它的移植工作并不复杂,相信各位的代码阅读功力不错的话,参照如下就可以完成。
×××××××××××××××××××××××××××××××××××
If the system board that you have is not listed, then you will need to port U-Boot to your hardware platform. To do this, follow these steps:
1. Add a new configuration option for your board to the toplevel "Makefile" and to the "MAKEALL" script, using the existing entries as examples. Note that here and at many other places boards and other names are listed in alphabetical sort order. Please keep this order.
2. Create a new directory to hold your board specific code. Add any files you need. In your board directory, you will need at least the "Makefile", a ".c", "flash.c" and "u-boot.lds".
3. Create a new configuration file "include/configs/.h" for your board
4. If you're porting U-Boot to a new CPU, then also create a new directory to hold your CPU specific code. Add any files you need.
5. Run "make _config" with your new name.
6. Type "make", and you should get a working "u-boot.srec" file
7. Debug and solve any problems that might arise. [Of course, this last step is much harder than it sounds.]
××××××××××××××××××××××××××××××××××××
(一)在board文件夹下面建立自己的开发板的文件夹。一般的,要选取与自己的开发板硬件设置最为接近的型号。在u-boot-1.1.1中,已经支持at91rm9200,所以可以选取at91rm9200dk作为模板进行修改。设置你的开发板的名字,随意即可,我的设置为:myboard。
[root@dding u-boot-1.1.1]$ cd board
[root@dding board]$ cp -R at91rm9200dk/ myboard/
[root@dding board]$ cd myboard
[root@dding myboard]$ ls
at91rm9200dk.c config.mk flash.c Makefile u-boot.lds
(二)可以看到,这里共有5个文件。首先,要修改主文件的名字,即要把at91rm9200dk.c更改为myboard.c。其次,要更改config.mk中TEXT_BASE的数值,与loader等一级bootloader的要一致。接下来,因为在at91rm9200dk用的是AMD的flash,而我的开发板上用的是Intel的28F128J3A,那么需要另外找Intel的flash.C,以减少工作量。在strong ARM构架里有xm250,它的flash是Intel的,修改的东西并不是很多。需要注意的是,xm250的flash位宽是32,而我的位宽是16,要根据这个进行相应的修改。最后,修改Makefile,主要是修改生成文件的名字。具体操作如下:
[root@dding myboard]$ mv at91rm9200dk.c myboard.c
[root@dding myboard]$ cat config.mk
TEXT_BASE = 0x21f80000
[root@dding myboard]$ vi config.mk
修改成:TEXT_BASE = 0x21f00000,然后保存退出。
[root@dding myboard]$ vi Makefile
include $(TOPDIR)/config.mk
LIB = lib$(BOARD).a
OBJS := myboard.o flash.o
SOBJS :=
$(LIB): $(OBJS) $(SOBJS)
$(AR) crv $@ $(OBJS) $(SOBJS)
clean:
rm -f $(SOBJS) $(OBJS)
[root@dding myboard]$ rm flash.c
[root@dding myboard]$ cp ../xm250/flash.c ./
[root@dding myboard]$ ls
config.mk flash.c Makefile myboard.c u-boot.lds
[root@dding myboard]$ vi flash.c
34 #undef FLASH_PORT_WIDTH32 /*不定义位宽32*/
35 #define FLASH_PORT_WIDTH16 /*定义位宽16*/
216 switch (value) {
217
218 case (FPW) INTEL_ID_28F128J3A: /*就是这个芯片*/
219 info->flash_id += FLASH_28F128J3A;
220 info->sector_count = 128;
221 info->size = 0x01000000;
222 break; /* => 16 MB */
223
224 case (FPW) INTEL_ID_28F640J3A:
225 info->flash_id += FLASH_28F640J3A;
226 info->sector_count = 64;
227 info->size = 0x00800000;
228 break; /* => 8 MB */
[root@dding myboard]$ cd ../..
[root@dding u-boot-1.1.1]$ vi Makefile
#########################################################################
## AT91RM9200 Systems
#########################################################################
at91rm9200dk_config : unconfig
@./mkconfig $(@:_config=) arm at91rm9200 at91rm9200dk
myboard_config : unconfig
@./mkconfig $(@:_config=) arm at91rm9200 myboard
#########################################################################
在这里,可以在命令模式下输入“/at91rm9200”快速查找at91rm9200dk,仿照它的例子,写出自己板子的配置。注意的是,第二行开头要用TAB键,不是空格,否则报错。选项arm表示目标板架构,at91rm9200表示CPU中对应的目录,myboard是你自己的开发板名字。
(三)修改主要的配置文件。配置选项比较多,主要是配置cpu,波特率,flash和sdram的类型大小,环境变量的偏移量等等,容易出错。应该首先了解硬件情况,仔细对应芯片资料进行修改。见上面的《AT91RM9200开发板的存储器情况》
[root@dding u-boot-1.1.1]$ cd include/configs
[root@dding configs]$ cp at91rm9200dk.h myboard.h
[root@dding configs]$ vi myboard.h
#define CONFIG_ myboard 1 /* on an myboard Board */
#undef CONFIG_USE_IRQ /* we don't need IRQ/FIQ stuff */
#define CONFIG_CMDLINE_TAG 1 /* enable passing of ATAGs */
#define CONFIG_SETUP_MEMORY_TAGS 1
#define CONFIG_INITRD_TAG 1
#define CFG_MALLOC_LEN (CFG_ENV_SIZE + 128*1024)
#define CONFIG_BAUDRATE 115200
#define CONFIG_BOOTDELAY 3 // u-boot延时等待时间
/* #define CONFIG_ENV_OVERWRITE 1 */
#define CONFIG_COMMANDS /
((CONFIG_CMD_DFL | /
CFG_CMD_DHCP ) & /
~(CFG_CMD_BDI | /
CFG_CMD_IMI | /
CFG_CMD_AUTOSCRIPT | /
CFG_CMD_FPGA | /
CFG_CMD_MISC | /
CFG_CMD_LOADS ))
/* this must be included AFTER the definition of CONFIG_COMMANDS (if any) */
#include <cmd_confdefs.h>
#define CONFIG_NR_DRAM_BANKS 1 // sdram banks,我的是一个,通常都是一个
#define PHYS_SDRAM 0x20000000 // sdram起始地址,at91rm9200统一为0x20000000
#define PHYS_SDRAM_SIZE 0x2000000 /* 32 M */
// sdram容量32MB,需要根据实际情况修改,芯片为两片三星的16位×16M K4S281632F
#define CFG_MEMTEST_START PHYS_SDRAM
#define CFG_MEMTEST_END CFG_MEMTEST_START + PHYS_SDRAM_SIZE - 262144
#define CONFIG_DRIVER_ETHER
#define CONFIG_NET_RETRY_COUNT 20
#define CONFIG_HAS_DATAFLASH 1 // 用了SPI的dataflash
#define CFG_SPI_WRITE_TOUT CFG_HZ
#define CFG_MAX_DATAFLASH_BANKS 2
#define CFG_MAX_DATAFLASH_PAGES 16384
#define CFG_DATAFLASH_LOGIC_ADDR_CS0 0xC0000000 /* Logical adress for CS0 */
#define CFG_DATAFLASH_LOGIC_ADDR_CS3 0xD0000000 /* Logical adress for CS3 */
// flash为intel的16M 28F128J3A in 128 Sectors
#define PHYS_FLASH_1 0x10000000 //起始地址,at91rm9200统一为0x10000000
#define PHYS_FLASH_SIZE 0x200000 /* 2 megs main flash */
#define CFG_FLASH_BASE PHYS_FLASH_1 // PHYS_FLASH_1 flash起始地址别名
#define CFG_MAX_FLASH_BANKS 1 // flash最大banks数
#define CFG_MAX_FLASH_SECT 40 //扇区总数
#define CFG_FLASH_ERASE_TOUT (2*CFG_HZ) /* Timeout for Flash Erase */
#define CFG_FLASH_WRITE_TOUT (2*CFG_HZ) /* Timeout for Flash Write */
#define CFG_ENV_IS_IN_FLASH 1 // 环境变量保存在flash中
#define CFG_ENV_ADDR (PHYS_FLASH_1 + 0xd000) // 环境变量在flash中的地址
#define CFG_ENV_SIZE 0x2000 // 环境变量的大小
#define CFG_LOAD_ADDR 0x21000000 /* default load address */
// 内核印象默认的加载地址,需要与自启动的参数匹配下
// 关于U-boot的启动代码等大小和地址对任何CPU都无需改动,但是实际往flash中存储时需要按照此地址来进行
//boot.bin 0x1000 0000
//u-boot.gz 0x1001 0000
#define CFG_BOOT_SIZE 0x6000 /* 24 KBytes */ // boot.bin的大小
#define CFG_U_BOOT_BASE (PHYS_FLASH_1 + 0x10000) // u-boot.gz的存放位置
#define CFG_U_BOOT_SIZE 0x10000 /* 64 KBytes */ // u-boot.gz占据的flash空间
#define CFG_BAUDRATE_TABLE {115200 , 19200, 38400, 57600, 9600 }
#define CFG_PROMPT "Uboot> " /* Monitor Command Prompt */ // U-boot的提示符,可随意更改
#define CFG_CBSIZE 256 /* Console I/O Buffer Size */
#define CFG_MAXARGS 16 /* max number of command args */
#define CFG_PBSIZE (CFG_CBSIZE+sizeof(CFG_PROMPT)+16) /* Print Buffer Size */
四、编译u-boot
[root@dding configs]$ cd ../..
[root@dding u-boot-1.1.1]$ make myboard_config
Configuring for myboard board...
[root@dding u-boot-1.1.1]$ make CROSS_COMPILE=arm-linux-
生成三个文件:u-boot.bin, u-boot, u-boot.srec
u-boot.bin is a raw binary image
u-boot is an image in ELF binary format
u-boot.srec is in Motorola S-Record format (objcopy -O srec -R.note -R.comment -S [inputfile] [outfile]
u-boot ELF格式的文件,可以被大多数Debug程序识别;
u-boot.bin—二进制bin文件,纯粹的U-BOOT二进制执行代码,不保存ELF格式和调试信息。这个文件一般用于烧录到用户开发板中;
u-boot.srec— Motorola S-Record格式,可以通过串行口下载到开发板中。
然后把生成的u-boot.bin保存,并且压缩一下得到u-boot.bin.gz。
一种方式是通过JTAG口将u-boot.bin烧写到Flash的零地址,复位后就可以启动系统了。此时无需boot.bin。
但是对于at91rm9200,我们是通过boot.bin来过渡的,烧写的是u-boot.bin.gz
以上工作完成我们可以通过串口将u-boot.bin下载到主板的SDRAM中,它会自动执行, 并出现uboot>
这里我们可以通过串口把boot.bin, u-boot.bin.gz下载到主板,再用u-boot的提供的写flash功能分别把boot.bin, u-boot.bin.gz写入到flash中,完成以上工作后,对主板跳线选择片外启动,板子复位后会自动启动u-boot.
如何引导Linux内核启动?
命令引导未用mkimage生成的内核
4.1.1 非压缩内核Image
运行地址!=链接地址0x20008000,不能启动
Uboot> tftp 21000000 Image;tftp 21100000 ramdisk;go 21000000
。。。。
done
Bytes transferred = 6993691 (6ab71b hex)
## Starting application at 0x21000000 ...
Error: a 在哪提示的?
运行地址=链接地址0x20008000,不能启动,难道是ramdisk的问题
Uboot> tftp 20008000 Image;tftp 21100000 ramdisk;go 20008000
。。。。
done
Bytes transferred = 6993691 (6ab71b hex)
## Starting application at 0x21000000 ...
Error: a
4.1.2 压缩内核zImage
运行地址!=链接地址0x20008000,能启动,内核自解压成功,但是解压后的内核运行错误
Uboot> tftp 21000000 zImage;tftp 21100000 ramdisk;go 21000000
。。。。。。。。。。。
done
Bytes transferred = 6993691 (6ab71b hex)
## Starting application at 0x21000000 ...
Uncompressing Linux............................................................. done, booting the kernel.
€?~??鄜屈
运行地址==链接地址0x20008000,能启动,内核自解压成功,但是解压后的内核运行错误
Uboot> tftp 20008000 zImage;tftp 21100000 ramdisk; go 20008000
## Starting application at 0x20008000 ...
Uncompressing Linux............................................................. done, booting the kernel.
€?~??鄜屈
上面的ramdisk都是添加了uboot的头的,去掉头部再试试。去掉了还是不行,go方法的ramdisk的地址是怎么设置的??要详细看下uboot在ramdisk这块是如何跟内核交互的?
参数意义解析
通过mkimage这个tool可以给zImage添加一个header:
typedef struct image_header {
uint32_t ih_magic; /* Image Header Magic Number */
uint32_t ih_hcrc; /* Image Header CRC Checksum */
uint32_t ih_time; /* Image Creation Timestamp */
uint32_t ih_size; /* Image Data Size */
uint32_t ih_load; /* Data Load Address */
uint32_t ih_ep; /* Entry Point Address */
uint32_t ih_dcrc; /* Image Data CRC Checksum */
uint8_t ih_os; /* Operating System */
uint8_t ih_arch; /* CPU architecture */
uint8_t ih_type; /* Image Type */
uint8_t ih_comp; /* Compression Type */
uint8_t ih_name[IH_NMLEN]; /* Image Name */
} image_header_t;
此header是如何生成的?利用u-boot里面的mkimage工具来生成uImage (u-boot源码包/tools/mkimage.c )
这里解释一下参数的意义:
-A ==> set architecture to 'arch'
-O ==> set operating system to 'os'
-T ==> set image type to 'type' “kernel或是ramdisk”
-C ==> set compression type 'comp'
-a ==> set load address to 'addr' (hex)
-e ==> set entry point to 'ep' (hex)(内核启动时在此位置查询完整的内核印象)
-n ==> set image name to 'name'
-d ==> use image data from 'datafile'
-x ==> set XIP (execute in place,即不进行文件的拷贝,在当前位置执行)
对于ARM linux内核映象用法:
-A arm -------- 架构是arm
-O linux -------- 操作系统是linux
-T kernel -------- 类型是kernel
-C none/bzip/gzip -------- 压缩类型
-a 20008000 ---- image的载入地址(hex),通常为0xX00008000
-e 200080XX---- 内核的入口地址(hex),XX为0x40或者0x00
-n linux-XXX --- image的名字,任意
-d nameXXX ---- 无头信息的image文件名,你的源内核文件
uImageXXX ---- 加了头信息之后的image文件名,任意取
的流程分析
Bootm命令在/common/cmd_bootm.c中do_bootm函数
》》》》》》》》》》》获取当前内核的地址
if (argc < 2) {
addr = load_addr;
} else {
addr = simple_strtoul(argv[1], NULL, 16);
}
printf ("## Booting image at %08lx .../n", addr);
》》》》》》》》》》》》获得image头,没有mkimage的就返回了
memmove (&header, (char *)addr, sizeof(image_header_t));
》》》》》》》》》》》》打印头部信息
print_image_hdr ((image_header_t *)addr);
实例:
Image Name: dd-kernel-2.4.19
Image Type: ARM Linux Kernel Image (gzip compressed)
Data Size: 869574 Bytes = 849.2 kB
Load Address: 20008000
Entry Point: 20008000
》》》》》》》》》》》》校验image头部
printf (" Verifying Checksum ... "); printf ("OK/n");
》》》》》》》》》》》》检查image的类型
switch (hdr->ih_type)
case IH_TYPE_KERNEL:
name = "Kernel Image";
break;
case IH_TYPE_MULTI:
》》》》》》》》》》判断内核的压缩类型
switch (hdr->ih_comp) {
case IH_COMP_NONE: // 非压缩内核
if(ntohl(hdr->ih_load) == addr) {
// 当前运行的地址与-a指定的一致,则不搬动,-e必须必-a大0x40
printf (" XIP %s ... ", name);
} else {
//当前运行的地址与-a指定的不一致,则将内核搬到-a地址,此时-a与-e必相同
memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len);
。。。。
case IH_COMP_GZIP:
printf (" Uncompressing %s ... ", name);
if (gunzip ((void *)ntohl(hdr->ih_load), unc_len,
//压缩内核,将内核解压到-a 指定的地址了,要求-a与-e相同
(uchar *)data, (int *)&len) != 0) {
do_reset (cmdtp, flag, argc, argv);
}
break;
》》》》》》》》》》》》》》》》判断操作系统类型
switch (hdr->ih_os) {
default: /* handled by (original) Linux case */
case IH_OS_LINUX:
do_bootm_linux (cmdtp, flag, argc, argv, addr, len_ptr, verify); // addr没有用处
break;
》》》》》》》》》》》》》》启动Linux内核
do_bootm_linux (cmd_tbl_t *cmdtp, int flag,
int argc, char *argv[],
ulong addr,
ulong *len_ptr,
int verify)
》》》》》》》》》》》》获取命令行参数
if ((s = getenv("bootargs")) == NULL)
s = "";
strcpy (cmdline, s);
》》》》》》》》》》》》赋内核启动地址
kernel = (void (*)(bd_t *, ulong, ulong, ulong, ulong))hdr->ih_ep;
注意,对于压缩过的内核,会将内核解压到-a指定的地址,此时-a 与-e 地址必须相同
》》》》》》》》》》》判断bootm的命令参数中是否有initrd
if (argc >= 3) {
addr = simple_strtoul(argv[2], NULL, 16);
printf ("## Loading RAMDisk Image at %08lx .../n", addr);
若有initrd则赋值,否则为0
》》》》》》》》》》》》》》》启动Linux内核
/*
* Linux Kernel Parameters:
* r3: ptr to board info data
* r4: initrd_start or 0 if no initrd
* r5: initrd_end - unused if r4 is 0
* r6: Start of command line string
* r7: End of command line string
*/
//*kbd = *(gd->bd); 在上面赋值的
(*kernel) (kbd, initrd _start, initrd_end, cmd_start, cmd_end);
启动流程的总结:
对于非gzip压缩的内核,bootm命令会首先判断bootm xxxx 这个指定的地址xxxx是否与-a指定的加载地址相同。
(1)如果不同的话会从这个地址开始提取出这个64byte的头部,对其进行分析,然后把去掉头部的内核复制到-a指定的load地址中去运行之(此时-e选型必须同-a)
(2)如果相同的话那就让其原封不动的放在那,但-e指定的入口地址会推后64byte,以跳过这64byte的头部。
对于gzip压缩过的内核,因为u-boot要对其解压,因此运行地址是不能等于-a指定的地址的,且必须有一定的间隔,否则解压到-a的内核会覆盖当前运行的程序。此时要求-a等于-e指定的地址。
如何用mkimage生成uImage
1> mkimage 如何指定入口参数 ( -e 0xxxxxx)
2> mkimage 指定了入口参数后, 你用tftpboot 下载kernel到哪个地址?
3> -c 如何指定?
u-boot里面的解压和内核自解压的区别: u-boot 里面的解压实际上是bootm 实现的 , 把 mkimage -C bzip2或者gzip 生成的 uImage进行解压 ; 而kernel的自解压是对zImage进行解压,发生在bootm解压之后。
U-boot 对内核添加头部时,前面已经用gzip压缩过一次内核了,而不是指原有的内核印象是否是压缩内核。指uImage 本身被压缩了,即对原来的zImage/Image添加了U-boot的压缩方式,使得生成的uImage变小了。此时-c gzip
若没有对zImage/Image用gzip命令压缩过,则-c none。
综合上面分析,mkimage的影响因子为:
-e,内核的入口地址是否与-a相同
Tftpaddr,即将内核加载到RAM中运行的地址,决定是否搬运或解压内核
-c,内核是否经过gzip压缩过,决定了是搬运还是解压
另外内核本身为非压缩的Image或zImage也是一个影响因子。组合情况共2^4 =16种
命令引导mkimage生成的内核全程解析
4.5.1 非压缩的Image内核
(1)Mkimage 之前用gzip对Image进行压缩
<1> -a=-e = 0x20008000,tftpaddr= 0x21000000
解压到-a指定的地址,成功启动
Uboot> tftp 21000000 uImage-zip-8000;tftp 21100000 ramdisk;bootm 21000000
## Booting image at 21000000 ...
Image Name: dd-kernel-2.4.19-zip-8000
Image Type: ARM Linux Kernel Image (gzip compressed)
Data Size: 869629 Bytes = 849.2 kB
Load Address: 20008000
Entry Point: 20008000
Verifying Checksum ... OK
Uncompressing Kernel Image ... OK
Starting kernel ...
Linux version 2.4.19-rmk7 (root@dding) (gcc version 2.95.3 20010315 (release)) #42 四 10月 11 14:15:35 CST 2007
AT91RM9200DK login: root
<2> -a=-e = 0x20008000, tftpaddr= 0x20008000
解压失败,启动失败
Uboot> tftp 20008000 uImage-zip-zImage-8000;tftp 21100000 ramdisk;bootm 20008000
Uboot> tftp 20008000 uImage-zip-8000;tftp 21100000 ramdisk;bootm 20008000
## Booting image at 20008000 ...
Image Name: dd-kernel-2.4.19-zip-8000
Image Type: ARM Linux Kernel Image (gzip compressed)
Data Size: 869629 Bytes = 849.2 kB
Load Address: 20008000
Entry Point: 20008000
Verifying Checksum ... OK
Uncompressing Kernel Image ... Error: inflate() returned -3
GUNZIP ERROR - must RESET board to recover
由于当前运行地址tftpaddr与解压缩后的地址-a重合了,导致解压缩失败,因此二者必须相隔一定的距离
<3> -a=0x20008000,-e = 0x20008040 ,tftpaddr= 0x21000000
能够解压到-a地址,但-e指定的入口不对,启动失败
Uboot> tftp 21000000 uImage-zip-8040;tftp 21100000 ramdisk;bootm 21000000
TFTP from server 192.168.0.12; our IP address is 192.168.0.15
Filename 'uImage-zip-8040'.
Load address: 0x21000000
。。。。。。。。。
## Booting image at 21000000 ...
Image Name: dd-kernel-2.4.19-zip-8040
Image Type: ARM Linux Kernel Image (gzip compressed)
Data Size: 869629 Bytes = 849.2 kB
Load Address: 20008000
Entry Point: 20008040
Verifying Checksum ... OK
Uncompressing Kernel Image ... OK
Starting kernel ... 死了
<4> -a=-e = 0x20008000, tftpaddr= 0x20008000
解压失败,入口也不对,启动失败
(2)Mkimage 之前未对Image进行压缩
<1> -a=-e = 0x20008000 tftpaddr= 0x21000000
搬动到-a指定的地址,成功启动
Uboot> tftp 21000000 uImage-nzip-8000;tftp 21100000 ramdisk;bootm 21000000
## Booting image at 21000000 ...
Image Name: dd-kernel-2.4.19-nzip-8000
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 1873059 Bytes = 1.8 MB
Load Address: 20008000
Entry Point: 20008000
Verifying Checksum ... Bad Data CRC
为什么总是校验失败呢?当前的内核印象为1.8M,难道太大了,后面的ramdisk将其覆盖??
下面未拷贝ramdisk,校验成功,成功启动,无法安装跟文件系统,是因为无ramdisk。说明上面确实是覆盖了,因此要对于大的内核印象要合理设置tftpaddr的地址和ramdisk的地址
Addr(ramdisk)= 0x2110 0000
Addr(tftpaddr)= 0x2100 0000
Addr(ramdisk)-Addr(tftpaddr)= 0x10 0000 = 1M < 1.8M
Uboot> tftp 21000000 uImage-nzip-8000;bootm 21000000
## Booting image at 21000000 ...
Image Name: dd-kernel-2.4.19-nzip-8000
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 1873059 Bytes = 1.8 MB
Load Address: 20008000
Entry Point: 20008000
Verifying Checksum ... OK
OK
Starting kernel ...
Linux version 2.4.19-rmk7 (root@dding) (gcc version 2.95.3 20010315 (release)) #44 四 10月 11 17:27:24 CST 2007
。。。。。。。
Kernel panic: VFS: Unable to mount root fs on 01:00
Addr(ramdisk)- 2M = 0x20f0 0000 = Addr(tftpaddr)成功启动
Uboot> tftp 20f00000 uImage-nzip-8000;tftp 21100000 ramdisk;bootm 20f00000
## Booting image at 20f00000 ...
Image Name: dd-kernel-2.4.19-nzip-8000
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 1873059 Bytes = 1.8 MB
Load Address: 20008000
Entry Point: 20008000
Verifying Checksum ... OK
OK
Starting kernel ...
Linux version 2.4.19-rmk7 (root@dding) (gcc version 2.95.3 20010315 (release)) #44 四 10月 11 17:27:24 CST 2007
AT91RM9200DK login: root
<2> -a=-e = 0x20008000, tftpaddr= 0x20008000
不搬动,但-e地址不对,失败
<3> -a=0x20008000,-e = 0x20008040 ,tftpaddr= 0x20008000
不搬动,但未成功启动,入口地址对的啊?????
Uboot> tftp 20008000 uImage-nzip-8040;tftp 21100000 ramdisk;bootm 20008000
## Booting image at 20008000 ...
Image Name: dd-kernel-2.4.19-nzip-8040
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 1873059 Bytes = 1.8 MB
Load Address: 20008000
Entry Point: 20008040
Verifying Checksum ... OK
XIP Kernel Image ... OK
Starting kernel ...死了????
<4> -a=0x20008000,-e = 0x20008040 ,tftpaddr= 0x21000000
搬动,但-e地址不对,失败
4.5.2 压缩的zImage内核
(1)Mkimage 之前用gzip对zImage进行压缩,即-c gzip
<1> -a=-e = 0x20008000,tftpaddr= 0x21000000
解压到-a指定的地址,成功启动
Uboot> tftp 21000000 uImage-zip-zImage-8000;tftp 21100000 ramdisk;bootm 21000000
TFTP from server 192.168.0.12; our IP address is 192.168.0.15
Filename 'uImage-zip-zImage-8000'.
Load address: 0x21000000
## Booting image at 21000000 ...
Image Name: dd-zip-zImage-8000
Image Type: ARM Linux Kernel Image (gzip compressed)
Data Size: 876753 Bytes = 856.2 kB
Load Address: 20008000
Entry Point: 20008000
Verifying Checksum ... OK
Uncompressing Kernel Image ... OK // U-boot对内核解压
Starting kernel ...
Uncompressing Linux..............压缩内核zImage自解压......................... done, booting the kernel.
Linux version 2.4.19-rmk7 (root@dding) (gcc version 2.95.3 20010315 (release)) #43 四 10月
AT91RM9200DK login: root
[root@AT91RM9200DK /root]$ls
<2> -a=-e = 0x20008000, tftpaddr= 0x20008000
解压失败,启动失败
Uboot> tftp 20008000 uImage-zip-zImage-8000;tftp 21100000 ramdisk;bootm 20008000
TFTP from server 192.168.0.12; our IP address is 192.168.0.15
Filename 'uImage-zip-zImage-8000'.
Load address: 0x20008000
## Booting image at 20008000 ...
Image Name: dd-zip-zImage-8000
Image Type: ARM Linux Kernel Image (gzip compressed)
Data Size: 876753 Bytes = 856.2 kB
Load Address: 20008000
Entry Point: 20008000
Verifying Checksum ... OK
Uncompressing Kernel Image ... Error: inflate() returned -3
GUNZIP ERROR - must RESET board to recover
由于当前运行地址tftpaddr与解压缩后的地址-a重合了,导致解压缩失败,因此二者必须相隔一定的距离
<3> -a=0x20008000,-e = 0x20008040 ,tftpaddr= 0x21000000,失败
Uboot> tftp 21000000 uImage-zip-zImage-8040;tftp 21000000 ramdisk;bootm 21000000
TFTP from server 192.168.0.12; our IP address is 192.168.0.15
Filename 'uImage-zip-zImage-8040'.
Load address: 0x21000000
。。。。。。。。。
## Booting image at 21000000 ...
Bad Magic Number 难道对于压缩内核,幻数对的条件是-a==-e地址??即压缩内核默认-a==-e??
此法肯定失败,但问题出在这,还真不对啊,不试了,感兴趣的朋友可以玩下
(2)Mkimage 之前未对zImage进行压缩,即-c none
<1> -a=-e = 0x20008000 tftpaddr= 0x21000000
搬动到-a指定的地址,成功启动
Uboot> tftp 21000000 uImage-nzip-zImage-8000;tftp 21100000 ramdisk;bootm 21000000
## Booting image at 21000000 ...
Image Name: dd-nzip-zImage-8000
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 881748 Bytes = 861.1 kB
Load Address: 20008000
Entry Point: 20008000
Verifying Checksum ... OK
OK
Starting kernel ...
Uncompressing Linux............................................................. done, booting the kernel.
Linux version 2.4.19-rmk7 (root@dding) (gcc version 2.95.3 20010315 (release)) #43 四 10月 11 14:25:14 CST 2007
AT91RM9200DK login:
<2> -a=-e = 0x20008000, tftpaddr= 0x20008000
不搬动,但-e地址不对,失败
Uboot> tftp 20008000 uImage-nzip-zImage-8000;tftp 21100000 ramdisk;bootm 20008000
## Booting image at 20008000 ...
Image Name: dd-nzip-zImage-8000
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 881748 Bytes = 861.1 kB
Load Address: 20008000
Entry Point: 20008000
Verifying Checksum ... OK
XIP Kernel Image ... OK
Starting kernel ... 死了。。。
<3> -a=0x20008000,-e = 0x20008040 ,tftpaddr= 0x20008000
不搬动,成功启动
Uboot> tftp 20008000 uImage-nzip-zImage-8040;tftp 21100000 ramdisk;bootm 20008000
## Booting image at 20008000 ...
Image Name: dd-nzip-zImage-8040
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 881748 Bytes = 861.1 kB
Load Address: 20008000
Entry Point: 20008040
Verifying Checksum ... OK
XIP Kernel Image ... OK
Starting kernel ...
Uncompressing Linux............................................................. done, booting the kernel.
Linux version 2.4.19-rmk7 (root@dding) (gcc version 2.95.3 20010315 (release)) #43 四 10月 11 14:25:14 CST 2007
AT91RM9200DK login:
<4> -a=0x20008000,-e = 0x20008040 ,tftpaddr= 0x21000000
搬动,但-e地址不对,失败
4.5.3 关于压缩及非压缩内核bootm启动的全面总结
由上面的16个例子,我们可以看出,能够启动内核的由以下几种情况:
各种情况对应的统一ramdiskaddr= 0x21100000
<1>非压缩的Image内核:
-a=-e = 0x20008000 ,–c=none,tftpaddr= 0x20f00000
此法主要由于内核太大,导致tftpaddr做了一定的修正
-a= 0x20008000 ,-e = 0x20008040,–c=none,tftpaddr=0x20008000
此法理论上可行,但我未试验成功,有兴趣的朋友可以探究下
对于非压缩的Image内核,mkimage之前不压缩的话,内核印象较大,此法不常用
-a=-e = 0x20008000 ,–c=gzip,tftpaddr= 0x21000000
–c=gzip压缩内核必须解压,只有这种情况成功;其他解压覆盖或者-e入口不对
<2>压缩的zImage内核:
-a=-e = 0x20008000 ,–c=none,tftpaddr= 0x21000000
-a= 0x20008000 ,-e = 0x20008040,–c=none,tftpaddr=0x20008000
-a=-e = 0x20008000 ,–c=gzip,tftpaddr= 0x21000000
–c=gzip压缩内核必须解压,只有这种情况成功;其他解压覆盖或者-e入口不对
zImage已经压缩过一次了,一般无需再压缩,此法不常用
常见方法:
<1>非压缩的Image内核:
-a=-e = 0x20008000 ,–c=gzip,tftpaddr= 0x21000000
<2>压缩的zImage内核:
-a=-e = 0x20008000 ,–c=none,tftpaddr= 0x21000000
-a= 0x20008000 ,-e = 0x20008040,–c=none,tftpaddr=0x20008000
待续:
U-boot如何向Linux内核传递命令行参数?
Go引导内核的详细方法?
Ramdisk与initrd怎么传给内核?
U-Boot在S3C2410上的移植――东华理工学院 焦玉全 黄乡生 鲍玉军
基于Atmel at91rm9200的armlinux的bootloader启动代码分析――balancesli
基于ATMEL AT91RM9200的嵌入式Linux移植笔记――super
基于ARM的嵌入式Linux系统移植技术研究与应用5_1Bootloader移植――未知
u-boot 移植步骤详解――EmbedTheWorld
U-boot与kernel的关系――bobzhang
U-BOOT下使用bootm引导内核方法――luofuchong
http://www.linuxforum.net/forum/showflat.php?Cat=&Board=embedded&Number=651003&page=&view=&sb=&o=&fpart=all&vc=1
http://docs.blackfin.uclinux.org/doku.php?id=customizing_u-boot_for_your_own_board
http://blog.csdn.net/sailor_8318/archive/2007/10/11/1820904.aspx?P_AVPASS=PHDGBITAVPASSP