U-Boot启动过程源码分析(1)-第一阶段

先综述:第一阶段文件为cpu/arm920t/start.S和board/smdk2410/lowlevel_init.S进行初始化,再跳到第二阶段的入口点lib_arm/board.c中的start_armboot函数

U-boot的定义
U-boot,全称Universal Boot Loader,它的主要功能是完成硬件设备初始化、操作系统代码搬运,并提供一个控制台及一个指令集在操作系统运行前操控硬件设备

一个嵌入式的存储设备通过通常包括四个分区:

第一分区:存放的当然是u-boot

第二个分区:存放着u-boot要传给系统内核的参数

第三个分区:是系统内核(kernel)

第四个分区:则是根文件系统
U-Boot源代码的目录结构
U-Boot启动过程源码分析(1)-第一阶段_第1张图片
U-Boot的两种模式:
(一)启动加载(Boot loading)模式: Boot Loader 从目标机上的某个固态存储设备上将操作系统加载到 RAM 中运行,整个过程并没有用户的介入。
(二)下载(Downloading)模式:在这种模式下,目标机上的 Boot Loader 将通过串口连接或网络连接等通信手段从主机(Host)下载文件。

u-boot 分为阶段 1(stage1)和阶段 2(stage2)两部分。依赖于 CPU 体系结构的代码(如 CPU 初始化代码等)通常都放在阶段 1 中且通常用汇编语言实现,而阶段 2 则通常用 C 语言来实现,有很好的可读性和移植性。

第一阶段文件为cpu/arm920t/start.S和board/smdk2410/lowlevel_init.S

结构分析
如smdk2410板子程序的入口点是在/board/smdk2410/u-boot.lds中指定的,其中ENTRY(_start)说明程序从_start开始运行,而他指向的是cpu/arm920t/start.o文件。

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
 /*OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-arm")*/
 OUTPUT_ARCH(arm)
 ENTRY(_start)
 SECTIONS
 {
 . = 0x00000000; //起始地址

. = ALIGN(4); //4字节对齐
.text : //test指代码段,上面3行标识是不占用任何空间的
{
cpu/arm920t/start.o (.text) //这里把start.o放在第一位就表示把start.s编译时放到最开始,这就是为什么把uboot烧到起始地址上它肯定运行的是start.s
 *(.text)
 }

 . = ALIGN(4); //前面的 “.” 代表当前值,是计算一个当前的值,是计算上面占用的整个空间,再加一个单元就表示它现在的位置
.rodata : { *(.rodata) }

 . = ALIGN(4);
 .data : { *(.data) }

 . = ALIGN(4);
 .got : { *(.got) }

 . = .;
 __u_boot_cmd_start = .;
 .u_boot_cmd : { *(.u_boot_cmd) }
 __u_boot_cmd_end = .;

 . = ALIGN(4);
 __bss_start = .; //bss表示归零段
.bss : { *(.bss) }
 _end = .;
 }

代码真正开始是在_start(u-boot-1.1.6\cpu\arm920t下),设置异常向量表,这样在cpu发生异常时就跳转到/cpu/arm920t/interrupts中去执行相应得中断代码。在interrupts文件中关键的是reset中断代码,跳到reset入口地址。

U-Boot启动第一阶段代码分析
cpu/arm920t/start.S
(1)上电先跳到reset处,这里b是无条件跳转。这里是中断向量表,和硬件合作,发生什么样的异常自动跳到对应异常处执行,

globl _startglobal

_start:    b     reset//b是不带返回的跳转,
   ldr   pc, _software_interrupt   /*  软件中断向量 */
   ldr   pc, _prefetch_abort  /*  预取指令异常向量 */
          .......
   /*  中断向量表入口地址 */
  _software_interrupt:  .word software_interrupt
  _prefetch_abort:  .word prefetch_abort
   ......

ldr pc, _undefined_instruction表示把_undefined_instruction存放的数值存放到pc指针上 _undefined_instruction: .word undefined_instruction表示未定义的这个异常是由.word来定义的,它表示定义一个字,一个32位的数

. word后面的数:表示把该标识的编译地址写入当前地址,标识是不占用任何指令的。把标识存放的数值copy到指针pc上面,那么标识上存放的值是什么?是由.word undefined_instruction来指定的。

(2)CPU进入SVC模式,reset在管理模式工作

   reset:

   /*  set the cpu to SVC32 mode */

   mrs r0, cpsr

   bic  r0, r0, #0x1f        /*工作模式位清零 */                     (管理模式),并将中断禁止位和快中断禁止位置1 */

   msr cpsr, r0

以上代码将CPU的工作模式位设置为管理模式,即设置相应的CPSR程序状态字,并将中断禁止位和快中断禁止位置一,从而屏蔽了IRQ和FIQ中断。
操作系统先注册一个总的中断,然后去查是由哪个中断源产生的中断,再去查用户注册的中断表,查出来后就去执行用户定义的用户中断处理函数。

(3)设置控制寄存器地址
(4)关闭看门狗
(5)屏蔽中断
(6)设置MPLLCON,UPLLCON, CLKDIVN 时钟
(7)关闭MMU,cache ——(也就是做bank的设置)

board/smdk2410/lowlevel_init.S
(8)初始化RAM控制寄存器
(9)复制U-Boot第二阶段代码到RAM
bBootFrmNORFlash函数作用是判断U-Boot是在NAND Flash启动还是NOR Flash启动,若在NOR Flash启动则返回1,否则返回0。根据ATPCS规则,函数返回值会被存放在r0寄存器中,因此调用bBootFrmNORFlash函数后根据r0的值就可以判断U-Boot在NAND Flash启动还是NOR Flash启动。bBootFrmNORFlash定义如下:

int bBootFrmNORFlash(void)
{
    volatile unsigned int *pdw = (volatile unsigned int *)0;

    unsigned int dwVal;

    dwVal = *pdw;         /* 先记录下原来的数据 */

    *pdw = 0x12345678;

    if (*pdw != 0x12345678)       /* 写入失败,说明是在NOR Flash启动 */

    {
        return 1;      
    }

    else        /* 写入成功,说明是在NAND Flash启动 */

    {
        *pdw = dwVal;        /* 恢复原来的数据 */

        return 0;
    }
}
下面来分析NOR Flash启动部分代码:

208      adr  r0, _start  /* r0 当前代码的开始地址*/
209      ldr   r1, _TEXT_BASE /* r1是代码段的连接地址*/
210      cmp      r0, r1     
/* 判断U-Boot是否已经在RAM若是,则不用再复制到RAM中了,直接跳到设置堆栈部分,这种情况通常在调试U-Boot时才发生 */

211      beq stack_setup

212  /* 、NOR Flash启动的代码 */

213      ldr   r2, _armboot_start   /*flash中armboot_start的起始地址*/


214      ldr   r3, _bss_start         /*uboot_bss的起始地址*/


215      sub  r2, r3, r2              /* r2 <- size of armboot  uboot实际程序代码的大小   */

216      add r2, r0, r2              /* r2 <- source end address         */

217  /* 搬运U-Boot自身到RAM中*/

218  copy_loop:

219      ldmia     r0!, {r3-r10} /* 从地址为[r0]的NOR Flash中读入8个字的数据 */

220      stmia      r1!, {r3-r10} /* 将r3至r10寄存器的数据复制给地址为[r1]的内存 */

221      cmp       r0, r2                    /* until source end addreee [r2]    */

222      ble  copy_loop

223      b     stack_setup         /* 跳过NAND Flash启动的代码 */

(10)设置堆栈

stack_setup:

       ldr   r0, _TEXT_BASE  /* 代码段开始地址:0x33F80000  */

       sub  r0, r0, #CONFIG_SYS_MALLOC_LEN   /* 代码段下面留出一段给malloc            */

       sub  r0, r0, #CONFIG_SYS_GBL_DATA_SIZE /*  留一段存全局参数               */

#ifdef CONFIG_USE_IRQ

       sub  r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
//IRQ、FIQ的栈
#endif

       sub  sp, r0, #12           /* 留12字节给abort异常,剩下内存就是栈    */

U-Boot启动过程源码分析(1)-第一阶段_第2张图片
(11)清除BSS段
初始值为0,无初始值的全局变量,静态变量将自动被放在BSS段。应该将这些变量的初始值赋为0,否则这些变量的初始值将是一个随机的值,若有些程序直接使用这些没有初始化的变量将引起未知的后果。

(12)跳转到第二阶段代码入口 ldr pc, _start_armboot
start_armboot函数在lib_arm/board.c中定义,是U-Boot第二阶段代码的入口

ldr   pc, _start_armboot
_start_armboot:   .word  start_armboot

你可能感兴趣的:(笔记-嵌入式-Linux韦东山)