bootloader(xboot)起動流程分析

1.   引言

      本文先引用一段《Boot Loader 技術內幕》中的一段話來簡介bootloader基本概念,再
通過xboot的實例分析來分析xboot接口。

作者: http://blog.csdn.net/dyron歡迎大家來討論相關內容。

2.  BootLoader 的概念

      簡單地說,Boot Loader 就是在操作系統內核運行之前運行的一段小程序。通過這段小
程序,我們可以初始化硬件設備、建立內存空間的映射圖,從而將系統的軟硬件環境帶到一
個合適的狀態,以便爲最終調用操作系統內核準備好正確的環境。

      通常,Boot Loader 是嚴重地依賴於硬件而實現的,特別是在嵌入式世界。因此,在嵌
入式世界裏建立一個通用的 BootLoader 幾乎是不可能的。儘管如此,我們仍然可以對 Boot
Loader 歸納出一些通用的概念來,以指導用戶特定的 Boot Loader 設計與實現。

1. Boot Loader 所支持的 CPU 和嵌入式板

      每種不同的 CPU 體系結構都有不同的 Boot Loader。有些 Boot Loader 也支持
多種體系結構的 CPU,比如 U-Boot 就同時支持 ARM 體系結構和MIPS 體系結構。
除了依賴於 CPU 的體系結構外,Boot Loader 實際上也依賴於具體的嵌入式板級設
備的配置。這也就是說,對於兩塊不同的嵌入式板而言,即使它們是基於同一種 CPU
而構建的,要想讓運行在一塊板子上的 Boot Loader 程序也能運行在另一塊板子上,
通常也都需要修改 Boot Loader 的源程序。

2. Boot Loader 的安裝媒介(Installation Medium)

      系統加電或復位後,所有的 CPU 通常都從某個由 CPU 製造商預先安排的地址
上取指令。比如,基於 ARM7TDMI core 的 CPU 在復位時通常都從地址 0x00000000
取它的第一條指令。而基於 CPU 構建的嵌入式系統通常都有某種類型的固態存儲設
備(比如:ROM、EEPROM 或 FLASH 等)被映射到這個預先安排的地址上。因此在
系統加電後,CPU 將首先執行 Boot Loader 程序。

下圖1就是一個同時裝有 Boot Loader、內核的啓動參數、內核映像和根文件系統映像
的固態存儲設備的典型空間分配結構圖。


1 固態存儲設備的典型空間分配結構
 

3. 用來控制 Boot Loader 的設備或機制

     主機和目標機之間一般通過串口建立連接,Boot Loader 軟件在執行時通常會通過串口
來進行 I/O,比如:輸出打印信息到串口,從串口讀取用戶控制字符等。

3.   Xboot簡介

     由於我們項目使用了mmc卡做爲主存儲器件,故我們的bootloader前需要存放
mbr信息,故我們的bootloader固件由mbr+bootloader兩部分組成。

Xboot的目錄如下:

boot      mbr.bin             rules.mk
Changelog  mbr-xboot.bin       spl
config.mk  mkcompatibility.sh  x-boot.bin
mkconfig  x-boot.lds          include
msc-spl.bin  x-boot-msc.bin   Makefile
README

Boot目錄下是主要的程序代碼及開發板代碼。

Include 目錄下是程序的頭文件。

Spl 是相應的環境腳本及製作mbr工具代碼。

Xboot的目錄結構相對簡單, x-boot.lds是鏈接腳本。

4.   x-boot彙編啓動分析

     下邊我們就開始分析x-boot的啓動流程, 首先看x-boot.lds文件,整個腳本內容
如下,看了腳本就會發現,x-boot還存在u-boot的影子,屬抄襲作品:-) .

OUTPUT_ARCH(mips)
ENTRY(_start)
SECTIONS
{
    . = 0x00000000;

    . = ALIGN(4);
    .text       :
    {
      *(.text)
    }

    . = ALIGN(4);
    .rodata  : { *(.rodata) }

    . = ALIGN(4);
    .data  : { *(.data) }

    . = ALIGN(4);
    .sdata  : { *(.sdata) }

    _gp = ALIGN(16);

    __got_start = .;
    .got  : { *(.got) }
    __got_end = .;

    .sdata  : { *(.sdata) }

    __u_boot_cmd_start = .;
    .u_boot_cmd : { *(.u_boot_cmd) }
    __u_boot_cmd_end = .;

    uboot_end_data = .;
    num_got_entries = (__got_end - __got_start) >> 2;

    . = ALIGN(4);
    .sbss  : { *(.sbss) }
    .bss  : { *(.bss) }
    uboot_end = .;
} 

       在24行我們發現,ENTRY(_start)指定入口爲_start,  接入尋找導出_start符號的文件,
發現是在boot/init/init.S中導出。

1 /*
 2  *  Startup Code for the X-Boot.
 3  *
 4  *  Copyright (c) 2005 - 2009   Ingenic Semiconductor Corp.
 5  *
 6  */
  7#include <config.h>
  8#include <regdef.h>
  9
 10    .set noreorder
 11
 12    .globl _start
 13    .text
 14_start:
 15    la  sp, TEXT_BASE       // set sp
 16     j   boot_main       // jump to c routine
 17    nop

     在init.S文件中的16行發現, 這段代碼最終跳轉到boot_main符號處執行c代碼, 最

終跳到./boot/common/boot_main.c中的boot_main 函數開始執行, 這就進入了我們的C程序。

5.      xboot的C啓動流程

     上章說到我們的pc指針進入了boot_main函數, 首先在738-761行設置開發板必要的
GPIO。 然後設置鍵盤及必要的gpio引腳。

     Xboot的啓動相對簡單,初始化引腳後,初始化MMC卡,進行檢測開機邏輯, 都是各
廠商自己定製的內容,主要就是選擇進入什麼樣的kernel模式,而進入kernel模式的選擇
有多種,這裏主要介紹一下通過內核參數來選擇的方式。

6.      內核的參數傳遞

這裏以BOOT_NORMAL爲例,分析一下xboot的參數傳遞

下邊是BOOTARGS_NORMAL的定義。

#define BOOTARGS_NORMAL             CONFIG_BOOTARGS
 
#define CONFIG_BOOTARGS        "mem=128M console=ttyS1,57600n8ip=off 
root=/dev/ram0 rw rdinit=/init"
 
在msc_boot.c的第56行,將啓動參數賦值到cmdline中
static u8 cmdline[256] = CFG_CMDLINE;
 
將kernel從MMC拷貝到啓動地址 0x80000000
#define CFG_KERNEL_DST    0x80000000   /* Load Linux kernel to this addr   */
do_msc((u8 *)CFG_KERNEL_DST, offset,size);
 
初始化kernel的啓動參數與及randisk
int init_boot_linux(unsigned char*data_buf, unsigned int data_size)
 
因爲我們的kernel並非正常的zImage, 而是由ramdisk+kernel打包成的boot.img, 故
在boot_linux中進行了分解,並設置了詳細的參數。
 
首先從MMC讀出的buf中讀出2k的bootimginfo, 裏邊包含有kernel的地址大小與
ramdisk的地址大小, 啓動命令行等信息.
memcpy(&bootimginfo, Bulk_Data_Buf,2048);
 
計算出kernel和ramdisk實際佔用的大小。
kernel_actual = (bootimginfo.kernel_size +page_mask) & (~page_mask);
ramdisk_actual = (bootimginfo.ramdisk_size+ page_mask) & (~page_mask);
 
獲得kernel與ramdisk在buf中的實際地址,填充到bootimginfo結構中.
bootimginfo.kernel_addr = (unsignedint)Bulk_Data_Buf;
bootimginfo.ramdisk_addr =bootimginfo.kernel_addr + kernel_actual;
 
從MMC中讀取ramdisk到CFG_RAMDISK_DST處。
do_msc((u8 *)CFG_RAMDISK_DST,ramdisk_offset, ramdisk_actual);
 
填充啓動kernel的參數結構及組成kernel 啓動的cmdline
       param_addr= (u32 *)PARAM_BASE;
       param_addr[0]= 0;/* might be address of ascii-z string: "memsize" */
       param_addr[1]= 0;/* might be address of ascii-z string: "0x01000000" */
       param_addr[2]= 0;
       param_addr[3]= 0;
       param_addr[4]= 0;
       param_addr[5]= PARAM_BASE + 32;//0x80004020
       param_addr[6]= CFG_KERNEL_START;
       tmpbuf= (u8 *)(PARAM_BASE + 32);
 
跳轉到kernel的啓動地址,啓動kernel
#define PARAM_BASE             0x80004000
(*kernel)(2, (char **)(PARAM_BASE + 16), (char*)PARAM_BASE);

     這裏的第1個參數是指傳入參數的個數, 第2個參數是指傳入參數的內存地址, 第3
個參數是指環境變量的內存地址。 (在uboot中傳遞爲4個參數,在xboot中這種實現似
乎不太符合標準。 待深入研究)

7.      本文結束語

     相對於uboot來講,xboot的啓動流程相對非常簡化,命令行工具都去除掉了,但啓動
速度確實大大優於uboot, 這也許就是君正選擇xboot的原因吧,歡迎大家來討論,
[email protected]



8. 補充內容, 回答網友yuenshu fong的問題

xboot的編譯流程

xboot的編譯流程如下:
根據 ALL = $(X_BOOT) $(X_BOOT_NAND) $(X_BOOT_SD) $(X_BOOT_MSC)
首先編譯x-boot.bin
$X_BOOT_NAND   X_BOOT_SD未定義,所以跳過
然後到X_BOOT_MSC: $(X_BOOT_MSC):  $(MSC_SPL) $(X_BOOT) MBR
它依賴於$(MSC_SPL) $(X_BOOT) MBR

先編譯 MSC_SPL, 接着會調用make -s -C spl msc-spl.bin
進入spl/Makefile


msc-spl.bin
$(obj)msc-spl.bin:  $(obj)msc-spl
        $(OBJDUMP) -Dz $< > $(obj)msc-spl.dump
        $(OBJCOPY) ${OBJCFLAGS} -O binary $< $@
        cp msc-spl.bin $(TOPDIR)

#define JZ_MSCBOOT_CFG       0x4d53504c
msc-spl.bin首先依賴於msc-spl, 直接編譯並鏈接__LIBS, 注意這裏調用了LDFLAGS, 又是x-boot.lds, 
這個LDFLAGS內有一個TEXT_BASE, 地址是0x80000200.  


$(obj)msc-spl:      depend $(SUBDIRS) $(OBJS) $(LIBS) $(LDSCRIPT)
        $(LD) $(LDFLAGS) $(__OBJS) \
            --start-group $(__LIBS) --end-group \
            -o $@
現在生成了msc-spl, 返回msc-spl.bin的編譯選項
$(OBJDUMP) -Dz $< > $(obj)msc-spl.dump, 將msc-spl生成msc-spl.dump
$(OBJCOPY) ${OBJCFLAGS} -O binary $< $@   將msc-spl生成msc-spl.bin
cp msc-spl.bin $(TOPDIR)   複製到頂層目錄


X_BOOT在之前已編譯過,暫時跳過
編譯MBR, 使用gcc spl/tools/mbr_creater/mbr_creater.c -o spl/tools/mbr_creater/mbr_creater
spl/tools/mbr_creater/mbr_creater mbr.bin


現在返回
$(X_BOOT_MSC):  $(MSC_SPL) $(X_BOOT) MBR
        $(MAKE) -C spl pad
        cat spl/msc-spl-pad.bin boot/x-boot.bin > x-boot-msc.bin
        cat mbr.bin x-boot-msc.bin > mbr-xboot.bin


現在生成pad, 調用spl/tools/msc-spl-pad.sh生成8k的msc-spl-pad.bin
返回X_BOOT_MSC, 調用cat生成x-boot-msc.bin,再將mbr.bin打進去,就生成了mbr-xboot.bin


mbr-xboot.bin組成格式
mbr[512byte] + msc-spl-pad.bin[8k] + x-boot.bin
msc-spl-pad.bin入口地址是0x80000200, 正好跳過前邊512字節的mbr.

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