我认为学习和应用u-boot对于刚接触的人来说最大的困难在于其功能比较全,文件(.S .c .h makefile文件)非常多,结构也比较复杂,难以理清各方面的关系。熟悉了一段时间之后,发现还是有很多的规律可以总结的。
一、首先要理解bootloader的作用,以及U-boot具体可以实现什么样的一些功能。
二、弄清U-boot的启动流程,这时候需要跟着整个程序走一遍,先不管实现这个流程的代码文件与文件结构。
三、熟悉整个U-boot的文件结构,重点是理解那些“移植到不同开发板上需要修改的”文件,以及它们之间的关系。重点这些文件是怎样与实际的硬件(开发板)对应和统一起来的。
四、进一步细化第二步和第三步,这是一个来回穿插的考查,考查在具体的实现整个程序流程和功能中,这些文件的结构与层次关系是怎样实施的。
五、动手针对一个开发板进行移植实验,可以参考别人的移植记录,但是通过自己的动手实验,会更加熟悉U-boot,并提高解决实际问题(排错)的能力。
U-boot下载地址: ftp://ftp.denx.de/pub/u-boot/
一
说到底,bootloader是一段裸机程序,是直接与硬件打交道的。① 它在系统上电后开始执行(PC系统中在bootloader之前有个BIOS固件,嵌入式系统中一般没有),其最终目的是“初始化硬件设备,准备好软件环境,最后调用操作系统内核”。② 为了方便进行底层开发(比如调试内核等),bootloader增加了很多功能,主要有UART、网络、USB、读写Flash(Nor or Nand)、解压缩、LCD支持等等。这样的bootloader具有比较强大的功能,除了不能进行多任务调度,已经类似一个小型操作系统了,被称为“Monitor”。U-boot就是这样一种bootloader,全功能体积高达数百Kbytes,如果不需要这些开发调试功能,发布产品时它能配置到很小的体积。
那我们也知道其实OS(如linux)的Kernel其实也是一段裸机程序,也是直接掌管硬件的,不过这个程序极其复杂,许许多多的计算机专家和黑客为其耗费心血,如果三四百K的U-boot已经比较复杂,那么一个典型功能的linux Kernel编译后竟然能高达万Kbytes,光从代码量来看,linux的复杂度已经是U-boot的百倍。而其实远不止。
虽然kernel很complex,但终究不过是一段裸机代码,Bootloader进行所谓的“kernel引导”,其过程不过是从bootloader里的一句跳转代码,跳转到kernel代码处(执行kernel中的第一个函数),所谓传递参数也不过是bootloader和kernel约定一个内存地点存放。在这个过程中,bootloader和kernel虽然都处于同一个内存里,但是它们除了“引导”与“传递有限的参数”这样的关系,并无其它关系,完全是两个独立的程序。之所以在kernel之前用一个bootloader来引导(为什么开机不能直接执行kernel,所有事情都交给kernel做?),其思想类似于一个板级支持包:kernel假定执行的时候已经具备了一个基本的硬件运行条件,这个环境的初始化(最底层的一些硬件初始化、硬件信息设定)需要bootloader来完成,也许这样kernel的设计才能保持一定的一致性。
U-boot为了能支持很多不同的体系结构、SoC、电路板,设计了一种代码文件的组织结构(包括makefile文件),这种设计很大程度上借鉴了linux kernel,很多硬件驱动代码也直接从linux kernel中搬迁过去,因为linux kernel也是一种移植性非常好的程序。“在通用代码与系统特定代码之间,不惜代价,保持一条明确的分界线”,这是可移植程序设计的不变箴言。
二
U-boot的启动流程分析,上一篇转载的博文叙述的很全面,也比较有条理。
---->《U-boot研究与移植心得》 http://www.cnblogs.com/andrew-wang/archive/2012/12/06/2806238.html
总的主线就是: 初始化硬件--->加载内核到内存--->设置好各项参数--->跳转到内核。其他命令都是开发调试时使用的。
三
U-boot的代码文件组织架构是比较重要的一项,首先是要知道U-boot的代码结构经历过什么样的“历史变迁”。博主的这篇《U-boot版本与移植概况》有些这方面的知识(http://www.cnblogs.com/andrew-wang/archive/2012/11/04/2753971.html)。无论怎样变化,代码的组织架构的目标都是要使U-boot的结构更清晰、移植性更好,而其实每次的变化都是微小的进步。以版本2010.09的U-boot为例:
├── api 存放uboot提供的API函数 ├── arch 与体系结构相关的代码,uboot的重头戏 +++ ├── board 根据不同开发板定制的代码,代码也不少 +++ ├── common 通用的代码,涵盖各个方面,已命令行处理为主 ├── disk 磁盘分区相关代码 ├── doc 文档,一堆README开头的文件 ├── drivers 驱动,很丰富,每种类型的设备驱动占用一个子目录 +++ ├── examples 示例程序 ├── fs 文件系统,支持嵌入式开发板常见的文件系统 ├── include 头文件,已通用的头文件为主 +++ ├── lib 通用库文件 ├── nand_spl NAND存储器相关代码 ├── net 网络相关代码,小型的协议栈 ├── onenand_ipl ├── post 加电自检程序 └── tools 辅助程序,用于编译和检查uboot目标文件
可以将这些代码分为4类
1、平台相关(arch)、或开发板相关(board)(系统特定代码) 2、通用的函数(include common) 3、通用驱动(disk drivers fs nand_spl onenand_ipl net post) 4、工具、实例、文档(api doc examples tools)
除了引导kernel的主线之外,U-boot的支持的各种硬件操作驱动实现,有一定的层次性:为了实现“通用的函数”,首先要调用“通用驱动”,这些通用驱动如果有引用“平台相关”的宏或者外部函数,就需要调用到这些系统特定代码。其层次是" 2 <--> 3 <--> 1 " 。
一般来说,移植U-boot主要就是修改“1”项中的代码,以及在include/configs/XXX.h中打开需要的功能以及定义一些常量,具体的实现就是在arch和board子目录,来看一下这两个子目录的组织:
|--arch\ |--config.mk ===>配置脚本 |--arm\ |--cpu\ 子目录对应一种处理器的不同产品型号或者系列; |--arm920t\ |--a320\ |... |--s3c24x0\ |--interrupts.c |--speed.c |--timer.c |--usb.c |--usb_ohci.c |--usb_ohci.h |--Makefile |--cpu.c |--interrupts.c |--start.S <==整个bootloader入口点 |--u-boot.lds <==链接脚本 |--Makefile |--include\ 子目录是处理器用到的头文件; |--asm\ |--arch-a320\ |... |--arch_s3c24x0\ |--memory.h |--s3c2400.h |--s3c2410.h |--s3c24x0_cpu.h |--s3c24x0.h |... |--proc-armv\(公用) |--atomic.h |--bitops.h |--byteorder.h |--cache.h |--... (等共24个.h公用头文件) |--lib\ 目录对应用到处理器公用的代码; |--board.c |--config.mk |--... (等共16个文件) |--board\ |--ppmc7xx\ |... |--samsung\ |--goni\ |--smdk2400\ |--smdk2410\ |--smdk2410.c |--flash.c |--lowlevel_init.S |--config.mk |--Makefile |--nand_read.c |--nand_read_save.c |...
我们知道ARM是ARM公司设计的一种体系结构,这个公司专门设计IP core出售给半导体厂商加上一些外围部件生产SoC,这些IP Core有很多代的产品,如v4、v5、v6、v7,还根据有没有thumb指令集(t)、DSP加强(e)、java硬件加速(j)等进一步细分。而每一代IP core都有不少半导体厂家生产SoC,如qualcomm、Samsung、TI等,因此ARM平台的组织最为复杂。
打开arch,其中所有的子目录代表一种体系结构11种;选取其中的ARM类,里面包含cpu、include、lib三个子目录,其中lib是这个体系结构的公用代码;include下面只有一个子目录asm,其中包含的是不同的SoC对应的头文件及一些公用头文件;cpu中是ARM体系结构中不同代际的IP core,每种IP core中都有一些子目录代表不同厂商的SoC以及公用代码。(其他体系结构的platform没这么复杂)。
board则是基于某种SoC设计的电路板,2010.09的版本支持有近290种board。一般选择相似的board进行U-boot的移植。当然board里面的代码都是跟特定的board有关的。因此,要分arch、IP-core、SoC、Board 4个层次来理解U-boot在ARM系统上的移植性,而其他体系结构有可能只有2-3层。以基于S3C2440开发板为例,这种层次关系像下图:
要完全理解U-boot的代码,需要熟悉平台硬件及其汇编、熟悉C语言、熟悉编译原理。由于U-boot的编译是基于linux和Gcc的,因此还需要对linux的技术文化有一定的理解,比如其makefile体系架构,前文的博主推荐看《从庖丁解牛说uboot如何编译》《详细分析make uboot 最后的编译链接的具体执行过程》这两篇(看官去Google~)。博主的《U-boot架构分析与移植重点》其中对编译链接过程有个主线的介绍(主要参考了韦东山的书)---> http://www.cnblogs.com/andrew-wang/archive/2012/11/04/2753972.html 。U-boot文件虽多,结构虽复杂,但搞清楚了主线,就比较好理解了。
四、
具体的移植过程,从下载原版U-boot、linux kernel、busybox、FSmaker、gcc,到最后用它们组件一个完整的嵌入式系统,参考下面的博文,它们主要转载自yanghao和赵春江两位的博客(都标明了source)(另外也推荐mobilefzb的博客,其中关于TQ2440的移植博文也很好 http://nervfzb.blog.163.com/ ),我也都在FL2440开发板上实验过,原文可能有问题的地方做了补充注释,有些地方稍作修改以便理解,有些是原创的:
FL2440开发板的U-boot-2010.09版本移植(一)建立特定开发板的U-boot框架
FL2440开发板的U-boot-2010.09版本移植(二)片上系统SoC初始化移植
FL2440开发板的U-boot-2010.09版本移植(三) 如何利用JLINK烧写U-boot到NAND Flash中
FL2440开发板的U-boot-2010.09版本移植(四)Nor Flash启动支持
FL2440开发板的U-boot-2010.09版本移植(五)支持DM9000网卡和板级相关LED等配置
FL2440开发板的U-boot-2010.09版本移植(六)uboot架构中NAND Flash驱动修改
FL2440开发板的U-boot-2010.09版本移植(七)NAND Flash启动支持
FL2440开发板的U-boot-2010.09版本移植(八)LCD的支持
FL2440开发板的U-boot-2010.09版本移植 ____ U-boot中常用参数设定及常用宏(u-boot环境变量、USB、内核引导)
FL2440开发板的U-boot-2010.09版本移植(九)NAND Flash启动支持的一种新型方法,利用U-Boot自带nand_spl/nand_boot.c
FL2440开发板的U-boot-2010.09版本移植(十)支持烧写YAFFS2文件系统
FL2440开发板的U-boot-2010.09版本移植(十一)U-boot引导内核设置、编译linux内核、编译文件系统、加载...
以下原创:
J-link & AXD仿真调试ARM开发板 “halt不住” 的问题的解决
最后,希望这篇博文能帮助到U-boot的初学者。^_^