84
技術社區[雲棲]
嵌入式Linux引導過程之1.6——Xloader的Xloader_Entry
我們已經看完了XLOADER_ENTRY裏調用的前兩個標號的代碼,分別是sys_init和ddr_init。對於一個嵌入式係統來說,這兩 個部分的代碼是在一開始就執行的,至少是在從bootrom裏麵的firmware出來之後最初執行的代碼,也是我們自己能夠控製的最初的代碼(在 bootrom裏麵的firmware是在芯片出廠的時候就固化在芯片裏麵的了,一般情況下,我們是無法改變裏麵的代碼的)。
正因為如 此,對於sys_init和ddr_init來說,需要做很多與係統底層硬件相關的初始化工作。而對於這種工作來說,匯編代碼無疑是最能夠勝任得了,因 此,sys_init和ddr_init這兩個代碼塊都是用ARM的匯編寫成的。不過對於代碼的閱讀者來說,至少對我來說,閱讀匯編還是比較痛苦的,畢竟 習慣了c程序那樣的高級語言,在.S的匯編世界裏還是非常掙紮的。
不過當我們走過了那兩道坎之後,終於看到了一絲光明,c終於出現了!c 的出現,不僅意味著我們將來閱讀代碼更加舒服了,更重要的是,c的出現意味著最底層的需要用匯編才能夠完成的初始化工作基本已經告一段落了,接下來需要處 理的過程,更多的是與邏輯相關,而與硬件多少關係少一點了。這對於我這種不懂硬件的人來說,無疑是一個值得鼓舞的消息。雖然在後麵的過程中多多少少還會遇 到與硬件打交道的匯編代碼,不過誰的人生能夠一路坦途呢,沒有那麼一點的跌宕起伏的人生反而會顯得缺少點什麼。程序也是一樣,玩的就是刺激。
嗬 嗬,好了,那麼多廢話,該切入正題了。先來回憶一下在XLOADER_ENTRY中,調用完了sys_init和ddr_init之後調用了一個叫做 Xloader_Entry的函數,而且我們說過,XLOADER_ENTRY在調用完Xloader_Entry之後就再也不會回到 XLOADER_ENTRY中了,也就是說,程序走到這裏的時候,未來怎麼走,都由Xloader_Entry說了算。是死是活都已經不關 XLOADER_ENTRY的事了,它也管不著。
好,現在來貼Xloader_Entry的代碼,該函數位於一個我們熟悉的源文件中,叫 做main.c,嗬嗬,似乎很早以前自己寫程序玩的時候都會把它叫做main.c,然後裏麵隻有一個函數叫做main,然後main函數就是整個程序的入 口函數,程序執行的第一個語句就是main函數裏麵的第一個語句。。。哈哈,懷念啊。。。
不過現在給自己提一個醒,在一個還沒有操作係統支持的係 統中,在它上電之後,它在執行語句的時候,是不會管這個語句對應的函數是不是叫作main的,它隻管執行目前PC(Program Counter)寄存器指向的地址處的指令。因此,在看與係統底層相關的代碼的時候,一定要注意看一看以下幾個文件:.lds文件,.map文 件,Makefile以及.code等等,往往這些文件能夠給我們一些啟示,然後我們就可以順著這個線索一步一步捋程序了。
來看代碼:
36 void Xloader_Entry(void)
37 {
38 int ret;
39 unsigned int loadaddress;
40 unsigned int data_size;
41 image_header_t *hdr = &header;
42
43 int i;
44
45 i = sizeof(t_flash_device);
46 if (i == 0) i =10;
47 else i = 15;
48
49 /* Authenticate Uboot and copy it in DDR */
50 ret = authenticate_uboot();
51 if( ret == 0)
52 {
53 /* UBoot is not there in flash so nothing to do */
54 // while(1);
55 }
56 else
57 {
58 // Program_Interconnection_Matrix();
59 /* Copy uboot from Flash to DDR*/
60 loadaddress = ntohl(hdr->ih_load);
61 data_size = ntohl(hdr->ih_size);
62 memcpy(loadaddress, (UBOOT_BASE_ADDRESS + 64),data_size);
63
64 /* @CHANGE: added by Subhash */
65 RunUBoot(loadaddress);
66
67 #if 0
68 /* Jump to uboot if UBOOT is to be xecuted by ARM1 */
69 #if defined(EXECUTE_UBOOT_ON_ARM1)
70 Program_Interconnection_Matrix();
71 Jump_To_UBoot(loadaddress);
72 while(1); /* will never come here */
73 #else
74 /* tell ARM2 to execute the UBoot, at this time ARM1 will be doing nothing */
75 Program_Interconnection_Matrix();
76 setup_branch(loadaddress);
77 Enable_ARM2_Core();
78 while(1);
79 #endif
80 #endif // #if 0
81 }
82
83 /* UBoot not found in NOR so authenticate it in NAND flash */
84 /* Authenticate Uboot in NAND Flash and copy it in DDR */
85 splus_nand_boot();
86
87 /* wait for infinite if both NAND & NOR boot fails */
88 while(1);
89 }
90
首先我們看到以下這個定義:
41 image_header_t *hdr = &header;
這邊的這個header就是我們在上文中所說的由mkimage這個工具程序添加在目標文件之前的那個image header。
那 為什麼會在Xloader的源文件中出現這樣一個header呢?其實很好理解,因為Xloader的功能是做一些初始化的工作(包括對係統及外設,主要 是時鍾,進行初始化,對SDRAM進行初始化,還有就是還沒有講的驗證uboot以及將uboot加載到SDRAM指定的地址中)。在完成這些初始化工作 之後,就將控製權交給uboot了。我們看到在xloader所做的所有工作中,有一個驗證uboot的工作,也就是看一看在指定的flash中有沒有 uboot image的存在,如果存在才能夠繼續往下走啊對吧。那麼xloader是如何完成這個工作的呢?跟我們上一篇文章中分析的BootRom的做法一樣,它 會去查看一個在flash的某一個地方是不是存在一個uboot image header,如果存在這樣一個header就說明存在整個image。
因此,在Xloader中出現的這個header其實是對應於uboot image header的,用來驗證在flash中是否存在有uboot image。
查看源代碼,可以發現,header變量定義在xloader根目錄下的xloader/xloader.c中。具體是這麼定義的:
4 image_header_t header;
進一步的,image_header_t定義在xloader根目錄下的include/image.h中,它的定義如下:
4 #define IH_NMLEN 32 /* Image Name Length */
24 typedef struct image_header {
25 uint32_t ih_magic; /* Image Header Magic Number */
26 uint32_t ih_hcrc; /* Image Header CRC Checksum */
27 uint32_t ih_time; /* Image Creation Timestamp */
28 uint32_t ih_size; /* Image Data Size */
29 uint32_t ih_load; /* Data Load Address */
30 uint32_t ih_ep; /* Entry Point Address */
31 uint32_t ih_dcrc; /* Image Data CRC Checksum */
32 uint8_t ih_os; /* Operating System */
33 uint8_t ih_arch; /* CPU architecture */
34 uint8_t ih_type; /* Image Type */
35 uint8_t ih_comp; /* Compression Type */
36 uint8_t ih_name[IH_NMLEN]; /* Image Name */
37 } image_header_t;
這就是傳說中的image header的定義了。它貫穿著從BootRom到uboot的整個過程,在BootRom、Xloader以及UBoot中,image header的定義都是它。
到 目前為止,我們需要關心的這個結構體裏麵這幾個域,ih_magic、ih_size、ih_load以及ih_name。其中ih_magic和 ih_name是對某一個image的標識,在識別image的時候就靠它們了。ih_load就是我們在上一篇文章中提到的那個load address,在使用mkimage製作xloader image或者uboot image的時候,會將Makefile中的TEXT_BASE填充到這個域中。而ih_size就表示image的字節大小,注意這個大小是不包括上述 64字節的header部分的,就是說,是header之後跟著的那個elf目標文件的大小。
好了,對header的說明暫且到這裏,我們接著往下看。
緊 接著看到了那幾句對局部變量i的賦值,這幾句我睜大了眼睛看了好幾遍都沒有發現有什麼作用,整個函數中,除了這幾句賦值語句根本就沒有其他什麼地方引用過 這個i。而這個i又是一個純粹的局部變量,並且也不是什麼寄存器地址,實在看不出它在這裏有什麼作用。所以我暫且判斷這幾句為xloader中的垃圾代 碼,或許代碼的原作者在寫代碼的時候打了個瞌睡,回頭就忘了刪了,嗬嗬。或者這部分代碼有什麼我等小輩不知道的神奇的功效,當我在這裏說這是垃圾代碼的時 候,或許那個被我冤枉為打瞌睡的人正在罵我為垃圾人。不管怎樣吧,我沒有看懂這部分內容。暫且定以為無效代碼吧,日後有新的發現再說。
接著往下看:
49 /* Authenticate Uboot and copy it in DDR */
50 ret = authenticate_uboot();
這裏,就是對flash中的uboot進行驗證啦。通過一句authenticate_uboot(),如果在flash的某個地方存在我們需要的uboot image的話,那麼它將返回1。否則調用該函數就將返回0,說明我們需要的uboot image並不在那裏。
對於authenticate_uboot()函數的細節,我們下次再說。這裏隻需要明白它的作用就可以了,這個作用也是Xloader的一個基本功能。
做 完下麵的這個if-else,在正常情況下,Xloader也就該功成身退了。粗粗一看這個if-else似乎是有不少代碼,但其實在else裏麵的 67-80行部分用#if 0 ... #endif括起來的代碼是不會被編譯進去的,因此實際上這部份代碼是可以刪掉的(這部分其實就是RunUBoot函數的代碼,可能作者最初沒有將這部份 代碼封裝到一個函數中,後來為了看著舒服就簡單的封裝了一下,說簡單是真的簡單,就是將原來的代碼原封不動的挪到了RunUBoot函數中,使得在 Xloader_Entry函數中看起來更加簡潔)。作者可能為了將來修改方便就沒有刪去。在這裏,為了看起來清楚一點我把這部分代碼去掉:
51 if( ret == 0)
52 {
53 /* UBoot is not there in flash so nothing to do */
54 // while(1);
55 }
56 else
57 {
58 // Program_Interconnection_Matrix();
59 /* Copy uboot from Flash to DDR*/
60 loadaddress = ntohl(hdr->ih_load);
61 data_size = ntohl(hdr->ih_size);
62 memcpy(loadaddress, (UBOOT_BASE_ADDRESS + 64),data_size);
63
64 /* @CHANGE: added by Subhash */
65 RunUBoot(loadaddress);
66
81 }
這 裏的ret就是上麵調用authenticate_uboot()的返回值。當ret為0時,說明在指定的flash地址中沒有找到uboot image。如果不考慮從NAND flash引導的情況的話,代碼走到這裏就沒有辦法繼續往下走了,因此作者在最初的時候在這裏放了一個while(1)。隻不過,後來作者添加了對從 NAND flash引導的支持,因此才將這句無限循環給去掉了。從這裏我們可以看到,對於這個Xloader來說,uboot image既可以放在NOR flash中,也可以放在NAND flash中進行引導。
如果ret為1,這也就是我所說的正常情況,這時,需要 做的工作就是將uboot的代碼從flash搬移到指定的SDRAM中,然後跳轉到SDRAM中的這個地址中,將程序的控製權交給uboot,開始執行 uboot的代碼,xloader的使命也就完成了。在這裏,具體的做法就是從header中獲取需要加載的SDRAM的地址以及uboot elf目標文件的大小(注意,這裏的header是uboot image中的header),然後,使用memcpy將flash中的代碼加載到header中指定的SDRAM中的loadaddress。在所有的 拷貝工作都完成之後,就直接調用RunUBoot(loadaddress)跳轉到這個SDRAM中的地址去執行uboot的代碼了。由此,控製權也交到 了uboot中。
這裏的RunUBoot我們以後再看,但是有一個問題需要在這裏說明,那就是關於上麵代碼中的UBOOT_BASE_ADDRESS。這個宏定義在include/splus_mmap.h中:
7 #define UBOOT_BASE_ADDRESS 0xF8010000 /* flash sector-1 */
從 定義這個宏的文件名可以看出,這個基地址的值是與具體的芯片相關的。參看spearplus的手冊,可以看到從 0xF8000000-0xFBFFFFFF這64M的地址空間是分配給NOR flash的。而在NOR flash中,一個sector是64k,因此,UBOOT_BASE_ADDRESS 0xF8010000也就是flash的sector 1。而flash的sector 0則是給了xloader自己占用了。
這裏對flash地址的限製,同時也就限製了xloader和uboot的燒寫地址,也就是說,xloader要燒寫在地址0xF8000000地址處,而uboot需要燒寫在0xF8010000處,這樣,才能夠正確引導。
上麵說了,如果在NOR flash中沒有發現uboot image的蹤影,xloader將去NAND flash中搜尋是否有可引導的uboot image,如果在那裏也找不到,那就隻好在原地轉圈了。這也就是下麵這部份代碼所幹的事情了。
83 /* UBoot not found in NOR so authenticate it in NAND flash */
84 /* Authenticate Uboot in NAND Flash and copy it in DDR */
85 splus_nand_boot();
86
87 /* wait for infinite if both NAND & NOR boot fails */
88 while(1);
到此為止,如果一切正常的話,我們就應該要離開xloader了。如果我足夠心急足夠狠心的話,現在就可以拋棄xloader奔向uboot的懷抱了。但是我是一個重感情的人,我不會如此輕易地拋棄一個人:)因此,我還要將xloader中剩餘的那部分代碼看一看
最後更新:2017-04-03 16:48:40