[kernel 启动流程]系列:
- [kernel 启动流程] 前篇——vmlinux.lds分析
- [kernel 启动流程] (第一章)概述
- [kernel 启动流程] (第二章)第一阶段之——设置SVC、关闭中断
- [kernel 启动流程] (第三章)第一阶段之——proc info的获取
- [kernel 启动流程] (第四章)第一阶段之——dtb的验证
- [kernel 启动流程] (第五章)第一阶段之——临时内核页表的创建
- [kernel 启动流程] (第六章)第一阶段之——打开MMU
- [kernel 启动流程] (第七章)第一阶段之——跳转到start_kernel
建议参考文档:
================================================
零、说明
本文是《[kernel 启动流程] (第一章)概述》的延伸,
阅读本文前建议先阅读《[kernel 启动流程] (第一章)概述》
1、kernel启动流程第一阶段简单说明
arch/arm/kernel/head.S
- kernel入口地址对应stext
- 1
-
第一阶段要做的事情,也就是stext的实现内容
- 设置为SVC模式,关闭所有中断
- 获取CPU ID,提取相应的proc info
- 验证tags或者dtb
- 创建临时内核页表的页表项
- 配置r13寄存器,也就是设置打开MMU之后要跳转到的函数。
- 使能MMU
- 跳转到start_kernel,也就是跳转到第二阶段
本文要介绍的是“跳转到start_kernel”的部分。
2、疑问
主要带着以下几个问题去理解
- 跳转到start_kernel的准备动作?环境设置?
一、跳转到start_kernel的准备动作
经过前面几章,我们已经知道MMU已经打开,后续都是在虚拟地址上执行。并且kernel代码段的链接地址都已经映射到对应的物理地址上了。
接下来的已经调用到__mmap_switched,作跳转到start_kernel的动作。
在跳转到start_kernel前,需要做如下准备动作:
- 数据段的准备
通过System.map,数据段对应的连接区域如下:
- 1
- 2
- 3
- 4
- 5
- 堆栈段的准备
因为后续start_kernel之后都是在C语言环境下运行,所以需要对堆栈段进行设置并清空。
- 1
- 2
- 3
-
一些后续会访问到的变量的设置
因为后续C语言代码会访问到一些变量,并且这些变量的值在启动过程中是存储到寄存器中的。那么就需要把寄存器上的值搬移到对应的变量的地址上。有如下这些变量- cpu id(processor ID)
- machine id(machine type)
- dtb的指针(atags pointer)
- 当前进程堆栈指针的设置
-
当前进程堆栈指针的设置
因为后续start_kernel之后都是在C语言环境下运行,需要完成其堆栈环境。
然后就可以跳转到start_kernel中了。
后续__mmap_switched的代码也是根据这些准备动作来的。
二、代码分析
如何跳转到__mmap_switched请参考《[kernel 启动流程] (第六章)第一阶段之——打开MMU》。
后面直接介绍__mmap_switched的实现。
1、__mmap_switched_data
首先了解一下__mmap_switched_data这个数据结构,存放了__mmap_switched过程中使用的变量的地址。
arch/arm/kernel/head-common.S
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
解释如下:
- __data_loc:数据段存储地址
- _sdata:数据段起始地址
- __bss_start:堆栈段起始地址
- _end:堆栈段结束地址
- processor_id:cpu处理器ID地址,其变量定义在arch/arm/kernel/setup.c中
- __machine_arch_type:machine id地址,其变量定义在arch/arm/kernel/setup.c中
- __atags_pointer:dtb指针的地址,其变量定义在arch/arm/kernel/setup.c中
- cr_alignment:cp15的c1寄存器的值的地址,也就是mmu控制寄存器的值,其变量定义在arch/arm/kernel/entry-armv.S中
2、进入前的寄存器说明
通过前面几章的分析,有几个寄存器专门存放了如下值:
- r0,存放了cp15协处理器c1寄存器的值,也就是MMU控制器的值
- r1,存放了由uboot传过来的mechine id
- r2,存放了dtb的地址
- r9,存放了cpu处理器id
3、代码分析
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
通过上述,最终跳转到start_kernel,kernel启动过程的第一阶段结束。
———————————————-分割线——————————————————-
下面对sp指针的设置进行说明
前面可知 sp-> init_thread_union + THREAD_START_SP
init_thread_union表示init进程的起始地址,在init/init_task.c中
- 1
- 2
将init_thread_union加上THREAD_START_SP后得到其堆栈地址.
这部分代码要到进程管理的部分才会更加了解。