uboot跳转RAM空间问题

该文章转载于百度文库中。

都知道 U-BOOT 分为两个阶段,第一阶段是(~/cpu/arm920t/start.S 中)在FLASH上运行(一般情况下),完成对硬件的初始化,包括看门狗,中断缓存等,并且负责把代码搬移到SDRAM 中(在搬移的时候检查自身代码是否在SDRAM 中),然后完成C 程序运行所需要环境的建立,包括堆栈的初始化等,最后执行一句跳转指令:
             ldr pc, _start_armboot
             _start_armboot: .word start_armboot,
进入到/lib_arm/board.c 中的函数void start_armboot (void),从此就进入了第二阶段。这是在很多资料上都有讲述的,所以勿需多言了。现在对于第一阶段有几个问题,以前我一直是没有搞明白的,既然在FLASH中的代码是把自己拷贝到SDRAM 中,那么在S3C2410 的内存地址空间,就有两份的启动代码,第一份就是在FLASH 中,第二份就是在SDRAM 中。根据链接脚本文件  (~/board/smdk2410/u-boot.lds)
             OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
             /*OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-arm")*/
             OUTPUT_ARCH(arm)
             ENTRY(_start)
            SECTIONS
            {
               . = 0x00000000; /* 后记:这个链接起始地址实际上被-Ttest $(TEST_BASE)更新了*/
               . = 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 = .;
       }
           其中的链接命令 . = 0x00000000;表示地址计数器从0 地址开始计数,而且_start 是程序代码段的入口,那么*.text 中的所有地址标号(cpu/arm920t/start.S 中定义的)就应该从0 地址开始计数,那么标号start_armboot(就是void start_armboot (void)函数的入口地址)应该在FLASH中才对啊,所以按照上边的分析,
           

                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: 0x33F8000(在./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 中,不影响第一阶段的正确执行。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值