Linux 是一套免费使用和自由传播的操作系统,吸收了来自全世界无数程序员的精华,不断完善壮大。Linux 内核高度可移植,其可以运行在各种硬件设备中,如:路由器、游戏机、智能手表、手机、台式计算机、超级计算机。笔者此处就 s3c2416 的 Linux 内核移植作一个简单的介绍。

1、Linux内核移植分类

1.1 架构级移植

Linux 内核源码把处理器架构相关的代码放到 arch 目录,Linux内核支持目前市面上绝大部分的处理器架构,如x86,arm,mips。对于特定架构的代码由专门的社区进行维护,一般用户无需做这部分代码的移植工作。

1.2 CPU级移植

arm通过授权的方式,得到了众多半导体厂商的支持,成为当今最流行的一种处理器架构。即使同一种arm核心,不同半导体厂商设计出来的芯片规格都是千差万别的。所以这部分Linux内核没法统一去实现,只能靠用户进行CPU级的移植。arm社区对于一些较流行的CPU,已经直接提供了移植支持,该部分的代码可以参考 arch\arm\mach-xxx 目录,包括本文所论的s3c2416,都可以直接支持。一般来说,上游的芯片厂商会提供其对应的CPU级移植,也无需用户做这方面的移植工作。如果没有相关的CPU级移植代码,或者需要把芯片厂商提供的Linux内核迁移到更高版本的内核等等,则需要参考已有的同类CPU级移植代码进行移植。

1.3 板级移植

Linux内核针对某款CPU已经提供了一些开发板的具体实现,此外,芯片厂商也会提供对应其CPU的Linux板级实现。但实际用户针对不同的应用,其板级外设是千差万别的。这部分只能由用户对比自身的板极硬件配置以及现有的开发板硬件配置,进行代码的删减增加。

2、s3c2416板级移植

Linux内核已经完整支持s3c2416,在arch->arm目录下plat-samsung以及mach-s3c24xx这两个目录代码实现了s3c24xx系列的CPU级代码。其中,plat-samsung目录代码为所有samsung平台芯片的公共实现部分,mach-s3c24xx目录代码为s3c24xx系列芯片公共实现部分。这些代码基本上用于CPU核心以及CPU外设的设置,对于同一款CPU,这些代码都是通用的,一般目标板无需进行更改。从Linux官网下载最新版本的内核源码,s3c2416移植Linux内核步骤如下:

2.1 修改主目录Makefile

Makefile默认获取得是主机的架构以及编译环境,我们的内核是要运行在arm平台上,所以修改:

ARCH=arm
CROSS_COMPILE=arm-linux-

2.2 板级代码mach-home2416.c

SofTool.CN Notes:
mach-s3c24xx 目录位置:arch\arm\mach-s3c24xx
mach-home2416.c文件位置:arch\arm\mach-s3c24xx\mach-home2416.c

在mach-s3c24xx目录下已支持多个开发板的板级代码,如:mach-qt2410.c、mach-mini2440.c、mach-smdk2416.c等等。其中:smdk为samsung官方开发板,我们以smdk板级代码文件作为参考,构建我们自己的板级代码文件。

在mach-s3c24xx目录下跟smdk2416相关的板级代码有common-smdk.c以及mach-smdk2416.c。

注:
common-smdk.c为所有smdk板的板级公共实现部分
mach-smdk2416为smdk2416 三星官方开发板独有的板级实现部分;

我们只需支持home2416板,在mach-home2416.c文件中实现所有的板级代码,拷贝mach-smdk2416.c为mach-home2416.c,把common-smdk.c中的代码复制整合进mach-home2416.c中,并把所有smdk/SMDK替换成home/HOME。

Linux内核通过machine_desc结构体来控制系统体系架构相关部分的初始化,其通过MACHINE_START/MACHINE_END宏定义进行描述:

MACHINE_START(HOME2416,"HOME2416")
     .atag_offset   =0x100,
     .init_irq      =s3c2416_init_irq,
     .map_io        =home2416_map_io,
     .init_machine  =home2416_machine_init,
     .init_time     =home2416_init_time,
MACHINE_END

注:
machine_desc结构即位于上面的mach-home2416.c中;

Linux内核在start_kernel()开始启动内核,按顺序调用machine_desc中的:map_io()、init_irq()、init_time()、init_machine()。

start_kernel()->setup_arch()->paging_init()->devicemaps_init()->map_io()实现调用home2416_map_io(),默认外设IO资源地址不在Linux内核空间(3G~4G),需访问一些外设IO资源,可以在初始化页表时,静态映射IO资源空间到内核地址空间。

start_kernel()->init_IRQ()->init_irq()实现调用s3c2416_init_irq(),该函数实现把特定CPU的中断信息注册进内核中断子系统中,使内核能响应并处理相应的中断。

start_kernel()->time_init()实现调用home2416_init_time(),该函数启用特定CPU的定时器,用于实现clock event以及clock source。clock event是可编程产生中断事件的,用于实现普通定时器和高精度定时器,产生系统运行tick;clock source不能被编程,无法产生事件,用来为Linux内核提供一个较精确的时间基线。

kernel_init()->kernel_init_freeable()->do_basic_setup()->do_initcalls()实现调用home2416_machine_init(),Linux在启动的最后会创建kernel_init进程,该进程中会初始化调用所有在”initcall”段的函数,arch_initcall(customize_machine)实现从customize_machine()中调用home2416_machine_init(),该函数实现所有板级外设的初始化。

对于CPU级移植而言,特定CPU只需产生系统运行tick即可让Linux核心正常工作,这需要特定CPU中断支持以及定时器支持,这对应machine_desc中init_irq()以及init_time()的实现。对于s3c2416来说,最简单的Linux内核CPU级移植只需实现对应s3c2416_init_irq()以及home2416_init_time(),让内核能正确产生系统运行tick。

对于板级移植而言,只需关注machine_desc中init_machine()实现,根据自身目标板进行板级外设的删减增加。

2.3. 板级编译支持

Linux内核通过Kconfig进行配置,在arch->arm->mach-s3c24xx目录,Kconfig文件加入home2416板的配置支持。

config MACH_SMDK2416
     bool "SMDK2416"
     select S3C2416_SETUP_SDHCI
     select S3C24XX_SMDK
     select S3C_DEV_FB
     select S3C_DEV_HSMMC
     select S3C_DEV_HSMMC1
     select S3C_DEV_NAND
     select S3C_DEV_USB_HOST
     help
       SayY here if you are using an SMDK2416

#根据上面的 config MACH_SMDK2416 仿写:
config MACH_HOME2416
     bool "HOME2416"
     select S3C2416_SETUP_SDHCI
     select S3C_DEV_FB
     select S3C_DEV_HSMMC
     select S3C_DEV_HSMMC1
     select S3C_DEV_NAND
     select S3C_DEV_USB_HOST
     help
       SayY here if you are using an HOME2416

在arch->arm->mach-s3c24xx目录,Makefile加入编译支持。

obj-$(CONFIG_MACH_SMDK2416) += mach-smdk2416.o

#增加mach-home2416.o
obj-$(CONFIG_MACH_HOME2416) += mach-home2416.o

2.4. 机器码

修改机器码,使之与bootloader所设置的一致,在arch->arm->tools目录, mach-types文件加入HOME2416板的机器码。

smdk2416  MACH_SMDK2416  SMDK2416 1685
#增加HOME2416板的机器码:
home2416  MACH_HOME2416  HOME2416 1685

2.5. 内核配置

我们基于s3c2410的配置文件进行配置,把arch->arm->configs文件夹中的s3c2410_defconfig拷贝到主目录,并命名成.config。
执行make menuconfig,进行Linux内核的配置。配置的注意事项请参考前一章节,选择HOME2416板。

07_linux内核移植 - 图1

2.6. 编译

执行make,进行编译,成功后会在arch->arm->boot目录下生成Image非压缩内核以及zImage压缩内核。

3、内核运行

把zImage下载到目标板后,识别出Machine: HOME2416

07_linux内核移植 - 图2

图3-1 Linux内核启动

4、附录

bootloader源码以及使用说明 https://pan.baidu.com/s/1slczwhJ
Qt5.8官网源码 https://pan.baidu.com/s/1eRDJtNs
本系列例程的根文件系统 https://pan.baidu.com/s/1nuGmSqt
opev3.2.0官网源码 https://pan.baidu.com/s/1i5btLGT
yaffs官网源码 https://pan.baidu.com/s/1pLpuHw3
busybox-1.26.2官网源码 https://pan.baidu.com/s/1bpkZynt
tslib官网源码 https://pan.baidu.com/s/1i4EtjfR
mplayer-1.3.0官网源码 https://pan.baidu.com/s/1i5MGRhb
基于S3C2416修改的linux-4.10.10源码 https://pan.baidu.com/s/1sl0fXlr

补充:
1、zImage的位置: arch/arm/boot/zImage
然后将zImage修改名为kernel.bin,拷贝到SD卡的image/下
2、Kernel command line的值是通过bootloader传递过去的,可以通过修改MDK中Common/LinuxBoot.c的BootCmd[]实现;