2008年12月5日星期五

转贴: U-boot在AT91RM9200上的全线移植分析

U-bootAT91RM9200上的全线移植分析

Sailor_forever  sailing_9806@163.com  转载请注明

 http://blog.csdn.net/sailor_8318/archive/2007/10/11/1820904.aspx?P_AVPASS=PHDGBITAVPASSP

**********************************************************************

摘要:本文介绍了bootloader的基础知识,分析了U-boot的源代码结构、常见命令及其启动流程,对于AT91RM9200的片内片外启动方式进行了详尽分析,分析启动所需的引导文件的详细流程。针对具体开发板的内存分配情况,介绍了U-bootAT91RM9200上移植的详细过程。分析了使用gobootm启动linux内核的方法,对于bootm方式的内核是否压缩、-a、-e、运行地址等16种组合情况,给出了详细的测试过程,提出了6种可用方法种的三种最优解。

关键字:U-bootAT91RM9200bootmmkimage;-a;-e;-c

 

后记:

"纸上得来终觉浅",以前由于课题任务较紧张,只是对U-boot的移植进行了一些了解,并没有仔细分析其详细的移植过程,尤其是对于一些地址设置方面的问题不太清楚。

 

上次给小布丁的参考资料有点零散,她也没看太明白,让我下定决心把Uboot的移植过程详细整理下。很多东西心里明白,但要写出来,确要花费更多的时间,但同时也会更注重于细节,对所做的项目也是一个总结完善的过程。求职在即,总结整理此文,也算是为求职做了些准备吧。

 

本文参考了众多网友的帖子,在此表示感谢!还有一些不太明白的地方,可能还有些错误,欢迎大家讨论指正。

 

谨以此文献给小布丁,希望她永远开心,工作学习顺利!

 

Sailor_forever

2007-10-10

 **********************************************************************

 

目录

 

bootloader介绍1

1.1 Bootloader移植的必要性1

1.2 BootLoader所支持的CPU和嵌入式系统板1

1.3. Boot Loader的安装媒介2

1.4 Boot Loader的操作模式(Operation Mode)2

15. Bootloader与主机之间进行文件传输所用的通信设备及协议3

 U-boot基础3

2.1 U-boot源代码目录结构3

2.2 U-Boot支持的主要功能5

2.3 U-boot命令介绍及环境变量5

2.4 U-Boot的启动流程分析6

 U-BOOTAT91RM9200上的移植11

3.1 at91rm9200的启动方式11

3.1.1 片内引导11

3.1.2 片外引导13

3.2 loader.bin, boot.bin, u-boot.bin代码执行流分析.13

3.2.1 loader.bin 13

3.2.2 boot.bin执行流程15

3.2.1 uboot.bin执行流程15

3.3 AT91RM9200开发板的存储器情况16

3.3.1 内部存储器映射17

3.3.2 外部存储器映射18

3.4 U-BOOTat91rm9200上移植修改的文件19

3.5 移植的具体步骤20

U-boot如何引导Linux内核启动?25

4.1 GO命令引导未用mkimage生成的内核25

4.1.1 非压缩内核Image25

4.1.2 压缩内核zImage25

4.2 Mkimage参数意义解析26

4.3 Bootm的流程分析27

4.4 如何用mkimage生成uImage29

4.5 Bootm命令引导mkimage生成的内核全程解析30

4.5.1 非压缩的Image内核30

4.5.2 压缩的zImage内核33

4.5.3 关于压缩及非压缩内核bootm启动的全面总结36

参考鸣谢:36

bootloader介绍

1.1 Bootloader移植的必要性

Bootloader是与系统硬件高度相关的初始化软件,它担负着初始化硬件和引导操作系统的双重责任。一些ARM平台可以共用同一种Bootloader,但是总的说来,每一个特定系统的Bootloader都会有所不同。Bootloader广泛用于有操作系统的手持终端设备,智能家电,机顶盒等嵌入式设备上,它负责完成硬件初始化,操作系统引导,系统配制等。Bootloader移植是在特定硬件平台上操作系统移植至关重要的一步。

 

1.2 BootLoader所支持的CPU和嵌入式系统板

每种不同的CPU体系结构都有不同的BootLoader。有些BootLoader也支持多种体系结构的CPU,比如U-BOOT就同时支持ARM, MIPS, POWERPC等体系结构。除了依赖于CPU的体系结构外,BootLoader实际上也依赖于具体的嵌入式板级设备的配置。也就是说,对于两块不同的嵌入式板而言,即使它们是基于同一种CPU而构建的,要想让运行在一块板子上的BootLoader程序也能运行在另一块板子上,通常也都需要修改BootLoader的源程序。

 

1.3. Boot Loader的安装媒介

系统加电或复位后,所有的CPU通常都从某个由CPU制造商预先安排的地址上取指令。比如,at91rm9200CPU在复位时通常都从地址0x00000000取它的第一条指令。而基于CPU构建的嵌入式系统通常都有某种类型的固态存储设备(EEPROMFLASH等,at91rm92000x10000000)被映射到这个预先安排的地址上。因此在系统加电后,CPU将首先执行Boot Loader程序。

上图是一个同时装有Boot Loader、内核的启动参数、内核映像和根文件系统映像的固态存储设备的典型空间分配结构图。

 

1.4 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继续启动,进行正常的启动加载。

 

15. Bootloader与主机之间进行文件传输所用的通信设备及协议

最常见的情况就是,目标机上的BootLoader通过串口与主机之间进行文件传输,传输协议通常是kermit / xmodem / ymodem协议中的一种。但是,串口传输的速度是有限的,因此如果该Bootloader对目标板的网卡支持良好,还可以通过以太网连接并借助TFTP (Trivial File Transfer Protocol)协议来下载文件是个更好的选择。此时,主机方所用的软件也要考虑,比如,在通过以太网连接和TFTP协议来下载文件时,主机方必须有一个软件用来提供TFTP服务,如Windows平台上的tftpd.exe等或Linux下面的tftp服务器。

 

1.6 Bootloader的通用执行流程

从操作系统的角度看,Bootloader的总目标就是正确地调用内核来执行。另外,由于Bootloader的实现依赖于 CPU的体系结构,因此大多数Bootloader都分为stagelstage2两大部分。依赖于CPU体系结构的代码,比如设备初始化代码等,通常都放在stagel中,而且通常都用汇编语言来实现,以达到短小精悍和高效的目的。而stage2则通常用C语言来实现,这样可以实现更复杂的功能,而且代码会具有更好的可读性和可移植性。

    Bootloaderstagel通常包括以下步骤(以执行的先后顺序):

l         硬件设备初始化(配置SDRAM存储控制器及IO),中断初始化

l         为加载Bootloaderstage2准备RAM空间(测试内存空间是否有效)

l         拷贝Bootloaderstage2RAM空间中

l         设置好堆栈

l         跳转到stage2C入口点

    Bootloaderstage2通常包括以下步骤(以执行的先后顺序):

l         初始化本阶段要使用到的硬件设备

l         检测系统内存映射(memory map)

l         没有用户干预时将kernel映像和根文件系统映像从flash读到RAM空间中

l         为内核设置启动参数

l         调用内核

 

  U-boot基础

现在为Linux开放源代码Bootloader有很多,blob, redboot, U-BOOT等,其中U-BOOT是目前用来开发嵌入式系统引导代码使用最为广泛的Bootloader。它支持POWERPC, ARM,MIPS, X86等处理器,支持嵌入式操作系统有Linux, Vxworks, NetBSD等。

 

2.1 U-boot源代码目录结构

|-- 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:设备分区处理代码。

docu-boot相关文档。


drivers
u-boot所支持的设备驱动代码, 网卡、支持CFIFlash、串口和USB总线等。

fs: u-boot所支持支持文件系统访问存取代码, jffs2.

 

includeu-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.

 

2.2 U-Boot支持的主要功能

主要功能如下:
系统引导 支持NFS挂载、RAMDISK(压缩或非压缩)形式的根文件系统
支持NFS挂载、从FLASH中引导压缩或非压缩系统内核;
基本辅助功能 强大的操作系统接口功能;可灵活设置、传递多个关键参数给操作系统,适合系统在不同开发阶段的调试要求与产品发布,尤对Linux支持最为强劲;
支持目标板环境参数多种存储方式,如FLASHNVRAMEEPROM
CRC32
校验,可校验FLASH中内核、RAMDISK镜像文件是否完好;
设备驱动 串口、SDRAMFLASH、以太网、LCDNVRAMEEPROM、键盘、USBPCMCIAPCIRTC等驱动支持;
上电自检功能 SDRAMFLASH大小自动检测;SDRAM故障检测;CPU型号;
特殊功能 XIP内核引导;

 

2.3 U-boot命令介绍及环境变量

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, serverIP=环境变量中设置的serverip)中/tftpboot/ 下的vmlinux通过TFTP读入到物理内存32000000处。

11.        kgo: 起动没有压缩的linux内核,kgo 32000000AT91RM9200不支持)

12.        bootm:起动UBOOT  TOOLS制作的压缩LINUX内核, bootm 3200000

13.        protect: FLASH进行写保护或取消写保护, protect on 1:0-3(就是对第一块FLASH0-3扇区进行保护)protect off 1:0-3取消写保护

14.        erase: 删除FLASH的扇区, erase 1:0-2(就是对每一块FLASH0-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 地址

 

2.4 U-Boot的启动流程分析

    和大多数的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; // 程序存储域和运行域的首地址,链接脚本决定了各个段在印象中的位置,默认情况下VMALMA

 

       . = 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-BOOTStage2拷贝到RAM中。//如何确定stage2的起始位置?

    5.设置好堆栈。

6.跳转到Stage2的入口,从而转到RAM中执行,该工作是调用指令ldr pc, start_armboot来完成的。

××××××××××××××××××××××××××

1.1.2开始,u-boot有初始化SDRAM并拷贝自己到SDRAM运行的代码,而之前的版本就没有这个功能(详细查看下代码??的确如此,因此对于以前的版本TEXT_BASE没有起作用?实际测试下??)。board/at91rm9200dkconfig.mk文件(TEXT_BASE = 0x21f0000032M 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.binflash中首先运行的是boot.bin,其将压缩的u-boot.bin.gz解压拷贝到TEXT_BASE处运行,此时已经在RAM中了,因此也无需实现自拷贝了。

 

对于U-Boot 1.1.2以后的版本,若像AT91RM9200boot.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,

没有评论: