Bootloader代码是嵌入式系统复位后进入操作系统前执行的一段代码。通过Bootloader的代码初始化处理器的各寄存器以及其他外部设备,建立存储器映射图以及初始化堆栈,为操作系统提供基本的运行环境。由于嵌入式系统的硬件的多样性,不可能有通用的Bootloader,因此需要根据具体硬件特点移植。本节以目前应用比较广泛的U-Boot为例讲解嵌入式系统Bootloader移植的方法。
U-Boot的源代码可以从ftp://ftp.denx.de/pub/u-boot/上获得。使用匿名用户身份登录到U-Boot的FTP服务器后,进入pub/u-boot目录,该目录包含了U-Boot所有代码。本书使用U-Boot 1.1.6版本代码作为分析的样本。
学习一个软件,尤其是开源软件,首先应该从分析软件的工程结构开始。一个好的软件有良好的工程结构,对于读者学习和理解软件的架构以及工作流程都有很好的帮助。
U-Boot的源代码布局和Linux类似,使用了按照模块划分的结构,并且充分考虑了体系结构和跨平台问题,其源代码树结构请参考表14-1。
表14-1 U-Boot源代码目录结构
子 目 录 名 |
作 用 |
board |
开发板相关的定义和结构 |
common |
包含U-Boot用到的各种处理函数 |
cpu |
各种不同类型的处理器相关代码 |
doc |
U-Boot文档 |
drivers |
常用外部设备驱动程序 |
examples |
存放U-Boot开发代码样例 |
fs |
文件系统有关的代码,包括cramfs、ext2、fat等常见文件系统 |
include |
U-Boot用到的头文件 |
lib_arm |
ARM体系结构有关的数据定义和操作 |
lib_generic |
U-Boot通用的操作函数 |
net |
常用的网络协议,包括bootp、rarp、arp、tftp等 |
post |
上电自检相关代码 |
rtc |
实时时钟有关操作 |
tools |
U-Boot有关的数据代码 |
表14-1仅列出了主要的目录,以lib_开头的目录还有很多,分别对应不同体系结构用到的函数操作,这里不一一列出。
q board目录存放与开发板有关的文件,每种开发板需要的文件被归纳在board目录的一个目录下。该目录包括每个子目录需要至少提供Makefile和u-boot.lds两个文件,用来设置文件编译的方式以及开发板的硬件资源。如board/smdk2410目录存放了与smdk2410开发板相关的硬件资源和配置函数。
q common目录是与体系结构无关的文件,包括实现各种命令的C语言源代码文件。
q cpu目录存放与CPU相关的文件,每种CPU需要的代码文件存放在以CPU名称命名的子目录下,arm920t存放了arm920t为内核的CPU相关的文件。在每个特定的子目录下都包括cpu.c、interrupt.c和start.S这3个文件,这3个文件是CPU初始化以及配置中断的代码。U-Boot自带了很多CPU相关的代码,用户可以在现有CPU支持的基础上修改自己所需要的配置。
q 通用设备的驱动程序存放在drivers目录下。U-Boot自带了许多设备的驱动,包括显示芯片、网络接口控制器、USB控制器、I2C器件等,对于大多数用户而言已经够用,用户也可以按照自己的需求增加或者修改设备驱动。
q fs存放支持的文件系统代码,U-Boot目前支持cramfs、ext2、fat、jffs、reiserfs、yaffs等多种常见的文件系统。
q net目录是与网络协议有关的代码,比如BOOTP协议、TFTP协议、RARP协议等。
q post存放与硬件自检有关的代码。
q rtc目录存放与硬件实时时钟相关的代码。
q tools目录存放U-Boot编译过程中用到的一些工具代码。
与大多数Bootloader类似,U-Boot的启动分成stage1和stage2两个阶段。stage1使用汇编语言编写,通常与CPU体系紧密相关,如处理器初始化和设备初始化代码等,该阶段在start.S文件中实现。图14-1展示了U-Boot中Stage1阶段的启动过程。
图14-1 U-Boot中Stage1工作流程
图14-1是U-Boot中Stage1工作流程。Stage1的代码都是与平台相关的,使用汇编语言编写占用空间小而且执行速度快。以ARM920为例,Stage1阶段主要是设置各模式程序异常向量表,初始化处理器相关的关键寄存器以及系统内存。Stage1负责建立Stage1阶段使用的堆栈和代码段,然后复制Stage2阶段的代码到内存。
Stage2阶段一般包括:初始化Flash器件、检测系统内存映射、初始化网络设备、进入命令循环,接收用户从串口发送的命令然后进行相应的处理。Stage2使用C语言编写,用于加载操作系统内核,该阶段主要是board.c中的start_armboot()函数实现。图14-2给出了U-Boot的Stage1和Stage2在Flash和RAM中的分配。
图14-2 U-Boot Stage2阶段内存映射
从图14-2中可以看出,U-Boot在加载到内存后,使用了操作系统空余的内存空间。