今天可以说是很开心的一天,因为我终于完成了从一个基础内核到 ARM嵌入式内核的学习和移植工作,加上自己的uboot和根文件系统,呵呵。虽然是菜鸟的成果,但我依旧很开心,从我发表那一篇ARM_Linux 项目拉力正式开始的一周内,我还算是有了一个好的开始了,我做到了,呵呵。 我的开发环境是VMware 虚拟机 Ubuntu10.10 ,飞凌OK6410开发板,最终移植的内核为Linux-2.6.34.11。板子最终的实现性能为,启动Uboot,加载内核,挂载NFS 主机上的根文件系统,并成功测试和加载OK6410 的LED 灯的驱动,运行LED 灯测试模块,驱动正常工作。当然在这几天完成的都是疯狂的参考网友们的资料,其中也有很多CSDN的博友。写这篇博文,不是为了详细的叙述ARM Linux移植的技术点,就是问了将我们在移植过程中出现的对应的问题,来寻找对应的博客。
第一、先来说说Uboot。
因为是参考飞凌提供的Uboot来 自己制作uboot 的,所以做起来能够简单一些, 飞凌的uboot是 仿照三星 smdk6410 这个板子主体来做的。而我们现在到的通用Uboot,里面就有这一个函数体,Makefile 里有这一项smdk6410_config : uconfig
@$(MKCONFIG) $(@:_config=) arm s3c64xx smdk6410 samsung s3c6410
如果你需要改成自己的板载文件的话,也可以借鉴百度文库“我的arm_linux移植笔记.pdf”这篇博文,不过如果你也是飞凌的OK6410,或者其他ARM11,系列开发板的话,我这人不建议你去修改这一项,因为,飞凌无论是 uboot 还是后面的内核(飞凌提供的内核有 2.6.28 和2.6.36.2) 都是依赖于smdk6410,后面我会写到。 移植Uboot的 另外需要修改的文件就是include文件,在一些通用的uboot 在 include/configs/ 下面会有一个smdk6410.h 的文件。 有关于 ./cpu/samsung/smdk6410/start.S的修改,你都可以参考一下网友的资源,其实也可以直接下载一些网友已经制作好了的开发板对应的uboot,来使用,或者学习他的源码,就可以事半功倍。所以我在详细描述了,
第二、根文件系统的制作
根文件系统预备知识
嵌入式Linux中都需要构建根文件系统,构建根文件系统的规则在FHS(Filesystem Hierarchy Standard)文档中,下面是根文件系统顶层目录。
目录 内容
bin 存放所有用户都可以使用的、基本的命令。
sbin 存放的是基本的系统命令,它们用于启动系统、修复系统等。
usr 里面存放的是共享、只读的程序和数据。
proc 这是个空目录,常作为proc文件系统的挂载点。
dev 该目录存放设备文件和其它特殊文件。
etc 存放系统配置文件,包括启动文件。
lib 存放共享库和可加载块(即驱动程序),共享库用于启动系统、运行根文件系统中的可执行程序。
boot 引导加载程序使用的静态文件
home 用户主目录,包括供服务账号锁使用的主目录,如FTP
mnt 用于临时挂接某个文件系统的挂接点,通常是空目录。也可以在里面创建空的子目录。
opt 给主机额外安装软件所摆放的目录。
root root用户的主目录
tmp 存放临时文件,通常是空目录。
var 存放可变的数据。
2.2、构建根文件按系统
2.2.1、建立根文件系统目录
进 入到/opt/studyarm目录,新建建立根文件系统目录的脚本文件create_rootfs_bash,使用命令chmod +x create_rootfs_bash改变文件的可执行权限,./create_rootfs_bash运行脚本,就完成了根文件系统目录的创建。
#!/bin/sh
echo "------Create rootfs directons start...--------"
mkdir rootfs
cd rootfs
echo "--------Create root,dev....----------"
mkdir root dev etc boot tmp var sys proc lib mnt home
mkdir etc/init.d etc/rc.d etc/sysconfig
mkdir usr/sbin usr/bin usr/lib usr/modules
echo "make node in dev/console dev/null"
mknod -m 600 dev/console c 5 1
mknod -m 600 dev/null c 1 3
mkdir mnt/etc mnt/jffs2 mnt/yaffs mnt/data mnt/temp
mkdir var/lib var/lock var/run var/tmp
chmod 1777 tmp
chmod 1777 var/tmp
echo "-------make direction done---------"
改变了tmp目录的使用权,让它开启 sticky位,为tmp目录的使用权开启此位,可确保tmp目录底下建立的文件,只有建立它的用户有权删除。尽管嵌入式系统多半是单用户,不过有些嵌入 式应用不一定用root的权限来执行,因此需要遵照根文件系统权限位的基本规定来设计。
2.2.2、建立动态链接库
动态链接库直接用友善之臂的,先解压友善之臂的根文件包,拷贝lib的内容到新建的根文件目录lib内。
cd /mnt/hgfs/share
tar –zxvf root_qtopia.tgz –C /opt/studyarm
cp –rfd /opt/studyarm/root_qtopia/lib/* /opt/studyarm/rootfs/lib/*
2.2.3 交叉编译Bosybox
Bosybox是一个遵循GPL v2协议的开源项目,它在编写过程总对文件大小进行优化,并考虑了系统资源有限(比如内存等)的情况,使用Busybox可以自动生成根文件系统所需的bin、sbin、usr目录和linuxrc文件。
1、解压busybox
cd /mnt/hgfs/share
tar –zxvf busybox-1.13.3.tar.tgz –C /opt/studyarm
2、进入源码,修改Makefile文件:
cd /opt/studyarm/busybox-1.13.3
修改:
CROSS_COMPILE ?=arm-linux- //第164行
ARCH ?=arm //第189行
3、配置busybox
输入make menuconfig进行配置
(1)、Busybox Settings--->
General Configuration--->
[*] Show verbose applet usage messages
[*] Store applet usage messages in compressed form
[*] Support –install [-s] to install applet links at runtime
[*] Enable locale support(system needs locale for this to work)
[*] Support for –long-options
[*] Use the devpts filesystem for unix98 PTYs
[*] Support writing pidfiles
[*] Runtime SUID/SGID configuration via /etc/busybox.config
[*] Suppress warning message if /etc/busybox.conf is not readable
Build Options--->
[*] Build BusyBox as a static binary(no shared libs)
[*] Build with Large File Support(for accessing files>2GB)
Installation Options->
[]Don’t use /usr
Applets links (as soft-links) --->
(./_install) BusyBox installation prefix
Busybox Library Tuning --->
(6)Minimum password legth
(2)MD5:Trade Bytes for Speed
[*]Fsater /proc scanning code(+100bytes)
[*]Command line editing
(1024)Maximum length of input
[*] vi-style line editing commands
(15) History size
[*] History saving
[*] Tab completion
[*]Fancy shell prompts
(4) Copy buffer size ,in kilobytes
[*]Use ioctl names rather than hex values in error messages
[*]Support infiniband HW
(2)、Linux Module Utilities--->
(/lib/modules)Default directory containing modules
(modules.dep)Default name of modules.dep
[*] insmod
[*] rmmod
[*] lsmod
[*] modprobe
-----options common to multiple modutils
[ ] support version 2.2/2.4 Linux kernels
[*]Support tainted module checking with new kernels
[*]Support for module .aliases file
[*] support for modules.symbols file
(3)、在busybox中配置对dev下设备类型的支持
dev的创建有三种方法:
手动创建:在制作根文件系统的时候,就在dev目录下创建好要使用的设备文件,系统挂接根文件系统后,就可以使用dev目录下的设备文件了。
使用devfs文件系统:这种方法已经过时,具有不确定的设备映射、没有足够的主/次设备号、devfs消耗大量的内存。
udev:它是个用户程序,能根据系统中硬件设备的状态动态的更新设备文件,包括设备文件的创建、删除等。它的操作相对复杂,但灵活性很高
mdev 是busybox自带的一个简化版的udev,适合于嵌入式的应用埸合。其具有使用简单的特点。它的作用,就是在系统启动和热插拔或动态加载驱动程序时, 自动产生驱动程序所需的节点文件。在以busybox为基础构建嵌入式linux的根文件系统时,使用它是最优的选择。下面的选项将增加对mdev的支 持。
Linux System Utilities --->
[*]Support /etc/mdev.conf
[*]Support command execution at device addition/removal
4、 编译busybox
编译busybox到指定目录:
cd /opt/studyarm/busybox-1.13.3
make CONFIG_PREFIX=/opt/studyarm/rootfs install
在rootfs目录下会生成目录bin、sbin、usr和文件linuxrc的内容。
2.2.4 建立etc目录下的配置文件
1、etc/mdev.conf文件,内容为空。
2、拷贝主机etc目录下的passwd、group、shadow文件到rootfs/etc目录下。
3、etc/sysconfig目录下新建文件HOSTNAME,内容为”MrFeng”。
4、etc/inittab文件:
#etc/inittab
::sysinit:/etc/init.d/rcS
::askfirst:-/bin/sh
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/umount -a –r
5、etc/init.d/rcS文件:
#!/bin/sh
PATH=/sbin:/bin:/usr/sbin:/usr/bin
runlevel=S
prevlevel=N
umask 022
export PATH runlevel prevlevel
echo "----------munt all----------------"
mount -a
echo /sbin/mdev>/proc/sys/kernel/hotplug
mdev -s
echo "***********************************************"
echo "****************Studying ARM*********************"
echo "Kernel version:linux-2.6.29.1"
echo "Student:Feng dong rui"
echo "Date:2009.07.15"
echo "***********************************************"
/bin/hostname -F /etc/sysconfig/HOSTNAME
使用以下命令改变rcS的执行权限:
Chmod +x rcS
6、etc/fstab文件:
#device mount-point type option dump fsck order
proc /proc proc defaults 0 0
none /tmp ramfs defaults 0 0
sysfs /sys sysfs defaults 0 0
mdev /dev ramfs defaults 0 0
7、 etc/profile文件:
#Ash profile
#vim:syntax=sh
#No core file by defaults
#ulimit -S -c 0>/dev/null 2>&1
USER="id -un"
LOGNAME=$USER
PS1='[\u@\h=W]#'
PATH=$PATH
HOSTNAME='/bin/hostname'
export USER LOGNAME PS1 PATH
2.2.5 制作根文件系统映像文件
使用以下命令安装好yaffs文件系统制作工具:
cd /mnt/hgfs/share
tar –zxvf mkyaffs2image.tgz –C /
在/opt/studyarm目录下,使用命令mkyaffs2image rootfs rootfs.img生成根文件系统映像文件。
更详细的步骤,可以参考http://www.cnblogs.com/hnrainll/archive/2011/06/09/2076214.html 这篇博文,我就是按照这个来操作的,最后可以从NFS 挂载启动跟文件系统。
第三、Linux 内核的编译和移植。
这一个是重点,因为网上的大多数资源是ARM9 的,而且因为内核版本上的差异问题自己很难发现,所以自己在做这一块费了很大的功夫,如果你需要在内核中添加一些小的驱动程序来测试一下自己的内核,可以参考一下http://blog.csdn.net/acanoe/article/details/7464752
make menuconfig 后加载你自己需要的驱动配置就可以了。 我自己在学习Linux内核移植,是参考网络资源和飞凌提供的ARM Linux2.6.28 源码做的,这些都可以在网上下载到。我要写的是在移植过程中出现的一些问题和解决办法,
1、需要修改nandflash 的分区信息
在 arch/arm/mach-s3c64xx/mach-smdk6410.c 中,这些信息我在飞凌提供的内核中找了好久都没有找到,最后才发现飞凌将这些硬件信息都进行了模块化的管理,在linux2.6.28 内核中这些信息放在了 ./arch/arm/plat-s3c/include/plat 中,而我们要使用的nandflash 分区信息 在partition.h。而在他提供的Linux2.6.36.11 将这些信息放在了 ./arch/arm/mach-s3c64xx/include/mach 中。 中飞凌将这些信息统一目录,然后系统分开调用,也是项目的需要,但这里却极大的增加了我们的开发难度,因为我们不可能一次就能高明白这里调用联系。如果我们需要加载LED 驱动的话就需要这里的 gpio 信息,如果你在编译的时候缺少那个gpio的信息头文件,就可以在这里找到。下面是mach-smdk6410.c 中添加 nandflash 分区信息的修改方法,其中蓝色字体的部分都是需要修改的部分。
添加头文件#include <plat/nand.h>#include <linux/mtd/partitions.h>#include <mtd/mtd-abi.h>#include <asm/mach/flash.h>struct mtd_partition s3c_partition_info[] = {
{
.name= "Bootloader",
.offset= 0,
.size= (256*SZ_1K),
.mask_flags=MTD_CAP_NANDFLASH,
},
{
.name= "Kernel",
.offset= (256*SZ_1K),
.size= (4*SZ_1M) - (256*SZ_1K),
.mask_flags= MTD_CAP_NANDFLASH,
},
#if defined(CONFIG_SPLIT_ROOT_FILESYSTEM)
{
.name= "Rootfs",
.offset= (4*SZ_1M),
.size= (80*SZ_1M),//
},
#endif
{
.name= "File System",
.offset= MTDPART_OFS_APPEND,
.size= MTDPART_SIZ_FULL,
}
};
static struct s3c2410_nand_set s3c_nandset[]={
[0]={
.name="s3c24xx-nand",
.nr_chips= 1,
.nr_partitions=ARRAY_SIZE(s3c_partition_info),
.partitions=s3c_partition_info,
}
};
static struct s3c2410_platform_nand s3c_platform={
.tacls =25,
.twrph0 =55,
.sets = &s3c_nandset,
.nr_sets =ARRAY_SIZE(s3c_nandset),};
//add here…
static struct platform_device *smdk6410_devices[] __initdata = {
#ifdef CONFIG_SMDK6410_SD_CH0
&s3c_device_hsmmc0,
#endif
#ifdef CONFIG_SMDK6410_SD_CH1
&s3c_device_hsmmc1,
#endif
&s3c_device_i2c0,
&s3c_device_i2c1,
&s3c_device_fb,
&s3c_device_ohci,
&s3c_device_usb_hsotg,
&s3c64xx_device_iisv4,
//add here
&s3c_device_nand,
//add here…
}
static void __init smdk6410_map_io(void){
u32 tmp;
//add here
s3c_device_nand.name = "s3c6410-nand";
//add here…
…
}
static void __init smdk6410_machine_init(void){
u32 cs1;
s3c_i2c0_set_platdata(NULL);
s3c_i2c1_set_platdata(NULL);
s3c_fb_set_platdata(&smdk6410_lcd_pdata);
//add here
s3c_nand_set_platdata(&s3c_platform);//
//add here…
}
详细的nand flash 修改方法你可以参考一下 。http://hi.baidu.com/mountmeng1990/blog/item/0edd39e920980afdcf1b3e20.html
1.1 关闭NAND。 ECC 校验。ECC是“Error Correcting Code”的简写,中文名称是“错误检查和纠正”。
在文件 drivers/mtd/nand/s3c_nand.c 文件中,将
nand->ecc.mode = NAND_ECC_SOFT; 改为:
nand->ecc.mode = NAND_ECC_NONE;
配置中去掉的这个选项在代码中并没有完全去掉,只是去掉了硬件校验的方式,换成了软件校验。只有在代码中给改成NAND_ECC_NONE,才不会校验,但是这样是不提倡的。
2、 给内核打yaffs2文件系统的补丁 如果你需要就打上。
第一步,我们需要YAFFS2的源码,如果大家有兴趣想了解YAFFS2文件系统的驱动原理的话可以直接去访问他们的官网,地址是www.yaffs2.net,上面说的很详细的,也有源码下载地址,怕大家进去后难找,直接贴出YAFFS2的最新源码地址吧http://www.aleph1.co.uk/gitweb?p=yaffs2.git;a=summary,直接点击,点那个2011年6月28号的那个版本,点击最右边的SNAPSHOT下载,这个是目前最新的YAFFS2的源码了
第二步,将下载下来的YAFFS2最新源码YAFFS2.tar.gz放入你LINUX的任意一个目录下,执行 tar zxvf YAFFS2.tar.gz然后进入YAFFS2源码包,由于最新的源码再执行脚本 patch-ker.sh时 有四个参数,可以追加:c,l,m,s,
./patch-ker.sh c/l m/s kernelpath
if c/l is c, then copy. If l then link
if m/s is m, then use multi version code. If s then use single version code
上面这段英文应该很好懂吧,简而言之,C 是复制文件,L是链接文件,M是多种,S是单个的,
那么在这里,我们只需执行 ./patch-ker.sh c m /your linux2.6.39.2 kernel directory
将yaffs2源码目录下的*.c *.h文件复制到内核fs/yaffs2目录下.打完补丁要将yaffs2的源码拷贝到fs目录下,要不然编译的时候会出现yaffs_fs.c 的报错信息。
配置支持 yaffs2 文件系统。
Filesystem--->
Miscellaneous filesystems--->
<*>YAFFS2 file system support
[*] Lets Yaffs do its own ECC
Native language support
<*> Codepage 437 (United States,Canada)
<*>Simplified Chinese charset(GB2312)
<*>Traditional Chinese charset(Big5)
<*>NLS ISO 8859-1(Latin1:Western European Languages)
<*>NLS UTF-8
3、修改的机器号
网上很多关于arm9 的都要这样做,这样做的原理是,在uboot启动的时候会传来一个机器号,如果的内核的机器号和uboot的传来的相同才能启动,而如果不是就不可以启动。 为什么我在写Uboot 时强调如果你是OK6410的板子,后者说是按照飞凌源码来学习的话,不要修改刚才的smdk6410 的参数,因为飞凌uboot 和 他们自己的内核使用这个smdk6410 的机器码来传递。 所以加入你上面的uboot没有修改的话,这里也不需要修改。
4、make menuconfig 配置内核。
我是将飞凌提供的Linux2.6.36.11 内核下的.config 直接copy 到我的Linux2.6.34.11 根目录下的。这时 你在make menuconfig 时 要注意去掉去除部分:
1) Device Drivers ----》Graphics support ---> Console display driver support ---> [ ] VGA text console 这一项。不然会报错为drivers/video/console/vgacon.c:486: error: 'PCIMEM_BASE' undeclared (first use in this function) 。。这一项应该是ARM11 视频VGA输出的驱动,这里我们不需要就去掉。
2) 在 Device Driver -----》 Generic Driver Oprios有一个(sd8686.bin sd8686_helper.bin) 的是飞凌提供的wifi 模块需要加载的固件 ,如果你需要的话 就将他提供的内核中的这两个硬件拷贝的2.6.34 对应的目录就行。可以使用 命令 find -name sd8686.bin ./ 找到它们。
5 、编译 make zImage
编译好了,以后通过OK6410 USB的下载方式,先将Uboot下载到开发板,在下载你自己编译好的内核到开发板,设置 为关在NFS 根文件系统。 看看你的内核成功启动了没。如果没有成功启动,这里可能的原因有。
1) 假如只显示Starting kernel ... 就不动了的话。可能原因是你编译的内核太大了,超过了3.4M 了 ,这里解决的办法,是在使用dnw 串口控制下载的时候,你擦除一个较大的空间 。我擦除了 100000 500000 。使用命令nand erase 100000 500000 。 然后下载到nandflash 使用命令nand write.e 50008000 100000 500000。 (注意上面的命令只对应飞凌的uboot,其他的uboot我没测试过)。
2) 当只出现Uncompressing Linux... done,booting the kernel 然后停下来不动时。可能出现的原因可以参考一下这篇博文:http://blog.chinaunix.net/space.php?uid=20543672&do=blog&id=129729
当也可能是你第二次启动错误,或者下载错误,也可以重新下载一下zImage 尝试一下。3) 当出现drivers/rtc/hctosys.c: unable to open rtc device(rtc0) 可以参考这篇博文
http://blog.csdn.net/mirkerson/article/details/6731969因为Linux2.6.36 的内核已经支持,ARM11 s3c6410 的RTC实时时钟,如果你是用的是2.6.36的内核 ,在配置的时只要 注意--- Real Time Clock
[*] Set system time from RTC on startup and resume
(rtc0) RTC used to set the system time
[ ] RTC debug support
*** RTC interfaces ***
[*] /sys/class/rtc/rtcN (sysfs)
[*] /dev/rtcN (character devices)
[ ] RTC UIE emulation on dev interface
*** on-CPU RTC drivers ***
<*> Samsung S3C series SoC RTC 这几个配置选项就可以了。
但是我在参考这篇博文的时候却发现我的内核 在make menuconfig 以后没有
*** on-CPU RTC drivers ***
<*> Samsung S3C series SoC RTC
这一项,这也是大多2410、 或者ARM9 开发板的博客没有提到的,在我编译时就没有Samsung S3C series SoC RTC 这一项,怎么办呢,我将飞凌提供的2.6.36 make menuconfig 了一下,发现有这一项。我才认识到这个能是内核差异造成的。我同时在两个根目录下搜素 on-CPU 发现在 drivers/rtc/Kconfig 中有这个关键的字符串 然后搜索Samsung 找到在Linux2.6.34 中 config RTC depend on ARCH_S3C24XX 而在Linux2.6.36中 depend on ARCH_S3C24XX || ARCH_S3C64XX 。 才知道Linux2.6.34 的内核中RTC时钟不支持ARM11 。现在要做的就是让它支持。这里你可以选择给内核打ARM 自己的补丁, 当时我们有。我将2.6.36的 RTC 文件目录 全部拷贝到对应的 2.6.34 下 当然 这是危险的,呵呵,不过我还是这样做了,因为打补丁可能会破环你先前的所有工作,所以还是选择这种方式修改。 不过在make zImage 的时候问题又来了,检测到在在rtc-s3c.c 文件中有一个宏变量S3C2410_INTP之前没有定义,
然后使用 grep -r [变量名] ./ 在2.6.36根目录下搜索所有有个变量名字符串的文件,找到该宏在regs-rtc.h 中定义的,将其拷贝过去,就可以编译通过了,其实这已经实在移植ARM11 的RTC Soc了,呵呵这可能是个笨办法,但绝对是个可以很快就绝问题的办法,我的很多问题都是通过 用 grep 这个命令去解决的。
到这里我编译好内核以后下载到开发板上就可以正常的进入文字终端了,如果你还没有成功,一定要细细找错误,不要着急。
博文大多借鉴了网友们的资料,在这一也就一并谢过了。