📄 阿冰blog 嵌入式系统 boot loader 技术内幕.htm
字号:
params->u.core.flags = 0;
params->u.core.pagesize = 0;
params->u.core.rootdev = 0;
params = tag_next(params);
</FONT></CODE></PRE></TD></TR></TBODY></TABLE>
<P>其中,BOOT_PARAMS 表示内核启动参数在内存中的起始基地址,指针 params 是一个 struct tag 类型的指针。宏
tag_next() 将以指向当前标记的指针为参数,计算紧临当前标记的下一个标记的起始地址。注意,内核的根文件系统所在的设备ID就是在这里设置的。
</P>
<P>下面是设置内存映射情况的示例代码: </P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc
border=1><TBODY>
<TR>
<TD><PRE><CODE>
<FONT face=Courier size=2>for(i = 0; i < NUM_MEM_AREAS; i++) {
if(memory_map[i].used) {
params->hdr.tag = ATAG_MEM;
params->hdr.size = tag_size(tag_mem32);
params->u.mem.start = memory_map[i].start;
params->u.mem.size = memory_map[i].size;
params = tag_next(params);
}
}
</FONT></CODE></PRE></TD></TR></TBODY></TABLE>
<P>可以看出,在 memory_map[]数组中,每一个有效的内存段都对应一个 ATAG_MEM 参数标记。 </P>
<P>Linux
内核在启动时可以以命令行参数的形式来接收信息,利用这一点我们可以向内核提供那些内核不能自己检测的硬件参数信息,或者重载(override)内核自己检测到的信息。比如,我们用这样一个命令行参数字符串"console=ttyS0,115200n8"来通知内核以
ttyS0 作为控制台,且串口采用 "115200bps、无奇偶校验、8位数据位"这样的设置。下面是一段设置调用内核命令行参数字符串的示例代码:
</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc
border=1><TBODY>
<TR>
<TD><PRE><CODE>
<FONT face=Courier size=2>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);
</FONT></CODE></PRE></TD></TR></TBODY></TABLE>
<P>请注意在上述代码中,设置 tag_header 的大小时,必须包括字符串的终止符'\0',此外还要将字节数向上圆整4个字节,因为
tag_header 结构中的size 成员表示的是字数。 </P>
<P>下面是设置 ATAG_INITRD 的示例代码,它告诉内核在 RAM 中的什么地方可以找到 initrd 映象(压缩格式)以及它的大小:
</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc
border=1><TBODY>
<TR>
<TD><PRE><CODE>
<FONT face=Courier size=2> 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);
</FONT></CODE></PRE></TD></TR></TBODY></TABLE>
<P>下面是设置 ATAG_RAMDISK 的示例代码,它告诉内核解压后的 Ramdisk 有多大(单位是KB): </P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc
border=1><TBODY>
<TR>
<TD><PRE><CODE>
<FONT face=Courier size=2>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);
</FONT></CODE></PRE></TD></TR></TBODY></TABLE>
<P>最后,设置 ATAG_NONE 标记,结束整个启动参数列表: </P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc
border=1><TBODY>
<TR>
<TD><PRE><CODE>
<FONT face=Courier size=2>static void setup_end_tag(void)
{
params->hdr.tag = ATAG_NONE;
params->hdr.size = 0;
}
</FONT></CODE></PRE></TD></TR></TBODY></TABLE>
<P><B xmlns:dw="http://www.ibm.com/developerworks/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">3.2.5 调用内核</B> </P>
<P>Boot Loader 调用 Linux 内核的方法是直接跳转到内核的第一条指令处,也即直接跳转到 MEM_START+0x8000
地址处。在跳转时,下列条件要满足: </P>
<P>1. CPU 寄存器的设置: </P>
<UL xmlns:dw="http://www.ibm.com/developerworks/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI>R0=0; <BR><BR>
<LI>R1=机器类型 ID;关于 Machine Type Number,可以参见
<B>linux/arch/arm/tools/mach-types。</B> <BR><BR>
<LI>R2=启动参数标记列表在 RAM 中起始基地址; <BR><BR></LI></UL>
<P>2. CPU 模式: </P>
<UL xmlns:dw="http://www.ibm.com/developerworks/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI>必须禁止中断(IRQs和FIQs); <BR><BR>
<LI>CPU 必须 SVC 模式; <BR><BR></LI></UL>
<P>3. Cache 和 MMU 的设置:</P>
<UL xmlns:dw="http://www.ibm.com/developerworks/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI>MMU 必须关闭; <BR><BR>
<LI>指令 Cache 可以打开也可以关闭; <BR><BR>
<LI>数据 Cache 必须关闭; </LI></UL>
<P>如果用 C 语言,可以像下列示例代码这样来调用内核: </P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc
border=1><TBODY>
<TR>
<TD><PRE><CODE>
<FONT face=Courier size=2>void (*theKernel)(int zero, int arch, u32 params_addr) = (void (*)(int, int, u32))KERNEL_RAM_BASE;
……
theKernel(0, ARCH_NUMBER, (u32) kernel_params_start);
</FONT></CODE></PRE></TD></TR></TBODY></TABLE>
<P>注意,theKernel()函数调用应该永远不返回的。如果这个调用返回,则说明出错。 </P>
<P><A name=4><SPAN class=atitle2><STRONG><FONT size=4>4.
关于串口终端</FONT></STRONG></SPAN></A><BR>在 boot loader
程序的设计与实现中,没有什么能够比从串口终端正确地收到打印信息能更令人激动了。此外,向串口终端打印信息也是一个非常重要而又有效的调试手段。但是,我们经常会碰到串口终端显示乱码或根本没有显示的问题。造成这个问题主要有两种原因:(1)
boot loader 对串口的初始化设置不正确。(2) 运行在 host
端的终端仿真程序对串口的设置不正确,这包括:波特率、奇偶校验、数据位和停止位等方面的设置。 </P>
<P>此外,有时也会碰到这样的问题,那就是:在 boot loader 的运行过程中我们可以正确地向串口终端输出信息,但当 boot loader
启动内核后却无法看到内核的启动输出信息。对这一问题的原因可以从以下几个方面来考虑: </P>
<P>(1) 首先请确认你的内核在编译时配置了对串口终端的支持,并配置了正确的串口驱动程序。 </P>
<P>(2) 你的 boot loader 对串口的初始化设置可能会和内核对串口的初始化设置不一致。此外,对于诸如 s3c44b0x 这样的
CPU,CPU 时钟频率的设置也会影响串口,因此如果 boot loader 和内核对其 CPU
时钟频率的设置不一致,也会使串口终端无法正确显示信息。 </P>
<P>(3) 最后,还要确认 boot loader 所用的内核基地址必须和内核映像在编译时所用的运行基地址一致,尤其是对于 uClinux
而言。假设你的内核映像在编译时用的基地址是 0xc0008000,但你的 boot loader 却将它加载到 0xc0010000
处去执行,那么内核映像当然不能正确地执行了。 </P>
<P><A name=5><SPAN class=atitle2><STRONG><FONT size=4>5.
结束语</FONT></STRONG></SPAN></A><BR>Boot Loader
的设计与实现是一个非常复杂的过程。如果不能从串口收到那激动人心的"uncompressing linux..................
done, booting the kernel……"内核启动信息,恐怕谁也不能说:"嗨,我的 boot loader 已经成功地转起来了!"。
</P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><A name=author1></A><STRONG><FONT size=4><SPAN
class=atitle2>关于作者</SPAN><BR></FONT></STRONG>詹荣开,研究兴趣包括:嵌入式
Linux、Linux 内核、驱动程序、文件系统等。您可以通过 <A href="mailto:zhanrk@sohu.com"
xmlns:dw="http://www.ibm.com/developerworks/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><FONT
color=#006699>zhanrk@sohu.com</FONT></A>连系他。 </TD></TR></TBODY></TABLE><BR
clear=all><IMG height=10 alt=""
src="阿冰BLOG 嵌入式系统 Boot Loader 技术内幕.files/c.gif" width=100
border=0><BR></DIV></DIV><!--
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
<rdf:Description
rdf:about="http://blogs.ustcers.com/blogs/devzhao/articles/5998.aspx"
dc:identifier="http://blogs.ustcers.com/blogs/devzhao/articles/5998.aspx"
dc:title="嵌入式系统 Boot Loader 技术内幕"
trackback:ping="http://blogs.ustcers.com/blogs/trackback.aspx?PostID=5998" />
</rdf:RDF>
-->
<DIV id=postToolbar></DIV>
<DIV id=commentmessage><A name=commentmessage></A></DIV>
<DIV id=comments>
<H3>评论数</H3></DIV></DIV></TD>
<TD class=sidebar-bg rowSpan=2>
<DIV class=sidebar-a>
<H3>公告</H3>
<DIV id=News>个人随笔,工作笔记, 网络文摘,仅此而已! ===========赵伟冰 </DIV>
<H3>编程资源</H3>
<UL>
<LI><A
id=_ctl0__ctl0__ctl0__ctl0_Linkcategorylist1__ctl0_Categories__ctl0_Links__ctl1_Link
href="http://www.91code.net/">破刀客VC</A>
<LI><A
id=_ctl0__ctl0__ctl0__ctl0_Linkcategorylist1__ctl0_Categories__ctl0_Links__ctl2_Link
href="http://www.vczx.com/">VC在线</A>
<LI><A
id=_ctl0__ctl0__ctl0__ctl0_Linkcategorylist1__ctl0_Categories__ctl0_Links__ctl3_Link
href="http://tech.ccidnet.com/pub/column/c1082.html">赛迪VC</A>
<LI><a
id="_ctl0__ctl0__ctl</LI></UL></DIV></TR></TBODY></TABLE></FORM></BODY></HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -