Linux内核移植
忙于论文,都没有时间写博客记录笔记了,趁着周五不想搞论文,记录以下最近看的Linux内核书
Linux内核启动过程
在移植Linux之前,先了解它的启动过程。Linux的启动过程分为两部分:架构/开发板相关的引导过程、后续的通用启动过程。如下图所示是ARM架构处理器上Linux内核vmlinux的启动过程。之所以强调是vmlinux,是因为其他格式的内核在进行与vmlinux相同的例如流程之前会有一些独特的操作。比如对应压缩格式的内核zImage,它首先自解压得到vmlinux,然后执行vmlinux开始“正常的”启动流程。
引导阶段通常使用汇编语言编写,它首先检查内核是否支持当前架构的处理器,然后检查是否支持当前开发板。通过检查后,就为调用下一阶段的start_kernel 函数作准备了。这主要分如下两个步骤:
- (1)连接内核时使用的虚拟地址,所以要设置页表、使能MMU。
- (2)调用C函数 start_kernel 之前的常规工作,包括复制数据段、清除BSS段、调用 start_kernel 函数。
第二阶段的关键代码主要使用C语言编写。它进行内核初始化的全部工作,最后调用 rest_init 函数启动 init 过程,创建系统第一个进程:init 进程。在第二阶段,仍有部分架构/开发板相关的代码。如下图中 setup_arch 函数用于进行架构/开发板相关的设置(比如重新设置页表,设置系统时钟、初始化串口等)。
修改内核以支持 S3C2410/S3C2440 开发板
- 首先配置、编译内核,确保内核可以正确编译。得到内核源码后,先修改顶层 Makefile
185 ARCH ?= $(SUBARCH)
186 CROSS_COMPILE ?=
改为
185 ARCH ?= arm
186 CROSS_COMPILE ?= arm-linux-
- 然后执行如下命令,使用arch/arm/configs/S3C2410_defconfig 文件来配置内核,它生成 .config 配置文件,以后就可以直接使用 “ make menuconfig” 修改配置
make smdk2401_defconfig
- 最后编译生成内核,执行 “make” 将顶层目录下生成内核映像文件 vmlinux;执行 “make uImage” 除生成 vmlinux 外,还在 arch/arm/boot/ 目录下生成 U-Boot 格式的内核映像文件 uImage 。我们使用 ”make uImae“
- 对于 S3C2410 开发板,上面生成 uImage 是可以使用的。在U-Boot 控制界面中使用如下命令下载 uImage 并启动它:
tftp 0x32000000 uImage 或 nfs 0x30000000 192.168.10.109:/work/nfs_root/uImage
bootm 0x32000000
- 但是对于S3C2410 开发板,使用同样的命令启动 uImage ,在打印如下信息之后(注意,这些信息是内核的misc.c文件解压内核时打印的),就出现了一大堆乱码,如下所示:
Start kernel........
Uncompressing Linux..................................done
,booting the kernel
所以,Linux2.6.22.6 还不支持本书所用的 S3C2440 开发板,需要进行一些修改,至于要修改哪些文件,需要了解内核的启动代码。
内核启动第二阶段的函数调用过程(以S3C2440开发板为例),相同缩进的函数表示它们是在同一个函数中被调用:
start_kernel ->
setup_arch ->
setup_processor
setup_machine
...
parse_tags
...
parse_cmdline
paging_init ->
devicemaps_init ->
mdesc->map_io() ->
s3c24xx_init_io
s3c24xx_init_clocks
s3c24xx_init_uarts
...
console_init ->
s3c24xx_serial_initconsole
register_console(&s3c24xx_serial_console)
...
修改内核
- 在 arch/arm/mach-s3c2440/mach-smdk2440.c 做如下修改:
//修改前
s3c24xx_init_clocks(16934400);
//修改后
s3c24xx_init_clocks(12000000);
- 然后执行 make uImage ,生成 uImage。对于S3C2410,S3CC2440开发板,上面生成的 uImage 都可以使用了。
把 uImage 放到tftp 服务器目录下,或者放到 Linux 中/work/nfs_root 目录下,然后在 U-Boot 控制界面中使用如下命令下载 uImage 并启动它。
tftp 0x32000000 uImage 或 nfs 0x30000000 192.168.10.109:/work/nfs_root/uImage
bootm 0x32000000
- 可以看到内核的启动信息,最后出现 panic 信息(这需要修改 mtd 分区、增加对 yaffs 文件系统的支持)
修改MTD分区
MTD(Memory Technology Device),即内存技术设备,是LInux中对ROM,NOR Flash,NAND Flash 等存储设备抽象出来的一个设备层,它向上提供统一的访问接口:读、写、擦除等;屏蔽了底层硬件的操作、各类存储设备的差别。得益于 MTD 设备的作用,重新划分 NAND Flash 的分区很简单。
驱动对设备的识别过程
驱动对设备识别时,有以下两种方法:
(1)驱动程序本身带有设备的信息,比如开始地址、中断号等;加载驱动程序时,就可以根据这些信息来识别设备
(2)驱动程序本身没有设备的信息,但是内核中已经(或以后)根据其他方式确定了很多设备的信息;加载驱动程序时,将驱动程序与这些设备逐个比较,确定两者是否匹配(mach)。如果驱动程序与某个设备匹配,就可以通过该驱动操作这个设备了。
内核中常使用第二种方法来识别设备,这可以将设备集中在一个文件中管理,当开发板的配置改变时,便于修改代码
在内核文件 include/linux/platform_device.h 中,定义了两个数据结构来表示这些设备和驱动程序:platform_device 结构用来描述设备的名称、ID、所占用的资源(比如内存地址/大小,中断号)等;platform_driver 结构用来描述各种操作函数,比如枚举函数、移除设备函数、驱动的名称等。
内核启动后,首先构造链表将描述设备的 platform_device 结构组织起来,得到一个设备的列表;当加载某个驱动程序的 platform_driver 结构时,使用一些匹配函数来检查驱动程序能否支持这些设备,常用的检查方法很简单:比较驱动程序和设备的名称。
修改MTD分区
要改变分区时,修改 arch/arm/plat-s3c24xx/common-smdk.c 文件中的 smdk_default_nand_part 结构即可。在此将 NAND Flash 和划分为3个分区,前 2MB 用于存放内核,接下来的 8MB 用于存放 JFFS2 文件系统,剩下的用来存放 YAFFS 文件系统。
static struct mtd_partition smdk_default_nand_part[] = {
[0] = {
.name = "kernel",
.size = SZ_2M,
.offset = 0,
},
[1] = {
.name = "jffs2",
.offset = MTDPART_OFS_APPEND,
.size = SZ_8M,
},
[2] = {
.name = "yaffs",
.offset = MTDPART_OFS_APPEND,
.size = MTDPART_SIZ_FULL,
},
};
其中的 MTDPART_OFS_APPEND 表示当前分区紧接着上一个分区,MTDPART_SIZ_FULL 表示当前分区的大小为剩余的Flash空间。
之后执行 make uImage 重新生成内核
移植 YAFFS 文件系统
YAFFS(yet another flash file system)是一种类似脚本于 JFFS/JFFS2 、专门为 NAND Flash 设计的嵌入式系统,适用于大容量的存储设备。
移植 yaffs 分两个步骤:
- 将 yaffs2 代码加入内核。
这可以通过yaffs2 目录下的脚本文件 patch-ker.sh 来给内核打补丁 - 配置、编译内核
最后执行 make uImage 编译内核