使用PSCI机制的SMP启动分析
其他core的入口
文件:arch/arm64/kernel/head.S
secondary_entry:
在从bl31切到EL1上的Linux Kernel后:
第595行,在el2_setup中设置EL1和EL0为小端模式,然后将w0设置为BOOT_CPU_MODE_EL1,并返回
第596行,记录cpuX的启动模式到__boot_cpu_mode,目前是BOOT_CPU_MODE_EL1
secondary_startup:
第604行,__cpu_setup:
1、无效本地tlb
2、使能FP/ASIMD
3、设置mdscr_el1,在EL0访问Debug Communication Channel寄存器时,陷入EL1
4、操作daif,使能debug中断
5、设置pmuserenr_el0,当EL0访问PMU寄存器时会陷入EL1
6、填充mair_el1,设置后面要用到的内存属性索引,目前用到了6中内存属性:
7、读取sctlr_el1,修改后存入x0,后面配置mmu时会用到x0的值:x0= (sctrl_el1 & ~0xfcffffff)|0x34d5d91d,即将控制大小端的bit保留(因为之前在el2_setup里设置过了),其他位清零,然后设置新值,新值的含义如下:
从低位到高位依次说明:
0 |
M |
1:表示开启EL1和EL0的stage1地址转换机制,目前因为用不到虚拟化,所有只有stage1 |
1 |
A |
0:关闭地址对齐检查,但是load/store exclusive和load-acquire/store-release除外 |
2 |
C |
1:EL0/1的data cache的控制不再由sctrl_el1控制,如果HCR_EL2.DC为1,那么EL0/1的data cache开启。(所以,不开MMU,数据cache也可以开?) |
3 |
SA |
1 EL1上栈指针对齐检查,需要16字节对齐 |
4 |
SA0 |
1:EL0上栈指针对齐检查,需要16字节对齐 |
5 |
CP15BEN |
0:EL0运行在AArch32时,不能使用CP15DMB、CP15DSB以及CP15ISB |
7 |
ITD |
0:EL0运行在AArch32模式时,仍旧可以使用IT指令(IF-THEN)http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0489c/Cjabicci.html |
8 |
SED |
1:EL0运行在AArch32时,不能使用SEDEND指令。这个指令是在ARMv6引入的,用于切换当前cpu的大小端,请参考:http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0489c/Cjacabbf.html |
9 |
UMA |
0:在EL0运行在AArch64上通过MRS或MSR指令访问DAIF时会陷入EL1 |
12 |
I |
1:EL0/1的指令cache不再由sctrl_el1控制。如果HCR_EL2.DC为1,那么EL0/1的指令 cache开启 |
14 |
DZE |
1:EL0运行AArch64时,如果执行DC ZVA指令(清除指令虚拟地址的data cache),会陷入EL1. |
15 |
UCT |
1:EL0运行AArch64时,访问CTR_EL0时不会陷入EL1. 这个寄存器用于获取当前系统的cache的架构信息 |
16 |
nTWI |
1: EL0上执行WFI时不会陷入EL1 |
18 |
nTWE |
1: EL0上执行WFE时不会陷入EL1 |
19 |
WXN |
0: 对EL1&0的内存访问权限没有影响。如果是1的话,在EL1&0下,如果某块内存具备可写权限,那么这块内存就是XN(Execute Never) |
24 |
E0E |
控制EL0数据访问的大小端控制,0小端,1大端 |
25 |
EE |
控制EL1以及EL1/0的table walk时的大小端,0小端,1大端 |
26 |
UCI |
1:EL0运行在AArch64时,如果执行DC CVAU/CIVAC/CVAC、IC IVAU时不会陷入EL1 |
8、设置TCR_EL1,用于建立1:1映射,目前配置的VA时48bit的:
[5:0] | T0SZ | 64-48=16 |
[21:16] | T1SZ | 64-48=16 |
[9:8] | IRGN0, 使用TTBR0_EL1进行内存访问时的Inner cacheability | 1:Normal memory, Inner Write-Back Write-Allocate Cacheable |
[25:24] | IRGN1, 使用TTBR1_EL1进行内存访问时的Inner cacheability | 1:Normal memory, Inner Write-Back Write-Allocate Cacheable |
[11:10] | ORGN0, 使用TTBR0_EL1进行内存访问时的Outer cacheability | 1:Normal memory, Outer Write-Back Write-Allocate Cacheable |
[27:26] | ORGN1, 使用TTBR1_EL1进行内存访问时的Outer cacheability | 1:Normal memory, Outer Write-Back Write-Allocate Cacheable |
[13:12] | SH0, 使用TTBR0_EL1进行内存访问时的Shareability attribute | 3: Inner Shareable |
[29:28] | SH1, 使用TTBR1_EL1进行内存访问时的Shareability attribute | 3: Inner Shareable |
[15:14] | TG0, 使用TTBR0_EL1时的页大小 | 0:4KB |
[31:30] | TG1, 使用TTBR1_EL1时的页大小 | 0:4KB |
[36] | AS, 表示ASID的大小 | 1:16bit |
[37] | TBI0, 在使用TTBR0_EL1做地址翻译时,是否忽略虚拟地址的高字节,如果不忽略的话,会被用来当tagged地址 | 1:忽略高字节 |
[22] | A1, 选择通过TTBR0_EL1或者TTBR1_EL1来定义ASID | 1:使用TTBR1_EL1.ASID来定义ASID |
[34:32] | IPS,物理地址大小 | 通过读取ID_AA64MMFR0_EL1的获取当前系统的Physical Address range,然后将值写到这里,目前是48 |
第605行,__enable_mmu:
1、首先判断当前系统硬件是否支持4KB的粒度,是的话继续执行,否则stuck住
2、更新boot cpu的状态信息,即将__early_cpu_boot_status的值设置为0
3、将idmap_pg_dir赋值给ttbr0_el1
4、将swapper_pg_dir赋值给ttbr1_el1
5、将之前填充好的x0的值赋值给sctlr_el1
6、将icache无效
此时,mmu已经开启了,上面用到的两个页目录的内容在cpu0启动时已经填写好了,其他的core直接用。
__secondary_switched:
第611行,设置CPUx的异常向量表基地址为vectors
第615~619,设置CPUx的栈指针的值,这里用到的secondary_data的内容是在CPU0启动CPUx时赋值的。sp指向了idle task的栈顶地址,sp_el0指向idle task的task_struct首地址
第622,跳转到secondary_start_kernel
secondary_start_kernel:
CPUHP_AP_IDLE_DEAD | ||
CPUHP_AP_OFFLINE | ||
CPUHP_AP_SCHED_STARTING | sched_cpu_starting | |
CPUHP_AP_RCUTREE_DYING | ||
CPUHP_AP_IRQ_GIC_STARTING | gic_starting_cpu | 动态注册的 |
CPUHP_AP_ARM_VFP_STARTING | ||
CPUHP_AP_ARM64_DEBUG_MONITORS_STARTING | ||
CPUHP_AP_PERF_ARM_HW_BREAKPOINT_STARTING | ||
CPUHP_AP_PERF_ARM_STARTING | ||
CPUHP_AP_ARM_ARCH_TIMER_STARTING | arch_timer_starting_cpu | 动态注册 |
CPUHP_AP_ARM_GLOBAL_TIMER_STARTING | ||
CPUHP_AP_DUMMY_TIMER_STARTING | ||
CPUHP_AP_ARM_CORESIGHT_STARTING | ||
CPUHP_AP_ARM64_ISNDEP_STARTING | ||
CPUHP_AP_SMPCFD_DYING | ||
CPUHP_AP_ONLINE |
第270,设置secondary_data.status为CPU_BOOT_SUCCESS,后面cpu0醒来后,会根据这个标志判断cpuX是否启动正常
第271,设置__cpu_online_mask中当前cpu对应的bit,后面cpu0醒来后,会根据这个标志判断cpuX是否启动正常
第272,通知cpu0,可以继续往下运行了,此时cpu0在arch\arm64\kernel\smp.c中调用完boot_secondary后,紧接着wait_for_completion_timeout(&cpu_running, msecs_to_jiffies(1000))
第274,开启本地中断
第275,使能SERROR中断
第280,执行cpu_startup_entry:
第374,执行do_idle
CPU0启动以及PSCI初始化
设备树
start_kernel:
下面进入rest_init:
在这个函数里先后创建个内核线程kernel_init和kthreadd,然后在kernel_init中会wait_for_completion(&kthreadd_done),当主流程准备就绪后,设置system_state为SYSTEM_SCHEDULING,
然后主流程会complete(&kthreadd_done),在主流程的最后,会执行cpu_startup_entry(CPUHP_ONLINE),在前面非boot cpu的启动分析中,最后也会执行cpu_startup_entry,不过它传递的cpu状态是CPUHP_AP_ONLINE_IDLE
下面重点分析kernel_init:
在执行这个内核线程是,使用的仍然是cpu0,因为当前系统中也只有cpu0.
第1058,smp_prepare_cpus,首先调用init_cpu_topology,解析当前/cpus节点以及下面的cpu-map子节点,填充cpu_topology[cpu],得到系统cpu的拓扑关系,然后
调用set_sched_topology设置sched_domain_topology为arm64_topology。如果没有从设备树里cpu-map获得拓扑关系,smp_prepare_cpus会调用store_cpu_topology来
设置cpu_topology[0]的内容。最后,这个函数会遍历当前所有cpu,设置per_cpu变量cpu_number为cpu编号,然后cpu_ops[cpu]->cpu_prepare(cpu),如果成功的话,
设置set_cpu_present(cpu, true)
第1064,do_pre_smp_initcalls:执行__initcall_start到__initcall0_start之间的回调函数
第1067,smp_init:这个函数就是cpu0唤醒其他core的地方,重点分析
smp_init:
第574~579,遍历cpu,如果cpu没有online(cpu_online_mask),那么调用cpu_on(cpu)
下面重点分析cpu_on:
4、如果待处理cpu的当前状态大于CPUHP_BRINGUP_CPU,那么会执行cpuhp_kick_ap_work(cpu)。
5、下面的唤醒步骤分两个阶段,首先从OFFLINE到CPUHP_BRINGUP_CPU,然后剩下从CPUHP_BRINGUP_CPU到CPUHP_ONLINE则通过AP hotplug thread进行,这个线程就是上面创建的"cpuhp/0",
它的task_struct存放在per_cpu变量cpuhp_state.thread中
下面是从OFFLINE->CPUHP_BRINGUP_CPU:
下面分析cpuhp_up_callbacks:
在获得cpuhp_step结构后,先判断该step是否支持multi_instance,如果不支持的话,对于bringup操作,会回调step->startup.single(cpu),否则回调step->teardown.single(cpu).对于支持multi_instance的step,会遍历step->list,对于bringup,回调step->startup.multi(cpu,node),其中node就是从step->list遍历得到的,否则回调step->teardown.multi(cpu,node)。
下面是cpuhp_bp_states中涉及到的回调:
CPUHP_CREATE_THREADS | smpboot_create_threads | 遍历hotplug_threads,对没有创建task的 |
CPUHP_PERF_PREPARE | perf_event_init_cpu | |
CPUHP_WORKQUEUE_PREP | workqueue_prepare_cpu | |
CPUHP_HRTIMERS_PREPARE | hrtimers_prepare_cpu | |
CPUHP_SMPCFD_PREPARE | smpcfd_prepare_cpu | |
CPUHP_RELAY_PREPARE | relay_prepare_cpu | |
CPUHP_SLAB_PREPARE | slab_prepare_cpu | |
CPUHP_RCUTREE_PREP | rcutree_prepare_cpu | |
CPUHP_TIMERS_PREPARE | timers_prepare_cpu | |
CPUHP_BRINGUP_CPU | bringup_cpu |
bringup_cpu:
psci_ops.cpu_on(cpu_logical_map(cpu), __pa_symbol(secondary_entry))
cpu_on是在drivers\firmware\psci.c赋值的:
5、如果上面返回成功,那么cpu0调用wait_for_completion_timeout(&cpu_running,msecs_to_jiffies(1000)); 根据前面的分析,当cpuX醒来之后,会complete这里的cpu_running
6、调用cpu_online(cpu)检查cpuX是否成功online
7、读取READ_ONCE(secondary_data.status),然后判断cpuX是否正常
第533,bringup_wait_for_ap(cpu):
1、调用wait_for_ap_thread(st, true),因为是bringup,所以会wait_for_completion(&st->done_up)。在cpuX唤醒后,执行idle任务之前会complete该变量
2、stop_machine_unpark(cpu);
3、kthread_unpark(st->thread),这里的st->thread是"cpuhp/0",是在cpuhp_threads_init中初始化的
4、如果st->target小于等于CPUHP_AP_ONLINE_IDLE,那么这个函数的任务就完成了,但是对于这里,target的值是CPUHP_ONLINE
5、调用cpuhp_kick_ap(st, st->target),这个函数进一步调用__cpuhp_kick_ap(st):
cpuhp_thread_fun:
1、比较cpuX的当前状态和目标状态,如果小于目标状态,那么设置st->should为true,这样这个函数返回后,还会被smpboot_thread_fn继续回调
2、调用st->result = cpuhp_invoke_callback(cpu, state, bringup, st->node, &st->last);
3、如果上个函数返回失败,设置st->should_run = false
4、如果st->should_run为false,那么调用complete_ap_thread(st, bringup),如果是bringup,这个函数会complete(&st->done_up)。即:在正常情况下,
如果当前状态达不到target状态,那么st->should_run一直为true,只有达到target,才会complete(&st->done_up)。别忘了,此时cpu0还在wait呢
第二阶段从CPUHP_AP_ONLINE_IDLE->CPUHP_ONLINE,下面的函数会被调用,这些函数位于cpuhp_ap_states中:
CPUHP_AP_SMPBOOT_THREADS | smpboot_unpark_threads | |
CPUHP_AP_IRQ_AFFINITY_ONLINE | irq_affinity_online_cpu | |
CPUHP_AP_PERF_ONLINE | perf_event_init_cpu | |
CPUHP_AP_WORKQUEUE_ONLINE | workqueue_online_cpu | |
CPUHP_AP_RCUTREE_ONLINE | rcutree_online_cpu | |
CPUHP_AP_ACTIVE | sched_cpu_activate | |
CPUHP_ONLINE | NULL |
SMP启动log
下面是多核启动时的log,有助于我们理解上面的分析过程:(log中<cpu_id 进程name:进程id>)
[ 0.117936] < swapper/:> smp: Bringing up secondary CPUs ...
[ 0.118893] < swapper/:> smpboot_create_threads, cpu:
[ 0.140551] < swapper/:> perf_event_init_cpu, cpu:
[ 0.140958] < swapper/:> workqueue_prepare_cpu, cpu:
[ 0.149409] < swapper/:> hrtimers_prepare_cpu, cpu:
[ 0.149555] < swapper/:> smpcfd_prepare_cpu, cpu:
[ 0.149780] < swapper/:> rcutree_prepare_cpu, cpu:
[ 0.150696] < swapper/:> timers_prepare_cpu, cpu:
[ 0.150999] < swapper/:> bringup_cpu, cpu:
[ 0.152518] < swapper/:> Detected PIPT I-cache on CPU1
[ 0.153478] < swapper/:> sched_cpu_starting, cpu:
[ 0.153575] < swapper/:> GICv3: CPU1: found redistributor region :0x00000000080c0000
[ 0.153849] < swapper/:> CPU1: cluster core thread - mpidr 0x00000080000001
[ 0.153955] < swapper/:> CPU1: Booted secondary processor [410fd083]
[ 0.157031] < cpuhp/:> smpboot_unpark_threads, cpu:
[ 0.158684] < cpuhp/:> irq_affinity_online_cpu, cpu:
[ 0.158999] < cpuhp/:> perf_event_init_cpu, cpu:
[ 0.159236] < cpuhp/:> workqueue_online_cpu, cpu:
[ 0.160831] < cpuhp/:> rcutree_online_cpu, cpu:
[ 0.161322] < cpuhp/:> sched_cpu_activate, cpu:
[ 0.162781] < swapper/:> smpboot_create_threads, cpu:
[ 0.185357] < swapper/:> perf_event_init_cpu, cpu:
[ 0.185528] < swapper/:> workqueue_prepare_cpu, cpu:
[ 0.193720] < swapper/:> hrtimers_prepare_cpu, cpu:
[ 0.193829] < swapper/:> smpcfd_prepare_cpu, cpu:
[ 0.193951] < swapper/:> rcutree_prepare_cpu, cpu:
[ 0.194451] < swapper/:> timers_prepare_cpu, cpu:
[ 0.194583] < swapper/:> bringup_cpu, cpu:
[ 0.194853] < swapper/:> Detected PIPT I-cache on CPU2
[ 0.194970] < swapper/:> sched_cpu_starting, cpu:
[ 0.195027] < swapper/:> GICv3: CPU2: found redistributor region :0x00000000080e0000
[ 0.195198] < swapper/:> CPU2: cluster core thread - mpidr 0x00000080000002
[ 0.195269] < swapper/:> CPU2: Booted secondary processor [410fd083]
[ 0.195899] < cpuhp/:> smpboot_unpark_threads, cpu:
[ 0.196294] < cpuhp/:> irq_affinity_online_cpu, cpu:
[ 0.196426] < cpuhp/:> perf_event_init_cpu, cpu:
[ 0.196594] < cpuhp/:> workqueue_online_cpu, cpu:
[ 0.197099] < cpuhp/:> rcutree_online_cpu, cpu:
[ 0.197325] < cpuhp/:> sched_cpu_activate, cpu:
[ 0.197811] < swapper/:> smpboot_create_threads, cpu:
[ 0.220870] < swapper/:> perf_event_init_cpu, cpu:
[ 0.221065] < swapper/:> workqueue_prepare_cpu, cpu:
[ 0.229439] < swapper/:> hrtimers_prepare_cpu, cpu:
[ 0.229550] < swapper/:> smpcfd_prepare_cpu, cpu:
[ 0.229675] < swapper/:> rcutree_prepare_cpu, cpu:
[ 0.230062] < swapper/:> timers_prepare_cpu, cpu:
[ 0.230195] < swapper/:> bringup_cpu, cpu:
[ 0.230439] < swapper/:> Detected PIPT I-cache on CPU3
[ 0.230549] < swapper/:> sched_cpu_starting, cpu:
[ 0.230604] < swapper/:> GICv3: CPU3: found redistributor region :0x0000000008100000
[ 0.230767] < swapper/:> CPU3: cluster core thread - mpidr 0x00000080000003
[ 0.230907] < swapper/:> CPU3: Booted secondary processor [410fd083]
[ 0.231723] < cpuhp/:> smpboot_unpark_threads, cpu:
[ 0.232132] < cpuhp/:> irq_affinity_online_cpu, cpu:
[ 0.232266] < cpuhp/:> perf_event_init_cpu, cpu:
[ 0.232439] < cpuhp/:> workqueue_online_cpu, cpu:
[ 0.232941] < cpuhp/:> rcutree_online_cpu, cpu:
[ 0.233179] < cpuhp/:> sched_cpu_activate, cpu:
[ 0.233572] < swapper/:> smp: Brought up node, CPUs
[ 0.233680] < swapper/:> SMP: Total of processors activated.
使用PSCI机制的SMP启动分析的更多相关文章
- 第3阶段——内核启动分析之start_kernel初始化函数(5)
内核启动分析之start_kernel初始化函数(init/main.c) stext函数启动内核后,就开始进入start_kernel初始化各个函数, 下面只是浅尝辄止的描述一下函数的功能,很多函数 ...
- mkimage工具 加载地址和入口地址 内核启动分析
第三章第二节 mkimage工具制作Linux内核的压缩镜像文件,需要使用到mkimage工具.mkimage这个工具位于u-boot-2013. 04中的tools目录下,它可以用来制作不压缩或者压 ...
- Android Zygote进程启动分析
dvm,app进程,linux进程三者关系 DVM指 dalivk 的虚拟机.每一个 Android 应用程序都在它自己的进程中运行,都拥有一个独立的 Dalvik 虚拟机实例.而每一个 DVM 都是 ...
- Guava cacha 机制及源码分析
1.ehcahce 什么时候用比较好:2.问题:当有个消息的key不在guava里面的话,如果大量的消息过来,会同时请求数据库吗?还是只有一个请求数据库,其他的等待第一个把数据从DB加载到Guava中 ...
- k8s replicaset controller分析(1)-初始化与启动分析
replicaset controller分析 replicaset controller简介 replicaset controller是kube-controller-manager组件中众多控制 ...
- ASP.NET MVC的运行机制--url的全局分析
全局 首先我们来看一副图片 首先,用户通过Web浏览器向服务器发送一条url请求,这里请求的url不再是xxx.aspx格式,而是http://HostName/ControllerNam ...
- SpringBoot源码解析:tomcat启动分析
>> spring与tomcat的启动分析:war包形式 tomcat:xml加载规范 1.contex-param: 初始化参数 2.listener-class: contextloa ...
- Android的消息循环机制 Looper Handler类分析
Android的消息循环机制 Looper Handler类分析 Looper类说明 Looper 类用来为一个线程跑一个消息循环. 线程在默认情况下是没有消息循环与之关联的,Thread类在ru ...
- linux SMP启动
SMP简介 1,硬件上,CPU没有主次之分 2,软件上,每个CPU平等动态地从进程就绪队列中调度进程加以执行,中断请求也是等概率动态的分布给某个CPU SMP启动 1,SMP结构中的CPU都是平等的, ...
随机推荐
- minio select api 试用
对于minio 我们可以使用基于sql 的对象内容查询,特别适合进行特定文件内容的获取,强大方便. 以下是一个简单的试用 环境准备 集成了prometheus docker-compose 文件 ...
- 【树论】FBI树
原题传送门 思路 讲这道题之前,先讲一个黑科技一般的函数:basic_string::substr(int x,int y). 这是一个string类的成员函数,它返回一个新的string对象,该对象 ...
- 黑苹果MacOS安装记录
https://blog.daliansky.net/macOS-Catalina-10.15-19A583-Release-version-with-Clover-5093-original-ima ...
- IntelliJ IDEA最新版2019年注册码,可激活至2099年 激活 破解
IntelliJ IDEA最新版2019年注册码,可激活至2099年 激活 破解 特别说明:图中的IDEA是2017版本,不过方法对2019版本的IDEA同样适用! 最近笔者测试了好多破解Idea的方 ...
- 推荐一款移动端小视频App玲珑视频
推荐一款移动端小视频App玲珑视频 一 应用描述 玲珑小视频,边看边聊![海量视频,刷个不停,还能找妹子语音聊天哦][随手拍一拍,记录美好生活,还能拿金币哦][看视频领金币.登录领金币.拍视频领金币. ...
- Spring Cloud(十四):Ribbon实现客户端负载均衡及其实现原理介绍
年后到现在一直很忙,都没什么时间记录东西了,其实之前工作中积累了很多知识点,一直都堆在备忘录里,只是因为近几个月经历了一些事情,没有太多的经历来写了,但是一些重要的东西,我还是希望能坚持记录下来.正好 ...
- 视觉融合定位github
1. obr_slam有关的非常有用的github链接: https://github.com/Ewenwan/MVision/tree/master/vSLAM/oRB_SLAM2 2. gnss, ...
- update改数据详解
update修改数据的要素 : 改哪张表? 改哪几列的值? 分别改成什么值? 在哪些行生效?(这个很重要,否则所有行都会受影响) mysql> update class ; where 表达式 ...
- 官方elasticsearch-certutiledit命令
地址:https://www.elastic.co/guide/en/elasticsearch/reference/7.5/certutil.html 语法: bin/elasticsearch-c ...
- 关于plupload组件无法拍照上传的解决方案
关于plupload组件无法拍照上传的解决方案 其实是由于文件大小的问题 filters: { max_file_size: '2mb',//把这个调大些就可以了 前提是服务器支持 prevent_d ...