Linux系统启动之——u-boot启动
来源:互联网 发布:大数据与信息安全 编辑:程序博客网 时间:2024/06/14 18:25
操作系统是人机交互的接口,其管理着计算机的硬件,例如内存,CUP,外设等。系统上电后,操作系统是怎么启动的呢?在嵌入式中,操作系统是由bootloader引导起来的,linux在嵌入式上应用时,其bootloader是u-boot。这篇文章讲述linux系统的启动,和大家一起学习学习。
U-boot的启动分成两个阶段:stage1和stage2。Stage1使用汇编语言编写,通常与CPU体系结构紧密相关,如处理器初始化,内存初始化;该阶段还会建立堆栈和代码段,复制stage2阶段的代码到内存。Stage2阶段一般包括:初始化flash器件,检测系统内存影像,初始化串口等外设,初始控制台接收用户从串口发来的命令并处理等。该阶段代码使用C编写,用于加载操作系统内核。第一阶段的启动流程如图1:
具体的在arm处理器上的启动流程如图2。该图以关键函数作为流程的某一阶段的名称,以便于参考具体代码,更便于了解学习u-boot的启动流程。
.globl _start //u-boot的启动入口
_start: b reset//复位向量;无条件跳转到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//快速中断向量
_undefined_instruction: .word undefined_instruction//定义中断向量表入口地址
_software_interrupt: .word software_interrupt
_prefetch_abort: .word prefetch_abort
_data_abort: .word data_abort
_not_used: .word not_used
_irq: .word irq
_fiq: .word fiq
......
//defined at /arch/arm/cpu/arm_cortexa8/start.s
上面代码即为u-boot入口_start。由code可以看到,进入_start后无条件跳转到reset标号。_start标号下面的代码主要是一些伪指令,设置全局变量,供启动程序吧U-boot影像从flash存储器复制到内存中。
reset:
/*
* set the cpu to SVC32 mode
*/
mrs r0, cpsr
bic r0, r0, #0x1f
orr r0, r0, #0xd3
msr cpsr,r0
......
/* the mask ROM code should have PLL and others stable */
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_crit //跳到开发板相关初始化代码
#endif
Reset标号处的代码是上电时最先执行的代码,其作用清除中断,设置watchdog,设置时钟等,最后部分是跳转到cpu_init_crit。
cpu_init_crit标志处的代码主要是对初始化处理器的关键寄存器;例如刷新cache和TLB(Translation Lookaside Buffer,:旁路缓冲,完成虚拟地址和物理地址的映射关系),关闭MMC(Memory Management Unit; 内存管理单元),之后跳到lowlevel_init标志处执行代码。
lowlevel_init主要工作,计算SMRDATA(开发板上内存映射的配置)需要加载的内存地址和大小,复制SDMDATA到内存,进行内存的配置。Lowlevel_init执行完之后返回,返回后去执行relocate标志处代码。
......
#ifndef CONFIG_SKIP_RELOCATE_UBOOT
relocate: @ relocate U-Boot to RAM
adr r0, _start @ r0 <- current position of code
..........
ldr pc, _start_armboot @ jump to C code
_start_armboot: .word start_armboot//C code的入口,之后开始执行C code
.......
上面是部分relocate标志处的关键代码,该部分代码首先检查当前是否是在内存中执行,如果不是则复制代码到内存。复制代码到内存需要完成获取当前代码地址,对应的内存地址,stage2代码的地址和内存地址,stage2的长度,然后完成复制。然后建立内存堆栈,初始化bss段等,最后跳到start_armboot()。
start_armboot()之前是u-boot的stage1,全是汇编语言编写。从此开始是stage2,从此开始使用C语言编写。
void start_armboot (void)
{....
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}//该循环进行一些必要的初始化例如CPU,中断, evn等
.....
//进一步进行初始化,例如控制台,串口等
/* main_loop() can return to retry autoboot, if so just run it again. */
for (;;) {
main_loop ();//进入主循环
}
}
start_armboot()是整个Uboot最重要的部分之一,该函数(1)完成系统大部分的初始化,例如flash存储器配置,显示flash存储器配置、计算FrameBuffer、设置FrameBuffer内存大小和内存起始地址、设置环境变量、初始化控制台,cup、ram、board初始化,初始化跳转表,打开中断,配置网卡,外设初始化等。(2)初始化全局变量gb,设置gb成员的初始值。在上面示例代码的for循环中,init_sequence[]是函数数组,数组元素是一些重要的函数名,这些函数完成一些初始化,其中,board_init()中有语句gd->bd->bi_arch_number = MACH_TYPE_DM385EVM; /* address of boot parameters */ gd->bd->bi_boot_params = PHYS_DRAM_1 + 0x100; 红色字体语句是对u-boot的参数地址赋值,该地址传递给kernel,kernel就可以读取u-boot传递过来的参数。(3)跳掉main_loop()主循环。通过for死循环调用main_loop()函数,作用是防止main_loop()函数开始的初始化代码调用失败后重新执行初始操作,保证程序能进入到U-boot的命令。
Main_loop()是u-boot启动过程中的最后一部分,该函数做的都是与具体平台无关的工作,主要包括初始化启动次数限制机制,设置软件版本,打印启动信息,初始化hash,检验启动延迟,运行boot命令等。到这里u-boot的启动已经完成,接下来就是要引导kernel启动。在main_loop()的最后有一个死循环读取命令,解析命令。读取到有用命令时,运行rum_command()解析命令,来引导kernel的启动。
接下来是引导kernel启动。引导kernel的动作主要由do_bootm()完成。
int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{......
for (i = 0; i < ARRAY_SIZE(boot_os); i++)
if (boot_os[i] != NULL)
boot_os[i] += gd->reloc_off;
.....
if (bootm_start(cmdtp, flag, argc, argv))
return 1;
.....
}//defined at /common/cmd_bootm.c
For循环中数组boot_os[]是函数数组,其成员全部是函数名。根据不同的系统,注册不同的函数。Linux系统注册函数为do_bootm_linux。其后调用bootm_start(),该函数最主要的工作就是初始化images这个全局变量;其中语句
/* find kernel entry point */
if (images.legacy_hdr_valid) {
images.ep = image_get_ep (&images.legacy_hdr_os_copy);
#if defined(CONFIG_FIT)
} else if (images.fit_uname_os) {
ret = fit_image_get_entry (images.fit_hdr_os,
images.fit_noffset_os, &images.ep);
if (ret) {
puts ("Can't get entry point property!\n");
return 1;
}
#endif
images.ep即为操作系统的入口地址。上面的语句作用即为获取操作系统的入口地址。在do_bootm_linux()中,{……void (*theKernel)(int zero, int arch, uint params); ……. theKernel = (void (*)(int, int, uint))images->ep; theKernel (0, machid, bd->bi_boot_params); ……..},这样程序跳到images.ep所指的地址执行,也就是开始kernel的启动,到这里,uboot启动全部完成。
由于对linux的理解不够深入,甚至可以说十分浅显,恳请各位大侠帮小弟解答以下疑惑:
疑问:1 u-boot的入口_start处,程序进入_start就无条件跳转到reset,并且该跳转是不返回的跳转,从reset代码可以一步一步的进入stage2,然后启动kernel;_start中reset标号下面的代码什么时候执行?
2 u-boot的stage2阶段start_armboot()的for循环,函数数组init_sequence[]中的函数什么时候执行的?相同的疑问,do_bootm()中的for循环boot_os[]中的函数什么时候执行的?
3 从main_loop()怎么跳到do_bootm()的?rum_command()与U_BOOT_CMD( bootm, CONFIG_SYS_MAXARGS, 1, do_bootm, ………}有什么关系?
- Linux系统启动之——u-boot启动
- U-BOOT全线移植分析系列之四——U-boot如何引导Linux内核启动
- 嵌入式Linux系统启动过程 u-boot
- u—boot 启动 学习
- 【Linux 移植 】——4、移植 u-boot-2012.04.01 之 支持NAND启动
- u-boot系统启动流程
- u-boot系统启动流程
- u-boot系统启动流程
- u-boot系统启动流程
- 用u-boot启动linux
- u-boot启动linux内核
- U-Boot启动Linux过程
- U-boot启动流程——eloader
- U—boot的启动步骤笔记
- u-boot系统启动流程分析
- u-boot之u-boot-2009.11启动过程分析
- u-boot之u-boot-2009.11启动过程分析
- u-boot之u-boot-2009.11启动过程分析
- 排序之简单选择排序(Simple Selection Sort)
- Linux学习笔记:DNS
- 转载:Unix环境中Real time, User time and Sys time
- WPF 的另类资源方式 Resources.resx
- 在iOS中判断系统是24小时时间制还是12小时时间制方法
- Linux系统启动之——u-boot启动
- solr整合IKAnalyzer后动态添加词汇不需要重启解决方案
- CodeSmith 学习积累
- JavaScript总结
- MVC模式(model2模式)实例--向数据库添加书目信息
- Ubuntu13.10下SDKmanager的更新
- 浅谈C++多态性
- ssacanf\Sprintf格式化字符串
- ACM通过100