您的位置:首页 > 其它

嵌入式系统学习(六)-bootloader基础及分析

2016-10-01 15:21 197 查看
为启动ARM Linux系统,BootLoader需要初始化多种设备,最终调用Linux内核,并向内核传递硬件相关的信息。
    BootLoader最终需要提供以下功能:
a. 建立和初始化内存
b. 初始化一个串口
c. 检测设备类型
d. 设置内核tagged列表(描述硬件参数)
e. 加载initramfs
f. 调用内核镜像
    调用内核时,需要满足以下要求:
(1)CPU寄存器设置
r0 =0,
r1 = 机器类型,
r2 =tagged list在内存中的物理地址或设备树块(dtb)在内存中的物理地址。
(2)CPU模式
所有形式的中断必须被禁止;
对于不包含ARM虚拟化扩展的CPU,CPU必须处于SVC模式;
包含虚拟化扩展支持的CPU必须进入HYP模式。
(3) 缓存,MMU
MMU必须关闭;
指令缓存可以是打开或者关闭;
数据缓存必须关闭。
(4)BootLoader应该通过直接跳至内核镜像的第一条指令地址来调用内核。
Bootloader

代码运行流程如下:
 首先,可以看到Bootloader的源码目录如下:



U-boot 的启动流程包括两个阶段,第一阶段进行一些基本的初始化动作,为启动第二阶段的主体做准备,此阶段代码由汇编代码写成。第二阶段是进行系统的初始化工作,并准备引导操作系统。下面对这两个阶段进行详细的分析。
从链接脚本文件u-boot.lds(\uboot_nanopi2-nanopi2-lollipop-mr1\arch\arm\cpu\slsiap\u-boot.lds)中可以找到代码的起始:



从中知道程序的入口点是_start,定位于arch/arm/cpu/slsiap/s5p4418/start.o
(即u-boot启动的第一阶段),我们通过分析start.S来看第一阶段进行了怎样的设置.
在start.S中,有:



在上面代码中,设置了u-boot的主入口,跳入了后面的reset。还有对PC赋值,即是实现代码跳转,设置发生“未定义指令”, “软件中断”,“预取指错误”,“数据错误”,“未定义”,“(普通)中断”,“快速中断”的时候,系统所要去执行的代码地址。



Uboot代码从这里开始执行,让系统进入SVC管理模式。首先mrs    r0, cpsr 初始化cpsr,而bic   r0,
r0, #0x1f 使用r0[0:5] = 0, bic=位清零。在orr   r0, r0, #0xd3
中,11010011为cpsr的最终值,其中中断关闭,
模式设置为SVC模式 。最后一句代码将CPSR的值赋给R0寄存器。



上述代码是对cp15协处理器进行初始化设置,进行了TLBs、icache、BP数组的无效处理,并禁止MMU代理和caches。



在这段代码中,跳入了主板确切的初始化位置来进行各种参数的设置。在这里调用board/lowlevel_init.S中的lowlevel_init函数,对系统总线的初始化,初始化了连接存储器的位宽、速度、刷新率等重要参数。经过这个函数的正确初始化,Nor
Flash、SDRAM才可以被系统使用。



在上述代码中,比较r0和r1,如果不相等,就把代码从flash中复制到ram中。然后进行BSS数据段清理。如果MMU被设置为可使用的话,则可以跳到mmu_turn_on中进行打开处理。



在上面代码中,设置了第二阶段的入口位置,即第二阶段入口函数board_init_r,下面我们进入board.c文件中分析:
在这个源代码文件中,完成的主要工作包括:
(1)  为U-boot内部私有数据分配存储空间,并清零;
(2)  依次调用函数指针数组init_sequence
中定义的函数进行一系列的初始化;
(3)  如果系统支持 NOR Flash,调用flash_init ()和display_flash_config()初始化并显示检测到的器件信息(AT91SAM9260EK 不需要);
(4)  如果系统支持 LCD
或VFD,调用lcd_setmem()或vfd_setmem()计算帧缓冲(Framebuffer)大小,然后在BSS 数据段之后为Framebuffer 分配空间,初始化gd->fb_base为Framebuffer的起始地址(AT91SAM9260EK 不需要);
(5)  调用mem_malloc_init()进行存储分配系统(类似于C 语言中的堆)的初始化和空间分配;
(6)  如果系统支持 NAND Flash,调用nand_init ()进行初始化;
(7)  如果系统支持 DataFlash,调用AT91F_DataflashInit()和dataflash_print_info()进行初始化并显示检测到的器件信息;
(8)  调用 env_relocate()进行环境变量的重定位,即从Flash 中搬移到RAM 中;
(9)  如果系统支持 VFD,调用drv_vfd_init()进行VFD 设备初始化(AT91SAM9260EK不需要);
(10)调用 jumptable_init()进行跳转表初始化,跳转表在global_data中;
(11)调用 console_init_r()进行控制台初始化;
(12)如果需要,调用 misc_init_r ()进行杂项初始化;
(13)调用 enable_interrupts()打开中断;
(14)如果需要,调用board_late_init()进行单板后期初始化;
(15)进入主循环:根据用户的选择启动 linux,或者进入命令循环执行用户输入的命令;
进行源代码流程分析,首先进行一些基本设置:



在上面,第一个方法进行波特率的初始化;第二个方法显示_STACK_START等的地址,比如_start, _bss_start,_bss_end 这些值。



上面的函数用来显示内存的配置,打印出DRAM的大小。



在上面,定义一个初始化的整型指针数组,将在后面被调用。在调用过程中,会依次进行一系列的初始化,分别是初始化环境变量、初始化波特率设置、串口通讯设置、控制台初始化阶段1、打印u-boot信息、显示CPU的配置大小、根据需要显示板的信息、配置可用的RAM。
以下函数内容:



该函数是启动阶段1结束后开始执行的函数,进行主板的初始化配置。我们从上面可以获悉该函数给全局数据变量gd分配内存,对FDT进行地址和空间分配,并且顺序执行init_sequence数组中的初始化函数。该数组在上面分析过,它将依次触发init_sequence定义的函数来进行一系列的初始化操作。
接着有这样一个函数should_load_env:



该函数用来判断是否进行完了uboot的环境加载设置,如果是的话返回1,否则返回0。当进行完环境配置后,会被调用来进行判断。如果是成功的话,进行下一部分的初始化,即进入board_init_r(gd_t *id, ulong dest_addr)函数。



从上面可以看出来,主要有以下几个作用:
a)    itor_flash_len =(ulong)&__rel_dyn_end - (ulong)_start: 设置了flash的长度;
b)    enable_caches():使缓存可以使用;
c)    set_cpu_clk_info():根据需要启动计时器;
d)    serial_initialize():串口初始化;
e)    mem_malloc_init(malloc_start, TOTAL_MALLOC_LEN):初始化堆空间;



f)    flash_size =flash_init():根据需要配置可用的flash;
g)    nand_init():根据命令初始化nandflash;
h)  mmc_initialize(gd->bd):根据需要MMC初始化;



i)    env_relocate():重新定位环境变量;
j)    stdio_init():调用相应驱动函数对硬件设备进行初始化;
k)    jumptable_init():初始化跳转表;
l)    api_init():如果配置了接口,初始化各个接口;
m)    console_init_r():完整地初始化控制台设备;
n)    interrupt_init():中断初始化;enable_interrupts():能使中断处理;
o)    board_late_init():进行单板后期初始化;
p)    eth_initialize(gd->bd):以太网初始化;


在board_init_r(gd_t *id, ulong dest_addr)函数的最后,调用一个for循环,跳入main.c中的main_loop()中去进行uboot启动后的操作,如下:



main_loop()在uboot初始化后,等待进程命令来执行。在该函数里面,进行了Modem功能的设置,打开该功能可以接受其他用户通过电话网络的拨号请求。同时设置U-Boot的版本号,启动延迟功能(s= bootdelay_process())。如果用户按下任意键打断,启动流程,会向终端打印出一个启动菜单。最后自动运行引导内核的命令(autoboot_command(s))。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: