CR3 值与 pgd_t 之间的差异

Difference between CR3 value and pgd_t

我正在玩,并尝试在安装了 Linux 的 x86_64 CPU 上手动执行页面 table。

我想尝试使用 Linux API 并通过手动查看页面 table 值来获得相同的值。

我在这里发现: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可以通过调用__sme_pa并传递来翻译成cr3中要使用的页全局目录的物理页地址给它pgd。请注意,返回值(表示 ASID)的最低有效 12 位为零。所以 ASID 必须与它进行“或”运算。

在Linux 4.14之前,可以使用__pa代替不支持的__sme_pa。请注意,__pa 等同于 Intel 处理器上的 __sme_pa,因为 SME 仅适用于 AMD 处理器。

至少从 Linux 2.6 开始,pgdcr3 可能等效也可能不等效,这取决于两个因素:

  • 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

在这种情况下,pgdcr3 是等价的。在 Linux 4.15:

cr3                 = 0x8980A005
pgd                 = 0xC980A000
__pa(pgd)           = 0x8980A000
__START_KERNEL_map  = 0x80000000
phys_base           = 0xEC000000