Cor*_*uzu 16 arm multiprocessing cpu-cores wakeup cortex-a
在过去的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系列程序员指南3.0版(这里找到:链接)规定在以下部分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
问题:
好的,我回来了.以下是结论:
对于我的ODROID-XU板,描述这个过程的源代码是UBOOT ODROID-v2012.07和linux内核:LINUX ODROIDXU-3.4.y(如果我从分支odroid-3.12查看内核版本会更好. Ÿ因为前者不启动所有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;
}
Run Code Online (Sandbox Code Playgroud)
这成功唤醒了7个辅助CPU中的3个.
现在,对于u-boot和linux内核中相关源文件的简短列表:
UBOOT:lowlevel_init.S - 通知行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:泛型 - smp.c(查看函数__cpu_up),特定于平台(odroid-xu):platsmp.c(函数boot_secondary,通用__cpu_up调用;另见platform_smp_prepare_cpus [在底部] =>这是函数实际设置启动基数和功率基值)