4、uboot源码——第一阶段的start.S文件析

来源:互联网 发布:中铁采购网络交易平台 编辑:程序博客网 时间:2024/06/05 00:36

以下内容源于朱有鹏《物联网大讲堂》课程的学习整理,如有侵权,请告知删除。

 

一、相关内容

(1) 阶段的定义

  • 第一阶段,即在内部SRAM运行的阶段,简单地理解为汇编阶段,此阶段主要涉及start.S文件,在cpu/s5pc11x/目录下。第一阶段以ldr pc _start_armboot为结束。
  • 第二阶段,即在DDR中运行的阶段,简单地理解为C语言阶段,此阶段主要涉及start_armboot函数,在uboot/lib_arm/board.c文件的444~908行。

(2)总结第一阶段主要完成的任务

  • 异常向量表的实现;     
  • 设置进入特权模式,即SVC模式;
  • 检查恢复状态;             
  • IO状态恢复;
  • 关看门狗;                     
  • 一些与SRAM、SROM相关的GPIO设置;
  • 开发板的供电锁存;     
  • 时钟的初始化;
  • DDR的初始化;          
  • 重新设置栈空间;
  • uboot的重定位;      
  • 转换表的建立;
  • 使能MMU。

可见,uboot的第一阶段初始化了SoC内部的一些部件,初始化DDR并且重定位。

另外,学完整个uboot内容后,回过头来看。uboot被分割成两部分,即前8kb和整个ubooot。前8k内容肯定包括了第一阶段的操作任务,其中很重要的操作有重定位。

(3)程序入口和SI工具

  • uboot中整个程序的入口取决于链接脚本中ENTRY声明的地方。因为链接脚本(即board\samsung\x210\u-boot.lds)中有ENTRY(_start),因此_start符号所在的文件就是起始文件,所处的位置就是起始位置。
  • SI工具的使用:search—>lookup reference.


二、start.S解析1

(1)config.h文件在mkconfig脚本中生成,此文件内容为#include <configs/x210.h>;

(2)version.h文件中的内容是#include "version_autogenerated.h。

  • version_autogenerated.h文件是在主Makefile中自动生成的,相关的生成代码:

 

  • version_autogenerated.h文件中内容为:#define U_BOOT_VERSION "U-Boot 1.3.4"

(3)由于定义了宏CONFIG_ENABLE_MMU,因此包含asm/proc/domain.h,实际包含include\asm-arm\proc-arm\domain.h文件。


三、start.S解析2

1、启动代码的16字节头部


(1)arm7和arm9的arm指令集,一个字类型是32bit。

(2)此段代码用16个字节填充占位(这些数字貌似可以任意?字节内容后续计算重新填充?)。这里填充的原因是SD卡需要16字节的校验?

(3).word是arm汇编的伪指令,含义是当前地址的值为XX,XX的内容一般为数值或者地址。

  • 如:.word 0x2000表示当前地址的值为0x2000;.word _start 表示当前地址的值为_start。


2、构建异常向量表

 

(1)异常向量表由硬件决定,软件只是参照硬件设计来实现。

(2)此向量表只是虚有其表,并未做非常细致的异常处理。

(3)复位异常处理代码是b reset,reset是个函数。

(4)上图中的最后一句是让内存16字节对齐,如果不对齐,用0xdeadbeef这个数字填充。

(5)学完裸机中的中断向量表可知,这里类似的处理。

 

3、一些地址值

 

(1)_TEXT_BASE(4字节)这个内存地址出的值为TEXT_BASE(即0xc3e00000)。

(2)_TEXT_PHY_BASE(4字节)这个内存存放的值为CFG_PHY_UBOOT_BASE(uboot的物理基地址)。

(3)CFG_PHY_UBOOT_BASE(定义在x210_sd.h中)为 MEMORY_BASE_ADDRESS + 0x3e00000

  • 在x210_sd.h中有#define MEMORY_BASE_ADDRESS 0x30000000;
  • 因此CFG_PHY_UBOOT_BASE为0x33e00000,这个是uboot在DDR中的物理地址。

(4)总结,即标签表示地址,.word后面的表示此内存地址中存储的值。

  • 如_bss_end: .word _end表示地址_bss_end上存放的值是_end。


四、start.S解析3 

1、reset中断处理函数

 

(1)由博客http://blog.csdn.net/oqqhutu12345678/article/details/71215628可知,因为0xd3=1101 0011,则cpu设置为SVC模式、arm状态,禁止FIQ、IRQ中断。

(2)整个uboot工作时,cpu一直处于SVC模式。

 

2、Cpu的初始化(设置l1,l2cache和MMU等内容)



3、识别并暂存启动介质选择

(1)启动介质由SoC的OM0:OM5这6个引脚的高低电平决定 

(2)由#define PRO_ID_BASE 0xE0000000和#define OMR_OFFSET 0x04得到寄存器0xE000 0004

  • 这个寄存器会根据OM引脚状况,硬件自动设置值。

(3)此三行代码后,r2存储了一个数字,后续通过该数字进行启动介质的判断。

  

(4)通过判断r2中的值,来确定是从哪里启动的。

  • 如果r2中的值为0xc,那从SD卡启动,然后把#BOOT_MMCSD(#define  BOOT_MMCSD  0x3)赋给r3。

(5)最后两行中,因为有#define INF_REG_BASE 0xE010F000和#define INF_REG3_OFFSET 0x0c,则合成寄存器0xE010F00C,然后把r3中的值放入其中。

 

4、第一次设置栈


(1)这次设置栈是在SRAM中设置的,因为当前整个代码还在SRAM中运行,此时DDR还未被初始化还不能用。

  • 栈地址0xd0036000是自己指定的,指定的原则就是这块空间只给栈用,不会被别人占用。

(2)之所以要初始化栈,是因为接下来调用的lowlevel_init函数中还要调用其他函数。

  • bl只会将返回地址存储到LR中,但是只有一个LR,所以在第二层调用函数前要先将LR入栈,否则函数返回时第一层的返回地址就丢了。


---------------------------------------lowlevel_init函数--------------------------------------------

五、start.S解析4 

1、lowlevel_init函数

(1)lowlevel_init在uboot\board\samsung\x210\lowlevel_init.S中。

(2)检查复位状态

  • 因为复杂CPU支持多种复位状态(冷上电、休眠复位等),因此在复位代码中检查复位状态,判断到底是哪一种。
  • 冷上电时DDR需要初始化,而休眠状态下复位不需要再次初始化DDR。

(3)IO状态恢复;

(4)关看门狗;

(5)一些与SRAM、SROM相关的GPIO设置(下图);

 

(6)开发板的供电锁存(下图)

 


六、start.S解析5 

1、判断当前代码执行位置(判断在SRAM还是DDR中)


为什么要判断?

  • BL1在SRAM中有一份,在DDR中也有一份。
  • 如果是冷启动,则当前代码是在SRAM中运行的BL1;如果是低功耗状态的复位,则当前代码是在DDR中运行的。
  • 判断的目的是指导后面代码的运行。
  • 譬如在lowlevel_init.S中判定当前代码的运行地址,就是为了确定是否执行时钟初始化和初始化DDR的代码。
  • 如果当前代码是在SRAM中,说明是冷启动,则需要初始化时钟和DDR;
  • 如果当前代码是在DDR中,说明是热启动,则时钟和DDR都不用再次初始化。

2、时钟初始化:system_clock_init

  • 详细可以查阅代码http://blog.csdn.net/oqqhutu12345678/article/details/73188932

3、内存的初始化:mem_ctrl_asm_init

  • 代码见博客http://blog.csdn.net/oqqhutu12345678/article/details/73189059

(1)在uboot/cpu/s5pc11x/s5pc110中。该函数和裸机中初始化DDR代码是一样的。实际裸机中初始化DDR的代码就是从这里抄的。

(2)配置值中有一个和裸机中讲的不一样,即DMC0_MEMCONFIG_0

  • 它在裸机中配置值为0x20E01323,在uboot中配置为0x30F01313。
  • 在裸机中DMC0的256 MB内存地址范围是0x20000000-0x2FFFFFFF;
  • 在uboot中DMC0的256MB内存地址范围为0x30000000-0x3FFFFFFF
  • DMC0上允许的地址范围是20000000-3FFFFFFF(一共是512MB),而实际只接了256MB物理内存,SoC允许我们给这256MB挑选地址范围。
  • 在uboot中,可用的物理地址范围为:0x30000000-0x4FFFFFFF,一共512MB,其中30000000-3FFFFFFF为DMC0,40000000-4FFFFFFF为DMC1。
  • 注意条件编译的条件,配置头文件中考虑了不同时钟配置下的内存配置值,主要目的是让不同时钟需求的客户都能找到合适自己的内存配置值。

4、串口初始化:打印一个“O”

  • uart_asm_init函数


5、pop {pc}返回前通过串口打印“K”

 

----------------------------------lowlevel_init函数结束----------------------------------------


七、start.S详解6

1、第二次设置栈

 

(1)第一次设置栈,是在调用lowlevel_init函数前。那时程序在SRAM中执行,所以在SRAM中分配了一部分内存作为栈。

(2)第二次因为DDR已经被初始化,因此要把栈挪移到DDR中,所以要重新设置栈。

  • 实际设置的栈的地址是33E00000,刚好在uboot的代码段的下面紧挨着。
  • 因为是满减栈,所以栈向下增长。注意uboot基地址在0x33e00000,向上增长。

(3)为什么要再次设置栈?

  • DDR已经初始化了,已经有大片内存可以用了,没必要再把栈放在SRAM中;
  • 原来SRAM中内存大小空间有限,栈放在那里要注意不能使用过多,否则栈会溢出;
  • 我们及时将栈迁移到DDR中也是为了尽可能避免使用栈时的诸多不便。

 

2、再次判断运行地址是在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启动,因此最终跳转到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)。

 

 

八、start.S详解7

最后一段代码,作用是配置MMU、重新设置栈、清bss、跳转到start_armboot函数中去执行BL2阶段。




1、MMU(memory management unit),内存管理单元。

  • MMU实际上是SOC中一个硬件单元,它的主要功能就是实现虚拟地址到物理地址的映射。
  • MMU单片在CP15协处理器中进行控制,也就是说要操控MMU进行虚拟地址映射,方法就是对cp15协处理器的寄存器进行编程。

2、第三次设置栈

  • 这次设置栈还是在DDR中,之前虽然已经在DDR中设置过一次栈了,但是本次设置栈的目的是将栈放在比较合适(安全,紧凑而不浪费内存)的地方。
  • 我们实际将栈设置在uboot起始地址上方2MB处,这样安全的栈空间是:2MB-uboot大小-0x1000=1.8MB左右。这个空间既没有太浪费内存,又足够安全。

3、清理bss

  • 注意表示bss段的开头和结尾地址的符号是从链接脚本u-boot.lds得来的。

4、ldr pc, _start_armboot

  • start_armboot函数在uboot/lib_arm/board.c中,这是一个C语言实现的函数。这个函数就是uboot的第二阶段。
  • 这句代码的作用就是将uboot第二阶段执行的函数的地址传给pc,实际上就是使用一个远跳转直接跳转到DDR中的第二阶段开始地址处。
  • 远跳转的含义就是这句话加载的地址和当前运行地址无关,而和链接地址有关。因此这个远跳转可以实现从SRAM中的第一阶段跳转到DDR中的第二阶段。
  • 这里这个远跳转就是uboot第一阶段和第二阶段的分界线。



1 0
原创粉丝点击