1.在上章-移植uboot里.咱们来分析下uboot是如何进入到内核的html
首先,uboot启动内核是经过bootcmd命令行实现的,在咱们以前移植的bootcmd命令行以下所示:linux
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变量ui
1.4最终,便跳到内核执行代码,步骤以下所示:spa
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
cd ../../.. 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, } };
上面部分宏的定义,以下所示:
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/下载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个文件
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下的全部设备节点
并给rcS加上可执行权限:
sudo chmod +x etc/init.d/rcS //使脚本rcS可以执行命令
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系统