首页 > 其他分享 >ARM64 SMP多核启动(下)- PSCI

ARM64 SMP多核启动(下)- PSCI

时间:2023-12-03 22:23:39浏览次数:34  
标签:PSCI svc SMP init psci ARM64 secondary cpu 处理器

4.支持psci情况

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

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

arch/arm64/boot/dts/xxx.dtsi:
  cpu0: cpu@0 {
                        device_type = "cpu";
                        compatible = "arm,armv8";
                        reg = <0x0>;
                        enable-method = "psci";
          
                };

  psci {
                compatible = "arm,psci";
                method = "smc";
               cpu_suspend = <0xC4000001>;
                cpu_off = <0x84000002>;
                cpu_on = <0xC4000003>;
        };

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文件的一段介绍:

Trusted Firmware-A (TF-A) provides a reference implementation of secure world
  software for `Armv7-A and Armv8-A`_, including a `Secure Monitor`_ executing
  at Exception Level 3 (EL3). It implements various Arm interface standards,
  such as:
  
  -  The `Power State Coordination Interface (PSCI)`_
  -  Trusted Board Boot Requirements (TBBR, Arm DEN0006C-1)
  -  `SMC Calling Convention`_
  -  `System Control and Management Interface (SCMI)`_
  -  `Software Delegated Exception Interface (SDEI)`_

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

那么就开始我们的正题:

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

4.1 el31处理总体流程

atf/bl31/aarch64/bl31_entrypoint.S:    //架构相关

bl31_entrypoint
->el3_entrypoint_common 
_exception_vectors=runtime_exceptions   //设置el3的异常向量表
->bl      bl31_early_platform_setup   //跳转到平台早期设置  
->bl      bl31_plat_arch_setup    //跳转到平台架构设置  
-> bl      bl31_main  //跳转到bl31_main   atf/bl31/aarch64/bl31_main.c:
->NOTICE("BL31: %s\n", version_string);  //打印版本信息
->NOTICE("BL31: %s\n", build_message); //打印编译信息
->bl31_platform_setup   //执行平台设置 
-> /* Initialize the runtime services e.g. psci. */  初始化运行时服务 如psci
 INFO("BL31: Initializing runtime services\n") //打印log信息
->runtime_svc_init   //调用各种运行时服务历程
...

4.2 服务注册

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

/*
   * Convenience macro to declare a service descriptor   定义运行时服务描述符结构的宏
   */
  #define DECLARE_RT_SVC(_name, _start, _end, _type, _setup, _smch) \
          static const rt_svc_desc_t __svc_desc_ ## _name \
                  __section("rt_svc_descs") __used = { \  //结构放在rt_svc_descs段中
                          .start_oen = _start, \
                          .end_oen = _end, \
                          .call_type = _type, \
                          .name = #_name, \
                          .init = _setup, \
                          .handle = _smch }

链接脚本中:
bl31/bl31.ld.S:
...
.rodata . : {

 __RT_SVC_DESCS_START__ = .; rt_svc_descs段开始
           KEEP(*(rt_svc_descs))  //rt_svc_descs段
  __RT_SVC_DESCS_END__ = .;  rt_svc_descs段结束

}
...

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

services/std_svc/std_svc_setup.c:

  DECLARE_RT_SVC(
                  std_svc,
  
                  OEN_STD_START,
                  OEN_STD_END,
                  SMC_TYPE_FAST,
                  std_svc_setup,//初始化
                  std_svc_smc_handler //处理
  );

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

   for (index = 0; index < RT_SVC_DECS_NUM; index++) {
                  rt_svc_desc_t *service = &rt_svc_descs[index];
                  ...
                    rc = service->init(); //调用每一个注册的运行时服务的设置函数
                  ...
                  }

4.3 运行时服务初始化处理

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

std_svc_setup  //services/std_svc/std_svc_setup.c
->psci_setup //lib/psci/psci_setup.c
 ->plat_setup_psci_ops   //设置平台的psci操作    调用平台的plat_setup_psci_ops函数去设置psci操作 eg:qemu平台
  ->*psci_ops = &plat_qemu_psci_pm_ops;
   208 static const plat_psci_ops_t plat_qemu_psci_pm_ops = {
    209         .cpu_standby = qemu_cpu_standby,
    210         .pwr_domain_on = qemu_pwr_domain_on,
    211         .pwr_domain_off = qemu_pwr_domain_off, 
    212         .pwr_domain_suspend = qemu_pwr_domain_suspend,
    213         .pwr_domain_on_finish = qemu_pwr_domain_on_finish,
    214         .pwr_domain_suspend_finish = qemu_pwr_domain_suspend_finish,
    215         .system_off = qemu_system_off,
    216         .system_reset = qemu_system_reset, 
    217         .validate_power_state = qemu_validate_power_state,
    218         .validate_ns_entrypoint = qemu_validate_ns_entrypoint
    219 };

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

4.4 运行时服务触发和处理

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

runtime_exceptions  //el3的异常向量表
->sync_exception_aarch64
->handle_sync_exception
->smc_handler64
->   ¦* Populate the parameters for the SMC handler.
          ¦* We already have x0-x4 in place. x5 will point to a cookie (not used
          ¦* now). x6 will point to the context structure (SP_EL3) and x7 will
          ¦* contain flags we need to pass to the handler Hence save x5-x7.
          ¦*
          ¦* Note: x4 only needs to be preserved for AArch32 callers but we do it
          ¦*       for AArch64 callers as well for convenience
       ¦*/
         stp     x4, x5, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X4]  //保存x4-x7到栈
         stp     x6, x7, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X6]

       /* Save rest of the gpregs and sp_el0*/
         save_x18_to_x29_sp_el0

       mov     x5, xzr  //x5清零
       mov     x6, sp //sp保存在x6

       /* Get the unique owning entity number */ //获得唯一的入口编号
         ubfx    x16, x0, #FUNCID_OEN_SHIFT, #FUNCID_OEN_WIDTH
         ubfx    x15, x0, #FUNCID_TYPE_SHIFT, #FUNCID_TYPE_WIDTH
         orr     x16, x16, x15, lsl #FUNCID_OEN_WIDTH

         adr     x11, (__RT_SVC_DESCS_START__ + RT_SVC_DESC_HANDLE)

       /* Load descriptor index from array of indices */
         adr     x14, rt_svc_descs_indices  //获得服务描述 标识数组
         ldrb    w15, [x14, x16] //根据唯一的入口编号 找到处理函数的 地址
       /*
       ¦* Restore the saved C runtime stack value which will become the new
       ¦* SP_EL0 i.e. EL3 runtime stack. It was saved in the 'cpu_context'
       ¦* structure prior to the last ERET from EL3.
       ¦*/
         ldr     x12, [x6, #CTX_EL3STATE_OFFSET + CTX_RUNTIME_SP]

       /*
       ¦* Any index greater than 127 is invalid. Check bit 7 for
       ¦* a valid index
       ¦*/
         tbnz    w15, 7, smc_unknown

       /* Switch to SP_EL0 */
         msr     spsel, #0  
  
          /*
          ¦* Get the descriptor using the index
          ¦* x11 = (base + off), x15 = index
          ¦*
          ¦* handler = (base + off) + (index << log2(size))
       ¦*/
       lsl     w10, w15, #RT_SVC_SIZE_LOG2
         ldr     x15, [x11, w10, uxtw]

       /*
       ¦* Save the SPSR_EL3, ELR_EL3, & SCR_EL3 in case there is a world
       ¦* switch during SMC handling.
       ¦* TODO: Revisit if all system registers can be saved later.
       ¦*/
   mrs     x16, spsr_el3 //spsr_el3保存在x16
    mrs     x17, elr_el3 //elr_el3保存在x17
   mrs     x18, scr_el3  //scr_el3保存在x18
         stp     x16, x17, [x6, #CTX_EL3STATE_OFFSET + CTX_SPSR_EL3]  /  x16, x17/保存在栈
       str     x18, [x6, #CTX_EL3STATE_OFFSET + CTX_SCR_EL3] //x18保存到栈

       /* Copy SCR_EL3.NS bit to the flag to indicate caller's security */
         bfi     x7, x18, #0, #1

       mov     sp, x12 

       /*
       ¦* Call the Secure Monitor Call handler and then drop directly into
       ¦* el3_exit() which will program any remaining architectural state
       ¦* prior to issuing the ERET to the desired lower EL.
       ¦*/
#if DEBUG
         cbz     x15, rt_svc_fw_critical_error
#endif
         blr     x15  //跳转到处理函数

         b       el3_exit  //从el3退出  会eret 回到el1 (后面会讲到)

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

下面是跳转的处理函数:

std_svc_smc_handler  //services/std_svc/std_svc_setup.c
->ret = psci_smc_handler(smc_fid, x1, x2, x3, x4,
                  ¦   cookie, handle, flags)
                  ...
 480         } else {
481                 /* 64-bit PSCI function */
  482 
  483                 switch (smc_fid) {
  484                 case PSCI_CPU_SUSPEND_AARCH64:
  485                         ret = (u_register_t)
  486                                 psci_cpu_suspend((unsigned int)x1, x2, x3);
  487                         break;
  488 
  489                 case PSCI_CPU_ON_AARCH64:
  490                         ret = (u_register_t)psci_cpu_on(x1, x2, x3);
  491                         break;
  492 
...
}

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

下面分析是重点:!!!

->psci_cpu_on()  //lib/psci/psci_main.c
 ->psci_validate_entry_point()  //验证入口地址有效性并  保存入口点到一个结构ep中
 ->psci_cpu_on_start(target_cpu, &ep)   //ep入口地址
  ->psci_plat_pm_ops->pwr_domain_on(target_cpu)
   ->qemu_pwr_domain_on  //实现核上电(平台实现)
  /* Store the re-entry information for the non-secure world. */
  ->cm_init_context_by_index()  //重点: 会通过cpu的编号找到 cpu上下文(cpu_context_t),存在cpu寄存器的值,异常返回的时候写写到对应的寄存器中,然后eret,旧返回到了el1!!!
   ->cm_setup_context()  //设置cpu上下文
     -> write_ctx_reg(state, CTX_SCR_EL3, scr_el3);  //lib/el3_runtime/aarch64/context_mgmt.c
              write_ctx_reg(state, CTX_ELR_EL3, ep->pc);  //注: 异常返回时执行此地址  于是完成了cpu的启动!!!
              write_ctx_reg(state, CTX_SPSR_EL3, ep->spsr);

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的请求,然后做开核处理,也就是实际的开核动作,但是从处理器最后还是要回到内核中执行,下面分析内核的处理:注意流程如下:

init/main.c
start_kernel
->boot_cpu_init   //引导cpu初始化  设置引导cpu的位掩码 online active present possible都为true
->setup_arch   // arch/arm64/kernel/setup.c
->  if (acpi_disabled)  //不支持acpi
                  psci_dt_init();     //drivers/firmware/psci.c(psci主要文件) psci初始化 解析设备树 寻找psci匹配的节点
          else
                  psci_acpi_init();   //acpi中允许使用psci情况
->rest_init
->kernel_init
->kernel_init_freeable
->smp_prepare_cpus  //准备cpu       对于每个可能的cpu 1. cpu_ops[cpu]->cpu_prepare(cpu)    2.set_cpu_present(cpu, true) cpu处于present状态
->do_pre_smp_initcalls   //多核启动之前的调用initcall回调
->smp_init  //smp初始化  kernel/smp.c   会启动其他从处理器

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

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

->psci_dt_init() //drivers/firmware/psci.c:
 ->init_fn()
  ->psci_0_1_init() //设备树中compatible = "arm,psci"为例
   ->get_set_conduit_method() //根据设备树method属性设置 invoke_psci_fn = __invoke_psci_fn_smc;  (method="smc")
       -> invoke_psci_fn = __invoke_psci_fn_smc
   ->   if (!of_property_read_u32(np, "cpu_on", &id)) {
       651                 psci_function_id[PSCI_FN_CPU_ON] = id;
       652                 psci_ops.cpu_on = psci_cpu_on;  //设置psci操作的开核接口
       653         }
    ->psci_cpu_on()
     ->invoke_psci_fn()
      ->__invoke_psci_fn_smc()
        -> arm_smccc_smc(function_id, arg0, arg1, arg2, 0, 0, 0, 0, &res)  //这个时候x0=function_id  x1=arg0, x2=arg1, x3arg2,...
         ->__arm_smccc_smc()
          ->SMCCC   smc //arch/arm64/kernel/smccc-call.S
            ->    20         .macro SMCCC instr
                21         .cfi_startproc
                22         \instr  #0   //即是smc #0  陷入到el3
                23         ldr     x4, [sp]
                24         stp     x0, x1, [x4, #ARM_SMCCC_RES_X0_OFFS]
                25         stp     x2, x3, [x4, #ARM_SMCCC_RES_X2_OFFS]
                26         ldr     x4, [sp, #8]
                27         cbz     x4, 1f /* no quirk structure */
                28         ldr     x9, [x4, #ARM_SMCCC_QUIRK_ID_OFFS]
                29         cmp     x9, #ARM_SMCCC_QUIRK_QCOM_A6
                30         b.ne    1f
                31         str     x6, [x4, ARM_SMCCC_QUIRK_STATE_OFFS]
                32 1:      ret
                33         .cfi_endproc
                34         .endm

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

smp_init函数做从处理器启动:

start_kernel
->arch_call_rest_init
 ->rest_init
  ->kernel_init,
   ->kernel_init_freeable
    ->smp_prepare_cpus  //arch/arm64/kernel/smp.c
     ->smp_init  //kernel/smp.c  (这是从处理器启动的函数)
      ->cpu_up
       ->do_cpu_up
        ->_cpu_up
         ->cpuhp_up_callbacks
          ->cpuhp_invoke_callback
          ->cpuhp_hp_states[CPUHP_BRINGUP_CPU]
           ->bringup_cpu
            ->__cpu_up  //arch/arm64/kernel/smp.c
             ->boot_secondary
              ->cpu_ops[cpu]->cpu_boot(cpu)
               ->cpu_psci_ops.cpu_boot
                ->cpu_psci_cpu_boot   //arch/arm64/kernel/psci.c
                 46 static int cpu_psci_cpu_boot(unsigned int cpu)
                   47 { 
                   48         int err = psci_ops.cpu_on(cpu_logical_map(cpu), __pa_symbol(secondary_entry));
                   49         if (err)
                   50                 pr_err("failed to boot CPU%d (%d)\n", cpu, err);
                   51   
                   52         return err;
                   53 } 

启动从处理的时候最终调用到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方式多核启动图示:

image

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

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

719 secondary_startup:
720         /*
721         ¦* Common entry point for secondary CPUs.
722         ¦*/
723         bl      __cpu_secondary_check52bitva
724         bl      __cpu_setup                     // initialise processor
725         adrp    x1, swapper_pg_dir   //设置内核主页表
726         bl      __enable_mmu  //使能mmu
727         ldr     x8, =__secondary_switched
728         br      x8
729 ENDPROC(secondary_startup)

||
\/

731 __secondary_switched:
--732         adr_l   x5, vectors      //设置从处理器的异常向量表
--733         msr     vbar_el1, x5
--734         isb      //指令同步屏障 保证屏障前面的指令执行完
  735 
--736         adr_l   x0, secondary_data //获得主处理器传递过来的从处理器数据
--737         ldr     x1, [x0, #CPU_BOOT_STACK]       // get secondary_data.stack  获得栈地址
  738         mov     sp, x1  //设置到从处理器的sp
--739         ldr     x2, [x0, #CPU_BOOT_TASK]  //获得从处理器的tsk  idle进程的tsk结构,
--740         msr     sp_el0, x2 //保存在sp_el0      arm64使用sp_el0保存当前进程的tsk结构
  741         mov     x29, #0  //fp清0
  742         mov     x30, #0  //lr清0
--743         b       secondary_start_kernel //跳转到c程序  继续执行从处理器初始化
  744 ENDPROC(__secondary_switched)

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

arch/arm64/kernel/smp.c:

112 int __cpu_up(unsigned int cpu, struct task_struct *idle)
   113 {
   114         int ret;
   115         long status;
   116 
   117         /*
   118         ¦* We need to tell the secondary core where to find its stack and the
   119         ¦* page tables.
   120         ¦*/
   121         secondary_data.task = idle;  //执行的进程描述符
   122         secondary_data.stack = task_stack_page(idle) + THREAD_SIZE; //栈地址   THREAD_SIZE=16k
   123         update_cpu_boot_status(CPU_MMU_OFF);
   124         __flush_dcache_area(&secondary_data, sizeof(secondary_data));
   125 
   126         /*
   127         ¦* Now bring the CPU into our world.
   128         ¦*/
   129         ret = boot_secondary(cpu, idle);

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

183 /*
   184  * This is the secondary CPU boot entry.  We're using this CPUs
   185  * idle thread stack, but a set of temporary page tables.
   186  */
   187 asmlinkage notrace void secondary_start_kernel(void)
   188 {
   189         u64 mpidr = read_cpuid_mpidr() & MPIDR_HWID_BITMASK;
   190         struct mm_struct *mm = &init_mm;
   191         unsigned int cpu;
   192 
   193         cpu = task_cpu(current);
   194         set_my_cpu_offset(per_cpu_offset(cpu));
   195 
   196         /*
   197         ¦* All kernel threads share the same mm context; grab a
   198         ¦* reference and switch to it.
   199         ¦*/
   200         mmgrab(mm); //init_mm的引用计数加1 
   201         current->active_mm = mm; //设置idle借用的mm结构
   202 
   203         /*
   204         ¦* TTBR0 is only used for the identity mapping at this stage. Make it
   205         ¦* point to zero page to avoid speculatively fetching new entries.
   206         ¦*/
   207         cpu_uninstall_idmap();
   208 
   209         preempt_disable(); //禁止内核抢占
   210         trace_hardirqs_off();
   211 
   212         /*
   213         ¦* If the system has established the capabilities, make sure
   214         ¦* this CPU ticks all of those. If it doesn't, the CPU will
   215         ¦* fail to come online.
   216         ¦*/
   217         check_local_cpu_capabilities();
 218 
   219         if (cpu_ops[cpu]->cpu_postboot)
   220                 cpu_ops[cpu]->cpu_postboot();
   221 
   222         /*
   223         ¦* Log the CPU info before it is marked online and might get read.
   224         ¦*/
   225         cpuinfo_store_cpu(); //存储cpu信息
   226 
   227         /*
   228         ¦* Enable GIC and timers.
   229         ¦*/
   230         notify_cpu_starting(cpu); //使能gic和timer
   231 
   232         store_cpu_topology(cpu); //保存cpu拓扑
   233         numa_add_cpu(cpu); ///numa添加cpu
   234 
   235         /*
   236         ¦* OK, now it's safe to let the boot CPU continue.  Wait for
   237         ¦* the CPU migration code to notice that the CPU is online
   238         ¦* before we continue.
   239         ¦*/
   240         pr_info("CPU%u: Booted secondary processor 0x%010lx [0x%08x]\n",
   241                                         ¦cpu, (unsigned long)mpidr,
   242                                         ¦read_cpuid_id());  //打印内核log
   243         update_cpu_boot_status(CPU_BOOT_SUCCESS);
   244         set_cpu_online(cpu, true);  //设置cpu状态为online
   245         complete(&cpu_running); //唤醒主处理器的 完成等待函数,继续启动下一个从处理器  
   246 
   247         local_daif_restore(DAIF_PROCCTX);  //从处理器继续往下执行
248 
   249         /*
   250         ¦* OK, it's off to the idle thread for us
   251         ¦*/
   252         cpu_startup_entry(CPUHP_AP_ONLINE_IDLE); //idle进程进入idle状态
   253 }

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

6.最后说两句

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

标签:PSCI,svc,SMP,init,psci,ARM64,secondary,cpu,处理器
From: https://www.cnblogs.com/linhaostudy/p/17873920.html

相关文章

  • Windows 11 version 22H2 中文版、英文版 (x64、ARM64) 下载 (released Nov 2023)
    Windows11version22H2中文版、英文版(x64、ARM64)下载(releasedNov2023)Windows11,version23H2,2023年10月31日发布(本月暂未更)请访问原文链接:https://sysin.org/blog/windows-11/,查看最新版。原创作品,转载请保留出处。作者主页:sysin.orgWindows11目前版......
  • filerun docker方式安装(debian arm64, tinkerboard2s类似树莓派)
    启动mysqldockerrun-p3366:3306--namemysql57--privileged=true-eMYSQL_ROOT_PASSWORD=12345-v/mnt/docker/varlibmysql:/var/lib/mysql-dliupeng0518/mysql:5.7-arm64#redis命令dockerrun-itd--nameredis--privileged=true-p6380:6379redis--requir......
  • jsmpeg视频播放器使用方法和常见问题解决方案
    JSMpeg是一个使用JavaScript编写的视频播放器,它可以在浏览器中播放MPEG1视频和MP2音频流。JSMpeg的特点是它能够通过WebSockets实时传输视频流,并且可以在不支持HTML5视频播放器的浏览器上运行。以下是JSMpeg的基本使用方法和一些常见问题的解决方案:主要用来解决移移动端视频播放问......
  • ubuntu arm64 配置静态IP 并实现VNC远程树梅派
    1.设置静态IP完成后ifconfig查看IPpingIP地址测试 ping192.168.10.1592.VNC远程树梅派树梅派VNC是server端,VNC版本是:RealVNC客户端:archubuntu18 一开始用gvncviewer连接,出现秒断开的问题,如下:$gvncviewer192.168.149.1ConnectedtoserverDisconnectedfromser......
  • x86机器上运行arm64 docker
    DockerHub上可以找到各种非x86_64平台的镜像,但是在x86上直接运行会报错:panic:standard_init_linux.go:175:execuserprocesscaused“execformaterror”[recovered]在x86_64平台上缺少运行arm32v7的Python解释器所需要的“环境依赖”,值得庆幸的是在Linux上我......
  • smp irq affinity介绍
    一、概述硬件中断发生频繁,是件很消耗CPU资源的事情,在多核CPU条件下如果有办法把大量硬件中断分配给不同的CPU(core)处理显然能很好的平衡性能。kernel2.4以后的版本利用可编程中断控制器的特性支持把不同的硬件中断请求(IRQs)分配到特定的CPU上,这个绑定技术被称为S......
  • Windows 11 version 23H2 中文版、英文版 (x64、ARM64) 下载 (released Oct 2023)
    Windows11version23H2中文版、英文版(x64、ARM64)下载(releasedOct2023)Windows11,version23H2,2023年10月31日发布请访问原文链接:https://sysin.org/blog/windows-11/,查看最新版。原创作品,转载请保留出处。作者主页:sysin.org全新Windows体验,让您与热爱的人......
  • 统信桌面操作系统UOS上安装arm64版nginx
    hello,大家好啊,今天给大家带来一篇在统信桌面操作系统UOS上安装arm64版nginx的文章,本篇文章主要是给大家提供一种下载离线nginx软件包的方法,拿到软件包可以去不能链接互联网又没有仓库环境的电脑上安装,欢迎大家浏览分享,点一个关注可好。1、下载nginx离线包到本地pdsyw@pdsyw-PC:~/De......
  • linux kernel的启动参数是怎么拿到的-以arm64为例
    linuxkernel拿到启动参数一定是在boot阶段,那就必须从start_kernel找起。asmlinkage__visible__init__no_sanitize_address__noreturn__no_stack_protectorvoidstart_kernel(void){。。。setup_arch(&command_line);setup_arch的参数里有command_lin......
  • arm64和x64与苹果芯片的关系备忘
    title:arm64和x64与苹果芯片的关系备忘date:2023-10-16T00:00:00.000Zauthor:KazooTTTtags:-备忘published:truelink:https://kazoottt.notion.site/arm64-x64-b527f80503f241d3a0f20503eb8a9c0cnotionID:b527f805-03f2-41d3-a0f2-0503eb8a9c0carm64和x64与苹果......