U-Boot启动过程

U-Boot启动过程
2007-04-28 17:15

6.3.3 U-Boot启动过程

http://www.dearbook.com.cn/WebResource.aspx?id=14904

尽管有了调试跟踪手段,甚至也可以通过串口打印信息了,但是不一定能够判断出错原因。如果能够充分理解代码的启动流程,那么对准确地解决和分析问题很有帮助。

开发板上电后,执行U-Boot的第一条指令,然后顺序执行U-Boot启动函数。函数调用顺序如图6.3所示。

看一下board/smsk2410/u-boot.lds这个链接脚本,可以知道目标程序的各部分链接顺序。第一个要链接的是cpu/arm920t/start.o,那么U-Boot的入口指令一定位于这个程序中。下面详细分析一下程序跳转和函数的调用关系以及函数实现。

1cpu/arm920t/start.S

这个汇编程序是U-Boot的入口程序,开头就是复位向量的代码。

6.3 U-Boot启动代码流程图

_start: b       reset        //复位向量

       ldr   pc, _undefined_instruction

       ldr   pc, _software_interrupt

       ldr   pc, _prefetch_abort

       ldr   pc, _data_abort

       ldr   pc, _not_used

       ldr   pc, _irq    //中断向量

       ldr   pc, _fiq    //中断向量

/* the actual reset code */

reset:          //复位启动子程序

       /* 设置CPUSVC32模式 */

       mrs   r0,cpsr

       bic   r0,r0,#0x1f

       orr   r0,r0,#0xd3

       msr   cpsr,r0

/* 关闭看门狗 */

/* 这些初始化代码在系统重起的时候执行,运行时热复位从RAM中启动不执行 */

#ifdef CONFIG_INIT_CRITICAL

       bl    cpu_init_crit

#endif

relocate:                       /* U-Boot重新定位到RAM */

       adr   r0, _start          /* r0是代码的当前位置 */

       ldr   r1, _TEXT_BASE      /* 测试判断是从Flash启动,还是RAM */

       cmp     r0, r1          /* 比较r0r1,调试的时候不要执行重定位 */

       beq     stack_setup    /* 如果r0等于r1,跳过重定位代码 */

       /* 准备重新定位代码 */

       ldr   r2, _armboot_start

       ldr   r3, _bss_start

       sub   r2, r3, r2          /* r2 得到armboot的大小   */

       add   r2, r0, r2          /* r2 得到要复制代码的末尾地址 */

copy_loop: /* 重新定位代码 */

       ldmia r0!, {r3-r10}   /*从源地址[r0]复制 */

       stmia r1!, {r3-r10}   /* 复制到目的地址[r1] */

       cmp   r0, r2          /* 复制数据块直到源数据末尾地址[r2] */

       ble   copy_loop

       /* 初始化堆栈等    */

stack_setup:

       ldr   r0, _TEXT_BASE              /* 上面是128 KiB重定位的u-boot */

       sub   r0, r0, #CFG_MALLOC_LEN     /* 向下是内存分配空间 */

       sub   r0, r0, #CFG_GBL_DATA_SIZE /* 然后是bdinfo结构体地址空间 */

#ifdef CONFIG_USE_IRQ

       sub   r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)

#endif

       sub   sp, r0, #12     /* abort-stack预留3个字 */

clear_bss:

       ldr   r0, _bss_start      /* 找到bss段起始地址 */

       ldr   r1, _bss_end        /* bss段末尾地址   */

       mov r2, #0x00000000     /* 清零 */

clbss_l:str r2, [r0]        /* bss段地址空间清零循环... */

       add   r0, r0, #4

       cmp   r0, r1

       bne   clbss_l

       /* 跳转到start_armboot函数入口,_start_armboot字保存函数入口指针 */

       ldr   pc, _start_armboot

_start_armboot: .word start_armboot     //start_armboot函数在lib_arm/board.c中实现

/* 关键的初始化子程序 */

cpu_init_crit:

…… //初始化CACHE,关闭MMU等操作指令

       /* 初始化RAM时钟。

       * 因为内存时钟是依赖开发板硬件的,所以在board的相应目录下可以找到memsetup.S文件。

       */

       mov   ip, lr

       bl    memsetup        //memsetup子程序在board/smdk2410/memsetup.S中实现

       mov   lr, ip

       mov   pc, lr

2lib_arm/board.c

start_armbootU-Boot执行的第一个C语言函数,完成系统初始化工作,进入主循环,处理用户输入的命令。

void start_armboot (void)

{

       DECLARE_GLOBAL_DATA_PTR;

       ulong size;

       init_fnc_t **init_fnc_ptr;

       char *s;

       /* Pointer is writable since we allocated a register for it */

       gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));

       /* compiler optimization barrier needed for GCC >= 3.4 */

       __asm__ __volatile__("": : :"memory");

       memset ((void*)gd, 0, sizeof (gd_t));

       gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));

       memset (gd->bd, 0, sizeof (bd_t));

       monitor_flash_len = _bss_start - _armboot_start;

       /* 顺序执行init_sequence数组中的初始化函数 */

       for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {

              if ((*init_fnc_ptr)() != 0) {

                      hang ();

               }

       }

       /*配置可用的Flash */

       size = flash_init ();

       display_flash_config (size);

       /* _armboot_start u-boot.lds链接脚本中定义 */

       mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);

       /* 配置环境变量,重新定位 */

       env_relocate ();

       /* 从环境变量中获取IP地址 */

       gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");

       /* 以太网接口MAC 地址 */

       ……

       devices_init ();     /* 获取列表中的设备 */

       jumptable_init ();

       console_init_r ();    /* 完整地初始化控制台设备 */

       enable_interrupts (); /* 使能例外处理 */

       /* 通过环境变量初始化 */

       if ((s = getenv ("loadaddr")) != NULL) {

               load_addr = simple_strtoul (s, NULL, 16);

       }

       /* main_loop()总是试图自动启动,循环不断执行 */

       for (;;) {

               main_loop ();      /* 主循环函数处理执行用户命令 -- common/main.c */

       }

       /* NOTREACHED - no way out of command loop except booting */

}

3init_sequence[]

init_sequence[]数组保存着基本的初始化函数指针。这些函数名称和实现的程序文件在下列注释中。

init_fnc_t *init_sequence[] = {

       cpu_init,             /* 基本的处理器相关配置 -- cpu/arm920t/cpu.c */

       board_init,           /* 基本的板级相关配置 -- board/smdk2410/smdk2410.c */

       interrupt_init,       /* 初始化例外处理 -- cpu/arm920t/s3c24x0/interrupt.c */

       env_init,             /* 初始化环境变量 -- common/cmd_flash.c */

       init_baudrate,        /* 初始化波特率设置 -- lib_arm/board.c */

       serial_init,          /* 串口通讯设置 -- cpu/arm920t/s3c24x0/serial.c */

       console_init_f,       /* 控制台初始化阶段1 -- common/console.c */

       display_banner,       /* 打印u-boot信息 -- lib_arm/board.c */

       dram_init,            /* 配置可用的RAM -- board/smdk2410/smdk2410.c */

       display_dram_config, /* 显示RAM的配置大小 -- lib_arm/board.c */

       NULL,

};

 

你可能感兴趣的:(U-Boot启动过程)