1.在上章-移植uboot里.我們來分析下uboot是如何進入到內核的 首先,uboot啟動內核是通過bootcmd命令行實現的,在我們之前移植的bootcmd命令行如下所示: 1.1然後我們進入cmd_bootm.c,找到對應的bootm命令對應的do_bootm(): 上面的boot_os_fn ...
1.在上章-移植uboot里.我們來分析下uboot是如何進入到內核的
首先,uboot啟動內核是通過bootcmd命令行實現的,在我們之前移植的bootcmd命令行如下所示:
bootcmd=nand read 0x30000000 kernel; bootm 0x30000000 //bootm:從0x30000000處啟動內核
1.1然後我們進入cmd_bootm.c,找到對應的bootm命令對應的do_bootm():
int do_bootm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { boot_os_fn *boot_fn; //boot_fn是個數組函數 ... .. boot_fn(0, argc, argv, &images); //調用數組函數 ... ... }
上面的boot_os_fn是個typedef型,如下圖所示:
1.2由於定義了巨集CONFIG_BOOTM_LINUX,最終會跳轉到do_bootm ->do_bootm_linux()
代碼如下所示:
int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t *images) { /* No need for those on ARM */ if (flag & BOOTM_STATE_OS_BD_T || flag & BOOTM_STATE_OS_CMDLINE) return -1; if (flag & BOOTM_STATE_OS_PREP) { boot_prep_linux(images); return 0; } if (flag & BOOTM_STATE_OS_GO) { boot_jump_linux(images); return 0; } boot_prep_linux(images); //該函數會將各個tag參數保存在指定位置,比如:記憶體tag、bootargs環境變數tag、串口tag等 boot_jump_linux(images); //該函數會跳轉到內核起始地址 return 0; }
1.3最終跳轉到do_bootm ->do_bootm_linux-> boot_jump_linux()
代碼如下所示:
static void boot_jump_linux(bootm_headers_t *images) { unsigned long machid = gd->bd->bi_arch_number; //獲取機器ID char *s; void (*kernel_entry)(int zero, int arch, uint params); unsigned long r2; kernel_entry = (void (*)(int, int, uint))images->ep; //設置kernel_entry()的地址為0x30000000 s = getenv("machid"); //判斷環境變數machid是否設置,若設置則使用環境變數里的值 if (s) { strict_strtoul(s, 16, &machid); //重新獲取機器ID printf("Using machid 0x%lx from environment\n", machid); //使用環境變數的machid }
... ...
r2 = gd->bd->bi_boot_params; //獲取tag參數地址, gd->bd->bi_boot_params在setup_start_tag()函數里被設置 kernel_entry(0, machid, r2); //跳轉到0x30000000,r0=0,r1=機器ID,r2=tag參數地址 }
上面的machid預設值為MACH_TYPE_SMDK2410(也就是193),我們也可以在環境變數里設置machid變數
1.4最終,便跳到內核執行代碼,步驟如下所示:
- 1)根據R1(機器ID),來判斷內核是否支持該機器,若支持則初始化機器相關函數
- 2)解析TAG參數,初始化串口,設置記憶體等
- 3)掛載根文件系統,並執行應用程式
2.接下來便從網上下載3.4.2內核來移植.
2.1修改Makefile,修改配置
tar xjf linux-3.4.2.tar.bz2 cd linux-3.4.2/ vi Makefile
找到下麵這句話:
ARCH ?= $(SUBARCH) CROSS_COMPILE ?= $(CONFIG_CROSS_COMPILE:"%"=%)
改為:
ARCH ?= arm
CROSS_COMPILE ?= arm-linux-
(PS:我使用的是4.3.2,若交叉編譯工具版本太低,可能無法編譯)
2.2 配置編譯
cd arch/arm/configs //由於我們板子是arm板,進入該目錄 ls *2440* //找到有mini2440_defconfig、 ls *2410* //找到有s3c2410_defconfig make s3c2410_defconfig //配置2410, 更新.config配置文件 make uImage //編譯,生成uImage cp uImage /work/nfs_root/ uImage_new //用nfs下載
3進入uboot燒寫
nfs 32000000 192.168.2.106:/work/nfs_root/uImage_new bootm 32000000
如下圖所示,發現串口輸出亂碼:
出現這個問題,可以先看看bootargs命令行的串口設置是否正確、uboot傳遞的機器ID是否正確.
3.1找到bootargs命令行的串口沒有設置波特率,修改bootargs:
set bootargs root=/dev/mtdblock3 console=ttySAC0,115200
3.2 測試機器ID是否正確
在我們1.3小節代碼分析里,講到過uboot傳遞進來的機器ID可以通過環境變數machid來設置
所以任意設置一個ID,這樣再次啟動內核時,內核識別不出來,就會列印出所有設備對應的機器ID
進入uboot,輸入:
set machid 33333 tftp 32000000 uImage bootm 32000000
如下圖所示,由於內核不支持這個機器ID,所以列印出內核能支持的ID表:
由於我們板子是2440,所以測試7cf(mini2440)以及16a(smdk2440)這兩個機器ID,是否支持我們開發板
發現只有7cf(mini2440)這個ID,有串口輸出正常.
來看看16a(smdk2440)為什麼串口亂碼,進入mach-smdk2440.c( 位於arch/arm/mach-s3c24xx)
找到問題出在smdk2440_map_io():
static void __init smdk2440_map_io(void) { s3c24xx_init_io(smdk2440_iodesc, ARRAY_SIZE(smdk2440_iodesc)); s3c24xx_init_clocks(16934400); //初始化時鐘clock s3c24xx_init_uarts(smdk2440_uartcfgs, ARRAY_SIZE(smdk2440_uartcfgs)); }
由於我們板子上的晶振是12Mhz,而mdk2440_map_io()里,初始化的時鐘是基於16934400hz的晶振.
所以將:
s3c24xx_init_clocks(16934400); //初始化時鐘clock
改為:
s3c24xx_init_clocks(12000000); //初始化時鐘clock
然後重新編譯uImage:
make s3c2410_defconfig //將mach-s3c2440.c配置進內核 make uImage cp uImage /work/nfs_root/ uImage_new
進入uboot,輸入:
set machid 16a nfs 32000000 192.168.2.106:/work/nfs_root/uImage_new boom 32000000
啟動內核,列印如下圖所示:
如上圖所示,內核創建了8個分區,而我們移植的uboot只有4個分區,代碼如下:
0x00000000-0x00040000 : "bootloader" //存放uboot 0x00040000-0x00060000 : "params" //存放環境變數 0x00060000-0x00260000 : "kernel" //存放內核 0x00260000-0x10000000 : "rootfs" //存放文件系統
uboot傳遞的文件系統路徑root=/dev/mtdblock3,所以內核便卡死在啟動文件系統上
4.所以接下來我們來修改內核分區
4.1在si里搜索上圖出現的”S3C2410 flash partition”欄位
如下圖所示:
找到位於common-smdk.c中,裡面有個數組smdk_default_nand_part[],內容如下所示:
4.2接下來便修改smdk_default_nand_part[]數組(位於arch/arm/mach-s3c24xx/common-smdk.c)
修改為:
static struct mtd_partition smdk_default_nand_part[] = { [0] = { .name = "bootloader", //0x00000000-0x00040000 .size = SZ_256K, .offset = 0, }, [1] = { .name = "params", //0x00040000-0x00060000 .offset = MTDPART_OFS_APPEND, .size = SZ_128K, }, [2] = { .name = "kernel", //0x00060000-0x00260000 .offset = MTDPART_OFS_APPEND, .size = SZ_2M, }, [3] = { .name = "rootfs", //0x00260000-0x10000000 .offset = MTDPART_OFS_APPEND, .size = MTDPART_SIZ_FULL, } };
上面部分巨集的定義,如下所示:
- MTDPART_OFS_RETAIN: 填在offset里,表示先後保留多少size空間大小
- MTDPART_OFS_NXTBLK: 填在offset里,表示從下一個塊開始
- MTDPART_OFS_APPEND: 填在offset里,表示該分區位置附加在上個分區結束的地址上
- MTDPART_SIZ_FULL: 填在size里,表示剩下的記憶體size都歸於該分區
4.3若需要mini2440的機器ID,則還需要修改mini2440單板對應的mach-mini2440.c
因為該單板的mtd分區也不對,將裡面的mini2440_default_nand_part[]內容改為和上面一樣
4.4改好後,重啟內核,發現內核還是啟動不了以前的yaffs文件系統
如下圖所示:
表示不支持該內核不支持yaffs文件系統,然後嘗試使用ext3 ext2 cramfs vfat msdos iso9660等來掛載
4.5 嘗試使用以前的jffs2文件系統
重新燒寫jffs2,設置uboot環境變數,啟動內核,列印如下圖:
上圖,表示jffs2已掛載,但是找不到init程式,因為這個文件系統的glibc庫是交叉編譯3.4版本的,由於3.4內核的交叉編譯是4.3版本,所以不支持,接下來我們便重新製作文件系統
5.構造根文件系統
5.1首先編譯安裝busybox(參考以前的busybox安裝章節)
進入https://busybox.net/oldnews.html下載busybox 1.20.0
tar -xjf busybox-1.20.0.tar.bz2 cd busybox-1.20.0 make menuconfig //設置交叉編譯首碼
進入Busybox Settings --->Build Options --->
() Cross Compiler prefix
在彈出的對話框裡面寫入:arm-linux-
make //編譯 mkdir /work/nfs_root/fs_mini_mdev_new //創建要安裝的文件系統目錄 make install CONFIG_PREFIX=/work/nfs_root/fs_mini_mdev_new //指定安裝位置
5.2 安裝glibc庫
輸入$PATH找到交叉編譯位於/work/tools/arm-linux-gcc-4.3.2/usr/local/arm/4.3.2位置,
通過find -name lib,找到有以下幾個lib
由於ARM9屬於ARMv4T架構,所以拷貝上面兩條紅線處的lib到fs_mini_mdev_new里
mkdir /work/nfs_root/fs_mini_mdev_new/lib mkdir /work/nfs_root/fs_mini_mdev_new/usr/lib -p cp arm-none-linux-gnueabi/libc/armv4t/usr/lib/*.so* /work/nfs_root/fs_mini_mdev_new/usr/lib -d /* -d:保持鏈接 */ cp arm-none-linux-gnueabi/libc/armv4t/lib/*.so* /work/nfs_root/fs_mini_mdev_new/lib -d
5.3 構造etc目錄
在etc目錄下,需要構造以下3個文件
- etc/inittab : init進程會根據inittab文件里,來創建其它子進程,比如
- etc/init.d/rcS:腳本文件,裡面用來執行命令,比如設置網卡,使用mount -a來裝載/etc/fstab中的文件系統
- etc/fstab :裡面保存要被掛載的哪個文件系統,比如proc、sysfs、tmpfs、devpts等系統
1)構造/etc/inittab
cd cd /work/nfs_root/fs_mini_mdev_new/ mkdir etc/ vi etc/inittab
添加以下幾句:
::sysinit:/etc/init.d/rcS //內核啟動時,執行/etc/init.d/rcS console::askfirst:-/bin/sh //啟動console對應的-/bin/sh進程之前,等待用戶按enter鍵 ::ctrlaltdel:/sbin/reboot //按下ctrl+alt+del組合鍵時,啟動reboot命令 ::shutdown:/bin/umount -a -r //系統關機前,卸載所有文件系統
2)構造etc/init.d/rcS
mkdir etc/init.d/
vi etc/init.d/rcS
添加以下幾句:
mount -a //裝載/etc/fstab中的文件系統 echo /sbin/mdev > /proc/sys/kernel/hotplug //使/sbin/medv指向hotplug,從而支持熱拔插 mdev -s //使用medv命令自動創建/dev下的所有設備節點
3)構造etc/fstab
PS:使用mdev命令需要sysfs、tmpfs、devpts這3個文件系統的支持
mkdir proc/ //創建proc要掛載的目錄 mkdir sys/ //創建sysfs要掛載的目錄, mkdir dev/pts -p //創建devpts要掛載的目錄 vi etc/fstab
添加以下幾句
# device mount-point type options dump fsck order proc /proc proc defaults 0 0 tmpfs /tmp tmpfs defaults 0 0 sysfs /sys sysfs defaults 0 0 devpts /dev/pts devpts defaults 0 0
5.4構造其它文件/目錄
1)創建終端文件(dev/console和dev/null)
sudo mknod –m 660 dev/console c 5 1 sudo mknod –m 660 dev/null c 1 3
2)創建其它目錄
mkdir mnt tmp root
6.製作jffs2映像文件
由於mkfs.jffs2工具之前已經安裝好了,所以直接使用mkfs.jffs2命令:
cd /work/nfs_root/ //返回到上個目錄 mkfs.jffs2 -n -s 2048 -e 128KiB -d fs_mini_mdev_new -o fs_mini_mdev_new.jffs2 //-n:表示每塊不添加清除標記,-s:NAND的每頁為2k,-e: NAND的每塊為128kb //-d fs_mini_mdev_new:表示要製作的根文件系統文件 //-o fs_mini_mdev_new.jffs2:表示生成的映像文件
7.燒寫jffs2,啟動內核
nfs 30000000 192.168.2.106:/work/nfs_root/fs_mini_mdev_new.jffs2 nand erase.part rootfs nand write.jffs2 30000000 260000 $filesize set bootargs console=ttySAC0,115200 root=/dev/mtdblock3 rootfstype=jffs2 nfs 32000000 192.168.2.106:/work/nfs_root/uImage_new bootm 32000000
7.1啟動內核
列印如下圖所示:
進入si,搜索exitcode,找到0x00000004對應的巨集定義是SIGILL,表示非法指令
是因為arm-linux-gcc-4.3.2是使用的EABI介面,內核由於未配置,所以出現非法
7.2 配置內核支持EABI
輸入make menuconfig,搜索EABI,找到位於:
kernel feature->
[*] Use the ARM EABI to compile the kernel
make uImage
重新編譯燒寫內核就沒問題了
未完待續,下章學習如何使內核支持yaffs系統