我在阅读 head.s(Linux 内核文件之一)时遇到问题

I confront an issue when I read head.s (one of Linux kernel files)

head.s 中的代码片段如下所示:

movl $swapper_pg_dir-0xc0000000,%eax
movl %eax,%cr3 /* set the page table pointer.. */
movl %cr0,%eax
orl [=12=]x80000000,%eax
movl %eax,%cr0 /* ..and set paging (PG) bit */

内核在开启分页机制之前(当然PE标志现在已经开启),加载临时Page Directory地址Table到%cr3.

问题是:
我认为内核应该直接将 $swapper_pg_dir 值放入 %eax 而不是 $swapper_pg_dir-0xc0000000。我知道我错了,但为什么呢?

内核将内存视为基于 0xC0000000。任何内存分配,指针或全局,都位于 0xC0000000 到 0xFFFFFFFF 之间。 然而,对于 HW 控制器,例如 MMU 或任何协处理器,内存 window 可能基于 0x00000000.

因此,当将指向 table 的指针或描述符加载到 HW 引擎时,它必须基于 0x00000000

OP 问为什么 Linux 从 swapper_pg_dir 中减去 0xc0000000。这个数学的原因是 %cr3 需要物理地址,而 $swapper_pg_dir 是虚拟地址。在内核页面初始化的初始阶段,8MB RAM (0 to 0x007fffff) 有两个映射。第一个映射是虚拟地址 0x00000000-0x007fffffone to one,而另一个是 0xc0000000-0xcoo7fffff。为了得到swapper_pg_dir的物理地址,我们必须从swapper_pg_dir的地址中减去0xc0000000