// plat/qemu/common/aarch64/plat_helpers.S /* ----------------------------------------------------- * void plat_secondary_cold_boot_setup (void); * * This function performs any platform specific actions * needed for a secondary cpu after a cold reset e.g * mark the cpu's presence, mechanism to place it in a * holding pen etc. * ----------------------------------------------------- */ func plat_secondary_cold_boot_setup /* Calculate address of our hold entry */ bl plat_my_core_pos lsl x0, x0, #PLAT_QEMU_HOLD_ENTRY_SHIFT mov_imm x2, PLAT_QEMU_HOLD_BASE /* Wait until we have a go */ poll_mailbox: ldr x1, [x2, x0] cbz x1, 1f /* Clear the mailbox again ready for next time. */ mov x1, #PLAT_QEMU_HOLD_STATE_WAIT str x1, [x2, x0] /* Jump to the provided entrypoint. */ mov_imm x0, PLAT_QEMU_TRUSTED_MAILBOX_BASE ldr x1, [x0] br x1 1: wfe b poll_mailbox endfunc plat_secondary_cold_boot_setup
// services/std_svc/std_svc_setup.c /* Register Standard Service Calls as runtime service */ DECLARE_RT_SVC( std_svc, OEN_STD_START, OEN_STD_END, SMC_TYPE_FAST, std_svc_setup, std_svc_smc_handler );
bl31_main ->runtime_svc_init // common/runtime_svc.c void __init runtime_svc_init(void) { ...... for (index = 0U; index < RT_SVC_DECS_NUM; index++) { ...... /* * The runtime service may have separate rt_svc_desc_t * for its fast smc and yielding smc. Since the service itself * need to be initialized only once, only one of them will have * an initialisation routine defined. Call the initialisation * routine for this runtime service, if it is defined. */ if (service->init != NULL) { rc = service->init(); if (rc != 0) { ERROR("Error initializing runtime service %s\n", service->name); continue; } } ...... } }
// arch/arm64/kernel/smp.c /* * This is the secondary CPU boot entry. We're using this CPUs * idle thread stack, but a set of temporary page tables. */ asmlinkage notrace voidsecondary_start_kernel(void) { // 获取当前CPU的多处理器标识寄存器(MPIDR)的值,并与掩码按位与,得到用于标识当前CPU的ID,后续用于区分不同CPU等操作 u64 mpidr = read_cpuid_mpidr() & MPIDR_HWID_BITMASK; // 指向所有内核线程共享的内存管理上下文结构体init_mm,二级CPU将关联此通用内存管理环境 structmm_struct *mm = &init_mm; // 用于获取当前CPU对应的特定操作函数集合结构体指针 conststructcpu_operations *ops; // 获取当前CPU的编号,用于后续针对特定CPU的操作 unsignedint cpu = smp_processor_id(); /* * All kernel threads share the same mm context; grab a * reference and switch to it. */ mmgrab(mm); current->active_mm = mm; /* * TTBR0 is only used for the identity mapping at this stage. Make it * point to zero page to avoid speculatively fetching new entries. */ cpu_uninstall_idmap(); // 如果系统使用中断优先级屏蔽机制,就进行相应的初始化,确保中断按期望的优先级规则处理 if (system_uses_irq_prio_masking()) init_gic_priority_masking(); rcu_cpu_starting(cpu); trace_hardirqs_off(); // 关闭硬件中断的跟踪功能,减少启动阶段不必要的开销,专注于关键初始化 /* * If the system has established the capabilities, make sure * this CPU ticks all of those. If it doesn't, the CPU will * fail to come online. */ check_local_cpu_capabilities(); ops = get_cpu_ops(cpu); // 获取当前CPU对应的操作函数集合指针,用于后续执行特定于该CPU的操作 if (ops->cpu_postboot) ops->cpu_postboot(); // 如果存在启动后特定操作函数,就执行它,完成CPU启动后的额外初始化工作 /* * Log the CPU info before it is marked online and might get read. */ cpuinfo_store_cpu(); store_cpu_topology(cpu); /* * Enable GIC and timers. */ notify_cpu_starting(cpu); ipi_setup(cpu); // 进行处理器间中断(IPI)相关设置,用于建立CPU间可靠的通信机制 numa_add_cpu(cpu); // 在NUMA架构下,将当前CPU添加到NUMA相关管理结构中,便于后续内存分配等考虑CPU与内存的关系 /* * OK, now it's safe to let the boot CPU continue. Wait for * the CPU migration code to notice that the CPU is online * before we continue. */ pr_info("CPU%u: Booted secondary processor 0x%010lx [0x%08x]\n", cpu, (unsignedlong)mpidr, read_cpuid_id()); update_cpu_boot_status(CPU_BOOT_SUCCESS); // 将当前CPU的启动状态更新为成功启动,方便其他模块判断启动情况 set_cpu_online(cpu, true); // 正式将当前CPU设置为在线状态,使其可参与系统的任务调度等正常运行操作 complete(&cpu_running); // 完成一个同步操作,通知其他等待的代码(如引导CPU相关代码)当前CPU已准备就绪 local_daif_restore(DAIF_PROCCTX); // 恢复本地(当前CPU)的DAIF寄存器状态到指定的进程上下文相关状态,保障中断等处理正常 /* * OK, it's off to the idle thread for us */ cpu_startup_entry(CPUHP_AP_ONLINE_IDLE); // 进入idle状态 }
可以看到从 CPU 进入到内核后所进行的一系列配置操作,获取当前 CPU 的标识相关信息(例如“mpidr”)以及编号(“cpu”),并将其与内核线程共享的内存管理上下文(“init_mm”)相关联。紧接着开展一系列初始化工作,例如处理与内存映射有关的“TTBR0”设置、中断相关功能的初始化(这些都是与处理器强相关的初始化代码,一些通用的初始化工作已由主处理器完成)。
随后向系统的其他部分通告当前 CPU 已成功启动且能够参与运行,最终进入空闲线程等待调度器分配任务,至此,二级 CPU 顺利完成启动流程并融入到多处理器系统的运行之中。
参考
ARM64 SMP多核启动(上)- spin-table - yooooooo - 博客园
ARM64 SMP多核启动(下)- PSCI - yooooooo - 博客园
ChinaUnix博客 - Article Blog
GitHub - u-boot/u-boot: “Das U-Boot” Source Tree
GitHub - ARM-software/arm-trusted-firmware: Read-only mirror of Trusted Firmware-A
This is copyright.