嵌入式系統(tǒng) Boot Loader 技術(shù)內(nèi)幕
嵌入式 linux 系統(tǒng)中,通常需要由 boot loader 設(shè)置的常見(jiàn)啟動(dòng)參數(shù)有:atag_core、atag_mem、atag_cmdline、atag_ramdisk、atag_initrd等。 比如,設(shè)置 atag_core 的代碼如下:
其中,boot_params 表示內(nèi)核啟動(dòng)參數(shù)在內(nèi)存中的起始基地址,指針 params 是一個(gè) struct tag 類(lèi)型的指針。宏 tag_next() 將以指向當(dāng)前標(biāo)記的指針為參數(shù),計(jì)算緊臨當(dāng)前標(biāo)記的下一個(gè)標(biāo)記的起始地址。注意,內(nèi)核的根文件系統(tǒng)所在的設(shè)備id就是在這里設(shè)置的。 下面是設(shè)置內(nèi)存映射情況的示例代碼:
可以看出,在 memory_map[]數(shù)組中,每一個(gè)有效的內(nèi)存段都對(duì)應(yīng)一個(gè) atag_mem 參數(shù)標(biāo)記。 linux 內(nèi)核在啟動(dòng)時(shí)可以以命令行參數(shù)的形式來(lái)接收信息,利用這一點(diǎn)我們可以向內(nèi)核提供那些內(nèi)核不能自己檢測(cè)的硬件參數(shù)信息,或者重載(override)內(nèi)核自己檢測(cè)到的信息。比如,我們用這樣一個(gè)命令行參數(shù)字符串"console=ttys0,115200n8"來(lái)通知內(nèi)核以 ttys0 作為控制臺(tái),且串口采用 "115200bps、無(wú)奇偶校驗(yàn)、8位數(shù)據(jù)位"這樣的設(shè)置。下面是一段設(shè)置調(diào)用內(nèi)核命令行參數(shù)字符串的示例代碼:
請(qǐng)注意在上述代碼中,設(shè)置 tag_header 的大小時(shí),必須包括字符串的終止符"\0",此外還要將字節(jié)數(shù)向上圓整4個(gè)字節(jié),因?yàn)?tag_header 結(jié)構(gòu)中的size 成員表示的是字?jǐn)?shù)。 下面是設(shè)置 atag_initrd 的示例代碼,它告訴內(nèi)核在 ram 中的什么地方可以找到 initrd 映象(壓縮格式)以及它的大?。?
下面是設(shè)置 atag_ramdisk 的示例代碼,它告訴內(nèi)核解壓后的 ramdisk 有多大(單位是kb):
最后,設(shè)置 atag_none 標(biāo)記,結(jié)束整個(gè)啟動(dòng)參數(shù)列表:
3.2.5 調(diào)用內(nèi)核 boot loader 調(diào)用 linux 內(nèi)核的方法是直接跳轉(zhuǎn)到內(nèi)核的第一條指令處,也即直接跳轉(zhuǎn)到 mem_start+0x8000 地址處。在跳轉(zhuǎn)時(shí),下列條件要滿足: 1. cpu 寄存器的設(shè)置:
2. cpu 模式:
3. cache 和 mmu 的設(shè)置:
如果用 c 語(yǔ)言,可以像下列示例代碼這樣來(lái)調(diào)用內(nèi)核:
注意,thekernel()函數(shù)調(diào)用應(yīng)該永遠(yuǎn)不返回的。如果這個(gè)調(diào)用返回,則說(shuō)明出錯(cuò)。
在 boot loader 程序的設(shè)計(jì)與實(shí)現(xiàn)中,沒(méi)有什么能夠比從串口終端正確地收到打印信息能更令人激動(dòng)了。此外,向串口終端打印信息也是一個(gè)非常重要而又有效的調(diào)試手段。但是,我們經(jīng)常會(huì)碰到串口終端顯示亂碼或根本沒(méi)有顯示的問(wèn)題。造成這個(gè)問(wèn)題主要有兩種原因:(1) boot loader 對(duì)串口的初始化設(shè)置不正確。(2) 運(yùn)行在 host 端的終端仿真程序?qū)Υ诘脑O(shè)置不正確,這包括:波特率、奇偶校驗(yàn)、數(shù)據(jù)位和停止位等方面的設(shè)置。 此外,有時(shí)也會(huì)碰到這樣的問(wèn)題,那就是:在 boot loader 的運(yùn)行過(guò)程中我們可以正確地向串口終端輸出信息,但當(dāng) boot loader 啟動(dòng)內(nèi)核后卻無(wú)法看到內(nèi)核的啟動(dòng)輸出信息。對(duì)這一問(wèn)題的原因可以從以下幾個(gè)方面來(lái)考慮: (1) 首先請(qǐng)確認(rèn)你的內(nèi)核在編譯時(shí)配置了對(duì)串口終端的支持,并配置了正確的串口驅(qū)動(dòng)程序。 (2) 你的 boot loader 對(duì)串口的初始化設(shè)置可能會(huì)和內(nèi)核對(duì)串口的初始化設(shè)置不一致。此外,對(duì)于諸如 s3c44b0x 這樣的 cpu,cpu 時(shí)鐘頻率的設(shè)置也會(huì)影響串口,因此如果 boot loader 和內(nèi)核對(duì)其 cpu 時(shí)鐘頻率的設(shè)置不一致,也會(huì)使串口終端無(wú)法正確顯示信息。 (3) 最后,還要確認(rèn) boot loader 所用的內(nèi)核基地址必須和內(nèi)核映像在編譯時(shí)所用的運(yùn)行基地址一致,尤其是對(duì)于 uclinux 而言。假設(shè)你的內(nèi)核映像在編譯時(shí)用的基地址是 0xc0008000,但你的 boot loader 卻將它加載到 0xc0010000 處去執(zhí)行,那么內(nèi)核映像當(dāng)然不能正確地執(zhí)行了。 boot loader 的設(shè)計(jì)與實(shí)現(xiàn)是一個(gè)非常復(fù)雜的過(guò)程。如果不能從串口收到那激動(dòng)人心的"uncompressing linux.................. done, booting the kernel……"內(nèi)核啟動(dòng)信息,恐怕誰(shuí)也不能說(shuō):"嗨,我的 boot loader 已經(jīng)成功地轉(zhuǎn)起來(lái)了!"。
|