这一篇文章继续讲解Linux内核镜像的交叉编译与移植。首先感谢一位大神的博客:https://blog.csdn.net/jklinux/article/details/72675929
如果你购买了某个厂家的开发板,一般在厂家提供的SDK源码包内就会提供适用于他们板子的内核源码,当然,Linux内核源码本身并不会局限于某个CPU框架,只是使用开发板厂家提供的内核版本可以避免很多因为版本不兼容带来的问题,其次,厂家还会针对他们设计的板子提供Linux内核源码编译时的默认配置文件(xxx_defconfig和Kconfig)。
首先,刚才烧写了uboot的SD卡现在需要创建文件分区了,这里我个人的做法是,使用ubuntu上的工具gparted对SD卡创建一个2G左右的分区,分区的起始位默认,分区类型ext2(推荐类型,因为有些芯片读取不了态新的);然后,需要在你的Ubuntu系统PC机上安装配置交叉编译器,交叉编译器下载官网:交叉编译器下载地址,下载的版本得是:
gcc-linaro-arm-linux-gnueabihf-v-xxxx //v是版本编号,xxx是日期
第一步:安装交叉编译器,配置交叉编译环境。解压交叉编译器到你想要的路径(这个路径一但确认,将不能修改),为了后期配置编译环境方便,我将解压后的目录改名为arm_gcc,配置系统编译环境:
vim /etc/bash 或 /etc/bash.bashrc //这个文件内就是系统运行时遵循的环境变量//
在文件最后一行的位置加一行,比如我的交叉编译器路径是/usr/arm_gcc
export PATH=/usr/arm_gcc/bin:$PATH
保存后退出,然后重启ubuntu系统让此配置生效。
之后如果终端输入指令arm-linux-,按TAB补全时,出现一堆arm-linux-gnueabihf-xx,就说明环境配置成功
这个交叉编译器在后面你编译运行在这个开发板上的驱动程序和系统应用时都必须是同一个,否则可能出现交叉编译的软件在板子上运行出错或找不到动态依赖库的情况!!!
第二步:这一步的细节操作流程与厂家提供的SDK包有很大的关系,其中一些细节问题我会在大神博客内容的基础上进行解释:
内核编译:
在orangepi_sdk目录下:
进入source/linux-3.4.112内核源码目录.
注意: uboot, linux内核都是可以支持多种CPU架构, 一个CPU架构下又有多种SOC, 一个SOC又可以做成多种方案.所以我们编译前都需要指定编译哪种架构,哪种SOC, 哪种方案
linux内核里的配置项非常多,厂家一般都会提供一些默认的配置。配置文件在CPU架构目录下的configs目录.
我们可用的配置在arm/configs:
sun8iw7p1smp_android_defconfig sun8iw7p1smp_linux_defconfig
sun8iw7p1smp_android_karaok_defconfig sun8iw7p1smp_min_defconfig
sun8iw7p1smp_android_secure_defconfig sun8iw7p1smp_secure_defconfig
sun8iw7p1smp_defconfig
1). 使用厂商提供的默认配置:
可以把xxx_defconfig 复制成源码根目录下的.config
也可以:
make sun8iw7p1smp_android_defconfig ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
//注意,以后只要在linux里打"make"都需指定ARCH, CROSS_COMPILE,用来指定编译的CPU框架以及使用的编译器。
2). 配置内核:
make menuconfig ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
出现界面后(按空格键选上或取消选中项):
[*] Enable loadable module support --->
[*] Forced module unloading
[ ] Module versioning support
[ ] Source checksum for all modules
Device Drivers --->
<*> Multimedia support --->
[*] Video capture adapters --->
[*] V4L USB devices --->
<*> USB Video Class (UVC)
General setup --->
[ ] Initial RAM filesystem and RAM disk (initramfs/initrd) support
CPU Power Management --->
CPU Frequency scaling --->
[ ] CPU Frequency scaling
后面会讲解这种配置界面背后的原理和选项符号*和M的区别!!!
3). 编译内核
make -j4 uImage ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
编译时出现错误:
drivers/base/firmware_class.c: In function ‘firmware_data_read’:
drivers/base/firmware_class.c:631:9: error: implicit declaration of function ‘kobj_to_dev’ [-Werror=implicit-function-declaration]
原因是那源文件里没有包含 "kobj_to_dev"的头文件.
在drivers/base/firmware_class.c里写上包含头文件
修改好后,接着编译即可.
编译完成后, 在arch/arm/boot/uImage就是编译出来的内核镜像
4). 把板上SD卡接上pc, 把编译出来的镜像文件和编译uboot时产生的script.bin放到SD卡的文件分区里(这里我只有一个分区)
5). 把SD卡重放到板上后,即可读出来到内存来启动(uboot启动中按任意按键进入uboot指令对话区)
ext2load mmc 0:1 0x43000000 /script.bin //加载内核用的配置文件
ext2oad mmc 0:1 0x42000000 /uimage //加载内核镜像
bootm 0x42000000 //运行Linux内核
这里注意:我的SD分区格式是ext2,所以使用指令ext2load,如果你是fat的格式,就使用fatload!!!
如果出现以下错误:
Error: unrecognized/unsupported machine ID (r1 = 0x00000000).
Available machine support:
ID (hex) NAME
00001029 sun8i
Please check your kernel config and/or bootloader.
修正方法: 设置uboot的环境变量:setenv machid 0x1029
saveenv //保存再重新启动内核即可
//启动后,只要有一堆的东西输出即是正常的了
6). 如果想要开发板开机后自动引导内核, 可以修改bootcmd环境变量来完成
setenv bootcmd "ext2load mmc 0:1 0x43000000 /script.bin; ext2load mmc 0:1 0x42000000 /uimage; bootm 0x42000000"
saveenv //保存环境变量
现在来解释编译内核过程中,配置编译选项界面的原理(这个对于想要自己扩展开发板硬件和编写驱动程序xxx.ko的工程师来说是基础知识):
首先从Kconfig这个文件讲起,在Linux内核源码里,有一个Kconfig文件。当执行#make menuconfig时会出现内核的配置界面,所有配置工具都是通过读取"arch/$(ARCH)Kconfig"文件来生成配置界面,这个文件就是所有配置的总入口,它会包含其他目录的Kconfig。Kconfig的基本语法参考:翻译手册
这里展示我的Kconfig
vim ./source/linux-3.4.112/Kconfig
内容如下:
#
# For a description of the syntax of this configuration file,
# see Documentation/kbuild/kconfig-language.txt.
#
#
#
mainmenu "Linux/$ARCH $KERNELVERSION Kernel Configuration" //配置界面显示出来后的头标题//
config SRCARCH
string
option env="SRCARCH"
source "arch/$SRCARCH/Kconfig" //这里指定配置界面首写列出的选项所在的Kconfig//
#source "drivers/0mydrvs/Kconfig"
每个Kconfig文件指向下一个Konfig文件,从而形程树状的目录结构,我们通过这个结构就可以配置到源码目录里面所有的源代码编译选项,最终决定了每个源代码编译的类型:*代表跟随内核编译为.o可执行文件,内核启动时就直接运行;M代表独立于内核镜像编译为xxx.ko的模块,那么需要我们另外指令:make modules ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-来编译出.ko模块,这些模块存放在对应的模块代码所在的目录下。
下面,我们来实际操作一下:
在我的内核源码里,我编写了一个驱动模块:/source/linux-3.4.112/drivers/test190710_v0
1、现在进入/source/linux-3.4.112/drivers/test190710_v0里面,编写一个Kconfig
vim ./Kconfig
内容如下:
menu TEST_V0 //menu定义该选项的名字//
config OPEN_DEV //config定义该选项内的模块配置项,可以很多个//
tristate open_dev //该项目可以有三个选项(tristate),编译后的名字为open_dev//
endmenu //这里结束定义//
2、编辑好Kconfig文件后,在此目录下,还需要一个Makefile文件,Kconfig在编译时会调用到它
vim ./Makefile
内容如下:
obj-$(CONFIG_OPEN_DEV) += OPEN_DEV.o //CONFIG_OPEN_DEV是上一级目录Makefile索引的入口//
OPEN_DEV-y := PA15.o PA20.o PL10.o USER_V0.o //如果在Kconfig中被选为*,则编译为.o文件,且指定了依赖的.o//
3、现在退到上一级目录,打开这个目录的Kconfig
cd ..
vim ./Kconfig
修改内容:
在"endmenu"前添加一句:source "drivers/test190710_v0/Kconfig" //引入刚才下一级Kconfig//
4、打开这个目录的Makefile
vim ./Makefile
添加内容如下:在文末
obj-$(CONFIG_OPEN_DEV) += test190710_v0/
5、OK,退回在内核源码路径,再此输入指令:make menuconfig ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
弹出界面,按照就可以找到刚才添加的选项:
Device Drivers --->
TEST_V0 --->
< > open_dev
总结:Kconfig需要和Makefile文件配合使用!!