七,移植linux-3.19内核

╄→гoц情女王★ 提交于 2020-12-28 05:49:14

文档时间: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\n");
        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\n", machid);
    }

    debug("## Transferring control to Linux (at address %08lx)" \
        "...\n", (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中增加以下选项:

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!