我正在玩,并试图在安装了Linux的x86_64 CPU上手动执行页表行走。
我想通过使用Linux API并手动查看页表值来尝试获得相同的值。
我在这里找到:https://www.kernel.org/doc/gorman/html/understand/understand006.html,CR3的值应该等于current-> mm-> pgd。但它不是:
current->mm->pgd = 0x457ec6067
cr3 = 0x45700a006
current-> mm-> pgd似乎在运行中保持不变。我错过了什么?
谢谢!
编辑。这是我的代码:
__asm__ __volatile__ (
"mov %%cr3, %%rax\n\t"
"mov %%rax, %0\n\t"
: "=m" (cr3)
:
: "%rax"
);
pr_err("cr3 = 0x%lx ", (long)cr3);
pr_err("\tcurrent->mm->pgd = 0x%lx\n", current->mm->pgd->pgd);
从Linux 4.14开始,pgd
可以通过调用cr3
并传递给它__sme_pa
,转换为pgd
中使用的页面全局目录的物理页面地址。请注意,返回值的最低有效12位(表示ASID)为零。所以ASID必须与它进行“或”运算。
在Linux 4.14之前,可以使用__pa
代替不支持的__sme_pa
。请注意,__pa
相当于英特尔处理器上的__sme_pa
,因为SME仅适用于AMD处理器。
至少从Linux 2.6开始,pgd
和cr3
可能会或可能不会取决于两个因素:
pgd
是否大于内核映像__START_KERNEL_map
的虚拟基址。phys_base
,它是内核映像的编译时物理基地址与映像的运行时物理基地址之间的差异。如果图像已经重新定位,phys_base
将不会为零。翻译过程由名为__phys_addr的函数执行,您可以参考以下示例。
我在两个系统上测试了这个。在Linux 4.4.0上,我得到以下值:
cr3 = 0x3581E000
pgd = 0x3581E000
__pa(pgd) = 0x3581E000
__START_KERNEL_map = 0x80000000
phys_base = 0x00000000
在这种情况下,pgd
和cr3
是等价的。在Linux 4.15上:
cr3 = 0x8980A005
pgd = 0xC980A000
__pa(pgd) = 0x8980A000
__START_KERNEL_map = 0x80000000
phys_base = 0xEC000000