• uboot源码——内核启动分析


    以下内容源于朱有鹏嵌入式课程的学习与整理,如有侵权请告知删除。

    uboot作用简介

    uboot的主要作用是用来启动linux内核。

    因为CPU不能直接从块设备中执行代码,需要把块设备中的程序复制到内存中,而复制之前还需要进行很多初始化工作,如时钟、串口、dram等;如果要想让CPU启动linux内核,只能通过另外的程序,进行必要的初始化工作,再把linux内核中代码复制到内存中,并执行这块内存中的代码,即可启动linux内核。一般情况下,linux镜像储存在块设备中(SD卡、iNand、Nandflash等)。uboot把块设备中的内核代码复制到内存地址0x30008000地址处,然后再执行bootm 0x30008000命令以启动内核代码。

    uboot启动内核的总流程

    开机时uboot会出现倒计时,当没有按键操作时,uboot会读取出bootcmd这个环境变量,并使用rum_command函数来执行这个命令。实质是执行了“movi read kernel 30008000;bootm 30008000”。其中,“movi read kernel”表示把sd卡中的kernel分区赋值到30008000内存处;“bootm 30008000”表示真正的传参以及跳转到linux内核中执行(实际执行do_bootm()函数)。

    do_bootm()函数首先判断内核镜像类型(zImage、uImage、设备树),然后再把必须的信息(linux操作系统、ep的值等)储存起来,最后调用do_bootm_linux函数来对内核传参并且启动内核。

    vmlinuz、Image、zImage、uImage

    linux内核代码经过编译链接,生成一个elf格式的可执行文件,即vmlinuz或者vmlinux。此文件不能直接烧录。

    vmlinuz文件经过arm-linux-objcopy后,生成一个Image镜像文件。vmlinuz.elf文件大小为70M以上,而Image镜像文件为7M左右。

    Image文件经过进一步压缩,并添加解压缩代码,形成zImage文件。因此zImage文件中除了linux内核镜像以外,还有一些头文件以及解压代码,所以内核实际处在addr地址再加一个偏移量的位置。当zImage文件作为启动镜像时,首先要解压,可以由uboot解压或者zImage文件本身自解压。

    uImage是uboot专用的启动内核镜像,基本上属于过时的技术。新一点的技术是设备树的启动方式。


    uboot启动内核的源码分析

    1、分析start_armboot()函数末尾的main_loop()函数。

     main_loop()函数中有一段代码如下。

    •  通过getenv()函数获取环境变量bootcmd的值,然后通过run_command()函数来执行。
    • 其中,bootcmd='movi read kernel 30008000;bootm 30008000'。
    • movi read kernel 30008000,表示把sd卡中kernel分区复制到30008000内存地址处。
    • bootm 30008000,表示到内存地址30008000处执行代码。
    • 执行bootm命令,实际执行do_bootm函数。

    2、分析do_bootm()函数。

    此函数位于uboot的/common/cmd_bootm.c文件中。片段代码如下。

     

     关于结构体变量hdr,它的类型是image_header_t。如下。 

    关于结构体变量image,它的类型是bootm_header_t。如下。 

    • 因为 “bootm 30008000”,所以argc==2,走的是else路线;argv[1]='30008000',这是字符串,要将字符串转化为ulong数字,simple_strtoul()完成此功能。
    • 接着判断0x30008000偏移36字节以后的地址中的值,如果为0x016f2818,说明启动镜像为zImage,则输出“boot with zImage”。
    • 因为没有开启虚拟地址映射功能,所以virt_to_phys(x)其实就是地址x。
    • 接着填充结构体变量hdr。主要填充两个成员,ih_os=IH_OS_LINUX,ih_ep=ntohl(addr)。前者表示内核的操作系统,后者表示内核代码的地址。ntohl()函数,将一个无符号长整形数从网络字节顺序转换为主机字节顺序,比如将0x12345678转换成0x78563412。
    • 然后把hdr中的值复制一份到image.legacy_hdr_os_copy中,即把内存地址0x30008000处设置好的zImage头复制一份到uboot的data段。
    • 然后直接跳转到after_header_check处。

    •  判断操作系统,然后调用do_bootm_linux函数。

    3、分析do_bootm_linux()函数。

    (1)do_bootm_linux()函数部分代码如下。

    •  获取环境变量bootargs的值。
    • 判断全局变量images中的legacy_hdr_valid是否为1,为1则获取ep值。
    • 把ep强制类型换成函数指针类型,赋值给thekernel。
    • 获取环境变量machid的值,赋值给s;如果s不为空,则变量machid = 环境变量中machid的值;打印machid。

    (2)uboot给内核传参。

    •  uboot把与硬件有关的信息传给linux内核,比如memory信息(多少bank、size、起始地址)、命令行信息、lcd 串口、initrd、MTD等信息。
    • 如要定义了任意一个CONFIG_XXXXX,则执行setup_start_tag(bd)。

    A、初始化第一块tag。 

    • params = (struct tag *) bd->bi_boot_params; 这句代码把bd->bi_boot_params强制转换为stuct tag* 类型,然后赋值给params。
    • bd是uboot中的全局变量gd的成员,gd->bd->bi_boot_params = (PHYS_SDRAM_1+0x100),这说明把“PHYS_SDRAM_1+0x100”这个地址设置为传参的起始地址。
    • 给hdr.tag 与hdr.size赋值,给联合体中的结构体参数赋值。
    • 从#define tag_next(t) ((struct tag *)((u32 *)(t) + (t)->hdr.size))可以看出,params指向下一个params,即把params移动sizeof(tag_core)大小继续赋值。
    • 这块tag表示一个开始,除了说明传参的起始地址,好像没什么其他实际的信息。

    上面的变量bt的类型是bd_t,它的定义如下。 

    上面的变量params的类型是(static struct tag *),struct tag定义如下。

    • 此结构体由一个stuct tag_header类型的结构体,加上一个由一系列结构体组成的union联合体组成。这一系列结构体中存放的就是与board有关的参数。

      struct tag_header定义如下。

    1. struct tag_header {
    2. u32 size;
    3. u32 tag;
    4. };

     

    B、传递内存参数,setup_memory_tags函数。

    • 记录内存中每个bank的信息,即每个bank的起始地址和大小。

     C、传递命令行参数,setup_commandline_tag函数。

    • commandline是一个char *类型,指向环境变量中的bootargs的值,即:#define CONFIG_BOOTARGS  "root=/dev/mtdblock4 rootfstype=yaffs2 init=/init console=ttySAC0,115200"

     D、初始化最后一块tag,即结束传参。

    (3)uboot的最后一句代码。

    • 通过执行thekernel函数直接启动linux内核,传递三个参数:0、machid、传参的首地址。
    • 通过r0、r1、r2三个寄存器来传递,r0传递0,r1传递machid,r2传递传参的首地址。

     

  • 相关阅读:
    深度分析c+引用的本质以及引用与指针的区别
    编写playbook实现LNMP架构基于源码方式、变量,加密文件
    Uni-app 苹果应用打包与上线指南
    计算机中丢失vcomp140.dll解决方案,可以使用这几个最新方法来修复
    技术团队:研发中的短跑冲刺和马拉松游戏
    数据挖掘 | Count数据去除批次效应后不是整数甚至还出现负值导致无法进行差异分析怎么办?
    CI/CD docker compose 部署 humpback - single mode
    Python基础知识入门(二)
    上海市青少年算法2022年10月月赛(丙组)
    处理火狐浏览器地址栏点击出现 百度/58同城/爱淘宝 链接
  • 原文地址:https://blog.csdn.net/oqqHuTu12345678/article/details/125416649