目前我正在开发一些与研究相关的程序,我需要找到一些特定地址的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/

10-11 15:25