加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

对u-boot重定向(relocate)的理解

(2012-10-04 12:10:14)
分类: u-boot学习
     序言: 一周前开始学习u-boot并尝试在S3C2440开发板上的移植。根据韦东山编写的《嵌入式Linux应用开发完全手册》中介绍u-boot的移植过程(NorFlash启动),我编译出适合S3C2440开发板u-boot二进制文件。但是,由于没有理解移植原理,我首先将二进制文件下载到飞凌2440核心板中的NandFlash中去运行,结果Nand启动后串口终端没有任何反映。然后,我通过J-Link下载器将二进制文件烧写到核心板的NorFlash中【1】,在终端看到了u-boot的启动信息。为什么NorFlash能够启动u-boot而NandFlash不能?u-boot的启动过程是怎样的?如何进行u-boot移植?这些问题使我开始想要去研究u-boot的源代码。

      源码中一个比较重要的概念是“重定向(relocate)”,简单来说就是将u-boot的运行环境转移到SDRAM中去。但是仔细研究相关代码,却发现还有很多值得思考的问题。以下内容基于u-boot 1.1.6源码以及飞凌S3C2440核心板。

      1. 重定向的意义和目标
      以S3C2440为例,启动方式有两种,一种是NorFlash启动另外一种是NandFlash启动【2】。
      NorFlash有自己的地址线和数据线,可以采用类似于Memory的随机访问方式,在
NorFlash上可以直接运行程序,所以NorFlash可以直接用来启动u-boot。但是,就运行速度而言SDRAM比NorFlash更能满足一些中断事件响应的需求。
      NandFlash是IO设备,数据、地址、控制线都是共用的,需要软件区控制读取时序,所以不能像
NorFlash、内存一样随机访问,不能EIP(片上运行),因此不能直接作为boot。但是S3C2440在选择NandFlash启动时,会首先将NandFlash中的前4K代码拷贝到其内部的SRAM中去运行,而超过4K的u-boot文件则无法完全通过内部SRAM执行,这就需要借助SDRAM来完成。
      所以,将u-boot转移到SDRAM中去运行就成为一种可行的方案,这一过程也就是“重定向”。所以,重定向的目标就是
将u-boot的运行环境转移到SDRAM中去。那么转移就要涉及两种地址,一种是转移前代码存放的地址,即加载地址,另一种转移后代码运行的地址,即运行地址。

      2. u-boot的
加载地址运行地址
      这两种地址是由链接脚本(.lds文件)定义的,在u-boot源码的根目录下Makefile中定义了u-boot的链接选项:
      $(obj)u-boot:        depend version $(SUBDIRS) $(OBJS) $(LIBS) $(LDSCRIPT)
              UNDEF_SYM=`$(OBJDUMP) -x $(LIBS) |sed  -n -e 's/.*\(__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\
              cd $(LNDIR) && $(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS) \
                  --start-group $(__LIBS) --end-group $(PLATFORM_LIBS) \
                  -Map u-boot.map -o u-boot

      执行make之后,会打印出对应的链接指令:
              arm-linux-ld -Bstatic -T /home/chengbo/uboot/u-boot-1.1.6/board/FL2440/u-boot.lds -Ttext 0x33F80000 $UNDEF_SYM cpu/arm920t/start.o ... ...
                  
--start-group ... ... \
              -Map u-boot.map -o u-boot

      其中,链接选项-T指定了链接脚本u-boot.lds,它定义了gcc编译出目标文件的排列顺序、运行、加载地址以及程序入口等,部分源码如下:
      OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
        OUTPUT_ARCH(arm)
        ENTRY(_start)
        SECTIONS
        {
               
. = 0x00000000;
           
. = ALIGN(4);
               
.text      :
                 {
 
             cpu/arm920t/start.o    (.text)
  
            *(.text)
                 }
               ... ...
      链接脚本中没有定义加载地址(由AT属性来定义),所以默认运行地址与加载地址相同。运行地址从0x00000000开始,但是被链接指令中的另一个选项-Ttext 0x33F80000偏移到了0x33F80000,这个地址属于SDRAM,也就是说运行地址是从0x33F80000开始的(此时加载地址是否也是0x33F80000开始的,我不确定,杜春雷的《 ARM体系结构与编程 》解释的比较详细,以后要认真读一读)。将u-boot二进制文件反编译后,也可以清楚的看出运行地址的起始地址:
      u-boot:     file format elf32-littlearm
        Disassembly of section .text:

        33f80000 <_start>:
        33f80000:    ea000012       33f80050 <reset>
        33f80004:    e59ff014     ldr    pc, [pc, #20]    ; 33f80020 <_undefined_instruction>
        33f80008:    e59ff014     ldr    pc, [pc, #20]    ; 33f80024 <_software_interrupt>
        33f8000c:    e59ff014     ldr    pc, [pc, #20]    ; 33f80028 <_prefetch_abort>
        33f80010:    e59ff014     ldr    pc, [pc, #20]    ; 33f8002c <_data_abort>
        33f80014:    e59ff014     ldr    pc, [pc, #20]    ; 33f80030 <_not_used>
        33f80018:    e59ff014     ldr    pc, [pc, #20]    ; 33f80034 <_irq>
        33f8001c:    e59ff014     ldr    pc, [pc, #20]    ; 33f80038 <_fiq>

        33f80020 <_undefined_instruction>:
        33f80020:    33f80140     .word    0x33f80140

        33f80024 <_software_interrupt>:
        33f80024:    33f801a0     .word    0x33f801a0

        33f80028 <_prefetch_abort>:
        33f80028:    33f80200     .word    0x33f80200

        33f8002c <_data_abort>:
        33f8002c:    33f80260     .word    0x33f80260

        33f80030 <_not_used>:
        33f80030:    33f802c0     .word    0x33f802c0

        33f80034 <_irq>:
        33f80034:    33f80320     .word    0x33f80320

        33f80038 <_fiq>:
        33f80038:    33f80380     .word    0x33f80380

这段代码对应的是整个u-boot的启动代码/cpu/arm920t/start.S的中断入口代码,对应的源码如下:
        .globl _start
        _start:         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

            _undefined_instruction:    .word undefined_instruction
           _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

      设想,如果u-boot运行在NorFlash中(物理地址的起始地址为0x00000000),而且SDRAM中没有任何u-boot代码,那么程序是无法正常运行的。也就是说,通过Makefile引导的编译、链接,最终我们得到了只有在SDRAM中才能够正常运行的代码。所以,u-boot要将自己搬运到SDRAM中才可以正常运行。
      但是,在搬运代码之前,u-boot为什么能在NorFlash中运行起来呢?这就需要借助两种跳转指令,相对跳转(汇编指令b XXXX)以及“绝对跳转(非术语)”(汇编指令ldr pc, XXXX)。

      3. 从NorFlash向SDRAM的跳转
      u-boot执行的第一条指令位于/cpu/arm920t/start.S,上述源代码包含了这条指令,即:
                                                            reset
      其反汇编指令为:
                                                      b          0x33f80050
      但是,0x33f80050这个地址只是反汇编的结果,并不是跳转到这个绝对位置。相对跳转指令b是以当前pc指针为基址,加上一个偏移量来计算实际要跳转的地址,这个偏移量信息是保存在指令本身(机器码:0x
ea000012)的,与要跳转目的地址无关。所以,相对跳转指令在任意位置都可以正确运行。
      我们可以发现在start.S中,除了最初的几个中断入口跳转指令,在重定向(relocate)之前,跳转指令都采用相对跳转,这一点也保证了u-boot在重定向之前等够在NorFlash中运行。
      与相对跳转不同,“绝对跳转”(
ldr pc, XXXX)的目的是要跳转到某个绝对地址。它的实现过程是把文字池中某一位置存储的数据赋值给pc实现跳转,文字池中的这个位置与当前指令的偏移量是保存在指令本身中的(这一点与相对跳转指令b相似),也就是说无论程序运行在什么环境中,ldr都可以准确找到文字池中的这个位置,而这个位置存储的数据就是要跳转的绝对位置。以上述代码中:
                                                         ldr
pc, _undefined_instruction
      为例,其反汇编代码为:
                                                         ldr    pc,[pc, #20]
      [pc,#20]对应的相对位置是文字池中
_undefined_instruction这个位置,无论代码运行在什么位置,该指令都可以找到_undefined_instruction的地址,因为这个偏移量就保存在机器码0xe59ff014中。而_undefined_instruction中保存的值0x33f80140则是实际要跳转的绝对位置。
      因此,在u-boot运行之初,通过与运行环境无关的代码将整个代码转移到SDRAM之后,通过指令:
                                                         ldr    pc, _start_armboot
      跳转到SDRAM中去运行。这也就实现了u-boot的重定向。

【1】烧写u-boot到NorFlash的方法:http://blog.sina.com.cn/s/blog_4bd13d48010189yu.html
【2】NorFlash与NandFlash启动u-boot的区别:http://blog.sina.com.cn/s/blog_4bd13d48010189e1.html

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有