乐趣区

关于c:S3C2440移植uboot之启动过程概述

  上节烧写了 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, #16
copyex:
       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

#endif

call_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 上的绝对动静信息完结地址 =0x6b568

fixloop:
       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     fixnext
fixabs:
       /* 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     fixnext

fixrel:
       /* relative fix: increase location by offset */
       ldr   r1, [r0]                  //r1=33F41020 地址处的内容 =0x1e0
       add r1, r1, r9                //r1=0x1e0+33F41000= 33F411e0

fixnext:
       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 那些事”]

退出移动版