MIT S6.828 Lab4 学习记录(二)

179 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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];
        }
    }
}

注意:

  1. 我们要将0号物理页标记为正在使用以保留实模式的IDT和BIOS结构(以备不时之需)。
  2. [PGSIZE,npages_basemem*PGSIZE)留作空闲
  3. IO hole的地址[IOPHYSMEM,EXTPHYSMEM)永远不要分配内容
  4. 不要实际访问物理内存的空闲页

首先我们将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已经处于了保护模式之下,这时我们需要对每个处理器的地址和页表进行物理地址的映射。