Arm64 Linux 页表遍历 [英] Arm64 Linux Page Table Walk

查看:33
本文介绍了Arm64 Linux 页表遍历的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

目前我正在开发一些与研究相关的程序,我需要找到一些特定地址的pte.我的开发环境是 Juno r1 板(CPU 是 A53 和 A57),它运行 arm64 Linux 内核.

Currently I'm developing some research-related programs and I need to find the pte of some specific addresses. My development environment is Juno r1 board (CPUs are A53 and A57 ) and it's running arm64 Linux kernel.

我使用了一些典型的页表遍历代码,如下所示:

I use some typical page table walk codes like this:

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.

However, when the program checks the pte for the address(0xffffffc0008b2000), it always returns an empty pmd.

我的猜测是我在第一步中弄错了pgd.我看到 Tims Notes 说使用 current->mm 只能得到 TTBR0的pgd(用户空间pgd),而我检查的地址是内核空间地址,所以我应该尝试获取TTBR1的pgd.

My guess is that I got the wrong pgd in the first step. I saw Tims Notes said that using current->mm only could get the pgd of TTBR0 (user space pgd) while the address I checked is a kernel space address so I should try to get the pgd of TTBR1.

所以我的问题是:如果我想获取内核空间地址的pte,我可以使用current->mm 来获取pgd?

So my question is: If I want to get the pte of a kernel space address, can I use current->mm to get the pgd?

如果我不能,还有什么我可以尝试的吗?

If I can't, is there anything else I could try instead?

欢迎任何建议!谢谢.

西蒙

推荐答案

我终于解决了这个问题.

I finally solved the problem.

实际上,我的代码是正确的.我唯一错过的部分是页表条目检查.

Actually, my code is correct. The only part I missed is a page table entry check.

根据ARMv8的页表设计,ARM 对 4kb 颗粒情况使用 4 级页表.每个级别(链接中定义的级别 0-3)在 Linux 代码中实现为 pgd、pud、pmd 和 ptep.

According to the page table design of ARMv8, ARM uses 4 levels page table for 4kb granule case. Each level (level 0-3 defined in the link) is implemented as pgd, pud, pmd, and ptep in Linux code.

在 ARM 体系结构中,每个级别可以是块条目或表条目(请参阅链接中的 AArch64 描述符格式部分).

In the ARM architecture, each level can be either block entry or the table entry (see the AArch64 Descriptor Format Section in the link).

如果内存地址属于一个4kb的表项,则需要向下追踪到3级项(ptep).但是,对于属于较大块的地址,对应的表项可能会保存在pgd、pud或pmd级别.

If the memory address belongs to a 4kb table entry, then it needs to be traced down till level 3 entry (ptep). However, for the address belongs to a larger chunk, the corresponding table entry may save in the pgd, pud, or pmd level.

通过检查每个级别中条目的最后 2 位,您就知道它是否是块条目,并且您只需要继续跟踪块条目.

By checking the last 2 bits of the entry in each level, you know it's block entry or not and you only keep tracing down for the block entry.

这里是如何改进我上面的代码:

Here is how to improve my code above:

根据页表指针检索描述符desc = *pgd,然后检查描述符的最后2位.

Retrieving the descriptor based on the page table pointer desc = *pgd and then checking the last 2 bits of the descriptor.

如果描述符是块条目 (0x01),那么您需要提取较低级别的条目,如我上面的代码所示.如果您已经在任何级别获得表条目 (0x11),那么您可以停在那里并根据您刚刚获得的描述符 desc 将 VA 转换为 PA.

If the descriptor is a block entry (0x01) then you need to extract the lower level entry as my code shows above. If you already get the table entry (0x11) at any level, then you can stop there and translate the VA to PA based on the descriptor desc you just get.

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;
}

这篇关于Arm64 Linux 页表遍历的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆