打印

NAND boot load 的疑问

[复制链接]
楼主: itelectron
手机看帖
扫描二维码
随时随地手机跟帖
21
itelectron|  楼主 | 2009-11-1 17:21 | 只看该作者 回帖奖励 |倒序浏览
本帖最后由 itelectron 于 2009-11-1 17:22 编辑

吃饭去  !

使用特权

评论回复
22
juventus9554| | 2009-11-1 17:44 | 只看该作者
?ls什么意思?

使用特权

评论回复
23
itelectron|  楼主 | 2009-11-1 20:13 | 只看该作者
ldr   pc, _start_armboot因该是 跳转到stage2 \

  start_armboot()在lib_arm/board.c中定义,它类似于Linux内核的start_kernel(),它们都是一种系统初始化的接口函数:在内核的start_kernel()中集中完成了内核几乎所有资源的初始化,包括CPU相关的资源和外设接口等;而在 start_armboot()中也要完成一些初始化工作

使用特权

评论回复
24
itelectron|  楼主 | 2009-11-1 20:24 | 只看该作者
/* main_loop() can return to retry autoboot, if so just run it again. */
        for (;;) {
                main_loop ();
        }

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

使用特权

评论回复
25
itelectron|  楼主 | 2009-11-1 20:49 | 只看该作者
google的

(5)跳转到第二阶段代码的C入口点。

在跳转之前,还要清除BSS段(初始值为0、无初始值的全局变量、静态变量放在BSS段),代码如下:

192 clear_bss:

193 ldrr0, _bss_start/* BSS段的开始地址,它的值在连接脚本u-boot.lds中确定 */

194 ldrr1, _bss_end/* BSS段的结束地址,它的值在连接脚本u-boot.lds中确定 */

195 mov r2, #0x00000000

196

197 clbss_l:strr2, [r0]/* 往BSS段中写入0值 */

198 addr0, r0, #4

199 cmpr0, r1

200 bleclbss_l

201

现在,C函数的运行环境已经完全准备好,通过如下命令直接跳转(这之后,程序才在内存中执行),它将调用lib_arm/board.c中的start_armboot函数,这是第二阶段的入口点:

223 ldrpc, _start_armboot

224

225 _start_armboot:.word start_armboot

226

使用特权

评论回复
26
itelectron|  楼主 | 2009-11-1 20:53 | 只看该作者
我 的疑问是  ldrpc, _start_armboot
这句是咋跳的  是 编译的时候 分配的地址么?
和这句 有关??_start_armboot:.word start_armboot

使用特权

评论回复
27
itelectron|  楼主 | 2009-11-1 20:55 | 只看该作者
呵呵 继续GOOGLE

ldr pc, _start_armboot

        _start_armboot: .word start_armboot

此条语句后,并没有跳转到SDRAM中的void start_armboot (void),而是跳转到了FLASH中的void start_armboot (void)中。

所以就出现了这样的矛盾,在FLASH中有一段代码把自己拷贝到SDRAM中,产生了两份UBOOT可执行的指令流,但是最后却没有跳转到SDRAM中去运行以提高指令执行的速度。

产生以上的认识是基于以下几个认识(肯定是错误的):

1.*.text中的所有地址标号(在链接时确定)是从0地址开始生成的。

      实际上在arm-linux-ld 执行时,原来定义的0x0地址被更新为TEXT_BASE定义的地址。

2.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
   sub r2, r3, r2  /* r2 <- size of armboot            */
   add r2, r0, r2  /* r2 <- source end address         */

如果不是出于调试阶段,这段搬移代码中的r0和r1肯定不相等的,r0=#0,r1=#TEXT_BASE: 0x33F80000(在./board/smdk2410/config.mk中),所以执行代码的自身拷贝与搬移。

注意:在GNU中:adr r0, _start 作用是获得 _start 的实际运行所在的地址值,而ldr r1, _TEXT_BASE 为获得地址_TEXT_BASE中所存放的数据,其中adr r0, _start翻译成 add r0,(PC+#offset),offset 就是 adr r0, _start 指令到_start 的偏移量,在链接时确定,这个偏移量是地址无关的。而 ldr r1, _TEXT_BASE 指令表示以程序相对偏移的方式加载数据,是索引偏移加载的另外一种形式,等同于ldr r1,[PC+#offset],offset 是 ldr r1, _TEXT_BASE 到 _TEXT_BASE 的偏移量。注意这种用法并不是伪指令,伪指令的特征是 ldr r1, =expr/lable_expr。对于LDR伪指令,ADS的情况有些不一样(细微差别),在ADS中的情况可以参考杜春雷<ARM体系结构与编程>144页。


比较一下:

add r0,(PC+#offset):(PC+#offset)是相对地址,表示把本指令上溯或下溯offset处的地址加载到 r0;

ldr r1,[PC+#offset]:[PC+#offset]也是相对地址,表示把偏移offset处的地址上的数据加载到 r1;

现在继续:

    刚才分析所得到的矛盾,肯定是在认识上存在的偏差,经过把U-BOOT进行make后,从所生成的两个.map文件来看(~/u-boot.map和Systen.map),所有的地址标号都是从0x33f80000开始的,就是从SDRAM的高地址开始,等于TEXT_BASE的值,也就是说,链接器是从0x33F80000开始来链接所编译生成的目标文件的,而不是从0地址开始,经过查看,start_armboot=0x33f80d9c,就是说void start_armboot (void)函数的入口地址在SDRAM中(链接器决定),所以执行

        ldr pc, _start_armboot

        _start_armboot: .word start_armboot,

PC指针肯定就指向了SDRAM中,换句话就是说进入到SDRAM中了,对于ldr pc, _start_armboot,其仍然是GNU中使用程序相对偏移的方式加载数据,翻译一下就是ldr pc, [pc+pc到_start_armboot的偏移值],结果就把_start_armboot地址中的数start_armboot放入pc中完成了跳转,而 start_armboot 的值(函数地址)是在链接时就确定了,是相对于 TEXT_BASE 的。因为在整个UBOOT的阶段1中所有的寻址都是相对位置的寻址(虽然链接器认为是阶段1的代码是从地址0x3ff80000中开始链接的),把阶段1的代码放在0地址开始的FLASH中也是可以正确的运行的,如果ARM的复位向量是在0x00000001(假设),那么把代码烧写到从0x00000004处开始的地方,上电时也可以正确的运行(假设ARM的复位向量是在0x00000004成立),当然ARM的复位向量不在这里,只是以此假设来说明以上的对于阶段1的分析。

    现在最后一个矛盾就是链接脚本(~/board/smdk2410/u-boot.lds)所描述的链接地址与实际的链接地址不相同的问题,因为根据链接脚本,所有的地址标号应该从0地址开始计数的,然而不是。经过查找Makefile文件,在顶层的Makefile文件中,在166行中链接是的链接命令:

$(LD) $(LDFLAGS) $$UNDEF_SYM $(OBJS) \,

其中的LDFLAGS在定义在顶层的config.mk中的145行:LDFLAGS += -Bstatic -T $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS),

最关键的就是 -Ttext $(TEXT_BASE)命令了,他的含义就是说,起始地址在TEXT_BASE,而TEXT_BASE在~/board/smdk2410/config.mk中TEXT_BASE = 0x3FF80000;

到此就弄清楚为什么链接从0x3ff80000开始的了,至于链接脚本,其主要作用是用来指明各个*.o文件的顺序,如入口地址标号(_start)等,以及使两个地址标号得到当前的地址

    __u_boot_cmd_start = .;    *.u_boot_cmd段的起始地址

    .u_boot_cmd : { *(.u_boot_cmd) }
    __u_boot_cmd_end = .;       *.u_boot_cmd段的结束地址

以供C程序使用。 __u_boot_cmd_start和__u_boot_cmd_end可以作为全局的一个常数使用。



总结:

    因为-Ttext $(TEXT_BASE)命令的使用,链接器把UBOOT从地址0x3ff80000开始连接,在第一阶段中,所有使用的目标地址寻址都是使用当前PC值加减偏移量的方法,所以把UBOOT烧写到0地址开始的FLASH中,不影响第一阶段的正确执行。

    文字功底有限,且由于本人不是计算机专业的,难免有许多错误、偏差和遗漏,望各位大虾斧正。

**出处:DIY部落(http://www.diybl.com/course/6_sy ... 2008813/135822.html)

使用特权

评论回复
28
itelectron|  楼主 | 2009-11-1 21:02 | 只看该作者
NND  对UBOOT是明白了
但是在KEIL MDK下该 如何做到 和UBOOT 那样复制 和运行呢????又郁闷了:L

使用特权

评论回复
29
itelectron|  楼主 | 2009-11-1 21:04 | 只看该作者
GOOGLE

BOOT阅读笔记



做了近两年ARM下的驱动开发,常用的各个设备驱动基本都碰过,不过Boot由于任务安排的缘故(公司一直有专人在做),一直没有机会接触,从刚开始接触嵌入式的时候,就一直想弄清楚板子上电后,程序是怎么执行的,不过看了下公司boot源码,就很快放弃了,当时对汇编充满了畏惧,做了1年多的驱动后,再看汇编感觉就没那边痛苦了,最近把boot的资料整理下,把我觉得boot比较核心的部分,完整的看了一遍,现在做个记号.我把我觉得我之前比较困惑的难点整理出来,也许大家一起讨论下,也许和我一样的新手就可以少走些弯路

使用特权

评论回复
30
itelectron|  楼主 | 2009-11-1 21:04 | 只看该作者
31
LZL_83| | 2009-11-1 21:19 | 只看该作者
拜读,学习中。

使用特权

评论回复
32
itelectron|  楼主 | 2009-11-1 21:38 | 只看该作者
33
itelectron|  楼主 | 2009-11-1 21:42 | 只看该作者
跑步去明天继续

使用特权

评论回复
34
mrbbsp| | 2009-11-2 10:29 | 只看该作者
学习ing。mark

使用特权

评论回复
35
itelectron|  楼主 | 2009-11-4 23:26 | 只看该作者
Realview MDK链接程序使用了两种方式控制程序的链接,即链接控制命令选项和链接脚本文件。当使用链接控制命令选项时,链接器定义了
Image$$RW$$Base、
Image$$RW$$Limit、
Image$$RO$$Base、
Image$$RO$$Limit、
Image$$ZI$$Base和
Image$$ZI$$Limit等6个段地址描述符。
这6个描述符可以直接在程序中引用。而在使用链接脚本文件后,这6个描述符号没有了,取而代之的是链接脚本文件中的段描述符,格式为:Image$$段名$$Base 和Image$$段名$$Limit。下面将结合3个具体的例子说明链接脚本文件的使用。

使用特权

评论回复
36
itelectron|  楼主 | 2009-11-5 00:10 | 只看该作者
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
        . = 0x00000000;

        . = ALIGN(4);
        .text      :
        {
          cpu/arm920t/start.o        (.text)
          board/100ask24x0/boot_init.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 = .;
}

使用特权

评论回复
37
itelectron|  楼主 | 2009-11-5 00:19 | 只看该作者
本帖最后由 itelectron 于 2009-11-5 00:21 编辑

u-boot代码链接的问题  


环境和配置:u-boot-1.1.2, arm-linux-gcc(v3.2), redhat linux9.0, cpu(s3c44b0), board(B2)
在/board/dave/B2/u-boot.lds有
. = 0x00000000;
. = ALIGN(4);
.text :
{
cpu/s3c44b0/start.o (.text)
*(.text)
}
而/board/dave/B2/config.mk中有
TEXT_BASE = 0x0C100000
最后编译出来的代码反汇编得到
0c100000 <_start>:
c100000: ea00000a b c100030 <reset>
c100004: e28ff303 add pc, pc, #201326592 ; 0xc000000
c100008: e28ff303 add pc, pc, #201326592 ; 0xc000000
c10000c: e28ff303 add pc, pc, #201326592 ; 0xc000000
c100010: e28ff303 add pc, pc, #201326592 ; 0xc000000
c100014: e28ff303 add pc, pc, #201326592 ; 0xc000000
c100018: e28ff303 add pc, pc, #201326592 ; 0xc000000
c10001c: e28ff303 add pc, pc, #201326592 ; 0xc000000

0c100020 <_TEXT_BASE>:
c100020: 0c100000 ldceq 0, cr0, [r0]
0c100024 <_armboot_start>:
c100024: 0c100000 ldceq 0, cr0, [r0]
0c100028 <_bss_start>:
c100028: 0c115694 ldceq 6, cr5, [r1], -#592
0c10002c <_bss_end>:
c10002c: 0c1198a4 ldceq 8, cr9, [r1], -#656
0c100030 <reset>:
c100030: e10f0000 mrs r0, CPSR
链接得到的起始地址为什么是TEXT_BASE,而不是0呢,所以现在只能够下载到ram中运行,但是无法烧写道flash中跑,这是怎么回事呢?u-boot中应该是start.S中的这段代码在flash中运行吧,后面就把自身拷贝到ram中TEXT_BASE地址处,为什么在链接文件中指定的_start的起始地址为0x00000000呢?
blob中把整个代码分为两部分,所以有两个连接文件,前面1k在flash中运行,链接起始地址为0x0,ram中运行的,然后通过工具把两部分组合到一起,但是u-boot是怎么做的呢?
迷惑中,请帮忙解答,谢先!


  Re: u-boot代码链接的问题  


好象与这个地址没关系的
u-boot既可以在SDRAM中,
也可在Flash中运行


  Re: u-boot代码链接的问题  


兄弟,首先需要知道CPU的启动方式,一般来说有BOOTROM,SPI,FLASH这三种方式;一般来说也不会存在从SDRAM/DRAM上启动机器,因为它们不可能作为永久存储。
然后就是需要知道,外设地址映射关系,对于CPU来说总是有统一的IO地址映射,并且在不同的访问模式下,地址映射关系不一定相同。
还有,代码可以运行在不同的介质上,如:上述三种再包括RAM/ROM。
另外:还要知道代码段的链接地址,这个你应该是知道的。
最后,就可以考虑你的FLASH启动问题了。在Uboot里面的-text的参数,应该是真对某个硬件系统的配置,这个就是FLASH的高端地址或者低端地址。
一般来说,bootloader的代码的第一段总是运行在永久介质上,例如:FLASH,然后才将代码搬移到DRAM中,这个时候在dram中执行第二段BOOTLOADER的代码段,也就是你说下载到内存中的说法,实际上他都已经运行了一个阶段了。在内存中的运行入口地址,按照系统情况可以自行安排了。

因此,这个最开始的FLASH地址,肯定不是0,是什么看看你的单板的DATASHEET和编程参考


  Re: u-boot代码链接的问题  


谢谢,我的板子cpu是s3c44b0,不支持memory remap,flash为4M,从0x00000000到0x00400000,
sdram为8M,地址从0x0c000000到0x0c800000,我不清楚的问题时链接脚本中指定的_start的入口地址为0x00000000,为何编译出来的代码。链接地址是从0x0c100000开始的,即把TEXT_BASE作为_start的入口地址了,如果我把TEXT_BASE改为0x00000000,那么后面的rellocate代码还有意义吗?


  Re: u-boot代码链接的问题  


我猜测,问题可能有可能,我说的仅仅是may be:
和我前面说的一样,boot loader一般有两段代码段(独立的),在连接的时候也是分别连接,故此有两个代码连接文件,和编译过程共产生的两个MAP文件,可以查一下你看的连接文件和编译文件是不是对应的。
另外有两个建议:
1:这个也是疑惑,我看了你的问题后,作了这样的前提假设(个人的理解):在dram里的loader stage起始地址是定义在sdram的最低端。从这个假设出来,我觉得这个链接也是有问题的,因为一般来说sdram的最低端是做向量表、和模式栈顶区、参数区来用的,这个肯定有问题
2:分析这个问题,首先要确定你的编译过程,可以把编译过程定向到一个文件中仔细的分析,可以看到编译链接各个细节。不妨发给我一份hls780204cn@vip.sina.com


  Re: u-boot代码链接的问题  


TEXTADDR 是内核的虚拟起始地址,并且在arch/<target>/ 下的Makefile 中指定它的值。这个地址必须与引导装载程序使用的地址相匹配。 一旦引导装载程序将内核复制到闪存或DRAM 中,内核就被重新定位到TEXTADDR — 它通常在DRAM 中,然后,引导装载程序将控制转给这个地址,以便内核能开始执行。


  Re: u-boot代码链接的问题  


我已经理解了:
“链接得到的起始地址为什么是TEXT_BASE,而不是0呢,”
因为u_boot如果从flash运行的话,那么它会将自己的代码拷贝到RAM中,然后运行。u-boot开始部分代码与编译的入口没有关系,而主要的代码是在RAM中运行,因此编译的入口地址是TEXT_BASE.因此u-boot既可以flash运行,也可以ram运行。
“为什么在链接文件中指定的_start的起始地址为0x00000000呢?”
lds文件中的起始地址为0x00000000是不起作用的,由-TTEXT_BASE参数替代的。
刚开始比较疑惑的原因是对:
126 relocate: /* relocate U-Boot to RAM */
127 adr r0, _start /* r0 <- current position of code */
adr这条指令没有理解正确,因为把它想成mv r0,_start了,实际上adr这里的_start是相对的,如果从flash运行的话,r0就是0, 如果从ram运行的话,r0就是C100000。
我现在可以运行u-boot了,串口可以显示内容并且可以使用命令。但网卡驱动和flash驱动还有问题。慢慢搞就可以搞定,因为可以用printf调试的。


  Re: u-boot代码链接的问题  


楼上是不是说 因为adr指令是小范围地址读取指令,所以在不同的运行环境下,_start的值不同啊。
针对44box中的代码
adr r0, real_vectors
add r2, r0, #1024
ldr r1, =0x0c000000
add r1, r1, #0x08
vector_copy_loop:
ldmia r0!, {r3-r10}
stmia r1!, {r3-r10}
cmp r0, r2
ble vector_copy_loop
复制向量中断,为什么要复制1024个字节呢?
问得比较弱,见笑了。


  Re: u-boot代码链接的问题  


多一点是没有关系的


  Re: u-boot代码链接的问题  


在FLASH中以相对地址运行,然后一个绝对跳转完成从FLASH到RAM的切换,应该制定为UBOOT在RAM中的起始地址!

使用特权

评论回复
38
yuhuihui| | 2009-11-6 09:21 | 只看该作者
强呀

使用特权

评论回复
39
itelectron|  楼主 | 2009-11-13 14:02 | 只看该作者
转!!!!!!!!!!!!!!

|Image$$RO$$Limit||Image$$RW$$Base||Image$$ZI$$Ba|2008-10-21 17:34对于刚学习ARM的人来说,如果分析它的启动代码,往往不明白下面几个变量的含义:|Image$$RO$$Limit|、|Image$$RW$$Base|、|Image$$ZI$$Base|。

首先申明我使用的调试软件为ADS1.2,当我们把程序编写好以后,就要进行编译和链接了,在ADS1.2中选择MAKE按钮,会出现一个Errors and Warnings 的对话框,在该栏中显示编译和链接的结果,如果没有错误,在文件的最后应该能看到Image component sizes,后面紧跟的依次是Code,RO Data ,RW Data ,ZI Data ,Debug 各个项目的字节数,最后会有他们的一个统计数据:

Code 163632 ,RO Data 20939 ,RW Data 53 ,ZI Data 17028

Tatal RO size (Code+ RO Data)             184571 (180.25kB)

Tatal RW size(RW Data+ ZI Data)           17081(16.68 kB)

Tatal ROM size(Code+ RO Data+ RW Data)   184624(180.30 kB)

后面的字节数是根据用户不同的程序而来的,下面就以上面的数据为例来介绍那几个变量的计算。

在ADS的Debug Settings中有一栏是Linker/ARM Linker,在output选项中有一个RO base选项,下面应该有一个地址,我这里是0x0c100000,后面的RW base 地址是0x0c200000,然后在Options选项中有Image entry point ,是一个初始程序的入口地址,我这里是0x0c100000 。

有了上面这些信息我们就可以完全知道这几个变量是怎么来的了:

|Image$$RO$$Base| = Image entry point = 0x0c100000 ;表示程序代码存放的起始地址

|Image$$RO$$Limit|=程序代码起始地址+代码长度+1=0x0c100000+Tatal RO size+1

= 0x0c100000 + 184571 + 1 = 0x0c100000 +0x2D0FB + 1

= 0x0c12d0fc

|Image$$RW$$Base| = 0x0c200000 ;由RW base 地址指定

|Image$$RW$$Limit| =|Image$$RW$$Base|+ RW Data 53 = 0x0c200000+0x37(4的倍数,0到55,共56个单元)

=0x0c200037

|Image$$ZI$$Base| = |Image$$RW$$Limit| + 1 =0x0c200038

|Image$$ZI$$Limit| = |Image$$ZI$$Base| + ZI Data 17028

                            =0x0c200038 + 0x4284

                            =0x0c2042bc

也可以由此计算:

|Image$$ZI$$Limit| = |Image$$RW$$Base| +TatalRWsize(RWData+ZIData) 17081

                            =0x0c200000+0x42b9+3(要满足4的倍数)

                            =0x0c2042bc

加点自己的补充:

其中RO为代码段;RW为已经初始化的全局变量;ZI是未初始化的全局变量,Bootloader要将RW段复制到RAM中并将ZI段清零;

void *mallocPt=Image$$RW$$Limit; //Image$$RW$$Limit+1 = Image$$ZI$$Base

RO 是code + RO Data ,RO data应该是const声明的常量

下载到固件中的代码包括RO和RW,ZI主要被malloc 函数用到,还有这些概念和堆栈的联系,malloc声明的变量在heap(堆)中,stack(栈)是用来存放临时变量的。

这些概念比较混杂,要理解清楚

使用特权

评论回复
40
china_fog| | 2009-11-13 15:21 | 只看该作者
顶一下,精神可嘉

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则