上节烧写了uboot到开发板,不能运行。这节咱们剖析uboot从新编译uboot,由最初一条链接命令开始剖析uboot
@[TOC]

  下图为编译uboot后显示的最初一条链接命令。

1.剖析start.S

  关上uboot.lds,发现链接地址为0,所以新的uboot只能在nor flash运行。运行开始文件为start.o。

  上面咱们剖析arch/arm/cpu/arm920t/start.S
  从start_code开始运行

.globl _start                                //申明_start全局符号,这个符号会被lds链接脚本用到_start:    b     start_code                            //跳转到start_code符号处,0x00       ldr   pc, _undefined_instruction                    //0x04       ldr   pc, _software_interrupt                       //0x08       ldr   pc, _prefetch_abort                           //0x0c       ldr   pc, _data_abort                               //0x10       ldr   pc, _not_used                                 //0x14       ldr   pc, _irq                                      //0x18       ldr   pc, _fiq                                      //0x20_undefined_instruction:  .word undefined_instruction           //定义_undefined_instruction指向undefined_instruction(32位地址)_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        //balignl应用,参考http://www.cnblogs.com/lifexy/p/7171507.html

2._start会跳转到start_code处

start_code:    /*设置CPSR寄存器,让CPU进入管理模式*/       mrs  r0, cpsr                 //读出cpsr的值       bic   r0, r0, #0x1f           //清位       orr   r0, r0, #0xd3          //位或       msr  cpsr, r0                 //写入cpsr#if   defined(CONFIG_AT91RM9200DK) || defined(CONFIG_AT91RM9200EK)       /*        * relocate exception table        */       ldr   r0, =_start                   ldr   r1, =0x0                //r1等于异样向量基地址       mov r2, #16copyex:       subs       r2, r2, #1           //减16次,s示意每次减都要更新条件标记位       ldr   r3, [r0], #4              str   r3, [r1], #4      //将_start标号后的16个符号存到异样向量基地址0x0~0x3c处       bne  copyex             //直到r2减为0#endif#ifdef CONFIG_S3C24X0       /* 关看门狗*/#  define pWTCON       0x53000000#  define INTMSK 0x4A000008    /* Interrupt-Controller base addresses */#  define INTSUBMSK  0x4A00001C#  define CLKDIVN       0x4C000014    /* clock divisor register */       ldr   r0, =pWTCON              mov r1, #0x0               str   r1, [r0]           //关看门狗,使WTCON寄存器=0       /*关中断*/       mov r1, #0xffffffff       ldr   r0, =INTMSK       str   r1, [r0]                  //敞开所有中断# if defined(CONFIG_S3C2410)       ldr   r1, =0x3ff       ldr   r0, =INTSUBMSK       str   r1, [r0]                  //敞开次级所有中断# endif    /* 设置时钟频率, FCLK:HCLK:PCLK = 1:2:4 ,而FCLK默认为120Mhz*/       ldr   r0, =CLKDIVN       mov r1, #3       str   r1, [r0] #ifndef CONFIG_SKIP_LOWLEVEL_INIT       bl    cpu_init_crit                         //敞开mmu,并初始化各个bank#endifcall_board_init_f:       ldr   sp, =(CONFIG_SYS_INIT_SP_ADDR) //CONFIG_SYS_INIT_SP_ADDR=0x30000f80       bic   sp, sp, #7         //sp=0x30000f80       ldr   r0,=0x00000000       bl    board_init_f

  下面的CONFIG_SYS_INIT_SP_ADDR =0x30000f80,是通过arm-linux-objdump -D u-boot>u-boot.dis生成反汇编,而后从u-boot.dis失去的。

3.而后进入第一个C数:board_init_f()

  该函数次要工作是:
  清空gd指向的构造体、通过init_sequence函数数组,来初  始化各个函数以及逐渐填充gd构造体,最初划分内存区域,  将数据保留在gd里,而后调用relocate_code()对uboot重定位。
  (gd是用来传递给内核的参数)
1)具体代码如下所示:

void board_init_f(ulong bootflag) // bootflag=0x00000000{       bd_t *bd;       init_fnc_t **init_fnc_ptr;         //函数指针       gd_t *id;       ulong addr, addr_sp;#ifdef CONFIG_PRAM       ulong reg;#endif       bootstage_mark_name(BOOTSTAGE_ID_START_UBOOT_F, "board_init_f");       /* Pointer is writable since we allocated a register for it */       gd = (gd_t *) ((CONFIG_SYS_INIT_SP_ADDR) & ~0x07);

  其中gd是一个全局变量,用来传递给内核的参数用的,如下图所示,在arch/arn/include/asm/global_data.h中定义,*gd指向r8寄存器,所以r8专门提供给gd应用

  而CONFIG_SYS_INIT_SP_ADDR,在之前失去=0x30000f80,所以gd=0x30000f80
2)持续来看board_init_f():

__asm__ __volatile__("": : :"memory");           //memory:让cpu从新读取内存的数据      memset((void *)gd, 0, sizeof(gd_t));        //将0x30000f80地址上的gd_t构造体清0      gd->mon_len = _bss_end_ofs;           // _bss_end_ofs =__bss_end__ - _start,在反汇编找到等于0xae4e0,所以mon_len等于uboot的数据长度      gd->fdt_blob = (void *)getenv_ulong("fdtcontroladdr", 16, (uintptr_t)gd->fdt_blob);       for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr)            //调用init_sequence[]数组里的各个函数      {              if ((*init_fnc_ptr)() != 0)     //执行函数,若函数执行出错,则进入hang()             {               hang ();   //打印错误信息,而后始终while             }       }

  下面的init_sequence[]数组里存了各个函数,比方有:
  board_early_init_f():设置零碎时钟,设置各个GPIO引脚
  timer_init():初始化定时器
  env_init():设置gd的成员变量
  init_baudrate():设置波特率
  dram_init():设置gd->ram_size= 0x04000000(64MB)
3)持续来看board_init_f():

addr = CONFIG_SYS_SDRAM_BASE + gd->ram_size;  // addr=0x34000000 // CONFIG_SYS_SDRAM_BASE:  SDRAM基地址,为0X30000000// gd->ram_size:          等于0x04000000 #if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF))       /* reserve TLB table */       addr -= (4096 * 4);        //addr=33FFC000       addr &= ~(0x10000 - 1);  // addr=33FF0000,          gd->tlb_addr = addr;   //将64kB调配给TLB,所以TLB地址为33FF0000~33FFFFFF#endif       /* round down to next 4 kB limit */       addr &= ~(4096 - 1);                    //4kb对齐, addr=33FF0000       debug("Top of RAM usable for U-Boot at: %08lx\n", addr);       /*        * reserve memory for U-Boot code, data & bss        * round down to next 4 kB limit        */       addr -= gd->mon_len; // 在后面剖析过gd->mon_len=0xae4e0,                           //所以addr=33FF0000 -0xae4e0=33F41B20,       addr &= ~(4096 - 1);  //4095=0xfff,4kb对齐, addr=33F41000                             //所以调配给uboot各个段的重定位地址为33F41000~33FFFFFF       debug("Reserving %ldk for U-Boot at: %08lx\n", gd->mon_len >> 10, addr);#ifndef CONFIG_SPL_BUILD       addr_sp = addr - TOTAL_MALLOC_LEN; //调配一段malloc空间给addr_sp                       //TOTAL_MALLOC_LEN=1024*1024*4,所以malloc空间为33BF1000~33F40FFF       addr_sp -= sizeof (bd_t);            //调配一段bd_t构造体大的空间    bd = (bd_t *) addr_sp;               //bd指向刚刚调配进去的bd_t构造体    gd->bd = bd;                         // 0x30000f80处的gd变量的成员bd等于bd_t基地址    addr_sp -= sizeof (gd_t);              //调配一个gd_t构造体大的空间    id = (gd_t *) addr_sp;                 //id指向刚刚调配的gd_t构造体    gd->irq_sp = addr_sp;                 //0x30000f80处的gd变量的成员irq_sp等于gd_t基地址    addr_sp -= 12;    addr_sp &= ~0x07;    ... ...    relocate_code(addr_sp, id, addr);  //进入relocate_code()函数,重定位代码,以及各个符号    // addr_sp: 栈顶,该栈顶向上的地位用来寄存gd->irq_sp、id 、gd->bd、malloc、uboot、TLB(64kb),    //id:       寄存 gd_t构造体的首地址    // addr:    等于寄存uboot重定位地址33F41000}

  执行完board_init_f()后,最终内存会划分如下图所示:


  其实此时uboot还在flash中运行,而后会进入start.S的relocate_code()里进行uboot重定位

4.接下来进入重定位

1)start.S的relocate_code()代码如下所示

relocate_code:       mov r4, r0      /* save addr_sp */              // addr_sp栈顶值       mov r5, r1      /* save addr of gd */           // id值       mov r6, r2      /* save addr of destination */  // addr值:uboot重定位地址       /* Set up the stack        */stack_setup:       mov sp, r4                //设置栈addr_sp       adr  r0, _start           //在顶层目录下system.map符号文件中找到_start =0,所以r0=0       cmp r0, r6                //判断_start(uboot重定位之前的地址)和addr(重定位地址)是否一样       beq clear_bss             /* skip relocation */        mov r1, r6             /* r1 <- scratch for copy_loop */ //r1= addr(重定位地址)       ldr   r3, _bss_start_ofs               //_bss_start_ofs=__bss_start - _start(uboot代码大小)       add r2, r0, r3         /* r2 <- source end address*/   //r2= uboot重定位之前的完结地址copy_loop:       ldmia      r0!, {r9-r10}  /* copy from source address [r0] */                              //将r0处的两个32位数据拷到r9-r10中,而后r0+=8       stmia      r1!, {r9-r10}  /* copy to   target address [r1]*/                             //将拷进去的两个数据放入r1(重定位地址)处,而后r1+=8       cmp r0, r2  /* until source end address [r2]*/   //判断拷贝的数据是否到完结地址       blo  copy_loop

  下面只是把代码复制到SDRAM上,而链接地址内容却没有扭转,比方异样向量0x04的代码内容还是0x1e0,
  咱们以异样向量0x04为例,来看它的反汇编:

   如上图所示,即便uboot在SDRAM运行,因为代码没批改,PC也会跳到0x1e0(flash地址)上,和之前老的uboot有很大区别,以前老的uboot间接是应用的SDRAM链接地址,如下图所示:

  所以,新的uboot采纳了动静链接地址的办法,在链接脚本uboot.lds中,能够看到这两个段(.rel.dyn、.dynsym):

  该两个段里,便是保留了各个文件的绝对动静信息(.rel.dyn)、动静链接地址的符号(.dynsym)
  以上图的.rel.dyn段为例来剖析,找到__rel_dyn_start符号处:

  如上图所示,其中0x17示意的是符号的完结标记位,咱们以0x20为例来解说:
  在之前,咱们讲过0x20外面保留的是异样向量0x04跳转的地址(0x1e0),如下图所示:

  所以,接下来的代码,便会依据0x20里的值0x1e0(flash地址),将SDRAM的33F41000+0x20的内容改为33F41000+0x1e0(SDRAM地址),来扭转uboot的链接地址
2)重定位的残余代码,如下所示:

#ifndef CONFIG_SPL_BUILD       /*        * fix .rel.dyn relocations        */       ldr   r0, _TEXT_BASE             /* r0 <- Text base */  //r0=text段基地址=0       sub  r9, r6, r0         /* r9 <- relocation offset */   //r9= 重定位后的偏移值=33F41000       ldr   r10, _dynsym_start_ofs  /* r10 <- sym table ofs */                                           //_dynsym_start_ofs =__dynsym_start - _start=0x73608                                          //所以r10=动静符号表的起始偏移值=0x73608       add r10, r10, r0            /* r10 <- sym table in FLASH */                                       //r10=flash上的动静符号表基地址=0x73608       ldr   r2, _rel_dyn_start_ofs     /* r2 <- rel dyn start ofs */                                          //r2=__rel_dyn_start - _start=0x6b568                                          //所以r2=绝对动静信息的起始偏移值=0x6b568       add r2, r2, r0         /* r2 <- rel dyn start in FLASH */                                      //r2=flash上的绝对动静信息基地址=0x6b568       ldr   r3, _rel_dyn_end_ofs      /* r3 <- rel dyn end ofs */                                          // _rel_dyn_end_ofs=__rel_dyn_end - _start=00073608                                          //所以r3=绝对动静信息的完结偏移值=00073608       add r3, r3, r0         /* r3 <- rel dyn end in FLASH */                                    //r3=flash上的绝对动静信息完结地址=0x6b568fixloop:       ldr   r0, [r2]           /* r0 <- location to fix up, IN FLASH! */                               //以0x20为例,r0=0x6b568地址处的内容= 0x20       add r0, r0, r9         /* r0 <- location to fix up in RAM */                                     //r0=33F41000+0x20=33F41020       ldr   r1, [r2, #4]             //r1= 33F41024地址处的内容=0x17       and  r7, r1, #0xff              cmp r7, #23                  /* relative fixup? */  //0x17=23,所以相等       beq fixrel                                       //跳到:fixerl       cmp r7, #2                    /* absolute fixup? */       beq fixabs       /* ignore unknown type of fixup */       b     fixnextfixabs:       /* absolute fix: set location to (offset) symbol value */       mov r1, r1, LSR #4         /* r1 <- symbol index in .dynsym */       add r1, r10, r1              /* r1 <- address of symbol in table */       ldr   r1, [r1, #4]             /* r1 <- symbol value */       add r1, r1, r9         /* r1 <- relocated sym addr */       b     fixnextfixrel:       /* relative fix: increase location by offset */       ldr   r1, [r0]                  //r1=33F41020地址处的内容=0x1e0       add r1, r1, r9                //r1=0x1e0+33F41000= 33F411e0fixnext:       str   r1, [r0]             //扭转链接地址里的内容, 33F41020=33F411e0  (之前为0x1e0)          add r2, r2, #8             //r2等于下一个绝对动静信息(0x24)的地址       cmp r2, r3                //若没到尾部__rel_dyn_end,便继续执行: fixloop       blo  fixloop                 #endif

5.革除bss段

/*重定位实现后,革除bss段*/clear_bss: #ifndef CONFIG_SPL_BUILD       ldr   r0, _bss_start_ofs                        //获取flash上的bss段起始地位       ldr   r1, _bss_end_ofs                          //获取flash上的bss段完结地位       mov r4, r6                    /* reloc addr */     //获取r6(SDRAM上的uboot基地址)       add r0, r0, r4                                  //加上重定位偏移值,失去SDRAM上的bss段起始地位       add r1, r1, r4                                     //失去SDRAM上的bss段完结地位       mov r2, #0x00000000           /* clear*/clbss_l:    str    r2, [r0]           /* clear loop...       */                 //开始革除SDRAM上的bss段       add r0, r0, #4       cmp r0, r1       bne  clbss_l       bl coloured_LED_init       bl red_led_on#endif

5.1持续往下剖析

#ifdef CONFIG_NAND_SPL                   //未定义,所以不执行  ... ...                          #else                                   //执行else       ldr   r0, _board_init_r_ofs         //r0=flash上的board_init_r()函数地址偏移值       adr  r1, _start                    //0       add lr, r0, r1                     //返回地址lr=flash上的board_init_r()函数       add lr, lr, r9                     //加上重定位偏移值(r9)后,lr=SDRAM上的board_init_r()函数       /* setup parameters for board_init_r */       mov r0, r5             /* gd_t */              //r0=id值       mov r1, r6             /* dest_addr */         //r1=uboot重定位地址       /* jump to it ... */       mov pc, lr              //跳转:  board_init_r()函数_board_init_r_ofs:       .word board_init_r - _start        //获取在flash上的board_init_r()函数地址偏移值#endif

  从下面代码看出, 接下来便会进入uboot的board_init_r()函数,该函数会对各个外设初始化、环境变量初始化等.
uboot的启动过程到此便完结了。
  下一节咱们将新建一块单板反对S3C2440。
如遇到排版错乱的问题,能够通过以下链接拜访我的CSDN。

**CSDN:[CSDN搜寻“嵌入式与Linux那些事”]