boot中的搬运总结

=================================================================================
作者 : Etual(太感谢了,指点迷津的神人)
日期 : 2009-4-17
邮箱 :
Etual@163.com
=================================================================================
    ldr    r0, =BWSCON
    ldr    r0, [r0]
    ands    r0, r0, #6        ;OM[1:0] != 0, NOR FLash boot
    bne    copy_proc_beg        ;do not read nand flash
;===========================================================
nand_boot_beg
        ......
        ...... nand 操作部分代码

;===========================================================
copy_proc_beg
    adr    r0, ResetEntry
    ldr    r2, BaseOfROM
    cmp    r0, r2
    ldreq    r0, TopOfROM
    beq    InitRam   
    ldr r3, TopOfROM
0   
    ldmia     {r4-r7}
    stmia     {r4-r7}
    cmp    r2, r3
    bcc    %B0
   
    sub    r2, r2, r3
    sub    r0, r0, r2               
       
InitRam   
    ldr    r2, BaseOfBSS
    ldr    r3, BaseOfZero   
0
    cmp    r2, r3
    ldrcc    r1, [r0], #4
    strcc    r1, [r2], #4
    bcc    %B0   

    mov    r0,    #0
    ldr    r3,    EndOfBSS
1   
    cmp    r2,    r3
    strcc    r0, [r2], #4
    bcc    %B1
   
    ldr    pc, =%F2        ;goto compiler address
2

=================================================================================
BaseOfROM    DCD    |Image$$RO$$Base|
TopOfROM    DCD    |Image$$RO$$Limit|
BaseOfBSS    DCD    |Image$$RW$$Base|
BaseOfZero    DCD    |Image$$ZI$$Base|
EndOfBSS    DCD    |Image$$ZI$$Limit|
=================================================================================

今天主要是分析一下这部分代码复制的部分,觉得很有意思,开始的时候被一个小逻辑死角
困扰着,想了半个小时,终于想懂了,顿时豁然开朗。

1,代码首先判断启动的是nand flash 还是 nor flash,今天主要的目的是分析nor flash部分
的代码,所以很自然的,我们从 copy_proc_beg 开始分析

    adr    r0, ResetEntry
    ldr    r2, BaseOfROM
    cmp    r0, r2
    ldreq    r0, TopOfROM
    beq    InitRam   

首先,需要非常十分了解 LDR 和 ADR 的区别,可以搜索一下,有不少人写过着方面的博客
简单点说,
ADR装载的是当时运行的时候的地址(相对于指令运行时pc的啊(即当前PC)不是相对绝对地址的,即是用目前PC算出来的地址,即指令运行时所在的指令地址)
LDR装载的是连接时候的绝对地址(即ADS中定义的RO运行地址,定义的绝对地址,是相对连接的ro的啊)其实ENTRY无需设定他就是代码段的起始地址(也是个绝对地址),但是由于代码开始是烧在0X00处的,搬运时用到了ADR RN,RESETENTRY(即该条指令实际运行地址)。方便计算代码实际烧写的偏移。而JLINK仿真,那RObase设哪就从那开始执行。

ResetEntry 是一个程序开头的地址,用了ADR,所以生成的是一个偏移量(是当前指针决定
的地址,当时运行时的地址,因为boot刚开始烧在0处),也就是当时运行
的时候的地址,如果确实从从nor flash启动的话,那么这个地址就应该为 0,如果这程序
是用仿真器下载到 0x30000000 的SDRAM中运行的话,那么这个地址就应该等于 0x30000000
这个千万要理解,否则下面的都白干了。ADR的好处就是表示当时运行的地址,要是烧在ram里
从ram0起始,自然装的地址就是RAM的0地址了 在44b0中 为0x0c000000

BaseOfROM 其实就是 |Image$$RO$$Base| ,是编译器给出的运行时地址,这个地址是根据
RO BASE 的偏移量得到的,例如本例中将 RO BASE 设置为 0x30000000 ,那么这个地址就
应该等于 0x30000000

cmp    r0, r2
好了,接着代码判断刚才的两个地址是否相等。我们可以分两种情况来看
1,如果从norflash启动,那么 ResetEntry=0 BaseOfROM=0x3000000,显然不相同
2,如果用工具将这个文件加载到 0x30000000 的话,那么这个时候 ResetEntry=0
BaseOfROM=0x3000000 显然这两个地址是相同的。
这么做的效果就是:如果从nor flash启动的话,那么就将程序代码 copy 到运行时地址,也
就是 |Image$$RO$$Base| ,如果地址是相同的,那么就不需要执行 COPY 动作了。

如果要执行的copy动作的话执行的是下面的代码
    adr    r0, ResetEntry
        ldr    r2, BaseOfROM
        ldr r3, TopOfROM
0   
    ldmia     {r4-r7}
    stmia     {r4-r7}
    cmp    r2, r3
    bcc    %B0
     sub    r2, r2, r3
     sub    r0, r0, r2


R0就是当前代码的开始地址,R2是目标地址,R3-R2 就是代码的总长度,这里是包含了RO+RW的
总大小,ZI不需要纳入输出段的。

其中最后的两句是修正

     sub    r2, r2, r3
     sub    r0, r0, r2

原来是每4个字节一次赋值操作,这样很容易就产生边界问题,如果代码段的长度不是4的倍数的话

就会出问题,而且问题的关键不是复制多1-2个自己的问题,问题的是影响到下面RW段的复制操作的

所以必须修正,修正办法很简单,先算出现在超出多少,然后减回去 ......这就是2个减法语句的

作用了。

这两句结束之后,R0的数值就是指向代码段的末尾,加载段。所以接着处理RW段就OK了

    ldreq    r0, TopOfROM
    beq    InitRam   
InitRam   
    ldr    r2, BaseOfBSS
    ldr    r3, BaseOfZero   
0
    cmp    r2, r3
    ldrcc    r1, [r0], #4
    strcc    r1, [r2], #4
    bcc    %B0   

接着进行的就是重定位RW段。默认状态RW是紧跟着RO段的,但是是可以以不同的地址定位的。
首先,他不判断是否有效,一定进行定位操作,也就是说,就算是RW跟在RO后面也做一次重定位
操作。这个只是写代码的人的喜好。
下面分析一下这个COPY也有个精巧的地方,那就是,我怎么知道RW段应该从什么地方开始COPY
要COPY多少,我们从编译器得到的只是运行时地址,而不是实际加载域的地址。怎么得到加载域
的地址?这里就是我的思维进入死角的地方。
想了一下终于想通了。
诀窍就在于,这个时候RO的运行域和加载域是一样的!!!(这是针对下载到SDRAM的情况)
那么 TopOfROM 本来是运行域的 RO段的尾段!注意,这里不是RW运行域的开始段!这时的加载
域和运行域是相同的,所以这个时候 TopOfROM 其实就是 RW 的加载域!!!要注意我们的前提
是 运行域和加载域是一样的 时候才有这个结论,RW的加载域是紧跟着RO的,RW的运行域就可以
简单的从编译器传过来的参数 BaseOfBSS 里面得到,既然加载域和运行域的实际地址都知道了
那么接着的COPY动作就很简单了。
    mov    r0,    #0
    ldr    r3,    EndOfBSS
1   
    cmp    r2,    r3
    strcc    r0, [r2], #4
    bcc    %B1
接着就是将所有的ZI 段全部清零,这个很简单,因为只需要知道运行域就OK,而这个参数是直接
从编译器传递而来的。

    ldr    pc, =%F2        ;goto compiler address
2

这个操作的最后一步,就是从加载域地址(也就是当前地址)跳转到运行域地址,理解好LDR指令的
话,这里就能理解了,2 这个标号的代表的是运行域地址,这个值只绝对的,编译的时候决定好的
所以将这个地址加载到 PC的话实现的就是从 加载域 到运行域 的无缝连接。

好了,下面接着的地址就是在运行域执行了,通常就是 SDRAM中了,这种COPY技术其实在bootloader
中是很常用的。如果理解了,那么uboot 和vivi上面的工作原理,其实是一样,不过只是作者不同
代码的风格也不同,但原理是不变的。

今天参透的一些东西。恩,先写这么多。
 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值