一、uboot和内核到底是什么?
uboot的本质就是一个复杂点的裸机程序。
操作系统内核在本质上也是一个裸机程序,和uboot、和其他裸机程序并没有本质区别。
区别就是操作系统运行起来后在软件上分为内核层和应用层,分层后两层的权限是不同的,内存访问和设备上更加精细
(内核可以随便访问各种硬件,而应用程序只能访问限制的硬件和内存地址)。
二、部署在SD卡中特定分区内
1、 一个完整的软件+硬件的嵌入式系统,静止时(未上电时)bootloader、kernel、rootfs等必须的软件都以镜像的
形式存储在启动介质中(x210中是iNand/SD卡);运行时都是在DDR中运行的,与存储介质无关。上面2个状态都是
稳点状态,第3个状态是动态过程,即从静止态到运行态的过程,也就是启动过程。
2、动态启动过程中就是一个从SD卡逐步搬迁DDR内存中,并且运行启动代码进行相关的硬件初始化和软件架构的建
立,最终达到运行时稳定状态。
3、静止时u-boot.bin zImage rootfs都在SD卡中,他们不可能随意存在SD卡的任意位置,因此需要对SD卡进行一个
分区,然后将各种镜像各自存在各自的分区中,这样在启动过程中uboot、内核等就知道到哪里去找谁。(uboot和
kernel中的分区表必须一致,同时和SD卡的实际使用的分区要一致)。
4、内核的启动条件
(1)、uboot在第一阶段中进行重定位时将第二阶段(整个uboot镜像)加载到DDR的0xc3e00000地址处,这个
地址就是uboot的链接地址。
(2)、内核也有类似要求,uboot启动内核时将内存SD卡读取放到DDR中(其实就是个重定位的过程),不能随
意放置,必须放在内核的链接地址处,否则启动不起来。譬如我们使用的内核链接地址是0x30008000。
(3)、uboot是无条件启动的,而内核启动需要uboot帮kernel重定位(从SD卡到DDR),uboot还要给内核提供
启动参数。
5、启动内核的步骤
(1)、将内核镜像从启动介质中加载到DDR中。
(2)、去DDR中启动内核镜像。
6、uboot还支持远程启动
(1)、内核镜像不烧录到开发板的SD卡中,而是放在主机的服务器中,然后需要启动时uboot通过网络从服务器
中下载镜像到开发板的DDR中。
7、zImage和uImage之间的区别连系
(1)、uboot经过编译直接生成的elf格式的可执行程序是u-boot,这个程序类似于windows下的exe格式,在操
作系统下是可以直接执行的。但是这种格式不能烧录,烧录下载的是u-boot.bin,这个东西就叫镜像(image)。
(2)、linux内核经过编译后也会生成一个elf格式的可执行程序,叫vmlinux或vmlinuz,这个就是未加工的原版
内核elf文件。嵌入式系统部署时并不是烧录这个,而是用交叉编译工具将这个东西加工成镜像格式,经过加工成
烧录镜像的文件叫Image(制作时把78M精简成了7.5M)。
(3)、原则上是可以直接烧录Image,但是linux作者觉得还是太大了所以对Image进行了压缩,并且在压缩后的
文件前端加上了一些解压代码,构成了zImage。
(4)、而uImage是由zImage加工得到的。这个过程是在zImage的头部加上64字节的头信息即可。
(5)、原则上uboot启动时应该给他uImage格式的内核镜像,单实际上uboot也支持zImage。有些uboot支持
zImage启动,有些不支持,但是所有uboot都支持uImage启动。
8、zImage启动
(1)、LINUX_ZIMAGE_MAGIC这个是一个定义的魔数,这个数等于0x016f2818,表示这个镜像是一个zImage。
也就是说zImage格式的镜像中在头部的一个固定位置存放了这个数作为格式标记。如果我们拿到了一个image,去
他的那个位置去取4字节判断它是否等于LINUX_ZIMAGE_MAGIC,则可以知道这个镜像是不是一个zImage。
9、uImage启动
(1)、 uImage方式是uboot本身发明的支持linux启动的镜像格式,但是后来这种方式被一种新的方式替代,
这个新的方式就是设备树方式(在do_bootm方式中叫FIT),uImage的启动校验主要在boot_get_kernel函数中,
主要任务就是校验uImage的头信息,并且得到真正的kernel的起始位置去启动。
10、镜像的entrypoint
(1)ep就是entrypoint的缩写,就是程序入口。一个镜像文件的起始执行部分不是在镜像的开头(镜像开头有n个字
节的头信息),真正的镜像文件执行时第一句代码在镜像的中部某个字节处,相当于头是有一定的偏移量的。这个
偏移量记录在头信息中。
(2)一般执行一个镜像都是:第一步先读取头信息,然后在头信息的特定地址找MAGIC_NUM,由此来确定镜像
种类;
第二步对镜像进行校验;第三步再次读取头信息,由特定地址知道这个镜像的各种信息(镜像长度、镜像种类、
入口地址);第四步就去entrypoint处开始执行镜像。
(3)theKernel = (void (*)(int, int, uint))ep;将ep赋值给theKernel,则这个函数指向就指向了内存中加载的OS镜
像的真正入口地址(就是操作系统的第一句执行的代码)。
11、机器码的再次确定
(1)uboot在启动内核时,机器码要传给内核。uboot传给内核的机器码是怎么确定的?第一顺序备选是环境变量
machid,第二顺序备选是gd->bd->bi_arch_num(x210_sd.h中硬编码配置的)。
12、tag方式传参
(1)struct tag,tag是一个数据结构,在uboot和linux kernel中都有定义tag数据机构,而且定义是一样的。
(2)tag_header和tag_xxx。tag_header中有这个tag的size和类型编码,kernel拿到一个tag后先分析tag_header
得到tag的类型和大小,然后将tag中剩余部分当作一个tag_xxx来处理。
(3)tag_start与tag_end。kernel接收到的传参是若干个tag构成的,这些tag由tag_start起始,到tag_end结束。
(4)tag传参的方式是由linux kernel发明的,kernel定义了这种向我传参的方式,uboot只是实现了这种传参方式
从而可以支持给kernel传参。
总结:第一步:将内核搬移到DDR中
第二步:校验内核格式、CRC等
第三步:准备传参
第四步:跳转执行内核
涉及到的主要函数是:do_boom和do_bootm_linux
uboot能启动的内核格式:zImage uImage fdt方式
跳转与函数指针的方式运行内核
欢迎各位指出不足之处