佛山市峰华卓立制造技术有限公司 广东佛山
摘要:本文介绍了U-BOOT在FREESCALE的嵌入式CPU芯片MPC5125上的移植方法。通过分析U-BOOT的使用方法、运行机理以及源码架构,给出了U-BOOT在MPC5125上的移植方法和步骤。应用结果表明,移植后的U-BOOT在目标板上稳定运行,能加载Linux内核和根文件系统。
关键词:U-BOOT;MPC5125;内核;移植
1引言
U-BOOT[1]是一种开源的BOOTLOADER,由德国DENX小组开发。BOOTLOADER是在操作系统内核运行之前运行的一段小程序。这段程序完成硬件的初始化和建立内存空间的映射图等重要工作,为内核的启动创建正确的环境,并最终启动内核[2]。因此,U-BOOT的主体功能包括三个方面,一是初始化硬件,为启动操作系统提供合适的硬件环境;二是解释执行用户的命令,这既包括在与用户的交互中解释执行的用户指令,也包括UBOOT自动启动时执行的命令;三是启动操作系统。
U-BOOT提供了多个命令给用户使用。通过这些命令可以查看目标系统的信息、设置环境变量等。通过在U-BOOT的命令行中输入help可以查看所有支持的命令。本文将这些命令分为六大类,分别是信息查询命令、内存控制命令、FLASH操作命令和运行控制命令、数据下载命令和环境变量设置命令。
本文基于FREESCALE的POWERPC核的嵌入式处理器MPC5125和U-BOOT源码资源,通过分析U-BOOT的源码架构以及启动操作系统的机理,得出在MPC5125上移植U-BOOT的方法,最后详细介绍了移植U-BOOT的操作步骤。
2 U-BOOT的源码分析
2.1 U-BOOT的命令
U-BOOT命令包括信息查询命令、内存控制命令、FLASH操作命令和运行控制命令、数据下载命令和环境变量设置命令[3]。
信息查询命令包括bdinfo、coninfo、flinfo、iminfo和help。bdinfo用于打印板子信息,包括内存地址、时钟频率、MAC地址等,这些板级信息最终将传递给内核。coninfo用于打印串口信息,包括设备名、标识符和当前使用情况。flinfo用于打印当前系统的flash信息,如flash容量、扇区数、每个扇区的起始地址。iminfo用于打印linux内核的头部信息,包括映像名、映像类型、大小和CRC校验和。help命令用于打印帮助信息,当不指定具体命令时,它将打印当前U-BOOT映像所支持的所有命令。
内存控制命令包括base、crc32、cmp、cp和md。base用于打印和设置内存中的地址偏移量。crc32用于计算一段内存空间的校验和,其第一个参数表示计算校验和的内存起始地址,第二个参数表示需要计算校验和的内存大小,第三个参数表示计算出来的校验和将存放到的内存地址。第三个参数可以省略。cmp命令可以比较两段内存的内容是否相同,其第一个参数表示第一段内存的起始地址,第二个参数表示第二段内存的起始地址,第三个参数可有可无,若有,则表示比较该参数指定的内存大小,若无,则表示在碰到第一个不相等处退出命令执行。cp命令是最常用的命令之一,既可用于进行烧录flash,也可进行内存中数据的拷贝。该命令有三种变体——cp.b、cp.w和cp.l,分别表示按字节拷贝、按字拷贝和按长字拷贝。其第一个参数表示源地址,第二个参数表示目的地址,第三个参数表示需要拷贝的内存大小。md命令用于以十六进制显示指定内存地址的内容。该命令也有三个变体——md.b、md.w和md.l,分别表示按字节、字和双字显示。
FLASH操作命令包括cp、flinfo、erase和protect命令。cp和flinfo命令在前文已经详细介绍,这里仅介绍erase和protect命令。
erase命令用于擦除flash中的内容,是最常用的命令之一,这是因为每次flash烧录前都必须对flash进行擦除。erase命令有四种形式:
1)erase start end:擦除由start和end指定的起始地址和末尾地址之间的一段flash空间
2)erase N:SF[-SL]:通过指定bank号和擦除块的范围进行flash擦除,擦除bank N中SF至SL之间的区域,一个bank通常指连接到CPU片选的一块存储区域
3)erase bank N:擦除整个bank N
4)erase all:擦除整块flash
protect命令用于进行flash写保护。为了防止flash的重要区域被不小心破坏,通常flash rom芯片都有写保护功能,在对起始块进行擦除和读写之前需要打开写保护。U-BOOT通常只对起始的第一块区域和第二块区域加上写保护,因为U-BOOT默认把U-BOOT自身放在了flash的第一块区域,而环境变量则放在第二块区域。在对flash进行擦除和烧录前,需要执行protect off命令将保护机制关闭,完成flash擦除和烧录后再将保护机制打开。protect有如下几种使用方式:protect on(or off)start end;protect on(or off)N:SF[-SL];protect on(or off)bank N;protect on(or off)all。protect on表示打开保护机制,而protect off则表示关闭保护机制,其参数与erase命令的参数具有相同的含义。
运行控制命令包括bootm和go。bootm用于加载并启动U-BOOT能识别的操作系统映像。这表明bootm只能启动由mkimage工具打包过的映像,不能启动直接的映像文件,因为bootm必须从系统映像的头部获取一些信息,如操作系统类型、映像是否压缩、映像的加载地址和压缩地址等。bootm可以带三个参数,第一个参数是内核映像的地址,既可以是RAM地址也可以是FLASH地址;第二个参数是initrd映像的地址,在使用ramdisk作为根文件系统时需要使用该参数;第三个参数是设备树文件所在的地址,专为针对powerpc硬件设计。
go命令是另外一种启动内核映像的命令,和bootm不同的是,它直接跳转至某个地址然后执行放在该地址处的命令。由于go命令既不会设置环境变量,也不能解析映像。因此,go命令更常用于加载standlone的应用程序,非常适合于加载各种硬件测试程序和bootstrap代码,因为这种应用程序不需要复杂的环境。go命令一般只带一个参数,即要跳转执行的地址信息。
数据下载命令包括tftpboot和bootp。tftpboot通过tftp协议下载映像,是最常用的数据下载命令。使用tftpboot命令之前,必须首先配置tftp服务器,即启动主机上的tftp服务,并将系统映像拷贝至tftp的共享目录。tftpboot命令的第一个参数为加载地址,其是内存的地址,不能使用flash地址;第二个参数是需要下载的系统映像名。
bootp命令将通过bootp协议下载映像。bootp是bootstrap protocol的简称,bootp协议将使用TCP/IP协议的UDP 67/68两个通讯端口,客户机从服务器得到IP地址、服务器的IP地址启动映像名、网关IP等信息。bootp命令的参数与tftpboot命令的参数一致。
环境变量设置命令包括printenv、setenv和saveenv。printenv用于打印环境变量,假如不带参数,那么将会打印所有的环境变量,如果后面带变量名,那么仅打印指定的环境变量。
setenv用于设置和更改环境变量。若其后仅有一个变量,那么表示将该环境变量删除;若带两个参数,那么表示将指定的环境变量(第一个参数)设置为第二个参数指定的值;若带多个参数,那么第三个参数及其后的参数用作对环境变量的说明。此外,假若要将环境变量设置为多条语句,那么必须将整个语句用双引号“括”起来。用setenv命令设置的环境变量不会被存储到flash中,故掉电后便会丢失。
为了保存设置和修改过的变量,必须使用saveenv命令将其保存起来。saveenv命令的作用便是将ram中的变量保存至flash中。该命令没有参数,故每次都是将所有的环境变量都保存一遍。
2.2 U-BOOT的目录结构分析
U-BOOT源代码的目录结构如图1所示。从图1可知,U-BOOT源代码主要包含如下几个部分:
1)U-BOOT提供的API接口,对应于api和api_examples。这两个目录提供的接口只有在需要设计特殊功能才有需要。
2)与目标板相关的代码,对应于board目录。打开该目录可见以各种开发板名字命名的目录,与TWR-MPC5125开发板对应的目录是ads5125。
3)公共代码,对应于common目录。该目录下放置了U-BOOT的一些核心代码,如MAIN函数,各种命令的实现代码,命令的解释执行代码,处理环境变量的相关文件,等。
4)与CPU相关的代码,对应于CPU目录。打开该目录,可见以各种CPU名字命名的文件夹,与MPC5125对应的目录是MPC512x目录。该目录下的代码用于进行CPU的初始化、中断和异常的处理、管脚初始化、串口的驱动等,其中还含有U-BOOT启动时首先要执行的一个代码文件——start.S。
5)磁盘分区和磁盘驱动对应的代码,对应于disk目录。在嵌入式系统中,这些代码较少使用,因为很少有嵌入式系统存在磁盘设备。
6)说明文档,对应于doc目录。
7)关键部件的驱动相关的代码,对应于drivers目录。打开该目录可见多种设备的驱动程序,如块设备、DMA、GPIO、I2C、SD卡、MTD设备、网络设备、PCI设备、实时时钟、串口、SPI设备、USB设备和音频设备。
8)简单应用的例程,对应于examples目录。
9)文件系统,对应于fs目录。打开该目录可见U-BOOT支持的多种文件系统,包括cramfs、ext2、fat、fdos、jffs2、reiserfs和yaffs2等。
10)头文件,对应于include目录。该目录下面包含了各种头文件,其中configs子目录下包含了各种开发板的U-BOOT配置文件,如TWR-MPC5125对应的配置文件是ads5125.h。由于U-BOOT没有像Linux那样的配置界面,因而U-BOOT的配置只能通过手动修改该目录下的配置文件来实现。
11)库文件,对应于以lib开头的那些目录名。其中lib_generic目录下的代码是与体系结构无关的库代码,其他目录下的代码是与特定的体系结构相关的代码,如lib_ppc目录下的代码是与powerpc体系相关的库代码。
12)网络传输代码,对应于net目录。打开该目录,可知U-BOOT实现了tftp、nfs和bootp等协议
13)上电自检代码,对应于post目录。
U-BOOT常用工具,对应于tools目录。该目录下最常用的工具是mkimage,该工具用于生成可被U-BOOT识别的操作系统映像,其具体工作便是在操作系统的映像头部填入64字节的文件头。
2.3 U-BOOT的MAKEFILE分析
Makefile是U-BOOT的编译管理文件,是分析U-BOOT源代码的起点。要分析清楚Makefile,必须要熟悉Makefile的语法规则以及sed流处理工具和shell的使用方法。U-BOOT项目的Makefile包括根目录下的Makefile、mkconfig、config.mk以及各个子目录下的Makefile。
根目录下的Makefile是整个U-BOOT工程主Makefile文件,其负责配置U-BOOT的编译方式,该文件的内容自上而下分别是定义编译环境、使用何种编译器、编译方式、目标文件的生成以及它们最终镜像文件中的链接次序等。因此,在使用make命令进行U-BOOT工程的编译之前必须运行make ads5125_nand_config(TWR-MPC5125开发板的nand flash启动方式)命令进行配置。打开Makefile文件,查找ads5125_nand_config,可见如下几行:
ads5125_nand_config\
:unconfig
@mkdir -p $(obj)include
@mkdir -p $(obj)board/ads5125
@mkdir -p $(obj)nand_spl/board/ads5125
echo "TEXT_BASE = 0x01000000" > $(obj)board/ads5125/config.tmp;
echo "#define CONFIG_NAND_U_BOOT" >> $(obj)include/config.h;
@$(MKCONFIG)-a ads5125 ppc mpc512x ads5125
echo "CONFIG_NAND_U_BOOT = y" >> $(obj)include/config.mk;
echo "NAND_PAD_SIZE = 2k" >> $(obj)include/config.mk;
由此可知,在运行make ads5125_nand_config进行配置时,首先执行unconfig目标,清理上次配置时生成的头文件以及Makefile的包含文件,主要是include/config.h文件和include/config.mk文件。接下来执行命令:
@$(MKCONFIG)-a ads5125 ppc mpc512x ads5125
在上述命令中,MKCONFIG即为顶层目录下的mkconfig脚本文件,后面跟的是传入的参数,分别表示的是宿主机平台、芯片的体系架构、芯片指令集版本和芯片厂商。mkconfig文件主要完成三件事:
1)在include文件夹下建立相应的文件夹软符号链接
2)生成Makefile包含文件include/config.mk,其中定义了ARCH、CPU、BOARD和VENDOR等变量,这些变量可供其他的Makefile文件使用,作为一个基本配置
3)生成include/config.h头文件
mkconfig脚本文件的执行至此结束,接下来分析config.mk文件。应该说,该文件才是真正的Makefile文件,上述两个脚本Makefile和mkconfig用于完成环境的配置,而该文件则定义具体的编译规则。该文件的主要功能是给编译过程中的变量赋值,包括编译执行的函数与编译时所带的参数等;还根据是否定义了ARCH、CPU、SOC、VENDOR、BOARD来决定是否包含相应位置的config.mk文件,该文件定义了相应平台在编译时应该加的参数,且各子模块目录的Makefile文件的第一句话就是包含顶层的config.mk。所以,若在移植过程中为自己的板子定义了新的名字,那么要检查这个文件,看相应位置上的config.mk文件有没有,内容是否为正确的。config.mk的主要内容如下:
1)包含体系结构、开发板、CPU特定的规则文件,第84行至107行
2)定义交叉编译工具,第61行至84行
3)定义AR选项ARFLAGS,调试选项DBGFLAGS,优化选项OPTFLAGS,预处理选项CPPFLAGS,C编译器选项CFLAGS,链接选项LDFLAGS,第107行至第227行
4)指定编译规则,第233至254行
在所有的配置完成之后,便可运行make命令生成ELF文件镜像。U-BOOT的顶层Makefile有多个目标,分别代表不同的含义。伪目标SUBDIRS用于执行tools、examples、post、post/CPU子目录下的Makefile文件;依赖目标$(OBJS),用于生成CPU/start.o;依赖目标$(LIBS),通过执行相应子目录下的Makefile生成子目录的库文件*.a;依赖目标$(LDSCRIPT),指代链接脚本,如board/ads5125/u-boot.lds,该文件定义了链接时各个目标文件是如何组织的。此外,在该链接文件中定义了整个U-BOOT工程的入口代码,即该文件的最后一句话:ENTRY(_start)。_start是在start.S中定义的全局变量,因此U-BOOT运行的第一句代码便是_start后的那句汇编代码。
概括起来,U-BOOT工程的编译就是通过执行make ads5125_nand_config传入ARCH、CPU、BOARD和SOC参数,mkconfig根据参数将include头文件与相应的头文件夹链接好,生成config.h头文件。然后执行make分别调用各子目录的Makefile生成所有的obj文件和obj库文件*.a。最后链接所有目标文件,生成elf镜像。不同格式的镜像都是调用相应工具由elf镜像直接或间接生成的。
2.4 U-BOOT启动流程分析
根据代码运行空间的区别,U-BOOT的启动过程分为两个阶段,第一阶段的代码在Flash中运行,第二阶段的代码在RAM中运行。第一阶段的主要代码体现在start.S文件,另外还有一些C语言编写的函数,如cpu_init_f和board_init_f。第一阶段代码的功能包括:
1)设置基地址寄存器IMMRBAR
2)CPU自身初始化:包括初始化机器状态、关闭看门狗、初始化HID寄存器
3)设置并使能CS0和CS2窗口寄存器
4)在Flash中分配堆栈空间
5)运行C语言代码编写的CPU第一阶段初始化,即cpu_init_f函数
6)运行C语言代码编写的BOARD第一阶段初始化,即board_init_f函数,该函数将运行初始化函数序列,最后跳转至relocate_code函数
7)运行relocate_code函数,该函数由汇编代码编写,该函数完成代码的搬移,将U-BOOT代码从Flash中搬移至RAM中,完成搬移后直接跳转至第二阶段的代码中去执行
第二阶段的代码是在RAM中运行的代码,这部分代码仅有很少一部分是start.S中编写的用汇编的代码,绝大部分代码用C代码编写,这是由于RAM中有足够的空间可用于分配堆栈,完成的功能总结如下:
1)调整got2指针
2)清除BSS段,并跳转至board_init_r函数
3)运行board_init_r函数,先后完成串口初始化、复位看门狗、使能ICACHE、初始化Flash、设备初始化、控制台初始化、中断初始化、以太网控制器初始化,最后,再次复位看门狗,进入主循环main_loop
4)在main_loop函数中,等待用户输入的指令或者读取默认的指令,处理该指令。若处理的是bootm指令,那么就会启动Linux内核
2.5 U-BOOT的命令实现
U-BOOT的命令为用户提供了交互的功能,其已经实现了几十个常用的命令。如果板件需要特殊的操作,还可以添加新的U-BOOT命令。因此,分析U-BOOT命令的实现显得十分重要。
U-BOOT的命令通过U_BOOT_CMD宏定义,该宏定义与include/command.h头文件中。通过这个宏的作用,每个U-BOOT命令都对应了一个cmd_tbl_t结构体,其定义如下:
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help)\
cmd_tbl_t __u_boot_cmd_##name Struct_Section = \
{#name,maxargs,rep,cmd,usage,help}
所有的U-BOOT命令对应的结构体都被放置在内存的一个特定区域中,形成一个特殊的段,该区域定义于U-BOOT的链接文件u-boot.lds中:
__u_boot_cmd_start =.;
.u_boot_cmd:{ *(.u_boot_cmd)}
__u_boot_cmd_end =.;
这样每一个U-BOOT命令都有一个结构体来描述,结构体中的成员包括命令名称、最大参数、重复数、命令执行函数、用法和帮助。从控制台输入的命令由common/command.c文件中的 run_command函数解释执行。其中,通过调用find_cmd函数负责匹配输入的命令,从列表中找出对应的命令结构体。在命令段中找到了对应的命令后,便会调用结构体中注册的cmd函数。cmd函数的注册便是通过U_BOOT_CMD宏实现的,在每个命令的实现文件中可以找到该宏。
通过上述分析可知,要实现一个命令,需要进行如下步骤:
1)定义该命令,在include/config_cmd_all.h中定义了定义了所有U_BOOT命令的标志位,添加新的命令时,也需要在此文件中定义其标志位。
2)实现该命令的操作函数,这取决于添加的命令需要实现的功能,需要注意的是,在该操作函数实现了之后,要通过U_BOOT_CMD宏注册该命令。
3)在common/Makefile文件中添加编译的目标文件。
4)打开CONFIG_COMMANDS选项的命令标志,这个标志在相应的板件的配置文件中,如对TWR-MPC5125而言,是include/configs/ads5125.h。
3 U-BOOT移植
3.1 修改主MAKEFILE
在U-BOOT工程根目录下的主Makefile中,要为新板件添加相应的配置项,参考开发板TWR-MPC5125的配置,可在其后添加如下配置:
jcx5125_config \
:unconfig
@mkdir -p $(obj)include
@$(MKCONFIG)-a jcx5125 ppc mpc512x jcx5125
jcx5125_nand_config \
:unconfig
@mkdir -p $(obj)include
@mkdir -p $(obj)board/jcx5125
@mkdir -p $(obj)nand_spl/board/jcx5125
echo "TEXT_BASE = 0x01000000" > $(obj)board/jcx5125/config.tmp;
echo "#define CONFIG_NAND_U_BOOT" >> $(obj)include/config.h;
@$(MKCONFIG)-a jcx5125 ppc mpc512x jcx5125
echo "CONFIG_NAND_U_BOOT = y" >> $(obj)include/config.mk;
echo "NAND_PAD_SIZE = 2k" >> $(obj)include/config.mk;
上述配置中,jcx5125_config是对Nor-Flash启动的配置项,而jcx5125_nand_config则是对Nand-Flash启动的配置项。
3.2 建立配置头文件
在U-BOOT工程中,没有类似于Linux内核那样的配置界面,所有的配置选项都集中在include/configs目录下的配置头文件中。本文的移植基于开发板TWR-MPC5125,因而其配置头文件也建立在开发板的配置头文件基础上。首先,在include/configs目录下新建一个空的头文件jcx5125.h。然后,将开发板的配置头文件ads5125.h的内容复制至jcx5125.h中。最后,根据需要,添加新的配置项,或者删除不需要的配置项,或者修改某些配置项的内容。例如,开发板的U-BOOT不支持SD卡,若要支持,那么需要添加如下配置项:
#define CONFIG_MMC
#define CONFIG_DOS_PARTITION
#define CONFIG_GENERIC_MMC
#define CONFIG_CMD_MMC
#define CONFIG_CMD_FAT
3.3 移植板级文件
板级文件,是指与板件密切相关的那部分代码,这部分代码主要存放在board目录下,且每块板件都有相应的子目录,开发板TWR-MPC5125对应的子目录是ads5125。板级文件的移植要进行如下五步(假设当前目录为U-BOOT工程的根目录):
a.进入board目录:cd board;
b.创建新板件对应的目录:mkdir jcx5125
c.将ads5125目录中的文件复制至jcx5125目录中:cp –r./ads5125/*./jcx5125
d.进入jcx5125目录,修改其中的C代码文件的名字:mv./ads5125.c./jcx5125.c
e.修改jxc5125.c文件的内容:该文件存放的是板级相关代码,主要是mpc5125芯片的引脚配置代码和DDR SDRAM的配置代码。因此,在移植该文件时,要根据实际板件的引脚使用情况,修改其IO引脚配置寄存器。
4 结束语
在嵌入式系统开发过程中,U-BOOT移植是一个必不可少的关键环节,在板件的调试过程中也起着非常重要的作用。本文详细分析了U-BOOT工程的命令使用和实现、源码结构和启动流程,并最终给出了在FREESCALE的POWERPC核的嵌入式处理器MPC5125上移植的操作步骤。目前,笔者移植的U-BOOT已经能够稳定运行于目标板上,并通过U-BOOT成功加载了LINUX内核和根文件系统,为后续的应用开发奠定了坚实基础。
参考文献:
[1].http://www.denx.de/wiki/view/DULG/UBoot
[2].朱继杭,杨世武.基于AT91RM9200U-Boot移植方法.北京:仪器与仪表用户,2005,12(6):121-122.
[3].http://www.cs.umass.edu/~tonyliu/index.html
论文作者:李国虎
论文发表刊物:《基层建设》2015年21期供稿
论文发表时间:2016/3/29
标签:命令论文; 文件论文; 代码论文; 参数论文; 目录论文; 映像论文; 地址论文; 《基层建设》2015年21期供稿论文;