uboot啓動第二階段

ubootC語言代碼入口函數(start_armboot)的註釋

發佈時間:2008-11-21 17:05   作者: 小郭同志  信息來源: PHPChina 開源社區門戶

在介紹該函數之前,我們需要看一看幾個數據結構,這些是u-boot中幾個重要的數據結構:
1
)、gd_t該數據結構保存了u-boot需要的配置信息(我暫時稱它爲全局信息表)
typedef    struct    global_data {
    bd_t        *bd; //
與板子相關的結構,見下面
    unsigned long    flags;
    unsigned long    baudrate;   //
波特率
    unsigned long    have_console;    /* serial_init() was called */
    unsigned long    reloc_off;    /* Relocation Offset,
重定位偏移 */
    unsigned long    env_addr;    /* Address  of Environment struct ,
存放環境變量結構的地址
*/
    unsigned long    env_valid;    /* Checksum of Environment valid? */
#ifdef CONFIG_VFD  //
我們一般沒有配置這個,這個是frame buffer的首地址

    unsigned long    fb_base;    /* base address of frame buffer,
顯存緩存區 基址*/
#endif
#if 0
unsigned long cpu_clk; /* CPU clock in Hz! CPU
的時鐘頻率
*/
unsigned long bus_clk; //
總線的時鐘頻率

unsigned long ram_size; /* RAM size, RAM
的大小*/
unsigned long reset_status; /* reset status register at boot */
#endif
void  **jt;  /* jump table ,
保存着些函數的入口地址,common/Exports.c中進行填充
*/
} gd_t;
2
)、bd_t 保存與板子相關的配置參數

typedef struct bd_info {
    int            bi_baudrate;    /* serial console baudrate ,
串口波特率 */
    unsigned long    bi_ip_addr;    /* IP Address ,IP
地址
*/
    unsigned char    bi_enetaddr[6]; /* Ethernet adress ,
以太網地址
*/
    struct environment_s           *bi_env; //
環境變量地址指針

    ulong            bi_arch_number;    /* unique id for this board  
架構號碼*/
    ulong            bi_
boot_params;    /* where this board expects params */
    struct                /* RAM configuration */
    {
    ulong start; //RAM
的起始地址
    ulong size; //RAM
的大小
    }             bi_dram[CONFIG_NR_DRAM_BANKS];
} bd_t;
3).
初始化函數列表(以數組的形式)
init_fnc_t *init_sequence[] = {
cpu_init,   /* basic cpu dependent setup  || cpu/arm920t/cpu.c ,cpu
的初始化,有待於分析
*/
//
這個是對板子的初始化
,
board_init,   /* basic board dependent setup|| board/smdk2440/smdk2440.c */
interrupt_init,  /* set up exceptions || cpu/arm920t,s3c24x0/interrupts.c */
env_init,   /* initialize environment */
init_baudrate,  /* initialze baudrate settings */
serial_init,  /* serial communications setup || cpu/arm920t/s3c24x0/serial.c */
//
串口初始化後我們就可以打印信息了

console_init_f,  /* stage 1 init of console */
display_banner,  /* say that we are here */
#if defined(CONFIG_DISPLAY_CPUINFO)
print_cpuinfo,  /* display cpu info (and speed) */
#endif
#if defined(CONFIG_DISPLAY_BOARDINFO)
checkboard,  /* display board info */
#endif
dram_init,  /* configure available RAM banks */
display_dram_config,
NULL,
};
//===========================================
//===========================================
int cpu_init (void) //cpu/arm920t/Cpu.c
中的函數

{
/*
  * setup up stacks if necessary
  */
//
這裏只是做了對中斷棧和快速中斷棧空間地址的定義
//IRQ_STACK_START
FIQ_STACK_START 的值在start.S的開始幾行中有定義
//
其中的那個 -4 操作是難道是爲PC跳轉留的一個地址???
#ifdef CONFIG_USE_IRQ
IRQ_STACK_START = _armboot_start - CFG_MALLOC_LEN - CFG_GBL_DATA_SIZE - 4;
FIQ_STACK_START = IRQ_STACK_START - CONFIG_STACKSIZE_IRQ;
#endif
return 0;
}

//===================================================
//===================================================
//---------mem_malloc_init----------------
//  
參數: malloc內在區的起始地址

//  
功能: 完成malloc函數所要用到的靜

//   
態變量的初始化.
//
返回值:  

//----------------------------------------
static
void mem_malloc_init (ulong dest_addr)
{
mem_malloc_start = dest_addr; //
緩衝區起始地址

mem_malloc_end = dest_addr + CFG_MALLOC_LEN; //
緩衝區結束地址

mem_malloc_brk = mem_malloc_start; //
已使用塊的地址,初始時應指向起始地址

memset ((void *) mem_malloc_start, 0, //
把這段空間初始化爲
0
   mem_malloc_end - mem_malloc_start);
}

......

init_fnc_t *init_sequence[] = {
//
該函數只是做了對中斷棧和快速中斷棧空間地址的定義

cpu_init,   /* basic cpu dependent setup  || cpu/arm920t/cpu.c */

//
完成各時鐘和端口還有gd中的兩個成員的初始化
board_init,   /* basic board dependent setup|| board/smdk2440/smdk2440.c */

//PWM(Pulse Width Modulation
脈寬調製器) TIMER的初始化
,
interrupt_init,  /* set up exceptions || cpu/arm920t,s3c24x0/interrupts.c */

//
環境的初始化,沒深入分析

env_init,   /* initialize environment */

//
初始化波特率,並寫進gd的成員變量中

init_baudrate,  /* initialze baudrate settings */

//
串口初始化後我們就可以打印信息了

serial_init,  /* serial communications setup || cpu/arm920t/s3c24x0/serial.c */

console_init_f,  /* stage 1 init of console */

//
打印一些信息

display_banner,  /* say that we are here */
#if defined(CONFIG_DISPLAY_CPUINFO)
print_cpuinfo,  /* display cpu info (and speed) */
#endif
#if defined(CONFIG_DISPLAY_BOARDINFO)
checkboard,  /* display board info */
#endif
//DRAM
的初始化,這裏只是對gd中的 bi_dram結構中的兩個成員賦值
,
//
也即BANK的起始地址和大小

dram_init,  /* configure available RAM banks */

//
打印BANK的相關信息

display_dram_config,
NULL, //
用以標識列表數組的結束
};
//------------start_armboot--------------------
//
功能: 完成uboot第二階級的一系列的

//  
硬件初始化工作, 然後轉入main函數.
//
備註: 該函數是C程序的入口函數,從彙編語

//  
言跳轉到此 .
//---------------------------------------------
void start_armboot (void)
{
    init_fnc_t **init_fnc_ptr; //init_fnc_t
是各初始化函數的數組

    char *s;
#ifndef CFG_NO_FLASH
    ulong size;
#endif
#if defined(CONFIG_VFD) || defined(CONFIG_LCD)
    unsigned long addr;
#endif
  /* Pointer is writable since we allocated a register for it
      gd_t:
定義在 /include/asm-arm/Global_data.h,包含一些全局通用的變量
.
    _armboot_start:
代碼的起始地址,它定義在start.S中的前幾行中,定義爲 _start

   
當系統第一次加電時,指令是從0x0地址開始執行的,所以此時的 _start
   
應爲0x0;而當uboot經過代碼重定位後,指令會從 _TEXT_BASE 處開始執行,
   
此時的 _start值就成了 _TEXT_BASE的值
.
   CFG_MALLOC_LEN:
/include/configs/smdk2440.h中有定義,該變量表示供

   malloc
函數使用的內存池空間,代碼中定義值爲:0x10000+128*1024
|-------|<--- _armboot_start
基址

|  4    |  
|-------|<--- malloc
函數池基址

|  3    |
|-------|<--- (gd_t)gd(
全局變量表)基址

|  2    |
|-------|<--- (bt_t)bd(
板卡信息表)基址
|  1    |
  -------
   4
就是爲malloc函數預留的數據空間
   3
是全信息表gd的數據區

2
是板卡信息表bd的數據區
網上找了個圖片,更能反應這個空間的分配關係
:

*/
     //
分配區域 3 gd ,gd是一個全局靜態變量
    gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));

    /* compiler optimization barrier needed for GCC >= 3.4 */
    __asm__ __volatile__("": : :"memory");
//
gd變量的內容填充爲0 ,填充 3 區的數據爲0 ,即初始化gd.注意:這裏並沒有

//
初始化bd,gd表中的bd成員只是一個指針,因爲對初始化的是個指針地址
    memset ((void*)gd, 0, sizeof (gd_t));
/*
  bd_t
結構體在/include/asm-arm/U-boot.h中定義, 定義板子的一些信息,包括
:
  
波特率,IP地址, 以太網地址, 架構編碼,啓動參數 ,BANK的起始地址和大小等

*/
//
分配區域 2 bd, bd的基址 = gd的基址 - bd的尺寸

    gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));

//
把區域 2 填充爲 0 ,即初始化 bd

    memset (gd->bd, 0, sizeof (bd_t));
/*monitor_falsh_len
定義在
/lib_arm/Board.c

bin文件中 BSS 段和 TEXT 段和 DATA 段存放的順序同前向後依次是:
  TEXT(
代碼段 RO)    DATA(已初始化數據段 RW)   BSS(未初始化數據段
ZI)
所以 _bss_start 的基址等於 TEXT的長度加上DATA的長度
.
: _bss_start(BSS段基址) = 代碼段長度+數據段長度


BSS(Block Started by Symbol)
段是未被初始化的數據段,是存放程序中

  
未被初始化的全局變量的一塊內存區域,初始化時應清零;該段只有
  
名稱和大小卻沒有值;該段不包含任何數據,只是簡單的維護開始和
  
結束的地址,以便內存區能在運行時被有效地清零,它在應用程序的
  
映像文件(ARM中也即bin文件)中並不存在.
text :
代碼段,是包含程序代碼的段

dat  :
已經初始化的數據段,保存已經初始化的全局變量.

   
在嵌入式系統中,bin文件(又稱Image文件)中只包含textdata
,
  
bss段不在其中,它是由系統初始化爲零
.
    */
  //_armboot_start
start.S中定義爲_start,_start爲代碼的起始地址

    //
只包含 RO(TEXT) RW(DATA) .重定位前的值爲0x0,此時指向
flash,
//
重定位後則指向RAM中的某一地址

//
由此可以知道:  _bss_start - _armboot_start 的值即是在第一階段從
//flash
中重定位到RAM中的那部分代碼的長度,也即可TEXTDATA數據段,
//
這個值與start.S中的重定位那部分代碼所計算的值是相等的

//
所以,monitor_flash_len表示從flash中搬來的代碼的長度
    monitor_flash_len = _bss_start - _armboot_start;  //_bss_start
u-boot.lds中定位


//
各設置的初始化.當返回值不爲0時表示初始化失敗 ,此時會調用 hang()函數

//
打印一錯誤提示信息,然後進入死循環
    for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
        if ((*init_fnc_ptr)() != 0) {
            hang ();
        }
    }
//CFG_NO_FLASH
表示沒有flash,如果沒定義該常量則表示板子上有flash,此時調用flash_init()對其進行初始化
.
#ifndef CFG_NO_FLASH
    /* configure available FLASH banks */
    size = flash_init ();
    display_flash_config (size); //
打印flash的信息,這裏僅輸出它的大小

#endif /* CFG_NO_FLASH */
#ifdef CONFIG_VFD
#   ifndef PAGE_SIZE
#     define PAGE_SIZE 4096
#   endif
     /*
      * reserve memory for VFD display (always full pages)
      */
    /* bss_end is defined in the board-specific linker script */
    addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1); //???
    size = vfd_setmem (addr);
    gd->fb_base = addr;
#endif /* CONFIG_VFD */
#ifdef CONFIG_LCD
# ifndef PAGE_SIZE
#   define PAGE_SIZE 4096
# endif
     /*
      * reserve memory for LCD display (always full pages)
      *///
LCD分配RAM(內存)空間

     /* bss_end is defined in the board-specific linker script */
    addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
    size = lcd_setmem (addr);
    gd->fb_base = addr; //
爲顯存緩衝區地址變量賦值

#endif /* CONFIG_LCD */
/* armboot_start is defined in the board-specific linker script */
//malloc
函數使用緩衝區的初始化

    mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);  
//
如果定義了命令和NAND命令,則初始化nand
#if (CONFIG_COMMANDS & CFG_CMD_NAND)
    puts ("NAND:  ");
    nand_init();  /* go init the NAND */
#endif
#ifdef CONFIG_HAS_DATAFLASH
    AT91F_DataflashInit();
    dataflash_print_info();
#endif
     /* initialize environment
環境的初始化,代碼在common/env_common.c
*/
    env_relocate ();
#ifdef CONFIG_VFD
     /* must do this after the framebuffer is allocated */
    drv_vfd_init();
#endif /* CONFIG_VFD */
     /* IP Address
爲全局變量的成員賦值:IP地址
*/
    gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");//ipaddr
smdk2440.h中的CONFIG_IPADDR中出現,應該是該常量

/* MAC Address *///
高處MAC地址 ,並賦給gd的成員變量

{
    int i;
    ulong reg;
    char *s, *e;
    char 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;
}
#ifdef CONFIG_HAS_ETH1
  i = getenv_r ("eth1addr", tmp, sizeof (tmp));
  s = (i > 0) ? tmp : NULL;
  for (reg = 0; reg < 6; ++reg) {
   gd->bd->bi_enet1addr[reg] = s ? simple_strtoul (s, &e, 16) : 0;
   if (s)
    s = (*e) ? e + 1 : e;
  }
#endif
}
//
這個函數涉及好多,我沒深入分析,若哪位分析了希望能分享一下:[email protected],將不勝感激

devices_init (); /* get the devices list going. */
#ifdef CONFIG_CMC_PU2
load_sernum_ethaddr ();
#endif /* CONFIG_CMC_PU2 */
//
初始化跳轉表,gd中的jt(函數跳轉表)數組進行初始化,其中保存着一些函數的入口地址

jumptable_init ();
  
console_init_r (); /* fully init console as a device
我沒具體分析內部實現
*/
#if defined(CONFIG_MISC_INIT_R)
/* miscellaneous platform dependent initialisations, miscellaneous:
各色各樣混在一起, 混雜的
*/
misc_init_r ();
#endif
/* enable exceptions
設置cpsrIF位以充許中斷
*/
enable_interrupts ();
/* Perform network card initialisation if necessary */
#ifdef CONFIG_DRIVER_CS8900
cs8900_get_enetaddr (gd->bd->bi_enetaddr);
#endif
#if defined(CONFIG_DRIVER_SMC91111) || defined (CONFIG_DRIVER_LAN91C96)
if (getenv ("ethaddr")) {
  smc_set_mac_addr(gd->bd->bi_enetaddr);
}
#endif /* CONFIG_DRIVER_SMC91111 || CONFIG_DRIVER_LAN91C96 */
/* Initialize from environment */
if ((s = getenv ("loadaddr")) != NULL) {
  load_addr = simple_strtoul (s, NULL, 16);
}
#if (CONFIG_COMMANDS & CFG_CMD_NET)
if ((s = getenv ("bootfile")) != NULL) {
  copy_filename (BootFile, s, sizeof (BootFile));
}
#endif /* CFG_CMD_NET */
#ifdef BOARD_LATE_INIT
board_late_init ();
#endif
#if (CONFIG_COMMANDS & CFG_CMD_NET)
#if defined(CONFIG_NET_MULTI)
puts ("Net:   ");
#endif
eth_initialize(gd->bd);
#endif
/* main_loop() can return to retry autoboot, if so just run it again. */
//
直接進入main_loop 該函數在common/main.c

//!!!!!!!!
至此,硬件初始化完成
for (;;) {
  main_loop ();
}

/* NOTREACHED - no way out of command loop except booting */
}

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章