下一页 上一页 目录

4. Linux 架构特定初始化

(来自 "linux/arch/i386/kernel/head.S")

"linux/arch/i386/boot/setup.S" 中的引导代码将执行转移到 "linux/arch/i386/kernel/head.S" 中的起始代码(标记为 "startup_32:")。

为了到达这一点,一个小的未压缩内核函数解压缩剩余的压缩内核镜像,然后跳转到新的内核代码。

这是对 "head.S" 代码所做工作的描述。

4.1 startup_32

swapper_pg_dir 是顶层页目录,地址为 0x00101000。

在入口处,%esi 指向实模式代码,作为一个 32 位指针。

4.2 将段寄存器设置为已知值

将 %ds、%es、%fs 和 %gs 寄存器设置为 __KERNEL_DS。

4.3 SMP BSP(引导处理器)检查

#ifdef CONFIG_SMP

如果 %bx 为零,则这是在引导处理器 (BSP) 上引导,因此跳过此步骤。否则,对于 AP(应用处理器)

如果期望的 %cr4 设置为非零,则启用分页选项(PSE、PAE 等),并跳过“初始化页表”(跳转到“启用分页”)。

#endif /* CONFIG_SMP */

4.4 初始化页表

从 pg0(第 0 页)开始,并将所有页初始化为 007(PRESENT + RW + USER)。

4.5 启用分页

将 %cr3(页表指针)设置为 swapper_pg_dir。

将 %cr0 的分页 ("PG") 位设置为
********** 启用分页 **********.

跳转 $ 以刷新预取队列。

跳转 *[$] 以确保 %eip 被重定位。

设置堆栈指针 (lss stack_start, %esp)。

#ifdef CONFIG_SMP

如果这不是 BSP(引导处理器),则清除所有标志位并跳转到 checkCPUtype。

#endif /* CONFIG_SMP */

4.6 清除 BSS

BSP 清除内核的所有 BSS(__bss_start 和 _end 之间的区域)。

4.7 32 位设置

为 32 位模式设置 IDT(调用 setup_idt)。setup_idt 设置一个 IDT,其中包含 256 个条目,指向默认中断处理程序 "ignore_int" 作为中断门。它实际上并没有加载 IDT;这只能在分页启用且内核移动到 PAGE_OFFSET 之后才能完成。中断在其他地方启用,那时我们可以相对确定一切正常。

清除 eflags 寄存器(在切换到保护模式之前)。

4.8 移出启动参数和命令行

_empty_zero_page 的前 2 KB 用于启动参数,第二个 2 KB 用于命令行。

4.9 checkCPUtype

将 X86_CPUID 初始化为 -1。

使用标志寄存器、push/pop 结果和 CPUID 指令来确定 CPU 类型和供应商:设置 X86、X86_CPUID、X86_MODEL、X86_MASK 和 X86_CAPABILITY。相应地设置 %cr0 中的位。

还检查是否存在 80287 或 80387 协处理器。如果找到数学协处理器或浮点单元,则设置 X86_HARD_MATH。

4.10 计数此处理器

对于 CONFIG_SMP 构建,递增 "ready" 计数器,以记录已初始化的 CPU 数量。

4.11 加载描述符表指针寄存器

使用 gdt_descr 加载 GDT,并使用 idt_descr 加载 IDT。GDT 包含 2 个内核条目(代码和数据各 4 GB,从 0 开始)和 2 个用户空间条目(代码和数据各 4 GB,从 0 开始)。在用户空间描述符和 APM 描述符之间有 2 个空描述符。

GDT 还包含 4 个 APM 段的条目。APM 段具有字节粒度,其基址和限制在运行时设置。

gdt_table 的其余部分(APM 段之后)是 TSS 和 LDT 的空间。

跳转到 __KERNEL_CS:%eip 以使 GDT 被使用。现在在
********** 保护模式 **********.

重新加载所有段寄存器:将 %ds、%es、%fs 和 %gs 寄存器设置为 __KERNEL_DS。

#ifdef CONFIG_SMP

仅使用 __KERNEL_DS 重新加载堆栈指针段 (%ss)。

#else /* not CONFIG_SMP */

使用 stack_start 重新加载堆栈指针 (%ss:%esp)。

#endif /* CONFIG_SMP */

将 LDT 指针清除为 0。

将处理器的方向标志 (DF) 清除为 0,以供 gcc 使用。

4.12 启动其他处理器

对于 CONFIG_SMP 构建,如果这不是第一个(引导)CPU,则调用 initialize_secondary(),它不会返回。辅助 (AP) 处理器被初始化,然后进入空闲状态,直到进程被调度到它们上面。

如果这是第一个或唯一的 CPU,则调用 start_kernel()。(见下文)

/* 上面的调用永远不应该返回,但以防万一:*/

L6: jmp L6


下一页 上一页 目录