免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1273 | 回复: 0
打印 上一主题 下一主题

U-Boot启动过程 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-09-18 13:53 |只看该作者 |倒序浏览
http://blog.chinaunix.net/u2/63775/showart_658150.html
U-Boot启动过程
尽管有了调试跟踪手段,甚至也可以通过串口打印信息了,但是不一定能够判断出错原因。如果能够充分理解代码的启动流程,那么对准确地解决和分析问题很有帮助。
开发板上电后,执行U-Boot的第一条指令,然后顺序执行U-Boot启动函数。函数调用顺序如图6.3所示。
看一下board/smsk2410/u-boot.lds这个链接脚本,可以知道目标程序的各部分链接顺序。第一个要链接的是
cpu/arm920t/start.o,那么U-Boot的入口指令一定位于这个程序中。下面详细分析一下程序跳转和函数的调用关系以及函数实现。
1.cpu/arm920t/start.S
这个汇编程序是U-Boot的入口程序,开头就是复位向量的代码。

图6.3  U-Boot启动代码流程图

_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      //中断向量

/* the actual reset code  */
reset:          //复位启动子程序
       /* 设置CPU为SVC32模式 */
       mrs   r0,cpsr
       bic   r0,r0,#0x1f
       orr   r0,r0,#0xd3
       msr   cpsr,r0
/* 关闭看门狗 */

/* 这些初始化代码在系统重起的时候执行,运行时热复位从RAM中启动不执行 */
#ifdef CONFIG_INIT_CRITICAL
       bl    cpu_init_crit
#endif

relocate:                       /* 把U-Boot重新定位到RAM */
       adr   r0, _start          /* r0是代码的当前位置 */
       ldr   r1, _TEXT_BASE      /* 测试判断是从Flash启动,还是RAM */
       cmp     r0, r1          /* 比较r0和r1,调试的时候不要执行重定位 */
       beq     stack_setup    /* 如果r0等于r1,跳过重定位代码 */
       /* 准备重新定位代码 */
       ldr   r2, _armboot_start
       ldr   r3, _bss_start
       sub   r2, r3, r2          /* r2 得到armboot的大小   */
       add   r2, r0, r2          /* r2 得到要复制代码的末尾地址 */
copy_loop: /* 重新定位代码 */
       ldmia r0!, {r3-r10}   /*从源地址[r0]复制 */
       stmia r1!, {r3-r10}   /* 复制到目的地址[r1] */
       cmp   r0, r2          /* 复制数据块直到源数据末尾地址[r2] */
       ble   copy_loop

       /* 初始化堆栈等    */
stack_setup:
       ldr   r0, _TEXT_BASE              /* 上面是128 KiB重定位的u-boot */
       sub   r0, r0, #CFG_MALLOC_LEN     /* 向下是内存分配空间 */
       sub   r0, r0, #CFG_GBL_DATA_SIZE /* 然后是bdinfo结构体地址空间  */
#ifdef CONFIG_USE_IRQ
       sub   r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
       sub   sp, r0, #12     /* 为abort-stack预留3个字 */
clear_bss:
       ldr   r0, _bss_start      /* 找到bss段起始地址 */
       ldr   r1, _bss_end        /*  bss段末尾地址   */
       mov   r2, #0x00000000     /* 清零 */
clbss_l:str r2, [r0]        /* bss段地址空间清零循环...  */
       add   r0, r0, #4
       cmp   r0, r1
       bne   clbss_l
       /* 跳转到start_armboot函数入口,_start_armboot字保存函数入口指针 */
       ldr   pc, _start_armboot
_start_armboot: .word start_armboot     //start_armboot函数在lib_arm/board.c中实现
/* 关键的初始化子程序 */
cpu_init_crit:
……  //初始化CACHE,关闭MMU等操作指令
       /* 初始化RAM时钟。
       * 因为内存时钟是依赖开发板硬件的,所以在board的相应目录下可以找到memsetup.S文件。
       */
       mov   ip, lr
       bl    memsetup        //memsetup子程序在board/smdk2410/memsetup.S中实现
       mov   lr, ip
       mov   pc, lr

2.lib_arm/board.c
start_armboot是U-Boot执行的第一个C语言函数,完成系统初始化工作,进入主循环,处理用户输入的命令。


void start_armboot (void)
{
       DECLARE_GLOBAL_DATA_PTR;
       ulong size;
       init_fnc_t **init_fnc_ptr;
       char *s;
       /* Pointer is writable since we allocated a register for it */
       gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
       /* compiler optimization barrier needed for GCC >= 3.4 */
       __asm__ __volatile__("": : :"memory");
       memset ((void*)gd, 0, sizeof (gd_t));
       gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
       memset (gd->bd, 0, sizeof (bd_t));
       monitor_flash_len = _bss_start - _armboot_start;
       /* 顺序执行init_sequence数组中的初始化函数 */
       for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
              if ((*init_fnc_ptr)() != 0) {
                      hang ();
              }
       }
       /*配置可用的Flash */
       size = flash_init ();
       display_flash_config (size);
       /* _armboot_start 在u-boot.lds链接脚本中定义 */
       mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);
       /* 配置环境变量,重新定位 */
       env_relocate ();
       /* 从环境变量中获取IP地址 */
       gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");
       /* 以太网接口MAC 地址 */
       ……
       devices_init ();      /* 获取列表中的设备 */
       jumptable_init ();
       console_init_r ();    /* 完整地初始化控制台设备 */
       enable_interrupts (); /* 使能例外处理 */
       /* 通过环境变量初始化 */
       if ((s = getenv ("loadaddr")) != NULL) {
               load_addr = simple_strtoul (s, NULL, 16);
       }
       /* main_loop()总是试图自动启动,循环不断执行 */
       for (;;) {
               main_loop ();      /* 主循环函数处理执行用户命令 -- common/main.c */
       }
       /* NOTREACHED - no way out of command loop except booting */
}

3.init_sequence[]
init_sequence[]数组保存着基本的初始化函数指针。这些函数名称和实现的程序文件在下列注释中。

init_fnc_t *init_sequence[] = {
       cpu_init,             /* 基本的处理器相关配置 -- cpu/arm920t/cpu.c */
       board_init,           /* 基本的板级相关配置 -- board/smdk2410/smdk2410.c */
       interrupt_init,       /* 初始化例外处理 -- cpu/arm920t/s3c24x0/interrupt.c */
       env_init,             /* 初始化环境变量 -- common/cmd_flash.c */
       init_baudrate,        /* 初始化波特率设置 -- lib_arm/board.c */
       serial_init,          /* 串口通讯设置 -- cpu/arm920t/s3c24x0/serial.c */
       console_init_f,       /* 控制台初始化阶段1 -- common/console.c */
       display_banner,       /* 打印u-boot信息 -- lib_arm/board.c */
       dram_init,            /* 配置可用的RAM -- board/smdk2410/smdk2410.c */
       display_dram_config,  /* 显示RAM的配置大小 -- lib_arm/board.c */
       NULL,
};

[color="#295200"]U-BOOT start_armboot浅析
   
        
            
            
               
                    
                        
                        
                           
                                
                                    
                                    
                                    
                                    start_armboot浅析
                                    ARM920t架构的CPU在完成基本的初始化后(ARM汇编代码),就进入它的C语言代码,而C语言代码的入口就是start_armboot, start_armboot在lib_arm/board.c中。start_armboot将完成以下工作。
                                    1.全局数据结构的初始化
                                    比如gd_t结构的初始化:
                                    251         gd = (gd_t*)(_armboot_start – CFG_MALLOC_LEN – sizeof(gd_t));
                                    _armboot_start是u-boot在RAM中的开始地址(对于u-boot最终搬移到RAM中运行的情况),CFG_MALLOC_LEN在include/configs/.h中定义。
                                    
                                    bd_t结构的初始化:
                                    272         gd->bd = (bd_t*)((char*)gd-sizeof(bd_t));
                                    u-boot把bd_t结构紧接着gd_t结构存放。
                                    
                                    内存分配的初始化
                                    316         mem_malloc_init(_armboot_start-CFG_MALLOC_LEN);
                                    经过以上的初始化后,u-boot在内存中的布局为(在底端为低地址)
                                    -----------------------------
                                    BSS
                                    -----------------------------
                                    U-BOOT TEXT/DATA
                                    -----------------------------
                                    CFG_MALLOC_LEN
                                    -----------------------------
                                    gd_t
                                    -----------------------------
                                    bd_t
                                    -----------------------------
                                    STACK
                                    -----------------------------
[color="#02368d"]U-Boot启动分析C语言部分(一)
                本文还是以u-boot-1.1.4为例,以make smdk2410_config命令配置源代码之后进行分析。
对于C语言部分代码的调用出现在cpu/arm920t/start.S的第223行:
  1.         ldr pc, _start_armboot
  2. _start_armboot: .word start_armboot
复制代码
这里的start_armboot就是lib_arm/board.c中第207行的start_armboot()函数,由此U-Boot开始执行C语言部分的代码。
start_armboot()函数一开始首先是一个宏调用:
  1. DECLARE_GLOBAL_DATA_PTR;
复制代码
这个宏的定义在include/asm-arm/global_data.h文件的第64行:
  1. #define DECLARE_GLOBAL_DATA_PTR     register volatile gd_t *gd asm ("r8")
复制代码
大概的意思是声明一个指向gd_t结构体变量的指针gd,并固定使用寄存器r8来存放该指针。而对gd_t结构体的定义从上面的第36行开始:
  1. typedef struct global_data {
  2.     bd_t  *bd;
  3.     unsigned long flags;
  4.     unsigned long baudrate;
  5.     unsigned long have_console; /* serial_init() was called */
  6.     unsigned long reloc_off; /* Relocation Offset */
  7.     unsigned long env_addr; /* Address  of Environment struct */
  8.     unsigned long env_valid; /* Checksum of Environment valid? */
  9.     unsigned long fb_base; /* base address of frame buffer */
  10. #ifdef CONFIG_VFD
  11.     unsigned char vfd_type; /* display type */
  12. #endif
  13. #if 0
  14.     unsigned long cpu_clk; /* CPU clock in Hz!  */
  15.     unsigned long bus_clk;
  16.     unsigned long ram_size; /* RAM size */
  17.     unsigned long reset_status; /* reset status register at boot */
  18. #endif
  19.     void  **jt;  /* jump table */
  20. } gd_t;
复制代码
在lib_arm/board.c文件中之所以能够直接使用这个宏和gd_t结构体是因为包含了include/common.h头文件,在common.h的第115行包含了include/asm-arm/global_data.h头文件。
继续来看start_armboot()函数的执行。lib_arm/board.c第229行开始的一个for循环:
  1. for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
  2.     if ((*init_fnc_ptr)() != 0) {
  3.         hang ();
  4.     }
  5. }
复制代码
这里的init_sequence是定义在上面第190行处的一个指针数组:
  1. init_fnc_t *init_sequence[] = {
  2.     cpu_init,  /* basic cpu dependent setup */
  3.     board_init,  /* basic board dependent setup */
  4.     interrupt_init,  /* set up exceptions */
  5.     env_init,  /* initialize environment */
  6.     init_baudrate,  /* initialze baudrate settings */
  7.     serial_init,  /* serial communications setup */
  8.     console_init_f,  /* stage 1 init of console */
  9.     display_banner,  /* say that we are here */
  10.     dram_init,  /* configure available RAM banks */
  11.     display_dram_config,
  12. #if defined(CONFIG_VCMA9) || defined (CONFIG_CMC_PU2)
  13.     checkboard,
  14. #endif
  15.     NULL,
  16. };
复制代码
数组类型init_fnc_t则是一个定义在第188行的函数指针类型:
  1. typedef int (init_fnc_t) (void);
复制代码
由此可知,init_sequence[]数组当中的所有元素都是函数指针了,而这个for循环的作用就是遍历这个数组的所有元素,然后用“(*init_fnc_ptr)()”就依次调用了这些函数来进行初始化的工作
                                    2.调用通用初始化函数
                                    for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
                                                  if ((*init_fnc_ptr)() != 0) {
                                                         hang ();
                                                  }
                                           }
                                    init_sequence[]是init_fnc_t函数指针数组,这个数组包含了众多初始化函数,比如cpu_init,board_init等。
                                    
                                    3.初始化具体设备
                                    这一部分包括对Flash,LCD,网络的初始化等,例如
                                    318 #if (CONFIG_COMMANDS & CFG_CMD_NAND)
                                           puts ("NAND: ");
                                           nand_init();            /* go init the NAND */
                                    #endif
                                    
                                    367 devices_init();
                                    
                                    386 #ifdef CONFIG_DRIVER_CS8900
                                           cs8900_get_enetaddr (gd->bd->bi_enetaddr);
                                    #endif
接上回,我们依次来看看init_sequence[]数组当中的各个元素。

首先是cpu_init()函数,定义于lib_arm/arm920t/cpu.c第88行。

接下来的board_init()函数定义于board/smdk2410/smdk2410.c第68行:
int board_init (void)
{
    DECLARE_GLOBAL_DATA_PTR;
    S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();
    S3C24X0_GPIO * const gpio = S3C24X0_GetBase_GPIO();
    /* to reduce PLL lock time, adjust the LOCKTIME register */
    clk_power->LOCKTIME = 0xFFFFFF;
    /* configure MPLL */
    clk_power->MPLLCON = ((M_MDIV  12) + (M_PDIV  4) + M_SDIV);
    /* some delay between MPLL and UPLL */
    delay (4000);
    /* configure UPLL */
    clk_power->UPLLCON = ((U_M_MDIV  12) + (U_M_PDIV  4) + U_M_SDIV);
    /* some delay between MPLL and UPLL */
    delay (8000);
    /* set up the I/O ports */
    gpio->GPACON = 0x007FFFFF;
    gpio->GPBCON = 0x00044555;
    gpio->GPBUP = 0x000007FF;
    gpio->GPCCON = 0xAAAAAAAA;
    gpio->GPCUP = 0x0000FFFF;
    gpio->GPDCON = 0xAAAAAAAA;
    gpio->GPDUP = 0x0000FFFF;
    gpio->GPECON = 0xAAAAAAAA;
    gpio->GPEUP = 0x0000FFFF;
    gpio->GPFCON = 0x000055AA;
    gpio->GPFUP = 0x000000FF;
    gpio->GPGCON = 0xFF95FFBA;
    gpio->GPGUP = 0x0000FFFF;
    gpio->GPHCON = 0x002AFAAA;
    gpio->GPHUP = 0x000007FF;
    /* arch number of SMDK2410-Board */
    gd->bd->bi_arch_number = MACH_TYPE_SMDK2410;
    /* adress of boot parameters */
    gd->bd->bi_boot_params = 0x30000100;
    icache_enable();
    dcache_enable();
    return 0;
}
interrupt_init()函数定义于cpu/arm920t/s3c24x0/interrupts.c第55行:
int interrupt_init (void)
{
    S3C24X0_TIMERS * const timers = S3C24X0_GetBase_TIMERS();
    /* use PWM Timer 4 because it has no output */
    /* prescaler for Timer 4 is 16 */
    timers->TCFG0 = 0x0f00;
    if (timer_load_val == 0)
    {
        /*
         * for 10 ms clock period @ PCLK with 4 bit divider = 1/2
         * (default) and prescaler = 16. Should be 10390
         * @33.25MHz and 15625 @ 50 MHz
         */
        timer_load_val = get_PCLK()/(2 * 16 * 100);
    }
    /* load value for 10 ms timeout */
    lastdec = timers->TCNTB4 = timer_load_val;
    /* auto load, manual update of Timer 4 */
    timers->TCON = (timers->TCON & ~0x0700000) | 0x600000;
    /* auto load, start Timer 4 */
    timers->TCON = (timers->TCON & ~0x0700000) | 0x500000;
    timestamp = 0;
    return (0);
}
                                    4.初始化环境变量
                                    环境变量在通用初始化函数里面,已经初始化一次(env_init),这里调用env_relocate对环境变量进行重新定位。在我的另一篇文章”U-BOOT ENV 实现”中有对环境变量实现的讨论。
                                    
                                    5.进入主循环
                                    当然start_armboot除了以上工作外,还完成其它的初始化工作,具体参考lib_arm/board.c,在一切准备就绪之后,就进入u-boot的主循环:
                                    416 for (;;) {
                                                  main_loop ();
                                           }
                                    main_loop的代码比较长,基本是就是执行用户的输入命令。
                                    
                                    
                                    
                                    
                                
                           
                        
                        
                    
               
            
            
        
   

   
        
            下内容来自笔者在中国Linux论坛Linux嵌入技术讨论区的张贴:[color="#ffffff"]x`"m
[color="#ffffff"]©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地  Uw/%#*
--------------------------------------------------------------------------------[color="#ffffff"]"
aaronwong: u-boot中代码的疑问(_armboot_start与_start)?[color="#ffffff"]12Gm
---------------------------[color="#ffffff"]=j
我使用的是u-boot-1.3.0-rc2。在cpu/pxa/start.S中,有如下的标号定义: [color="#ffffff"]w'
_TEXT_BASE: [color="#ffffff"]7B
.word TEXT_BASE /*uboot映像在SDRAM中的重定位地址,我设置为0xa170 0000 */ [color="#ffffff"]k&BnQf
[color="#ffffff"]©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地  A%
.globl _armboot_start [color="#ffffff"]/'b&%
_armboot_start: [color="#ffffff"]50m B8
.word _start /*_start是程序入口,链接完毕它的值应该是0xa170 0000=TEXT_BASE*/ [color="#ffffff"]2inlX
/* 这句话的意思应该是在_armboot_start标号处,保存了_start的值,也就是说,_armboot_start是存放_start的地址,该地址对应的存储单元内容是0xa170 0000*/ [color="#ffffff"]~1
/* [color="#ffffff"]©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地  [-S(
* These are defined in the board-specific linker script. 下面的定义与上面应该是一个意思。 [color="#ffffff"]y1sDB
*/ [color="#ffffff"]©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地  ?Ud=F}
.globl _bss_start [color="#ffffff"]W8
_bss_start: [color="#ffffff"]4V1kfj
.word __bss_start [color="#ffffff"]5`
====================== [color="#ffffff"]XM
按照上面的理解,__bss_start是uboot 的bss段起始地址,那么uboot映像的大小就是__bss_start - _start;在relocate代码段中计算uboot的大小时,也体现了这一点。 [color="#ffffff"]fHK'f0
实际上,_armboot_start并没有实际意义,它只是在"ldr r2, _armboot_start"中用來寻址_start的值而已,_bss_start也是一样的道理,真正有意义的应该是_start和 __bss_start本身。 [color="#ffffff"];{I
但是,令我不解的是,在C入口函数start_armboot()中(对应文件为lib_arm/board.c),有如下代码: [color="#ffffff"]=-yz!
void start_armboot (void) [color="#ffffff"]6#F[C
{ [color="#ffffff"]©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地  dCb
......... [color="#ffffff"]*=
gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t)); //第一句话 [color="#ffffff"]7
.......... [color="#ffffff"]xfw,,
monitor_flash_len = _bss_start - _armboot_start; //第二句话 [color="#ffffff"]=r1m,
............... [color="#ffffff"]=cN^x+
mem_malloc_init (_armboot_start - CFG_MALLOC_LEN); //第三句话 [color="#ffffff"]W
.......... [color="#ffffff"]?
} [color="#ffffff"]©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地  u
============================================== [color="#ffffff"]v#HG/

照上面的理解,_armboot_start与_bss_start都是没有实际意义的,它们只是一个地址,有实际意义的是地址中的内容_start和
__bss_start(虽然也还是地址)。象第一句话,其“意图”很明显,是把gd作为全局数据结构体的指针,并初始化为“SDRAM中的uboot起
始地址(即TEXT_BASE)-CFG_MALLOC_LEN-全局数据结构体大小”。 [color="#ffffff"]
1197: a171b070 0 NOTYPE GLOBAL DEFAULT ABS __bss_start [color="#ffffff"]m[
1142: a1700000 0 NOTYPE GLOBAL DEFAULT 1 _start [color="#ffffff"]QNr+Pc
1197: a171b070 0 NOTYPE GLOBAL DEFAULT ABS __bss_start [color="#ffffff"]+=
[color="#ffffff"]©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地  E-Y>
我想: [color="#ffffff"]FCNh{M
_start所在的地址是a1700000, [color="#ffffff"]eHEsMt
_armboot_start 所在的地址是a1700044, [color="#ffffff"]?
那么 根据这句: [color="#ffffff"]7.Iy
_armboot_start: .word _start [color="#ffffff"]} u-boot.s [color="#ffffff"]*{|(q#
并提取了monitor_flash_len = _bss_start - _armboot_start;这条语句相关的反汇编代码如下: [color="#ffffff"]\
============================== [color="#ffffff"]^o#c7
a1700044 : [color="#ffffff"]b?
a1700044: a1700000 .word 0xa1700000 [color="#ffffff"]e{Zn
[color="#ffffff"]©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地  >
a1700048 : [color="#ffffff"]l#"{w
a1700048: a171b070 .word 0xa171b070 [color="#ffffff"]U3sK
[color="#ffffff"]©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地  !.
a171b070 : [color="#ffffff"]R
a171b070: 00000000 .word 0x00000000 [color="#ffffff"]Q^$
[color="#ffffff"]©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地  MeE9
..... [color="#ffffff"]m4
a1700f40: e59f41d0 ldr r4, [pc, #464] ; a1701118  [color="#ffffff"]lF-4
//r4=[a1701118]=a1700044 [color="#ffffff"]2/NL_;
..... [color="#ffffff"]EW0Th
a1700f7c: e59f3198 ldr r3, [pc, #408] ; a170111c  [color="#ffffff"][T4Uwy
//r3=[a1700044]=a1700048 [color="#ffffff"]D
a1700f80: e5942000 ldr r2, [r4] [color="#ffffff"]2/0N0
//r2=[a1700044]=a1700000 [color="#ffffff"]mV
a1700f84: e59f4194 ldr r4, [pc, #404] ; a1701120  [color="#ffffff"]bWFU
//r4=[a1701120]=a1719d24 [color="#ffffff"]#Bnq
a1700f88: e5933000 ldr r3, [r3] [color="#ffffff"]*
//r3=[a1700048]=a171b070 [color="#ffffff"] [color="#ffffff"]f1XV
//r2=[a1701124]=a171b070 [color="#ffffff"]}
a1700f94: e5823000 str r3, [r2] [color="#ffffff"]h`lC]
//monitor_flash_len=[r2]=r3=1b070 [color="#ffffff"]mJT:HJ
...... [color="#ffffff"]=op4
[color="#ffffff"]©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地  9
a1701118: a1700044 .word 0xa1700044 [color="#ffffff"]Z0
a170111c: a1700048 .word 0xa1700048 [color="#ffffff"]fr3g(
a1701120: a1719d24 .word 0xa1719d24 [color="#ffffff"]EpcDe
a1701124: a171b070 .word 0xa171b070 [color="#ffffff"]XT&
======================================== [color="#ffffff"]:
上面//是我自己的注释。这表明,你的理解的确是正确的。 [color="#ffffff"]:}6
经过这个过程之后,我终于认识到自己的误解在哪里了。原来,我是把"汇编语言中LDR伪指令对符号的引用"与"C语言中对汇编程序中符号/常量/变量的引用"搞混淆了。我想说明以下几点:[color="#ffffff"]`[I
[color="#ffffff"]©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地  WY
(1) readelf以及u-boot.map和System.map所给出的符号表中符号的值,实际上是表示符号所在的地址,而不是指符号本身的值。 [color="#ffffff"]E?F'R
[color="#ffffff"]©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地  u
(2) 汇编语言中没有指针的概念,因此对符号的引用是"赤裸裸"的。例如: [color="#ffffff"]M"
========== [color="#ffffff"]wM
.globl _armboot_start [color="#ffffff"]J%
_armboot_start: .word _start [color="#ffffff"]d_
ldr r2, _armboot_start [color="#ffffff"]Kf
========== [color="#ffffff"],
实际上反汇编以后是: [color="#ffffff"]466
============ [color="#ffffff"];/g-oE
a1700044 : [color="#ffffff"]}b
a1700044: a1700000 .word 0xa1700000 [color="#ffffff"]R
a1700074: e51f2038 ldr r2, [pc, #-56] ; a1700044  [color="#ffffff"]b b}/4
============ [color="#ffffff"][7A
也就是说,_armboot_start是一个地址0xa1700044,其中的内容是0xa1700000,上面对_armboot_start的引用是直接将其替换为其表示的地址0xa1700044,而非其中的内容0xa1700000。这就是"赤裸裸"的引用。 [color="#ffffff"]m
[color="#ffffff"]©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地  )bR;
(3)
C语言则不同,对变量/符号/常量的引用必须要通过地址来寻址,不管是全局变量还是局部变量,不同的是局部变量在生命期结束后,所占的地址空间会被释放而
已。即使是函数调用时的参数传递,虽然是将实参的值"拷贝"给形参,但"拷贝"的过程也是通过实参和形参的地址来对两者进行访问的。 [color="#ffffff"]p

以,在C语言中的 "monitor_flash_len = _bss_start - _armboot_start"
这句话中对_armboot_star的引用,实际上是把它用作了指针,把它作为访问对象的地址来使用,通过这个地址即a1700044
来访问对应存储空间所存放的内容亦即0xa1700000,_bss_start也是同样的道理。所以这句话实际上是monitor_flash_len
=[a1700048]-[0xa1700044]=a171b070-a1700000 = 1b070,这样就得到了正确的结果。 [color="#ffffff"]eNe#ij
[color="#ffffff"]©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地  MnK-47

在,我们再回答最前面的问题:_start的值是什么?_start表示地址0xa1700000
,在汇编语言中,对_start的"绝对引用"(这里是与用相对寻址进行跳转进行区别)就是将其替换为0xa1700000,但其中存放的内容的的确确就
是"b
reset"这条指令的机器码,所以如果在C语言中引用_start,得到的结果反而就是这个指令的机器码了。其实这个问题很简单,只是和C语言的引用搅
在一起,一些概念被偷换了而已。 [color="#ffffff"]*
        
   

               
               
               
               
                由汇编部分转入C语言后第一个执行这个文件中的start_armboot ()函数。
部分代码分析如下:
typedef int (init_fnc_t) (void);   // 定义函数类型
// 下面为初始化函数定义
init_fnc_t *init_sequence[] = {
cpu_init,    // cp/pxa/cpu.c文件, 执行CPU相关的初始化.
board_init,   // board/psbec270/board.c文件, 执行board相关的初始化.
interrupt_init,  // cp/pxa/interrupt.c文件, 中断初始化, 一般不需要使用中断.
env_init,    // 环境变量初始化
init_baudrate,  /* initialze baudrate settings */
serial_init,   /* serial communications setup */
console_init_f,  /* stage 1 init of console */
display_banner,  /* say that we are here */
dram_init,   /* configure available RAM banks */
display_dram_config,
#if defined(CONFIG_VCMA9)
checkboard,
#endif
NULL,
};
// 在start_armboot()函数中实现如下代码, 用于执行上面定义的初始函数.
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
  if ((*init_fnc_ptr)() != 0) {
   hang ();
  }
}
接下来就是一些系统中用到的环境,变量等初始化,最后进入主循环, 下面是具体分析:
typedef int (init_fnc_t) (void);
init_fnc_t *init_sequence[] = {
cpu_init,    /* basic cpu dependent setup */
board_init,   /* basic board dependent setup */
interrupt_init,   /* set up exceptions */
env_init,    /* initialize environment */
init_baudrate,  /* initialze baudrate settings */
serial_init,   /* serial communications setup */
console_init_f,  /* stage 1 init of console */
display_banner,  /* say that we are here */
dram_init,   /* configure available RAM banks */
display_dram_config,
NULL,
};
// 全局数据结构信息
typedef struct global_data {
bd_t  *bd;       // 开发板相关参数
unsigned long flags;
unsigned long baudrate;   // 串行口通讯速率
unsigned long have_console;  // console_init_f()中使用控制台
unsigned long reloc_off;   // Relocation Offset
unsigned long env_addr;   // Address  of Environment struct
unsigned long env_valid;   // Checksum of Environment valid?
unsigned long fb_base;    // base address of frame buffer
void  **jt;       // jumptable_init()初始化
} gd_t;
// 开发板相关的信息
typedef struct bd_info {
    int bi_baudrate;      // serial通讯接口的速率
    unsigned long bi_ip_addr;   // 本机IP地址
    unsigned char bi_enetaddr[6];   // MAC地址
    struct environment_s *bi_env;   // 环境变量
    ulong bi_arch_number;    // 开发板ID
/*
   该变量标识每一种开发板相关的ID号, 对于本系统来说:
gd->bd->bi_arch_number = MACH_TYPE_MAINSTONE;
  该值将传递给内核, 如果这个参数与内核配置的不相同, 那么内核启动解压缩完成后将出现”Error: a”错误, 提示用户这个是体系结构参数传递的不正确. 由于本开发板内核是从Intel的mainstone开发板内核修改而来, 内部的配置都是使用的MAINSTONE开发板的参数, 故这里将ARCH设置为MACH_TYPE_MAINSTONE.
*/
    ulong bi_boot_params;   // Uboot传递给linux内核的参数保存地址
/*
该变量在board/psbec270/psbec270.c中的int board_init(void)中赋值, 这个值的定义是:
gd->bd->bi_boot_params = 0xA0000100;
该变量保存了Uboot传递给linux的参数的地址, 在linux的引导过程中,
head.s文件中没有对传递进来的参数进行处理, 在init/main.c文件中的
start_kernel函数中, 进行解析.
*/
    struct       // RAM configuration
    {
  ulong start;
  ulong size;
    } bi_dram[CONFIG_NR_DRAM_BANKS];
     /*
      SDram设置, 可以存在多个bank, 由宏定义
    CONFIG_NR_DRAM_BANKS决定.
      board/psbec270/psbec270.c文件中dram_init()执行RAM初始化
       int dram_init(void)
    gd->bd->bi_dram[0].start = PHYS_SDRAM_1;
    gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;
     */
} bd_t;
void start_armboot (void)
{
DECLARE_GLOBAL_DATA_PTR;
// #define DECLARE_GLOBAL_DATA_PTR  register volatile gd_t *gd asm ("r8")

ulong size;
init_fnc_t **init_fnc_ptr;
char *s;
// gd分配内存在空闲区(直接地址引用)
gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
/* compiler optimization barrier needed for GCC >= 3.4 */
__asm__ __volatile__("": : :"memory");
// 给gd分配内存, 这里是在uboot使用的前面, 属于空闲内存.
memset ((void*)gd, 0, sizeof (gd_t));   
// bd分配内存在空闲区, gd的低端(直接地址引用)
gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));  
memset (gd->bd, 0, sizeof (bd_t));
monitor_flash_len = _bss_start - _armboot_start;// 受监控flash空间大小

// 初始化函数, 见上面定义的函数数组, 其中有几个是开发板相关的.
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
  if ((*init_fnc_ptr)() != 0) {
   hang ();
  }
}
/* configure available FLASH banks */
size = flash_init ();    // flash初始化
display_flash_config (size);
/* armboot_start is defined in the board-specific linker script */
// malloc使用的内存空间
mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);
// 如果有nandflash的话就在下面的代码中进行初始化.
#if (CONFIG_COMMANDS & CFG_CMD_NAND)
puts ("NAND:");
nand_init();  /* go init the NAND */
#endif
env_relocate (); // 执行环境初始化

gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr"); // 环境变量中获得本机IP

// 获得Mac地址
{
  int i;
  ulong reg;
  char *s, *e;
  uchar tmp[64];
  i = getenv_r ("ethaddr", tmp, sizeof (tmp));
  s = (i > 0) ? tmp : NULL;
  for (reg = 0; reg  6; ++reg) {
   gd->bd->bi_enetaddr[reg] = s ? simple_strtoul (s, &e, 16) : 0;
   if (s)
    s = (*e) ? e + 1 : e;
  }
}
devices_init ();   // 设备初始化
jumptable_init ();   // 给gd->jt分配内存, 然后加入相关的执行函数
console_init_r ();   // fully init console as a device
enable_interrupts ();
/* Initialize from environment */
if ((s = getenv ("loadaddr")) != NULL) {
  load_addr = simple_strtoul (s, NULL, 16);
}
if ((s = getenv ("bootfile")) != NULL) {
  copy_filename (BootFile, s, sizeof (BootFile));
}
board_late_init ();
/* main_loop() can return to retry autoboot, if so just run it again. */
for (;;) {    // 进入主循环后等待命令
  main_loop ();
}
/* NOTREACHED - no way out of command loop except booting */
}
               
               
               

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u2/72848/showart_1205300.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP