愚人的linux内核2440移植札记(超曲折版)

http://blog.csdn.net/dreambegin/article/details/6904822node


原来文章叫——编译内核之初体验。后来想了想,这篇文章让我体验了好多遍。不应叫这么大气的名字,仍是改了吧。中间记录了不少在内核移植中可能遇到的问题。linux

 

仍是把名字改成:愚人的内核移植札记(超曲折版)git

 

千呼万唤始出来,让咱们开始移植内核吧!web

环境:Ubuntu  +  EABI-4.3.3(就是前边配置好的环境)shell

内核版本:linux-2.6.34.10          /*这个版本有什么特殊意义?木有!恰好电脑上有它就拿来用了,还不知道里边有没有bug。*/ubuntu

 

何为内核移植?windows

       Linux内核移植就是对linux内核源码进行修改,使其能够移植到对应的平台上,并能够正常运行。实际上就是作一些必要的修改,并编译出一个能在开发板上跑起来的.bin的二进制文件。工具

 

开始:测试

一、 将源码放置到虚拟机中,你能够选择FTP上传、共享文件目录、经过VMwaretools的直接复制粘贴、甚至其余任何你想使用的方式(这些方法都在前边文章中有所介绍)ui

二、 解压源码,获得源码树(注意,这里的源码树千万不要和前边将linux内核编译模块时的源码树搞混了,你能够认为这个源码树就是一个开源裸板的应用程序的源代码目录)

/*个人工做目录依然是 “/home/jun/arm” */

$ tar –xjvf linux-2.6.34.10.tar.bz2    //由于在用户目录下,因此不用sudo

三、 在获得源码目录后,就能够对源码进行相应的修改,使之知足运行条件。

 

修改ing :

一、 第一个要修改的是Makefile。

源码根目录下的Makefile是整个内核工程的总Makefile文件,向下联系着每一个子目录中的Makefile。子目录中的Makefile又控制着所在目录的编译,并联系着它的下级目录,就这样循环往复。

Makefile文件控制着linux下工程开发中,整个项目的编译连接顺序和选项以及规则。也是一个自动编译工具,使得部分文件改动时只用对改动文件的进行编译,而不用再次编译链接整个工程。

默认状况下,源码根目录下的Makefile中定义着编译成的目标平台为x86,编译器选项为Gcc;而咱们的需求是ARM平台和arm-linux-gcc编译器。知道了咱们想要什么,就知道该改些什么了。

/*进入源码目录:*/

$ cd linux-2.6.34.10/

$ vi Makefile

在vi的底行名利模式中输入:set number 能够显示出行号来,而后,咱们找到第18九、190行:

        ARCH            ?= $(SUBARCH)

CROSS_COMPILE   ?=

       咱们把它改成:

              ARCH            ?=arm

        CROSS_COMPILE   ?=arm-linux-



一、 第二个要修改的是.config文件

源码根目录下的.config是整个内核工程的总配置单,向下联系着每一个子目录的Kconfig。子目录中的Kconfig又联系着它的下级目录,就这样循环往复。

刚解压出来的源码目录并无.config文件。咱们经过下面命令从内核已经包含的一些平台的配置文件中copy来一份。

$ cparch/arm/configs/s3c2410_defconfig .config

$ makemenuconfig     /*咱们来预览一下拷贝过来的配置单*/


Oops!报错了,哈哈,我喜欢,报错预示着能够学到新的知识。报错内容:

make: ***/home/jun/arm/linux-2.6.34.10/arch/arm: Is a directory.  Stop.

       哦,疏忽,这个错误比较常见,就是在上边配置Makefile时:

ARCH            ?= $(SUBARCH)

       这一行后边不当心加了有了空格了,改过来,try again。(在vi的底行模式:num 189,这样能够直接跳至189行)



Ok,预览成功。在这样的menu菜单里,就能够比较方便的配置内核了。(默认状况下Ubuntu下是不能够直接menuconfig的,不过咱们在前面构建内核源码树的文章中已经安装了相应的支持包,这里主要依赖的是ncurses包,参照Ubuntu+下构建内核源码树_图文教程刚开始安装的几个工具。)

由于咱们用的是EABI编译的,因此要在内核中加上EABI支持:

       在上述的配置菜单中配置:

       KernelFeatures  --->

                  Memory split (3G/1G user/kernel split)  --->                     | |

  ||        Preemption Model (No ForcedPreemption (Server))  --->           | |

  ||    [*] Use the ARM EABI to compile thekernel                           | |

  ||    [*]  Allow old ABI binaries to run with this kernel (EXPERIMENTAL) (| |

  ||    [ ] High Memory Support(EXPERIMENTAL)                              | |

  ||        Memory model (Flat Memory)  --->                                 | |

  ||    [ ] Enable KSM for page merging                                      | |

         (4096) Low address space to protect from userallocation             | |

  [ ] Use kernelmem{cpy,set}() for {copy_to,clear}_user() (EXPERIMENTA|

 

一、 第三要改的是晶振。

默认的晶振是16934400,而个人板子TQ2440的晶振是12000000,据我所知当前主流的2440板子的外部时钟晶振都是12000000吧,TQ2440、mini2440、精智2440的板子都是。

要改的在文件arch/arm/mach-s3c2440/mach-smdk2440.c  中的第163行。



 

OK,不出意外的话,如今的内核已经能够跑起来了,咱们make一下,下载到板子里边试试:

       $make zImage -j4

一番等待以后,个人编译经过了,在arch/arm/boot/ 下会生成适用于arm平台的压缩格式内核zImage。



我经过cp命令把它拷贝到虚拟机和我本机的共享目录中,这样,它已经在个人windows下了,大小1.92M,多么惊人的尺寸,这就见证了linux强悍的可移植性。

 

此时已经能够在开发板里跑起来了,可是能跑并不表明会跑,这样的结果是会摔跟头。那么咱们就边摔边跑吧,不经历风雨,怎么见彩虹?!

四、第四要改的是机器ID(板子的ID)。

将上边编译出来的zImage用bootloader(TQ2440自带的出场Uboot),经过dnw烧到nand flash里,而后boot the system:(让咱们看看它到底摔了什么跟头?)

出现:


这种问题一般是系统不认识uboot传过来的机器ID,要修改内核中的机器ID与uboot的一致,由于开启的过程是,先加载bootloader-----》bootloader准备好硬件信息(以及OS的运行环境)-----》OS检测传递过来的硬件信息(cpu信息、开发板信息等)本身是否支持……支持就继续下一步工做,不支持就直接停掉,不浪费时间了。而TQ2440出场的uboot很是蛋疼的设置为了168,因此咱们进到内核源码,把对应文件修改一下,在内核源码的“arch/arm/tools/mach-types”文件中,大约379行,原来是362,咱们把它改的和uboot中默认的一致就OK了:



而后从新编译,并下载到开发板,结果:



这证实系统已经能够跑了,只是尚未作好分区与文件系统。

 

五、第五要改的是Nand分区。(缘由是不一样的镜像,如内核镜像、文件系统镜像…要烧写到不一样的分区中,由于虽然咱们不知道分区内部究竟是什么,可是咱们知道分区的起始地址和大小,这样只要按照约定把相应的镜像放到相应的分区(对应的地址),这样咱们就能够正常启动系统)。

 

既然是分区的事,那下一步就来修改分区。这个分区通常称做MTD分区(MemoryTechnology Device),是linux抽象出来的存储设备层。它提供了底层存储设备的抽象,封装了底层硬件的读、写、擦除等操做。直入主题,咱们要改的就是内核源码“arch/arm/plat-s3c24xx/commonsmdk.c”文件的smdk_default_nand_part[]结构体,这里,由于本人用的是TQ2440出场Uboot(固然这是linux移植开篇,没有用本身移植的Uboot,无论用哪一个,Uboot和linux都是要配合好才能够启动的),而TQ2440出厂Uboot的默认启动参数是mtdblock1,也就是第二个分区,它的nand flash分区设计是,第一个分区存放Nand下的Uboot镜像,第二个是kernel镜像,第三个是文件系统。而分区的约定又是和Uboot的下载命令中设置的默认下载地址是对应的,因此,这里咱们仍是得按着这个约定来。

 

首先先改smdk_nand_info结构体(大约在commonsmdk.c 的141行):

       修改后如图:(原来)(后边我作测试的时候把名字都改成了“jun_uboot”,"jun_kernel"和"jun_yaffs2")


下边是咱们要定义的smdk_default_nand_part[]结构体:

 

static structmtd_partition smdk_default_nand_part[] = {

       [0] = {

.name = "nand_uboot",

.offset = 0x00000000,

.size = 0x00040000,

},

[1] = {

.name = "nand_kernel",

.offset = 0x00200000,

.size = 0x00200000,

},

[2] = {

.name = "nand_yaffs2",

.offset = 0x00400000,

.size = 0x0FB80000,

}

};


由于这里,nandflash是256M的,给Uboot分了0x00040000(256k),kernel分了0x00200000(2M),剩余的0x0FB80000(251M)给了yaffs分区。

编辑好之后,保存,在编译运行一下,看看效果。。。。

在源码目录下再make一下获得zImage,再次测试,结果为:



看吧,虽然分区调整成三个了,他仍是不认识咱们的分区依然是mtdblock0( driver? ),

虽然识别了分区,可是识别不出来分区的名字。为何呢?(由于咱们尚未添加文件系统的支持) 不过不能否认的是分区已经被咱们调整成3个,且kernel已经知道了如今是三个,以及他们的大小。因此全宇宙我没法阻止咱们继续下去。

 

主要要看内核此时崩溃的缘由(kernel是经过panic反馈给咱们的):

No filesystem could mount root, tried:  ext3 ext2 cramfs vfat msdos iso9660 romfs

Kernel panic - not syncing: VFS: Unable tomount root fs on unknown-block(31,2)。。。


这个panic的缘由一目了然,由于咱们尚未添加文件系统的支持。兵来将挡,水来土掩。咱们这就添加内核对yaffs2文件系统的支持。

这里,发现好多资料文档里给的下载源码地址http://www.aleph1.co.uk/cgi-bin/viewcvs.cgi/这个网址已经失效了,下载yaffs源码,找到了http://www.yaffs.net官网。看了一番说明……额时代变了,yaffs和yaffs2早已分开维护了,小看了一下,应该是这样下载的:http://www.aleph1.co.uk/gitweb?p=yaffs2.git;a=tree  ,他这里是用git做的版本控制,打开网页直接点击“snapshot”会下载到一个源码快照,固然,这里下到的是yaffs2。你手头有原来的老版本的包的话,也能够用老的。这里获得的是“yaffs2-HEAD-d43e901.tar.gz”,和老版本(仍是用的cvs版本控制时的)源码包比较,少了yaffs目录。咱们把它解压到工程目录中(你作开发的目录,不用放到kernel目录里)。而后执行下边一系列命令,将yaffs2补丁打到咱们的内核中:

$ tar -xzvf yaffs2-HEAD-d43e901.tar.gz              # 解压到当前目录。

$ cd yaffs2-HEAD-d43e901/                                     # 进入该目录

$ ./patch-ker.sh c s ../linux-2.6.34.10/                 # 这里是打补丁的命令,要加上c和s选项

打补丁和打后效果如图,证实Makefile和内核配置单都已经修改过了。



而后咱们makemenuconfig 在配置中加入yaffs选项,给内核添加yaffs文件系统支持。

在配置单的这里添加:

       Filesystems  --->

              [*]Miscellaneous filesystems  --->

                            <*>   yaffs2 file system support(new)

多人性化,他会有个(new),告诉你这就是你才添加的。

另外:

在256MB 以及更大容量的Nand Flash 须要选择上硬件ECC 校验的选项,不然会出现yaffs2 文件系统不能挂载而致使系统起不来的状况。咱们照办就是了:

Device Drivers --->

<*> MemoryTechnology Device (MTD) support --->

<*> NAND Device Support --->

<*> NAND Flash support for S3C2410/S3C2440 SoC

[*] S3C2410 NAND Hardware ECC

 

好的,咱们再次make 一下内核进行测试。。。。。$ make zImage -j5 ……..

编译报错了:

 

fs/yaffs2/yaffs_vfs.c: In function'yaffs_setattr':

fs/yaffs2/yaffs_vfs.c:522: error: implicitdeclaration of function 'setattr_copy'

fs/yaffs2/yaffs_vfs.c:525: error: implicitdeclaration of function 'truncate_setsize'

fs/yaffs2/yaffs_vfs.c: At top level:

fs/yaffs2/yaffs_vfs.c:871: warning:initialization from incompatible pointer type

fs/yaffs2/yaffs_vfs.c:902: warning:initialization from incompatible pointer type

fs/yaffs2/yaffs_vfs.c: In function'yaffs_evict_inode':

fs/yaffs2/yaffs_vfs.c:1062: error: implicitdeclaration of function 'end_writeback'

fs/yaffs2/yaffs_vfs.c: At top level:

fs/yaffs2/yaffs_vfs.c:1952: error: unknownfield 'evict_inode' specified in initializer

fs/yaffs2/yaffs_vfs.c:1952: warning: initializationfrom incompatible pointer type

make[2]: *** [fs/yaffs2/yaffs_vfs.o] Error1

 

应该是版本问题,那就不用新版本的,咱们退而求其次,还用老的吧,这里用的是嵌入式Linux开发彻底手册里附带的版本,【下载地址】。

 

先把已经打的补丁去除了,再按照一样的方法打该版本的补丁。这里的补丁不是经过patch打的,而是用的shell,那就笨着来吧,手动去除,手动去除的话你得明白它到底对你的内核作了哪些手脚:执行patch-ker.sh,主要作了一下工做:

<1>修改内核文件/fs/Kconfig,增长下面两行(在177行附近):

 

if MISC_FILESYSTEMS

 

source "fs/adfs/Kconfig"

source "fs/affs/Kconfig"

source "fs/ecryptfs/Kconfig"

source "fs/hfs/Kconfig"

source "fs/hfsplus/Kconfig"

source "fs/befs/Kconfig"

source "fs/bfs/Kconfig"

source "fs/efs/Kconfig"

source"fs/yaffs2/Kconfig"

source "fs/jffs2/Kconfig"

# UBIFS File system configuration

 

<2>修改内核文件/fs/Makefile,增长下面两行(在129行附近):

 

obj-$(CONFIG_GFS2_FS)           += gfs2/

obj-$(CONFIG_EXOFS_FS)          += exofs/

obj-$(CONFIG_YAFFS_FS)  += yaffs2/

 

<3>在内核文件的fs目录下建立yaffs2子目录,而后复制以下文件:

将yaffs2源码目录下的Makefile.kernel文件复制为内核fs/yaffs2/Makefile文件。

将yaffs2源码目录下的Kconfig文件复制为内核fs/yaffs2/目录下。

将yaffs2源码目录下的*.c、*.h文件(不包括子目录下的文件)复制为内核fs/yaffs2/目录下。

 

笨人有笨法,咱们手动的把它修改回去,而后记着编译一下,确保没有其余错误,而后再继续。。。。。。。。。。。。。。测试中。。。。。。。。。。。。。。。。。。。

 

OK,编译正常,证实去除没有干扰到其余部分,能够继续了。

一样,以相同的方式将上边给出版本的yaffs安装到内核,再也不嗷述。(不过这里的安装后确实和上边的有些出入,初步估计是上边的源码下载有误,非彻底版。)

 

就绪后,咱们从新编译一下。。。。。。。。。。。。。。。。编译中。。。。。。。。。。。。。。。。。。。

 

我勒个去,报了如此多的错误。。。。。都是yaffs的。。。。。。

 

fs/yaffs2/yaffs_fs.c:212: error: unknownfield 'prepare_write' specified in initializer

fs/yaffs2/yaffs_fs.c:212: warning:initialization from incompatible pointer type

fs/yaffs2/yaffs_fs.c:213: error: unknownfield 'commit_write' specified in initializer

fs/yaffs2/yaffs_fs.c:213: warning: initializationfrom incompatible pointer type

fs/yaffs2/yaffs_fs.c:287: error: unknownfield 'read_inode' specified in initializer

fs/yaffs2/yaffs_fs.c:287: warning:initialization from incompatible pointer type

fs/yaffs2/yaffs_fs.c:288: error: unknownfield 'put_inode' specified in initializer

fs/yaffs2/yaffs_fs.c: In function'yaffs_get_inode':

fs/yaffs2/yaffs_fs.c:847: error: implicitdeclaration of function 'iget'

fs/yaffs2/yaffs_fs.c:847: warning:assignment makes pointer from integer without a cast

fs/yaffs2/yaffs_fs.c: In function'yaffs_mknod':

fs/yaffs2/yaffs_fs.c:1021: error: 'structtask_struct' has no member named 'fsuid'

fs/yaffs2/yaffs_fs.c:1022: error: 'structtask_struct' has no member named 'fsgid'

fs/yaffs2/yaffs_fs.c: In function 'yaffs_symlink':

fs/yaffs2/yaffs_fs.c:1201: error: 'structtask_struct' has no member named 'fsuid'

fs/yaffs2/yaffs_fs.c:1202: error: 'structtask_struct' has no member named 'fsgid'

fs/yaffs2/yaffs_fs.c: In function'yaffs_internal_read_super':

fs/yaffs2/yaffs_fs.c:1676: warning: format'%d' expects type 'int', but argument 2 has type 'uint64_t'

fs/yaffs2/yaffs_fs.c: In function'init_yaffs_fs':

 

从报错的内容来看,应该是版本问题,这个内核是比较新的,而yaffs的版本比较老。

咱们从新把内核还原回去(固然仍是手动的完成那三步了)。真蛋疼。。。。

 

(如今官网上不用cvs作版本控制了,用的git,ubuntu下安装git工具,能够直接clone到最新的yaffs2,用$ sudo apt-get install git-core安装,而后用 $ git clone  git://www.aleph1.co.uk/yaffs2 命令会直接在当前命令下clone一份,并在当前目录下建立yaffs2目录,是当前目录下,因此最好不要在内核目录下执行该命令)

实际测试是用git在官网上clone下来的最新版本,也报错了。。。。。。。

 

fs/yaffs2/yaffs_vfs.c: In function'yaffs_setattr':

fs/yaffs2/yaffs_vfs.c:522: error: implicitdeclaration of function 'setattr_copy'

fs/yaffs2/yaffs_vfs.c:525: error: implicitdeclaration of function 'truncate_setsize'

fs/yaffs2/yaffs_vfs.c: At top level:

fs/yaffs2/yaffs_vfs.c:871: warning:initialization from incompatible pointer type

fs/yaffs2/yaffs_vfs.c:902: warning:initialization from incompatible pointer type

fs/yaffs2/yaffs_vfs.c: In function'yaffs_evict_inode':

fs/yaffs2/yaffs_vfs.c:1062: error: implicitdeclaration of function 'end_writeback'

fs/yaffs2/yaffs_vfs.c: At top level:

fs/yaffs2/yaffs_vfs.c:1952: error: unknownfield 'evict_inode' specified in initializer

fs/yaffs2/yaffs_vfs.c:1952: warning:initialization from incompatible pointer type

make[2]: *** [fs/yaffs2/yaffs_vfs.o] Error1

make[1]: *** [fs/yaffs2] Error 2

make[1]: *** Waiting for unfinishedjobs....

 

想了想,内核版本是比较新的,yaffs2版本也是比较新的……应该没有太大落差,是否是尝试本身用新内核往板子上作移植的上辈子真的是折翼的天使。记得上次用的是2.6.30的内核用的老版本yaffs2一下就过了。。。。。。。。

 

待我用2.6.30的内核再从新作一遍上面所有的工做,来测试一下这个新版本的yaffs。。。(真的很悲催…………)

………………………工做中,等待………………………

 

对于2.6.30的内核添加yaffs2最新版也会报出和2.6.34一样的错误,因而我猜想'setattr_copy' 、'truncate_setsize'……等这些应该是新内核(3.0)的新特性,不过该如何选择适合本身内核的yaffs2版本呢……确实使人纠结。

 

而后对2.6.30添加yaffs2老版本进行测试(记着去除当前版本先)……

 

至此,嵌入式linux开发彻底手册里的版本也会报错,我不淡定了。我决定,必须把这个问题解决,因此我去追溯一个和linux2.6.30 时间比较温和的版本,在这里,我决定选择2009年圣诞节的版本 http://www.aleph1.co.uk/gitweb?p=yaffs2.git;a=shortlog;pg=4  ,2009-12-25,希望圣诞老人给我面子,让它过了吧……(我不淡定了,编译时我都开始-j6,开六个线程了,其实时间都是在等待中给浪费掉了……)

 

………………………编译中,等待………………………

 

OK,圣诞节仍是神圣的,它编译经过了……如今测试经过的版本是:2.6.30.4的kernel + 2009-12-25的yaffs2 。你是否是也感受很纠结。咱们烧乳板子测试下,刚刚编译出来的zImage。

运行后效果:






好的,到这里,内核的移植已经暂时告一段落,我该把文章帖出来了……事实证实,虽然笨,可是花些时间仍是能够作到的……

 (有时候版本问题仍是很使人头疼的!!!!)

下一步是进行跟文件系统的构建…… (待续……)

相关文章
相关标签/搜索