全志平台linux启动流程分析

全志平台linux启动流程分析

分类:Linux Kernel

一、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进程。

版权声明:本文为博主原创文章,未经博主允许不得转载。

上一篇ubuntu安装备忘录下一篇Android安全机制介绍

顶1踩0

时间慢慢的流淌,人生有风雨阳光,

全志平台linux启动流程分析

相关文章:

你感兴趣的文章:

标签云: