Singularity 代码阅读笔记[1]
版本:
2008-10-16 [004]: \base\Kernel\Singularity\Kernel.cs[145]。
2008-10-15 [003]: \base\boot\Singldr\undump.cpp[1671]。
2008-10-14 [002]: \base\boot\Singldr\singldr0.asm[971]。
2008-10-09 [001]: 初稿。
注:以下所有的分析建立在 x86 单 CPU 的假设下。
1. Singularity 的引导程序
1.1 16-bit Boot Loader
从 \base\boot\Singldr\singldr0.asm 开始:
\base\boot\Singldr\singldr0.asm[142] _TEXT segment public use16 'CODE'
一个16位的代码段
\base\boot\Singldr\singldr0.asm[155] BootPhase0 PROC NEAR
首先判断是通过 PXE 网络引导,还是从本地的 HD\CD 引导 Singularity。相对应的,通过 PXE 引导时 cs=5000,而通过 HD\CD 引导时 cs=0000。有关 PXE 的配置、初始化等等不管他,先往下看。
在 BootPhase0 的最后,会 call 2个过程:EnableA20 和 BootPhase1。
\base\boot\Singldr\singldr0.asm[562] EnableA20 PROC NEAR
这个过程用于开启 A20 地址线。大约有2种方法开启,最简单的就是通过 int 15h。某些情况下这种办法有可能失败,所以在后面用了一种更加原始的办法:通过操作 8042 模拟键盘响应来完成 A20 的开启。
\base\boot\Singldr\singldr.cpp[643] extern "C" int BootPhase1(PXE __far *pxe, PXENV __far * pxenv, uint32 diskid)
这是一个用 C 语言写的函数。很长很长,初始化了一堆乱七八糟的东西,然后还配置了 GDT、页表等等很多东西,还没仔细看,大致浏览了一下注视。Singularity 做的貌似非常先进的样子,还支持 PNP、1394 等等。
这个函数的最后,又回到汇编中去了。调用 BootPhase2。[1121]加载 dump 影响(s)
[1320]配置GDT
[1430]配置分页相关数据:PAE、PDE...
\base\boot\Singldr\singldr0.asm[433] BootPhase2 PROC NEAR
首先是关闭中断(cli)。加载 gdt,打开 cr0 中的 protect mode 标志。然后通过 push 一个 32bit 代码段的地址,用 retf 跳转过去。这里有一个很别扭的东西:在 retf 指令前,要硬编码一个 066H 进去才能跳转到 32bit 代码段。这个 066H 困扰了我很久,翻阅若干资料才知道他是干什么用的……
\base\boot\Singldr\singldr0.asm::Line912 _TEXT32 segment para public use32 'CODE'
到这里进入 32bit 代码段
\base\boot\Singldr\singldr0.asm[920] BootPhase3 PROC NEAR
[923]加载 32bit 段选择器,[933]设置初始堆栈。
[939]接下来一段是把 undump.asb 文件复制到内存中。在生成 singldr0.asm 之前,会首先把 undump.cpp 编译并连接成 umdump.exe,然后通过 mkasm.exe 工具,把他转换为 undump.asb 文件。singldr0.asm 中直接 include 了这个文件。
\base.obj\Boot\Prototype.x86\undump.asb 文件看上去像是把二进制文件转换为数组影像。[951]接下来准备开启 protect mode 的分页。源代码的注释上说 masm 不能直接对 cr4 寄存器操作,所以它采用硬编码的方式实现打开 cr4 中的分页标志。
[957]初始化页目录表(PDE, Page-Directory Entry)基址(cr3 高20位)。PDE 和 PTE 是在 BootPhase1 中设置的。
[961]开启分页(cr0 )。然后是一个跳转 965->967。
[971]加载TSS选择器,为了跳转到 ring3 作准备。
[976]修改 EFLAGS 寄存器 IOPL = 0x3。使 ring3 特权级可以进行 IO 操作。
[983]初始化堆栈。
[992]跳转至 undump_ent。undump_ent 就在上面[939]所加载的那个文件的入口点。
1.2 32-bit Boot Loader
\base\boot\Singldr\undump.cpp[1655] extern "C" void undump(Struct_Microsoft_Singularity_BootInfo *bi, int cpu)
到这里终于又来到 C 语言部分了,虽然看上去比汇编要清晰,但是 undump.cpp 这个文件几乎没有什么注释……
[1657]从 BootPhase3 跳入 undump(...) 时,cpu = 0。所以紧接着会调用 undump1()。
\base\boot\Singldr\undump.cpp[1671] static void __fastcall undump1(void)
首先初始化调试器。设置屏幕文字颜色,清理屏幕。然后打印一些自身信息。
[1682]检查 BootInfo 自身是否健全。
[1688]初始化IDT(Interrupt Descriptor Table)。
\base\boot\Singldr\undump.cpp[1488] static void IdtInit()
这个函数会用到2个结构:IDTP, IDTE。均来自 \base\Kernel\Singularity\X86\Idt.cs。
IDTP, IDTE 结构的详细定义参见《Intel 64 and IA-32 Architectures Software Developer's Manual》
初始化IDT之后,加载它。[1691]检查 Minidump。加载一些组件到。并且确定 g_Entry 和 g_Stack。
[1705]调用 undump2() 函数,同时会切换堆栈:mov esp, g_Stack。
\base\boot\Singldr\undump.cpp[1708] static void __fastcall undump2(void)
[1831]检查硬件。
[1839]跳入 HAL(minidump 的第一个线程)。
1.3 Hardware Adaptation Layer (HAL)
\base\Kernel\Native\hal.cpp[194] extern "C" void __cdecl Hal(Struct_Microsoft_Singularity_BootInfo *bi, int cpu)
跳入 HalBspEnter。
\base\Kernel\Native\hal.cpp[127] extern "C" static void __cdecl HalBspEnter(Struct_Microsoft_Singularity_BootInfo *bi)
[133]这里要说的是,hal.cpp 文件看上去像是一个 C++ 文件,事实上它也差不多就是一个 C++ 文件。_cinit() 这个函数的作用就是执行构造函数……嗯,在这里是使用 C 来模拟 C++。
[136]初始化一些和内核调试(Kernel Debugging)有关的东西。
[143]初始化中断向量,这里的中断向量应该也不是最终的。
[145]这个函数位于 \base\Kernel\Native\Processor.cpp[709]。初始化 CPU。
[146]不太明太这个函数的作用。注释中说的是:Check for a broken image produce when Bartok compiles through an assembler.
[156]跳转到内核主函数(Class_Microsoft_Singularity_Kernel::g_Main())。
1.4 The Singularity Kernel
\base\Kernel\Singularity\Kernel.cs[145] internal static int Main()
同学们,我们终于来到 C# 代码了!让我们拭目以待 Singularity 有什么奇特的地方吧。
to be continued...
浙公网安备 33010602011771号