注意:该博客基于由crosstool-ng制作的交叉编译器编译出armLinux内核。交叉编译器的具体制作请参考我上两篇博客。
1.fl2440简介
FL2440开发板是飞凌公司使用三星的ARM9 S3C2440 CPU做的一个ARM Linux学习开发板,该CPU是使用armv4t指令集的ARM920T核,工作主频
最高在400MHz。每个CPU厂商在研发并生产出CPU时并不是直接将该CPU推给客户让客户自己研究该CPU特性后再做硬件设计(因为客户不懂这些具体
的CPU使用细节,而研发该CPU的公司会更懂它),而会先使用该CPU设计出母板(demo板,如SMDK2440),在母板上尽可能将该CPU的硬件使用信
息展示给客户,客户再参考母板根据实际的功能、市场需求来高度定制自己的硬件,例如产品不需要显示则可以把母板中的LCD硬件部分去掉、CS8900网
卡供货存在问题那就换成DM9000等等。
相应的CPU厂商在开发出相应的硬件demo板后,也不是直接丢给客户来开发相应的软件,而是会安排软件研发人员针对母板硬件完成u-boot和Linux内
核的开发,以支持相应硬件。因为我们的硬件是参考母板来定制开发的,所以我们的软件移植(u-boot和linux内核)也可以在相关源码里的母板代码上做
针对硬件的修改来支持我们的产品。
在FL2440开发板的移植过程中,我们将以SMDK2440为模版在它的基础上做些修改来支持我们相应的硬件。在开始移植之前,我们先创建FL2440整个
项目的目录框架:
zhanghang@ubuntu:~$ mkdir fl2440
zhanghang@ubuntu:~$ cd fl2440/
zhanghang@ubuntu:~/fl2440$ mkdir -p {crosstool,bootloader,linux/{kernel,rootfs},driver,3rdparty,program,images}
zhanghang@ubuntu:~/fl2440$ tree
.
├── 3rdparty
├── bootloader
├── crosstool
├── driver
├── images
├── linux
│ ├── kernel
│ └── rootfs
└── program
9 directories, 0 files
2.源码修改
Linux 是一个源码开放的操作系统,无论是普通用户还是企业用户都可以编写自己的内核代码,再加上对标准内核的裁剪从而制作出适合自己的操作系统。Linux操作系统有很多发行版本,如Redhat, CentOS, Ubuntu等等,但所有的这些Linux操作系统都是选择一个Linux内核稳定版本,外加不同的C基础库和应用程序构建的一个操作系统。嵌入式Linux系统开发其本质就是我们拿到源码并进行修改DIY(Do It Yourself)一个属于我们自己的操作系统,这个过程包括Booloader和Linux内核(裁剪)移植、驱动模块编写、根文件系统制作、第三方应用程序移植等,这个工作叫做BSP(Board Support Packet,板级支持包)开发,这里面需要相应的硬件协议、操作系统、以及C语言和数据结构等知识,难度较高。
zhanghang@ubuntu:~$ cd fl2440/linux/
zhanghang@ubuntu:~/fl2440/linux$ wget https://www.kernel.org/pub/linux/kernel/v3.x/linux-3.0.tar.bz2 //下载内核
zhanghang@ubuntu:~/fl2440/linux$ tar -xjf linux-3.0.tar.bz2 //解压
zhanghang@ubuntu:~/fl2440/linux$ cd linux-3.0/
zhanghang@ubuntu:~/fl2440/linux/linux-3.0$ ls
arch Documentation init lib README sound
block drivers ipc MAINTAINERS REPORTING-BUGS tools
COPYING firmware Kbuild Makefile samples usr
CREDITS fs Kconfig mm scripts virt
crypto include kernel net security
修改1:
SMDK2440上使用的是16MHz的晶振,而FL2440上使用的是12MHz的晶振,所以开发板相应代码要做修改,linux-3.0/arch/arm/mach-s3c2440/下支持很多使用S3C2440 CPU做的开发板(大家可以使用ls命令看看具体有哪些开发板,今后驱动添加我们可以参考其他开发板如mini2440的信息),我们以mach-smdk2440.c为原型来进行修改:
zhanghang@ubuntu:~/fl2440/linux/linux-3.0$ vim arch/arm/mach-s3c2440/mach-smdk2440.c
160 static void __init smdk2440_map_io(void)
161 {
162 s3c24xx_init_io(smdk2440_iodesc, ARRAY_SIZE(smdk2440_iodesc));
163 // s3c24xx_init_clocks(16934400);
164 s3c24xx_init_clocks(12000000);
165 s3c24xx_init_uarts(smdk2440_uartcfgs, ARRAY_SIZE(smdk2440_uartcfgs));
166 }
167
修改2:
我们的u-boot给Linux内核传的machine ID值为1999,而Linux内核里smdk2440开发板对应的machine ID是362,所以我们要修改内核代码让smdk2440的machine ID与u-boot里的保持一致,这里我们在源码中将两个machine ID值互换:
enp2611 ARCH_ENP2611 ENP2611 356
s3c2440 ARCH_S3C2440 S3C2440 1999
gumstix ARCH_GUMSTIX GUMSTIX 373
...
exeda MACH_EXEDA EXEDA 1994
mini2440 MACH_MINI2440 MINI2440 362
colibri300 MACH_COLIBRI300 COLIBRI300 2000
修改3:
samsung的串口驱动设备名字默认叫ttySAC,而我们一般使用ttyS,所以将源码中的设备名改掉:
55 /* UART name and device definitions */
56
57 //#define S3C24XX_SERIAL_NAME "ttySAC"
58 #define S3C24XX_SERIAL_NAME "ttyS"
59 #define S3C24XX_SERIAL_MAJOR 204
60 #define S3C24XX_SERIAL_MINOR 64
修改4:
修改顶层Makefile的ARCH为arm, CROSS_COMPILE为我们自己相应的交叉编译器:
zhanghang@ubuntu:~$ cd /opt/xtools/arm920t/bin/
zhanghang@ubuntu:/opt/xtools/arm920t/bin$ ls
arm-arm920t-linux-gnueabi-addr2line arm-arm920t-linux-gnueabi-gprof
arm-arm920t-linux-gnueabi-ar arm-arm920t-linux-gnueabi-ld
arm-arm920t-linux-gnueabi-as arm-arm920t-linux-gnueabi-ldd
arm-arm920t-linux-gnueabi-c++ arm-arm920t-linux-gnueabi-nm
arm-arm920t-linux-gnueabi-cc arm-arm920t-linux-gnueabi-objcopy
arm-arm920t-linux-gnueabi-c++filt arm-arm920t-linux-gnueabi-objdump
arm-arm920t-linux-gnueabi-cpp arm-arm920t-linux-gnueabi-populate
arm-arm920t-linux-gnueabi-ct-ng.config arm-arm920t-linux-gnueabi-ranlib
arm-arm920t-linux-gnueabi-g++ arm-arm920t-linux-gnueabi-readelf
arm-arm920t-linux-gnueabi-gcc arm-arm920t-linux-gnueabi-size
arm-arm920t-linux-gnueabi-gcc-4.6.0 arm-arm920t-linux-gnueabi-strings
arm-arm920t-linux-gnueabi-gcov arm-arm920t-linux-gnueabi-strip
195 #ARCH ?= $(SUBARCH)
196 #CROSS_COMPILE ?= $(CONFIG_CROSS_COMPILE:"%"=%)
197 ARCH ?= arm
198 CROSS_COMPILE ?= /opt/xtools/arm920t/bin/arm-arm920t-linux-gnueabi- //这里改成大家自己的交叉编译器,注意arm-arm920t-linux-gnueabi- 后面应该紧跟回车,不能有其他任何字符
下面是我的交叉编译器位置:
zhanghang@ubuntu:~$ cd /opt/xtools/arm920t/bin/
zhanghang@ubuntu:/opt/xtools/arm920t/bin$ ls
arm-arm920t-linux-gnueabi-addr2line arm-arm920t-linux-gnueabi-gprof
arm-arm920t-linux-gnueabi-ar arm-arm920t-linux-gnueabi-ld
arm-arm920t-linux-gnueabi-as arm-arm920t-linux-gnueabi-ldd
arm-arm920t-linux-gnueabi-c++ arm-arm920t-linux-gnueabi-nm
arm-arm920t-linux-gnueabi-cc arm-arm920t-linux-gnueabi-objcopy
arm-arm920t-linux-gnueabi-c++filt arm-arm920t-linux-gnueabi-objdump
arm-arm920t-linux-gnueabi-cpp arm-arm920t-linux-gnueabi-populate
arm-arm920t-linux-gnueabi-ct-ng.config arm-arm920t-linux-gnueabi-ranlib
arm-arm920t-linux-gnueabi-g++ arm-arm920t-linux-gnueabi-readelf
arm-arm920t-linux-gnueabi-gcc arm-arm920t-linux-gnueabi-size
arm-arm920t-linux-gnueabi-gcc-4.6.0 arm-arm920t-linux-gnueabi-strings
arm-arm920t-linux-gnueabi-gcov arm-arm920t-linux-gnueabi-strip
修改5:
添加DM9000网卡的支持,没有网卡驱动,开发板只能通过串口进行连接,更不能上网,通过ssh或dropbear远程登陆。
zhanghang@ubuntu:~/fl2440/linux/linux-3.0$ vim arch/arm/mach-s3c2440/mach-smdk2440.c
#include <linux/dm9000.h>//add
//并添加如下代码
#define DM9000_BASE (S3C2410_CS4 + 0x300)
static struct resource s3c_dm9000_resource[] = {
[0] = {
.start = DM9000_BASE,
.end = DM9000_BASE + 3,
.flags = IORESOURCE_MEM
},
[1] = {
.start = DM9000_BASE + 4,
.end = DM9000_BASE + 7,
.flags = IORESOURCE_MEM
},
[2] = {
.start = IRQ_EINT7,
.end = IRQ_EINT7,
.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE,
}
};
static struct dm9000_plat_data s3c_dm9000_pdata = {
.flags = (DM9000_PLATF_16BITONLY | DM9000_PLATF_NO_EEPROM),
};
static struct platform_device s3c_device_dm9000 = {
.name = "dm9000",
.id = -1,
.num_resources = ARRAY_SIZE(s3c_dm9000_resource),
.resource = s3c_dm9000_resource,
.dev = {
.platform_data = &s3c_dm9000_pdata,
},
};
//在结构体static struct platform_device *smdk2440_devices[] __initdata中添加dm9000网卡支持
static struct platform_device *smdk2440_devices[] __initdata = {
&s3c_device_ohci,
&s3c_device_lcd,
&s3c_device_wdt,
&s3c_device_i2c0,
&s3c_device_iis,
&s3c_device_dm9000,//add
};
2.内核配置和编译
zhanghang@ubuntu:~/fl2440/linux/linux-3.0$ find -name *.c | wc -l
16198
zhanghang@ubuntu:~/fl2440/linux/linux-3.0$ find -name *.S | wc -l
1223
Linux-3.0内核有16198个C文件和1223个汇编文件,就这一份源码支持了不同体系结构CPU, 并且包含了绝大部分硬件的驱动源码。那么根据不同的硬件设计和功能需求,定制化编译生成一个具有特定用途的嵌入式Linux系统image将是一场灾难,好在Linux的源码编译系统有一个非常巧妙的铁三角关系,导致这个过程显得没那么复杂。这个铁三角分别是Kconfig、.config和Makefile文件,他们三者之间的关系简单来说就是去饭店点菜:Kconfig是菜单,.config就是你点的菜,Makefile是做法。
**Kconfig:**一个文本形式的文件,存在内核源码中的每一个文件夹下,内核配置命令make menuconfig读取相应的Konfig文件生成菜单界面;
**.config:**隐藏文件存放在内核源码顶层目录中,make menuconfig命令配置的结果,里面的每个选项用来指导Makefile哪些C文件需要编译,哪些不需要编译;
**Makefile:**一个文本形式的文件,存在内核源码中的每一个文件夹下,用来控制编译该目录下的源码编译;
zhanghang@ubuntu:~/fl2440/linux/linux-3.0$ make s3c2410_defconfig
zhanghang@ubuntu:~/fl2440/linux/linux-3.0$ export TERM=vt100
zhanghang@ubuntu:~/fl2440/linux/linux-3.0$ make menuconfig
我们是使用S3C2440做的SMDK2440开发板,所有其他的开发板都不应该选择
System Type --->
S3C2400 Machines ---> 里面全部不选
S3C2410 Machines ---> 里面全部不选
S3C2412 Machines ---> 里面全部不选
S3C2416 Machines ---> 里面全部不选
S3C2440 and S3C2442 Machines --->
[*] SMDK2440
[*] SMDK2440 with S3C2440 CPU module
其它全部不选
S3C2443 Machines --->
里面全部不选
... ...
我们的交叉编译器使用的是EABI接口,所以这里一定要修改配置,否则跑不起来。
Kernel Features --->
[*] Use the ARM EABI to compile the kernel
[*] Allow old ABI binaries to run with this kernel (EXPERIMENTAL) (NEW)
为防止为串口驱动冲突:
Device Drivers --->
Character devices --->
Serial drivers --->
把<> 8250/16550 and compatible serial support取消
<*> Samsung SoC serial support
[ ] Samsung SoC serial debug
[*] Support for console on Samsung SoC serial port
<*> Samsung S3C2440/S3C2442/S3C2416 Serial port support
写个shell脚本完成这些相应命令
zhanghang@ubuntu:~/fl2440/linux/linux-3.0$ vim build.sh
#!/bin/bash
make
mkimage -A arm -O linux -T kernel -C none -a 30008000 -e 30008040 -n "Linux Kernel" -d arch/arm/boot/zImage linuxroms3c2440.bin
chmod a+x linuxrom-s3c2440.bin
Shell脚本解释说明:
make命令是编译Linux内核源码,编译完成后将生产arch/arm/boot/zImage内核启动文件。
zImage并不能被u-boot的bootm命令启动,而需要使用mkimage工具在其前面加上bootm命令启动内核所需要的64字节(0x40)头信息方可启动,处
理后的文件一般叫做uImage。
mkimage命令是由u-boot编译产生(u-boot-2010.09/tools/mkimage),在编译完成u-boot后我们需要将它以root权限拷贝到系统/bin路径下。
mkimage -A arm -O linux -T kernel -C none -a 30008000 -e 30008040 -n “Linux Kernel” -d arch/arm/boot/zImage linuxrom- s3c2440.bin
-A arm 指定ARCH为arm
-O linux 指定操作系统(OS)为Linux
-T kernel 指定类型(Type)为内核
-C none 指定压缩类型为未压缩,zImage里有自解压的代码;
-a 30008000 指定Image加载的地址, u-boot下使用tftp命令下载linux内核到内存的相应地址
-e 30008040 指定Linux内核的入口地址, uImage的地址在30008000,uImage是在zImage前面加了64字节(0x40)头,所以内核
zImage入口地址为30008040。
-n “Linux Kernel” 指定Image的名字
-d arch/arm/boot/zImage 指定zImage文件所在位置
linuxrom-s3c2440.bin 指定生成的uImage文件名
chmod a+x linuxrom-s3c2440.bin 将生成的uImage文件(linuxrom-s3c2440.bin)变成绿色显眼。
zhanghang@ubuntu:~/fl2440/linux/linux-3.0$ ./build.sh
...//编译过程可能出现错误,由于我是用全新的ubuntu14.04去做的,出现如下错误:
...
IHEX2FW firmware/emi26/loader.fw
IHEX2FW firmware/emi26/firmware.fw
IHEX2FW firmware/emi26/bitstream.fw
IHEX2FW firmware/emi62/loader.fw
IHEX2FW firmware/emi62/bitstream.fw
IHEX2FW firmware/emi62/spdif.fw
IHEX2FW firmware/emi62/midi.fw
./build.sh: line 3: mkimage: command not found
chmod: cannot access ‘linuxrom-s3c2440.bin’: No such file or directory
解决办法:
zhanghang@ubuntu:~/fl2440/linux/linux-3.0$ sudo apt-get install u-boot-tools
...//最后编译结束,整个过程时常大约20分钟
KSYM .tmp_kallsyms2.S
AS .tmp_kallsyms2.o
LD vmlinux
SYSMAP System.map
SYSMAP .tmp_System.map
OBJCOPY arch/arm/boot/Image
Kernel: arch/arm/boot/Image is ready
GZIP arch/arm/boot/compressed/piggy.gzip
AS arch/arm/boot/compressed/piggy.gzip.o
SHIPPED arch/arm/boot/compressed/lib1funcs.S
AS arch/arm/boot/compressed/lib1funcs.o
LD arch/arm/boot/compressed/vmlinux
OBJCOPY arch/arm/boot/zImage
Kernel: arch/arm/boot/zImage is ready
Building modules, stage 2.
MODPOST 360 modules
LD [M] kernel/configs.ko
Image Name: Linux Kernel
Created: Sun Apr 28 07:06:03 2019
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 2277412 Bytes = 2224.04 kB = 2.17 MB
Load Address: 30008000
Entry Point: 30008040//编译结束
zhanghang@ubuntu:~/fl2440/linux/linux-3.0$ ls
arch firmware lib net tools
block fs linuxrom-s3c2440.bin README usr
build.sh include MAINTAINERS REPORTING-BUGS virt
COPYING init Makefile samples vmlinux
CREDITS ipc mm scripts vmlinux.o
crypto Kbuild modules.builtin security
Documentation Kconfig modules.order sound
drivers kernel Module.symvers System.map
zhanghang@ubuntu:~/fl2440/linux/linux-3.0$ du -h linuxrom-s3c2440.bin
2.2M linuxrom-s3c2440.bin
编译完成后将会生成linuxrom-s3c2440.bin(约2.2M),该文件即为u-boot里bootm命令能识别的uImage文件。
此时,该文件就可以移植到开发板上。但是由于没有根文件系统,启动会停止并提示没有根文件系统。
下一节我会讲根文件系统的制作。