linux|Linux kernel移植笔记

到内核官网下载最新版本的内核
https://www.kernel.org/

目前,下载的内核版本是 linux-3.4.2
交叉编译版本:arm-linux-4.3.2
PC机系统:ubuntu 12.04 LTS
开发板:TQ2440

1.下载完内核后,进行解压

tar xvfj linux-3.4.2.tar.bz2 -C ./

进入内核目录:cd linux-3.4.2
查找内核的默认配置文件:find -iname "*defconfig"
内核的默认配置文件,都是在某个架构的目录下
./arch/arm/configs/s3c2410_defconfig
跟 2440 有关的两个配置文件:mini2440_defconfig 和 s3c2410_defconfig

因为之前的 u-boot 移植是使用 2410 作为模板,因此,内核配置选用 s3c2410_defconfig
如果使用 mini2440_defconfig ,则需要重新在 u-boot 里设置机器 ID

修改内核顶层目录的 Makefile ,把编译器改为 arm-linux-gcc
在内核目录下,执行 make s3c2410_defconfig,执行完后,会生成 .config
执行内核顶层目录下,执行 make uImage 编译内核
编译成功后,在 arch/arm/boot/目录下会生成 uImage 镜像,
复制到 /opt/work/other_board/kernel_projects/images 并重新命名为 uImage_new
使用以下命令下载和启动内核:
nfs 32000000 192.168.1.61:/opt/work/other_board/kernel_projects/images/uImage_new && bootm 32000000

内核启动后,出现乱码,寻找乱码原因(此时内核已经成功运行起来)
通过查看 u-boot 源码,得知 u-boot 传入的 machine-id 是 #define MACH_TYPE_SMDK2410193
另外,我们可以从 u-boot 设置启动参数,传入 machine-id 给内核 :set machid 193
随便设置一个 machid ,重新启动,会提示 machid 不匹配的错误,并且列出内核支持的 mach-id

查看内核源码,在 mach-smdk2440.h文件的最后几行,有这样一个宏:
MACHINE_START(S3C2440, "SMDK2440") /* Maintainer: Ben Dooks */ .atag_offset = 0x100, .init_irq= s3c24xx_init_irq, .map_io= smdk2440_map_io, .init_machine = smdk2440_machine_init, .timer= &s3c24xx_timer, .restart= s3c244x_restart, MACHINE_END

这个宏展开后,会有 MACH_TYPE_S3C2440 这样一个宏,这个宏在 arch\arm\tools\mach-types 中定义
这个宏会被转换为一个头文件 include/asm-arm/mach-types.h(此文件自动生成) 供其他文件包含,
在内核里面,查找 mach-types.h ,发现这个文件里,又包含了 #include
打开 ./include/generated/mach-types.h 文件,里面包含了该内核支持的单板id

在 u-boot 里,分别设置机器 ID 为 16A(SMDK2440) 和 7CF(MINI2440)
启动内核,均不能正确输出信息,出现乱码。
查看源码,在 mach-s3c24xx文件里,时钟初始化为 s3c24xx_init_clocks(16934400);
在 mach-mini2440文件里,时钟初始化为 s3c24xx_init_clocks(12000000);
因此,我们选择 machid 为 7CF ,因为 mini2440的时钟设置为 12MHz ,跟开发板一样

在 u-boot 命令行中 ,执行 print 打印出参数,发现没有设置波特率
设置波特率:set bootargs console=ttySAC0,115200 root=/dev/mtdblock3 ,并保存

重新启动内核,信息已经正确输出。

查看信息,发现内核无法挂载根文件系统。内核打印信息,其中有一段内容:
0x000000000000-0x000000004000 : "Boot Agent" mtd: partition "Boot Agent" doesn't end on an erase block -- force read-only 0x000000000000-0x000000200000 : "S3C2410 flash partition 1" 0x000000400000-0x000000800000 : "S3C2410 flash partition 2" 0x000000800000-0x000000a00000 : "S3C2410 flash partition 3" 0x000000a00000-0x000000e00000 : "S3C2410 flash partition 4" 0x000000e00000-0x000001800000 : "S3C2410 flash partition 5" 0x000001800000-0x000003000000 : "S3C2410 flash partition 6" 0x000003000000-0x000010000000 : "S3C2410 flash partition 7"


修改内核分区信息,使其与u-boot分区一致(可以不一致,u-boot分区跟内核分区无关联)
在内核里,搜索 "Boot Agent" , 执行 grep "\"Boot\ Agent\"" * -nR( 反斜杠表示转义的意思 )
发现分区信息在 arch/arm/mach-s3c24xx/common-smdk.c 文件里
在 u-boot 里执行 mtdparts 查看 u-boot 的分区信息:
#: namesizeoffsetmask_flags 0: u-boot0x000400000x000000000 1: params0x000200000x000400000 2: kernel0x002000000x000600000 3: rootfs0x0fda00000x002600000

【linux|Linux kernel移植笔记】修改 arch/arm/mach-s3c24xx/common-smdk.c 文件,mtd_partition 改为以下内容 :
static struct mtd_partition smdk_default_nand_part[] = { [0] = { .name = "bootloader",//分区名字 .size = SZ_256K,//分区大小 .offset = 0,//偏移起始地址 }, [1] = { .name = "params", .offset = MTDPART_OFS_APPEND,//MTDPART_OFS_APPEND 表示紧接着上一个分区 .size = SZ_128K, }, [2] = { .name = "kernel", .offset = MTDPART_OFS_APPEND, .size = SZ_2M, }, [3] = { .name = "rootfs", .offset = MTDPART_OFS_APPEND, .size = MTDPART_SIZ_FULL,//MTDPART_SIZ_FULL 表示剩余的所有空间 }, };


分区信息修改完成后,烧写一个文件系统进去,看内核能不能正确挂载文件系统
执行以下命令:
nfs 30000000 192.168.1.61:/opt/work/other_board/kernel_projects/images/first_fs.yaffs2 nand erase.part rootfs nand write 30000000 260000 862080

(30000000-内存地址,260000-rootfs分区起始地址,862080-文件大小16进制)

重新烧写内核,并启动,发现内核还不支持 yaffs2 文件系统

查看 .config ,发现内核可以支持 jffs2 文件系统
执行以下命令,烧写 jffs2 文件系统
nfs 30000000 192.168.1.61:/opt/work/other_board/kernel_projects/images/fs_mini_mdev.jffs2 nand erase.part rootfs nand write 30000000 260000 $filesize

设置启动参数:
set bootargs console=ttySAC0,115200 root=/dev/mtdblock3 rootfstype=jffs2

重新烧写内核,并启动,内核打印信息:VFS: Mounted root (jffs2 filesystem) on device 31:3.
说明文件系统已经被成功挂载,但是还不能用,因为 init 进程出现了问题,需要制作新的文件系统

使用 busybox 制作新的根文件系统
到 busybox 官网下载 busybox 源码 :https://busybox.net/
使用的版本是 busybox 1.20.0
解压busybox:
tar xvfj busybox 1.20.0.tar.bz2 -C ./

解压完成后,在 busybox 根目录下,执行 make menuconfig,进入配置界面
选择Busybox Settings ---> Busybox Library Tuning ---> [*] Tab completion ,增加 Tab 补全功能

不使用静态链接,Build Options ---> []Build BusyBox as a static binary(no share libs)
修改交叉编译前缀,Build Options ---> (arm-linux-) Cross Compiler prefix

其他选项保持默认配置,然后直接 make 编译

编译完成后,把 busybox 安装在 /opt/work/other_board/kernel_projects/root_fs 目录,执行以下指令:
make CONFIG_PREFIX=/opt/work/other_board/kernel_projects/root_fs install

安装完成后,会在 /opt/work/other_board/kernel_projects/root_fs 目录下,生成以下文件和目录:
bin目录、linuxrc链接、sbin目录、usr目录

安装 glibc 库,交叉编译器目录下,一般都有 glibc 库 :
进入交叉编译器目录,执行 find -iname "lib" 查找库文件,查找结果如下:
./lib ./arm-none-linux-gnueabi/lib ./arm-none-linux-gnueabi/libc/armv4t/lib ./arm-none-linux-gnueabi/libc/armv4t/usr/lib ./arm-none-linux-gnueabi/libc/lib ./arm-none-linux-gnueabi/libc/thumb2/lib ./arm-none-linux-gnueabi/libc/thumb2/usr/lib ./arm-none-linux-gnueabi/libc/usr/lib

使用的库文件是
./arm-none-linux-gnueabi/libc/armv4t/lib ./arm-none-linux-gnueabi/libc/armv4t/usr/lib

建立两个目录:
mkdir /opt/work/other_board/kernel_projects/root_fs/lib mkdir -p /opt/work/other_board/kernel_projects/root_fs/usr/lib

执行以下命令,复制 glibc 库到根文件系统的 lib 目录:
cp ./arm-none-linux-gnueabi/libc/armv4t/lib/*so*/opt/work/other_board/kernel_projects/root_fs/lib-d cp ./arm-none-linux-gnueabi/libc/armv4t/usr/lib/*so*/opt/work/other_board/kernel_projects/root_fs/usr/lib-d

-d 参数表示,保持库文件的链接属性,不加 -d 的话,会导致复制的库文件很庞大

构建 etc 目录,etc 目录包含 :
fstab文件 ,该文件列出了需要挂载的文件系统
init.d目录,里面有 rcS脚本 ,该脚本的 mount -a 会执行 fstab 文件
inittab 文件,该文件是 init 进程执行的第一个文件,第一句话是执行 init.d/rcS 脚本

构建 dev 目录,执行 mkdir dev
在 dev 目录下,构建两个设备节点:
mknod console c 5 1 mknod null c 1 3

构建其他空目录:
mkdir proc tmp sys mnt root

到此,最小根文件系统制作完成

使用工具制作 .jffs2 文件系统,执行以下命令:
mkfs.jffs2 -n -s 2048 -e 128KiB -d /opt/work/other_board/kernel_projects/root_fs -o root_fs.jffs2

-n 表示不需要 cleanmaker 节点,-s 表示一个扇区大小,-e 表示一个擦除块的大小,-d 表示制作的目录 -o 表示输出的镜像名

制作完成后,烧写 内核 和 文件系统 到开发板
set bootargs console=ttySAC0,115200 root=/dev/mtdblock3 rootfstype=jffs2 nfs 30000000 192.168.1.61:/opt/work/other_board/kernel_projects/images/root_fs.jffs2 nand erase.part rootfs nand write 30000000 260000 $filesize nfs 32000000 192.168.1.61:/opt/work/other_board/kernel_projects/images/uImage_new && bootm 32000000

启动内核后,发现 jffs2 文件系统已经挂载成功,但提示不能加载 init 进程,
内核打印信息提示:Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000004

在内核源码中搜索 “Attempted to kill init”,跟踪源码,
发现exitcode为#defineSIGILL 4

上网搜索内核打印信息“Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000004”
发现需要配置内核支持 EABI (嵌入式应用二进制接口),重新配置内核支持 EABI 并编译
Kernel Features --> Use the ARM EABI to compile the kernel

重新下载 内核 和 文件系统 到开发板,并启动:
set bootargs console=ttySAC0,115200 root=/dev/mtdblock3 rootfstype=jffs2 nfs 30000000 192.168.1.61:/opt/work/other_board/kernel_projects/images/root_fs.jffs2 nand erase.part rootfs nand write 30000000 260000 $filesize nfs 32000000 192.168.1.61:/opt/work/other_board/kernel_projects/images/uImage_eabi && bootm 32000000

至此,内核启动成功,文件系统挂载成功

添加yaffs2文件系统
linux-3.4.2里,没有添加对yaffs2的支持,需要移植yaffs2到内核
yaffs2源码从以下网址获得:http://www.yaffs.net/download-yaffs-using-git
使用 git 工具获得yaffs2源码:git clone git://www.aleph1.co.uk/yaffs2(如PC没有 git 工具,则需要安装)

下载完成后,进入 yaffs2 目录,查看 README-linux ,里面有 yaffs2 的安装方法:
Integrating YAFFS2 into a Linux 2.6.x kernel -------------------------------------------- We'll start by assuming you have a building linux 2.6.x source tree called linux-dir and have the yaffs2 source code in a directory calls yaffs-dir. yaffs-dir has a handy shell script called patch-ker.sh will painlessly do all the patching for you. patch-ker.sh takes three parameters: c/l copy or link: c will copy yaffs files into the kernel tree, l will create symbolic links. m/s multi-version or single version vfs glue layer. Suggest you use m. linux-tree eg.cd yaffs-dir ./patch-ker.shc m linux-tree

执行以下指令给内核打上 yaffs2 的补丁
cd yaffs2 ./patch-ker.shc m /opt/work/other_board/kernel_projects/linux-3.4.2

补丁打完后,在 fs/yaffs2/ 目录下会有 yaffs2 源码

执行 make menuconfig 重新配置内核使其支持 yaffs2
-> File systems│
-> Miscellaneous filesystems (MISC_FILESYSTEMS [=y])│
-> yaffs2 file system support (YAFFS_FS [=n])

制作 yaffs2 文件系统映像:
mkyaffs2image /opt/work/other_board/kernel_projects/root_fsroot_fs.yaffs2

重新编译内核,编译成功后,重新烧写到开发板
set bootargs console=ttySAC0,115200 root=/dev/mtdblock3 nfs 30000000 192.168.1.61:/opt/work/other_board/kernel_projects/images/root_fs.yaffs2 nand erase.part rootfs nand write.yaffs 30000000 260000 $filesize nfs 32000000 192.168.1.61:/opt/work/other_board/kernel_projects/images/uImage_yaffs2 && bootm 32000000

烧写完成,启动,发现 yaffs2 文件系统挂载成功,但不能启动,使用替代法解决

发现问题出现在 u-boot 源码里,对yaffs烧写的那部分:
在 cmd_nand.c --> do_nand() --> nand_write_skip_bad() 函数里,
need_skip = check_skip_len(nand, offset, *length); //检查nandflash 里有多少坏块 ...... ...... if (!need_skip && !(flags & WITH_DROP_FFS)) {//如果没有坏块(need_skip == 0)&& (flags != WITH_DROP_FFS) rval = nand_write (nand, offset, length, buffer); //就会执行以下的烧写 if (rval == 0) return 0; *length = 0; printf ("NAND write to offset %llx failed %d\n",offset, rval); return rval; }

但对于 yaffs2 ,真正的烧写是在:

if (flags & WITH_YAFFS_OOB) { int page, pages; size_t pagesize = nand->writesize; size_t pagesize_oob = pagesize + nand->oobsize; struct mtd_oob_ops ops; ops.len = pagesize; ops.ooblen = nand->oobsize; ops.mode = MTD_OOB_RAW; ops.ooboffs = 0; pages = write_size / pagesize_oob; for (page = 0; page < pages; page++) { WATCHDOG_RESET(); ops.datbuf = p_buffer; ops.oobbuf = ops.datbuf + pagesize; rval = nand->write_oob(nand, offset, &ops); if (rval)break; offset += pagesize; p_buffer += pagesize_oob; } }

因此,需要修改以上的代码,修改后为:
if (!need_skip && !(flags & WITH_DROP_FFS) && !(flags & WITH_YAFFS_OOB)) { rval = nand_write (nand, offset, length, buffer); if (rval == 0) return 0; *length = 0; printf ("NAND write to offset %llx failed %d\n",offset, rval); return rval; }

修改完成后,重新烧写 u-boot ,执行以下指令:
usb 1 30000000 nand erase 0 80000 nand write 30000000 0 80000

重新烧写内核和文件系统,成功挂载并运行 yaffs 文件系统

裁剪内核并制作补丁
进入 make menuconfig 去掉一些不需要用到的配置项,比如:
去掉一些无关的单板:
-> System Type│
-> SAMSUNG S3C24XX SoCs Support
去掉一些无关的文件系统:如 ext2、ext3、ext4 等。。。

重新编译内核,执行 make uImage

制作内核补丁:
cp .config config_ok make distclean mv -f linux-3.4.2 linux3.4.2_ok tar xvfj linux-3.4.2.tar.bz2 diff -urN linux-3.4.2 linux-3.4.2_ok > linux-3.4.2_ok.patch

至此,内核移植完成。


    推荐阅读