嵌入式系统的BootLoader
嵌入式系统的BootLoader
3.1 BootLoader概述
一个嵌入式Linux系统从软件的角度看通常分为4个层次:引导加载程序、Linux内核、文件系统、用户应用程序。
引导加载程序是系统加电后运行的第一段代码。大家熟悉的PC中的引导程序一般由BIOS和位于MBR的操作系统BootLoader(例如LILO或者GRUB)一起组成。然而在嵌入式系统中通常没有像BIOS那样的固件程序,因此整个系统的加载启动任务就完全由BootLoader来完成。在嵌入式Linux中,引导加载程序即等效为BootLoader。简单地说,BootLoader就是在操作系统内核运行前执行的一段小程序。通过这段小程序,我们可以初始化必要的硬件设备,创建内核需要的一些信息并将这些信息通过相关机制传递给内核,从而将系统的软硬件环境带到一个合适的状态,最终调用操作系统内核,真正起到引导和加载内核的作用。
BootLoader是依赖于硬件实现的,特别是在嵌入式系统中。不同体系结构需求的BootLoader是不同的,除了体系结构,BootLoader还依赖于具体的嵌入式板级设备的配置。也就是说,对于两块不同的嵌入式板而言,即使它们基于相同的CPU构建,运行在其中一块电路板上的BootLoader,未必能够运行在另一块电路开发板上。
Bootloader的启动过程可以是单阶段的,也可以是多阶段的。大多数单阶段的BootLoader应用于简单的系统,比如没有操作系统的系统。通常多阶段的Bo otLoader能提供更为复杂的功能以及更好的可移植性。从固态存储设备上启动的BootLoader大多数是两阶段的启动过程,也就是启动过程可以分为stage 1和stage 2两部分。依赖于CPU体系结构的代码,比如设备初始化代码等,通常都放在stage1中,而且通常都用汇编语言来实现,以达到短小精悍的目的。而stage2则通常用C语言来实现,这样可以实现更复杂的功能,而且代码会具有更好的可读性和可移植性。
大多数BootLoader都包含两种不同的操作模式:启动加载(Boot loading)模式和下载(Down loading)模式,这种区别仅对于开发人员才有意义。但从最终用户的角度看,BootLoader的作用就是用来加载操作系统,而并不存在所谓的启动加载模式与下载工作模式的区别。
(1)启动加载模式:这种模式也称为自主(Autonomous)模式,即BootLoa der从目标机上的某个固态存储设备上将操作系统加载到RAM中运行,整个过程没有用户的介入。这种模式是BootLoader的正常工作模式。因此在嵌入式产品发布的时候,BootLoader显然必须工作在这种模式下。
(2)下载模式:在这种模式下,目标机上的BootLoader将通过串口连接或网络连接等通信手段从主机上下载文件,比如下载应用程序、数据文件、内核映像等。从主机下载的文件通常首先被BootLoader保存到目标机的RAM中然后再被BootLoader写到目标机上的固态存储设备中,BootLoader的这种模式通常在系统更新时使用。工作于这种模式下的BootLoader通常都会向它的终端用户提供一个简单的命令行接口,比如U-Boot、Blob、VIVI等。
3.2 常用的嵌入式Linux BootLoader
从上一节的内容可以了解到BootLoader是嵌入式系统中非常重要的一部分,也是系统运行工作的必要组成部分。在嵌入式系统中常见的BootLoader有以下几种。
3.2.1 U-Boot
U-Boot是德国DENX小组开发的用于多种嵌入式CPU的BootLoader程序,它可以运行在基于PowerPC、ARM、MIPS等多种嵌入式开发板上。从http://u-boo https://www.360docs.net/doc/e216545666.html,/或ftp://ftp.denx. de/pub/u-boot/站点都可以下载U-Bo ot的源代码,U-Boot源代码的主要目录解释如下。
● board目标板相关文件,主要包含SDRAM、Flash驱动;
● common独立于处理器体系结构的通用代码,如内存大小探测与故障
检测;
● cpu与处理器相关的文件,如mpc8xx子目录下含串口、网口、LCD驱
动及中断初始化等文件;
● driver通用设备驱动,如CFI Flash驱动(目前对Intel Flash支持较好);
● doc U-Boot的说明文档;
● examples可在U-Boot下运行的示例程序,如hello_world.c、timer.c;
● include U-Boot头文件,尤其是configs子目录下与目标板相关的配置
头文件是移植过程中经常要修改的文件;
● lib_xxx处理器体系相关的文件,如lib_ppc、lib_arm目录分别包含与P
owerPC、ARM体系结构相关的文件;
● net与网络功能相关的文件目录,如bootp、nfs、tftp;
● post上电自检文件目录,尚有待于进一步完善;
● rtc RTC(Real Time Clock,实时时钟)驱动程序;
● tools用于创建U-Boot S-RECORD和BIN镜像文件的工具。
3.2.2 VIVI
VIVI是由韩国MIZI公司开发的专门用于ARM产品线的一种BootLoader。因为VIVI 目前只支持使用串口和主机通信,所以必须使用一条串口电缆来连接目标板和主机。VIVI的源代码下载地址为:https://www.360docs.net/doc/e216545666.html,/developer/s3c 2410x/download/vivi.html,VIVI一般有如下作用。
(1)把内核(kernel)从Flash复制到RAM,然后启动它;
(2)初始化硬件;
(3)下载程序并写入Flash;
(4)检测目标板。
vivi.tar.bz2源代码包解压后的目录结构如下所示:
# tree –L 1
.
|-- COPYING
|-- CVS
|-- Documentation
|-- Makefile
|-- Rules.make
|-- arch
|-- drivers
|-- include
|-- init
|-- lib
|-- scripts
|-- test
`-- util
10 directories, 3 files
其中VIVI主要目录介绍如下。
● CVS存放CVS工具相关的文件;
● Docu mentation 存放一些使用VIVI的帮助文档;
● arch存放一些平台相关的代码文件;
● drivers存放VIVI相关的驱动代码;
● include存放所有VIVI源码的头文件;
● init存放VIVI初始化代码;
● lib存放VIVI实现的库函数文件;
● scripts存放VIVI脚本配置文件;
● test存放一些测试代码文件;
● util存放一些NAND Flash烧写image相关的工具实现代码。
3.2.3 Blob
Blob是Boot Loader Object的缩写,是一款功能强大的BootLoader。其源码在http://sourceforge. net/projects/blob上可以获取。Blob最初是由Ja n-Derk Bakker和Erik Mouw两人为一块名为LART(Linux Advanced Radio Te rminal)的开发板写的,该板使用的处理器是StrongARM SA-1100,现在Blob 已经被成功地移植到许多基于ARM的CPU上。
3.2.4 RedBoot
RedBoot是一个专门为嵌入式系统定制的引导启动工具,最初由Redhat开发,它是基于eCos(Embedded Configurable Operating System)的硬件抽象层,同时它继承了eCos的高可靠性、简洁性、可配置性和可移植性等特点。Re dBoot集Bootloader、调试、Flash烧写于一体,支持串口、网络下载,执行嵌入式应用程序。既可以用在产品的开发阶段(调试功能),也可以用在最终的产品上(Flash更新、网络启动)。RedBoot支持下载和调试应用程序,开发板可以通过BOOTP/DHCP协议动态配置IP地址,支持跨网段访问。用户可以通过tft p协议下载应用程序和image,或者通过串口用x-modem/y-modem下载。RedBoo t支持用GDB(the GNU debugger)通过串口或者网卡调试嵌入式程序,可对gc c编译的程序进行源代码级的调试。相比于简易jtag调试器,它可靠、高速(C PU的cache打开后,通过网卡tftp下载能达到1Mbps,GDB下载的速度能达到2 Mbps)、稳定,用户可通过串口或网卡,以命令行的形式管理Flash上的image,下载image到Flash。动态配置RedBoot启动的各种参数、启动脚本,上电后R edBoot可自动从Flash或tftp服务器上下载应用程序执行。在http://source https://www.360docs.net/doc/e216545666.html,/redboot站点可以下载RedBoot源码,同时可以了解更多的关于RedB oot的详细信息,它在嵌入式系统应用中非常广泛。
3.2.5 ARMboot
ARMboot是一个以ARM或StrongARM 为内核CPU的嵌入式系统的BootLoade r固件程序,该软件的主要目标是使新的平台更容易被移植并且尽可能地发挥其强大性能。它只基于ARM固件,但是它支持多种类型的启动,比如Flash,网络下载通过bootp、dhcp、tftp等。它也是开源项目,可以从http://www.source https://www.360docs.net/doc/e216545666.html,/projects/armboot网站获得最新的ARMboot源码和详细资料,它在A RM处理器方面应用非常广泛。
3.2.6 DIY
DIY(Do It Yourself),即自己制作。以上U-Boot、VIVI、Blob、RedBoo t和ARMboot等成熟工具移植起来简单快捷,但同时它们都存在着一定的局限性,首先是因为它们是面向大部分硬件的工具,所以说在功能上要满足大部分硬件的
需求,但一般情况下我们只需要与特定的开发板相关的实现代码,其他型号开发板的实现代码对它来说是没有用的,所以通常它们的代码量较大。其次它们在使用上不够灵活,比如在这些工具上添加自己的特有功能相对比较困难,因为必须熟悉该代码的组织关系,以及了解它的配置编译等文件。用DIY的方式自己编写针对目标板的BootLoader不但代码量短小,同时灵活性很大,最重要的是将来容易维护。所以在实际嵌入式产品开发时大都选择DIY的方式编写BootLoader。
3.3 基于S3C2410开发板的BootLoader实现
本节将以实例讲述基于S3C2410开发板的BootLoader的具体实现,主要分两个方面进行介绍,一是介绍基于U-Boot的移植,二是介绍DIY方式开发Boot Loader。要移植或开发BootLoader首先要清楚具体的硬件系统,在这里就是要了解我们使用的目标板——S3C2410开发板。
3.3.1 S3C2410开发板介绍
本书中所设计的开发板相关的实例都是基于S3C2410开发板设计和测试的。S3C2410开发板是非常通用的一款ARM 9开发板,读者使用任何类型的ARM 9开发板都能参考书中的实例。S3C2410开发板的基本配置如下:
● CPU
采用三星的S3C2410 ARM920T,主频203MHz。集成有SDRAM内存控制器、NA ND Flash控制器、SD卡控制器、USB Host和USB Device控制器、LCD控制器、IIC总线控制器、IIS控制器、SPI接口等多种接口。
● 存储器
64MB的SDRAM;64MB的NAND Flash[注1]。*
● 以太网控制器
10MB网口、CS8900Q3、带联接和传输指示灯。
● 串行接口
系统提供两个串行收发DB9母口连接器,上面分别表示COM0、COM1。
● USB Host接口
两个USB1.1HOST接口;一个USB 1.1Device接口。
● 存储接口
一个SD卡接口;一个十针的AD接口;一个IDE接口。
● LCD和触摸屏接口
一个50芯LCD接口引出了LCD控制器和触摸屏的全部信号。
提供TFT真彩LCD的接口,LCD模块不需要外接电源等,插入该接口直接可以使用。接口另外还带触摸屏的接口。
● 调试及下载接口
20针Multi-ICE标准JTAG接口,支持SDT2.51和ADS1.2调试。
● 音频接口
采用IIS接口芯片UDA1341,一路立体声音频输出接口可接耳机或音箱;支持录音,开发板自带主机体话筒可直接录音,另有一路话筒输入接口可接麦克风。
● 电源接口
5V电源供电,带电源开关和指示灯。
● 操作系统
支持Linux 2.4或以上系统,支持Windows https://www.360docs.net/doc/e216545666.html,。
开发板上包括1片64M×8位数据宽度的NAND Flash(K9F1208)和2片16M ×16位数据宽度的SDRAM,地址范围为 0x30000000~0x34000000。S3C2410将系统的存储空间分为8组(Bank),每组大小为128MB,共1GB。Bank0到Bank 5之间的开始地址是固定的,用于ROM 或SRAM;Bank6和Bank7用于ROM、SRAM或SDRAM,这两个组是可编程且大小相同的。S3C2410具有3种启动方式,通过OM[1:0]管脚进行选择。
① O M[1:0] = 00时,处理器通过NAND Flash启动;
② OM[1:0] = 01时,处理器通过16位宽的ROM启动;
③ OM[1:0] = 10时,处理器通过32位宽的ROM启动。
由于NAND Flash有容量大,比Nor Flash便宜等优势,所以经常选择NAND Flash启动。当从Nor Flash启动时,要把Flash芯片的首地址映射到0x00000 000位置,系统启动后,启动程序本身把自己从Flash搬运到RAM中去。当从N AND Flash启动时,S3C2410会自动把NAND Flash的前4KB数据搬到自己内部的RAM中去,并把内部RAM的首地址设为0x00000000,CPU从0x00000000地址开始运行。本章选择的实现启动方式就是通过NAND Flash启动。如图3.1所示为通过Nor Flash启动和NAND Flash启动两种方式存储空间的分配,图(a)是n GCS0片选的Nor Flash启动模式存储分配图;图(b)是NAND Flash 启动模式的存储分配图。其中SFR为Special Function Register的缩写,即特殊功能寄存器。
3.3.2 U-Boot分析与移植
本节以应用非常广泛的U-Boot为例讲述基于S3C2410开发板的BootLoader 分析与移植。解压u-boot-1.1.6.tar.bz2包,查看其目录结构如下所示:
# tree –L 1 -d
.
|-- board
|-- common
|-- cpu
|-- disk
|-- doc
|-- drivers
|-- dtt
|-- examples
|-- fs
|-- include
|-- lib_arm
|-- lib_avr32
|-- lib_blackfin
|-- lib_generic
|-- lib_i386
|-- lib_m68k
|-- lib_microblaze
|-- lib_mips
|-- lib_nios
|-- lib_nios2
|-- lib_ppc
|-- nand_spl
|-- net
|-- post
|-- rtc
`-- tools
26 directories
(a) (b)
图3.1 两种启动方式内存映射的比较
(a) Not using NAND Flash for booting ROM; (b) Using NAND Flash for booti
ng ROM
大多数BootLoader都包含“启动加载”模式和“下载”模式,U-Boot作为一款强大的Boot- Loader也支持这两种工作模式,并且允许用户在这两种模式之间切换。同时U-Boot也分为Stage1和Stage2两个阶段,其中依赖于CPU体系结构的代码通常都放在Stage1里,并且通常用汇编语言实现;Stage2通常用C语言实现,可以实现更复杂的功能,并且有更好的移植性和可读性。
1.U-Boot Stage1分析
U-Boot的Stage1通常是在start.S文件中实现的,并且都是用汇编语言编写的。一个可执行性image文件必须有一个入口点,并且只能有一个全局入口点,通常这个入口点的地址放在ROM(Flash)0x0位置,因此必须使编译器知道这个入口地址,该过程通常通过修改连接器的脚本文件来完成。由于S3C2410开发板与smdk2410开发板的配置接近,所以此处参考smdk2410的U-Boot实现程序。打开board/smdk2410/ u-boot.lds文件,该脚本文件的内容如下所示:OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
. = 0x00000000;
. = ALIGN(4);
.text :
{
cpu/arm920t/start.o (.text)
(.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 = .;
. = ALIGN(4);
__bss_start = .;
.bss : { *(.bss) }
_end = .;
}
其中,ENTRY(_start)在cpu/arm920t/start.S文件定义了入口点,入口地址为0x00000000。在cpu/arm920t/config.mk文件中定义了代码区基地址TEXT _BASE = 0x33F80000。接下来分析U-Boot的Stage1的核心文件start.S。
(1)设置异常向量表
ARM处理器一般包括复位、未定义指令、SWI、预取终止、数据终止、IRQ、F IQ等异常,关于ARM处理器这些异常在后面会有专门的介绍,其中U-Boot中关于异常向量的定义如下,当发生异常时执行cpu/arm920t/ interrupts.c文件。.globl _start
_start: b reset
ldr pc, _undefined_instruction
ldr pc, _software_interrupt
ldr pc, _prefetch_abort
ldr pc, _data_abort
ldr pc, _not_used
ldr pc, _irq
ldr pc, _fiq
(2)设置CPU模式为SVC模式
Reset即复位,在系统中经常会用到,该操作是异常处理的第一个操作,其主要目的是设置CPU模式为SVC模式。在此有必要介绍一下ARM处理器的7种工作模式。
● 用户模式(usr)ARM处理器正常的程序执行状态;
● 快速中断模式(fiq)用于高速数据传输或通道处理;
● 外部中断模式(irq)用于通用的中断处理;
● 管理模式(svc)操作系统使用的保护模式;
● 数据访问终止模式(abt)当数据或指令预取终止时进入该模式,可用于虚拟存储及存储保护;
● 系统模式(sys)运行具有特权的操作系统任务;
● 未定义指令中止模式(und)当未定义的指令执行时进入该模式,可用于支持协处理器的软件仿真。
ARM微处理器共有37个32位寄存器,其中31个为通用寄存器,6个为状态寄存器。但是这些寄存器不能被同时访问,具体哪些寄存器是可编程访问的,取决于微处理器的工作状态及具体的运行模式。但在任何时候,通用寄存器R0~R 14、程序计数器PC、一个或两个状态寄存器都是可访问的。通用寄存器包括R0~R15,可以分为3类。
● 未分组寄存器R0~R7 在所有的运行模式下,未分组寄存器都指向同一个物理寄存器,它们未被系统用作特殊的用途。因此,在中断或异常处理进行运行模式转
换时,由于不同处理器的运行模式均使用相同的物理寄存器,可能会造成寄存器中数据的破坏,这一点在进行程序设计时应引起注意。
● 分组寄存器R8~R14 对于分组寄存器,它们每一次所访问的物理寄存器与处理器当前的运行模式有关。对于R8~R12来说,每个寄存器对应两个不同的物理寄存器,当使用fiq模式时,访问寄存器R8_fiq~R12_fiq;当使用除fiq模式以外的其他模式时,访问寄存器R8_usr~R12_usr。对于R13、R14来说,每个寄存器对应6个不同的物理寄存器,其中的一个是用户模式与系统模式共用,另外5个物理寄存器对应于其他5种不同的运行模式。
● 程序计数器PC(R15) 在ARM状态下,位[1:0]为0,位[31:2]用于保存PC;在Thumb状态下,位[0]为0,位[31:1]用于保存PC。虽然可以用作通用寄存器,但是有一些指令在使用R15时有一些特殊限制,若不注意,执行的结果将是不可预料的。在ARM状态下,PC的0和1位是0,在Thumb状态下,PC的0位是0。注意,T humb状态下的寄存器集是ARM状态下寄存器集的一个子集,程序可以直接访问8个通用寄存器(R7~R0)、程序计数器(PC)、堆栈指针(SP)、连接寄存器(L R)和CPSR。同时,在每一种特权模式下都有一组SP、LR和SPSR。
设置CPU模式为SVC模式操作的具体实现代码如下,其中CPSR是Current Program Status Register的缩写,即当前程序状态寄存器,寄存器 R16 用作C PSR,CPSR可在任何运行模式下被访问,它包括条件标志位、中断禁止位、当前处理器模式标志位,以及其他一些相关的控制和状态位。
每一种运行模式下又都有一个专用的物理状态寄存器,称为SPSR是Saved Program Status Register的缩写,即备份程序状态寄存器,当异常发生时,SP SR用于保存CPSR的当前值,从异常退出时则可由SPSR来恢复CPSR。与CPU相关的寄存器设置需要参考S3C2410的用户手册来实现。
mrs r0,cpsr
bic r0,r0,#0x1f
orr r0,r0,#0xd3
msr cpsr,r0
(3)关闭看门狗
看门狗即watchdog timer,是一个定时器电路,一般有一个输入叫喂狗,一个输出到MCU(Micro Controller Unit,多点控制单元)的RST端(复位端)。MCU正常工作的时候,每隔一段时间输出一个信号到喂狗端,给 WDT(watchdog timer的简写)清零,如果超过规定的时间不喂狗,一般在程序跑飞时WDT 定时超过,就会给出一个复位信号到MCU,然后MCU复位。看门狗的作用就是防止
程序发生死循环,或者说程序跑飞。根据S3C2410的用户手册,关闭看门狗的具体实现如下:
#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)
ldr r0, =pWTCON
mov r1, #0x0
str r1, [r0]
(4)禁止所有中断
在SVC模式下,不允许有任何中断发生,根据S3C2410的用户手册,通过设置相应的寄存器值的位来禁止中断,具体实现如下:
mov r1, #0xffffffff
ldr r0, =INTMSK
str r1, [r0]