现在来分析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就是通过这个函数指针来实现启动内核的。
可以看到,指向的是内核头部的跳转地址。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210330192043307.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMzMTQxMzUz,size_16,color_FFFFFF,t_70
那么,跳转到入口地址之前,这些参数要怎么设置呢?
猜测:在Flash的某个位置,按某种格式保存这些参数,在设置时读出来设置即可。
对于2440开发板,就是在某个地址(0x30000100)按某种格式(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。
上一篇:1_5.1.5_U-boot分析与使用_u-boot分析之u-boot命令实现_P
下一篇:1_5.3.1_内核配置裁剪及启动流程_内核启动流程分析之编译
推荐阅读最新更新时间:2024-11-02 01:31
设计资源 培训 开发板 精华推荐
- NCP300LSN30T1 3V 窗口电压检测器的典型应用
- EVB-USB2514BC,评估板使用 USB2514B 高速 USB 2.0 多 TT 4 端口集线器,支持电池充电
- 用于STM32 Nucleo,基于LED1202器件的LED驱动扩展板
- 用于白光 LED 驱动器的 TB62752BFUG 升压型 DC/DC 转换器的典型应用
- 用于恒流源的 TL431A 可编程精密基准的典型应用
- 底层BEC与分电板 FOR FlyingRC F4Wing MK2
- LF25CDT-TR 2.5V 延迟开启低压降稳压器的典型应用
- EVAL-AD5535EBZ,用于 AD5535、32 通道、14 位、串行输入、高压输出 DAC 的评估板
- FSL538HFLYGEVB:FSL538HFLYGEVB:具有高压启动和 SenseFET 评估板的高性能 800 V 离线开关
- DC1410A-A,基于 LTC2498 称重传感器数字化仪应用的演示板
- Fluke 总有一款适合你需求的红外热像仪!参与赢好礼
- 有奖直播报名:TI DLP®技术在汽车行业的创新应用——增强型抬头显示
- TI EP类课程年度精选出炉,推荐分享赢好礼!
- 【有奖知识问答】光电子,点亮梦想!
- 2024安路科技FPGA技术研讨会-广州站 火热报名中
- 有奖直播预报名|TI 新一代Sitara™ AM62处理器革新人机交互——加速边缘AI的开发
- 【泰有聊】系列技术文章连载1:示波器“芯”升级,聊一聊TEK061/041 ASIC创新平台
- 百度大脑EdgeBoard 边缘AI计算盒(FZ5)免费测评体验
- 你评论,我送礼!《玩转TI MSP430 Launchpad》TI社区与EEWORLD联合首发!