前面我們已經實現了鏡像布局設計,可以燒錄鏡像,并加載鏡像進行啟動。但是每個段都需要單獨燒錄,不是很方便。工欲善其事必先利其器,我們現在來設計一個簡單的工具,打包所有段文件作為一個大的鏡像整體,這樣就可以一起燒錄提高效率。
代碼見:https://github.com/qinyunti/stm32f429-boot.git
按照我們設計的存儲布局,主要是HEAD和段信息,以及后面的段對應的文件。所以我們需要一個配置文件來告訴我們的工具如何去生成HEAD和段信息,如何去拼接段文件。這里我們選擇json文件來作為配置文件,使用cjson開源庫作為解析工具。
我們在原來工程下創建imgpack目錄
mkdir imgpack
cd imgpack/
創建一個imgpack.json的文件
Json結構設計和我們存儲結構對應
對應一個head信息,段信息對應一個數組。
Json文件對應如下,注意json不支持16進制數據,對于vma和lma的16進制地址我們使用字符串,然后代碼中使用sscanf轉換。
{"head": {"version": 1,"flag": 0,"section":[{"type": 0,"subtype": 0,"flag": 16,"vma": "0x93000000","lma": "0x200","len": 19604,"file": "../img/sdram_img/stm32f429-disco-sdram.dtb"},{"type": 1,"subtype": 0,"flag": 16,"vma": "0x93008000","lma": "0x8200","len": 1935164,"file": "../img/sdram_img/xipImage-sdram.bin"}]}}
下載cjson源碼
git clone https://github.com/DaveGamble/cJSON.git
只保留cJSON.c和cJSON.h文件
然后添加其他文件
imgpack.c/h 實現json解析以及打包
load.c/h 提取原來文件中head和段信息結構體轉為buffer的實現。
文件目錄如下

詳見imgpack.c
int main(int argc, char* argv[]){if(argc != 3){printf("usage:imgpack jsonfile outfile\r\n");return -1;}FILE* jsonfile;FILE* outfile;FILE* infile;char* jsonbuffer;long jsonfilesize;cJSON* cjson = NULL;cJSON* cjson_head = NULL;cJSON* cjson_section = NULL;cJSON* cjson_section_n = NULL;cJSON* cjson_tmp = NULL;load_head_st head;load_section_info_st section_info;long outbuffersize = 0;char* outbuffer;int sections;long section_lma;long section_len;memset(&head,0,sizeof(head));head.magic[0]='M';head.magic[1]='L';head.magic[2]='O';head.magic[3]='A';head.magic[4]='D';jsonfile = fopen(argv[1], "rb");if(jsonfile == NULL){printf("open %s err %s\r\n", argv[1],strerror(errno));return -2;}else{printf("open %s ok\r\n", argv[1]);}outfile = fopen(argv[2], "wb+");if(outfile == NULL){printf("open %s err \r\n", argv[2]);fclose(jsonfile);return -3;}else{printf("open %s ok\r\n", argv[2]);}fseek(jsonfile, 0, SEEK_END);jsonfilesize = ftell(jsonfile);printf("jsonfilesize=%ld\r\n",jsonfilesize);jsonbuffer = malloc(jsonfilesize);fseek(jsonfile, 0, SEEK_SET);fread(jsonbuffer,1,jsonfilesize,jsonfile);fclose(jsonfile);printf("read jsonfile to jsonbuffer\r\n");printf("%s\r\n",jsonbuffer);cjson = cJSON_Parse(jsonbuffer);if(cjson == NULL){printf("cJSON_Parse err\r\n");goto fail0;}cjson_head = cJSON_GetObjectItem(cjson, "head");if(cjson_head == NULL){printf("cJSON_GetObjectItem head err\r\n");goto fail0;}cjson_tmp = cJSON_GetObjectItem(cjson_head, "version");if(cjson_tmp == NULL){printf("cJSON_GetObjectItem version err\r\n");goto fail0;}head.version = cjson_tmp->valueint;cjson_tmp = cJSON_GetObjectItem(cjson_head, "flag");if(cjson_tmp == NULL){printf("cJSON_GetObjectItem flag err\r\n");goto fail0;}head.flag = cjson_tmp->valueint;cjson_section = cJSON_GetObjectItem(cjson_head, "section");if(cjson_section == NULL){printf("cJSON_GetObjectItem section err\r\n");goto fail0;}sections = cJSON_GetArraySize(cjson_section);printf("sections = %d\r\n",sections);head.sectinos = sections;head.len = LOAD_HDR_LEN + sections*LOAD_SECTION_INFO_LEN;for(int i=0; i<sections; i++){/* 找到lma+len最大的位置決定outbuffer的大小 */cjson_section_n = cJSON_GetArrayItem(cjson_section, i);if(cjson_section_n == NULL){printf("cJSON_GetArrayItem cjson_section_%d err\r\n",i);goto fail0;}cjson_tmp = cJSON_GetObjectItem(cjson_section_n, "lma");if(cjson_tmp == NULL){printf("cJSON_GetObjectItem cjson_section_%d lma err\r\n",i);goto fail0;}long tmp;sscanf(cjson_tmp->valuestring, "%lx", &tmp);section_lma = tmp;cjson_tmp = cJSON_GetObjectItem(cjson_section_n, "len");if(cjson_tmp == NULL){printf("cJSON_GetObjectItem cjson_section_%d len err\r\n",i);goto fail0;}section_len = cjson_tmp->valueint;if((section_lma+section_len) > outbuffersize){outbuffersize = section_lma+section_len;}}printf("malloc outbuffer %ld\r\n",outbuffersize);outbuffer = malloc(outbuffersize);memset(outbuffer,0xFF,outbuffersize);for(int i=0; i<sections; i++){/* 找到lma+len最大的位置決定outbuffer的大小 */cjson_section_n = cJSON_GetArrayItem(cjson_section, i);if(cjson_section_n == NULL){printf("cJSON_GetArrayItem cjson_section_%d err\r\n",i);goto fail1;}cjson_tmp = cJSON_GetObjectItem(cjson_section_n, "type");if(cjson_tmp == NULL){printf("cJSON_GetObjectItem cjson_section_%d type err\r\n",i);goto fail1;}section_info.type = cjson_tmp->valueint;cjson_tmp = cJSON_GetObjectItem(cjson_section_n, "subtype");if(cjson_tmp == NULL){printf("cJSON_GetObjectItem cjson_section_%d subtype err\r\n",i);goto fail1;}section_info.subtype = cjson_tmp->valueint;cjson_tmp = cJSON_GetObjectItem(cjson_section_n, "flag");if(cjson_tmp == NULL){printf("cJSON_GetObjectItem cjson_section_%d flag err\r\n",i);goto fail1;}section_info.flag = cjson_tmp->valueint;cjson_tmp = cJSON_GetObjectItem(cjson_section_n, "vma");if(cjson_tmp == NULL){printf("cJSON_GetObjectItem cjson_section_%d vma err\r\n",i);goto fail1;}long tmp;sscanf(cjson_tmp->valuestring, "%lx", &tmp);section_info.vma = tmp;cjson_tmp = cJSON_GetObjectItem(cjson_section_n, "lma");if(cjson_tmp == NULL){printf("cJSON_GetObjectItem cjson_section_%d lma err\r\n",i);goto fail1;}sscanf(cjson_tmp->valuestring, "%lx", &tmp);section_info.lma = tmp;cjson_tmp = cJSON_GetObjectItem(cjson_section_n, "len");if(cjson_tmp == NULL){printf("cJSON_GetObjectItem cjson_section_%d len err\r\n",i);goto fail1;}section_info.len = cjson_tmp->valueint;cjson_tmp = cJSON_GetObjectItem(cjson_section_n, "file");section_info.check = 0;if(cjson_tmp->valuestring != NULL){infile = fopen(cjson_tmp->valuestring,"rb");if(infile == NULL){printf("open %s err %s\r\n",cjson_tmp->valuestring, strerror(errno));goto fail1;}fread(outbuffer+section_info.lma,1,section_info.len,infile);printf("read %s len %d to outbuffer of %d\r\n",cjson_tmp->valuestring, section_info.len,section_info.lma);fclose(infile);if(section_info.flag & LOAD_SECTION_FLAG_CRC32){/* @todo *///section_info.check = crc32(outbuffer+section_info.lma,outbuffer+section_info.lma);}}printf("type=%d,subtype=%d,flag=%d,vma=%#x,lma=%#x,len=%d\r\n",section_info.type,section_info.subtype,section_info.flag,section_info.vma,section_info.lma,section_info.len);load_sectioninfo2buffer(§ion_info, outbuffer+LOAD_HDR_LEN+i*LOAD_SECTION_INFO_LEN);}。/if(head.flag & 0x01){/* @todo *///head.check = xxx;}else{head.check = 0;}load_hdr2buffer(&head, outbuffer);printf("write to outfile\r\n");fwrite(outbuffer,1,outbuffersize,outfile);fail1:free(outbuffer);fail0:free(jsonbuffer);fclose(outfile);return 0;}
./build.sh
./imgpack imgpack.json img.bin
打印如下
qinyunti@qinyunti:/mnt/c/Users/qinyu/Desktop/stm32f429-boot/imgpack$ ./imgpack imgpack.json img.binopen imgpack.json okopen img.bin okjsonfilesize=662read jsonfile to jsonbuffer{"head": {"version": 1,"flag": 0,"section":[{"type": 0,"subtype": 0,"flag": 16,"vma": "0x93000000","lma": "0x200","len": 19604,"file": "../img/sdram_img/stm32f429-disco-sdram.dtb"},{"type": 1,"subtype": 0,"flag": 16,"vma": "0x93008000","lma": "0x8200","len": 1935164,"file": "../img/sdram_img/xipImage-sdram.bin"}]}}sections = 2malloc outbuffer 1968444read ../img/sdram_img/stm32f429-disco-sdram.dtb len 19604 to outbuffer of 512type=0,subtype=0,flag=16,vma=0x93000000,lma=0x200,len=19604read ../img/sdram_img/xipImage-sdram.bin len 1935164 to outbuffer of 33280type=1,subtype=0,flag=16,vma=0x93008000,lma=0x8200,len=1935164write to outfile
生成img.bin.
使用二進制對比工具對比img.bin
和HEAD.bin比較前面0x36個字節完全一樣

和stm32f429-disco-sdram.dtb
偏移0x200處完全一樣,即設備樹的內容

和xipImage-sdram.bin比較,偏移0x8200處完全一樣,即內核鏡像部分

此時img.bin就是燒錄到spiflash的0地址處的一整個鏡像。
將整個img.bin燒錄到spiflash
先導入到SDRAM
rxmem 0x90000000 1968444

然后寫入spiflash
restorespiflash 0x90000000 0 1968444

重啟,看到從spiflash加載運行OK。

目前為止,我們從0開始完成了一個功能更完善的boot的設計,具備了boot下shell交互環境,實現了一些有用的命令比如spiflash查看與修改,文件導入與導出等。 同時也完成了從spiflash加載啟動的功能。整個過程幾乎都是使用的我們之前公眾號發布的文章積累的輪子,這里就是移植拼接一下,所以實現起來非常快速高效,這就是自己造輪子積累自己輪子的意義。當然為了打包鏡像我們還設計了打包工具。總之我們一切都自己動手來,體現”玩轉”的精神。