Cortex-A8中bootloader研究(2)

上篇文章中,講到Bootloader執行完成之後,系統運行環境搭建完成,本篇繼續分析後續應用程序的引導流程。

AM335x支持的啓動方式有很多種,包括SPI,UART,NAND FLASH以及SD卡,針對於beaglebone板子,支持UART啓動和SD卡啓動,這裏僅僅分析從SD卡啓動。內容包括從SD卡讀取應用程序bin文件並將其複製到RAM中,確定函數的入口點,啓動應用程序,對這一部分內容做詳細論述。

1. 應用程序的裝載

該功能的實現主要通過ImageCopy()函數實現。

重要的數據結構:

//ti_header結構體,用於存儲應用程序大小及起始地址
    typedef struct _ti_header_
    {
        unsigned int image_size;  
        unsigned int load_addr; 
    }ti_header;
ti_header imageHdr;
// g_cCwdBuf[512],用於裝載應用程序所在路徑,以’/’字符開頭
#define PATH_BUF_SIZE   512
static char g_cCwdBuf[PATH_BUF_SIZE] = "/";
//指向應用程序的文件指針
static  FIL  g_sFileObject;

裝載流程

代碼的層面上來闡述上述實現的具體過程。

(1)目標文件路徑的填充

應用程序放置在SD卡的根目錄下,需要將目錄及文件名賦值給路徑變量空間。

g_cCwdBuf[0] = '/';
g_cCwdBuf[1] = '\0';
/* *賦值到備份空間域* */
strcpy(g_cTmpBuf, g_cCwdBuf);
……
//將應用程序名粘貼到路徑之後,最終路徑爲”/app”
strcat(g_cTmpBuf, "app");

經過上述操作之後,路徑空間中內容爲”/app”。由此可知,複製到SD卡中的應用程序文件名必須爲app,這樣才能被訪問。

(2)文件指針指向該文件

fresult = f_open(&g_sFileObject, g_cTmpBuf, FA_READ);

將路徑g_cTmpBuf指定的文件(這裏爲/app)指向g_sFileObject文件指針,並返回讀取狀態。

(3)裝載地址信息的讀取

fresult = f_read(&g_sFileObject, (unsigned char *)&imageHdr, 8,&usBytesRead);

讀取目標文件前八個字節並放置到imageHdr結構體中。函數形參中將imageHdr指針強制轉化成char型指針用以實現字節爲單位的複製。imageHdr結構體高四位表示目標文件的大小,後四字節表示目標文件將要被裝載到得地址。
問題:這八個字節的信息是如何被放置在應用程序bin文件的高八位的?是不是通過Makefile文件中特別配置編譯產生的?

(4)裝載地址的保存

destAddr = (unsigned char*)imageHdr.load_addr;
entryPoint = imageHdr.load_addr;

這兩種保存是由差別的,destAddr定義爲char*型,該賦值語句首先將unsigned int型的變量imageHdr.load_addr轉換成unsigned char*指針類型,再賦值給destAddr,該變量用於下面的文件賦值,即將目標文件以字節爲單位從SD卡複製到RAM中。後面一個賦值語句直接將地址值imageHdr.load_addr賦值給entryPoint變量,該變量將用於程序的啓動,後面將會看到。

(5)目標文件的複製

該過程將目標文件從SD卡複製到RAM中,使用f_read函數

fresult = f_read(&g_sFileObject, g_cTmpBuf, sizeof(g_cTmpBuf) - 1,&usBytesRead);

一次讀取511個字節集,第512個字節放置’\0’字符,並將其複製到RAM空間,當某次讀取的數量小於511時,即認爲本次讀取已經到了文件結尾,退出該過程。

2. 目標文件啓動

目標文件的啓動有賴於上述裝載地址的保存,由上面可知,應用程序裝載在entryPoint指定的起始地址中,要利用該地址完成程序的啓動,需要以下幾個工作:

//定義一個函數指針
static void (*appEntry)();
//函數指針賦值爲應用程序入口點
appEntry = (void (*)(void)) entryPoint;
//啓動應用程序
(*appEntry)( );

其中第二句,將unsigned int型變量entryPoint轉換成返回值爲空型(前一個void的作用),形參爲空(後一個void的作用)的函數指針,再賦值給函數指針appEntry。第三句直接通過函數指針啓動應用程序,這是一個函數指針在嵌入式系統中的典型例子。至此,bootloader工作完成。

3.bootloader的編譯

bootloader的編譯既可以通過CCS進行,也可以通過Linux環境下的Makefile進行,CCS下的編譯看不到相關的配置,這裏選擇分析Linux環境下的Makefile編譯方式,來研究bootloader的編譯方式和相關配置。

bootloader的Makefile文件如下,紅色爲註釋:

# 定位到starterware的根目錄
ROOT=../../../../../../
#定義設備和開發板版本,用於的路徑搜尋
DEVICE=am335x
EVM=beaglebone
#包含makedefs文件,該文件內定義了相關路徑
include ${ROOT}/build/armv7a/gcc/makedefs
#指定編譯所依賴的文件的路徑,該路徑在上述makedefs中被定義
DIRS=${DRIVERS_BLD} ${PLATFORM_BLD} ${UTILITY_BLD} ${MMCSDLIB_BLD} ${NANDLIB_BLD}
#指定生成文件的文件名
APPDIR=bootloader
APPNAME=boot
#bootloader將要被裝載到得空間,重要的變量
START_ADDR=0x402F0400
#生成文件的文件位置
APP=${ROOT}bootloader/
APP_BIN=${ROOT}/binary/${TARGET}/${COMPILER}/${DEVICE}/${EVM}/$(APPDIR)
#bootloader編譯需要的C文件
COMMON=$(APP)src/bl_main.c\
       $(APP)src/bl_am335x.c\
       $(APP)src/bl_copy.c\
       $(APP)src/bl_pmI2c.c\
       $(APP)src/bl_pmic.c\
       $(APP)src/$(TARGET)/gcc/*.S
#SD卡啓動時需要的C文件
ifeq ($(BOOT), MMCSD)
SOURCE=$(APP)/src/bl_hsmmcsd.c \
       $(FATFS_SRC)/src/ff.c       \
       $(FATFS_SRC)/port/fat_mmcsd.c
endif
#UART啓動時需要的C文件
ifeq ($(BOOT), UART)
SOURCE=$(APP)/src/bl_uart.c \
       ${ROOT}/third_party/xmodem/xmodem.c \
       ${ROOT}/third_party/xmodem/crc16.c
endif

APP_SRC=$(SOURCE) $(COMMON)
#需要的庫文件
APP_LIB=-ldrivers  \
    -lutils    \
    -lplatform \
    -lmmcsd 
#編譯
all: debug release

debug:
    make TARGET_MODE=debug lib
    make TARGET_MODE=Debug bin

release:
    make TARGET_MODE=release lib
    make TARGET_MODE=Release bin

lib:
    @for i in ${DIRS};             \
    do                      \
        if [ -f $${i}/makefile ] ;		    \
		then					  \
			make $(TARGET_MODE) -C $${i} || exit $$?; \
        fi;                    \
    done;
bin:
    $(CC)  $(CFLAGS) $(APP_SRC)
    @mkdir -p $(TARGET_MODE)/
    @mv *.o* $(TARGET_MODE)/
    $(LD) ${LDFLAGS} ${LPATH} -o $(TARGET_MODE)/$(APPNAME).out -Map $(TARGET_MODE)/$(APPNAME).map \
 $(TARGET_MODE)/*.o*  --defsym BOOT_START_ADDR=$(START_ADDR) -T $(APPNAME).lds $(APP_LIB) -lc -lgcc $(APP_LIB) -lc -lgcc 
    @mkdir -p $(APP_BIN)/$(TARGET_MODE)
    @cp $(TARGET_MODE)/$(APPNAME).out $(APP_BIN)/$(TARGET_MODE)/$(APPNAME).out
    $(BIN) $(BINFLAGS) $(APP_BIN)/$(TARGET_MODE)/$(APPNAME).out \
               $(APP_BIN)/$(TARGET_MODE)/$(APPNAME).bin 
    cd $(ROOT)/tools/ti_image/; gcc tiimage.c -o a.out; cd - 
           $(ROOT)/tools/ti_image/a.out $(START_ADDR) $(BOOT) \
               $(APP_BIN)/$(TARGET_MODE)/$(APPNAME).bin \
               $(APP_BIN)/$(TARGET_MODE)/$(APPNAME)_ti.bin; rm -rf $(ROOT)/tools/ti_image/a.out;
#清除Release文件 需要make clean顯式執行
clean:
    @rm -rf Debug Release $(APP_BIN)/Debug $(APP_BIN)/Release
clean+: clean
    @make TARGET_MODE=clean lib

對Makefile的分析,僅僅對bootloader編譯文件依賴方面理解的較爲清除,具體編譯的細節參數,尚未理解透徹,需要對gcc編譯的相關參數做學習,有研究的朋友可以進一步補充。

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