目前,我正在开发一些与研究相关的计划,我需要找到某些特定地址的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;
}
但是,当程序检查pte
地址(0xffffffc0008b2000)时,它总是返回空pmd
。
我的猜测是我在第一步中遇到了错误的pgd
。我看到Tims Notes说使用current->mm
只能获得pgd of TTBR0
(用户空间pgd
)而我检查的地址是内核空间地址所以我应该尝试获取pgd of TTBR1
。
所以我的问题是:如果我想获得内核空间地址的pte
,我可以使用current->mm
来获取pgd
吗?
如果我不能,还有什么我可以尝试的吗?
欢迎任何建议!谢谢。
西蒙
答案 0 :(得分:1)
我认为您遇到的问题是您正在传递当前进程的struct mm_struct *
指针。但是你从内核虚拟地址空间传递的地址。您需要将mm指针传递给init进程(&init_mm
):
pgd = pgd_offset(&init_mm, address);
我认为剩下的应该没问题,但我还没有测试过。您还可以在文件arch/arm64/mm/dump.c
答案 1 :(得分:1)
我终于解决了这个问题。
实际上,我的代码是正确的。我错过的唯一部分是页表条目检查。
根据page table design of ARMv8,ARM使用4级页面表来表示4kb颗粒情况。每个级别(链接中定义的级别0-3)在Linux代码中实现为pgd, pud, pmd, and ptep
。
在ARM体系结构中,每个级别可以是块条目或表条目(请参阅链接中的 AArch64描述符格式部分 )。
如果内存地址属于4kb表条目,则需要将其追踪到3级条目(ptep
)。但是,对于地址属于较大的块,相应的表条目可以保存在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;
}