历史上的今天
今天是:2025年08月20日(星期三)
2021年08月20日 | 1_5.1.6_U-boot分析与使用_uboot启动内核_P
2021-08-20 来源:eefocus
现在来分析uboot是怎么启动内核的。
我们知道,u-boot启动内核是通过两条指令来实现的。
nand read.jffs2 0x30007FC0 kernel; bootm 0x30007FC0
读出内核
nand read.jffs2 0x30007FC0 kernel
从kernel分区中读出内核,放到地址0x30007FC0去。
分区:对于windows系统来说,每个硬盘上都有分区表;对于嵌入式Linux来说,Flash没有分区表,但是我们可以人为的将这块Flash分为几个区,这些分区没有分区表,而是在源码中(100ask24x0.h)写死的。需要注意的是,对于这些分区,我们关心的不是分区的名字,而是分区的起始地址和大小。

可以使用mtd指令来查看分区,首先是256KB的BootLoader,然后是128KB的环境变量参数,然后是2M的kernel,起始地址是0x00060000,最后是root分区(根文件系统)。

所以nand read.jffs2 0x30007FC0 kernel就等于nand read.jffs2 0x30007FC0 0x00060000 0x00200000,这里再说明一下,分区名字不重要,重要的是起始地址和大小。
下面分析一下nand指令,在u-boot中使用? nand查看一下帮助信息,可以看到nand read的格式,后面的参数可以是地址+偏移,也可以直接是分区名字。

do_nand函数中对于read操作有单独的处理,如果是.jffs2,后面的size可以不需要页对齐,具体的实现暂时不管。

启动内核
bootm 0x30007FC0
Flash上存的内核是什么格式?
答:是UImage,UImage是由头部+真正的内核构成的。

使用指令bootm 地址,启动内核,会先读入头部,然后看内核是否已经加载完毕,如果加载完毕了就跳到 入口地址 去执行内核,否则先加载内核再跳转。
首先是读入头部。

然后判断内核是否已经加载完毕,如果没有就加载内核。

跳转地址是0x30008000,加载地址是0x30007FC0,中间相差64字节,这64字节就是头部的大小。
bootm的功能是:
根据头部,移动内核到合适的地方;
启动内核,这是通过do_bootm_linux函数完成的。

加载完毕之后,并不是直接跳到跳转地址就可以启动内核,正如PC机,在启动操作系统之前还需要检测内存等操作,Linux还需要告诉内核一些参数,也就是设置启动参数,然后才跳转到入口地址去启动内核。
所以启动内核之前,do_bootm_linux要做这些事情:
设置启动参数;
跳转入口地址。
我们先看一下是怎么跳转到入口地址的,在do_bootm_linux函数的末尾,有一个函数指针,u-boot就是通过这个函数指针来实现启动内核的。

可以看到,指向的是内核头部的跳转地址。
按某种格式(TAG)保存数据,在启动内核时把这些数据读出来解析。


下图是设置启动参数的一些函数调用,其中start和end都是必须的,其他则是设置不同参数的tag。

这里选出四个函数来分析,分别是:
setup_start_tag
setup_memory_tags
setup_commandline_tag
setup_end_tag
setup_start_tag
首先是setup_start_tag,其中的bd->bi_boot_params在前面设置过,为0x30000100,所以这个函数会从0x30000100地址开始设置参数




函数执行完,结果如下,从0x30000100开始存放数据,结束后params指向下一个空闲地址。

setup_memory_tags
之前有说过,windows系统在启动会需要检测内存,检测出大小,然后告诉操作系统,Linux系统同样需要,并且是通过setup_memory_tags来实现的。
函数如下,可以看到是通过设置不同的块信息来实现内存设置的。

我们使用的是两块32MB的SDRAM拼在一起,也就是一块,共64MB。


setup_memory_tags执行完成后,params指向下一个空闲地址。

setup_commandline_tag
调用setup_commandline_tag时还传入了一个临时变量commandline。

该变量是来自于环境变量bootargs的。

可以看到,bootargs = noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0,115200。
我们大概来分析一下这个指令,root就是根文件系统(相当于windows的C盘),它位于第3个Flash分区(从0开始),然后init=/linuxrc表示第一个应用程序是/linuxrc,最后console=ttySAC0,115200表示内核的调试信息从串口打印出来,波特率是115200。

下面是setup_commandline_tag的源码。

执行过后,数据的存放如下,可以看到这个就是把指令commandline存到内存中。

setup_end_tag
我们初始化变量,何时结束呢?就是setup_end_tag函数来结束的。
直接在tag位置设为结束标志0x00000000,就表示参数设置完成了。


这些数据都有特定的格式,内核启动时,就会来这些地方按tag格式来读取这些数据。
u-boot的最终目标是启动内核,为了实现这个目标,需要:
从Flash中读出内核;
启动:a.设置启动参数;b.跳转到入口地址。
跳转到入口地址时,我们传了三个参数,第一个参数是0,第三个参数是参数地址也就是0x30000100,那么第二个参数是什么呢?

答:是机器ID,u-boot可以支持很多种单板,这种单板也就是机器,那么具体现在使用的要支持哪种机器,就是通过这个机器ID来决定的。
在一开始board_init初始化的时候,我们就给bi_arch_number赋了一个值MACH_TYPE_S3C2440也就是362,这个值表示现在使用的机器是2440。

史海拾趣
|
开机的时候那个消磁按钮上的红色指示灯不停的闪,按一下会进行消磁动作,但好像不能完成消磁,数码管显示364一闪一闪的。 有时候开机一段时间后能消磁成功,有时候开机几个小时也不行。用过的DD指导一下,是有故障还是我使用问题?… 查看全部问答> |
|
如题,这个恐怕是大家经常遇到的问题吧? 一种方法是: 在任何时候都把“程序规模”限制在你所能够掌控的范围内。 那如果超过范围咋办?模块化——合理划分和封装细节。其实如果是自上而下的设计(通常如此),模块化设计是非常自然的事(因为 ...… 查看全部问答> |
|
最近看了一个电路,就是在触摸屏的四根线上,分别接了4个屏蔽电阻下拉接地。 想请教一下: 1。为什么触摸屏那四根线要下拉?我看很多触摸屏的电路,没有下拉使用也很正常啊! 2。为什么要用屏蔽电阻?普通电阻不行吗?会造成什么样的影响? 麻 ...… 查看全部问答> |




