[嵌入式Linux]uboot启动kernel的过程分析

文章目录

    • 一、 摘要
      • 1. 下文将提及
      • 2. 下文将不讲
    • 二、各文件的大小比较
    • 三、各存储器的大小比较
    • 四、各文件在存储器中的分布
    • 五、uboot启动kernel过程

一、 摘要

这篇文章结合JZ2440v3开发板和uboot1.1.6代码讲述uboot如何启动内核(kernel版本为linux2.2.26,但实际没讲到和kernel代码有关的阶段)。
太长不看:可直接跳到第5节看uboot启动kernel过程

1. 下文将提及

(1) 各存储器大小:NAND、NOR、SDRAM;
(2) 各文件大小:uboot、param、kernel、root;
(3) uboot启动kernel过程中涉及的关键函数及其之间的调用关系;

2. 下文将不讲

(1) 不讲uboot如何提前于kernel加载到SDRAM中的;
(2) 不讲kernel被uboot启动后如何开始它自己的启动流程的;
(3) 不讲使用nfs网络文件系统时可以没有root分区的情况;
(4) 不讲uboot存放在NOR下的启动过程;

二、各文件的大小比较

一个正常能运行linux的开发板,其NAND flash中包含4部分数据:bootloader/uboot、params、kernel、root。
(1) bootloader/uboot:uboot1.1.6打补丁后编译的bin文件将近200k
(2) params:不固定,上限设为128k;params即uboot要传给kernel的全部参数的链表;
(3) kernel:linux2.2.26打补丁后编译的bin文件将近2M
(4) root:根文件系统,上限为NAND flash中剩余的200M+空间。一个最小的、仅包含busybox(暂且叫做“命令工具箱”吧)和必备文件夹的yaffs2文件系统,大小约9M;

三、各存储器的大小比较

提一下:这里说的(我所理解)“存储器”包括外部RAM和ROM类存储器:SDRAM、NAND、NOR。
但从S3C2440手册看到,SDRAM和NOR flash由于共用一样的总线所以统称为“存储器(Memory)”,NAND单独算作“NAND Flash”类(如图):
[嵌入式Linux]uboot启动kernel的过程分析_第1张图片

S3C2440给4个文件在NAND中分配的空间大小如下:
SDRAM = 0x30000000 + 64MB
NOR = 0x00000000 + 2MB
NAND = 0x00000000 + 256MB

我本来觉得:kernel上电“启动”、“加载”后,把多少字节从flash(NAND)拷贝到SDRAM中备用呢?SDRAM毕竟是用来做内存条的闪存颗粒,可不便宜。应该装不下整个kernel吧?
比较kernel文件和SDRAM空间大小发现:完整加载kernel只占用1/16的空间——SDRAM还剩余大部分空间可用~
而且后来发现SDRAM和NAND价钱差不多,加大SDRAM容量把kernel甚至和别的数据(如有必要)一起完整拷也没问题(反而是NOR的单价是SDRAM、NAND的30倍。所以只用了2MB的片子):
SDRAM = 4.2/32 = 0.063/MB
[嵌入式Linux]uboot启动kernel的过程分析_第2张图片
NOR = 4/2 = 2元/MB:
[嵌入式Linux]uboot启动kernel的过程分析_第3张图片
NAND = 17.5/256 = 0.07元/MB:
[嵌入式Linux]uboot启动kernel的过程分析_第4张图片

1_bootloader大小
2.kernel大小
4.最小yaffs2根文件系统
所以uboot和kernel都完整加载到SDRAM后,实际占用/剩余空间大小:
[嵌入式Linux]uboot启动kernel的过程分析_第5张图片

四、各文件在存储器中的分布

(1) (不重要)uboot除了存放在NAND,也可以存放在NOR中:从地址0x0开始,把NOR中uboot前面的一小段代码当做SDRAM执行(毕竟两者共用总线,而且NOR对比NAND的一大特点就是支持代码就地执行(XIP,execute in place)),然后跳转到SDARAM执行剩余的大部分代码——这正是由前面一小段代码从NOR搬运来SDRAM的。
(2) 上电启动前,文件都在NAND flash中,(用mtd命令可看到)分布(起始地址和大小):
[嵌入式Linux]uboot启动kernel的过程分析_第6张图片

值得注意的是:图中左边的NAND Controller Space表示JZ2440v3开发板上使用的NAND flash的总大小256MB及其分区划分;右边的Memory Controller Space表示SDRAM和NOR因共用总线而得到的命名空间分布(即它们各自地址的范围),并不是实际容量——因为SDRAM和NOR都是比NAND贵很多的存储器,所以即使有1G的命名空间,SDRAM只占用其中的64MB、NOR占用2MB。

 #: name                 size           offset    mask_flags
 0: bootloader          0x00040000      0x00000000      0
 1: params              0x00020000      0x00040000      0
 2: kernel              0x00200000      0x00060000      0
 3: root                0x0fda0000      0x00260000      0

即:

NAND = 0x00000000 + 256MB
	=> uboot = 256kB
	=> params = 128kB
	=> kernel = 2MB
	=> root = 256M - 256k - 128k - 2M ~= 254MB

(3) 上电启动后,文件都在SDRAM中,分布(起始地址和大小):
[嵌入式Linux]uboot启动kernel的过程分析_第7张图片

五、uboot启动kernel过程

这里正式开始讲uboot启动kernel过程涉及的函数及其之间的调用关系。
该过程从uboot代码执行设定好的bootcmd命令作为开始(在uboot命令行下输入print可以看到它的值):
bootcmd=nand read.jffs2 0x30007FC0 kernel; bootm 0x30007FC0
bootm命令作为bootcmd命令的后半部分,对应uboot代码中的do_bootm()函数。
(1) uboot先提前于kernel加载到SDRAM中了,然后执行bootcmd命令的前半段:
把kernel从NAND中完整读取到SDRAM;
(2) 执行bootcmd命令的后半段:检查并移动SDRAM中的kernel到“正确”位置,然后启动它(以下提到的image可能指代SDRAM的kernel或代码的image全局结构体变量):

do_bootm ->
	bootm_start ->
		boot_get_kernel:
			复制头部64B到image.image_header_t;
			并提取其中的kernel地址、大小、类型,另外保存到image.os
		根据检查到的image类型,填充image其他成员
	boot_load_os ->
		对比头部信息中的kernel“设置启动SDRAM地址”(iamge_addr)与当前已加载到的实际所在地址(load);
		若不相等,则重新搬运(SDRAM->SDRAM)
	boot_fn=boot_os ->
		即运行do_bootm_linux():准备跳转启动kernel

(3) (这也属于bootm命令的内容;但属于另一阶段的动作,所以另起一小节)

do_bootm ->
	do_bootm_linux ->
		boot_prep_linux:
			以链表形式保存要传递给kernel的参数到某个位置(128kB的param分区):
				它以gd->bd->bi_boot_params为起始地址(gd="global data";bd="boot data");
				这些参数包括:kernel信息、代码设置的变量参数、bootargs传入的(根文件系统路径)命令等
		boot_jump_linux:
			传3个参数到寄存器,并跳转到kernel,由kernel取出这3个参数、然后正式启动;
			(R0=0x0,R1=机器号,R2=参数链表地址:指向更多启动参数)

你可能感兴趣的:(嵌入式,嵌入式,Linux,uboot)