uboot源代码startS详解.docx
《uboot源代码startS详解.docx》由会员分享,可在线阅读,更多相关《uboot源代码startS详解.docx(15页珍藏版)》请在冰豆网上搜索。
uboot源代码startS详解
以下内容源于朱友鹏老师《物联网大讲堂》视频的学习整理。
如有侵权,请告知删除。
一、基本的概念
∙阶段的定义:
1、第一阶段,即在内部SRAM运行的阶段,简单地理解为汇编阶段,此阶段主要涉及start.S文件,在cpu/s5pc11x/目录下。
第一阶段以ldrpc_start_armboot为结束。
2、第二阶段,即在DDR中运行的阶段,简单地理解为C语言阶段,此阶段主要涉及start_armboot函数,在uboot/lib_arm/board.c文件的444~908行。
∙第一阶段主要完成的任务有:
1、异常向量表的实现;
2、设置进入特权模式,即SVC模式;
3、检查恢复状态;
4、IO状态恢复;
5、关看门狗;
6、一些与SRAM、SROM相关的GPIO设置;
7、开发板的供电锁存;
8、时钟的初始化;
9、DDR的初始化;
10、重新设置栈空间;
11、uboot的重定位。
12、转换表的建立;
13、使能MMU。
可见,uboot的第一阶段初始化了SoC内部的一些部件,初始化DDR并且重定位。
一、start.S的引入
1、uboot中整个程序的入口取决于链接脚本中ENTRY声明的地方。
因为链接脚本(即board\samsung\x210\u-boot.lds)中有ENTRY(_start),因此_start符号所在的文件就是起始文件,所处的位置就是起始位置。
2、SI工具的使用:
search—>lookupreference.
二、start.S解析1
(1)config.h文件在mkconfig脚本中生成,此文件内容为#include;
(2)version.h文件中的内容是#include"version_autogenerated.h"。
version_autogenerated.h文件是在主Makefile中自动生成的。
生成代码为:
生成内容(即version_autogenerated.h文件中内容为):
#defineU_BOOT_VERSION"U-Boot1.3.4"
(3)由于定义了宏CONFIG_ENABLE_MMU,因此包含asm/proc/domain.h。
由于采用了符号链接(为了让uboot具有可移植性),实际包含include\asm-arm\proc-arm\domain.h文件。
三、start.S解析2
1、启动代码的16字节头部
(1)arm7和arm9(其他的我不清楚)的arm指令集中,一个字类型是32bit。
(2)此段代码用16个字节填充占位(这些数字貌似可以任意?
),字节内容需要后续计算重新填充。
.word是arm汇编的伪指令,含义是当前地址的值为XX,XX的内容一般为数值或者地址。
如:
.word0x2000表示当前地址的值为0x2000;
.word_start表示当前地址的值为_start函数
2、构建异常向量表
(1)异常向量表由硬件决定,软件只是参照硬件设计来实现。
(2)此向量表只是虚有其表,并未做非常细致的异常处理。
(3)复位异常处理代码是breset,reset是个函数。
(4)最后一句是让内存16字节对齐,如果不对齐,用0xdeadbeef这个数字填充。
3、
(1)_TEXT_BASE(4字节)这个内存地址出的值为TEXT_BASE(即0xc3e00000);
_TEXT_PHY_BASE(4字节)这个内存存放的值为CFG_PHY_UBOOT_BASE(uboot的物理基地址):
(2)CFG_PHY_UBOOT_BASE(定义在x210_sd.h中)为MEMORY_BASE_ADDRESS+0x3e00000,而在x210_sd.h中有#defineMEMORY_BASE_ADDRESS0x30000000,因此CFG_PHY_UBOOT_BASE为0x33e00000,这个是uboot在DDR中的物理地址。
(3)总结:
标签表示地址,.word后面的表示值。
如_bss_end:
.word_end表示地址_bss_end上存放的值是_end。
4、reset中断处理函数
(1)0xd3=11010011,参照之前cpsr的位含义,可知此代码将cpu设置为SVC模式、arm状态,禁止FIQ、IRQ中断。
(2)整个uboot工作时,cpu一直处于SVC模式(特权模式)。
5、Cpu的初始化(设置l1,l2cache和MMU等内容)
6、识别并暂存启动介质选择
(1)由SoC的OM0:
OM5这6个引脚的高低电平决定启动介质。
(2)由#definePRO_ID_BASE0xE0000000和#defineOMR_OFFSET0x04得到寄存器0xE0000004,其根据OM引脚自动硬件设置值。
(3)此三行代码后,r2存储了一个数字,后续通过该数字进行启动介质的判断。
(3)通过判断r2中的值,来确定是从哪里启动的。
如果r2中的值为0xc,那从SD卡启动,然后把#BOOT_MMCSD赋值给r3,即r3存储的值为0x3(#defineBOOT_MMCSD0x3)。
(4)最后两行中,因为有#defineINF_REG_BASE0xE010F000和#defineINF_REG3_OFFSET0x0c,因此其把BOOT_MMCSD(0x3)这个值放入寄存器0xE010F00C中。
7、第一次设置栈
、
(1)这次设置栈是在SRAM中设置的,因为当前整个代码还在SRAM中运行,此时DDR还未被初始化还不能用。
栈地址0xd0036000是自己指定的,指定的原则就是这块空间只给栈用,不会被别人占用。
(2)在调用函数前初始化栈,主要原因是接下来调用的lowlevel_init函数中还要调用其他函数。
而bl只会将返回地址存储到LR中,但是我们只有一个LR,所以在第二层调用函数前要先将LR入栈,否则函数返回时第一层的返回地址就丢了。
8、lowlevel_init函数
函数解释第一部分:
(1)lowlevel_init在uboot\board\samsung\x210\lowlevel_init.S中。
(2)检查复位状态
A、因为复杂CPU支持多种复位状态(冷上电、休眠复位等),因此在复位代码中检查复位状态,判断到底是哪一种。
B、冷上电时DDR需要初始化,而休眠状态下复位不需要再次初始化DDR。
(3)IO状态恢复;
(4)关看门狗;
(5)一些与SRAM、SROM相关的GPIO设置;
(6)开发板的供电锁存。
函数解释第二部分:
(1)判断当前代码执行位置(判断在SRAM还是DDR中)
为什么要判断?
●BL1(uboot的前一部分)在SRAM中有一份,在DDR中也有一份,因此如果是冷启动那么当前代码应该是在SRAM中运行的BL1,如果是低功耗状态的复位这时候应该就是在DDR中运行的。
●指导后面代码的运行。
譬如在lowlevel_init.S中判定当前代码的运行地址,就是为了确定要不要执行时钟初始化和初始化DDR的代码。
如果当前代码是在SRAM中,说明冷启动,那么时钟和DDR都需要初始化;如果当前代码是在DDR中,那么说明是热启动则时钟和DDR都不用再次初始化。
(2)时钟初始化:
system_clock_init
(3)内存的初始化:
system_clock_init
●该函数和裸机中初始化DDR代码是一样的。
实际裸机中初始化DDR的代码就是从这里抄的。
●配置值中有一个和裸机中讲的不一样,即DMC0_MEMCONFIG_0,它在裸机中配置值为0x20E01323,在uboot中配置为0x30F01313.这个配置不同就导致结果不同。
●在裸机中DMC0的256MB内存地址范围是0x20000000-0x2FFFFFFF,在uboot中DMC0的256MB内存地址范围为0x30000000-0x3FFFFFFF。
●之前在裸机中时配置为2开头的地址,当时并没有说可以配置为3开头。
从分析九鼎移植的uboot可以看出:
DMC0上允许的地址范围是20000000-3FFFFFFF(一共是512MB),而我们实际只接了256MB物理内存,SoC允许我们给这256MB挑选地址范围。
●总结一下:
在uboot中,可用的物理地址范围为:
0x30000000-0x4FFFFFFF。
一共512MB,其中30000000-3FFFFFFF为DMC0,40000000-4FFFFFFF为DMC1。
●分析的时候要注意条件编译的条件,配置头文件中考虑了不同时钟配置下的内存配置值,这个的主要目的是让不同时钟需求的客户都能找到合适自己的内存配置值。
(4)串口初始化:
打印一个“O”
(5)返回前通过串口打印“K”
9、第二次设置栈(已经退出lowlevel_init)
(1)在调用lowlevel_init程序前设置过1次栈,那时候是因为DDR尚未初始化,因此程序执行都是在SRAM中,所以在SRAM中分配了一部分内存作为栈。
本次因为DDR已经被初始化了,因此要把栈挪移到DDR中,所以要重新设置栈。
(2)实际设置的栈的地址是33E00000,刚好在uboot的代码段的下面紧挨着。
因为是满减栈,所以栈向下增长。
注意uboot基地址在0x33e00000,向上增长。
(3)为什么要再次设置栈?
DDR已经初始化了,已经有大片内存可以用了,没必要再把栈放在SRAM中可怜兮兮的了;原来SRAM中内存大小空间有限,栈放在那里要注意不能使用过多,否则栈会溢出,我们及时将栈迁移到DDR中也是为了尽可能避免栈使用时候的小心翼翼。
10、再次判断运行地址是在SRAM中还是DDR中
(1)上次判断是为了决定是否要执行初始化时钟和DDR的代码,本次判断是为了决定是否进行uboot的重定位。
上图中最后一行代码如果不执行,则说明需要重定位。
●冷启动时,uboot的前一部分(16kb或者8kb)开机自动从SD卡加载到SRAM中运行,uboot的第二部分(其实第二部分是整个uboot)还躺在SD卡的某个扇区开头的N个扇区中。
此时uboot的第一阶段已经即将结束了(第一阶段该做的事基本做完了),结束之前要把第二部分加载到DDR中链接地址处(0x33e00000),这个加载过程就叫重定位。
(2)如下图:
●D0037488这个内存地址在SRAM中,这个地址中的值是被硬件自动设置的。
硬件根据我们实际电路中SD卡在哪个通道中,会将这个地址中的值设置为相应的数字。
譬如我们从SD0通道启动时,这个值为EB000000;从SD2通道启动时,这个值为EB200000
●我们确定是从MMCSD启动(这里和6(4)一起看),因此最终跳转到mmcsd_boot函数中去执行重定位动作,即把SD卡中相应的内容复制到内存中。
●真正的重定位是通过调用movi_bl2_copy函数完成的,在uboot/cpu/s5pc11x/movi.c中。
此函数包含copy_bl2(2,MOVI_BL2_POS,MOVI_BL2_BLKCNT,CFG_PHY_UBOOT_BASE,0)函数;分析其参数,2表示通道2,MOVI_BL2_POS是uboot的第二部分在SD卡中的开始扇区,这个扇区数字必须和烧录uboot时烧录的位置相同;MOVI_BL2_BLKCNT是uboot的长度占用的扇区数;CFG_PHY_UBOOT_BASE是重定位时将uboot的第二部分复制到DDR中的起始地址(33E00000)。
11、最后一段代码:
(1)作用是配置MMU、重新设置栈、清bss、跳转到start_armboot函数中去执行BL2阶段。
(2)MMU(memorymanagementunit),内存管理单元。
●MMU实际上是SOC中一个硬件单元,它的主要功能就是实现虚拟地址到物理地址的映射。
●MMU单片在CP15协处理器中进行控制,也就是说要操控MMU进行虚拟地址映射,方法就是对cp15协处理器的寄存器进行编程。
(3)第三次设置栈。
●这次设置栈还是在DDR中,之前虽然已经在DDR中设置过一次栈了,但是本次设置栈的目的是将栈放在比较合适(安全,紧凑而不浪费内存)的地方。
●我们实际将栈设置在uboot起始地址上方2MB处,这样安全的栈空间是:
2MB-uboot大小-0x1000=1.8MB左右。
这个空间既没有太浪费内存,又足够安全。
(4)清理bss:
注意表示bss段的开头和结尾地址的符号是从链接脚本u-boot.lds得来的。
(5)ldrpc,_start_armboot
●start_armboot是uboot/lib_arm/board.c中,这是一个C语言实现的函数。
这个函数就是uboot的第二阶段。
这句代码的作用就是将uboot第二阶段执行的函数的地址传给pc,实际上就是使用一个远跳转直接跳转到DDR中的第二阶段开始地址处。
●远跳转的含义就是这句话加载的地址和当前运行地址无关,而和链接地址有关。
因此这个远跳转可以实现从SRAM中的第一阶段跳转到DDR中的第二阶段。
●这里这个远跳转就是uboot第一阶段和第二阶段的分界线。