基于S3C2440平台的Linux内核引导的过程实现
2016-08-11赵兴海衡友跃
赵兴海,衡友跃
(淮北职业技术学院 计算机科学技术系,安徽 淮北 235000)
基于S3C2440平台的Linux内核引导的过程实现
赵兴海,衡友跃
(淮北职业技术学院 计算机科学技术系,安徽 淮北235000)
摘要:u-boot是一种通用型的引导程序,u-boot引导Linux内核是一个复杂过程。在此主要介绍了Bootloader的作用和种类,并详细分析u-boot引导Linux内核的功能实现过程。
关键词:Bootloader; u-boot;Linux内核
1Bootloader简介
Bootloader是操作系统引导程序的总称,它里面包含有很多种操作系统引导程序。由于设备的硬件架构不同,有些是适合于某一种硬件架构的针对型的引导程序,有些是适合于多种硬件架构通用型的引导程序。Bootloader负责完成硬件设备的初始化,为软件环境做准备,最后把操作系统内核调入到指定位置。[1]这些是它最基本的功能,可以根据开发的需要或使用的需要对它的功能进行添加扩展,让它使用更加的方便高效,Bootloader对于Linux操作系统常见有LILO、GRUB、U-boot、Vivi等。LILO和GRUB常用于X86平台的台式机,Vivi是针对于SAMSUNG的arm架构的嵌入式设备,U-boot是一种通用的Linux内核,它可以用于包括X86架构、ARM架构在内的多种硬件架构。U-boot和Vivi比较而言,U-boot能够提供更多的下载方式和大量的命令,使用更加的广泛。Bootloader的作用主要是实现内核的引导、保存和传递Boot parameters(启动参数)给操作系统内核,如下图所示;
图1 Bootloader的作用和位置
2U-boot简介及启动过程
2.1U-boot简介
U-boot是一种通用型的引导程序,通用型不止表现在它适用于绝大多数的CPU架构,还体现在它能够支持多种操作系统的引导。U-boot的最主要功能就是把Linux内核进行加载和运行,由于U-boot和Linux内核无法同时运行,那么就需要通过U-boot来引导Linux内核以及把U-boot初始化得到的硬件信息传递给内核,让内核能够更快的加载硬件驱动运行起来。一个操作系统除了内核以外,还包括大量的驱动程序、编译语言、数据库和其它工具。[2]Linux内核由5个模块组成:网络接口模块、进程间通信模块、文件系统模块、内存管理模块和进程调度模块。[3]U-boot最核心的部分就是实现引导内核的命令。U-boot的目录里面含有二十多个子目录,主要分为四类,分类如下;
第一类为开发板相关的目录board子目录,里面包括各种支持类型的电路板类似于电脑的主板。
第二类为平台相关的目录。CPU目录里面包括了支持的不同CPU架构子目录。
Lib-i386(CPU架构类型)的目录里面包含了每一种架构下通用的库文件。
第三类为通用的函数类型目录。inlcude目录里面包括头文件和常用的配置文件。common目录里面是通用的函数,多是对下一层驱动程序的封装。
第四类为通用的设备驱动程序目录。disk目录里面主要是硬盘接口相关的程序。Drivers目录里面包含了各种设备的驱动程序。Dtt目录里面是数字稳定测量器或传感器的驱动。Fs目录是文件系统相关的实现目录。Nand-spl目录保存的是支持U-boot从NAND Flash启动的代码文件。Net目录保存的是各种网络协议代码文件。Post目录保存的是机器上电自检程序文件。Rtc目录保存的是实时时钟的驱动文件目录。
2.2U-boot的启动过程
U-boot的启动过程可分为单阶段和多阶段两种方式,多阶段是为了让U-boot能够提供更多的功能。这里针对S3C2440开发板使用的是多阶段的方式,使用了两阶段的方式实现启动过程。
第一个阶段使用的是start.s汇编代码实现,内容如下:
a.实现了开发板CPU体系包括看门狗、Interrupt 设置、处理器时钟频率、加载操作系统使用的RAM空间等)
b.为加载第二阶段的U-boot代码准备RAM空间
c.复制第二阶段的U-boot代码到RAM空间
d.为第二阶段代码(C语言为主)的运行设置好栈
e.跳转到第二阶段代码的入口点。
第二阶段的代码主要实现的功能包括:
a.初始化本阶段要使用的硬件设备
b.检测系统内存映射
c.将根文件系统和内核镜像从FLASH设备复制到RAM空间
d.为内核配置启动选项
e.调用内核使内核运行起来。
3U-boot的主要功能实现
U-boot引导Linux内核功能主要有两个部分,一个是引导内核的命令,一个是传递内核启动参数。
3.1U-boot引导内核的命令功能实现
U-Boot的命令包括很多,包括下载文件的命令,帮助命令help、内存操作、命令、NOR Flash操作命令、NAND Flash操作命令、环境变量命令、启动命令等。以启动命令为例阐述实现步骤。
首先在include/command.h文件中找到宏U-BOOT-CMD的定义。定义如下;
#define U-BOOT-k_CMD(k_name,k_maxargs,k_rep,k_cmd,k_usage,k_help)
Cmd_tbl_t __u_boot_k_cmd_##k_name Struct_Section = {#k_name,k_maxargs,k_rep,k_cmd,k_usage,k_help}
Struct_Section定义如下;
#define Struct_Section _attribute_(unused,section(“.u_boot_cmd”))
而在连接脚本U-BOOT.lds中有”.u_boot_cmd”的描述,如下;
__u_boot_cmd_start = .;
.u_boot_cmd : {*(.u_boot_cmd)}
__u_boot_cmd_end = .;
这里k_name为命令的字符串,k_maxargs为最大的参数个数,k_rep标识命令是否可重复,cmd为实现命令功能的函数指针,k_usage为简短的使用说明,k_help为详细的使用说明,_attribute_为函数具有的特殊属性,.u_boot_cmd表示连接脚本中设计的专用于保存命令的特殊段。
对于命令bootm,定义如下;
U_BOOT_CMD{
Bootm,CFG_MAXARGS,1,do_bootm,
“String1”,
“String2”,
}
宏U_BOOT_CMD的定义扩展开得到;
Cmd_tbl_t __u_boot_cmd_bootm _attribute__((unused,section(“.u_boot_cmd”)))
最后要为内核设置启动参数,在配置文件include/configs/smdk2440.h中增加如下两个配置项;
#define CONFIG_SETUP_MEMORY_TAGS
#define CONFIG_CMDLINE_TAG
通过lib_arm/armLinux.c中的do_bootm_Linux函数来启动内核。这个函数先设置标记列表,最后调用theKernel(0,bd→bi_arch_number,bd→bi_boot_params)函数来调用内核。
3.2U-boot为内核传递启动参数的实现
U-boot和内核不能同时运行,所以它们的传递方式是两者约定好保存启动参数的地方,U-boot通过把参数放到事先约定好的位置,接着内核启动,到指定的地方把参数读入内核。启动参数包括三个,分别是内核存放的地址、board_init函数设置的机器类型ID和标记列表的开始地址。
3.2.1内核存放的地址
2.1.5 方法 照薄层色谱法(《中国药典》2015年版四部通则0502)试验。吸取供试品溶液与对照药材溶液各6μL、对照品溶液2μL、阴性对照溶液2μL,分别点于同一高效硅胶G薄层板上,以石油醚(60℃~90℃)-丙酮(9∶2)作展开剂,展开,取出,晾干,喷以10%硫酸乙醇溶液,加热至斑点显色清晰。在供试品色谱中,在与对照药材色谱和对照品色谱相应的位置上以上各溶液分别显相同颜色的斑点。见图1。
内核存放的地址通过在连接脚步中定义。
3.2.2机器类型ID的参数bd→bi_arch_number
在 uboot/board/s3c2440/s3c2440.c的board_init函数中指定机器码为:
gd->bd->bi_arch_number = MACH_TYPE_S3C2440
而MACH_TYPE_S3C2440定义在include/asm-arm/mach-types.h
3.2.3标记列表的开始地址bd→bi_boot_params
a.在U_boot的保存形式
在U_boot中保存内核采用参数使用的是一种叫标记列表(tagged list)的格式进行保存,里面使用标记列表以ATAG_CORE开始,以标记ATAG_NONE结束,具体的定义在文件include/asm/setup.h头文件中。
Struct tag_header{
U32size;
U32 tag;
};
Structtag{
Struct tag_header hdr;
Union{
Struct tag_core core;
Structtag_mem32 mem;
……
}u;
b.内核使用的方式为bd→bi_boot_params,理解传递为内核函数参数形式需要理解两个结构体gd和bd,具体的定义如下:
gd结构体的定义在include/asm-arm/global_data.h文件中。
c.保存的方式和传递的方式如何关联
保存的时候使用的是指针paramas进行存放,而传递的时候使用的bg类型的结构体。那么就需要把它们关联起来。
它们都在lib_arm/armLinux.c文件中定义,如下
static void setup_start_tag (bd_t *bd)
{
params = (struct tag *) bd->bi_boot_params; /* 内核的参数的开始地址 */
……
}
4结论
u-boot引导Linux内核是一个复杂的过程,要使用到启动命令和启动参数的传递功能。通过详细分析u-boot引导Linux内核的功能实现过程让读者能够对u-boot与Linux内核的关系更加的清晰,也对u-boot的阶段启动有所了解。
参考文献:
[1]韦东山.嵌入式Linux应用开发完全手册[M].北京:人民邮电出版社,2008.
[2]宋宝华,等.精通Linux设备驱动程序开发[M].北京:人民邮电出版社,2010.
[3]赵炯.Linux内核完全注释[M].北京:机械工业出版社,2004.
责任编辑:净草
收稿日期:2016-06-30
基金项目:本文系安徽省高等学校省级质量工程项目“计算机应用技术专业综合改革试点”(编号:2013zy110)阶段性研究成果。
作者简介:赵兴海(1982-),男,安徽凤阳人,助教,研究放心为嵌入式系统工程。
中图分类号:TP368.1
文献标识码:A
文章编号:1671-8275(2016)04-0134-03