uboot启动内核是什么,认识 uboot 和 内核 之间不可不说的关系

2023-05-16

uboot启动内核是什么,认识 uboot和内核之间不可不说的关系

c9a974d8c6b22015b4c0260b4997fae7.pnguboot镜像为 uboot.bin,Linux镜像为 zImage

嵌入式设备中的分区表是自己定义的,uboot和内核中的分区表应一致

内核运行前必须加载到 ddr中指定的地址处

uboot需要提供内核必要的参数

内核启动的方式

uboot启动内核有两种方式,一种是等待倒计时结束后直接启动内核,一种是在 uboot命令行中使用 boot命令启动内核

88e54045a866e17c7f9dfe82d8158a96.png

其代码分别如下

其中 parse_string_outer的作用是解析 boot参数并执行

/*------------------倒计时----------------------*/

s = getenv ("bootcmd");

if (bootdelay >= 0 && s && !abortboot (bootdelay)) {

    ...

    parse_string_outer(s, FLAG_PARSE_SEMICOLON |

                    FLAG_EXIT_FROM_LOOP);

    ...

}

/*------------------命令行----------------------*/

int do_bootd (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])

{

    ...

    if (parse_string_outer (getenv ("bootcmd"),

            FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP) != 0)

        rcode = 1;

    ...

}

U_BOOT_CMD(

    boot, 1, 1, do_bootd,

    "boot - boot default, i.e., run 'bootcmd'\n",

    NULL

);

/*-----------------相关宏定义----------------------*/

#ifdef CONFIG_BOOTARGS

    "bootargs=" CONFIG_BOOTARGS "\0"

#endif

#ifdef CONFIG_BOOTCOMMAND

    "bootcmd=" CONFIG_BOOTCOMMAND "\0"

#endif

#define CONFIG_BOOTARGS "console=ttySAC2,115200 root=/dev/mmcblk0p2 rw init=/linuxrc rootfstype=ext3"

#define CONFIG_BOOTCOMMAND "movi read kernel 30008000; movi read rootfs 30B00000 300000; bootm 30008000 30B00000"

加载内核到DDR中

d290225cf178fb07436ce12bd7c6c282.png

uboot启动内核的步骤

·内核镜像从启动介质中加载到DDR中

·去DDR中启动内核镜像

本文使用的开发板 x210将镜像存放在 SD卡中,要加载到 ddr中需要使用到 movi指令

movi提供了对 iNand/SD卡的操作,movi read用来读取 iNand/SD卡中的内容到DDR中;movi write用来将DDR中的内容写入到 iNand/SD卡中

上面的代码中 bootcmd中的命令就是用来加载 kernel rootfs到 ddr

除了从 SD卡加载,还可以通过 tftp nfs等网络下载方式加载镜像

通过movi read kernel 30008000可以知道,内核加载到了 0x30008000的位置

内核的镜像生成

Linux直接编译得到 elf文件,叫 vmlinux或 vmlinuz。这种文件会比较大,为了烧录方便,会使用 objcopy工具制作成镜像文件,叫 Image(从78M精简成了7.5M)

早期使用的软盘比较小,Image对与软盘来说还是太大了,放不下。Linux对 Image做进一步的压缩,并在压缩文件前端附加了一部分解压缩代码,形成 zImage

uboot可以使用 mkimage工具,在 zImage前面加上64字节的uImage的头信息,形成 uImage

加载启动内核

内核的加载启动是通过 do_bootm完成的

前面介绍过,镜像文件分为两个部分,头部以及真正的内核

所以 do_bootm会先对镜像进行头部信息的校验,然后再进行内核的启动

头部信息的结构体如下

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;

在 do_bootm中就是通过 ih_os判断镜像的类型,然后使用相应的方法启动内核

这里的镜像是 Linux镜像,所以使用的是 do_bootm_linux, do_bootm_linux的参数大部分是通过 do_bootm传递的

启动的参数bootm 30008000,告诉 uboot去 30008000这个地址去找镜像文件

内核启动

镜像的程序入口叫做 entrypoint,在 do_bootm_linux中使用 ep保存,镜像的程序入口在头信息的 ih_ep中,可以通过读取头信息得到

得到 ep后,通过theKernel = (void (*)(int, int, uint))ep;将 ep格式化后传递给 theKernel,这样 theKernel函数就指向了内存中加载的OS镜像的真正入口地址

前面也提到了,每个开发板在 uboot中都有唯一的机器码,这个编码用来验证开发板与 uboot是否匹配,这个机器码还会传到内核中再次验证。这个机器码获取的第一顺序备选是环境变量machid,第二顺序备选是gd->bd->bi_arch_num(x210_sd.h中的 #define MACH_TYPE 2456)

接下来就是传参的过程。先看看 Linux的 Documentation/arm/Booting中对 CPU寄存器设置的描述

- CPU register settings

  r0 = 0,

  r1 = machine type number discovered in (3) above.

  r2 = physical address of tagged list in system RAM, or

       physical address of device tree block (dtb) in system RAM

通过读取 r0 r1 r2这三个寄存器的值来设置 CPU,r0固定为0,r1为前面提到的机器码,r2为存放启动参数 tag结构体的首地址

所以在 do_bootm_linux通过theKernel (0, machid, bd->bi_boot_params);完成传参的过程

传参是通过 struct tag这个结构体完成的,获取参数就是获取一个个 tag的过程。这些 tag也有着规定的格式,do_bootm_linux中通过 setup_start_tag和 setup_end_tag函数设置 tag的开始和结束,这个函数的作用就是设置当前 tag的类型为 ATAG_CORE和 ATAG_NONE,用作 tag起始终止位置的判别

需要注意的是,传参是一个很重要的过程,内核启动不成功与传参错误有很大关系

uboot启动4步骤总结

第一步:将内核搬移到DDR中

第二步:校验内核格式、CRC等

第三步:准备传参

第四步:跳转执行内核

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

uboot启动内核是什么,认识 uboot 和 内核 之间不可不说的关系 的相关文章

  • C++1-C语言和C++的区别

    一 C语言与C 43 43 的区别 1 头文件 xff1a C 43 43 xff1a span class token macro property span class token directive hash span span cl
  • BW:LO数据源初始化步骤(精简版)

    首先在r3上删除 setup table xff0c 然后再填充 setup table xff08 锁定凭证 xff09 xff0c 同时 xff0c 在bw做无数据的初始化 xff0c 最后在bw做full load
  • C++3--构造函数、冒号语法

    一 构造函数 1 为什么要有这个概念 xff1a 例如下面的代码 xff0c 对于Table类 xff0c 可以通过t Set公有的方法给对象设置内容 xff0c 但是如果每次创建对象都调用该方法设置信息 xff0c 就会有点麻烦 xff0
  • c/c++语言面试题目整理

    1 static有什么用途 xff1f 在C语言中 xff0c static主要定义全局静态变量 xff0c 定义局部静态变量 xff0c 定义静态函数 限制变量的作用域 xff0c 设置变量的存储域 static 关键字主要有两种作用 x
  • c语言宏定义一个MAX函数

    1 span class token macro property span class token directive hash span span class token directive keyword define span sp
  • C++ -- 异常:try、throw、catch

    异常 申请内存的时候 xff0c 内存不够用 空间配置器申请失败 xff0c 不做处理 拷贝时内存错误 除数分母不能为0 等情况 会抛出异常 try throw catch 1 使用示例 1 xff09 除数为零 xff0c 情况 未处理情
  • C++ -- 笔试题

    1 下列对派生类的描述中错误的说法是 D A 派生类至少有一个基类 B 派生类可作为另一个派生类的基类 C 派生类除了包含它直接定义的成员外 xff0c 还包含其基类的成员 D 派生类所继承的基类成员的访问权限保持不变 2 当派生类中有和基
  • Linux 用户切换、修改用户名、修改密码

    一 用户切换 34 34 xff1a 普通用户提示符 34 34 xff1a root用户提示符 1 普通用户到root 方式一 xff1a 命令 xff1a su 然后输入root密码 此种方式只是切换了root身份 xff0c 但She
  • C++中的.和::和:和->的区别

    在学习C 43 43 的过程中我们经常会用到 和 和 xff1a 和 gt xff0c 在此整理一下这些常用符号的区别 1 A B则A为对象或者结构体 xff1b 2 A gt B则A为指针 xff0c gt 是成员提取 xff0c A g
  • AirSim学习日志 5-LQR实现无人机轨迹跟踪

    1 LQR控制器算法原理推导 1 1 状态反馈控制 连续线性系统的状态空间表示为 x
  • 英语常用短语

    1 xff0e 经济的快速发展 the rapid development of economy 2 xff0e 人民生活水平的显著提高 稳步增长 the remarkable improvement steady growth of pe
  • PX4进入系统控制台以及运行程序

    这里提供进入控制台两种办法 1 运行 Tools mavlink shell py dev ttyACM0 是我进入Px4系统控制台的命令 xff0c 进入之后应该是这样 Pixhawk src Firmware Tools mavlink
  • 哈哈,终于知道CSDN怎么改头像了

    话说之前一直郁闷 xff0c 说改头像的功能还没修好 xff0c 一直说服务器错误 今天偶尔发现 xff0c 右上角有个设置 xff0c 原来在这里可以改 我还发了几封邮件给CSDN的admin xff0c 居然只知道道歉 xff0c 不告
  • Hello Sky! pixhawk第一个例程学习解读

    学习px4的第一个程序 xff0c 这个例子作为官方给出解释的例程 xff0c 对于新手上手来说 xff0c 是很好的范例 接下来我对照程序源码 xff08 已经经过自己的修改和添加注释 xff09 进行讲解 1 PX4 INFO是标准的L
  • ShadowSSDT Hook

    ShadowSSDT表的获取 这里的ShadowSSDT表的获取是通过函数KeAddSystemServiceTable来获取的 使用这个函数的原因 xff1a 1 这个函数是已经导出的 xff0c 可以在代码中直接使用 2 这个函数里面使
  • 2013&2014

    2013总结 2013 毕业了 xff0c 算是正式工作半年 xff0c 2013年7月开始 xff0c 算是我的生活 xff0c 工作之外的时间都是自己的 一 收获 1 压力测试 差不多算是一个月的时间 xff0c 疯狂的一个月 xff0
  • BCTF总结

    缘由 上周 xff0c 我们小组Sigma参加了 百度杯 BCTF比赛 xff0c 经历了难忘的双休 xff0c 这次的BCTF跟以前参加的国内类似的安全比赛有些不同 xff0c 时间只有48小时 xff0c 题目不多 xff0c 但难度大
  • BCTF_海报探秘(300)

    这个题目来自上周的BCTF比赛 xff0c 题目是海报探秘 xff08 300 xff09 xff0c 一张png图片中隐藏了KEY xff0c 解出KEY xff0c 具体报告 xff0c 请下载 xff1a http download
  • 博客转移

    最近好久不来CSDN了 xff0c 自己搭建了一个博客 欢迎各位去新博客留言 http www l0g1n cn 以前学习汇编的博客 http www asmedu net blog user usermain jsp neighborId
  • 《Windows程序设计》之BLOKOUT1

    LRESULT CALLBACK WndProc HWND hwnd UINT message WPARAM wParam LPARAM lParam static BOOL fBlocking fValidBox static POINT

随机推荐