開發板fl2440Linux內核交叉編譯

注意:該博客基於由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文件。
此時,該文件就可以移植到開發板上。但是由於沒有根文件系統,啓動會停止並提示沒有根文件系統。
下一節我會講根文件系統的製作。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章