在过去的3-4天里,我一直在用这个来敲打我的脑袋,我找不到DECENT解释性文件(来自ARM或非官方的)来帮助我。 我有一个 ODROID-XU板(big.LITTLE 2 x Cortex-A15 + 2 x Cortex-A7)板,我正在尝试更多地了解ARM架构。在我的“试验”代码中,我现在已经到了我想要从其WFI(等待中断)状态唤醒其他核心的阶段。
我仍在尝试找到的缺失信息是:
1。当获取内存映射GIC的基地址时,我明白我需要读取CBAR;但没有任何文档解释如何安排CBAR中的位(2个PERIPHBASE值)来获取最终的GIC基址
2. 当通过GICD_SGIR寄存器发送SGI时,我应该选择0到15之间的中断ID?这有关系吗?
3. 当通过GICD_SGIR寄存器发送SGI时,如何告知其他内核从哪里开始执行?
4. U-BOOT引导程序加载我的代码的事实如何影响此上下文?
Cortex-A系列程序员指南v3.0 (在此处找到:link)在第22.5.2节中说明了以下内容(Linux中的SMP启动) ,页面 271 ):
当主核心正在启动时,辅助核心将保持待机状态,使用 WFI指令。它(主核心)将为辅助核心提供启动地址,并使用一个唤醒它们 处理器间中断(IPI),即通过GIC发出信号的SGI
Linux如何做到这一点?文档 S 不提供有关“它将为辅助核心提供启动地址”的任何其他详细信息。
我的挫折感正在增长,我会非常感谢答案。 非常感谢你提前!
额外详细信息
我使用的文档:
我现在做了什么:
以上所有内容似乎都能正常运作。
我现在要做的是:
.... .... UPDATE
我开始查看Linux内核和QEMU源代码以寻找答案。这是我发现的(如果我错了请纠正我):
**(1)** the secondary cores enter WFI and when
**(2)** the primary core sends an SGI to wake them up
**(3)** they check if the value at address (0x40 + 0x10 * coreid) is non-null;
**(4)** if it is non-null, they use it as an address to jump to (execute a BX)
**(5)** otherwise, they re-enter standby state, by re-executing WFI
**(6)** So, if I had an EnergyCore ECX-1000 board, I should write (0x40 + 0x10 * coreid) with the address I want each of the cores to jump to and send an SGI
问题:
答案 0 :(得分:9)
好的,我回来了。以下是结论:
对于我的ODROID-XU板,描述这个过程的源代码是UBOOT ODROID-v2012.07,并且在这里找到了linux内核:LINUX ODROIDXU-3.4.y(如果我从分支机构查看内核版本会更好{{3}因为前者没有启动所有8个处理器,只有4个处理器,但后者确实如此)。
无论如何,这是我提出的源代码,我将发布上述源代码树中的相关源文件,帮助我编写此代码:
typedef unsigned int DWORD;
typedef unsigned char BOOLEAN;
#define FAILURE (0)
#define SUCCESS (1)
#define NR_EXTRA_CPUS (3) // actually 7, but this kernel version can't wake them up all -> check kernel version 3.12 if you need this
// Hardcoded in the kernel and in U-Boot; here I've put the physical addresses for ease
// In my code (and in the linux kernel) these addresses are actually virtual
// (thus the 'VA' part in S5P_VA_...); note: mapped with memory type DEVICE
#define S5P_VA_CHIPID (0x10000000)
#define S5P_VA_SYSRAM_NS (0x02073000)
#define S5P_VA_PMU (0x10040000)
#define EXYNOS_SWRESET ((DWORD) S5P_VA_PMU + 0x0400)
// Other hardcoded values
#define EXYNOS5410_REV_1_0 (0x10)
#define EXYNOS_CORE_LOCAL_PWR_EN (0x3)
BOOLEAN BootAllSecondaryCPUs(void* CPUExecutionAddress){
// 1. Get bootBase (the address where we need to write the address where the woken CPUs will jump to)
// and powerBase (we also need to power up the cpus before waking them up (?))
DWORD bootBase, powerBase, powerOffset, clusterID;
asm volatile ("mrc p15, 0, %0, c0, c0, 5" : "=r" (clusterID));
clusterID = (clusterID >> 8);
powerOffset = 0;
if( (*(DWORD*)S5P_VA_CHIPID & 0xFF) < EXYNOS5410_REV_1_0 )
{
if( (clusterID & 0x1) == 0 ) powerOffset = 4;
}
else if( (clusterID & 0x1) != 0 ) powerOffset = 4;
bootBase = S5P_VA_SYSRAM_NS + 0x1C;
powerBase = (S5P_VA_PMU + 0x2000) + (powerOffset * 0x80);
// 2. Power up each CPU, write bootBase and send a SEV (they are in WFE [wait-for-event] standby state)
for (i = 1; i <= NR_EXTRA_CPUS; i++)
{
// 2.1 Power up this CPU
powerBase += 0x80;
DWORD powerStatus = *(DWORD*)( (DWORD) powerBase + 0x4);
if ((powerStatus & EXYNOS_CORE_LOCAL_PWR_EN) == 0)
{
*(DWORD*) powerBase = EXYNOS_CORE_LOCAL_PWR_EN;
for (i = 0; i < 10; i++) // 10 millis timeout
{
powerStatus = *(DWORD*)((DWORD) powerBase + 0x4);
if ((powerStatus & EXYNOS_CORE_LOCAL_PWR_EN) == EXYNOS_CORE_LOCAL_PWR_EN)
break;
DelayMilliseconds(1); // not implemented here, if you need this, post a comment request
}
if ((powerStatus & EXYNOS_CORE_LOCAL_PWR_EN) != EXYNOS_CORE_LOCAL_PWR_EN)
return FAILURE;
}
if ( (clusterID & 0x0F) != 0 )
{
if ( *(DWORD*)(S5P_VA_PMU + 0x0908) == 0 )
do { DelayMicroseconds(10); } // not implemented here, if you need this, post a comment request
while (*(DWORD*)(S5P_VA_PMU + 0x0908) == 0);
*(DWORD*) EXYNOS_SWRESET = (DWORD)(((1 << 20) | (1 << 8)) << i);
}
// 2.2 Write bootBase and execute a SEV to finally wake up the CPUs
asm volatile ("dmb" : : : "memory");
*(DWORD*) bootBase = (DWORD) CPUExecutionAddress;
asm volatile ("isb");
asm volatile ("\n dsb\n sev\n nop\n");
}
return SUCCESS;
}
这成功唤醒了7个辅助CPU中的3个。
现在,对于u-boot和linux内核中相关源文件的简短列表:
UBOOT:odroid-3.12.y - 注意行 363-369 ,辅助CPU如何在WFE中等待 _hotplug_addr的值非归零并跳转到它; _hotplug_addr实际上是上面代码中的bootBase; 也是行 282-285 告诉我们_hotplug_addr要重新定位在 CONFIG_PHY_IRAM_NS_BASE + _hotplug_addr - nscode_base ( _hotplug_addr - nscode_base 0x1C 且 CONFIG_PHY_IRAM_NS_BASE为0x02073000 ,因此上面的硬编码在linux内核中)
LINUX KERNEL:通用 - lowlevel_init.S (查看函数 __ cpu_up ),特定于平台(odroid-xu):{{ 3}} (函数 boot_secondary ,由泛型__cpu_up调用;同时查看 platform_smp_prepare_cpus [在底部] =&gt;这是实际设置启动的函数基数和权力基础值)
答案 1 :(得分:2)
为了清晰和未来的参考,由于缺乏Exynos引导协议的适当文档,这里缺少一些微妙的信息(这个问题应该真的标记为“Exynos 5”而不是“Cortex-A15” - 它是特定于SoC的东西以及ARM所说的只是一般性建议。 从冷启动开始,辅助核心不在WFI中,它们仍然关闭。
更简单的最小解决方案(基于Linux的hotplug所做的),我在编写启动填充程序以在XU上运行虚拟机管理程序的过程中制定出来,需要两个步骤:
(0x02073000 + 0x1c)
除非你计划一个完整的CPU热插拔实现,否则你可以跳过检查群集ID - 如果我们正在启动,我们就在群集0上而不是其他地方(检查具有向后群集寄存器的预生产芯片在Odroid上也应该没必要 - 当然对我来说。)
从我的调查来看,启动A7更加复杂。从the Exynos big.LITTLE switcher driver来看,似乎你需要先选择一组独立的电源控制器寄存器来启用集群1(你可能也需要搞乱CCI,尤其是让MMU和缓存打开) - I没有进一步发展,因为到那时它更“玩得开心”而不是“做实事”......
顺便说一句,Samsung's mainline patch for CPU hotplug on the 5410使核心功率控制的东西比下游代码IMO中的混乱更清晰。
答案 2 :(得分:0)
转到www.arm.com并下载DS-5开发套件的评估版。安装后,在示例下将有startup_Cortex-A15MPCore directory
。看看startup.s
。
答案 3 :(得分:0)
QEMU使用PSCI
在https://developer.arm.com/docs/den0022/latest/arm-power-state-coordination-interface-platform-design-document上记录了ARM电源状态协调接口(PSCI),它控制诸如打开和关闭内核电源之类的事情。
TL; DR,这是用于唤醒QEMU v3.0.0 ARMv8 aarch64上的CPU 1的aarch64代码段:
/* PSCI function identifier: CPU_ON. */
ldr w0, =0xc4000003
/* Argument 1: target_cpu */
mov x1, 1
/* Argument 2: entry_point_address */
ldr x2, =cpu1_entry_address
/* Argument 3: context_id */
mov x3, 0
/* Unused hvc args: the Linux kernel zeroes them,
* but I don't think it is required.
*/
hvc 0
以及对于ARMv7:
ldr r0, =0x84000003
mov r1, #1
ldr r2, =cpu1_entry_address
mov r3, #0
hvc 0
此答案的ARM部分提供了带有自旋锁的完整可运行示例:What does multicore assembly language look like?
然后hvc
指令由EL2处理程序处理,另请参见:What are Ring 0 and Ring 3 in the context of operating systems?的ARM部分
Linux内核
在Linux v4.19中,该地址通过设备树通知Linux内核,例如QEMU自动生成以下形式的条目:
psci {
method = "hvc";
compatible = "arm,psci-0.2", "arm,psci";
cpu_on = <0xc4000003>;
migrate = <0xc4000005>;
cpu_suspend = <0xc4000001>;
cpu_off = <0x84000002>;
};
hvc
的调用来自:https://github.com/torvalds/linux/blob/v4.19/drivers/firmware/psci.c#L178
static int psci_cpu_on(unsigned long cpuid, unsigned long entry_point)
最终将转到:https://github.com/torvalds/linux/blob/v4.19/arch/arm64/kernel/smccc-call.S#L51