全志平台linux启动流程分析

来源:互联网 发布:软件评测师真题2015 编辑:程序博客网 时间:2024/05/18 02:07

一、BROM阶段

       机器上电之后会执行固化在BROM里面的一段引导程序,这个程序会依次遍历所有支持的启动介质,直到找到第一个支持的。目前支持的启动介质是sd/mmc卡、nand和spinor。当程序初始化启动介质成功后,就从固定位置读入Bootloader的Boot0到SRAM,然后跳到SRAM执行。

       下面展示了BROM的执行流程



二、Bootloader阶段

       Bootloader是全志平台上从小系统一直沿用下来的内核加载器,在这里的主要职责是加载U-Boot到DRAM。

       Bootloader分为两个部分,分别是Boot0和Boot1。

       Boot0:初始化DRAM,加载Boot1到DRAM;

       Boot1:调频,加载U-Boot到DRAM;

       为什么Bootloader要划分成Boot0和Boot1两个部分?因为在Bootloader阶段,使用的SRAM大小是32KB,除去C运行环境需要的栈空间,可用的空间在24KB左右,这点不足以载入整个Bootloader。因此,需要将Bootloader划分成两个部分,尽可能将繁重的任务放在Boot1执行,这个情况类似于Linux系统中断执行环境的上半部和下半部。

       1. boot0执行过程


       2. boot1的执行过程



       Boot1会进行一次系统调频,将CPU的频率调到用户在sys_config1.fex target段配置的boot_clock。

       如何在Boot1让机器进入升级模式?

       (1)按住power键,再按任意键3下;

       (2)接上串口启动,进入Boot1后在键盘输入2;

       如何替换Bootloader分区的内容?

       接上串口启动,进入Boot1后在键盘输入1,USB会挂载Bootloader分区到PC上,卷标是“Volume”,替换掉相关的文件之后重启机器即可生效。Boot1会检测低电关机,以及插入火牛开机的情况进入关机程序。后者需要在sys_config1.fex里配置。


三、u-boot阶段

       概括地说,U-Boot引导内核分为两个阶段,第一阶段负责重定位U-Boot到最高地址,第二阶段才是真正的引导内核。

       1. 第一阶段



       在第一阶段会关闭I/D cache和MMU,因此,整个U-Boot是直接运行在物理地址上。


       2. 第二阶段


       U-Boot第二阶段有一个完整、宽松的C环境,不再受制于栈空间,各个平台可以在这个阶段完成一些复杂的操作,以求达到定制的目的。

       a. board init

       执行平台相关的初始化,这些比较复杂,不适宜在第一阶段完成。这部分的功能在board/allwinner目录下实现。

       b. device init

       主要是根据用户的编译配置选择初始化对应的存储设备。这个地方可以改进,比如根据用户的配置文件

       sys_config1.fex选择初始化对应的存储设备。这样可以做到一份u-boot.bin适应不同的存储设备。

       c. env relocate

       初始化环境变量。

       d. board later init

       初始化fastboot和android recovery,可能修改bootcmd,影响引导流程。

       e. main loop

       U-Boot的主程序,负责引导内核以及处理用户的命令请求。这部分的功能在common目录下实现。


四、内核启动

       在我们平台上使用的是非压缩的内核(bImage),因此内核的启动省去了自解压的过程。内核的链接依赖链接脚本vmlinux.lds,我们平台使用了ARM的内核,对应的链接脚本是arch/arm/kernel/vmlinux.lds。

       1. 调用__lookup_processor_type@head-common.S查找处理器信息

       2. 调用__create_page_tables@head.S为内核自身创建页表

       3. 调用处理器底层初始化函数__v7_setup@arch/arm/mm/proc-v7.S,初始化 MMU,Cache,TLB

       4. 调用__enable_mmu@head.S使能MMU

       5. 调用__mmap_switched@head-common.S重定位数据段,清零BSS,然后跳转到C函数入口        

           start_kernel@init/main.c,start_kernel()函数是内核初始化 C 语言部分的主体。这个函数完成系统底层基本机

           制,包括处理器、存储管理系统、进程管理系统、中断机制、定时机制等的初始化工作。由于这个函数过于复 

           杂,这里仅阐述关键点。

       6. 调用setup_arch完成架构相关的初始化

       7. 调用pidhash_init初始化pid hast机制

       8. 调用sched_init初始化调度器

       9. 调用init_IRQ初始化中断机制

       10. 调用softirq_init完成软中断初始化

       11. 调用local_irq_enable打开中断

       12. 调用console_init完成控制台初始化

       13. 调用rest_init

             由上可知,rest_init函数创建了两个内核线程,分别是kernel_init和kthreadd。kernel_init函数将完成设备驱动

             程序的初始化,并调用init_post函数启动用户空间的init进程。kthreadd的作用是管理调度其他的内核线程。

       14. 调用do_basic_setup函数初始化设备,完成外设及其驱动程序(直接编译进内核的模块)的加载和初始化。

             a. cpuset_init_smp@init/cpuset.c

             创建cpuset工作队列,它的作用是控制每个程序在哪个核心执行,对于多核的CPU。

             b. usermodehelper_init@init/kmod.c

             创建khelper工作队列,它的作用是指定用户空间的程序路径和环境变量,最终运行user space的程序。

             c. init_tmpfs@mm/shmem.c

             初始化tmpfs。

             d. driver_init@driver/base/init.c

             初始化设备模型。

             e. init_irq_proc@kernel/irq/proc.c

             初始化/proc/irq。

             f. do_ctors@init/main.c

             调用所有构造函数。

             g. do_initcalls

             初始化子系统。注意,.initcall.init节所保存的函数地址有一定的优先级,越前面的函数优先级越高,会被先调

             用。include/linux/init.h定义若干的宏协助内核模块添加它们的初始化函数到.initcall.init节。如需控制同一优先

             级的初始化函数执行顺序,可以通过修改模块的Makefile调动编译链接顺序。

       15. 调用init_post函数启动用户空间的init进程。在Android系统中,init进程在system/core/init目录下实现。

             init_post函数会调用run_init_process 执行ramdisk_execute_command,在run_init_process中,

             kernel_execve负责创建用户空间的init进程。





2 0
原创粉丝点击