U-Boot启动过程
开发板上电后,执行U-Boot的第一条指令,然后顺序执行U-Boot启动函数。看一下board/smdk2410/u-boot.lds这个链接脚本,可以知道目标程序的各部分链接顺序。第一个要链接的是cpu/arm920t/start.o,那么U-Boot的入口指令一定位于这个程序中。下面分两阶段介绍启动流程:
第一阶段
.cpu/arm920t/start.S
这个汇编程序是U-Boot的入口程序,开头就是复位向量的代码。
_start: b reset //复位向量
ldr pc, _undefined_instruction
ldr pc, _software_interrupt
ldr pc, _prefetch_abort
ldr pc, _data_abort
ldr pc, _not_used
ldr pc, _irq //中断向量
ldr pc, _fiq //中断向量
…
/* the actual reset code */
reset: //复位启动子程序
/*
设置CPU为SVC32模式 */
mrs r0,cpsr
bic r0,r0,#0x1f
orr r0,r0,#0xd3
msr cpsr,r0
/* 关闭看门狗 */
…… ……
relocate:
/* 把U-Boot重新定位到RAM */
adr r0, _start /* r0是代码的当前位置 */
ldr r1, _TEXT_BASE /*_TEXT_BASE是RAM中的地址 */
cmp r0, r1 /*
比较r0和r1,判断当前是从Flash启动,还是RAM
*/
beq stack_setup /*
如果r0等于r1,跳过重定位代码 */
/* 准备重新定位代码 */
ldr r2, _armboot_start
ldr r3, _bss_start
sub r2, r3, r2 /* r2
得到armboot的大小
*/
add r2, r0, r2 /* r2
得到要复制代码的末尾地址 */
copy_loop: /* 重新定位代码 */
ldmia r0!, {r3-r10} /*从源地址[r0]复制 */
stmia r1!, {r3-r10} /*
复制到目的地址[r1] */
cmp r0, r2 /*
复制数据块直到源数据末尾地址[r2] */
ble copy_loop
/* 初始化堆栈等
*/
stack_setup:
ldr r0, _TEXT_BASE /*
上面是128 KiB重定位的u-boot */
sub r0, r0, #CFG_MALLOC_LEN /*
向下是内存分配空间 */
sub r0, r0, #CFG_GBL_DATA_SIZE /*
然后是bdinfo结构体地址空间
*/
#ifdef CONFIG_USE_IRQ
sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
sub sp, r0, #12 /*
个字 */
clear_bss:
ldr r0, _bss_start /*
找到bss段起始地址 */
ldr r1, _bss_end /* bss段末尾地址
*/
mov r2, #0x00000000 /*
清零 */
clbss_l:str r2, [r0]
/* bss段地址空间清零循环...
*/
add r0, r0, #4
cmp r0, r1
bne clbss_l
/* 跳转到start_armboot函数入口,_start_armboot字保存函数入口指针
*/
ldr pc, _start_armboot
_start_armboot: .word start_armboot
//start_armboot函数在lib_arm/board.c中实现
第二阶段
.lib_arm/board.c
start_armboot是U-Boot执行的第一个C语言函数,完成系统初始化工作,进入主循环,处理用户输入的命令。
.init_sequence[]
init_sequence[]数组保存着基本的初始化函数指针。
init_fnc_t *init_sequence[] = {
cpu_init, /*
基本的处理器相关配置 -- cpu/arm920t/cpu.c */
board_init, /* 基本的板级相关配置 -- board/smdk2410/smdk2410.c */
interrupt_init, /*
初始化中断处理 -- cpu/arm920t/s3c24x0/interrupt.c */
env_init, /*
初始化环境变量 -- common/cmd_flash.c */
init_baudrate, /*
初始化波特率设置 -- lib_arm/board.c */
serial_init, /*
串口通讯设置 -- cpu/arm920t/s3c24x0/serial.c */
console_init_f, /*
控制台初始化阶段1 -- common/console.c */
display_banner, /*
打印u-boot信息 -- lib_arm/board.c */
dram_init, /*
配置可用的RAM -- board/smdk2410/smdk2410.c */
display_dram_config, /*
显示RAM的配置大小 -- lib_arm/board.c */
NULL,
};
void start_armboot (void)
{
/* 顺序执行init_sequence数组中的初始化函数 */
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}
/*配置可用的Flash */
size = flash_init ();
display_flash_config (size);
/* _armboot_start
在u-boot.lds链接脚本中定义 */
mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);
/* 配置环境变量*/
env_relocate ();
/* 从环境变量中获取IP地址 */
gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");
/* 以太网接口MAC
地址 */
……
devices_init (); /*
获取列表中的设备 */
jumptable_init ();
console_init_r (); /*
完整地初始化控制台设备 */
enable_interrupts (); /*
使能中断处理 */
/* 通过环境变量初始化 */
if ((s = getenv ("loadaddr")) != NULL) {
load_addr = simple_strtoul (s, NULL, 16);
}
/* main_loop()循环不断执行 */
for (;;)
{
main_loop (); /*
主循环函数处理执行用户命令 -- common/main.c */
}
命令实现
U-Boot作为Bootloader,具备多种引导内核启动的方式。常用的go和bootm命令可以直接引导内核映像启动。U-Boot与内核的关系主要是内核启动过程中参数的传递。
.go命令的实现
/* common/cmd_boot.c */
int do_go (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
ulong addr, rc;
int rcode = 0;
if (argc < 2) {
printf ("Usage:\n%s\n", cmdtp->usage);
return 1;
}
addr = simple_strtoul(argv[1], NULL, 16);
printf ("## Starting application at 0x%08lX ...\n", addr);
rc = ((ulong (*)(int, char []))addr) (--argc, &argv[1]);
/* 运行程序 */
if (rc != 0) rcode = 1;
printf ("## Application terminated, rc = 0x%lX\n", rc);
/*如果是运行linux,这条指令是否能运行?*/
return rcode;
}
go命令调用do_go()函数,跳转到某个地址执行的。如果在这个地址准备好了自引导的内核映像,就可以启动了。尽管go命令可以带变参,实际使用时不用来传递参数。
.bootm命令的实现
/* common/cmd_bootm.c */
int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
…… ……
/* 检查头部 */
if (crc32 (0, (uchar *)data, len) != checksum) {
puts ("Bad Header Checksum\n");
SHOW_BOOT_PROGRESS (-2);
return 1;
}
…… ……
/*解压缩*/
switch (hdr->ih_comp) {
case
IH_COMP_NONE:
if(ntohl(hdr->ih_load) == addr) {
printf (" XIP %s ... ", name);
} else {
#if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG)
size_t l = len;
void *to = (void *)ntohl(hdr->ih_load);
void *from = (void *)data;
printf (" Loading %s ... ", name);
while (l > 0) {
size_t tail = (l > CHUNKSZ) ? CHUNKSZ : l;
WATCHDOG_RESET();
memmove (to, from, tail);
to += tail;
from += tail;
l -= tail;
}
#else /* !(CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG) */
memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len);
#endif /* CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG */
}
break;
case
IH_COMP_GZIP:
printf (" Uncompressing %s ... ", name);
if (gunzip ((void *)ntohl(hdr->ih_load), unc_len,
(uchar *)data, &len) != 0) {
puts ("GUNZIP ERROR - must RESET board to recover\n");
SHOW_BOOT_PROGRESS (-6);
do_reset (cmdtp, flag, argc, argv);
}
break;
#ifdef CONFIG_BZIP2
case IH_COMP_BZIP2:
printf (" Uncompressing %s ... ", name);
/*
* If we've got less than 4 MB of malloc() space,
* use slower decompression algorithm which requires
* at most 2300 KB of memory.
*/
i = BZ2_bzBuffToBuffDecompress ((char*)ntohl(hdr->ih_load),
&unc_len, (char *)data, len,
CFG_MALLOC_LEN < (4096 * 1024), 0);
if (i != BZ_OK) {
printf ("BUNZIP2 ERROR %d - must RESET board to recover\n", i);
SHOW_BOOT_PROGRESS (-6);
udelay(100000);
do_reset (cmdtp, flag, argc, argv);
}
break;
#endif /* CONFIG_BZIP2 */
default:
if (iflag)
enable_interrupts();
printf ("Unimplemented compression type %d\n", hdr->ih_comp);
SHOW_BOOT_PROGRESS (-7);
return 1;
}
}
…… …… ……
switch (hdr->ih_os) {
default: /* handled by (original) Linux case */
case
IH_OS_LINUX:
do_bootm_linux (cmdtp, flag, argc, argv,
addr, len_ptr, verify);
break;
case
IH_OS_NETBSD:
do_bootm_netbsd (cmdtp, flag, argc, argv,
addr, len_ptr, verify);
break;
case
IH_OS_RTEMS:
do_bootm_rtems (cmdtp, flag, argc, argv,
addr, len_ptr, verify);
break;
case
IH_OS_VXWORKS:
do_bootm_vxworks (cmdtp, flag, argc, argv,
addr, len_ptr, verify);
break;
case
IH_OS_QNX:
do_bootm_qnxelf (cmdtp, flag, argc, argv,
addr, len_ptr, verify);
break;
}
bootm命令调用do_bootm函数。这个函数专门用来引导各种操作系统映像,可以支持引导Linux、vxWorks、QNX等操作系统。引导Linux的时候,调用do_bootm_linux()函数。
.do_bootm_linux函数的实现
/* lib_arm/armlinux.c */
void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],
ulong addr, ulong *len_ptr, int verify)
{
theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);
… … … …
/* we assume that the kernel is in place */
printf ("\nStarting kernel ...\n\n");
… … … …
theKernel (0, bd->bi_arch_number, bd->bi_boot_params);
/*启动内核,传递启动参数*/
}
do_bootm_linux()函数是专门引导Linux映像的函数,它还可以处理ramdisk文件系统的映像。这里引导的内核映像和ramdisk映像,必须是U-Boot格式的。U-Boot格式的映像可以通过mkimage工具来转换,其中包含了U-Boot可以识别的符号。
U-Boot启动过程的更多相关文章
- Spring Boot启动过程(四):Spring Boot内嵌Tomcat启动
之前在Spring Boot启动过程(二)提到过createEmbeddedServletContainer创建了内嵌的Servlet容器,我用的是默认的Tomcat. private void cr ...
- Spring Boot启动过程(七):Connector初始化
Connector实例的创建已经在Spring Boot启动过程(四):Spring Boot内嵌Tomcat启动中提到了: Connector是LifecycleMBeanBase的子类,先是设置L ...
- Spring Boot启动过程及回调接口汇总
Spring Boot启动过程及回调接口汇总 链接: https://www.itcodemonkey.com/article/1431.html 来自:chanjarster (Daniel Qia ...
- Spring Boot启动过程(三)
我已经很精简了,两篇(Spring Boot启动过程(一).pring Boot启动过程(二))依然没写完,接着来. refreshContext之后的方法是afterRefresh,这名字起的真.. ...
- Spring Boot启动过程(一)
之前在排查一个线上问题时,不得不仔细跑了很多遍Spring Boot的代码,于是整理一下,我用的是1.4.3.RELEASE. 首先,普通的入口,这没什么好说的,我就随便贴贴代码了: SpringAp ...
- Spring Boot启动过程(二)
书接上篇 该说refreshContext(context)了,首先是判断context是否是AbstractApplicationContext派生类的实例,之后调用了强转为AbstractAppl ...
- Spring Boot 启动过程及 自定义 Listener等组件
一.启动过程 二.自定义组件 package com.example.jdbc.listener; import org.springframework.context.ApplicationCont ...
- 转:Spring Boot启动过程
之前在排查一个线上问题时,不得不仔细跑了很多遍Spring Boot的代码,于是整理一下,我用的是1.4.3.RELEASE. 首先,普通的入口,这没什么好说的,我就随便贴贴代码了: SpringAp ...
- Spring Boot 启动过程
一切从SpringApplication.run()开始,最终返回一个ConfigurableApplicationContext 构造了一个SpringApplication对象,然后调用它的run ...
- Spring Boot启动过程(六):内嵌Tomcat中StandardHost与StandardContext的启动
看代码有助于线上出现预料之外的事的时候,不至于心慌... StandardEngine[Tomcat].StandardHost[localhost]的启动与StandardEngine不在同一个线程 ...
随机推荐
- editplus 常用正则
EditPlus的查找,替换,文件中查找支持以下的正则表达式: Expression Description\t Tab character.\n New line.. Matches any cha ...
- JAVA设计模式---迭代器模式
1.定义: 提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示. 2.实例:1)需求: 菜单(煎饼屋菜单.餐厅菜单和咖啡菜单)采用不同的集合存取(ArrayList,String[] ...
- 获取sap登陆用户名的中文描述
一.业务场景: 当通过MKPF-USNAM查找ADRP-NAME_LAST时,中间缺少一个表,即USR21.否则,MKPF-USNAM不能和ADRP-PERSNUMBER直接对等. 二.解决方法: D ...
- iOS-iOS调用相机调用相册【将图片保存到本地相册】
设置头部代理 <UINavigationControllerDelegate, UIImagePickerControllerDelegate> 1.调用相机 检测前置摄像头是否可用 - ...
- ABP官方文档翻译 3.2 值对象
值对象 介绍 值对象基类 最佳实践 介绍 "展现领域描述性层面且没有概念性身份的对象称之为值对象."(Eric Evans). 和实体相反,实体有身份标示(Id),值对象没有身份标 ...
- SDN第一次作业
作业链接 你会选择作 网络编程 方向的程序员吗?为什么? 光凭阅读此篇文章我还无法确定以后是否选择作 网络编程 方向的程序员.出于自身知识的匮乏,文章中提到的很多东西都没有概念,全篇一口气阅读下来,给 ...
- 初涉扫码登录:edusoho实现客户端扫码登录(简版)
一.项目简介及需求 edusoho是一套商业版的在线教育平台,项目本身基于symfony2框架开发,现在有一款自己的APP,要求在不多修改edusoho自身代码的基础上,实现客户端对PC端扫码登录.不 ...
- Maven编译问题
Maven构建的Project默认使用JDK1.5进行编译,要想使用JDK1.8进行编译,最好在项目的POM文件中加上以下的字段. <build> <plugins> < ...
- phpstorm使用之——常用快捷键
phpstorm使用之--常用快捷键 使用IDE的根本所在乃是为了提高工作效率. windows下phpstorm的快捷键 ctrl+shift+n查找文件 ctrl+shift+f 在一个目录里查找 ...
- 用最简单的例子实现jQuery图片即时上传
[http://www.cnblogs.com/Zjmainstay/archive/2012/08/09/jQuery_upload_image.html] 最近看了一些jQuery即时上传的插件, ...