欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-04-10 20:47:21
...

先综述:第一阶段文件为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)-第一阶段
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)-第一阶段
(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

————————————————
版权声明:本文为CSDN博主「张勇1234」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_23084801/article/details/75228959