在上篇文章中,我们较为清楚的了解的内核启动的主要过程,这一次,将分析一下如果spike模拟多个处理器时,将会有怎样的不同
由于已经分析过主要的hart(hardware thread)的相关过程了,找其他部分的难度相对减少
// riscv-pk/machine/mentry.S
do_reset:
.....
csrr a3, mhartid
slli a2, a3, RISCV_PGSHIFT
add sp, sp, a2
bnez a3, .LmultiHartInit
.....
.LmultiHartInit:
# set MSIE bit to receive IPI
li a2, MIP_MSIP
csrw mie, a2
.LmultiHart:
#if MAX_HARTS > 1
# wait for an IPI to signal that it's safe to boot
wfi
# masked harts never start
la a4, disabled_hart_mask
LOAD a4, 0(a4)
srl a4, a4, a3
andi a4, a4, 1
bnez a4, .LmultiHart
# only start if mip is set
csrr a2, mip
andi a2, a2, MIP_MSIP
beqz a2, .LmultiHart
# make sure our hart id is within a valid range
fence
li a2, MAX_HARTS
bltu a3, a2, init_other_hart
很清晰的看到,代码中会通过检测 mhartid 寄存器来判断目前的处理器,非0的处理器都将跳向.LmultiHartInit
;
在其中,注释中写明了要通过设置MIE寄存器来允许IPI的终端
MIE(Machine Interrupt Enable) 其指定处理器目前能处理和需要忽略的中断;手册上指明,其MSIE bit表示了允许M-mode software interrupts
void init_other_hart(uintptr_t hartid, uintptr_t dtb)
{
hart_init();
hart_plic_init();
boot_other_hart(dtb);
}
然后来到了C代码的init_other_hart
,首先也是初始化工作,与init_first_hart
有许多的不同,没有了设备相关的初始化工作和内存相关的设置。
void boot_other_hart(uintptr_t unused __attribute__((unused)))
{
const void* entry;
do {
entry = entry_point;
mb();
} while (!entry);
long hartid = read_csr(mhartid);
if ((1 << hartid) & disabled_hart_mask) {
while (1) {
__asm__ volatile("wfi");
#ifdef __riscv_div
__asm__ volatile("div x0, x0, x0");
#endif
}
}
#ifdef BBL_BOOT_MACHINE
enter_machine_mode(entry, hartid, dtb_output());
#else /* Run bbl in supervisor mode */
protect_memory();
enter_supervisor_mode(entry, hartid, dtb_output());
#endif
}
other hardware thread 的启动过程在这里也将跳往Linux,多处理器的过程模拟一定是复杂的,由于本身没有学习过Linux kernel在多处理器下启动的内核代码,所以这里分析的bootloader的工作也就是比较简易了。
如果有阅读过上文的分析,就会发现,在硬件的spike实现上并没有清晰的感觉到多核的处理。那么spike是写出了怎样的C++程序来驱动多处理器架构的呢?我们会在未来进行分析。