引用 uboot启动代码之汇编代码分析(原创)

 

引用

东海 的 oot启动代码之汇编代码分析(原创)

说明u-boot版本为u-boot-1.1.3,使用的开发板是飞凌的TE2410,64M SDRAM,64M NAND Flash,10M网卡芯片cs8900a.

为了便于分析,需要对生成的elf格式文件u-boot(编译成功后会在u-boot-1.1.3目录下生成)进行反汇编,并将反汇编代码保存为文本文件.这里使用的编译器为arm-linux-gcc-2.95.3(原来使用的是自己构建的支持软件浮点数的交叉编译器,arm-linux-gcc-3.4.1不支持软件浮点数,直到前几天才发现arm-linux-gcc-2.95.3支持软件浮点数,而且编译过程很顺利,不需要对编译选项做修改).

在目录u-boot-1.1.3

#arm-linux-objdump –D u-boot > Disassembly.txt

上面的命令将u-boot反汇编,并将反汇编代码保存在文本文件Disassembly.txt,

打开这个文件,开始的内容如下:

u-boot:     file format elf32-littlearm

 

Disassembly of section .text:

 

33f80000 <_start>:

33f80000:  ea000012   b   33f80050

33f80004:  e59ff014   ldr pc, [pc, #14] ; 33f80020 <_undefined_instr tion>

33f80008:  e59ff014   ldr pc, [pc, #14] ; 33f80024 <_software_interrupt>

33f8000c:  e59ff014   ldr pc, [pc, #14] ; 33f80028 <_prefetch_abort>

33f80010:  e59ff014   ldr pc, [pc, #14] ; 33f8002c <_data_abort>

33f80014:  e59ff014   ldr pc, [pc, #14] ; 33f80030 <_not_used>

33f80018:  e59ff014   ldr pc, [pc, #14] ; 33f80034 <_irq>

33f8001c:  e59ff014   ldr pc, [pc, #14] ; 33f80038 <_fiq>

 

33f80020 <_undefined_instr tion>:

33f80020:  33f80140   mvnccs r0, #16    ; 0x10

33f80024 <_software_interrupt>:

33f80024:  33f801a0   mvnccs r0, #40    ; 0x28

33f80028 <_prefetch_abort>:

33f80028:  33f80200   mvnccs r0, #0 ; 0x0

33f8002c <_data_abort>:

33f8002c:  33f80260   mvnccs r0, #6 ; 0x6

33f80030 <_not_used>:

33f80030:  33f802c0   mvnccs r0, #12    ; 0xc

33f80034 <_irq>:

33f80034:  33f80320   mvnccs r0, #-2147483648  ; 0x80000000

33f80038 <_fiq>:

33f80038:  33f80380   mvnccs r0, #2 ; 0x2

33f8003c:  deadbeef   cdple  14, 10, cr11, cr13, cr15, {7}

33f80040 <_TEXT_BASE>:

33f80040:  33f80000   mvnccs r0, #0 ; 0x0

33f80044 <_armboot_start>:

33f80044:  33f80000   mvnccs r0, #0 ; 0x0

 

33f80048 <_bss_start>:

33f80048:  33f986f4   mvnccs r8, #255852544    ; 0xf400000

33f8004c <_bss_end>:

33f8004c:  33f9c7cc   mvnccs ip, #53477376 ; 0x3300000

33f80050 :

33f80050:  e10f0000   mrs r0, CPSR

33f80054:  e3c0001f   bic r0, r0, #31   ; 0x1f

33f80058:  e38000d3   orr r0, r0, #211  ; 0xd3

33f8005c:  e129f000   msr CPSR_fc, r0

33f80060:  e3a00453   mov r0, #1392508928   ; 0x53000000

33f80064:  e3a01000   mov r1, #0 ; 0x0

33f80068:  e5801000   str r1, [r0]

. . .

. . .

其中第一条反汇编代码如下

33f80000 <_start>:

33f80000:  ea000012   b   33f80050

对应的源代码u-boot-1.1.3\cpu\arm920t\start.S中的

.globl _start

_start:    b       reset

前面的33f80000是这条指令的地址(采用16进制表示),这里先说明一下这个地址是如何得来的,这个地址是通过连接器来设置的,arm-linux-ld有个-Ttext选项

#man arm-linux-ld

查看arm-linux-ld的用法

       --section-start sectionname=org

           Locate a section in the output file at the absolute address given by org.  You may  use  this  option  as  many

           times  as necessary to locate multiple sections in the command line.  org must be a single hexadecimal integer;

           for compatibility with other linkers, you may omit the leading 0x us lly associated with  hexadecimal  vals.

           Note: there should be no white space between sectionname,  the eq ls sign ("="), and org.

 

       -Tbss org

       -Tdata org

       -Ttext org

           Same as --section-start, with ".bss", ".data" or ".text" as the sectionname.

-Ttext的作用就是将输出文件中的段(section)定位在给定的绝对地址处.这里的段是指代码段、数据段

还有一个-T选项是用来指定连接脚本的,连接脚本主要是用来描述输入文件的段如何被映射到输出文件中,并且控制输出文件的存储分布.(The main purpose of the linker script is to describe how the sections in the input files should be mapped into the output file, and to control the memory layout of the output file)

关于连接脚本在后面还会提到

       -T scriptfile

       --script=scriptfile

           Use  scriptfile  as  the linker script.  This script replaces ld’s default linker script (rather than adding to

           it), so commandfile must specify everything necessary to describe the output file.    If  scriptfile  does  not

           exist  in  the  current  directory, "ld" looks for it in the directories specified by any preceding -L options.

           Multiple -T options accumulate.

找到u-boot-1.1.3\config.mk文件,有如下内容:

LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot.lds

LDFLAGS += -Bstatic –T $(LDSCRIPT) –Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS)

这里的–Ttext $(TEXT_BASE)指定了连接地址为TEXT_BASE变量的值,TEXT_BASE定义在u-boot-1.1.3\board\smdk2410\config.mk文件中

TEXT_BASE = 0x33f80000

这个地址是干什么的呢?bootloader最终是要在SDRAM中运行的(SDRAM的地址范围0x30000000~0x34000000),bootloader开始的4KB代码是在SRAM中运行的(using NAND flash for boot ROM,s3c2410处理器内部有4KBSRAM,处理器会将NAND flash的前4KB代码copy至这个SRAM,处理器的这个功能是由固件程序(firmware)实现的,固件程序是预先固化在处理器内部的一段代码,固件程序通过判断OM0OM1这两个引脚电平来判断是否使用NAND Flash作为booting ROM,OM0=0OM1=0时使用NAND Flash作为booting ROM,处理器内部的4KB BootSRAM被映射到nGCS0片选的空间,也就是被映射到地址0x00000000,然后将NAND flash的前4KB代码copyBootSRAM),bootloader的代码大小远超过4KB,所以在这4KB代码运行结束之前,必须将bootloader的所有代码copySDRAM,那么到底copy到哪个地址呢,这个地址就是TEXT_BASE = 0x33f80000

下面是s3c2410a用户手册中第六部分nand flash controller的一段描述

Recently,a NOR flash memory gets high in price while an SDRAM and a NAND flash memory get moderate,motivating some users to execute the boot code on a NAND flash and execute the main code on an SDRAM.

S3C2410A boot code can be executed on an external NAND flash memory.In order to support NAND flash boot loader,the S3C2410A is equipped with an internal SRAM b?r called "Steppingstone".When booting,the first 4Kbytes of the NAND flash memory will be loaded into Steppingstone and the boot code loaded into Steepingstone will be executed.

Generally.the boot code will copy NAND flash content to SDRAM.Using hardware ECC generating,the NAND flash data validity will be checked.Upon the completion of the copy,the main program will be executed on the SDRAM.

 

OM[1:0]=00b,Enable NAND flash controller auto boot mode

 

Auto boot mode seqnce

1.Reset is completed.

2.When the auto boot mode is enabled,the first 4Kbytes of NAND flash memory is copied onto Steppingstone 4KB internal b?r.

3.The Steppingstone is mapped to nGCS0.

4.CPU starts to execute the boot code on the Steppingstone 4-KB internal b?r.

–T $(LDSCRIPT)则指定了连接脚本,这个链接脚本就是u-boot-1.1.3\board\smdk2410\u-boot.lds,脚本内容如下

OUTPUT_FORMAT("elf32-littlearm","elf32-littlearm","elf32-littlearm")

OUTPUT_ARCH(arm)

ENTRY(_start)

SECTIONS

{

    . = 0x00000000;

    . = ALIGN(4);

    .text      :

    {

      cpu/arm920t/start.o (.text)

      *(.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) }

    _end = .;

}

关于这段脚本的含义可以参考《杜云海的arm学习报告系列》,百度一下即可找到.

好了,啰嗦了这么多,现在正式进入u-boot的汇编代码.

.globl _start

_start:    b       reset

    ldr pc, _undefined_instr tion

    ldr pc, _software_interrupt

    ldr pc, _prefetch_abort

    ldr pc, _data_abort

    ldr pc, _not_used

    ldr pc, _irq

    ldr pc, _fiq

 

_undefined_instr tion:  .word undefined_instr tion

_software_interrupt: .word software_interrupt

_prefetch_abort:  .word prefetch_abort

_data_abort:      .word data_abort

_not_used:    .word not_used

_irq:         .word irq

_fiq:         .word fiq

这段代码是建立中断向量表 ,没什么好说的,接着往下看

.balignl 16,0xdeadbeef      ???????????

_TEXT_BASE:

    .word  TEXT_BASE

.globl _armboot_start

_armboot_start:

    .word _start

/*

 * These are defined in the board-specific linker script.

 */

.globl _bss_start

_bss_start:

    .word __bss_start

.globl _bss_end

_bss_end:

    .word _end

上面这些代码是定义一些变量,注意这里的.word表示是32位整型变量,而不是通常所理解的16位整型变量,还记得刚才的u-boot.lds脚本文件吗,__bss_start_end就是脚本文件中的变量,他们分别是bss段的起始地址和结束地址 

#ifdef CONFIG_USE_IRQ

/* IRQ stack memory (calculated at run-time) */

.globl IRQ_STACK_START

IRQ_STACK_START:

    .word  0x0badc0de

 

/* IRQ stack memory (calculated at run-time) */

.globl FIQ_STACK_START

FIQ_STACK_START:

    .word 0x0badc0de

#endif

根据反汇编代码可以看出这段代码没有被编译,因为_bss_end变量之后就是reset,反过来推测就是宏CONFIG_USE_IRQ没有被定义.

33f80040 <_TEXT_BASE>:

33f80040:  33f80000   mvnccs r0, #0 ; 0x0

33f80044 <_armboot_start>:

33f80044:  33f80000   mvnccs r0, #0 ; 0x0

33f80048 <_bss_start>:

33f80048:  33f986f4   mvnccs r8, #255852544    ; 0xf400000

33f8004c <_bss_end>:

33f8004c:  33f9c7cc   mvnccs ip, #53477376 ; 0x3300000

33f80050 :

33f80050:  e10f0000   mrs r0, CPSR

33f80054:  e3c0001f   bic r0, r0, #31   ; 0x1f

33f80058:  e38000d3   orr r0, r0, #211  ; 0xd3

33f8005c:  e129f000   msr CPSR_fc, r0

 

--------------------------------------------------------------------------

reset:

    /*

     * set the cpu to SVC32 mode

     */

    mrs r0,cpsr/*设置处理器模式为系统模式,可以访问所有的系统资源*/

    bic r0,r0,#0x1f

    orr r0,r0,#0xd3

    msr cpsr,r0

/* turn off the watchdog */

#if defined(CONFIG_S3C2400)

# define pWTCON      0x15300000

# define INTMSK      0x14400008 /* Interupt-Controller base addresses */

# define CLKDIVN  0x14800014 /* clock divisor register */

#elif defined(CONFIG_S3C2410)

# define pWTCON      0x53000000

# define INTMSK      0x4A000008 /* Interupt-Controller base addresses */

# define INTS MSK   0x4A00001C

# define CLKDIVN  0x4C000014 /* clock divisor register */

#endif

 

#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)

    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, =INTS MSK

    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_S3C2400 || CONFIG_S3C2410 */

 

#ifndef CONFIG_SKIP_LOWLEVEL_INIT

    bl  cpu_init_crit/*系统关键的初始化代码*/

#endif

下面进入子函数cpu_init_crit

 

 

 

cpu_init_crit:

    /*

     * flush v4 I/D caches

     */

    mov r0, #0

    mcr p15, 0, r0, c7, c7, 0    /* flush v3/v4 cache */

    mcr p15, 0, r0, c8, c7, 0    /* flush v4 TLB */

    /*

     * disable MMU st? and caches

     */

    mrc p15, 0, r0, c1, c0, 0

    bic r0, r0, #0x00002300  @ clear bits 13, 9:8 (--V- --RS)

    bic r0, r0, #0x00000087  @ clear bits 7, 2:0 (B--- -CAM)

    orr r0, r0, #0x00000002  @ set bit 2 (A) Align

    orr r0, r0, #0x00001000  @ set bit 12 (I) I-Cache

    mcr p15, 0, r0, c1, c0, 0

    /*

     * before relocating, we have to setup RAM timing

     * because memory timing is board-dependend, you will

     * find a lowlevel_init.S in your board directory.

     */

    mov ip, lr

    bl  lowlevel_init

    mov lr, ip

    mov pc, lr

这里先说明一下mov  ip, lr的作用,ARM汇编子函数返回是通过mov  pc, lr来完成的,其中lr寄存器保存了调用子函数前的PC,在子函数cpu_init_crit中又调用了子函数lowlevel_init,执行

bl  lowlevel_init,先前lr寄存器保存的pc值将被覆盖,所以需要将lr寄存器的值保存起来,调用完子函数    lowlevel_init后再通过mov lr, ip恢复lr寄存器的值,这样才能够正确返回.

 

下面进入子函数lowlevel_init

    /*

     * before relocating, we have to setup RAM timing

     * because memory timing is board-dependend, you will

     * find a lowlevel_init.S in your board directory.

     */

根据这段注释,lowlevel_init函数的功能是初始化SDRAM控制器,其源代码在lowlevel_init.S,这个文件在u-boot-1.1.3\board\smdk2410目录下,源代码如下

_TEXT_BASE:

    .word  TEXT_BASE

lowlevel_init:

    /* memory control configuration */

    /* make r0 relative the current location so that it */

    /* reads SMRDATA out of FLASH rather than memory ! */

    ldr     r0, =SMRDATA

    ldr r1, _TEXT_BASE

    s r0, r0, r1

    ldr r1, =BWSCON   /* Bus Width Status Controller */

    add     r2, r0, #13*4

0:

    ldr     r3, [r0], #4

    str     r3, [r1], #4

    cmp     r2, r0

    bne     0b

    /* everything is fine now */

    mov pc, lr

 

    .ltorg

/* the literal pools origin */

 

SMRDATA:

    .word (0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+(B4_BWSCON<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+(B7_BWSCON<<28))

    .word ((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC))

    .word ((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+(B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC))

    .word ((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+(B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC))

    .word ((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+(B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC))

    .word ((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+(B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC))

    .word ((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+(B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC))

    .word ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN))

    .word ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN))

    .word ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT)

    .word 0x32

    .word 0x30

.word 0x30

上面的代码是使用一个循环来实现对SDRAM Controller的初始化,用于初始化SDRAM Controller的初始值存放在以SMRDATA为存储地址的13*4个字节中,

ldr     r0, =SMRDATA

    ldr r1, _TEXT_BASE

    s r0, r0, r1

现在来分析为什么会有s     r0, r0, r1这条指令,先看看lowlevel_init子函数对应的反汇编代码

33f80400 <_TEXT_BASE>:

33f80400:  33f80000   mvnccs r0, #0 ; 0x0

33f80404 :

33f80404:  e59f0020   ldr r0, [pc, #20] ; 33f8042c

33f80408:  e51f1010   ldr r1, [pc, #fffffff0]  ; 33f80400 <_TEXT_BASE>

33f8040c:  e0400001   s r0, r0, r1

33f80410:  e3a01312   mov r1, #1207959552   ; 0x48000000

33f80414:  e2802034   add r2, r0, #52   ; 0x34

33f80418:  e4903004   ldr r3, [r0], #4

33f8041c:  e4813004   str r3, [r1], #4

33f80420:  e1520000   cmp r2, r0

33f80424:  1afffffb   bne 33f80418

33f80428:  e1a0f00e   mov pc, lr

33f8042c:  33f80430   mvnccs r0, #805306368    ; 0x30000000

33f80430 :

33f80430:  2211d120   andcss sp, r1, #8 ; 0x8

33f80434:  00000700   andeq  r0, r0, r0, lsl #14

33f80438:  00000700   andeq  r0, r0, r0, lsl #14

33f8043c:  00000700   andeq  r0, r0, r0, lsl #14

33f80440:  00001f4c   andeq  r1, r0, ip, asr #30

33f80444:  00000700   andeq  r0, r0, r0, lsl #14

33f80448:  00000700   andeq  r0, r0, r0, lsl #14

33f8044c:  00018005   andeq  r8, r1, r5

33f80450:  00018005   andeq  r8, r1, r5

33f80454:  008e0459   addeq  r0, lr, r9, asr r4

33f80458:  00000032   andeq  r0, r0, r2, lsr r0

33f8045c:  00000030   andeq  r0, r0, r0, lsr r0

33f80460:  00000030   andeq  r0, r0, r0, lsr r0

SMRDATA地址标号对应的地址为0x33f80430,r0寄存器的值为初始化数据值的地址,也就是r0指向SDRAM控制器初始化数据区,就是c语言中的指向32位整型数据的指针变量,从这段反汇编代码来看r0的值应该为0x33f80430,前面已经说明了,这段代码是在SRAM中运行的(使用NAND Flash作为booting ROM),这段SRAM对应的地址范围是0x00000000~0x00001000,在拷贝bootloader代码至SDRAM之前,可以访问到这段初始化数据值的地址是在0x00000000~0x00001000范围内.所以此时访问地址0x33f80430,无法得到正确的数据.那么正确的访问地址是多少呢?首先不管在SRAM中运行还是在SDRAM中运行,这块用于初始化SDRAM控制器的数据区的首地址SMRDATA相对于入口地址_start的偏移量是固定的,offset=0x33f80430-0x33f80000=0x000000430,那么此时_start的值是多少呢,答案是0x00000000,前面我们说过4kBSRAM被映射到地址0x00000000,bootloader的前4kB代码被copySRAM,那么bootloader代码的第一条指令就是_start,它的地址也就是0x00000000.所以要访问以SMRDATA为首地址的数据区,正确的访问地址应该是_start+offset=0x00000000+0x00000430=0x00000430,那么你可能会问地址33f80430又是怎么回事呢,0x00000430是当前的程序在SRAM中运行时,对应的数据区首地址,0x33f80430是根据连接时指定的连接地址TEXT_BASE(0x33f80000)计算得来的,0x33f80000+offset=33f80430,一个是连接地址,一个是运行时的地址,在将bootloader拷贝至SDRAM正确地址之前,这两个地址是不一致的,

如果将bootloader代码拷贝至SDRAM0x33f80000,那么这两个地址就是一致的.

执行s     r0, r0, r1r0=0x33f80430-0x33f80000=0x000000430 

 

到此函数cpu_init_crit执行完毕.

继续往下:

 

 

#ifndef CONFIG_SKIP_RELOCATE_ OOT

relocate:            /* relocate U-Boot to RAM       */

    adr r0, _start    /* r0 <- current position of code   */

    ldr r1, _TEXT_BASE       /* test if we run from flash or RAM */

    cmp     r0, r1                  /* don't reloc during debug         */

    beq     stack_setup

 

    ldr r2, _armboot_start

    ldr r3, _bss_start

    s r2, r3, r2    /* r2 <- size of armboot            */

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

 

copy_loop:

    ldmia  r0!, {r3-r10}     /* copy from source address [r0]    */

    stmia  r1!, {r3-r10}     /* copy to   target address [r1]    */

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

    ble copy_loop

#endif /* CONFIG_SKIP_RELOCATE_ OOT */

 

    /* Set up the stack                        */

stack_setup:

    ldr r0, _TEXT_BASE       /* upper 128 KiB: relocated oot   */

    s r0, r0, #CFG_MALLOC_LEN  /* malloc area                      */

    s r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo                        */

#ifdef CONFIG_USE_IRQ

    s r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)

#endif

    s sp, r0, #12       /* leave 3 words for abort-stack    */

clear_bss:

    ldr r0, _bss_start       /* find start of bss segment        */

    ldr r1, _bss_end      /* stop here                        */

    mov    r2, #0x00000000      /* clear                            */

clbss_l:str   r2, [r0]      /* clear loop...                    */

    add r0, r0, #4

    cmp r0, r1

    ble clbss_l

这段代码实现的功能如下:先判断当前程序是否在SDRAM中运行,如果不再SDRAM中运行,那么需要拷贝bootloader代码至SDRAM,然后初始化stack和初始化bss段为0.

如何判断当前程序是否在SDRAM中运行呢,下面的三条指令实现这个功能

adr r0, _start    /* r0 <- current position of code   */

    ldr r1, _TEXT_BASE       /* test if we run from flash or RAM */

    cmp     r0, r1                  /* don't reloc during debug         */

从右边的英文注释可以看出这段代码是先获取_start标号的值,也就是当前程序运行的首地址,然后与_TEXT_BASE(0x33f80000)比较,如果相同则当前程序运行在SDRAM.这里比较难懂的就是adr  r0, _start这条语句

ADRARM伪指令,ARM中的伪指令不是真正的ARM指令或者Thumb指令,这些伪指令在汇编编译器对源程序进行汇编处理时被替换成对应的ARM或者Thumb指令.

ADR伪指令是小范围的地址读取伪指令,该指令将基于PC的地址值或基于寄存器的地址值读取到寄存器中,在汇编编译器处理源程序时,ADR伪指令被编译器替换成一条合适的指令,通常,编译器用一条ADD指令或S 指令来实现该ADR伪指令的功能.

 

我们来看看对应的反汇编代码

33f80094 <relocate>:

33f80094:  e24f009c   s r0, pc, #156  ; 0x9c

33f80098:  e51f1060   ldr r1, [pc, #ffffffa0]  ; 33f80040 <_TEXT_BASE>

33f8009c:  e1500001   cmp r0, r1

33f800a0:  0a000007   beq 33f800c4

33f800a4:  e51f2068   ldr r2, [pc, #ffffff98]  ; 33f80044 <_armboot_start>

33f800a8:  e51f3068   ldr r3, [pc, #ffffff98]  ; 33f80048 <_bss_start>

33f800ac:  e0432002   s r2, r3, r2

33f800b0:  e0802002   add r2, r0, r2

可以看出adr    r0, _start这条语句最终是用s r0, pc, #156来实现的

由于ARM采用了流水线机制,当正确读取了PC的值时,该值为当前指令地址值加8个字节,也就是说,对于ARM指令集来说,PC指向当前指令的下两条指令的地址

所以执行到33f80094:   e24f009c   s r0, pc, #156,当前的PC值为,0x33f80094+8=0x33f8009c,

r0=0x33f8009c-156(0x9c)=0x33f80000,

这样计算正确吗?答案是错误的,还是前面提到过的,0x33f800000x33f800940x33f8009c,这些地址都是链接器在链接时由指定的链接地址(TEXT_BASE)得出的,而当前指令的地址值是0x00000094(这里使用NAND Flash作为booting ROM,当前的程序运行在被映射到地址0x00000000的处理器内部4KB SRAM,_start标号对应的地址值是0x00000000,而偏移量是固定的offset=0x33f80094-0x33f80000=0x00000094,当前指令的地址为0x00000000+0x00000094=0x00000094),那么

r0=0x00000094+8–156(0x9c)=0x0000009c-156(0x9c)=0x00000000;如果将bootloader下载到SDRAM0x33f80000,然后运行,这时_start标号对应的地址值就是0x33f80000.

因为我们的程序运行在SRAM,所以计算出的r0的值为0x00000000,r1的值0x33f80000不相等,执行beq     stack_setup时结果判断不相等,所以会执行下面的代码

    ldr r2, _armboot_start

    ldr r3, _bss_start

    s r2, r3, r2    /* r2 <- size of armboot  计算出代码段的大小   */

    add r2, r0, r2    /* r2 <- source end address    计算代码段的结束地址     */

/*r0=0x00000000, r1=0x33f80000*/

copy_loop:

    ldmia  r0!, {r3-r10}     /* copy from source address [r0]    */

    stmia  r1!, {r3-r10}     /* copy to   target address [r1]    */

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

    ble copy_loop

这段代码就是将bootloadere二进制代码从NOR flash中拷贝到SDRAM0x33f80000地址开始处,注意事实上这段代码是针对NOR flash(using NOR flash for booting ROM),不适用于NAND flash(using NAND flash for booting ROM),访问NOR flash可以像访问SDRAM一样,通过地址总线访问.

 

_armboot_start _bss_start的值如下:

 

 

.globl _armboot_start

_armboot_start:

    .word _start

 

.globl _bss_start

_bss_start:

    .word __bss_start

 

最后就是跳转到stage2

    ldr pc, _start_armboot

_start_armboot:   .word start_armboot

 

 

如果想将u-boot的二进制代码下载到SDRAM中运行,对这个start.S文件的修改只需添加两个宏定义

#define CONFIG_SKIP_LOWLEVEL_INIT

#define CONFIG_SKIP_RELOCATE_ OOT

添加的位置如下:

#define CONFIG_SKIP_LOWLEVEL_INIT

#ifndef CONFIG_SKIP_LOWLEVEL_INIT

    bl  cpu_init_crit

#endif

#define CONFIG_SKIP_RELOCATE_ OOT

#ifndef CONFIG_SKIP_RELOCATE_ OOT

relocate:            /* relocate U-Boot to RAM       */

    adr r0, _start    /* r0 <- current position of code   */

    ldr r1, _TEXT_BASE       /* test if we run from flash or RAM */

    cmp     r0, r1                  /* don't reloc during debug         */

    beq     stack_setup

 

    ldr r2, _armboot_start

    ldr r3, _bss_start

    s r2, r3, r2    /* r2 <- size of armboot            */

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

 

copy_loop:

    ldmia  r0!, {r3-r10}     /* copy from source address [r0]    */

    stmia  r1!, {r3-r10}     /* copy to   target address [r1]    */

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

    ble copy_loop

#endif /* CONFIG_SKIP_RELOCATE_ OOT */

 

 

既然下载到SDRAM中运行,那么就不需要初始化SDRAM控制器了,同时也不需要重定位到SDRAM中了,添加这两个宏之后,相应的两段代码就不会被编译连接了.

 

下面的这段英文解释(来自u-boot-1.1.3\README文件)也说明了这一点

- CONFIG_SKIP_LOWLEVEL_INIT

- CONFIG_SKIP_RELOCATE_ OOT

 

       [ARM only] If these variables are defined, then

       certain low level initializations (like setting up

       the memory controller) are omitted and/or U-Boot does

       not relocate itself into RAM.

       Normally these variables MUST NOT be defined. The

       only exception is when U-Boot is loaded (to RAM) by

       some other boot loader or by a debugger which

       performs these intializations itself.

 ------------------------------------------------------------------------------------

 _armboot_start:0x33f80000

CFG_MALLOC_LEN:0x30000

size of gd_t  :0x24

size of bd_t  :0x24

address of gd :0x33f4ffdc

address of bd :0x33f4ffb8

_bss_start    :0x33f99d78

_bss_end    :0x33f9e0f4

 

U-Boot 1.1.3 (Feb 11 2009 - 16:00:46)

 

U-Boot code: 33F80000 -> 33F99D78  BSS: -> 33F9E0F4

RAM Configuration:

Bank #0: 30000000 64 MB

Flash: 512 kB

NAND:nand_dev_desc[i].ChipID == NAND_ChipID_UNKNOWN,nand = &nand_dev_desc[0]

NanD_Command (ReadID) got ec 76

nand->mfr: 0

Flash chip found:

         Man?turer ID: 0xEC, Chip ID: 0x76 (Samsung unknown 64Mb)

filename:cmd_nand.c,at line 710,functionname:NanD_IdentChip

Flash chip found:

         Man?turer ID: 0xEC, Chip ID: 0x76 (Samsung unknown 64Mb)

1 flash chips found. Total nand_chip size: 64 MB

nand->data_b:0x33f50018

64 MB

env_relocate[211] offset = 0x0

address of env_ptr:0x0

address of default_environment:0x33f99468

env_relocate[232] malloced ENV at 33f50230

ENV_NOT_EMBEDDED

gd->env_valid == 1

ret=0

total=65536

CFG_ENV_SIZE=0x10000

CFG_ENV_OFFSET=0x1f0000

address of env_ptr:0x33f50230

address of gd->env_addr:0x33f50234

XF_get_version=0, XF_malloc=8

gd->jt=0x33f60390

*(gd->jt)=0x33f8e108

gd->jt=0x33f60390

*(gd->jt)=0x33f8e10c

address of function get_version:0x33f8e10c

In:    serial

Out:   serial

Err:   serial

TE2410 #

 

你可能感兴趣的:(引用 uboot启动代码之汇编代码分析(原创))