Issue
I am playing around, and trying to perform manually a page table walk on my x86_64 CPU, with Linux installed.
I want to try and get the same value by using the Linux API, and by manually looking at the page table values.
I found here: https://www.kernel.org/doc/gorman/html/understand/understand006.html that the value of CR3 should be equal to current->mm->pgd. But it is not:
current->mm->pgd = 0x457ec6067
cr3 = 0x45700a006
current->mm->pgd seems to stay constant across runs. What am I missing?
Thanks!
edit. This is my code:
__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);
Solution
Starting with Linux 4.14, pgd
can be translated to the physical page address of the page global directory to be used in cr3
by calling __sme_pa
and passing to it pgd
. Note that the least significant 12 bits of the returned value (which represent ASID) are zero. So the ASID has to be OR'ed with it.
Before Linux 4.14, __pa
can be used instead of __sme_pa
which was not supported. Note that __pa
is equivalent to __sme_pa
on Intel processors because SME is only available on AMD processors.
At least since Linux 2.6, pgd
and cr3
may or may not be equivalent depending on two factors:
- Whether
pgd
is larger than the virtual base address of the kernel image__START_KERNEL_map
. phys_base
, which is the difference between the compile-time physical base address of the kernel image and the run-time physical base address of the image. If the image has been relocated,phys_base
would not be zero.
The translation process is performed by a function called __phys_addr which you can refer to to follow the following examples.
I've tested this on two systems. On Linux 4.4.0, I got the following values:
cr3 = 0x3581E000
pgd = 0x3581E000
__pa(pgd) = 0x3581E000
__START_KERNEL_map = 0x80000000
phys_base = 0x00000000
In this case, pgd
and cr3
are equivalent. On Linux 4.15:
cr3 = 0x8980A005
pgd = 0xC980A000
__pa(pgd) = 0x8980A000
__START_KERNEL_map = 0x80000000
phys_base = 0xEC000000
Answered By - Hadi Brais