比如,我們用這樣一個命令行參數(shù)字符串“console=ttyS0,115200n8”來通知內(nèi)核以ttyS0 作為控制臺,且串口采用 “115200bps、無奇偶校驗、8位數(shù)據(jù)位”這樣的設(shè)置。下面是一段設(shè)置調(diào)用內(nèi)核命令行參數(shù)字符串的示例代碼:
char *p;
/* eat leading white space */
for(p = commandline; *p == ‘ ’; p++)
;
/* skip non-existent command lines so the kernel will still
* use its default command line.
*/
if(*p == ‘\0’)
return;
params->hdr.tag = ATAG_CMDLINE;
params->hdr.size = (sizeof(struct tag_header) + strlen(p) + 1 + 4) >> 2;
strcpy(params->u.cmdline.cmdline, p);
params = tag_next(params);
請注意在上述代碼中,設(shè)置 tag_header 的大小時,必須包括字符串的終止符‘\0’,此外還要將字節(jié)數(shù)向上圓整4個字節(jié),因為 tag_header 結(jié)構(gòu)中的size 成員表示的是字?jǐn)?shù)。
下面是設(shè)置 ATAG_INITRD 的示例代碼,它告訴內(nèi)核在 RAM 中的什么地方可以找到 initrd 映象(壓縮格式)以及它的大?。?/p>
params->hdr.tag = ATAG_INITRD2;
params->hdr.size = tag_size(tag_initrd);
params->u.initrd.start = RAMDISK_RAM_BASE;
params->u.initrd.size = INITRD_LEN;
params = tag_next(params);
下面是設(shè)置 ATAG_RAMDISK 的示例代碼,它告訴內(nèi)核解壓后的 Ramdisk 有多大(單位是KB):
params->hdr.tag = ATAG_RAMDISK;
params->hdr.size = tag_size(tag_ramdisk);
params->u.ramdisk.start = 0;
params->u.ramdisk.size = RAMDISK_SIZE; /* 請注意,單位是KB */
params->u.ramdisk.flags = 1; /* automatically load ramdisk */
params = tag_next(params);
最后,設(shè)置 ATAG_NONE 標(biāo)記,結(jié)束整個啟動參數(shù)列表:
static void setup_end_tag(void)
{
params->hdr.tag = ATAG_NONE;
params->hdr.size = 0;
}
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)時,下列條件要滿足:
CPU 寄存器的設(shè)置:
R0=0;
R1=機器類型 ID;關(guān)于 Machine Type Number,可以參見 linux/arch/arm/tools/mach-types。
R2=啟動參數(shù)標(biāo)記列表在 RAM 中起始基地址;
CPU 模式:
必須禁止中斷(IRQs和FIQs);
CPU 必須 SVC 模式;
Cache 和 MMU 的設(shè)置:
MMU 必須關(guān)閉;
指令 Cache 可以打開也可以關(guān)閉;
數(shù)據(jù) Cache 必須關(guān)閉;
如果用 C 語言,可以像下列示例代碼這樣來調(diào)用內(nèi)核:
void (*theKernel)(int zero, int arch, u32 params_addr) = (void (*)(int, int,u32))KERNEL_RAM_BASE;
……
theKernel(0, ARCH_NUMBER, (u32) kernel_params_start);
注意,theKernel()函數(shù)調(diào)用應(yīng)該永遠(yuǎn)不返回的。如果這個調(diào)用返回,則說明出錯。
四. 關(guān)于串口終端
在 boot loader 程序的設(shè)計與實現(xiàn)中,沒有什么能夠比從串口終端正確地收到打印信息能更令人激動了。此外,向串口終端打印信息也是一個非常重要而又有效的調(diào)試手段。但是,我們經(jīng)常會碰到串口終端顯示亂碼或根本沒有顯示的問題。造成這個問題主要有兩種原因:
boot loader 對串口的初始化設(shè)置不正確。
運行在 host 端的終端仿真程序?qū)Υ诘脑O(shè)置不正確,這包括:波特率、奇偶校驗、數(shù)據(jù)位和停止位等方面的設(shè)置。
此外,有時也會碰到這樣的問題,那就是:在 boot loader 的運行過程中我們可以正確地向串口終端輸出信息,但當(dāng) boot loader 啟動內(nèi)核后卻無法看到內(nèi)核的啟動輸出信息。對這一問題的原因可以從以下幾個方面來考慮:
首先請確認(rèn)你的內(nèi)核在編譯時配置了對串口終端的支持,并配置了正確的串口驅(qū)動程序。
你的 boot loader 對串口的初始化設(shè)置可能會和內(nèi)核對串口的初始化設(shè)置不一致。此外,對于諸如 s3c44b0x 這樣的 CPU,CPU 時鐘頻率的設(shè)置也會影響串口,因此如果boot loader 和內(nèi)核對其 CPU 時鐘頻率的設(shè)置不一致,也會使串口終端無法正確顯示信息。
最后,還要確認(rèn) boot loader 所用的內(nèi)核基地址必須和內(nèi)核映像在編譯時所用的運行基地址一致,尤其是對于 uClinux 而言。假設(shè)你的內(nèi)核映像在編譯時用的基地址是0xc0008000,但你的 boot loader 卻將它加載到 0xc0010000 處去執(zhí)行,那么內(nèi)核映像當(dāng)然不能正確地執(zhí)行了。
五. 結(jié)束語
Boot Loader 的設(shè)計與實現(xiàn)是一個非常復(fù)雜的過程。
評論
查看更多