uboot分析之第一阶段源码

这两天在分析uboot,昨天分析完了makefile之后,今天分析了第一阶段的源码,故而分享之。新手望各位指教
开始先插一下,uboot 移植的思路:
1 首先来确定UBOOT 的每一步动作,以及这个动作所使用的文件,函数等等
2 再来确定哪一个动作需要修改的
3 一般CPU 都是 ARM920T 所以主要修改的是板级函数,在board 文件夹下
下来进行分析:分析的前提是假设所有的配置都是打开的。
首先,看start.S
.globl_start
_start:     b      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
知道上电后执行的是 b reset
找到rest 如下:
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 */
#elifdefined(CONFIG_S3C2410)
#define pWTCON        0x53000000
#define INTMSK         0x4A000008   /* Interupt-Controller base addresses */
#define INTSUBMSK   0x4A00001C
#define CLKDIVN       0x4C000014   /* clock divisor register */
#endif
#ifdefined(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]
# ifdefined(CONFIG_S3C2410)
       ldr   r1,=0x3ff
       ldr   r0,=INTSUBMSK
       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 */
       /*
        *we do sys-critical inits only at reboot,
        *not when booting from ram!
        */
#ifndefCONFIG_SKIP_LOWLEVEL_INIT
       bl    cpu_init_crit
#endif
很清楚地看到,首先上电后执行的是:
1.      设为SVC MODE
2.      关闭watchdog
3.      屏蔽IRQ中断
4.      设置时钟
5.      跳转到cpu_init_crit
好了,我们来找到cpu_init_crit在cpu/arm920t/start.S中。
看一看代码如下:
#ifndefCONFIG_SKIP_LOWLEVEL_INIT
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 stuff 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 boarddirectory.
       */
      mov       ip,lr
      bl    lowlevel_init
      mov       lr,ip
      mov       pc,lr
#endif /*CONFIG_SKIP_LOWLEVEL_INIT */
代码的注释十分详细,不用看代码都可以知道,这个函数主要做的事情有
1 flush v4 I/D caches
2 disable MMU stuff and caches
3这句话很重要:
      /*
       *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.
       */
我们可以知道,接下来他要做的事情是初始化RAM,原因是because memory timing isboard-dependend,,所以我们要根据自己的板子来设置好RAM,以进行代码的重定位。
4
bl      lowlevel_init
  跳转到lowlevel_init,根据这句you will find alowlevel_init.S in your board directory.,我们就到/board去找找看。
果然找到了
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtml1/01/clip_image002.jpg
代码有点长,所以节选来看。
#include<config.h>
#include <version.h>
首先它包含了两个头文件,第一个是我们的配置文件主要是选择打开的配置,是我们手动创建生成的。
第二 个是版本信息。没什么值得分析的
接下来定义了一个
_TEXT_BASE:
      .word    TEXT_BASE
这个TEXT_BASE其实才是镜像文件的真正其实地址。我们来看,连接器脚本,look一下,
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 = .;
}
看起来好像是0地址开始的,可是......我们来看看map文件吧,look一下
Linker script andmemory map
                0x00000000                . = 0x0
                0x00000000                . = ALIGN (0x4)
.text           0x33f80000    0x12844
cpu/arm920t/start.o(.text)
.text         0x33f80000      0x400cpu/arm920t/start.o
                0x33f80048                _bss_start
                0x33f8004c                _bss_end
                0x33f80044                _armboot_start
                0x33f80000                _start
看吧,地址是从0x33f80000开始的,为什么会这样呢?
其实奥妙就在makefile 当中.
我们看看实际上makefile 是怎么链接的
这一句cd $(LNDIR) &&$(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS) \
而在mkconfig脚本生成的config.mk文件中可以看到:
LDFLAGS += -Bstatic-T $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS)
吓尿了有木有!事实上连接起始地址是TEXT_BASE这个变量在board/smdk2410中定义是:
TEXT_BASE =0x33F80000
为什么要这么做?因为我们的代码最终要拷贝到RAM 内以提高速度,而SMDK2410这块板子的RAM起始地址是0x30000000,所以0x3ff80000是RAM的地址代码就被链接到了RAM里面啦。
路人甲发问:为毛不是从0x30000000开始?
我:……不要在意这些细节…….
如果非要弄明白这个问题,那就请看start.S的代码:
stack_setup:
      ldr   r0,_TEXT_BASE           /* upper 128 KiB:relocated uboot   */
      sub       r0,r0, #CFG_MALLOC_LEN     /* mallocarea                      */
      sub       r0,r0, #CFG_GBL_DATA_SIZE /* bdinfo                        */
#ifdefCONFIG_USE_IRQ
      sub       r0,r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
      sub       sp,r0, #12          /* leave 3 words forabort-stack    */
很明白了,RAM起始部分的地址是留给栈和MALLOC,全局参数,以及ABORT异常的。
路人乙:那为毛链接到RAM里面的代码在NOR上跑起来了
我:卧槽!你有种就再说一遍!
咳咳咳,咱们不能再开叉了,不然就跑题了,如果想要明白这个问题的,请谷歌“位置无关码”“GUN程序加载数据方式”
好了花了那么多口舌去讲一个TEXT_BASE可见其很重要,为毛重要,是因为移植的时候我们必须要注意到这一点,不然写程序的时候没有意识到链接地址是TEXT_BASE开始的,那么我们自己添加的代码就会作废了。
继续分析
Lowlevel,刚刚分析到第四步,现在是第五步啦
5第五步就是初始化RAM啦
lowlevel_init:
      /* memory control configuration */
      /* make r0 relative the current locationso that it */
      /* reads SMRDATA out of FLASH rather thanmemory ! */
      ldr    r0, =SMRDATA
      ldr   r1,_TEXT_BASE
      sub       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
其中SMRDATA是RAM相关寄存器设置值的存放起始地址,一共十三个。因为这时候代码所在的位置是0起始的,而实际上连接的地址是0x33f80000,所以直接去访问数据肯定是错误的,为什么?因为程序在连接的时候已经确定了所有的符号地址,比如说你去访问SMRDATA这个数组,因为连接时地址已经确定,所以访问的地址应该是RAM中的地址,但是现在我们的镜像是在NOR里的,访问自然理所当然会出错。故而我们应该找到SMRDATA这个数组在NOR的位置,于是:sub r0,r0,r1.。然后做基址寻址,ok!大功告成。
6最后一步,事情都完成了,要返回咯
      /* everything is fine now */
      mov       pc,lr
嗯?这个代码注释挺有喜感的,德国人都这么幽默吗?
返回哪里?我的天,当然是哪里调用的lowlevel_init就返回哪里呀。返回cpu_init_crit。
返回后我们看到这么一段代码
mov       ip, lr
      bl    lowlevel_init
      mov lr,ip
      mov pc,lr
嗯,从cpu_init_crit返回。返回到rest中。
看看接下来会发生什么吧。

好了现在我们回到rest的bl cpu_init_crit处接着执行。
回顾一下我们已经完成的工作:
1 设为SVC MODE
2 关闭watchdog
3 屏蔽IRQ 中断
4 设置时钟
5 清缓冲
6 关闭 MMU
7 初始化 RAM
既然更舒适温馨的房子 ----RAM 已经准备好了,那么接下来就搬到 RAM 里面去住吧。
代码很简单,就是平常我们写的 copy 函数:
relocate:                       /* relocate U-Boot to RAM       */
       adr   r0,_start        /* r0 <- current positionof code   */
       ldr   r1,_TEXT_BASE         /* test if we run fromflash or RAM */
       cmp    r0, r1                  /* don'treloc during debug         */
       beq    stack_setup
       ldr   r2,_armboot_start
       ldr   r3,_bss_start
       sub  r2,r3, r2        /* r2 <- size ofarmboot            */
       add  r2,r0, r2        /* r2 <- source endaddress         */
copy_loop:
       ldmia      r0!,{r3-r10}         /* copy from sourceaddress [r0]    */
       stmia       r1!,{r3-r10}         /* copy to   target address [r1]    */
       cmp r0,r2                    /* until source endaddreee [r2]    */
       ble   copy_loop
首先取出来当前代码所在的位置adr r0,_start 然后判断一下代码是不是在RAM里面,cmp r0,r1如果在RAM里面就直接跳到下一步,如果不在就copy_loop吧。
接下来,设置好栈,然后清BSS段,接着跳转到第二阶段(RAM内),开始新的旅程了。
跳转的代码如下:
      ldr   pc,_start_armboot
_start_armboot:     .word start_armboot
这个_start_armboot就是第二阶段的入口,在/lib_arm/board.c内。
                                                                                   ———第一阶段分析结束
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值