比如,我們用這樣一個(gè)命令行參數(shù)字符串“console=ttyS0,115200n8”來通知內(nèi)核以ttyS0 作為控制臺(tái),且串口采用 “115200bps、無奇偶校驗(yàn)、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 的大小時(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 映象(壓縮格式)以及它的大小:
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é)束整個(gè)啟動(dòng)參數(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)時(shí),下列條件要滿足:
CPU 寄存器的設(shè)置:
R0=0;
R1=機(jī)器類型 ID;關(guān)于 Machine Type Number,可以參見 linux/arch/arm/tools/mach-types。
R2=啟動(dòng)參數(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)不返回的。如果這個(gè)調(diào)用返回,則說明出錯(cuò)。
四. 關(guān)于串口終端
在 boot loader 程序的設(shè)計(jì)與實(shí)現(xiàn)中,沒有什么能夠比從串口終端正確地收到打印信息能更令人激動(dòng)了。此外,向串口終端打印信息也是一個(gè)非常重要而又有效的調(diào)試手段。但是,我們經(jīng)常會(huì)碰到串口終端顯示亂碼或根本沒有顯示的問題。造成這個(gè)問題主要有兩種原因:
boot loader 對(duì)串口的初始化設(shè)置不正確。
運(yùn)行在 host 端的終端仿真程序?qū)Υ诘脑O(shè)置不正確,這包括:波特率、奇偶校驗(yàn)、數(shù)據(jù)位和停止位等方面的設(shè)置。
此外,有時(shí)也會(huì)碰到這樣的問題,那就是:在 boot loader 的運(yùn)行過程中我們可以正確地向串口終端輸出信息,但當(dāng) boot loader 啟動(dòng)內(nèi)核后卻無法看到內(nèi)核的啟動(dòng)輸出信息。對(duì)這一問題的原因可以從以下幾個(gè)方面來考慮:
首先請確認(rèn)你的內(nèi)核在編譯時(shí)配置了對(duì)串口終端的支持,并配置了正確的串口驅(qū)動(dòng)程序。
你的 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ì)使串口終端無法正確顯示信息。
最后,還要確認(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í)行了。
五. 結(jié)束語
Boot Loader 的設(shè)計(jì)與實(shí)現(xiàn)是一個(gè)非常復(fù)雜的過程。
評(píng)論
查看更多