曾福振,闵联营
(武汉理工大学 计算机科学与技术学院,湖北 武汉430063)
进入后PC时代以来,伴随着设计和制造技术的发展,集成电路从当初的晶体管集成发展到现在的IP集成,即SoC(System on Chip)设计技术。促使嵌入式系统渗透到了当今社会中的各个行业,并且发挥越来越重要的作用。嵌入式系统一般可定义为以应用为中心、以计算机技术为基础、软硬件可裁剪、适用于应用系统且对功能、成本、体积、功耗有严格要求的专用计算机系统,它的主要特点是嵌入、应用[1]。
随着各种嵌入式设备功能越来越强大,在设备中使用嵌入式操作系统也成为必然。Linux操作系统具有开放源代码、易于移植、资源丰富、免费等特点,在嵌入式领域的地位越来越重要。嵌入式Linux和PC上的Linux是同一套内核代码,只是裁剪的程度不一样,所以,很多在PC上开发的软件,经过交叉编译后可以直接在嵌入式设备上运行。本文主要涉及到Bootloader移植和Linux-2.6.32.2内核的移植、根文件系统移植、在S3C2440平台上构建完整的嵌入式开发平台三个方面。
在进行嵌入式软件开发之前,必须要在PC上建立ARM的交叉编译环境。交叉编译就是在PC平台上生成可以在ARM平台上运行的代码。其中主要包括ARM的交叉编译器arm-elf-gcc和交叉连接器arm-elf-ld。本文采用的交叉编译器的版本是gcc-3.4.5-glibc-2.3.6[2]。交叉编译流程如图1所示。
图1 嵌入式系统交叉编译流程
BootLoader是一段在系统上电时开始执行的程序,用以初始化硬件设备,准备好软件环境,设置好启动参数,最后引导操作系统,与PC上的 BIOS程序相似。当前开放源码的Linux引导程序主要有x86架构的LILO、GRUB,对于ARM架构的主要有Vivi和U-Boot。本文使用 U-Boot作为引导程序。U-Boot(Universal Boot Loader),即通用的BootLoader,遵循GPL条款开放源代码。U-Boot相对于Vivi功能更加强大,也更方便后续程序的调试。
BootLoader的启动一般分为两个阶段,第一阶段的代码主要是用汇编语言编写,主要的功能是完成硬件设备的初始化,为加载第二阶段的代码准备RAM空间,设置好堆栈;第二阶段主要用C语言编写,检测内存映射,将内核映像和根文件系统从Nand Flash读到RAM中,为内核启动设置参数,引导内核。
U-Boot的源代码可以从ftp://ftp.denx.de/pub/u-boot/进行下载,本文使用的U-Boot版本是U-Boot2009.08。
移植U-Boot的关键步骤如下:
(1)首先,将 include/configs目录下的 smdk2410.h复制并改名为mini2440.h,根据U-Boot的说明可以知道,如果要使用开发板board/
(2)本文使用的U-Boot是从Nand Flash启动的,CPU可以直接访问Nand Flash中前4 KB代码,利用这4 KB代码把U-Boot中绝大部分代码拷贝到内存中[3]。其中下面的代码就是调用C语言中的Nand Flash的读写函数,该函数主要把Nand Flash中4 KB以后的代码复制到RAM中。在编写nand_read_ll的函数时,注意参考Nand Flash的数据手册,对大页和小页的Nand Flash,其读写的命令和时序是不同的。
由于在后面加载Linux内核和根文件系统时,使用的是tftp方式,所以必须添加DM9000EP网卡的驱动。在mini2440.h文件中,其主要的配置如下:
其中,CONFIG_DM9000_BASE宏是最重要的,因为它定义的是网卡的地址,不同的网卡有不同的地址,DM9000EP访问的基址为0x20000000,之所以再偏移0x300是由它的特性决定的。
(3)要正确引导Linux内核,还需要配置下面几个重要的宏定义,这几个宏定义不同,意味着引导Linux内核的方式也不同。
其中,root=/dev/mtdblock3是由Linux中的Nand Flash分区所决定的,意味着Nand Flash的第4个分区为根文件系统。
这个宏定义是将Nand Flash中0x60000-0x560000(和kernel分区一致)的内容读到内存 0x32000000中,然后用bootm命令来执行。
要正常地引导Linux内核,必须要具备如下几个条件
[4]:
(1)CPU寄存器
R0=0;
R1=机器类型ID;对于 ARM结构的CPU,其机器类型 ID 在 linux/arch/arm/tools/mach-types;
R2=启动参数标记列表在RAM中起始基地址。
(2)CPU工作模式
必须禁止中断(IRQs和 FIQs);
CPU必须为SVC模式。
(3)Cach和MMU的设置
MMU必须关闭;
指令Cach可以打开也可以关闭;
数据Cach必须关闭。
Linux内核的更新很快,可以从http://www.kernel.org/pub/linux/kernel/得到最新的Linux内核版本,本文使用的Linux内核版本是Linux-2.6.32.2,交叉编译工具使用符合EABI标准的arm-linux-gcc-4.3.2。
可以在内核的根目录下,运行make menuconfig命令,对内核进行适当的裁剪,以适应硬件平台。
(1)修改 Makefile文件
欲设置Linux的默认平台为ARM平台,需进入Linux-2.6.32文件夹中,修改此目录下的Makefile文件。
(2)关于机器码
在启动内核时,根据BootLoader传入的机器码(MACH_TYPE)来决定应启动哪种目标平台[6],本开发平台的机器码为1999。机器码存放在文件opt/kernel/linux-2.6.32.2/arch/arm/tools/mach-types中。
如果机器码不匹配,引导内核不成功,则会出现如下的错误提示:
(3)修改时钟源
将/kernel/linux-2.6.32.2/arch/arm/mach-s3c2440/目录下的 mach-smdk2440.c文件改名为 mach-mini2440.c。因为mini2440和mach-smdk2440.c极其相似,以该文件为基础进行修改,在mach-mini2440.c文件中将static void__init smdk2440_map_io(void)函数中的晶振频率修改为mini2440开发板上实际使用的12000000。
(4)为内核打上 yaffs2补丁
①Yaffs2文件系统是专门针对嵌入式设备,特别是使用Nand Flash作为存储器的嵌入式设备而创建的一种文件系统,使用yaffs2就可以支持大页的Nand Flash。进入 yaffs2源代码目录执行如下命令:
②配置内核以支持Yaffs2文件系统
在 Linux内核源代码根目录运行 make xconfig,在“File Systems”选 项 中 ,找 到 “Miscellaneous filesystems”菜单项,找到“YAFFS2 file system support”并选中它,这样就在内核中添加了yaffs2文件系统的支持,保存并退出。然后在命令行中,执行make zImage。
(5)修改 Nand Flash分区信息
①在mach-mini2440.c文件中添加Nand Flash的分区信息,下面的代码将Nand Flash分成了4个分区,第1分区也是 BootLoader所在的分区,对应 dev/mtdblock0;第2个分区是 U-Boot的参数分区,对应 dev/mtdblock1;第 3个分区是内核分区,对应dev/mtdblock2;第 4个分区为根文件系统分区对应dev/mtdblock3。分区结构图如表1所示。
表1 128 MB Nand Flash的分区结构图
其部分实现代码如下:
其中 name是分区的名字,offset是偏移的开始地址,size是分区的大小,其余部分的分区与此类似。
②下面代码是添加Nand Flash的设置表,因为板子上只有一片Nand Flash,因此也就只有一个设置表。
③上面的设置完成后,还需要将Nand Flash设备注册到系统中。下面这段代码就是将Nand Flash设备添加到开发板的设备列表结构。
现在可以进入kernel/linux-2.6.32.2/arch/arm/boot目录,然后执行下面的命令,就会在该目录下生成uImage.img格式的、U-Boot可以引导的内核镜象。
至此,可以把生成的uImage.img格式的镜像文件复制到tftp目录下,使用tftp进行下载。
所谓根文件系统,就是创建各个目录,例如在/bin、/sbin/目录下存放各种可执行的程序,在/etc目录下存放配置文件,在/lib目录下存放库文件。
可以利用Busybox工具创建根文件系统,Bosybox是一个遵循 GPL v2协议的开源项目,它在编写过程中对文件大小进行优化,并考虑了系统资源有限(例如内存)的情况,使用 Busybox可以自动生成根文件系统所需的bin、sbin、usr目录和 linuxrc文件,可以使用 make menuconfig对Busybox的选项进行配置。
(1)进入 opt/kernel,创建一个 shell脚本用于构建根文件系统的各个目录,并且为其增加执行权限;
(2)Linux中的init进程会根据etc/inittab文件创建其他子进程,下面代码是inittab文件中的内容,说明了系统启动后首先执行的脚本文件是rcS,虚拟的终端是串口 0,当按下 ctr+alt+del时重启系统,inittab文件的作用就是控制系统启动时和启动后一些程序的运行。
使用yaffs源码提供的工具制作文件系统的映像文件。由于128 MB的Nand Flash是大页结构,所以需要使用相应的大页制作工具;使用命令mkyaffs2image rootfs rootfs.img生成根文件系统映像文件。
本文通过对U-Boot移植和Linux内核移植的讨论,给出了移植U-Boot和Linux到大多数开发板的关键部分。由于移植的复杂性,不可能包括全部步骤,但通过本文的阐述可以了解移植的基本流程和关键点,为移植不同版本到其他硬件平台提供了参考,也为应用程序的开发搭建了一个比较完整的嵌入式平台。
[1]韦东山.嵌入式 Linux应用开发完成手册[M].北京:人民邮电出版社,2008.
[2]孙琼.嵌入式Linux应用程序开发详解[M].北京:人民邮电出版社,2006.
[3]Samsung Electronics.S3C2440A 32-bit RISC microprocessor user′s manual[S].2004.
[4]RUSSELL K.ARM Linux kernel Boot requirements[EB/OL].[2002-03-18].http://www.arm.linux.org.uk/developer/booting.php.
[5]陈莉君.深入理解 Linux内核[M].北京:中国电力出版社,2007.
[6]JONATHAN C,ALESSANDRO R,GREG KROAH H.Linux
设备驱动程序[M].北京:中国电力出版社,2006.