目前我正在开发一些与研究相关的程序,我需要找到一些特定地址的pte
。我的开发环境是 Juno r1 板(CPU 是 A53 和 A57),它运行 arm64 Linux 内核。
我使用一些典型的页表遍历代码,如下所示:
int find_physical_pte(void *addr)
{
pgd_t *pgd;
pud_t *pud;
pmd_t *pmd;
pte_t *ptep;
unsigned long long address;
address = (unsigned long long)addr;
pgd = pgd_offset(current->mm, address);
printk(KERN_INFO "\npgd is: %p\n", (void *)pgd);
printk(KERN_INFO "pgd value: %llx\n", *pgd);
if (pgd_none(*pgd) || pgd_bad(*pgd))
return -1;
pud = pud_offset(pgd, address);
printk(KERN_INFO "\npud is: %p\n", (void *)pud);
printk(KERN_INFO "pud value: %llx\n", (*pud).pgd);
if (pud_none(*pud) || pud_bad(*pud))
return -2;
pmd = pmd_offset(pud, address);
printk(KERN_INFO "\npmd is: %p\n", (void *)pmd);
printk(KERN_INFO "pmd value: %llx\n",*pmd);
if (pmd_none(*pmd) || pmd_bad(*pmd))
return -3;
ptep = pte_offset_kernel(pmd, address);
printk(KERN_INFO "\npte is: %p\n", (void *)ptep);
printk(KERN_INFO "pte value: %llx\n",*ptep);
if (!ptep)
return -4;
return 1;
}
然而,当程序检查地址(0xffffffc0008b2000)的
pte
时,它总是返回一个空的 pmd
。我的猜测是我在第一步中得到了错误的
pgd
。我看到 Tims Notes 说使用 current->mm
只能获取 pgd of TTBR0
(用户空间 pgd
),而我检查的地址是内核空间地址,所以我应该尝试获取 pgd of TTBR1
。所以我的问题是:如果我想获取内核空间地址的
pte
,我可以使用 current->mm
来获取 pgd
吗?如果我不能,还有什么我可以尝试的吗?
欢迎任何建议!谢谢你。
西蒙
最佳答案
我终于解决了这个问题。
实际上,我的代码是正确的。我错过的唯一部分是页表条目检查。
根据 page table design of ARMv8 ,ARM 对 4kb 颗粒情况使用 4 级页表。每个级别(链接中定义的级别 0-3)在 Linux 代码中实现为 pgd, pud, pmd, and ptep
。
在 ARM 体系结构中,每个级别可以是块条目或表条目(请参阅链接中的 AArch64 描述符格式部分 )。
如果内存地址属于一个 4kb 的表条目,那么它需要被追踪到第 3 级条目( ptep
)。但是,对于属于较大chunk的地址,对应的表项可以保存在pgd, pud, or pmd
级别。
通过检查每个级别中条目的最后 2 位,您知道它是否是块条目,并且您只继续跟踪块条目。
以下是如何改进我上面的代码:
根据页表指针 desc = *pgd
检索描述符,然后检查描述符的最后 2 位。
如果描述符是块条目 (0x01),那么您需要提取较低级别的条目,如我上面的代码所示。
如果您已经在任何级别获得表条目 (0x11),那么您可以停在那里并根据您刚刚获得的描述符 desc
将 VA 转换为 PA。
int find_physical_pte(void *addr)
{
pgd_t *pgd;
pud_t *pud;
pmd_t *pmd;
pte_t *ptep;
unsigned long long address;
address = (unsigned long long)addr;
pgd = pgd_offset(current->mm, address);
printk(KERN_INFO "\npgd is: %p\n", (void *)pgd);
printk(KERN_INFO "pgd value: %llx\n", *pgd);
if (pgd_none(*pgd) || pgd_bad(*pgd))
return -1;
//check if (*pgd) is a table entry. Exit here if you get the table entry.
pud = pud_offset(pgd, address);
printk(KERN_INFO "\npud is: %p\n", (void *)pud);
printk(KERN_INFO "pud value: %llx\n", (*pud).pgd);
if (pud_none(*pud) || pud_bad(*pud))
return -2;
//check if (*pud) is a table entry. Exit here if you get the table entry.
pmd = pmd_offset(pud, address);
printk(KERN_INFO "\npmd is: %p\n", (void *)pmd);
printk(KERN_INFO "pmd value: %llx\n",*pmd);
if (pmd_none(*pmd) || pmd_bad(*pmd))
return -3;
//check if (*pmd) is a table entry. Exit here if you get the table entry.
ptep = pte_offset_kernel(pmd, address);
printk(KERN_INFO "\npte is: %p\n", (void *)ptep);
printk(KERN_INFO "pte value: %llx\n",*ptep);
if (!ptep)
return -4;
return 1;
}
关于linux - Arm64 Linux 页表遍历,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/42963312/