分享一篇2013年上学时期的帖子
bootloader引导加载程序是系统运行的第一步,嵌入式系统加电后运行的第一段代码。
本文是使用2410开发板完成的2410开发板_百度百科。
补充:嵌入式闪存的文件系统位于flash内存设备上,相比ext2、ext3和ext4较大linux文件系统而言更加小巧灵活。把制作好的根文件压缩后写入flash,由bootloader加载到RAM,解压缩,然后挂载到 /。
嵌入式系统大部分CPU并没有BIOS那样固件程序,系统的加载启动任务就完全由bootloader来完成,初始化硬件设备、建立内存空间的映射图。使系统的软硬件环境处于ready状态,为调用操作系统内核准备好运行环境。
bootloader依赖CPU体系结构分为stage1和stage2两个部分。
stage1执行设备初始化代码逻辑,通常用汇编语言实现。
stage2通常c语言实现,实现更复杂的功能具有更好的可读性、移植性。
stage1通常包括:
硬件设备初始化。
为加载stage2准备RAM空间。
复制stage2到RAM空间中。
设置好堆栈。
跳转到stage2的c程序入口点。
stage2通常包括:
初始化本阶段要使用到的硬件设备。
检测系统内存映射。
将kernel镜像和根文件系统镜像从Flash上读到RAM空间中。
为内核设置启动参数。
调用内核。
启动加载模式
正常工作模式在此模式下才能引导整个系统,从目标机上的某个固态存储设备上将操作系统加载到内存中运行。
下载模式
通过串口、网络等手段从开发主机上下载内核镜像和根文件系统镜像到内存中,然后可以再被BootLoader写到目标机删的固态存储媒介中,或者直接进行系统引导。
前一种功能通常用于第一次烧写内核与根文件到固态媒介时或系统更新时使用。
后者多用于开发人员在开发过程中的系统调试阶段。工作于这种模式下的BootLoader通常都会向它的终端用户提供一个简单的命令接口。
vivi
vivi是由韩国MIZI公司开发的专门用于ARM产品线的一种bootloader。
因为vivi目前只支持使用串口和主机通信,所以必须使用一条串口电缆来连接目标板和主机。vivi一般由一下作用:检测目标板、下载程序并写入Flash、初始化硬件、把内核(kernel)从Flash复制到RAM,然后启动它。
u-boot
U-boot是德国DENX小组开发的用于多种嵌入式CPU的bootloader程序,可以运行在基于PowerPC、ARM、MIPS等多种嵌入式开发板上。源代码的主要目录解释如下:
board:目标板相关文件,主要包含SDRAM、Flash驱动。
common:独立于处理器体系结构的通用代码,如内存大小探测与故障检测。
cpu:与处理器相关的文件,如mpc8xx子目录下含串口、网口、LCD驱动及中断初始化等文件。
driver:通用设备驱动,如CFI Flash驱动(目前对Intel Flash支持较好)。
doc:U-Boot的说明文档。
example:可在U-Boot下运行的示例程序,如hello_world.c和timer.c。
include:U-Boot头文件,尤其是configs子目录下与开发板相关的配置头文件是移植过程中经常要修改的文件。
lib_xxx:处理器体系相关的文件,如lib_ppc、lib_arm目录分别包含与PowerPC、ARM体系结构相关的文件。
net:与网络功能相关的文件目录,如bootp、NFS和TFTP。
post:上电自检文件目录,尚有待于进一步完善。
rtc:RTC(Real Time Clock,实时时钟)驱动程序。
tools:用于创建U-Boot、S-RECORD和BIN镜像文件的工具。
redboot
专门为嵌入式系统定制的引导启动工具由Redhat开发,它基于ECOS(Embedded Configurable Operating System)的硬件抽象层,同时继承了ECOS的高可靠性、简洁性、可配置性和可移植性等特点。集bootloader、调试、Flash烧写于一体的,支持串口、网络下载、调试应用程序。
开发板可以通过BOOTP/DHCP协议动态配置IP地址,支持跨网段访问。用户可以通过TFTP协议下载应用程序和image,或者通过串口用x-modem/y-modem下载。支持使用gdb通过串口、网卡调试嵌入式程序,可对gcc编译的出现进行源代码级的调试。可通过串口或网卡,以命令的形式管理Flash上的image,下载image到Flash。动态配置redboot启动的各种参数、启动脚本,上电后redboot可自动从Flash或tftp服务器上下载应用程序执行。
armboot
ARMBoot是一个以ARM或StrongARM为内核CPU的嵌入式系统的bootloadr固件程序,该软件的主要目标是使新的平台更容易被移植,并且尽可能地发挥其强大性能。它只基于ARM固件,但是它支持多种类型的启动,如Flash,网络下载通过bootp、dhcp和TFTP等。
blob
Blob是boot loader object的缩写,是一款功能强大的bootloader,被移植到许多基于ARM的CPU上。
本课题采用的是u-boot-1.2.0版本,下面详细介绍u-boot的移植。
U-boot的stage1代码通常放在start.s文件中,它用汇编语言写成,其主要功能如下:
定义入口。一个可执行的Image必须有一个入口的,并且只能有一个全局入口,通常这个入口放在ROM(Flash)的0x0地址。
设置异常向量。
本地硬件设备初始化(设置CPU模式/关闭看门狗计时器/屏蔽中断等)。
初始化内存控制器。如果从固态存储介质中启动,则复制bootloader的第二阶段到RAM。
设置堆栈,跳转到stage2入口点。自此stage1执行完毕。
U-boot的stage2开始函数为lib_arm/board.c中的start_armboot,是整个启动代码中C语言的主函数,同时还是整个U-boot的主函数,该函数主要功能如下:
调用一系列的设备初始化函数。
确定目标板是进入下载操作模式还是启动加载模式。
如果是启动加载模式,则将内核映像和根文件系统映像从FLASH上读到RAM空间中。
为内核设置启动参数。
调用内核。
U-boot启动流程具体流程如图所示:
由于U-boot1.2.0并没有对s3c2440开发板的支持,所以首先需要修改U-boot根目录下的Makefile,添加编译选项,一般用开发板的名称命名。
smdk2440_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t smdk2440 NULL s3c24x0
然后需要添加开发板目录,用来存放开发板特有的文件。
由于U-boot对s3c2440a的同系列芯片s3c2410有着良好的支持,并且已经提供了对smdk2410开发板的支持。移植工作主要是参考s3c2410的基础上展开。
在/board子目录中建立自己的开发板smdk2440目录。
将smdk2410目录下的文件全部拷贝到smdk2440目录,同时将smdk2410.c改名为smdk2440.c。
由于文件名已改所以要对该目录下的Makefile进行修改,将“COBJS:=smdk2440.o nand_read.o flash.o”替换原来的“COBJS:=smdk2410.o flash.o”。
s3c2440的NAND Flash控制器与s3c2410的不同,因此需要自己添加新的Flash读取函数,该函数在nand_read.c中。
在include/configs/中建立s3c2440.h,并直接拷贝同目录下的最相近的头文件s3c2410.h的内容到里面。
s3c2440是arm920T的核,程序是从/cpu/arm920t/start.S中开始执行的。
改写屏蔽所有中断的代码,仿照s3c2410的写法,在其下面添加:
#if defined(CONFIG_S3C2440)
ldr r1,=0x7fff //根据数据手册,INTSUBMSK寄存器有15位可用
ldr r0,=INTSUBMSK
str r1,[r0]
#endif
添加NAND Flash启动。U-boot固有支持从NOR Flash启动,不支持从NAND Flash启动,因此需要添加读取代码。
由于NAND Flash需要驱动程序才可以读写,所以上电后,芯片无法直接读取NAND Flash中的内容。所以大部分嵌入式系统只能从NOR Flash中启动,不过s3c2440支持从NAND Flash启动。这是因为从NAND Flash启动的时候,Flash中开始4k的代码会被s3c2440自动地复制到芯片内部一个叫“Steppingston”的RAM中,并把0x0设置为内部RAM的起始地址,然后CPU从内部RAM的0x0位置开始启动。这个过程不需要程序干涉。我们需要使用这4k代码来把更多的代码从NAND Flash中读到SDRAM中去。
stage2代码已经进入C语言部分。具体修改步骤如下。
第一步:修改头文件。在include/configs/s3c2440.h添加相关宏定义:
#define CONFIG_S3C2440 1
#define CONFIG_SMDK2440 1
#define CONFIG_S3C2440_NAND_BOOT 1//用来支持从NAND Flash启动
修改宏定义:
#difine CFG_LOAD_ADDR 0x30008000 /*default load address*/
由于s3c2440和s3c2410的NAND Flash控制器不一样,因此这里需要添加相关寄存器的定义,具体可以查看s3c2440的数据手册。例如:
#define NAND_CTL_BASE 0x4e000000
#define Bint_CTL(Nb) _REG(INT_CTL_BASE+(Nb))
#define oNFCONF 0x00
#define oNFCONT 0x04
#define oNFCMD 0x08
#define oNFADDR 0x0c
#define oNFDATA 0x10
#define oNFSTAT 0x20
#define oNFFCC 0x2c
/include/common.h
/include/s3c2440.h
/cpu/arm920t/s3c2440/interrupts.c
/cpu/arm920t/s3c2440/serial.c
/cpu/arm920t/s3c2440/speed.c
/cpu/arm920t/s3c2440/usb_ohci.c
/rtc/s3c2440_rtc.c
编译smdk2440项目的时候会用到上述文件,因此需要修改这些文件里的部分内容,主要是修改寄存器定义,添加类似于“#if defined(CONFIG_S3C2440)”的判断。可以仿照s3c2410选项进行修改。
大部分都只需要将
#if defined(CONFIG_S3C2410)
改为
#if defined(CONFIG_S3C2410)||defined(CONFIG_S3C2440)
即可
U-boot没有NAND Flash芯片K9F1208U0B的信息,因此需要在/inlcude/linux/mtd/nand_ids.h的结构体nand_flash_ids加入NAND Flash(K9F120U0B)的信息。
{“Samsung K9F1208U0B”,NAND_MFR_SAMSUNG,0x76,26,0,0x4000,0}
修改/board/smdk2440/smdk2440.c。在文件的末尾添加对Nand Flash的初始化函数,因为在后面Nand Flash的操作都要用到它。
U-boot运行至第二阶段进入start_armboot()函数,其中调用的nand_init()函数是对NAND Flash的初始化函数。nand_init函数在两个文件中实现。其调用与CFG_NAND_LEGACY宏定义有关,如果没有定义这个宏,系统调用drivers/nand/nand.c中的nand_init(),否则调用自己在smdk2440中的nand_init()函数。
至此U-boot的移植工作基本结束。
代码修改完成后重新编译。执行以下命令:
#make clean
#make smdk2440_config
#make
将编译后的u-boot.bin通过JTAG口烧写到目标板Flash的0x0地址。
移植成功后,开发板上电U-boot启动,终端可以看到串口打印的消息。