kernel 启动分析讲解
文件状态:
草稿
正式发布
正在修改
作 者: john
当前版本: V0.1
完成日期:
审 定:
版本历史
版本/状态
V0.1
作者
参与者
起止日期
2015.11.11
新建
备注
1. 概述
本文档的目的是帮助理解 zynq 底层 kernel 启动过程,本项目采用的是 SMP
的系统架构模式,从而了解单板在内核系统启动阶段多核加载,总线初始化,外
设初始化的过程。本文重点介绍对单板关键外设初始化注册与调用分析,例如
NAND FLASH,以太网,串口, USB 协议栈等做了介绍。下面就围绕以上几个
关键外设进行分析。
2. kernel 启动基本流程介绍
系统启动过程极为复杂,可以分为以下几个重要环境分别分析
图 1 kernel 启动过程流程图
1) start.S 文件
系统启动阶段,首先定义了内核存放的实际物理地址为 0x8000,堆栈向上
增长的方式,
* 0x0 - 0x4000 - reserving below not to be used by DMA
* 0x4000 - 0x8000 swapper page table
并且定义了
#define PG_DIR_SIZE 0x4000 //4k page size
#define PMD_ORDER 2
//2 order
每页的大小为 4K,并且页目录为 2 级目录,而后获取 cpu type,然后利用
__create_page_tables 函数建立 PGD,PMD 目录项。
__create_page_tables
//head.S
__mmap_switched
//head_common.S 文件中
start_kernel
//main.c 文件中
以上的代码均在汇编语言中完成,从 start_kernel 函数,开始 C 语言的函数执行。
2) main.c 文件
在该文件中对内核的初始化工作非常多,也非常复杂,下面挑出和多核 cpu
启动相关的部分,以及和设备驱动的部分讲解。
多核 CPU 启动过程中,首先是 CPU0 启动,包括 uboot 阶段,以及在 main.c
函数之前的部分都是在 CPU0 上执行的。在 main.c 函数中的 setup_arch 函数中,
一个 cpu 执行的情况将彻底结束。下面是函数调用的关系。
setup_arch//初始化与当前 cpu 相关的部分,每个 cpu 厂家,初始化代码不同
setup_processor//查询当前 cpu id 是否支持
lookup_processor_type//调用 head_common.S 文件中的代码获取 cpu id
setup_machine_tags//
for_each_machine_desc//查询 arch_info 结构体定义,查找与 machine_nr 一致的 cpu
此处应该重点说明的是对于每种 cpu 在 arch/arm/mach-zynq 的 common.c 文件中,通常会声
明一个重要结构体:
DT_MACHINE_START(XILINX_EP107, "Xilinx Zynq Platform")
.smp
= smp_ops(zynq_smp_ops),
.map_io
= zynq_map_io,
.init_irq = zynq_irq_init,
.init_machine = zynq_init_machine,
.init_late = zynq_init_late,
.init_time = zynq_timer_init,
.dt_compat
= zynq_dt_match,
.reserve = zynq_memory_init,
.restart
= zynq_system_reset,
MACHINE_END
其中宏定义 MACHINE_START 属性如下:
#define MACHINE_START(_type,_name)
static const struct machine_desc __mach_desc_##_type \
__used
\
__attribute__((__section__(".arch.info.init"))) = { \
.nr
= MACH_TYPE_##_type,
\
.name
= _name,
#define MACHINE_END
\
};
该结构体通过连接脚本 vmlinux.lds 链接,编译的时候,编译器根据链接脚
本的指定,将此部分结构体统一放在.arch.info.init 部分。系统启动阶段通过链接
脚本加载,查询是否支持当前 cpu,并且调用相关函数。
setup_arch//初始化与当前 cpu 相关的部分,每个 cpu 厂家,初始化代码不同
setup_processor//查询当前 cpu id 是否支持
lookup_processor_type//调用 head_common.S 文件中的代码获取 cpu id
setup_machine_tags//
for_each_machine_desc//查询 arch_info 结构体定义,查找与 machine_nr 一致的 cpu
parse_early_param//早期参数初始化
paging_init //页表初始化
else if (mdesc->smp)//由于此处的 zynq 函数有定义所以执行 smp_set_ops
smp_set_ops(mdesc->smp);
其中 mdesc->smp 在此处定义
DT_MACHINE_START(XILINX_EP107, "Xilinx Zynq Platform")
.smp
= smp_ops(zynq_smp_ops),
而 zynq_smp_ops 定义如下
struct smp_operations zynq_smp_ops __initdata = {
.smp_init_cpus
= zynq_smp_init_cpus,
设置相关 smp 相关结构体,最终的 CPU0 外的其他核启动是在
rest_init
kernel_init
kernel_init_freeable();
smp_init();
for_each_present_cpu(cpu) //对于每个 cpu 都会调用 cpu_up,是一个循环
cpu_up(cpu);
__cpu_up
boot_secondary
if (smp_ops.smp_boot_secondary)
return smp_ops.smp_boot_secondary(cpu, idle);
对于 zynq 在初始化阶段.smp_boot_secondary = zynq_boot_secondary,
zynq_boot_secondary
zynq_cpun_start(virt_to_phys(zynq_secondary_startup), cpu);
ENTRY(zynq_secondary_startup)
bl
b
v7_invalidate_l1
secondary_startup
ENDPROC(zynq_secondary_startup)
经过下面的初始化后,CPU0 外的其他 CPU 才真正的启动。