📄 嵌入式bootloader技术内幕arm9ads使用arm应用.htm
字号:
tag_header 定义在 Linux 内核源码的include/asm/setup.h 头文件中: </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee
border=1><TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">/* The list ends with an ATAG_NONE node. */
#define ATAG_NONE 0x00000000
struct tag_header {
u32 size; /* 注意,这里size是字数为单位的 */
u32 tag;
};
……
struct tag {
struct tag_header hdr;
union {
struct tag_core core;
struct tag_mem32 mem;
struct tag_videotext videotext;
struct tag_ramdisk ramdisk;
struct tag_initrd initrd;
struct tag_serialnr serialnr;
struct tag_revision revision;
struct tag_videolfb videolfb;
struct tag_cmdline cmdline;
/*
* Acorn specific
*/
struct tag_acorn acorn;
/*
* DC21285 specific
*/
struct tag_memclk memclk;
} u;
};
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR></P>
<P>在嵌入式 Linux 系统中,通常需要由 Boot Loader
设置的常见启动参数有:ATAG_CORE、ATAG_MEM、ATAG_CMDLINE、ATAG_RAMDISK、ATAG_INITRD等。 </P>
<P>比如,设置 ATAG_CORE 的代码如下: </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee
border=1><TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">params = (struct tag *)BOOT_PARAMS;
params->hdr.tag = ATAG_CORE;
params->hdr.size = tag_size(tag_core);
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><BR></P>
<P>其中,BOOT_PARAMS 表示内核启动参数在内存中的起始基地址,指针 params 是一个 struct tag 类型的指针。宏
tag_next() 将以指向当前标记的指针为参数,计算紧临当前标记的下一个标记的起始地址。注意,内核的根文件系统所在的设备ID就是在这里设置的。
</P>
<P>下面是设置内存映射情况的示例代码: </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee
border=1><TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">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><BR></P>
<P>可以看出,在 memory_map[]数组中,每一个有效的内存段都对应一个 ATAG_MEM 参数标记。 </P>
<P>Linux
内核在启动时可以以命令行参数的形式来接收信息,利用这一点我们可以向内核提供那些内核不能自己检测的硬件参数信息,或者重载(override)内核自己检测到的信息。比如,我们用这样一个命令行参数字符串"console=ttyS0,115200n8"来通知内核以
ttyS0 作为控制台,且串口采用 "115200bps、无奇偶校验、8位数据位"这样的设置。下面是一段设置调用内核命令行参数字符串的示例代码:
</P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee
border=1><TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">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><BR></P>
<P>请注意在上述代码中,设置 tag_header 的大小时,必须包括字符串的终止符'\0',此外还要将字节数向上圆整4个字节,因为
tag_header 结构中的size 成员表示的是字数。 </P>
<P>下面是设置 ATAG_INITRD 的示例代码,它告诉内核在 RAM 中的什么地方可以找到 initrd 映象(压缩格式)以及它的大小:
</P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee
border=1><TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console"> 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><BR></P>
<P>下面是设置 ATAG_RAMDISK 的示例代码,它告诉内核解压后的 Ramdisk 有多大(单位是KB): </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee
border=1><TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">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>
<P>最后,设置 ATAG_NONE 标记,结束整个启动参数列表: </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee
border=1><TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">static void setup_end_tag(void)
{
params->hdr.tag = ATAG_NONE;
params->hdr.size = 0;
}
</FONT></CODE></PRE></TD></TR></TBODY></TABLE></P>
<P><B>3.2.5 调用内核</B> </P>
<P>Boot Loader 调用 Linux 内核的方法是直接跳转到内核的第一条指令处,也即直接跳转到 MEM_START+0x8000
地址处。在跳转时,下列条件要满足: </P>
<P>1. CPU 寄存器的设置: </P>
<UL>
<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>
<LI>必须禁止中断(IRQs和FIQs); <BR><BR>
<LI>CPU 必须 SVC 模式; <BR><BR></LI></UL>
<P>3. Cache 和 MMU 的设置:</P>
<UL>
<LI>MMU 必须关闭; <BR><BR>
<LI>指令 Cache 可以打开也可以关闭; <BR><BR>
<LI>数据 Cache 必须关闭; </LI></UL>
<P>如果用 C 语言,可以像下列示例代码这样来调用内核: </P>
<P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee
border=1><TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">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><BR></P>
<P>注意,theKernel()函数调用应该永远不返回的。如果这个调用返回,则说明出错。 </P>
<P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt=""
src="嵌入式BootLoader技术内幕ARM9ADS使用ARM应用.files/20066793129734.gif"
width="100%"><BR><IMG height=6 alt=""
src="嵌入式BootLoader技术内幕ARM9ADS使用ARM应用.files/20066793130580.gif"
width=8 border=0></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD><IMG height=4 alt=""
src="嵌入式BootLoader技术内幕ARM9ADS使用ARM应用.files/20066793130580.gif"
width="100%"><BR>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><BR></TD>
<TD vAlign=top align=right><B><FONT face=Verdana
color=#996699></FONT></B></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE></P>
<P><A name=4><SPAN class=atitle><STRONG><FONT size=4>4.
关于串口终端</FONT></STRONG></SPAN></A></P>
<P>在 boot loader
程序的设计与实现中,没有什么能够比从串口终端正确地收到打印信息能更令人激动了。此外,向串口终端打印信息也是一个非常重要而又有效的调试手段。但是,我们经常会碰到串口终端显示乱码或根本没有显示的问题。造成这个问题主要有两种原因:(1)
boot loader 对串口的初始化设置不正确。(2) 运行在 host
端的终端仿真程序对串口的设置不正确,这包括:波特率、奇偶校验、数据位和停止位等方面的设置。 </P>
<P>此外,有时也会碰到这样的问题,那就是:在 boot loader 的运行过程中我们可以正确地向串口终端输出信息,但当 boot loader
启动内核后却无法看到内核的启动输出信息。对这一问题的原因可以从以下几个方面
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -