zoukankan      html  css  js  c++  java
  • 七,移植linux-3.19内核

    文档时间:2018-08-18

    交叉编译器:arm-linux-gcc-4.3.2

    Ubuntu版本:16.04

    kernel版本:linux-3.19

    1,分析 uboot 如何启动内核

    通过之前对环境变量保存的分析可知,uboot是通过 bootcmd 来启动内核的,在 include/configs/jz2440.h 中我们有定义:

    #define CONFIG_BOOTCOMMAND "nand read 0x30000000 kernel; bootm 0x30000000" //bootcmd  

    有以下图片可知:执行 bootcmd 时,会将内核代码从nand 读到内存30000000 处,然后开始执行。 

     由于要执行 bootm 命令,所以我们需要打开与 bootm 命令相关的文件进行分析,从名字可知,需要打开 cmd_bootm.c (位于 common 目录下)文件,找到对应的 do_bootm 函数:

    (PS:一般与 xxx 命令相关的文件都在 common 目录下,名为cmd_xxx.c)

    如上图,可以看出,do_bootm 会执行 boot_os 里的所有函数,进入到 boot_os 结构体中:

    发现,虽然函数很多,但是与启动内核有关的只有 do_bootm_linux 函数,进入到 do_bootm_linux 函数 (位于 arch/arm/lib/bootm.c 文件中):

    发现 do_bootm_linux 函数最终会 跳转执行 boot_prep_linux 和 boot_jump_linux 函数,首先分析 boot_prep_linux 函数(位于 bootm.c 文件中):

    static void boot_prep_linux(bootm_headers_t *images)
    {
        char *commandline = getenv("bootargs");      //从环境变量中获取 bootargs 的值
    
      。。。。。。。
            setup_board_tags(&params);      
            setup_end_tag(gd->bd);    //将 tag 参数保存在指定位置
        } else {
            printf("FDT and ATAGS support not compiled in - hanging
    ");
            hang();
        }
        do_nonsec_virt_switch();
    }

     从代码可以看出来,boot_prep_linux,主要功能是将 tag 参数保存到指定位置,比如 bootargs 环境变量 tag,串口 tag,接下来分析 boot_jump_linux 函数(位于 bootm.c 文件中):

    static void boot_jump_linux(bootm_headers_t *images, int flag)
    {
        unsigned long machid = gd->bd->bi_arch_number;      //获取机器id (在 board/samsung/jz2440/jz2440.c 中设置,为 MACH_TYPE_SMDK2410(193))
        char *s;
        void (*kernel_entry)(int zero, int arch, uint params);
        unsigned long r2;
        int fake = (flag & BOOTM_STATE_OS_FAKE_GO);
    
        kernel_entry = (void (*)(int, int, uint))images->ep;    //获取 kernel的入口地址,此处应为 30000000
    
        s = getenv("machid");        //从环境变量里获取机器id (本例中还未在环境变量里设置过机器 id)
        if (s) {            //判断环境变量里是否设置机器id
            strict_strtoul(s, 16, &machid);    //如果设置则用环境变量里的机器id
            printf("Using machid 0x%lx from environment
    ", machid);
        }
    
        debug("## Transferring control to Linux (at address %08lx)" 
            "...
    ", (ulong) kernel_entry);
        bootstage_mark(BOOTSTAGE_ID_RUN_OS);
        announce_and_cleanup(fake);
    
        if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len)
            r2 = (unsigned long)images->ft_addr;
        else
            r2 = gd->bd->bi_boot_params;    //获取 tag参数地址,gd->bd->bi_boot_params在 setup_start_tag 函数里设置
    if (!fake) kernel_entry(0, machid, r2); }  //进入内核

    通过分析可以看出,最终进入内核的函数为 :

    kernel_entry(0, machid, r2)

    该函数向内核传递了三个参数,分别为:

    (1),0  (2),机器id  (3),参数列表地址,即 tag 地址

     到此 uboot 的任务就结束了,接下来内核代码便开始执行

    2,移植 kernel-3.19

    1),下载 3.19内核源码

    下载地址为:https://mirrors.edge.kernel.org/pub/linux/kernel/,然后拷贝到 Ubuntu 中(可以用FileZilla Client工具拷贝),输入以下命令进行解压:

    tar -zxvf linux-3.19.tar.gz 

    2),Linux 内核目录结构

    目录名 描述
    arch 体系结构相关的代码,对于么个架构的CPU,arch 目录下都有一个对应的子目录,如 arch/arm/、arch/x86等
    block 块设备相关的通用函数
    crypto 常用加密和散列算法(如 AES、SHA等),还有一些压缩和CRC校验算法
    drivers

    所有的设备驱动程序,里面每一个子目录对应一类驱动程序,比如 drivers/block/ 为块设备驱动程序,drivers/char/为字符设备驱动程序,

    drivers/mtd/为nor flash、nand flash 等存储设备的驱动程序

    firmware 设备相关的固件程序
    fs Linux 支持的文件系统的代码,每个子目录对应一种文件系统,比如 fs/jffs2/、fs/ext2/、fs/ext4/等
    include

    内核头文件,有基本头文件(存放在 include/linux/目录下)、各种驱动或功能部件的头文件(如 include/media/、include/video/、include/net等)、

    各种体系相关的头文件(如 include/asm-generic/等)

    init 内核的初始化代码(不是系统的引导代码),其中的 main.c 文件中的 start_kernel 函数是内核引导后运行的第一个函数
    ipc 进程间通信的代码
    kernel 内核管理的核心代码
    lib 内核用到的一些库函数代码,如 crc32.c、string.c、shal.c等
    mm 内存管理代码
    net 网络支持代码,每个子目录对应子网络的一个方面
    samples 一些示例程序,如断点调试,功能测试等
    scripts 用于配置、编译内核的脚本文件
    security 安全、密钥相关的代码
    sound 音频设备驱动程序
    tools 工具类代码,比如 USB 传输等,通常会将 u-boot 下生成的mkimage工具放到此目录下,同事修改Linux的Makefile支持生成uImage
    usr 一般不会用到
    virt 一般不会用到
    Documentation Linux内核的使用帮助文档

    3),配置,编译内核

     修改顶层的 Makefile,打开 Makefile 文件,找到下面语句:

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

    修改为:

    ARCH        ?= arm
    CROSS_COMPILE    ?= arm-linux-

    然后选择默认配置,单板的默认配置文件在 arch/arm/configs 目录下,由于没有2440相关的默认配置,所以我们选择比较相近的 2410 的配置,选择s3c2410 和mini2410 都可以,这里我们选择 s3c2410_defconfig,在linux-3.19 目录下输入以下命令编译内核:

    make s3c2410_defconfig
    make uImage

    出现如下错误:

    说明缺少 mkimage ,有两种解决办法:(1),利用uboot生成mkimage工具,然后拷贝到/usr/bin 目录下 (2),输入 sudo apt-get install u-boot-tools 命令在线安装,本文选择第二种方法,输入命令:

    sudo apt-get install u-boot-tools

     然后 make uImage 编译内核,编译成功,在 arch/arm/boot 目录下生成 uImage,将生成的 uImage 烧写到板子上,然后启动内核:

    tftp 30000000 uImage
    bootm 30000000

    发现程序卡死在 booting the kernel,查找问题,在 Ubuntu 中输入命令 make menuconfig 进入可视化配置界面,发现出现如下错误:

    缺少 curses.h 文件,在网上查找原因发现是因为缺少一个套件 ncurses devel,把此套件安装下来即可,输入以下命令进行安装:

    apt-get install libncurses5-dev

    然后就可以使用make menuconfig 进入可视化配置界面,发现在可视化配置界面中串口选项已经配置,继续查找问题,发现可能是机器id 不匹配,在 uboot 下输入 print machid,显示如下错误:

    说明环境变量中未定义机器 id,所以启动失败(因为在内核自解压完成以后内核会首先会进入 bl      __lookup_machine_type函数(在arch/arm/kernel/head.S中),检查machine_type是否匹配,如果不匹配会跳入__error_a函数(在arch/arm/kernel/head-common.S中),导致启动失败),我们先随便设置一个 id,如下所示:

    set machid 123456

     然后再启动内核,打印如下:

     

    可以发现我们随便设置的机器id并没有被支持,内核打印出来所支持的机器id,与2440有关的有:MINI2440(7CF),SMDK2440(16A),我们先把机器id设置为 0x7cf:

    set machid 0x7cf
    save

    然后启动内核,打印如下:

    打印乱码,怀疑可能是波特率不对,在之前设置的 bootargs 参数里加上波特率:

    set bootargs noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0,115200
    save

    然后下载启动内核,发现可以正常启动,说明内核支持MINI2440机器id 可用,接下来把机器id修改为 0x16a:

    set machid 0x16a
    save

     然后下载启动内核,打印如下:

    打印乱码,由于之前我们已经设置过波特率,所以出现乱码可能是时钟设置不对,打开 mach-smdk2440.c (位于arch/arm/mach-s3c24xx目录下)文件,定位到如下位置:

    可见设置时钟时用的是16934400,而我们板子用的晶振是 12M,所以修改代码如下(红色部分为修改代码):

    static void __init smdk2440_init_time(void)
    {
        s3c2440_init_clocks(12000000);
        samsung_timer_init();
    }

     在Ubuntu 下输入 make uImage 重新编译,然后烧写测试,打印信息如下:

    说明内核启动正常,但是未能成功挂载文件系统,原因是我们在uboot中设置了4个分区,把文件系统放在了 block3上,而如上图内核设置了8个分区,因此不可能挂载成功

     3),修改内核分区

    搜索 S3C2410 flash partition 1 字段,找到:

    打开 arch/arm/mach-s3c24xx/common-smdk.c 文件,并定位到119行,仿照uboot的分区,修改代码如下:

    static struct mtd_partition smdk_default_nand_part[] = {    //changed by zhyy
        [0] = {
            .name    = "u-boot",
            .size    = SZ_512K,
            .offset    = 0,
        },
        [1] = {
            .name    = "params",
            .offset = MTDPART_OFS_APPEND,
            .size    = SZ_128K,
        },
        [2] = {
            .name    = "kernel",
            .offset = MTDPART_OFS_APPEND,
            .size    = SZ_4M,
        },
        [3] = {
            .name    = "rootfs",
            .offset    = MTDPART_OFS_APPEND,
            .size    = MTDPART_SIZ_FULL,
        },
    };

    上面部分宏的定义位于 include/linux/mtd/partitions.h 文件中,如下所示:

    #define MTDPART_OFS_RETAIN    (-3)
    #define MTDPART_OFS_NXTBLK    (-2)
    #define MTDPART_OFS_APPEND    (-1)
    #define MTDPART_SIZ_FULL    (0)

     如果设置的是MINI2440的机器id,则需要将mach-mini2440.c 文件中的分区改成与上面一样,代码如下所示:

    static struct mtd_partition mini2440_default_nand_part[] __initdata = {
            [0] = {
            .name    = "u-boot",
            .size    = SZ_512K,
            .offset    = 0,
        },
        [1] = {
            .name    = "params",
            .offset = MTDPART_OFS_APPEND,
            .size    = SZ_128K,
        },
        [2] = {
            .name    = "kernel",
            .offset = MTDPART_OFS_APPEND,
            .size    = SZ_4M,
        },
        [3] = {
            .name    = "rootfs",
            .offset    = MTDPART_OFS_APPEND,
            .size    = MTDPART_SIZ_FULL,
        },
    };

    修改完之后,编译内核,烧写新内核,烧写板子自带的 .jffs2 和 .yaffs2文件系统,看看能否挂接成功,命令如下:

    烧写新内核:

    tftp 30000000 uImage
    nand erase.part kernel
    nand write 30000000 kernel

    烧写.jffs2 文件系统:

    tftp 30000000 fs_mini_mdev.jffs2
    nand erase.part rootfs
    nand write.jffs2 30000000 4a0000 $filesize 

     运行.jffs2 文件系统要重新设置 bootargs:

    set bootargs noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0,115200 rootfstype=jffs2

     烧写.yaffs2文件系统:

    tftp 30000000 fs_mini_mdev.yaffs2
    nand erase.part rootfs
    nand write.yaffs 30000000 4a0000 $filesize 
    set bootargs noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0,115200 rootfstype=yaffs2

    运行结果是,可以正常运行.jffs2 文件系统,不能运行 .yaffs2 文件系统,原因是当前内核不支持yaffs文件系统,下一节移植yaffs2 文件系统

    如果在运行.jffs2 文件系统是出现以下错误:

    exitcode=0x00000004

    则需要执行 make menuconfig,在kernel feature中增加以下选项:

  • 相关阅读:
    poj2187 Beauty Contest 旋转卡壳
    2017/8/6 考试吐槽
    bzoj2618 凸多边形 半平面交
    cogs1600 奶牛冰壶 计算几何
    cogs896 圈奶牛 凸包
    cogs1743 忠诚 zkw
    BZOJ 3224 普通平衡树 平衡树的两种姿势:SBT,splay。01trie
    BZOJ 3196 二逼平衡树
    BZOJ 1901 Dynamic Rankings
    BZOJ 4325[NOIP2015]斗地主
  • 原文地址:https://www.cnblogs.com/zhyy-mango/p/9477973.html
Copyright © 2011-2022 走看看