IMX6Solo启动流程-从Uboot到kernel 下

写在前头

*.版权声明:本篇文章为原创,可随意转载,转载请注明出处,谢谢!另我创建一个QQ群82642304,欢迎加入!
*.目的:整理一下RIotBoard开发板的启动流程,对自己的所学做一个整理总结,本系列Uboot代码基于2009.08版。
*.备注:整个系列只是对我所学进行总结,记录我认为是关键的点,另我能力有限,难免出现疏漏错误,如果读者有发现请多指正,以免我误导他人!


读取内核镜像

上一篇我们讲到如果用户在启动Uboot过程中没有输入任意键,那么Uboot将执行默认命令bootcmd.
在进入Uboot的命令交互模式后输入printenv:


bootargs=console=ttymxc1,115200 nosmp video=mxcfb0:dev=hdmi,1280x720M@60,bpp=32 video=mxcfb1:off
bootargs_mmc=setenv bootargs ${bootargs} root=/dev/mmcblk0p1 rootwait
bootcmd_mmc=run bootargs_mmc; mmc dev 3; mmc read ${loadaddr} 0x800 0x2000; bootm
bootcmd=run bootcmd_mmc

可以看出执行bootcmd过程中分别执行:
1. 设置bootargs(启动参数)到环境变量
2. mmc切换到dev 3(即emmc)中.
3. 从mmc dev 3(即emmc)的偏移量为0x800块的位置读取0x2000块长度的数据(一个数据块的大小是512B)到${loadaddr}
4. 执行bootm命令
根据RIotBoard板子上的配置,在emmc上,从0~1M的地址内存放的是Uboot(包括环境变量),从1~9M的地址内存放的是内核镜像,其他的存放文件系统.
所以第2、3步实际上就是将emmc上的1~9M上的数据拷贝到内存上${loadaddr}的位置.


bootm命令

Uboot将内核镜像加载到${loadaddr}的位置上之后,就执行bootm命令,bootm命令的定义在uboot/common/cmd_bootm.c中,它的接口函数是do_bootm,也就是说Uboot调用了do_bootm函数.

  1. 重新计算启动函数表地址boot_os
    /* relocate boot function table */
    if (!relocated) {
        int i;
        for (i = 0; i < ARRAY_SIZE(boot_os); i++)
            if (boot_os[i] != NULL)
                boot_os[i] += gd->reloc_off;
        relocated = 1;
    }

启动函数表boot_os里面存放的是一些内核启动函数的函数指针.Uboot是一个通用的内核启动器,不仅支持Linux内核的启动,还支持其他例如Vxworks、netbsd等内核的启动,每一种内核启动函数各不一样,Uboot通过读取内核镜像文件头部的信息来判断该镜像为何种镜像以及在boot_os表中对应的加载函数.
在Uboot汇编入口的那段代码上有个函数是relocate函数,将Uboot的执行镜像重新定位到新地址,如果有重新定位(RIotBoard开发板没有重新定位),需要重新计算内核启动函数表的位置,这样子才能准确的调用.
2. 根据argc的值判断是否有子命令,如果有则执行子命令并返回.
3. 调用bootm_start函数,该函数主要就是读取镜像文件头部信息,然后到填充static bootm_headers_t images;这个数据结构.内核镜像文件就是uImage文件,它是在Image上面增加一个头部信息,后续讲到内核的时候再详细讲它的生成流程,在这边我稍微讲下头部信息,它包含如下信息:

typedef struct image_header {
    uint32_t    ih_magic;   /* Image Header Magic Number    */
    uint32_t    ih_hcrc;    /* Image Header CRC Checksum    */
    uint32_t    ih_time;    /* Image Creation Timestamp */
    uint32_t    ih_size;    /* Image Data Size      */
    uint32_t    ih_load;    /* Data  Load  Address      */
    uint32_t    ih_ep;      /* Entry Point Address      */
    uint32_t    ih_dcrc;    /* Image Data CRC Checksum  */
    uint8_t     ih_os;      /* Operating System     */
    uint8_t     ih_arch;    /* CPU architecture     */
    uint8_t     ih_type;    /* Image Type           */
    uint8_t     ih_comp;    /* Compression Type     */
    uint8_t     ih_name[IH_NMLEN];  /* Image Name       */
} image_header_t;

其中ih_load指明内核最终被加载的地址,ih_ep指明内核的入口地址,ih_comp指明内核的压缩方式
另外一点就是如果执行bootm时没有带参数,则do_bootm默认从${loadaddr}处读取内核镜像文件,否则,从第一个参数指示的地址读取内核镜像文件.
4. 调用bootm_load_os函数,该函数就是根据已填充好的static bootm_headers_t images;数据结构来加载内核,其中包括:根据内核压缩方式对内核进行解压缩,将内核移动到指定的加载地址.
5. 跳转到对应的启动函数

...
boot_fn = boot_os[images.os.os];
...
boot_fn(0, argc, argv, &images);
...

linux内核对应的就是do_bootm_linux函数


do_bootm_linux函数

do_bootm_linux函数先保存一下内核参数,然后跳转到内核入口地址.

...
theKernel = (void (*)(int, int, uint))images->ep;
...
cleanup_before_linux ();
theKernel (0, machid, bd->bi_boot_params);

传给内核的有三个参数,第一个参数为0,第二个参数为机器ID,第三个为内核参数的起始地址.
内核的各个参数是一个struct tag params结构体,该结构体的定义如下:

struct tag {
    struct tag_header hdr;
    union {
        struct tag_core     core;
        struct tag_mem32    mem;
        struct tag_videotext    videotext;
        struct tag_ramdisk  ramdisk;
        struct tag_initrd   initrd;
        struct tag_serialnr serialnr;
        struct tag_revision revision;
        struct tag_videolfb videolfb;
        struct tag_cmdline  cmdline;

        /*
         * Acorn specific
         */
        struct tag_acorn    acorn;

        /*
         * DC21285 specific
         */
        struct tag_memclk   memclk;
    } u;
};

struct tag_header hdr;指明里该结构体是什么内核参数以及参数长度,union u;存放参数的数据.
内核参数以ATAG_CORE开头,以ATAG_NONE结尾,依次存放在以bd->bi_boot_params为起始地址的内存中,然后传给内核进行解析.
最后,调用theKernel,进入内核…


总结

Uboot的代码中有许许多多的宏定义给阅读代码造成一些困难,我跳过许多东西,只是陈述一下启动流程.其余的代码读者有兴趣的可以去研究.

参考

暂无

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值