1.在上章-移植uboot里.我们来分析下uboot是如何进入到内核的

首先,uboot启动内核是通过bootcmd命令行实现的,在我们之前移植的bootcmd命令行如下所示:

  1. bootcmd=nand read 0x30000000 kernel; bootm 0x30000000 //bootm:从0x30000000处启动内核

1.1然后我们进入cmd_bootm.c,找到对应的bootm命令对应的do_bootm():

  1. int do_bootm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
  2. {
  3. boot_os_fn *boot_fn; //boot_fn是个数组函数
  4. ... ..
  5.  
  6. boot_fn(, argc, argv, &images); //调用数组函数
  7. ... ...
  8. }

上面的boot_os_fn是个typedef型,如下图所示:

1.2由于定义了宏CONFIG_BOOTM_LINUX,最终会跳转到do_bootm ->do_bootm_linux()

代码如下所示:

  1. int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t *images)
  2. {
  3. /* No need for those on ARM */
  4. if (flag & BOOTM_STATE_OS_BD_T || flag & BOOTM_STATE_OS_CMDLINE)
  5. return -;
  6. if (flag & BOOTM_STATE_OS_PREP) {
  7. boot_prep_linux(images);
  8. return ;
  9. }
  10. if (flag & BOOTM_STATE_OS_GO) {
  11. boot_jump_linux(images);
  12. return ;
  13. }
  14.  
  15. boot_prep_linux(images); //该函数会将各个tag参数保存在指定位置,比如:内存tag、bootargs环境变量tag、串口tag等
  16. boot_jump_linux(images); //该函数会跳转到内核起始地址
  17. return ;
  18. }

1.3最终跳转到do_bootm ->do_bootm_linux-> boot_jump_linux()

代码如下所示:

  1. static void boot_jump_linux(bootm_headers_t *images)
  2. {
  3. unsigned long machid = gd->bd->bi_arch_number; //获取机器ID
  4. char *s;
  5. void (*kernel_entry)(int zero, int arch, uint params);
  6. unsigned long r2;
  7. kernel_entry = (void (*)(int, int, uint))images->ep; //设置kernel_entry()的地址为0x30000000
  8. s = getenv("machid"); //判断环境变量machid是否设置,若设置则使用环境变量里的值
  9. if (s) {
  10. strict_strtoul(s, , &machid); //重新获取机器ID
  11. printf("Using machid 0x%lx from environment\n", machid); //使用环境变量的machid
  12. }
         ... ...
    r2 = gd->bd->bi_boot_params; //获取tag参数地址, gd->bd->bi_boot_params在setup_start_tag()函数里被设置
  13. kernel_entry(, machid, r2); //跳转到0x30000000,r0=0,r1=机器ID,r2=tag参数地址
  14. }

上面的machid默认值为MACH_TYPE_SMDK2410(也就是),我们也可以在环境变量里设置machid变量

1.4最终,便跳到内核执行代码,步骤如下所示:

  • 1)根据R1(机器ID),来判断内核是否支持该机器,若支持则初始化机器相关函数
  • 2)解析TAG参数,初始化串口,设置内存等
  • 3)挂载根文件系统,并执行应用程序

2.接下来便从网上下载3.4.2内核来移植.

2.1修改Makefile,修改配置

  1. tar xjf linux-3.4..tar.bz2
  2. cd linux-3.4./
  3. vi Makefile

找到下面这句话:

  1. ARCH ?= $(SUBARCH)
  2. CROSS_COMPILE ?= $(CONFIG_CROSS_COMPILE:"%"=%)

改为:

  1. ARCH ?= arm
  2. CROSS_COMPILE ?= arm-linux-

(PS:我使用的是4.3.2,若交叉编译工具版本太低,可能无法编译)

2.2 配置编译

  1. cd arch/arm/configs //由于我们板子是arm板,进入该目录
  2. ls ** //找到有mini2440_defconfig、
  3. ls ** //找到有s3c2410_defconfig
  4.  
  5. cd ../../..
  6. make s3c2410_defconfig //配置2410, 更新.config配置文件
  7. make uImage //编译,生成uImage
  8. cp uImage /work/nfs_root/ uImage_new //用nfs下载

3进入uboot烧写

  1. nfs 192.168.2.106:/work/nfs_root/uImage_new
  2. bootm

如下图所示,发现串口输出乱码:

出现这个问题,可以先看看bootargs命令行的串口设置是否正确、uboot传递的机器ID是否正确.

3.1找到bootargs命令行的串口没有设置波特率,修改bootargs:

  1. set bootargs root=/dev/mtdblock3 console=ttySAC0,

3.2 测试机器ID是否正确

在我们1.3小节代码分析里,讲到过uboot传递进来的机器ID可以通过环境变量machid来设置

所以任意设置一个ID,这样再次启动内核时,内核识别不出来,就会打印出所有设备对应的机器ID

进入uboot,输入:

  1. set machid
  2. tftp uImage
  3. bootm

如下图所示,由于内核不支持这个机器ID,所以打印出内核能支持的ID表:

由于我们板子是2440,所以测试7cf(mini2440)以及16a(smdk2440)这两个机器ID,是否支持我们开发板

发现只有7cf(mini2440)这个ID,有串口输出正常.

来看看16a(smdk2440)为什么串口乱码,进入mach-smdk2440.c( 位于arch/arm/mach-s3c24xx)

找到问题出在smdk2440_map_io():

  1. static void __init smdk2440_map_io(void)
  2. {
  3. s3c24xx_init_io(smdk2440_iodesc, ARRAY_SIZE(smdk2440_iodesc));
  4. s3c24xx_init_clocks(); //初始化时钟clock
  5. s3c24xx_init_uarts(smdk2440_uartcfgs, ARRAY_SIZE(smdk2440_uartcfgs));
  6. }

由于我们板子上的晶振是12Mhz,而mdk2440_map_io()里,初始化的时钟是基于16934400hz的晶振.

所以将:

  1. s3c24xx_init_clocks(); //初始化时钟clock

改为:

  1. s3c24xx_init_clocks(); //初始化时钟clock

然后重新编译uImage:

  1. make s3c2410_defconfig //将mach-s3c2440.c配置进内核
  2. make uImage
  3. cp uImage /work/nfs_root/ uImage_new

进入uboot,输入:

  1. set machid 16a
  2. nfs 192.168.2.106:/work/nfs_root/uImage_new
  3. boom

启动内核,打印如下图所示:

如上图所示,内核创建了个分区,而我们移植的uboot只有个分区,代码如下:

  1. 0x00000000-0x00040000 : "bootloader" //存放uboot
  2. 0x00040000-0x00060000 : "params" //存放环境变量
  3. 0x00060000-0x00260000 : "kernel" //存放内核
  4. 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)

修改为:

  1. static struct mtd_partition smdk_default_nand_part[] = {
  2. [] = {
  3. .name = "bootloader", //0x00000000-0x00040000
  4. .size = SZ_256K,
  5. .offset = ,
  6. },
  7.  
  8. [] = {
  9. .name = "params", //0x00040000-0x00060000
  10. .offset = MTDPART_OFS_APPEND,
  11. .size = SZ_128K,
  12. },
  13.  
  14. [] = {
  15. .name = "kernel", //0x00060000-0x00260000
  16. .offset = MTDPART_OFS_APPEND,
  17. .size = SZ_2M,
  18. },
  19.  
  20. [] = {
  21. .name = "rootfs", //0x00260000-0x10000000
  22. .offset = MTDPART_OFS_APPEND,
  23. .size = MTDPART_SIZ_FULL,
  24. }
  25. };

上面部分宏的定义,如下所示:

  • 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/下载busybox 1.20.0

  1. tar -xjf busybox-1.20..tar.bz2
  2. cd busybox-1.20.
  3. make menuconfig //设置交叉编译前缀

进入Busybox Settings --->Build Options --->

() Cross Compiler prefix

在弹出的对话框里面写入:arm-linux-

  1. make //编译
  2. mkdir /work/nfs_root/fs_mini_mdev_new //创建要安装的文件系统目录
  3. 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里

  1. mkdir /work/nfs_root/fs_mini_mdev_new/lib
  2. mkdir /work/nfs_root/fs_mini_mdev_new/usr/lib -p
  3. cp arm-none-linux-gnueabi/libc/armv4t/usr/lib/*.so* /work/nfs_root/fs_mini_mdev_new/usr/lib -d /* -d:保持链接 */
  4. 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

  1. cd cd /work/nfs_root/fs_mini_mdev_new/
  2. mkdir etc/
  3. vi etc/inittab

添加以下几句:

  1. ::sysinit:/etc/init.d/rcS //内核启动时,执行/etc/init.d/rcS
  2. console::askfirst:-/bin/sh //启动console对应的-/bin/sh进程之前,等待用户按enter键
  3. ::ctrlaltdel:/sbin/reboot //按下ctrl+alt+del组合键时,启动reboot命令
  4. ::shutdown:/bin/umount -a -r //系统关机前,卸载所有文件系统

2)构造etc/init.d/rcS

  1. mkdir etc/init.d/
  2. vi etc/init.d/rcS

添加以下几句:

  1. mount -a //装载/etc/fstab中的文件系统
  2. echo /sbin/mdev > /proc/sys/kernel/hotplug //使/sbin/medv指向hotplug,从而支持热拔插
  3. mdev -s //使用medv命令自动创建/dev下的所有设备节点

并给rcS加上可执行权限:

  1. sudo chmod +x etc/init.d/rcS //使脚本rcS能够执行命令

3)构造etc/fstab

PS:使用mdev命令需要sysfs、tmpfs、devpts这3个文件系统的支持

  1. mkdir proc/ //创建proc要挂载的目录
  2. mkdir sys/ //创建sysfs要挂载的目录,
  3. mkdir dev/pts -p //创建devpts要挂载的目录
  4. vi etc/fstab

添加以下几句

  1. # device mount-point type options dump fsck order
  2. proc /proc proc defaults
  3. tmpfs /tmp tmpfs defaults
  4. sysfs /sys sysfs defaults
  5. devpts /dev/pts devpts defaults

5.4构造其它文件/目录

1)创建终端文件(dev/console和dev/null)

  1. sudo mknod m dev/console c
  2. sudo mknod m dev/null c

2)创建其它目录

  1. mkdir mnt tmp root

6.制作jffs2映像文件

由于mkfs.jffs2工具之前已经安装好了,所以直接使用mkfs.jffs2命令:

  1. cd /work/nfs_root/ //返回到上个目录
  2. mkfs.jffs2 -n -s -e 128KiB -d fs_mini_mdev_new -o fs_mini_mdev_new.jffs2
  3. //-n:表示每块不添加清除标记,-s:NAND的每页为2k,-e: NAND的每块为128kb
  4. //-d fs_mini_mdev_new:表示要制作的根文件系统文件
  5. //-o fs_mini_mdev_new.jffs2:表示生成的映像文件

7.烧写jffs2,启动内核

  1. nfs 192.168.2.106:/work/nfs_root/fs_mini_mdev_new.jffs2
  2. nand erase.part rootfs
  3. nand write.jffs2 $filesize
  4. set bootargs console=ttySAC0, root=/dev/mtdblock3 rootfstype=jffs2
  5. nfs 192.168.2.106:/work/nfs_root/uImage_new
  6. bootm

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系统

1.移植3.4内核-分析内核启动过程,重新分区,烧写jffs2文件系统的更多相关文章

  1. ARM linux解析之压缩内核zImage的启动过程

    ARM linux解析之压缩内核zImage的启动过程 semilog@163.com 首先,我们要知道在zImage的生成过程中,是把arch/arm/boot/compressed/head.s  ...

  2. Linux内核分析(四)----进程管理|网络子系统|虚拟文件系统|驱动简介

    原文:Linux内核分析(四)----进程管理|网络子系统|虚拟文件系统|驱动简介 Linux内核分析(四) 两天没有更新了,上次博文我们分析了linux的内存管理子系统,本来我不想对接下来的进程管理 ...

  3. Windows内核分析——内核调试机制的实现(NtCreateDebugObject、DbgkpPostFakeProcessCreateMessages、DbgkpPostFakeThreadMessages分析)

    本文主要分析内核中与调试相关的几个内核函数. 首先是NtCreateDebugObject函数,用于创建一个内核调试对象,分析程序可知,其实只是一层对ObCreateObject的封装,并初始化一些结 ...

  4. Nimbus<三>Storm源码分析--Nimbus启动过程

    Nimbus server, 首先从启动命令开始, 同样是使用storm命令"storm nimbus”来启动看下源码, 此处和上面client不同, jvmtype="-serv ...

  5. mybatis源码分析:启动过程

    mybatis在开发中作为一个ORM框架使用的比较多,所谓ORM指的是Object Relation Mapping,直译过来就是对象关系映射,这个映射指的是java中的对象和数据库中的记录的映射,也 ...

  6. workerman源码分析之启动过程

    PHP一直以来以草根示人,它简单,易学,被大量应用于web开发,非常可惜的是大部分开发都在简单的增删改查,或者加上pdo,redis等客户端甚至分布式,以及规避语言本身的缺陷.然而这实在太委屈PHP了 ...

  7. 二、先在SD卡上启动U-boot,再烧写新的U-boot进Nandflash

    1. 制作SD卡 先准备一张2G的SD卡(不能用8G的,2G的卡和8G的卡协议不一样),和烧写SD卡的工具write_sd以及需要烧写到SD卡中的u-boot-movi.bin.将SD卡格式化后连接到 ...

  8. Linux内核分析--内核中的数据结构双向链表续【转】

    在解释完内核中的链表基本知识以后,下面解释链表的重要接口操作: 1. 声明和初始化 实际上Linux只定义了链表节点,并没有专门定义链表头,那么一个链表结构是如何建立起来的呢?让我们来看看LIST_H ...

  9. Linux内核分析--内核中的数据结构双向链表【转】

    本文转自:http://blog.csdn.net/yusiguyuan/article/details/19840065 一.首先介绍内核中链表 内核中定义的链表是双向链表,在上篇文章--libev ...

随机推荐

  1. JavaScript的简单入门

    一.导读 简介:JavaScript简称js,是基于对象和事件驱动的脚本语言,主要运用于客户端.原名LiveScript,本身和Java没有任何关系,但语法上很类似. 特点:交互性(它可以做的就是信息 ...

  2. tr 命令详解

    tr  作用:标准输入中通过替换或删除操作进行字符转换 语法:tr -c -d -s ["string1_to_translate_from"] ["string2_to ...

  3. Nodejs使用redis

    安装方法 安装redis方法请自行百度, 用npm方法,安装nodejs的redis模块 npm install redis 实战 var redis = require("redis&qu ...

  4. 本机向windows服务器传输文件的三种方法

    闲来无事,在腾讯云上申请了一个免费的服务器,想将自己写的网页发布到服务器上,服务器的申请很简单,百度搜索 腾讯云 ,然后新人第一次注册能申请到免费一个月的云主机,虽然配置不怎么高,但是还是能用的,这是 ...

  5. 例子:js超级玛丽小游戏

    韩顺平_轻松搞定网页设计(html+css+javascript)_第34讲_js超级玛丽小游戏_学习笔记_源代码图解_PPT文档整理 采用面向对象思想设计超级马里奥游戏人物(示意图) 怎么用通过按键 ...

  6. JavaScript实现ZLOGO: 用语法树实现多层循环

    原址: https://zhuanlan.zhihu.com/p/32571516 照例先上演示弱效果图. 演示地址照旧: 代码如下: 开始 循环4次 循环4次 前进50 左转90度 到此为止 右转9 ...

  7. PHP重要知识点

    1 获取文件名或目录路径 getcwd() :显示是 在哪个文件里调用此文件 的目录 __DIR__ :当前内容写在哪个文件就显示这个文件目录 __FILE__ : 当前内容写在哪个文件就显示这个文件 ...

  8. 【JavaScript 】for 循环进化史

    ECMAScript 6已经逐渐普及,经过二十多年的改进,很多功能也有了更成熟的语句,比如 for 循环 这篇博客将介绍一下从最初的 for 循环,到 ES6 的 for-of 等四种遍历方法 先定义 ...

  9. SpringMVC 控制器默认支持GET和POST两种方式

    在SpringMVC的controller中,@RequestMapping只写路径,不包含RequetMethod.GET和RequetMethod.POST,HttpServletRequest的 ...

  10. exports与module.exports,export与export default 之间的关系和区别

    首先我们要明白一个前提,CommonJS模块规范和ES6模块规范完全是两种不同的概念. CommonJS模块规范 Node应用由模块组成,采用CommonJS模块规范. 根据这个规范,每个文件就是一个 ...