欢迎光临散文网 会员登陆 & 注册

一文玩转ARM64 SMP多核启动(二)- PSCI(超级详细~)

2022-05-30 19:56 作者:补给站Linux内核  | 我要投稿

4.支持psci情况

  • 上面说了pin-table的多核启动方式,看似很繁琐,实际上并不复杂,无外乎主处理器唤醒从处理器到指定地址上去执行指令,说他简单是相对于功能来说的,因为他只是实现了从处理器的启动,仅此而已,所以,现在社区几乎很少使用spin-table这种方式,取而代之的是psci,他不仅可以启动从处理器,还可以关闭,挂起等其他核操作,现在基本上arm64平台上使用多核启动方式都是psci。下面我们来揭开他神秘的面纱,其实理解了spin-table的启动方式,psci并不难(说白了也是需要主处理器给从处理器一个启动地址,然后从处理器从这个地址执行指令,实际上比这要复杂的多)。

  • 首先,我们先来看下设备树cpu节点对psci的支持:

  • psci节点的详细说明可以参考内核文档:Documentation/devicetree/bindings/arm/psci.txt

  • 可以看到现在enable-method 属性已经是psci,说明使用的多核启动方式是psci, 下面还有psci节点,用于psci驱动使用,method用于说明调用psci功能使用什么指令,可选有两个smc和hvc。其实smc, hvc和svc都是从低运行级别向高运行级别请求服务的指令,我们最常用的就是svc指令了,这是实现系统调用的指令。高级别的运行级别会根据传递过来的参数来决定提供什么样的服务。smc是用于陷入el3(安全), hvc用于陷入el2(虚拟化, 虚拟化场景中一般通过hvc指令陷入el2来请求唤醒vcpu), svc用于陷入el1(系统)。

  • 注:本文只讲解smc陷入el3启动多核的情况。

  • 下面开始分析源代码:

  • 我们都知道armv8将异常等级分为el0 - el3,其中,el3为安全监控器,为了实现对它的支持,arm公司设计了一种firmware叫做ATF(ARM Trusted firmware),下面是atf源码readme.rst文件的一段介绍:

  • ATF代码运行在EL3, 是实现安全相关的软件部分固件,其中会为其他特权级别提供服务,也就是说提供了在EL3中服务的手段,我们本文介绍的PSCI的实现就是在这里面,本文不会过多的讲解(注:其实本文只会涉及到atf如何响应服务el1的smc发过来的psci的服务请求,仅此而已,有关ATF(Trustzone)请参考其他资料)。

  • 那么就开始我们的正题:

  • 下面从源代码角度分析服务的注册处理流程:

【文章福利】小编推荐自己的Linux内核技术交流群:【891587639】整理了一些个人觉得比较好的学习书籍、视频资料共享在群文件里面,有需要的可以自行添加哦!!!前100名进群领取,额外赠送一份价值699的内核资料包(含视频教程、电子书、实战项目及代码)  


4.2 服务注册

  • 下面的宏是用于注册运行时服务的接口,每种服务通过它来注册:

在标准的运行时服务中将服务初始化和处理函数放到rt_svc_descs段中,供调用。

在runtime_svc_init函数中,调用每一个通过DECLARE_RT_SVC注册的服务,其中包括std_svc服务:

4.3 运行时服务初始化处理

  • std_svc_setup (主要关注设置psci操作集)

  • 可以看到,在遍历每一个注册的运行时服务的时候,会导致std_svc_setup调用,其中会做psci操作集的设置,操作集中我们可以看到对核电源的管理的接口如:核上电,下电,挂起等,我们主要关注上电 .pwr_domain_on = qemu_pwr_domain_on,这个接口当我们主处理器boot从处理器的时候会用到。

4.4 运行时服务触发和处理

  • smc指令触发进入el3异常向量表:

  • 上面其实主要的是找到服务例程,然后跳转执行

  • 下面是跳转的处理函数:

  • 处理函数根据funid来决定服务,可以看到PSCI_CPU_ON_AARCH64为0xc4000003,这正是设备树中填写的cpu_on属性的id,会委托psci_cpu_on来执行核上电任务。

  • 下面分析是重点:!!!

  • psci_cpu_on主要完成开核的工作,然后会设置一些异常返回后寄存器的值(eg:从el1 -> el3 -> el1),重点关注 ep->pc写到cpu_context结构的CTX_ELR_EL3偏移处(从处理器启动后会从这个地址取指执行)。

  • 实际上,所有的从处理器启动后都会从bl31_warm_entrypoint开始执行,在plat_setup_psci_ops中会设置(每个平台都有自己的启动地址寄存器,通过写这个寄存器来获得上电后执行的指令地址)。

  • 大致说一下:主处理器通过smc进入el3请求开核服务,atf中会响应这种请求,通过平台的开核操作来启动从处理器并且设置从处理的一些寄存器eg:scr_el3、spsr_el3、elr_el3,然后主处理器,恢复现场,eret再次回到el1,而处理器开核之后会从bl31_warm_entrypoint开始执行,最后通过el3_exit返回到el1的elr_el3设置的地址。

  • 分析到这atf的分析到此为止,atf中主要是响应内核的snc的请求,然后做开核处理,也就是实际的开核动作,但是从处理器最后还是要回到内核中执行,下面分析内核的处理:注意流程如下:

  • 我们主要关注两个函数:psci_dt_init和smp_init

  • psci_dt_init是解析设备树,设置操作函数,smp_init用于启动从处理器。

  • 最终通过22行 陷入了el3中。

  • smp_init函数做从处理器启动:

  • 启动从处理的时候最终调用到psci的cpu操作集的cpu_psci_cpu_boot函数,会调用上面的psci_cpu_on,最终调用smc,传递第一个参数为cpu的id标识启动哪个cpu,第二个参数为从处理器启动后进入内核执行的地址secondary_entry(这是个物理地址)。

  • 所以综上,最后smc调用时传递的参数为arm_smccc_smc(0xC4000003, cpuid, secondary_entry, arg2, 0, 0, 0, 0, &res)。

  • 这样陷入el3之后,就可以启动对应的从处理器,最终从处理器回到内核(el3->el1),执行secondary_entry处指令,从处理器启动完成。

  • 可以发现psci的方式启动从处理器的方式相当复杂,这里面涉及到了el1到安全的el3的跳转,而且涉及到大量的函数回调,很容易绕晕。

  • 下面给出psci方式多核启动图示:

5.从处理器启动进入内核世界之后做了些什么

  • 无论是spin-table还是psci,从处理器启动进入内核之后都会执行secondary_startup:

__cpu_up中设置了secondary_data结构中的一些成员:

跳转到secondary_start_kernel这个C函数继续执行初始化:

  • 实际上,可以看的当从处理器启动到内核的时候,他们也需要设置异常向量表,设置mmu等,然后执行各自的idle进程(这些都是一些处理器强相关的初始化代码,一些通用的初始化都已经被主处理器初始化完),当cpu负载均衡 的时候会放置一些进程到这些从处理器,然后进程就可以再这些从处理器上欢快的运行。


6.最后说两句

  • 写到这里,关于arm64平台的多核启动已经介绍完成,可以发现里面还是会涉及到很多细节,源码散落在uboot,atf,kernel等源码目录中,多核启动并不是很神秘,都是需要告诉从处理器从那个地方开始取值执行,然后从处理器进入内核后需要自身做一些必要的初始化,就进入idle状态等待有任务来 调度,我们主要以分析源代码的方式讲解了spin-table和psci两种方式来启动多核,而arm64平台使用psci更为广泛。


一文玩转ARM64 SMP多核启动(二)- PSCI(超级详细~)的评论 (共 条)

分享到微博请遵守国家法律