基于嵌入式S3C2440系统Bootloader设计与实现
2016-11-17范蟠果邢保毫米晓亮余书宝
范蟠果,邢保毫,米晓亮,余书宝, 王 婷
(西北工业大学 自动化学院,西安 710129)
基于嵌入式S3C2440系统Bootloader设计与实现
范蟠果,邢保毫,米晓亮,余书宝, 王 婷
(西北工业大学 自动化学院,西安 710129)
Bootloader是嵌入式系统的一个重要环节,对不同的硬件平台,其Bootloader都不尽相同,因此设计Bootloader是嵌入式系统开发的难点;文中分析S3C2440嵌入式系统的硬件组成和u-boot源码对linux内核的启动流程,得出u-boot启动内核两个阶段必备阶段:第一个阶段是用汇编初始与具体硬件平台相关的操作等,第二阶段是用C语言编写复杂功能以及启动内核;以加载linux-2.6.22.6内核为例,根据u-boot启动内核两个阶段所做的工作,设计出适用于S3C2440嵌入式系统的精简Bootloader;通过实验表明,该设计的Bootloader成功启动linux内核,具有良好的稳定性,可靠性和简洁性。
系统设计;分析u-boot;实现Bootloader;启动内核
0 引言
Bootloader是嵌入式系统内核运行前的一段程序,这段程序初始化硬件设备,并且建立一个内存空间映射图,从而建立适当的系统软硬件环境,为最终启动内核和加载文件系统做准备[1]。由于Bootloader依赖于硬件,它与处理器架构,具体设计的硬件平台资源相关,因此设计一个适合某平台的Bootloader是开发嵌入式系统重要工作。
在设计引导程序时,一般会移植u-boot开源代码,但是这样代码量大,占用较大存储空间;本文通过分析u-boot启动流程,根据硬件平台资源,设计一个精简,稳定的Bootloader。
1 u-boot 分析
1.1 u-boot启动流程分析
分析支持S3C2410的u-boot源代码,具体启动流程分析如图1所示。
图1 u-boot启动流程
根据分析得出启动过程一般分为两个阶段。
stage1:主要通过汇编来实现和硬件相关代码:硬件设备初始化,加载u-boot第二段代码到RAM空间,设置好栈,跳转到第二段代码入口[2]。
stage2:主要用C语言来实现一些复杂的功能:初始化本阶段使用的硬件设备,检测系统内存映射,将内核从flash读取到RAM中,为内核设置启动参数,调用内核[2]。
1.2 u-boot编译以及大小分析
u-boot在编译之前,需要编写好Makefile和连接文件u-boot.lds,然后通过make命令编译生成u-boot.bin;Makefile规定u-boot所有函数的依赖关系;连接文件u-boot.lds指定u-boot编译的连接地址,第一个被编译的文件,存放的代码段,数据段以及BSS段的位置。
如下面一个连接文件u-boot.lds:
SECTIONS
{
. = 33f00000;//指定的连接地址
.text ://代码段
{
_start = .;//代码段开始位置
arch/arm/cpu/arm920t/start.o (.text)//执行编译的第一个文件start.S
... }
.data ://数据段
.....
__bss_start = .;//BSS段开始位置,此处为u-boot.bin大小结束位置
......}
BSS段是由静态和未初始化的全局变量组成,不会被编译到u-boot.bin中。
由连接文件可知u-boot的大小为__bss_start 地址减去_start的地址 ,可以查看u-boot的反汇编得到具体的大小,执行arm-linux-objdump -D u-boot > u-boot.dis命令来生成如下u-boot.dis反汇编文件,:{
Disassembly of section .text:
33f00000 <_start>:注释:代码开始位置,和连接文件中的连接地址一致
33f00000: ea000013 b 33f00054
......
33f00044 <_bss_start_ofs>:
33f00044: 00069c4c .word 0x00069c4c
.......} 注释:在启动文件start.S中定义一个表示u-boot.bin大小的全局变:
.globl _bss_start_ofs
_bss_start_ofs:
.word __bss_start - _start
编译的u-boot.bin大小为(0x00069c4c)423 KB,占有很大存储空间。由于u-boot是一个支持多平台的源代码,所以它的结构复杂,若要让这个u-boot成功应用到S3C2440系统上,需要对u-boot源代码进行裁剪,并且添加支持平台外设的代码,工作量比较大,而且不便于调试。所以很有必要设计适用S3C2440系统的Bootloader,具体设计如下。
2 系统设计
2.1 硬件设计
图2 是一个基于ARM嵌入式系统硬件框图,S3C2440芯片是基于ARM920T的架构处理器,片内有4 k的SRAM,nandflash控制器,SDRAM控制器等资源。板级设备包含:全功能的串口,JTAG,Nandflash (256 M), SDRAM(64 M)等。
图2 硬件平台框图
2.2 系统软件内存设计
1)根据S3C2440手册可知SDRAM映射在BANK7,所以起始地址为0x30000000;结束地址为0x34000000。在调用C语言之前要设置栈指针,ARM栈是往下增长的,设计栈指针为0x34000000。为给内核以及文件系统留有足够的内存,设置启动代码连接地址为0x33f80000。内核编译后大小不超过2 M,设计内核连接地址为0x30008000。内核把启动参数放在0x30000100位置处,具体内存分布如图3。
2)Nandflash是存储程序和数据,掉电后能保存数据和程序,所以启动代码和内核都存放nandflash中,为了保证两者代码不覆盖,设计Bootloader存储的起始地址为0x0,内核存储的起始地址为0x60000。
根据设计的存储空间和内存分配,两者的映射关系如图3所示。
图3 映射空间图
3)代码重定位。
根据两者映射关系,需要对代码进行重新定位,详细分析如下:
由于cpu对nandflash读取是以块(2 048 KB)为单位的,所以不能在nandflash上运行程序。当系统上电或者重启时,硬件自动把存储器中前4 KB代码拷贝到cpu内部SRAM(映射地址为0);对于连接地址为0x33f80000的代码若要成功运行在地址为0的内存中,在设计代码时必须使用位置无关指令。通过运行在SRAM程序把nandflash中启动代码和内核拷贝到SDRAM,然后通过一条跳转指令,使PC指向SDRAM。
3 Bootlaoder软件实现
根据分析得到u-boot启动流程,以及分配好的内存,下面针对S3C2440特定的嵌入式系统,实现一个完整的Bootloader。
3.1 第一阶段
Stage1主要是完成,建立向量表,初始化硬件,设置堆栈,把Bootloader拷贝到内存中,具体流程如图4所示。
图4 Bootloader一阶段流程
(1)设置ARM异常向量表。
根据ARM920T的架构可知,异常向量入口地址从0x00000000开始到0x0000001c结束,分别设置对应入口地址,当一个异常或中断发生时,处理器会把pc指向对应中断向量的入口地址。
2)设置相关硬件。
设置时钟,使CPU主频为400 MHz;屏蔽中断;初始化串口以便于调试。
3)初始化SDRAM。
SDRAM用来运行启动代码和内核,必须对其进行设置,SDRAM控制器的寄存器,包含处理器对SDRAM读写访问的时序,数据宽度;系统使用两片16位数据接口的K4S561632N,其数据宽度为32位。
4)设置堆栈指针。
Bootloader在启动阶段是运行在管理模式下,只需设置管理模式下的堆栈指针(向下增长),代码为: ldr sp, =0x34000000;设置成功后就可以调用C语言代码。
5)nandflash的初始化。
由于需要把存储在nandflash的启动代码和内核读入SDRAM,所以要设置nandflash控制器的寄存器,设置cpu对存储器读写时序,以及数据宽度。
6)把启动代码拷贝到SDRAM执行。
SDRAM配置成功后,就可以从nandflash拷贝Bootloader启动代码到内存中运行;具体代码如下:
mov r0, 0 /*从nandflash 的0x0地址开始拷贝*/
ldr r1, =_start /*这个值是由连接脚本确定为0x33f80000*/
ldr r2, =__bss_start /*Bootlaoder代码结束位置*/
sub r2, r2, r1 /*获得拷贝的大小*/
bl copy_code_to_sdram / *从nandflash拷贝到SDRAM*/
ldr pc, =main /*重新给pc赋值,跳转到SDRAM中,开始执行第二阶段代码*/
3.2 第二阶段
Stage2的主要工作设置内核启动参数,拷贝内核到内存中,然后启动内核,具体流程如如图5所示。
图5 Bootloader二阶段流程
1)设置内核启动参数。
Bootloader和内核之间参数传递是单向的,Bootloader将参数放在某个约定地址[3](0x30000100),再启动内核,启动后会从0x30000100地址处取参数。
内核启动前会检查传入的参数,比如内存的大小,命令等参数。因为 Linux2.6.22.6内核检查参数以标记ATAG_CODE开始,以标记ATAG_NONE结束。详细结构体可以参考linux内核源码include/asm/setup.h头文件。
以设置参数开始ATAG_CODE标记为例:
void setup_start_tag(void)
{
params = (struct tag *)0x30000100; /*约定的存放参数的起始地址*/
params->hdr.tag = ATAG_CODE; /*参数标记*/
.......
params = tag_next (params); /*指向下一个参数*/
}
其他参数设置 如内存标记ATAG_MEM:告诉内核开发平台外设SDRAM内存起始地址和大小;命令行标记ATAG_CMDLINE:用来控制内核一些行为,结束标记ATAG_NONE用来标记参数结束[3]。
最后配置的参数结束标记:
void setup_end_tag(void)
{ params->hdr.tag = ATAG_NONE;
}
设置参数具体代码:
puts("Set boot param "); /*打印提示信息*/
setup_start_tag(); /*设置起始参数*/
setup_memory_tags(); /*设置内存参数*/
setup_commandline_tag("noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0"); /*设置命令参数*/
setup_end_tag(); /*参数设置结束*/
2)把内核从flash中拷贝到内存的0x30008000地址处。
编译内核是通过执行:make uImage生成内核影像uImage;用JTAG把内核影像uImage 下载到nandflash的0x60000处,但是uImage开始的64字节表示内核头帧数据,真正的内核是从0x60000+64地址开始。具体拷贝代码如下:
nand_read(0x60000+64, (unsigned char *)0x30008000, 0x200000);
第一个参数: 0x60000+64代表的是内核起始地址
第二个参数:0x30008000代表是在SDRAM中内核的运行起始地址
第三个参数:0x200000 代表从nandflash拷贝2M(内核大小一般小于2M)大小程序到SDRAM中。
3)启动内核。
根据分析u-boot源程序可知;启动内核的过程就是把内核在SDRAM的连接地址赋值给一个能启动内核的函数指针;然后通过调用此函数指针把机器类型ID(S3C2440的ID为362)和启动参数在SDRAM中的起始基地址(0x30000100)传递给内核[4]。
调用内核具体代码:
void (*theKernel)(int zero, int arch, unsigned int params);/*声明启动内核的函数指针*/
theKernel = (void (*)(int, int, unsigned int))0x30008000;/*把内核在SDRAM中起始地址赋值给函数指针*/
puts("Boot kernel ");/*打印提示信息*/
theKernel(0, 362, 0x30000100); /*启动内核*/
puts("error ");/*如果内核启动成功,就不会执行这一句,否则会打印出error:表示没有成功启动内核*/
4 实验结果与分析
为了测试设计的Bootloader是否具备稳定和可靠特点,需要在嵌入式S3C2440平台多次测试是否能启动内核,并分析Bootloader是否具备精简性。
4.1 Bootloader精简性分析
在编写Bootloader的start.S文件中定义一个全局变量来计算Bootloader大小,具体代码如下:
.globl _boot_size
_boot_size:
.word __bss_start - _start反汇编结果显示如下:
33f80000 <_boot_size>:
33f80000: 00000730 andeq r0, r0, r0, lsr r7
则代码大小为_boot_size地址处值:0x0000730=1.79 KB,所以远远小于u-boot.bin的432 KB, 节省大量的存储空间,说明设计的Bootloader具有精简性。
4.2 Bootloader稳定和可靠性分析
把编译后的Bootloader下载到nandflash中,经过多次按下复位键后都能成功启动linux内核。图6显示启动结果:
图6 启动结果
5 结束语
通过实验可知,编写的Bootloader比u-boot源码简洁,代码量小,并且成功加载linux内核,通过借鉴u-boot源代码的流程,以S3C2440为平台,设计出了精简,稳定的Bootloader。该设计方法具有一定通用性,对于其他不同平台,具有很强借鉴性。
[1]刘 坤,韩朝智. 浅析基于ARM嵌入式开发的BootLoader设计及其实现[J]. 电子技术与软件工程,2016(2):203-204.
[2] 范展源,六 韬.深度实践嵌入式Linux系统移植[M]北京:机械工业出版社,2015.
[3] 韦东山.嵌入式linux应用开发[M],北京:人民邮电出版社,2008.
[4] 戚长城,等.总线式ECU两级Bootloader的设计与实现[J]. 计算机工程,2015(7):95-99.
Development and Implement of Bootloader Based on S3C2440 and Embedded Linux System
Fan Panguo, Xing Baohao , Mi Xiaoliang, Yu Shubao,Wang Ting
(College of Automation,Northwestern Polytechnical University,Xi’an 710129,China)
Bootloader is the important part of embedded system. For the different hardware platforms, Bootloader is different. So bootloader is the difficult for the developmemt of embedded system. This paper mainly analyzes the design of embedded S3C2440 system and the process of u-boot startup kernel, drawing two essential phases of u-boot startup kernel, the first phase is to use assembler language to write some functions of the specific hardware platforms ,The second phase is to use C language to write codes about some complicated functions and loading the kernel.For example,Loading the linux-2.6.22.6,according to the two stages work of u-boot startup kernel. Designing of streamlined Bootloader is suitable for the S3C2440 embedded system.Experiment shows that the design of Bootloader successfully starts the linux kernel ,and has a good stability,reliability and simplicity.
design system; analyze u-boot; realize Bootloader;load kernel
2016-03-30;
2016-04-18。
范蟠果(1960-),男,陕西西安人,硕士生导师,副教授,主要从事计算机测控方向的研究。
1671-4598(2016)09-0012-03
10.16526/j.cnki.11-4762/tp.2016.09.004
TP273
A