u-boot-2014.10移植第7天----深入分析代码(二)

硬件平台:tq2440

开发环境:Ubuntu-3.11

u-boot版本:2014.10

本文允许转载,请注明出处:http://blog.csdn.net/fulinus


现在从代码的角度来分析启动的流程:

从u-boot.lds文件知:

ENTRY(_start)
SECTIONS
{
    . = 0x00000000;

    . = ALIGN(4);
    .text :
    {
        *(.__image_copy_start)
        *(.vectors)
        CPUDIR/start.o (.text*)
        *(.text*)
    }
文件中__image_copy_start位映像文件复制起始地址,它在文件arch/arm/lib/sections.c中如下定义:

char __image_copy_start[0] __attribute__((section(".__image_copy_start")));
char __image_copy_end[0] __attribute__((section(".__image_copy_end")));
这个__image_copy_start在文件common/board_r.c中被调用,

static int initr_reloc_global_data(void)
{
#ifdef __ARM__
    monitor_flash_len = _end - __image_copy_start;

文件中.vectors(英文矢量)在arch/arm/lib/vectors.S的文件中定义:

.section ".vectors", "x"

.section伪操作
用户可以通过.section伪操作来自定义一个段,格式如下:
 .section section_name [, "flags"[, %type[,flag_specific_arguments]]]
每一个段以段名为开始, 以下一个段名或者文件结尾为结束。这些段都有缺省的标志(flags),连接器可以识别这些标志。(与armasm中的AREA相同)。

下面是ELF格式允许的段标志
<标志> 含义
a 允许段
w 可写段
x 执行段

参考:http://blog.sina.com.cn/s/blog_59b189220100au1k.html

所以x表示可执行段

该段下面就是可执行代码的起始位置:

_start:

通过下面的语句跳转到reset的入口:

b   reset
b汇编指令见:http://blog.chinaunix.net/uid-20799298-id-99633.html

先不跳入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

ldr汇编指令见:http://blog.chinaunix.net/uid-28458801-id-4084264.html

举其中一个存储器地址“_undefined_instruction”为例,它定义如下:

    .globl  _undefined_instruction
    .globl  _software_interrupt
    .globl  _prefetch_abort
    .globl  _data_abort
    .globl  _not_used
    .globl  _irq
    .globl  _fiq

_undefined_instruction: .word undefined_instruction
undefined_instruction定义如下:

undefined_instruction:
    get_bad_stack
    bad_save_user_regs
    bl  do_undefined_instruction

获取坏掉的栈,保存用户寄存器,有打印信息供开发人员调试,最后就是在死循环了。

下面我们再来看看reset代码段,reset定义在:arch/arm/cpu/arm920t/start.S文件中,reset(英文复位),系统复位时也会从这里开始,处理硬件上的复位,软件复位命令时通过看门狗操作实现。reset开始的代码段实现切换到超级用户模式(SVC 模式),接着是异常重定位异常表:

#if defined(CONFIG_AT91RM9200DK) || defined(CONFIG_AT91RM9200EK)
    /*
     * relocate exception table
     */
    ldr r0, =_start
    ldr r1, =0x0
    mov r2, #16
copyex:
    subs    r2, r2, #1
    ldr r3, [r0], #4
    str r3, [r1], #4
    bne copyex
#endif

CONFIG_AT91RM9200EK宏有定义,

LDR   r0,=label  
如果label是立即数,就把数值赋给r0, 如果lable是标识符,就把label地址的值赋给r0

r0保存了_start表示的地址,r1保存0,r2保存16,r2 = r2-1, subs中的s可以影响CPSR中的条件标志位,将r0地址处的内容读到r3中,再r0 = r0+4,str这条指令将R3中的字数据写入以R1为地址的存储器中,并将新地址R1+4写入R1,bne指令根据CPSR中的条件标志位判断是否跳转,如果subs指令中r2初值为16减为0时即影响了条件标志位,没有影响就跳转到copyex。所以异常重定位就是将__start开始的16个字拷贝到0地址开始的位置中去。

异常重定位结束后就是我们常说的关看门狗、屏蔽中断、设置系统时钟,

# if defined(CONFIG_S3C2400)
#  define pWTCON    0x15300000
#  define INTMSK    0x14400008  /* Interrupt-Controller base addresses */
#  define CLKDIVN   0x14800014  /* clock divisor register */
#else
#  define pWTCON    0x53000000
#  define INTMSK    0x4A000008  /* Interrupt-Controller base addresses */
#  define INTSUBMSK 0x4A00001C
#  define CLKDIVN   0x4C000014  /* clock divisor register */
# endif

    ldr r0, =pWTCON
    mov r1, #0x0
    str r1, [r0]

    /*
     * mask all IRQs by setting all bits in the INTMR - default
     */
    mov r1, #0xffffffff
    ldr r0, =INTMSK
    str r1, [r0]
# if defined(CONFIG_S3C2410)
    ldr r1, =0x3ff
    ldr r0, =INTSUBMSK
    str r1, [r0]
# endif

    /* FCLK:HCLK:PCLK = 1:2:4 */
    /* default FCLK is 120 MHz ! */
    ldr r0, =CLKDIVN
    mov r1, #3
    str r1, [r0]
#endif  /* CONFIG_S3C24X0 */

下面是:

    /*
     * we do sys-critical inits only at reboot,
     * not when booting from ram!
     */
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
    bl  cpu_init_crit
#endif
从它的注释我们看到只在重启就进行系统临界初始化,我们计入cpu_init_curit查看。

明天再看。






你可能感兴趣的:(bootloader)