U-Boot启动Linux过程
2015-06-06 23:27
441 查看
U-boot会给Linux Kernel传递很多参数,如:串口,RAM,videofb、MAC地址等。而Linux kernel也会读取和处理这些参数。两者之间通过struct tag来传递参数。U-boot把要传递给kernel的东西保存在struct tag数据结构中,启动kernel时,把这个结构体的物理地址传给kernel;Linux kernel通过这个地址,用parse_tags分析出传递过来的参数。
U-Boot启动Linux过程
[align=left] U-Boot使用标记列表(tagged list)的方式向Linux传递参数。标记的数据结构式是tag,在U-Boot源代码目录include/asm-arm/setup.h中定义如下:[/align]
[align=left]struct tag_header {[/align]
[align=left] u32 size; /* 表示tag数据结构的联合u实质存放的数据的大小*/[/align]
[align=left] u32 tag; /* 表示标记的类型 */[/align]
[align=left]};[/align]
[align=left][/align]
[align=left]struct tag {[/align]
[align=left] struct tag_header hdr;[/align]
[align=left] union {[/align]
[align=left] struct tag_core core;[/align]
[align=left] struct tag_mem32 mem;[/align]
[align=left] struct tag_videotext videotext;[/align]
[align=left] struct tag_ramdisk ramdisk;[/align]
[align=left] struct tag_initrd initrd;[/align]
[align=left] struct tag_serialnr serialnr;[/align]
[align=left] struct tag_revision revision;[/align]
[align=left] struct tag_videolfb videolfb;[/align]
[align=left] struct tag_cmdline cmdline;[/align]
[align=left][/align]
[align=left] /*[/align]
[align=left] * Acorn specific[/align]
[align=left] */[/align]
[align=left] struct tag_acorn acorn;[/align]
[align=left] /*[/align]
[align=left] * DC21285 specific[/align]
[align=left] */[/align]
[align=left] struct tag_memclk memclk;[/align]
[align=left] } u;[/align]
[align=left]};[/align]
U-Boot使用命令bootm来启动已经加载到内存中的内核。而bootm命令实际上调用的是do_bootm函数。对于Linux内核,do_bootm函数会调用do_bootm_linux函数来设置标记列表和启动内核。do_bootm_linux函数在lib_arm/bootm.c
中定义如下:
[align=left]59 int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t *images)[/align]
[align=left]60 {[/align]
[align=left]61 bd_t *bd = gd->bd;[/align]
[align=left]62 char *s;[/align]
[align=left]63 int machid = bd->bi_arch_number;[/align]
[align=left]64 void (*theKernel)(int zero, int arch, uint params);[/align]
[align=left]65 [/align]
[align=left]66 #ifdef CONFIG_CMDLINE_TAG[/align]
[align=left]67 char *commandline = getenv ("bootargs"); /* U-Boot环境变量bootargs */[/align]
[align=left]68 #endif[/align]
[align=left] … …[/align]
[align=left]73 theKernel = (void (*)(int, int, uint))images->ep; /* 获取内核入口地址 */[/align]
[align=left] … …[/align]
[align=left]86 #if defined (CONFIG_SETUP_MEMORY_TAGS) || \[/align]
[align=left]87 defined (CONFIG_CMDLINE_TAG) || \[/align]
[align=left]88 defined (CONFIG_INITRD_TAG) || \[/align]
[align=left]89 defined (CONFIG_SERIAL_TAG) || \[/align]
[align=left]90 defined (CONFIG_REVISION_TAG) || \[/align]
[align=left]91 defined (CONFIG_LCD) || \[/align]
[align=left]92 defined (CONFIG_VFD)[/align]
93 setup_start_tag (bd); /* 设置ATAG_CORE标志
*/
[align=left] … …[/align]
[align=left]100 #ifdef CONFIG_SETUP_MEMORY_TAGS[/align]
[align=left]101 setup_memory_tags (bd); /* 设置内存标记 */[/align]
[align=left]102 #endif[/align]
[align=left]103 #ifdef CONFIG_CMDLINE_TAG[/align]
[align=left]104 setup_commandline_tag (bd, commandline); /* 设置命令行标记 */[/align]
[align=left]105 #endif[/align]
[align=left] … …[/align]
113 setup_end_tag (bd); /* 设置ATAG_NONE标志*/
[align=left]114 #endif[/align]
[align=left]115[/align]
[align=left]116 /* we assume that the kernel is in place */[/align]
[align=left]117 printf ("\nStarting kernel ...\n\n");[/align]
[align=left] … …[/align]
[align=left]126 cleanup_before_linux (); /* 启动内核前对CPU作最后的设置 */[/align]
[align=left]127[/align]
[align=left]128 theKernel (0, machid, bd->bi_boot_params); /* 调用内核 */[/align]
[align=left]129 /* does not return */[/align]
[align=left]130[/align]
[align=left]131 return 1;[/align]
[align=left]132 }[/align]
[align=left] 其中的setup_start_tag,setup_memory_tags,setup_end_tag函数在lib_arm/bootm.c中定义如下:[/align]
[align=left] (1)setup_start_tag函数[/align]
[align=left]static void setup_start_tag (bd_t *bd)[/align]
[align=left]{[/align]
[align=left] params = (struct tag *) bd->bi_boot_params; /* 内核的参数的开始地址 */[/align]
[align=left][/align]
[align=left] params->hdr.tag = ATAG_CORE;[/align]
[align=left] params->hdr.size = tag_size (tag_core);[/align]
[align=left][/align]
[align=left] params->u.core.flags = 0;[/align]
[align=left] params->u.core.pagesize = 0;[/align]
[align=left] params->u.core.rootdev = 0;[/align]
[align=left][/align]
[align=left] params = tag_next (params);[/align]
[align=left]}[/align]
[align=left] 标记列表必须以ATAG_CORE开始,setup_start_tag函数在内核的参数的开始地址设置了一个ATAG_CORE标记。[/align]
[align=left] (2)setup_memory_tags函数[/align]
[align=left]static void setup_memory_tags (bd_t *bd)[/align]
[align=left]{[/align]
[align=left] int i;[/align]
[align=left]/*设置一个内存标记 */[/align]
[align=left] for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { [/align]
[align=left] params->hdr.tag = ATAG_MEM;[/align]
[align=left] params->hdr.size = tag_size (tag_mem32);[/align]
[align=left][/align]
[align=left] params->u.mem.start = bd->bi_dram.start;[/align]
[align=left] params->u.mem.size = bd->bi_dram.size;[/align]
[align=left][/align]
params = tag_next (params);
}
[align=left]}[/align]
setup_memory_tags函数设置了一个ATAG_MEM标记,该标记包含内存起始地址,内存大小这两个参数。
(3)setup_end_tag函数
static
void setup_end_tag (bd_t *bd)
[align=left]{[/align]
params->hdr.tag = ATAG_NONE;
params->hdr.size = 0;
[align=left]}[/align]
标记列表必须以标记ATAG_NONE结束,setup_end_tag函数设置了一个ATAG_NONE标记,表示标记列表的结束。
U-Boot设置好标记列表后就要调用内核了。但调用内核前,CPU必须满足下面的条件:
(1)
CPU寄存器的设置
[align=left]? r0=0[/align]
[align=left]? r1=机器码[/align]
[align=left]? r2=内核参数标记列表在RAM中的起始地址[/align]
(2)
CPU工作模式
[align=left]? 禁止IRQ与FIQ中断[/align]
[align=left]? CPU为SVC模式[/align]
(3)
使数据Cache与指令Cache失效
do_bootm_linux中调用的cleanup_before_linux函数完成了禁止中断和使Cache失效的功能。cleanup_before_linux函数在cpu/arm920t/cpu.中定义:
int cleanup_before_linux
(void)
[align=left]{[/align]
/*
*
this is called just before we call linux
*
it prepares the processor for linux
[align=left] *[/align]
*
we turn off caches etc ...
[align=left] */[/align]
[align=left][/align]
disable_interrupts (); /* 禁止FIQ/IRQ中断 */
[align=left][/align]
/* turn off I/D-cache */
icache_disable(); /* 使指令Cache失效 */
dcache_disable(); /* 使数据Cache失效 */
/* flush I/D-cache */
cache_flush(); /* 刷新Cache */
[align=left][/align]
return 0;
[align=left]}[/align]
由于U-Boot启动以来就一直工作在SVC模式,因此CPU的工作模式就无需设置了。
[align=left]do_bootm_linux中:[/align]
64
void (*theKernel)(int zero, int arch, uint params);
[align=left]… …[/align]
73
theKernel = (void (*)(int, int, uint))images->ep;
[align=left]… …[/align]
128
theKernel (0, machid, bd->bi_boot_params);
第73行代码将内核的入口地址“images->ep”强制类型转换为函数指针。根据ATPCS规则,函数的参数个数不超过4个时,使用r0~r3这4个寄存器来传递参数。因此第128行的函数调用则会将0放入r0,机器码machid放入r1,内核参数地址bd->bi_boot_params放入r2,从而完成了寄存器的设置,最后转到内核的入口地址。
到这里,U-Boot的工作就结束了,系统跳转到Linux内核代码执行。
U-Boot启动Linux过程
[align=left] U-Boot使用标记列表(tagged list)的方式向Linux传递参数。标记的数据结构式是tag,在U-Boot源代码目录include/asm-arm/setup.h中定义如下:[/align]
[align=left]struct tag_header {[/align]
[align=left] u32 size; /* 表示tag数据结构的联合u实质存放的数据的大小*/[/align]
[align=left] u32 tag; /* 表示标记的类型 */[/align]
[align=left]};[/align]
[align=left][/align]
[align=left]struct tag {[/align]
[align=left] struct tag_header hdr;[/align]
[align=left] union {[/align]
[align=left] struct tag_core core;[/align]
[align=left] struct tag_mem32 mem;[/align]
[align=left] struct tag_videotext videotext;[/align]
[align=left] struct tag_ramdisk ramdisk;[/align]
[align=left] struct tag_initrd initrd;[/align]
[align=left] struct tag_serialnr serialnr;[/align]
[align=left] struct tag_revision revision;[/align]
[align=left] struct tag_videolfb videolfb;[/align]
[align=left] struct tag_cmdline cmdline;[/align]
[align=left][/align]
[align=left] /*[/align]
[align=left] * Acorn specific[/align]
[align=left] */[/align]
[align=left] struct tag_acorn acorn;[/align]
[align=left] /*[/align]
[align=left] * DC21285 specific[/align]
[align=left] */[/align]
[align=left] struct tag_memclk memclk;[/align]
[align=left] } u;[/align]
[align=left]};[/align]
U-Boot使用命令bootm来启动已经加载到内存中的内核。而bootm命令实际上调用的是do_bootm函数。对于Linux内核,do_bootm函数会调用do_bootm_linux函数来设置标记列表和启动内核。do_bootm_linux函数在lib_arm/bootm.c
中定义如下:
[align=left]59 int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t *images)[/align]
[align=left]60 {[/align]
[align=left]61 bd_t *bd = gd->bd;[/align]
[align=left]62 char *s;[/align]
[align=left]63 int machid = bd->bi_arch_number;[/align]
[align=left]64 void (*theKernel)(int zero, int arch, uint params);[/align]
[align=left]65 [/align]
[align=left]66 #ifdef CONFIG_CMDLINE_TAG[/align]
[align=left]67 char *commandline = getenv ("bootargs"); /* U-Boot环境变量bootargs */[/align]
[align=left]68 #endif[/align]
[align=left] … …[/align]
[align=left]73 theKernel = (void (*)(int, int, uint))images->ep; /* 获取内核入口地址 */[/align]
[align=left] … …[/align]
[align=left]86 #if defined (CONFIG_SETUP_MEMORY_TAGS) || \[/align]
[align=left]87 defined (CONFIG_CMDLINE_TAG) || \[/align]
[align=left]88 defined (CONFIG_INITRD_TAG) || \[/align]
[align=left]89 defined (CONFIG_SERIAL_TAG) || \[/align]
[align=left]90 defined (CONFIG_REVISION_TAG) || \[/align]
[align=left]91 defined (CONFIG_LCD) || \[/align]
[align=left]92 defined (CONFIG_VFD)[/align]
93 setup_start_tag (bd); /* 设置ATAG_CORE标志
*/
[align=left] … …[/align]
[align=left]100 #ifdef CONFIG_SETUP_MEMORY_TAGS[/align]
[align=left]101 setup_memory_tags (bd); /* 设置内存标记 */[/align]
[align=left]102 #endif[/align]
[align=left]103 #ifdef CONFIG_CMDLINE_TAG[/align]
[align=left]104 setup_commandline_tag (bd, commandline); /* 设置命令行标记 */[/align]
[align=left]105 #endif[/align]
[align=left] … …[/align]
113 setup_end_tag (bd); /* 设置ATAG_NONE标志*/
[align=left]114 #endif[/align]
[align=left]115[/align]
[align=left]116 /* we assume that the kernel is in place */[/align]
[align=left]117 printf ("\nStarting kernel ...\n\n");[/align]
[align=left] … …[/align]
[align=left]126 cleanup_before_linux (); /* 启动内核前对CPU作最后的设置 */[/align]
[align=left]127[/align]
[align=left]128 theKernel (0, machid, bd->bi_boot_params); /* 调用内核 */[/align]
[align=left]129 /* does not return */[/align]
[align=left]130[/align]
[align=left]131 return 1;[/align]
[align=left]132 }[/align]
[align=left] 其中的setup_start_tag,setup_memory_tags,setup_end_tag函数在lib_arm/bootm.c中定义如下:[/align]
[align=left] (1)setup_start_tag函数[/align]
[align=left]static void setup_start_tag (bd_t *bd)[/align]
[align=left]{[/align]
[align=left] params = (struct tag *) bd->bi_boot_params; /* 内核的参数的开始地址 */[/align]
[align=left][/align]
[align=left] params->hdr.tag = ATAG_CORE;[/align]
[align=left] params->hdr.size = tag_size (tag_core);[/align]
[align=left][/align]
[align=left] params->u.core.flags = 0;[/align]
[align=left] params->u.core.pagesize = 0;[/align]
[align=left] params->u.core.rootdev = 0;[/align]
[align=left][/align]
[align=left] params = tag_next (params);[/align]
[align=left]}[/align]
[align=left] 标记列表必须以ATAG_CORE开始,setup_start_tag函数在内核的参数的开始地址设置了一个ATAG_CORE标记。[/align]
[align=left] (2)setup_memory_tags函数[/align]
[align=left]static void setup_memory_tags (bd_t *bd)[/align]
[align=left]{[/align]
[align=left] int i;[/align]
[align=left]/*设置一个内存标记 */[/align]
[align=left] for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { [/align]
[align=left] params->hdr.tag = ATAG_MEM;[/align]
[align=left] params->hdr.size = tag_size (tag_mem32);[/align]
[align=left][/align]
[align=left] params->u.mem.start = bd->bi_dram.start;[/align]
[align=left] params->u.mem.size = bd->bi_dram.size;[/align]
[align=left][/align]
params = tag_next (params);
}
[align=left]}[/align]
setup_memory_tags函数设置了一个ATAG_MEM标记,该标记包含内存起始地址,内存大小这两个参数。
(3)setup_end_tag函数
static
void setup_end_tag (bd_t *bd)
[align=left]{[/align]
params->hdr.tag = ATAG_NONE;
params->hdr.size = 0;
[align=left]}[/align]
标记列表必须以标记ATAG_NONE结束,setup_end_tag函数设置了一个ATAG_NONE标记,表示标记列表的结束。
U-Boot设置好标记列表后就要调用内核了。但调用内核前,CPU必须满足下面的条件:
(1)
CPU寄存器的设置
[align=left]? r0=0[/align]
[align=left]? r1=机器码[/align]
[align=left]? r2=内核参数标记列表在RAM中的起始地址[/align]
(2)
CPU工作模式
[align=left]? 禁止IRQ与FIQ中断[/align]
[align=left]? CPU为SVC模式[/align]
(3)
使数据Cache与指令Cache失效
do_bootm_linux中调用的cleanup_before_linux函数完成了禁止中断和使Cache失效的功能。cleanup_before_linux函数在cpu/arm920t/cpu.中定义:
int cleanup_before_linux
(void)
[align=left]{[/align]
/*
*
this is called just before we call linux
*
it prepares the processor for linux
[align=left] *[/align]
*
we turn off caches etc ...
[align=left] */[/align]
[align=left][/align]
disable_interrupts (); /* 禁止FIQ/IRQ中断 */
[align=left][/align]
/* turn off I/D-cache */
icache_disable(); /* 使指令Cache失效 */
dcache_disable(); /* 使数据Cache失效 */
/* flush I/D-cache */
cache_flush(); /* 刷新Cache */
[align=left][/align]
return 0;
[align=left]}[/align]
由于U-Boot启动以来就一直工作在SVC模式,因此CPU的工作模式就无需设置了。
[align=left]do_bootm_linux中:[/align]
64
void (*theKernel)(int zero, int arch, uint params);
[align=left]… …[/align]
73
theKernel = (void (*)(int, int, uint))images->ep;
[align=left]… …[/align]
128
theKernel (0, machid, bd->bi_boot_params);
第73行代码将内核的入口地址“images->ep”强制类型转换为函数指针。根据ATPCS规则,函数的参数个数不超过4个时,使用r0~r3这4个寄存器来传递参数。因此第128行的函数调用则会将0放入r0,机器码machid放入r1,内核参数地址bd->bi_boot_params放入r2,从而完成了寄存器的设置,最后转到内核的入口地址。
到这里,U-Boot的工作就结束了,系统跳转到Linux内核代码执行。
相关文章推荐
- Linux 自检和 SystemTap
- Linux内核链表实现过程
- C++中Semaphore内核对象用法实例
- 修改内核 内存分配 root、文件系统和内核镜像的位置
- 移植linux-2.6.30.4到S3C2440
- 看《Linux0.11内核完全注释2.01》的方法
- 升级LINUX内核(支持8G内存)的命令
- FreeBSD系统优化部分内核参数调整中文注释
- Linux2.6X内核中文件相关结构体总结
- 内核的主要配置文件的详细说明
- redhat AS4内核配置更改再编译
- Linux启动添加内核参数简介
- 几个重要的Linux操作系统 内核文件介绍
- linux 2.4内核编译详解
- linux2.6内核编译方法详述
- 自己动手编译Linux内核
- linux 2.4内核编译详解
- selinux介绍及关闭方式!
- 如何编译内核
- linux 每日学一点《 Linux内核编译配置过程》