上篇文章中,讲到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编译的相关参数做学习,有研究的朋友可以进一步补充。