Skip to main content
 首页 » 操作系统

Linux tiny4412-Uboot启动分析

2022年07月19日25lovecherry

一、从本质上将,引导转载程序至少应提供以下功能

  (1)设置和初始化RAM

  (2)初始化一个串口

  (3)检测机器类型(machine type)

  (4)设置内核标签列表(tag list)

  (5)调用内核映像

二、.Uboot引导Linux内核时的启动状态

  zImage可以放在RAM的任意位置被调用,但是通常放在32KB处,因为前16KB用于存放Linux页表,不能占用。

  (1)停止所有的DMA

  (2)CPU寄存器配置:

    r0 = 0;

    r1 = 机器类型码

    r2 = 标签列表在系统RAM中的物理地址,或者设备树块(dtb)在系统RAM中的物理地址

  (3)CPU模式:必须在SVC模式,而且必须禁止所有类型的中断(irq和fiq)

  (4)缓存和MMU:MMU必须关闭,数据缓存必须关闭,指令缓存可关可不关

  引导加载程序应通过直接跳转到LInux镜像的第一条代码处来调用内核镜像

三、相关的bootm命令讲解

    在常用的 uboot 中完成一系列的初始化后最后通过 bootm 命令加载 linux 内核。bootm 将内核镜像从各种媒介中读出,存放在指定的位置;然后设置标记列表给   内核,传递参数;最后跳到内核的入口点去执行。

u-boot中每个命令都是通过 U_BOOT_CMD 宏来定义的,格式如下:

   U_BOOT_CMD(name,maxargs,repeatable,command,"usage","help")

各项参数的意义如下:

  (1) -- name:命令的名字,注意,它不是一个字符串(不要用双引号括起来);

  (2)-- maxargs:最大的参数个数;

  (3)-- repeatable:命令是否可以重复,可重复是指运行一个命令后,下次敲回车即可再次运行;

  (4)-- command:对应的函数指针,类型为(*cmd)(struct cmd_tbl_s *, int, int, char *[]);

  (5) -- usage:简单的使用说明,这是个字符串;

  (6)-- help:较详细的使用说明,这是个字符串。

bootm命令的源码路径为:u-boot源码路径/common/cmd_bootm.c,入口函数为d_bootm,(参考1中有对它的稍具体分析),该函数主要的工作流程是,通过bootm_start来获取内核镜像文件的信息,然后通过bootm_load_os函数来加载内核,

最后通过boot_fn来启动内核(这三个函数参考1中有对它的稍具体分析)。

四、kernel_entry函数

真正将控制权交给内核, 启动内核;满足arm架构linux内核启动时的寄存器设置条件:第一个参数为0 ;第二个参数为板子id需与内核中的id匹配,第三个参数为启动参数地址bi_boot_params 。主要操作:

(1)首先取出环境变量bootargs,这就是要传递给内核的参数。( u-boot 是通过标记列表向内核传递参数,标记在定义为tag,是一个结构体,在 arch/arm/include/asm/setup.h 中定义)

(2)调用setup_XXX_tag

参考1中有自己实现的6410的启动内核的例子

五、与启动系统相关的环境变量

  有bootcmd和bootargs,可保证在Uboot不重新编译的情况下可以以不同的方式启动

    ①bootcmd命令可以是好几个命令的集合,主要负责内核的引导工作,例如:bootcmd=movi read kernel 30008000;bootm 30008000,将内核拷贝到0x30008000的地方,然后从内存的0x30008000的地方启动内核;

    Uboot在完成一系列的初始化后,就开始加载内核到DRAM中,加载到的地址可由bootcmd命令指定这个30008000是由内核的链接地址所确定的。

    ②bootargs命令主要是负责向内核传递参数,例如:bootargs=console=ttySAC2,115200 root=dev/mmcblk0p2 rw init=/linuxrc rootfstype=ext3,表示引导后控制台使用串口2,波特率115200,根文件系统在SD卡(0号

    设备)的第2分区,并且可读可写;进程1(init进程)的地址为/linuxrc,使用的根文件系统是ext3类型。

六、判断镜像的种类

  Uboot将Linux内核加载到DRAM的链接地址处后,就开始读镜像的头信息,找到MAGIC_NUM,以此来判断镜像的类型,镜像有两种格式:在Image和uImage

    zImage:Linux内核编译出来后,最先生成的是.elf格式的可执行文件,类似Windows下的.exe文件,其中包含了在操作系统下运行的大量信息,但是Linux内核是个逻辑程序,不需要这些信息,删掉他们后大小变为Image(.bin,

  已经可以直接运行了)文件,大小为原来的1/10,然后再对其进行压缩,就形成了zImage文件。这个zImage是自解压的,解压操作由其头部的解压程序来完成,不需要Uboot的参与。

    uImage是Uboot发明的一种镜像类型。它是在zImage的前面加上一些信息和一些校验信息,64字节。将zImage加工为uImage的工具在U-boot根目录下/tools中的mkimage程序,只需将其复制到/usr/local/bin/,编译内核时输

  入make uImage,然后就能产生uImage了。

  判断完镜像种类后 ,Uboot将对镜像头进行校验,然后再次读取头信息,从头信息中读取这个镜像的各种信息(比如镜像长度,镜像种类,入口地址等)。

七、新版的Uboot与内核之间的传参引导

  近年来的内核在启动时还需要设备树的dts文件,于是比较新的uboot都实现了传递dtb的功能,为了使能设备树,需要在编译U-boot的时候在config文件中加入:#define CONFIG_OF_LIBFDT

  一般分为三种情况:

  (1)利用U-boot的命令,在引导kernel时将dts传入。这种方式需要将dtb的地址写到uboot中(一般是环境变量),比如:首先将kernel载入内存,然后fdt addr ${fdtaddr}命令将dtb载入内存

   最后使bootz ${loadaddr} ${initrdaddr} ${fdtaddr}来引导内核,(其中initrd是临时文件系统,嵌入式中用得极少)实际使用时用“-”代替:bootz ${loadaddr} - ${fdtaddr}

  (2)将dts和kernel打包为pImage。这种方式无需将dtb的地址写到uboot中(但uboot中要实现读pImage头部的功能),uboot可以去pImage的头部信息处读取到dtb的地址,然后传给传递给kernel。

  (3)启用kernel中”ARM_APPENDED_DTB”选项,该选项的意思是将dtb和kernel打包在一起,如此一来kernel启动时会去紧挨着它的地方寻找dtb,这样就不需要uboot来传递dtb地址了。

八、引导内核时可能出现的问题

  如果U-boot确认无误可以启动起来,而kernel的启动却出现了问题,那么一般是以下几种情况:

  1. 对于老版本的U-boot,有大概率是U-boot传给kernel参数的时候出了问题,着重注意一下创建tag的宏有没有正常定义,如CONFIG_CMDLINE_TAG、CONFIG_MTDPARTITION等
  2. kernel的链接地址与加载地址不符,链接地址可以通过kernel的head.S获知,详见kernel启动汇编阶段分析
  3. kernel的自解压地址与链接地址不符,zImage这类kernel格式必须把自己解压到自己的链接地址,自解压地址在kernel源码目录arch/arm/mach-xxxx/Makefile.boot文件中
  4. 环境变量未被正常设置

参考1:http://www.cnblogs.com/CoderTian/p/6006400.html

参考2:http://blog.csdn.net/qq_28992301/article/details/51873201


本文参考链接:https://www.cnblogs.com/hellokitty2/p/7402044.html