zoukankan      html  css  js  c++  java
  • S3C2440移植uboot之启动过程概述

      上节烧写了uboot到开发板,不能运行。这节我们分析uboot重新编译uboot,由最后一条链接命令开始分析uboot

      下图为编译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
    ", 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
    ", 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。

    有任何问题,均可通过公告中的二维码联系我

  • 相关阅读:
    HDU 5492 Find a path
    codeforce gym 100548H The Problem to Make You Happy
    Topcoder SRM 144 Lottery
    codeforce 165E Compatible Numbers
    codeforce gym 100307H Hack Protection
    区间DP总结
    UESTC 1321 柱爷的恋爱 (区间DP)
    HDU 4283 You Are the One (区间DP)
    HDU 2476 String painter (区间DP)
    UESTC 426 Food Delivery (区间DP)
  • 原文地址:https://www.cnblogs.com/dongxb/p/14198428.html
Copyright © 2011-2022 走看看