1、第5章 Bootloader开发基础 第第5章章 Bootloader开发基础开发基础 5.1 Bootloader基础5.2 U-Boot5.3 ViVi第5章 Bootloader开发基础 5.1.1 Bootloader的启动的启动Linux系统是通过Bootloader引导启动的,上电后执行Bootloader来初始化系统。系统加电或复位后,所有CPU都会从某个地址开始执行,这是由处理器设计决定的。比如,X86的复位向量在高地址端,ARM处理器在复位时从地址0 x00000000读取第一条指令。嵌入式系统的开发板都要把板上ROM或Flash映射到这个地址。因此,必须把Bootloade
2、r程序存储在相应的Flash位置上,系统加电后,CPU将首先执行它。5.1 Bootloader基础基础第5章 Bootloader开发基础 主机和目标机之间一般有串口可以连接,Bootloader软件通常会通过串口来输入/输出。例如:输出出错或者执行结果信息到串口终端,从串口终端读取用户控制命令等。Bootloader的启动过程通常是多阶段的,这样既能提供复杂的功能,又有很好的可移植性。例如:从Flash启动的Bootloader多数是两阶段的启动过程,本章后面U-Boot内容可以详细分析该特性。第5章 Bootloader开发基础 大多数Bootloader包含两种不同的操作模式:本地加载
3、模式和远程下载模式。这两种操作模式的区别仅对于开发人员才有意义,也就是不同启动方式的使用。从最终用户的角度看,Bootloader的作用就是用来加载操作系统,而并不存在所谓的本地加载模式与远程下载模式的区别。因为Bootloader的主要功能是引导操作系统启动,所以详细讨论一下各种启动方式的特点。第5章 Bootloader开发基础 1网络启动方式网络启动方式这种方式开发板不需要配置较大的存储介质,跟无盘工作站有点类似。但是使用这种启动方式之前,需要把Bootloader安装到板上的EPROM或者Flash中。Bootloader通过以太网接口远程下载Linux内核映像或者文件系统。第3章介绍
4、的交叉开发环境就是以网络启动方式建立的。网络启动方式对于嵌入式系统开发来说非常重要。第5章 Bootloader开发基础 使用这种方式也有前提,就是目标板有串口、以太网接口或者其他连接方式。串口一般可以作为控制台,同时可以用来下载内核影像和文件系统。串口通信传输速率过低,不适合用来挂接NFS文件系统。所以以太网接口成为通用的互连设备,一般的开发板都可以配置10M以太网接口。对于PDA等手持设备来说,以太网的RJ-45接口结构尺寸大,而USB接口,特别是USB的迷你接口,结构尺寸小。对于开发的嵌入式系统,可以把USB接口虚拟成以太网接口通信。这种方式在开发主机和开发板两端都需要驱动程序。第5章
5、Bootloader开发基础 另外,可在服务器上配置启动相关网络服务。Bootloader下载文件一般都使用TFTP网络协议,还可以通过DHCP的方式动态配置IP地址。DHCP/BOOTP服务为Bootloader分配IP地址并配置网络参数后,才能够支持网络传输功能。如果Bootloader可以直接设置网络参数,则可不使用DHCP。TFTP服务为Bootloader客户端提供文件下载功能,把内核映像和其他文件放在/tftpboot目录下。这样Bootloader可以通过简单的TFTP协议远程下载内核映像到内存,如图5-1所示。第5章 Bootloader开发基础 图5-1 网络启动示意图第5章
6、 Bootloader开发基础 大部分引导程序都能够支持网络启动方式。例如:BIOS的PXE(Preboot eXecution Environment)功能就是网络启动方式;U-Boot也支持网络启动功能。第5章 Bootloader开发基础 2磁盘启动方式磁盘启动方式传统的Linux系统运行在台式机或者服务器上,这些计算机一般都使用BIOS引导,并且使用磁盘作为存储介质。进入BIOS设置菜单,可以探测处理器、内存、硬盘等设备,也可以设置BIOS从软盘、光盘或者某块硬盘启动。也就是说,BIOS并不直接引导操作系统。那么在硬盘的主引导区,还需要一个Bootloader,这个Bootloader
7、可以从磁盘文件系统中把操作系统引导起来。Linux传统上是通过LILO(LInux LOader)引导的,后来出现GNU软件GRUB(GRand Unified Bootloader)。这两种Bootloader广泛应用在X86的Linux系统上。第5章 Bootloader开发基础 LILO软件工程是由Werner Almesberger创建,专门为引导Linux开发的。现在LILO的维护者是John Coffman,最新版本下载站点:http:/lilo.go.dyndns.org。LILO有详细的文档,例如LILO套件中附带的使用手册和参考手册。此外,还可以在LDP的“LILO mini
8、-HOWTO”中找到LILO的使用指南。第5章 Bootloader开发基础 GRUB是GNU计划的主要Bootloader。GRUB最初是由Erich Boleyn为GNU Mach操作系统撰写的引导程序,后来由Gordon Matzigkeit和Okuji Yoshinori接替Erich的工作,继续维护和开发GRUB。GRUB的网站http:/www.gnu.org/software/grub/上有对套件使用的说明文件,(GRUB manual)。GRUB能够使用TFTP和BOOTP或者DHCP通过网络启动,这种功能对于系统开发过程很有用。除了传统的Linux系统上的引导程序以外,还有其
9、他一些引导程序,也支持磁盘引导启动。例如:LoadLin可以从DOS下启动Linux;还有ROLO、LinuxBIOS,U-Boot也支持这些功能。第5章 Bootloader开发基础 3Flash启动方式启动方式大多数嵌入式系统上都使用Flash存储介质。Flash有很多类型,包括NOR Flash、NAND Flash和其他半导体盘。其中,NOR Flash(也就是线性Flash)使用最为普遍。NOR Flash可以支持随机访问,所以代码可以直接在Flash上执行。Bootloader一般是存储在Flash芯片上的。另外,Linux内核映像和RAMdisk也可以存储在Flash上。通常需要
10、把Flash分区使用,每个区的大小应该是Flash擦除块大小的整数倍。图5-2是Flash存储示意图。第5章 Bootloader开发基础 图5-2 Flash存储示意图第5章 Bootloader开发基础 Bootloader一般存贮在Flash的底端或者顶端,这要根据处理器的复位向量来设置。要使Bootloader的入口位于处理器上电执行第一条指令的位置。接着需要分配参数区,可以作为Bootloader的参数保存区域。然后是内核映像区。Bootloader引导Linux内核,就是要从此处把内核映像解压到RAM中去,然后跳转到内核映像入口执行。最后是文件系统区。如果使用RAMdisk文件系统
11、,则需要Bootloader解压文件系统到RAM中。如果使用JFFS2文件系统,将直接挂接为根文件系统。这两种文件系统将在第7章详细讲解。最后还可以分出一些数据区,这要根据实际需要和Flash大小来考虑。第5章 Bootloader开发基础 这些分区是开发者定义的,Bootloader一般直接读/写对应的偏移地址。在Linux内核空间,可以配置成MTD设备来访问Flash分区。但是,有的Bootloader也支持分区的功能,例如:Redboot可以创建Flash分区表,并且内核MTD驱动可以解析出Redboot的分区表。第5章 Bootloader开发基础 除了NOR Flash,还有NAND
12、 Flash、Compact Flash、DiskOnChip等。这些Flash具有芯片价格低、存储容量大的特点。但是这些芯片一般通过专用控制器的I/O方式来访问,不能随机访问,因此引导方式跟NOR Flash也不同。在这些芯片上,需要配置专用的引导程序。通常,这种引导程序起始的一段代码将整个引导程序复制到RAM中运行,从而实现自举启动,与磁盘启动相似。第5章 Bootloader开发基础 5.1.2 Bootloader的种类的种类嵌入式系统已经有各种各样的Bootloader,种类划分也有多种方式。除了按照处理器体系结构不同划分以外,还有功能复杂程度的不同。首先区分一下“Bootloade
13、r”和“Monitor”的概念。严格来说,“Bootloader”只是引导设备并且执行主程序的固件;而“Monitor”还提供了更多的命令行接口,可以调试、读/写内存、烧写Flash、配置环境变量等。“Monitor”在嵌入式系统开发过程中可以提供很好的调试功能,开发完成以后,就完全设置成了一个“Bootloader”。所以,习惯上大家把它们统称为Bootloader。第5章 Bootloader开发基础 表5-1列出了Linux的开放源码引导程序及其支持的体系结构。表中给出了X86、ARM及PowerPC体系结构的常用引导程序,并且注明了每一种引导程序是不是“Monitor”。第5章 Boo
14、tloader开发基础 表表5-1 开放源码的开放源码的Linux引导程序引导程序BootloaderMonitor描 述X86ARMPowerPCLILO否Linux磁盘引导程序是否否GRUB否GNU的LILO替代程序是否否Loadlin否从DOS引导Linux是否否ROLO否从ROM引导Linux而不需要BIOS是否否Etherboot否通过以太网卡启动Linux系统的固件是否否LinuxBIOS否完全替代BUIS的Linux引导程序是否否BLOB否LART等硬件平台的引导程序否是否U-Boot是通用引导程序是是是RedBoot是基于eCos的引导程序是是是第5章 Bootloader开发
15、基础 对于每种体系结构,都有一系列开放源码Bootloader可以选用。(1)X86。X86的工作站和服务器上一般使用LILO和GRUB。LILO是Linux发行版主流的Bootloader。不过Redhat Linux发行版已经使用了GRUB,GRUB比LILO有更有好的显示界面,使用配置也更加灵活方便。在某些X86嵌入式单板机或者特殊设备上,会采用其他Bootloader,如ROLO。这些Bootloader可以取代BIOS的功能,能够从Flash中直接引导Linux启动。现在ROLO支持的开发板已经并入U-Boot,所以U-Boot也可以支持X86平台。第5章 Bootloader开发基
16、础(2)ARM。ARM处理器的芯片商很多,所以每种芯片的开发板都有自己的Bootloader,结果ARM Bootloader也变得多种多样。最早有ARM720处理器开发板的固件,后来又有了ARMboot、StrongARM平台的Blob,还有S3C2410处理器开发板上的ViVi等。现在ARMboot已经并入了U-Boot,所以U-Boot也支持ARM/XSCALE平台。U-Boot已经成为ARM平台事实上的标准Bootloader。(3)PowerPC。PowerPC平台的处理器有标准的Bootloader,就是PPCboot。PPCboot在合并ARMboot等之后创建了U-Boot,成
17、为各种体系结构开发板的通用引导程序。U-Boot仍然是PowerPC平台的主要Bootloader。第5章 Bootloader开发基础(4)MIPS。MIPS公司开发的YAMON是标准的Bootloader,也有许多MIPS芯片商为自己的开发板编写了Bootloader。现在,U-Boot也已经支持MIPS平台。(5)SH。SH平台的标准Bootloader是SH-boot。Redboot在这种平台上也可使用。(6)M68K。M68K平台没有标准的Bootloader。Redboot能够支持M68K系列的系统。第5章 Bootloader开发基础 值得说明的是Redboot,它几乎能够支持所
18、有的体系结构,包括MIPS、SH、M68K等体系结构。Redboot是以eCos为基础,采用GPL许可的开源软件工程。现在由Core eCos的开发人员维护,源码下载网站http:/ Users Guide。第5章 Bootloader开发基础 5.1.3 Bootloader的基本原理的基本原理Bootloader是依赖于硬件而实现的,特别是在嵌入式领域。因此,在嵌入式领域里建立通用的Bootloader几乎是不可能的。尽管如此,仍然可以对Bootloader归纳出通用的概念,以指导用户对特定的Bootloader设计与实现。1Bootloader的操作模式(Operation Mode)大
19、多数Bootloader都包含两种不同的操作模式:启动加载模式和下载模式,这种区别仅对于开发人员才有意义。但从最终用户的角度看,Bootloader 的作用就是用来加载操作系统,而并不存在所谓的启动加载模式与下载工作模式的区别。第5章 Bootloader开发基础(1)启动加载(Bootloading)模式:这种模式也称为自主(Autonomous)模式,也即Bootloader从目标机上的某个固态存储设备上将操作系统加载到RAM中运行,整个过程并没有用户的介入。这种模式是Bootloader的正常工作模式,因此在嵌入式产品发布的时侯,Bootloader工作在这种模式下。第5章 Bootlo
20、ader开发基础(2)下载(Downloading)模式:在这种模式下,目标机上的Bootloader将通过串口连接或网络连接等通信手段从主机(Host)下载文件,比如下载内核映像和根文件系统映像等。从主机下载的文件通常首先被Bootloader保存到目标机的RAM中,然后再被Bootloader写到目标机上的Flash类固态存储设备中。Bootloader的这种模式通常在第一次安装内核与根文件系统时被使用;此外,以后的系统更新也会使用Bootloader的这种工作模式。工作于这种模式下的Bootloader通常都会向它的终端用户提供一个简单的命令行接口。第5章 Bootloader开发基础
21、像Blob或U-Boot等这样功能强大的Bootloader通常同时支持以上两种工作模式,而且允许用户在这两种工作模式之间进行切换。比如,Blob在启动时处于正常的启动加载模式,但是它会延时10s等待终端用户按下任意键将Blob切换到下载模式;如果在10s内没有用户按键,则Blob继续启动Linux内核。第5章 Bootloader开发基础 2Bootloader与主机之间进行文件传输所用的通信设与主机之间进行文件传输所用的通信设备及协议备及协议目标机上的Bootloader一般可通过串口与主机之间进行文件传输,传输协议通常是 xmodem/ymodem/zmodem协议中的一种。但是,串口传
22、输的速度是有限的,因此通过以太网连接并借助TFTP协议来下载文件是个更好的选择。此外,在通过以太网连接和 TFTP 协议来下载文件时,主机方必须有一个软件用来提供TFTP服务。第5章 Bootloader开发基础 3Bootloader 的主要任务与典型结构框架的主要任务与典型结构框架首先假定内核映像与根文件系统映像都被加载到RAM中运行。之所以提出这样一个前提,是因为在嵌入式系统中内核映像与根文件系统映像也可以直接在 ROM 或 Flash 这样的固态存储设备中运行。但这种做法无疑是以牺牲运行速度为代价的。从操作系统的角度看,Bootloader的总目标就是正确地调用内核执行。第5章 Boo
23、tloader开发基础 由于Bootloader的实现依赖于CPU的体系结构,大多数Bootloader都分为stage1和 stage2两大部分。依赖于CPU体系结构的代码,比如设备初始化代码等,通常都放在stage1 中,而且通常都通过汇编语言来实现,以达到短小精悍的目的;而stage2则通常用C语言来实现,这样可以实现更复杂的功能,而且代码会具有更好的可读性和可移植性。第5章 Bootloader开发基础 Bootloader的stage1通常包括以下工作(以执行的先后顺序):(1)硬件设备初始化。(2)为加载Bootloader的stage2准备RAM空间。(3)拷贝Bootloade
24、r的stage2到RAM空间中。(4)设置堆栈。(5)跳转到stage2的C入口点。第5章 Bootloader开发基础 Bootloader的stage2通常包括以下工作(以执行的先后顺序):(1)初始化本阶段要使用到的硬件设备。(2)检测系统内存映射(Memory Map)。(3)将内核映像和根文件系统映像从Flash设备上复制到RAM空间中。(4)设置内核启动参数。(5)调用启动内核。第5章 Bootloader开发基础 4Bootloader stage1(1)基本的硬件初始化。Bootloader首先执行初始化操作,其目的是为stage2的执行以及随后的内核执行准备好基本的硬件环境。
25、通常包括以下步骤(以执行的先后顺序):屏蔽所有的中断。为中断提供服务通常是OS设备驱动程序的责任,因此在 Bootloader的执行全过程中不必响应任何中断。中断屏蔽可以通过写CPU的中断屏蔽寄存器或状态寄存器(比如ARM的CPSR寄存器)来完成。设置CPU的速度和时钟频率。第5章 Bootloader开发基础 RAM初始化,包括正确地设置系统的内存控制器的功能寄存器以及各内存库控制寄存器等。初始化LED。典型地,通过GPIO来驱动LED,其目的是表明系统的状态。如果板子上没有LED,那么也可以通过初始化UART向串口打印Bootloader的Logo字符信息完成。关闭CPU内部指令/数据Ca
26、che。第5章 Bootloader开发基础(2)为加载stage2准备RAM空间。为了获得更快的执行速度,通常把stage2加载到RAM空间中执行,因此必须为加载Bootloader的stage2准备好可用的RAM空间。由于stage2通常是C语言执行代码,因此在考虑空间大小时,除了stage2可执行映像的大小外,还必须考虑堆栈空间。空间大小最好是内存映射大小(通常是4KB)的倍数。一般而言,1MB的RAM空间已经足够。具体的地址范围可以任意安排,比如Blob就将它的stage2可执行映像安排到从系统RAM起始地址0 xc0200000开始的1MB空间内执行。但是,将stage2安排到整个R
27、AM空间的最顶1MB(也即(RamEnd-1MB)RamEnd)也值得推荐。第5章 Bootloader开发基础 为了方便后面的叙述,把所安排的 RAM 空间范围的大小记为stage2_size(字节),起始地址和终止地址分别记为stage2_start 和 stage2_end(这两个地址均以4字节边界对齐),因此有stage2_endstage2_startstage2_size必须确保所安排的地址范围是可读/写的RAM空间,因此,必须对所安排的地址范围进行测试。具体的测试方法以内存映射为被测试单位,测试每个内存映射开始的两个字是否是可读/写的。为方便后面叙述,记检测算法为test_mem
28、page,具体步骤如下:第5章 Bootloader开发基础 保存内存映射一开始两个字的内容。向这两个字中写入任意的数字。比如向第一个字写入0 x55,第2个字写入0 xaa。将这两个字的内容读回。读到的内容应该分别是0 x55和0 xaa,如果不是,则说明这个内存映射所占据的地址范围不是一段有效的RAM空间。向这两个字中写入任意的数字。比如向第一个字写入0 xaa,第2个字中写入0 x55。将这两个字的内容读回。读到的内容应该分别是0 xaa和0 x55。如果不是,则说明这个内存映射所占据的地址范围不是一段有效的RAM空间。第5章 Bootloader开发基础 恢复这两个字的原始内容。测试完
29、毕。为了得到一段有效的RAM空间,也可以将所安排的RAM空间进行清零操作。为了得到一段有效的RAM空间,也可以将所安排的RAM空间进行清零操作。(3)拷贝stage2到RAM中。拷贝时要确定:stage2的可执行映像在固态存储设备的存放起始地址和终止地址;RAM空间的起始地址。(4)设置堆栈指针sp。堆栈指针的设置是为执行C语言代码做好准备。可以把sp的值设置为(stage2_end-4),也即在1MB的RAM空间的最顶端(堆栈向下生长)。第5章 Bootloader开发基础 此外,在设置堆栈指针sp前,可以关闭LED灯,以提示用户准备跳转到stage2。经过上述这些执行步骤后,系统的物理内存
30、布局应该如图5-3所示。(5)跳转到stage2的C入口点。在上述一切都就绪后,可以跳转到Bootloader的stage2 执行。在ARM系统中,可以通过修改PC寄存器为合适的地址实现。第5章 Bootloader开发基础 图5-3 Bootloader的stage2可执行映像刚被拷贝到RAM空间时的系统内存布局第5章 Bootloader开发基础 5Bootloader stage2stage2的代码通常用C语言来实现,以便于实现更复杂的功能以及取得更好的代码可读性和可移植性。但是与普通C语言应用程序不同的是,在编译和链接Bootloader程序时,不能使用glibc库中的任何支持函数。其
31、原因是显而易见的。但从哪里跳转进main()函数呢?直接把main()函数的起始地址作为整个stage2 执行映像的入口点或许是最直接的想法,但是这样做有两个缺点:无法通过main()函数传递函数参数;无法处理main()函数的返回。一种更为巧妙的方法是利用trampoline(弹簧床)的概念。第5章 Bootloader开发基础 用汇编语言写一段trampoline小程序,并将这段trampoline小程序来作为stage2可执行映像的执行入口点。然后在 trampoline汇编小程序中用CPU跳转指令跳入main()函数中去执行;而当main()函数返回时,CPU执行路径显然再次回到tra
32、mpoline 程序。简而言之,这种方法的思想就是:用这段trampoline小程序来作为main()函数的外部包裹(External Wrapper)。第5章 Bootloader开发基础 下面给出一个简单的trampoline 程序示例(来自Blob):.text.globl _trampoline_trampoline:blmain/*if main ever returns we just call it again*/b_trampoline第5章 Bootloader开发基础 由代码可以看出,当main()函数返回后,又用一条跳转指令重新执行trampoline程序也就重新执行了m
33、ain()函数,这也就是trampoline(弹簧床)一词的意思。(1)初始化本阶段要使用到的硬件设备。通常包括:初始化至少一个串口以便和终端用户进行I/O输出信息;初始化计时器等。在初始化这些设备之前,也可以重新把LED灯点亮,以表明已经进入main()函数。设备初始化完成后,输出如程序名字字符串、版本号等打印信息。第5章 Bootloader开发基础(2)检测系统的内存映射。内存映射,就是指在整个4GB物理地址空间中分配用来寻址系统的RAM单元。比如,在SA-1100 CPU中,从0 xC000 0000开始的512MB地址空间被用作系统的RAM地址空间,而在 Samsung S3C44B
34、0X CPU中,0 x0c00 00000 x1000 0000之间的64 MB地址空间被用作系统的RAM地址空间。虽然CPU通常预留出一大段足够的地址空间给系统RAM,但是在搭建具体的嵌入式系统时却不一定会实现CPU预留的全部 RAM地址空间。也就是说,具体的嵌入式系统往往只把CPU预留的全部RAM地址空间中的一部分映射到RAM单元上,而让剩下的那部分预留RAM地址空间处于未使用状态。第5章 Bootloader开发基础 因此Bootloader的stage2必须在运行(比如将存储在Flash上的内核映像读到RAM空间中)之前检测整个系统的内存映射情况,也即必须了解CPU预留的全部RAM地址
35、空间中哪些是被真正映射到RAM地址单元,哪些是处于未使用状态的。内存映射的描述。可以用如下数据结构描述RAM地址空间中的一段连续的地址空间:第5章 Bootloader开发基础 typedef struct memory_area_struct u32 start;/*the base address of the memory region*/u32 size;/*the byte number of the memory region*/int used;memory_area_t;第5章 Bootloader开发基础 这段RAM地址空间中的连续地址空间可以处于两种状态之一:a.used=
36、1,说明这段连续的地址范围已被真正地映射到RAM单元;b.used=0,说明这段连续的地址范围并未被系统所实现,而是处于未使用状态。基于上述memory_area_t数据结构,整个CPU预留的RAM地址空间可以用一个 memory_area_t类型的数组来表示,如下所示:第5章 Bootloader开发基础 memory_area_t memory_mapNUM_MEM_AREAS=0.(NUM_MEM_AREAS-1)=.start=0,.size=0,.used=0,;第5章 Bootloader开发基础 内存映射的检测。一个可用来检测整个RAM地址空间内存映射情况简单而有效的算法如下:/
37、*数组初始化*/for(i=0;i NUM_MEM_AREAS;i+)memory_mapi.used=0;/*first write a 0 to all memory locations*/for(addr=MEM_START;addr MEM_END;addr+=PAGE_SIZE)*(u32*)addr=0;for(i=0,addr=MEM_START;addr hdr.tag=ATAG_CORE;params-hdr.size=tag_size(tag_core);params-u.core.flags=0;params-u.core.pagesize=0;params-u.core
38、.rootdev=0;params=tag_next(params);第5章 Bootloader开发基础 其中,BOOT_PARAMS 表示内核启动参数在内存中的起始基地址,指针params是struct tag 类型的指针。宏tag_next()将以指向当前标记的指针为参数,计算当前标记的下一个标记起始地址。注意,内核的根文件系统所在的设备ID就是在此设置的。以下为设置内存映射情况的示例代码:第5章 Bootloader开发基础 for(i=0;i hdr.tag=ATAG_MEM;params-hdr.size=tag_size(tag_mem32);params-u.mem.start
39、=memory_mapi.start;params-u.mem.size=memory_mapi.size;params=tag_next(params);第5章 Bootloader开发基础 可以看出,在memory_map数组中,每一个有效的内存段都对应一个 ATAG_MEM 参数标记。Linux内核在启动时可以以命令行参数的形式来接收信息,利用这一点可以向内核提供那些内核不能检测的硬件参数信息,或者重载(Override)内核检测到的信息。比如,用一个命令行参数字符串“console=ttyS0,115200n8”来通知内核以ttyS0作为控制台,且串口采用“115200b/s、无奇偶校
40、验、8位数据位”这样的设置。下面为一段设置调用内核命令行参数字符串的示例代码:第5章 Bootloader开发基础 char*p;/*eat leading white space*/for(p=commandline;*p=;p+);/*skip non-existent command lines so the kernel will still*use its default command line.*/if(*p=0)return;第5章 Bootloader开发基础 params-hdr.tag=ATAG_CMDLINE;params-hdr.size=(sizeof(struct
41、 tag_header)+strlen(p)+1+4)2;strcpy(params-u.cmdline.cmdline,p);params=tag_next(params);第5章 Bootloader开发基础 在上述代码中,设置tag_header的大小时,必须包括字符串的终止符0,此外还要将字节数向上圆整4个字节,因为tag_header结构中的size成员表示的是字数。以下为设置ATAG_INITRD的示例代码,通知内核在RAM中何处可以找到initrd映像(压缩格式)以及映像的大小:第5章 Bootloader开发基础 params-hdr.tag=ATAG_INITRD2;para
42、ms-hdr.size=tag_size(tag_initrd);params-u.initrd.start=RAMDISK_RAM_BASE;params-u.initrd.size=INITRD_LEN;params=tag_next(params);第5章 Bootloader开发基础 以下是设置 ATAG_RAMDISK的示例代码,通知内核解压后的RAMdisk大小(单位是KB):params-hdr.tag=ATAG_RAMDISK;params-hdr.size=tag_size(tag_ramdisk);params-u.ramdisk.start=0;params-u.ramd
43、isk.size=RAMDISK_SIZE;/*请注意,单位是KB*/params-u.ramdisk.flags=1;/*automatically load ramdisk*/params=tag_next(params);第5章 Bootloader开发基础 最后,设置 ATAG_NONE 标记,结束整个启动参数列表:static void setup_end_tag(void)params-hdr.tag=ATAG_NONE;params-hdr.size=0;第5章 Bootloader开发基础 7调用内核调用内核 Bootloader调用Linux内核的方法是直接跳转到内核的第一条
44、指令处,也即直接跳转到MEM_START+0 x8000地址处。在跳转时要满足下列条件:(1)CPU寄存器的设置。R0=0。R1=机器类型ID。关于Machine Type Number,可以参见linux/arch/arm/tools/mach-types。R2=启动参数标记列表在RAM中的起始基地址。(2)CPU模式。必须禁止中断(IRQs和FIQs);CPU必须为SVC模式。第5章 Bootloader开发基础(3)Cache 和MMU的设置。MMU 必须关闭;指令 Cache 可以打开也可以关闭;数据Cache 必须关闭;如果用C语言,可以像下列示例代码这样来调用内核:void(*th
45、eKernel)(int zero,int arch,u32 params_addr)=(void(*)(int,int,u32)KERNEL_RAM_BASE;theKernel(0,ARCH_NUMBER,(u32)kernel_params_start);注意,theKernel()函数调用应该是永远不返回的,如果这个调用返回,则出错。第5章 Bootloader开发基础 8串口终端调试显示串口终端调试显示Bootloader程序的设计与实现中,向串口终端打印信息是一个非常重要而有效的调试手段。但是,经常会碰到串口终端显示乱码或根本没有显示的问题。造成这个问题的原因主要有两种:Bootl
46、oader串口初始化设置不正确;运行在主机端的终端仿真程序对串口的设置不正确,这包括波特率、奇偶校验、数据位和停止位等方面的设置。此外,在Bootloader的运行过程中可以正确地向串口终端输出信息,但当Bootloader启动内核后却无法看到内核的启动输出信息。对这一问题的原因有以下几点:第5章 Bootloader开发基础(1)确认内核在编译时已配置了对串口终端的支持,并配置了正确的串口驱动程序。(2)Bootloader对串口的初始化设置可能会和内核对串口的初始化设置不一致。此外,对于诸如S3C44b0X这样的CPU,CPU时钟频率的设置也会影响串口,因此如果Bootloader 和内核
47、对其CPU时钟频率的设置不一致,会使串口终端无法正确显示信息。(3)确认Bootloader所用的内核基地址必须和内核映像在编译时所用的运行基地址一致。假设内核映像在编译时用的基地址是 0 xc0008000,但Bootloader却将它加载到0 xc0010000处去执行,那么内核映像当然不能正确地执行。第5章 Bootloader开发基础 5.2 U-Boot 5.2.1 U-Boot工程简介工程简介DENX软件工程中心的Wolfgang Denk最早基于8xxrom的源码创建了PPCBoot工程,并且不断添加处理器的支持。后来,Sysgo Gmbh把PPCBoot移植到ARM平台上,创建
48、了ARMBoot工程,然后以PPCBoot工程和ARMBoot工程为基础,创建了U-Boot工程。第5章 Bootloader开发基础 现在U-Boot已经能够支持PowerPC、ARM、X86、MIPS体系结构的上百种开发板,已经成为功能最多、灵活性最强并且开发最积极的开放源码Bootloader,目前仍然由DENX的Wolfgang Denk维护。U-Boot的源码包可以从sourceforge网站下载,还可以订阅该网站活跃的U-Boot Users邮件论坛,这个邮件论坛对于U-Boot的开发和使用都很有帮助。U-Boot软件包下载网站:http:/ Bootloader开发基础 5.2.
49、2 U-Boot源码结构源码结构从网站上可以下载得到U-Boot源码包,例如U-Boot-1.1.2.tar.bz2,解压后就可以得到U-Boot源程序。在顶层目录下有18个子目录,分别存放和管理不同的源程序。这些目录中所要存放的文件有其规则,可以分为三类:第一类目录与处理器体系结构或者开发板硬件直接相关配置。第二类目录存放通用的函数或者驱动程序。第三类目录存放U-Boot的应用程序、工具或者文档。第5章 Bootloader开发基础 表5.2列出了U-Boot顶层目录下各级目录的存放原则。U-Boot的源代码包含对几十种处理器、数百种开发板的支持。但对于特定的开发板,配置编译过程只需要其中的
50、部分程序。这里具体以S3C2410 arm920t处理器为例,具体分析S3C2410处理器和开发板所依赖的程序,以及U-Boot的通用函数和工具。第5章 Bootloader开发基础 表表5.2 U-Boot的源码顶层目录说明的源码顶层目录说明目 录特 性解 释 说 明board平台依赖存放电路板相关的目录文件,例如RPXlite(mpc8xx)、smdk2410(arm920t)、sc520_cdp(x86)等目录cpu平台依赖存放CPU相关的目录文件,例如mpc8xx、ppc4xx、arm720t、arm920t、xscale、i386等目录lib_ppc平台依赖存放对PowerPC体系结