(八)uboot源码分析启动第一阶段
1、start.S的引入
u-boot.lds中找到start.S入口
(1)在C语言中整个项目的入口是main()函数(这是c规定的)
譬如说有一个10000个.c文件的项目,第一个要分析的就是包含了main函数的那个文件。
(2)在uboot中因为有汇报阶段参与,因此不能找main.c。整个程序入口地址取决于lds链接脚本里ENTRY声明的地方
ENTRY(_START)因此_start所在的文件就是整个文件的起始文件,_start所在的代码就是整个程序的起始代码。
sourceinsight中如何找到文件
(1)当前状况:我们知道在uboot中的10000多个文件中有一个符合叫做_start,但是我们不知道这个符号在哪个文件中。这种情况我们查找一个符号在所在项目的文件中的引用,需要用到sourceinisght或者VScode的搜索功能。
一般是整个工程里找。大小写敏感不敏感也选上。
找到本板子cpu的start文件
在SI中知道文件不知道目录,那么我们怎么找到并打开:
2、头文件分析
(1)#include<config.h>的config.h是在include目录下的,这个文件不是源码中本身存在的,而是配置过程中自动生成的文件(详见mkconfig脚本),这个文件的内容起始是包含了一个头文件:
#include<configs/x210_sd.h>
(2)经过分析后,发现start.S中包含的第一个头文件就是:include/config/x210_sd.h,这个文件是整个uboot移植时候的配置文件,这里面是好多宏。
下面的宏判断就是判断在不在x210_sd.h这个里面。因此这个头文件包含将include/configs/x210_sd.h文件和start.S文件关联了起来。因此之后在分相似start.S文件时,主要要考虑的就是x210_sd.h文件。
包含一个#include<version.h>
include/version_autogenerated.h,这个头文件就是配置过程中自动生成的。定义了一个宏
U_BOOT_VERSION“U-BOOT_VERSION”的值是一个字符串。字符串中的版本号信息来自于Makefile中的配置值。
这个宏在程序中会被调用,在uboot启动过程中会串口打印出uboot的版本号,那个版本号信息就是从这里来的。
#include <asm/proc/domain.h>
asm不是我们原始目录,是我们配置时候创建的一个符号链接,实际指向的是asm_arm
经过分析发现实际文件是:include/asm-arm/proc-armv/domain.h
从这里可以看出之前配置时候创建的符号链接的作用。如果没有这些符号链接就没法在windows下搞。
思考:为啥start.S不直接包含domain.h,而要用asm/proc/domain.h
这样的设计主要是为了可移植性。因为如果直接包含,则start.S文件就和CPU架构有关了,可移植性就差了。用符号链接之后则start.S中代码不用改,只有该配置时候创建的符号链接指向不同,则可以有可移植性。
3、代码分析
头部字节校验
.word相当于伪指令一个数据类型的意思,定义了四个数组,第一个是0x2000,剩下都是0x0;一共16个字节。
一个.word是4个字节,类似于int
这16个就是整个镜像开头的16字节的校验头。(裸机程序中没考虑过这16字节的校验头,因为:1、usb启动直接下载的方式启动的不需要。2、如果SD卡启动mkv210miage.c中会给原镜像前加16字节的校验头)
uboot这里start.S中在开头位置放了16字节的填充占位,这个占位的16字节只是保证正式的image之前的头部确实有16字节,但这16字节的内容是不对的,还是需要后面去计算校验然后重新填充的。
异常向量表的构建
每一个cpu都有一个异常向量表
第一个是复位异常
后面是未定义指令
软中断异常
预取指异常
数据异常
没用的
irq
frq
安装cpu设计时候的异常向量表。发生异常后会跳到函数过去
可以看到函数
发生instruction后
这就是处理未定义指令异常的办法。
这里reset是cpu复位后来这里进行(cpu进入复位异常向量表,看到b reset这个命令,就会直接跳转到reset这个地方来执行)
(1)异常向量表的构建是硬件决定的,软件只是参照硬件的设计来实现。
(2)异常向量表中的每种异常都应该被处理,否则真遇到了这种异常程序就跑飞了,但是我们在uboot中并未非常细致的处理各种异常。
uboot本身运行时间特别短,出问题概率小所以不用花太多精力。出了问题就会死掉,硬件上再来重启。操作系统内核就不可以这样了,必须非常细致的处理每一个异常。
(3)复位异常处的代码是 b reset
因此在cpu复位后真正有效去执行的代码是reset处的代码,真正有意义的代码是在这里开始的!!!
之后填充一些字节;
.balignl是伪指令,让它以16字节为单位对齐,如果没有对齐则用后面的二进制数据来填充它。
本身这句指令就是对齐排布,若没对齐则地址自动向后走(用0xdeadbeef来填充)直到对齐。
0xdeadbeef是一个十六进制的数字!(不是字符串),这个数字很有意思,组成这个数字的十六进制数全是abcdef中的。而且这8个字母刚好组成个文字。
为啥要对齐呢?有时候是硬件要求有时候是效率。
TEXT_BASE
第100行这个TEXT_BASE就是之前分析Makefile配置阶段的TEST_BASE,其实就是我们链接时指定的uboot的链接地址。
源代码(.S)中和配置makefile中很多变量是可以互相运送的,简单来说有些符号的值(.S)可以从Makefile中传递到源代码中
标号:
类型 值
这三者之间类似于定义了一个指针,名字叫做_TEXT_BASE,类型是.word,值是TEXT_BASE。
.word是四字节的
后面可以LDR这个TEXT_BASE
指的是物理地址;
CFG_PHY_UBOOT_BASE是在include/configs里面配置的x210.h里
MEMORY_BASE_ADDRESS可以看到是
0x30000000是DDR内存的起始地址
config uboot的值就是 0x33e00000
这个值是uboot在DDR内存中的物理地址;虚拟地址是0xC3e00000。(相对应的虚拟地址映射)
用来将来清除bss段用,决定重定位时候用。
宏定义式,如果用到了中断则要处理一些与中断有关的东西。
我们uboot这里不用中断的一般,所以一般是没啥用的
这里是cpu上电后执行的地方,145-148被注释掉了所以没有用。
msr是向cpsr_c写0xd3,这个有什么用呢?
是在armd 37个寄存器里的CPSR,cpsr_c表示只修改最后的8位,0xd3表示1101 0011
低5位代表了使用SVC模式。
bit5 表示处理器处于ARM状态。
bit6,bit7分别表示禁止IRQ和FIQ。
0xd3表示I&F disable ,model :SVC
这句是
将我们CPU设置为禁止FIQ IRQ,QRM状态,SVC模式。(一上来先禁止了中断不然跑飞了)
其实ARMCPU复位时候默认就进入了SVC模式,但是这里还是使用代码将其置为了SVC模式。
整个CPU在uboot模式下需要处于SVC模式(特权模式)。
CPU inti crit:
如果没有定义CONFIG_EVT,那么就进行操作但是已经定义了。
bl disable_l2cache //禁止L2 cache(CPU底层的初始化)
bl set_l2chache_auxctrl_cycle //L2 cache初始化
bl enable_l2cache //使能l2 cache
刷新L1cache的icache和dcache icache是指令cache,dcache是数据cache。
关闭MMU
上面这些都是与CPU的cache和mmu有关的,不用去细看,大概知道即可。
先给r0加载一个PRO_ID_BASE 是0XE0000000
这个在S5pc110.h这个头文件中包含,这个头文件是在#include <regs.h>中。
之后加了一个偏移量
这个偏移量是4
说明我们查的寄存器是0xE0000004
这个寄存器记录了一个数值,就是
从哪里启动是由SoC的OM5:OM0这六个引脚的高低电平决定的。
实际上在210内部有一个寄存器,这个寄存器地址是0xE0000004,这个寄存器的值是我们硬件根据OM引脚的设置而自动设置值的。这个值反应的是OM引脚的接法,也就是OM引脚的高低(真正启动介质是谁)
我们代码中可以读取这个寄存器的值然后判断其值来确定当前选中的介质是Nand还是Nor还是其他的。
在r2寄存器中存储了一个数字,这个数字等于某个特定的值时就表示从SD启动,等于另一个特定值表示从Nand启动。。。
如果是CONFIG_VOGUES这个开发板就怎么怎么做,我们不是这个板子不需要看
NAND有好多种,0x2,4,6,8便是的是NAND
0xc表示
后续待补充