前面解析了MP config, 这次准备为babyos2实现启动其他processor。
解析config的时候可以看到,其中一个processor被指定为bootstrap processor(BSP),其他的称为application processor(AP)。AP 的启动有固定的流程,在Intel MP spec中有描述。
为了简化cpu_t类,把一些代码挪到了新的类,在此不再描述。然后在arch_t类中,把cpu_t变成一个数组,表示多个CPU。在此处CPU指代一个processor。解析MP config的时候设置响应的apic id以及是不是BSP。
当只有一个processor时,babyos2启动流程:
1)CPU上电自检,BIOS加载引导扇区到0x7c00并跳转到该处执行
2)引导程序将kernel的前两个扇区(loader)加载到0x0处,进入保护模式,并跳转到0x0处开始执行
3)loader负责加载elf格式的kernel,并load一个字体文件到指定位置,然后跳转到elf格式的kernel的entry入口处执行
4)entry是一段汇编代码,负责设置启动阶段的页表,并开启分页,然后跳转到main开始执行
5)设置新的页表,各种初始化,创建进程等。
当有多个processor时,BSP的启动流程不变,当BSP启动基本完成,创建进程前,需要唤醒各个APs。
1)加载start_ap到指定位置,准备好几个参数,如页表,内核栈,入口地址等
2)利用APIC 发送IPI,让AP进入start_ap开始执行
3)start_ap负责进入保护模式,及开启分页
4)跳转到apmain执行其他初始化
5)告诉BSP启动完成,让BSP启动其他AP
1.BSP的准备工作
void arch_t::start_ap()
{
uint32* ap_start_addr = (uint32 *) PA2VA(AP_START_ADDR);
uint32* ap_main = (uint32 *) PA2VA(AP_MAIN);
uint32* ap_kstack = (uint32 *) PA2VA(AP_KSTACK);
uint32* ap_pgdir = (uint32 *) PA2VA(AP_PGDIR);
uint32* ap_index = (uint32 *) PA2VA(AP_INDEX);
*ap_main = (uint32) apmain;
*ap_pgdir = (uint32) VA2PA(entry_pg_dir);
memcpy(ap_start_addr, _binary_start_ap_start, (uint32) _binary_start_ap_size);
console()->kprintf(PINK, "start_ap: %p, size: %x\n", (uint32) _binary_start_ap_start,
(uint32) _binary_start_ap_size);
console()->kprintf(PINK, "ap_main: %p, %p, %p, %p\n", AP_MAIN, ap_main, *ap_main, apmain);
console()->kprintf(PINK, "ap_pgdir: %p, %p, %p, %p\n", AP_PGDIR, ap_pgdir, *ap_pgdir,
os()->get_mm()->get_kernel_pg_dir());
for (int i = 0; i < (uint32) _binary_start_ap_size / 4; i++) {
if (i % 8 == 0) {
console()->kprintf(PINK, "\n");
}
console()->kprintf(PINK, "%x, ", ap_start_addr[i]);
}
console()->kprintf(PINK, "\n");
for (int i = 0; i < m_cpu_num; i++) {
if (m_cpu[i].is_bsp()) {
continue;
}
*ap_index = i;
*ap_kstack = (uint32) m_cpu[i].get_kstack() + KSTACK_SIZE;
console()->kprintf(PINK, "ap_kstack: %p, %p, %p\n", AP_KSTACK, ap_kstack, *ap_kstack);
m_cpu[i].get_local_apic()->start_ap(m_cpu[i].get_apic_id(), AP_START_ADDR);
console()->kprintf(YELLOW, "wait ap started\n");
while (!m_cpu[i].is_started()) {
delay_t::ms_delay(1000);
console()->kprintf(CYAN, "check cpu %u started: %u\n", i, m_cpu[i].is_started());
}
console()->kprintf(YELLOW, "CPU %u started.\n", m_cpu[i].get_apic_id());
}
}