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

查看:242
本文介绍了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;
}

但是,当程序检查pte的地址(0xffffffc0008b2000)时,它总是返回一个空的pmd.

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

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

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?

欢迎任何建议!谢谢.

西蒙(Simon)

推荐答案

我终于解决了这个问题.

I finally solved the problem.

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

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

根据 ARMv8页面表设计,对于4kb颗粒大小写,ARM使用4个级别的页表.每个级别(在链接中定义的0-3级)在Linux代码中均以pgd, pud, pmd, and 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, or 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天全站免登陆