weiqi7777

cortex-a8 uboot系列:第十一章 uboot源码分析 uboot如何启动内核1

0
阅读(4382)

一、            Uboot和内核到底是什么(从系统启动角度看)

1.    Uboot

Uboot的本质是一个复杂的裸机程序。

2.    内核

操作系统内核本身也是一个裸机程序,和uboot、其他裸机程序并没有什么本质区别。

但是和其他裸机程序的区别:操作系统运行起来后在软件上分为内核层和应用层,分层后两层的权限不同,内核访问和设备操作的管理上更加精细(内核可以随意访问各种硬件,而应用程序只能被限制的访问硬件和内存地址)。

Uboot的镜像是u-boot.binlinux系统镜像是zImage,这两个文件都是两个裸机程序bin文件镜像。

二、            SD卡中的分区

一个完整的软件+硬件的嵌入式系统,未上电时,bootloaderkernelrootfs等必须的软件都以镜像的形式(.bin的形式),存储在启动介质中(x210iNand/SD卡)。运行时,都是在DDR内存中运行,与存储介质无关。这两个状态都是稳定状态。

还有一个状态是动态过程,从静止态到运行态的过程,也就是启动过程。

动态启动过程就是从启动介质(以下以SD卡为例)逐步搬移到DDR内存,并且运行启动代码进行相关的硬件初始化和软件结构的建立,最终达到运行时稳定状态。

静止时u-boot.bin,zImage,rootfs都在SD卡中,他们不能随意存放在SD卡的任意位置,因此需要对SD卡进行一个分区,然后将各种镜像各自存在各自的分区中,这样在启动过程中就知道去什么位置查找uboot、内核等。(ubootkernel中的分区表必须一致,同时和SD卡的实际使用的分区要一致)。

三、            运行时必须要先加载到DDR中链接地址处

Uboot在第一阶段中进行重定位时将第二阶段(整个uboot镜像)加载到DDR0xc3e00000地址处,这个地址就是uboot的链接地址。

内核也有类似要求,uboot启动内核时要将内存从SD卡读取放到DDR中(其实就是重定位的过程),不能随意放置,必须放在内核的链接地址处,否则不能启动内核。如使用的内核链接地址是0x30008000,那么uboot拷贝kernel代码时,就要将kernel拷贝到地址0x30008000DDR中去。

四、            内核启动需要必要的启动参数

Uboot是无条件启动的,从零开始启动。

内核是不能开机自动完全从零开始启动的,内核启动要别人帮忙。Uboot要帮助内核实现重定位(从SD卡到DDR),以及给内核提供必要的启动参数。如dram的起始地址以及大小,所用串口以及波特率,根文件系统位置等等。

uboot启动kernel时,会调用kernel的入口函数,此时要给kernel传递三个参数。这三个参数是固定的:

r0 固定为0

r1 机器码

r2 启动参数在ddr中的位置

五、            启动内核

uboot要启动内核,分为两个步骤:第一步是将内核镜像从启动介质中加载到DDR中,第二步是去DDR中启动内核镜像。(内核代码没有考虑重定位,因为内核知道会有一个bootloader将内核代码加载到DDR链接地址处,所以内核直接从链接地址处运行)

1.    加载内核到DDR

静态内核镜像放在哪里?

有两种情况:

第一种:SD/iNand/Nand/Norflash等:raw分区

常规启动时各种镜像都在SD卡中,因此uboot只需要从SD卡的kernel分区读取内核镜像到DDR即可。

读取要使用uboot的命令来读取(x210iNand版本是movi命令,x210Nand版本就是Nand命令)

clip_image002

如读取kernel到内存0x30008000

         movi read kernel 0x30008000

这里的kernel指的是uboot中的kernel分区(就是uboot中规定的SD卡中的一个区域范围,这个区域范围被设计来存放kernel镜像,就是所谓的kernel分区)

 

第二种:tftpnfs等网络下载方式从远端服务器获取镜像

uboot还支持远程启动,也就是内核镜像不烧录到开发板的SD卡中,而是放在主机的服务器中,然后需要启动时uboot通过网络从服务器中下载镜像到开发板的DDR中。

首先在主机上搭建tftp服务器,然后将要下载的文件拷贝到tftp设置的传输目录中。

clip_image004

开发板的uboot中,使用tftp命令将远程的文件通过网络下载下来。

clip_image005

tftp命令格式

         tftp     DDR地址  远程下载的文件名

 

不管哪种方式,最终结果要的是内核镜像到DDR中特定地址即可,不管内核镜像是怎么到DDR中的。以上2种方式各有优劣。产品出厂时会设置为从SD卡中启动;tftp下载远程启动这种方式一般用来开发。

 

镜像要放在DDR的什么地址

内核一定要放在链接地址处。链接地址要去内核源代码的链接脚本或者makefile中去查找。X210中是0x30008000

2.    执行内核

ubootkernel拷贝到DDRkernel的链接地址处后,直接调用kernel的入口函数,即可启动内核。

uboot使用命令bootm来启动内核。

六、            zImageuImage的区别联系

1.    bootm命令对应do_bootm函数(common/cmd_bootm.c)

clip_image007

CONFIG_SECURE_BOOT宏表示安全启动,对程序做了更安全的校验,对于一般应用,这个是不需要的。

CONFIG_ZIMAGE_BOOT宏很重要,这个宏控制条件编译支持zImage格式的内核启动的一段代码。

2.    vmlinuzzImageuImage

uboot经过编译直接生成的elf格式的可执行程序是u-boot,这个程序是可执行,类似于windows下的exe格式,在linux下可直接执行。但是这种格式不能用来烧录下载。用来烧录下载的是u-boot.bin。这个是由u-boot使用arm-linux-objcopy工具来进行转化(主要是去掉无用的信息)。这个u-boot.bin就叫镜像(image),镜像就是用来烧录到启动介质中让CPU去取指执行的。

Linux内核经过编译后也会生成一个elf格式的可执行程序,叫vmlinuxvmlinuz,这个是原始的未经任何处理加工的原版内核elf文件。嵌入式系统部署时烧录的一般不是这个vmlinux/vmlinuz,而是要用objcopy工具去制作成烧录镜像格式(就是u-boot.bin这种,但是内核没有.bin后缀),制作出来的镜像文件就叫Image(这个镜像就比elf格式的文件要小很多)。

原则上Image就可以直接烧录到启动介质中,但是实际上linux的开发者认为Image的大小还是太大,所以对Image进行了压缩,并且在Image压缩后的文件的前端附加了一部分解压缩代码。构成了压缩格式的镜像就叫zImage

clip_image009

         Uboot为了启动linux内核,还发明了一种内核格式叫uImageuImage是由zImag加工得到的,uboot中有一个工具,可以将zImage转换成uImage。注意:uImage不关linux内核的事,linux内核只管生成zImage即可,然后uboot中的mkimage工具再去由zImage转换成uImage来给uboot启动。这个转换过程其实就是在zImage的前面加上64字节的uImage的头信息即可。

         原则上uboot启动时应该给他uImage格式的内核镜像,但是实际上uboot中也可以支持zImage启动,是否支持就看x210_sd.h中是否定义了CONFIG_ZIMAGE_BOOT这个宏。

         这也就是,有些uboot支持zImage启动,有些则不支持。但是所有的uboot肯定都支持uImage启动。

 

         对于mkimage工具,在uboot下的tools目录下,由mkimage.cmkimage.h生成mkimage,通过mkimage.c就可以知道添加的64字节头信息是什么内容。

clip_image011

         上面这一段代码就是提供ubootzImage启动。当判断镜像是zImage,对其进行校验,校验完后,直接跳转到after_header_check符号处,后面的其他镜像就不校验了。

         LINUX_ZIMAGE_MAGIC是一个定义的魔数,这个数等于0x016f2818,表示这个镜像是一个zImage。也就是zImage格式的镜像中在头部的一个固定位置存放了这个数作为格式标记,如果有一个image,去这个位置读取4个字节的数据,判断是否为0x016f2818,是的话,说明是zImage,否则不是。

         命令bootm 地址(0x30008000),所以do_bootmargc=2argv[0]=bootm,argv[1]=0x30008000。但是有argc小于2的情况,这个时候启动地址就是默认的地址,追寻,这个地址就是内存的基地址0x30000000。所以对于bootm命令,可以不带参数执行。不带参数,就默认为启动地址为0x30000000

         从镜像的37字节开始的4个字节数据读取出来,判断是否为0x016f2818,从而可以判断出这个镜像是不是zImage

 

clip_image013

         image_header_t(include/image.h)这个数据结构式uboot启动内核使用的一个标准启动数据结构,zImage头信息也是一个image_header_t,但是在实际启动之前需要进行一些改造。

         imagesbootm_headers结构体,include/image.h)全局变量是在do_bootm函数中使用,用来完成启动过程的固定。zImage的校验过程就是先确认是不是zImage,确认后再修改zImage的头信息到合适,修改后用头信息去初始化images这个全局变量,然后就完成了校验。

         clip_image015

clip_image017

 

         编译kernel时,

         直接make 生成zImage

         make uImage 生成uImage 但是会用到mkimage工具,而这个工具在linux源码中是没有的,因此会直接报错。

clip_image019

 

需要将uboottools目录中的mkimage拷贝至$PATH的目录下,就可以了。

clip_image021

 

此时在make uImage,就会在arch/arm/boot目录下生成uImage文件了。

clip_image023

 

 

将该文件通过tftp下载带开发板的DDR,然后使用bootm命令运行。

clip_image025

         黄色部分就是uboot解析uImage打印的信息。