上篇文章中,講到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編譯的相關參數做學習,有研究的朋友可以進一步補充。