持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第14天,点击查看活动详情
在上篇博客中我记录了一些多处理器支持的内容,这篇文章讲一讲紧接着的 应用处理器引导程序
应用处理器引导程序
在启动接入点之前,BSP 应首先收集有关多处理器系统的信息,例如 CPU 的总数、其 APIC ID 和 LAPIC 单元的 MMIO 地址。kern/mpconfig.c 中的mp_init()函数通过读取驻留在 BIOS 内存区域中的 MP 配置表来检索此信息。
boot_aps()函数(在 kern/init.c 中)驱动 AP 引导进程。AP 以实模式启动,就像引导加载程序在引导/引导时启动的方式一样。因此boot_aps()复制 AP 输入代码(kern/mpentry.S) 到在实模式中可寻址的内存位置。与引导加载程序不同,我们可以控制AP开始执行代码的位置;我们将输入代码复制到0x7000(MPENTRY_PADDR),但任何未使用的低于640KB的页面对齐的物理地址都可以进行工作。
之后,通过将IPI发送到相应AP的LAPIC单元以及AP应开始运行其输入代码的初始CS:IP地址(MPENTRY_PADDR),一个接一个地激活AP。kern/mpentry.S中的入口代码与boot/boot.S非常相似。 经过一些简短的设置后,它会将 AP 置于启用寻呼的保护模式,然后调用 C 设置例程mp_main()(也在 kern/init.c 中)。boot_aps()等待 AP 在其CpuInfo字段中发出CPU_STARTED信号,然后再继续唤醒下一个标志。
Exercise 2. Read boot_aps() and mp_main() in kern/init.c, and the assembly code in kern/mpentry.S. Make sure you understand the control flow transfer during the bootstrap of APs. Then modify your implementation of page_init() in kern/pmap.c to avoid adding the page at MPENTRY_PADDR to the free list, so that we can safely copy and run AP bootstrap code at that physical address. Your code should pass the updated check_page_free_list() test (but might fail the updated check_kern_pgdir() test, which we will fix soon).
void
page_init(void){
pages[0].pp_ref = 1;
pages[0].pp_link = NULL;
size_t i;
size_t kernel_end_page = PADDR(boot_alloc(0)) / PGSIZE;
size_t mpentry = MPENTRY_PADDR / PGSIZE;
for (i = 1; i < npages; i++) {
if (i >= npages_basemem && i < kernel_end_page) {
pages[i].pp_ref = 1;
pages[i].pp_link = NULL;
} else if (i == mpentry) {
pages[i].pp_ref = 1;
pages[i].pp_link = NULL;
} else {
pages[i].pp_ref = 0;
pages[i].pp_link = page_free_list;
page_free_list = &pages[i];
}
}
}
注意:
- 我们要将0号物理页标记为正在使用以保留实模式的IDT和BIOS结构(以备不时之需)。
- [PGSIZE,npages_basemem*PGSIZE)留作空闲
- IO hole的地址[IOPHYSMEM,EXTPHYSMEM)永远不要分配内容
- 不要实际访问物理内存的空闲页
首先我们将0号页面进行处理,让其视为被使用。然后设置mpentry,并进行单独处理避免被添加到空闲页面中,让我们可以在该物理地址安全复制和运行AP的引导代码。
问题
比较kern/mpentry.S和boot/boot.S,考虑kern/mpentry.S和内核其他东西一样都被链接到KERNBASE上方运行,那么宏MPBOOTPHYS的目的是什么?为什么在kern/mpentry.S中是必须的但是在boot/boot.S并不需要?如果省略了这个宏会出现什么问题?
Hint: recall the differences between the link address and the load address that we have discussed in Lab 1.
这里我们再次简单讨论一下链接地址和加载地址的区别。当链接指定的地址和实际运行地址相同,即链接地址==加载地址,二者没有区别。但是当链接指定的地址和实际运行地址不同时,如代码的地址是相对偏移地址(MPBOOTPHYS)时,二者就会出现偏差,这时我们应使用实际代码运行的地址而不是链接地址(指定运行地址)。
在boot.S中,我们还没有启动分页机制和多处理器,这时程序开始的地址和指定地址是相同的。但是在mpentry.S中,我们的CPU已经处于了保护模式之下,这时我们需要对每个处理器的地址和页表进行物理地址的映射。