Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Traversing all the physical pages of a process

In a kernel module (2.6.32-358.el6.x86_64) I'd like to print out all the physical addresses, which are mapped into a process' virtual memory. Given task->mm, I attempt to traverse the process' struct page's as follows:

int i, j, k, l;
for (i = 0; i < PTRS_PER_PGD; ++i)
{
    pgd_t *pgd = mm->pgd + i;
    if (pgd_none(*pgd) || pgd_bad(*pgd))
        continue;
    for (j = 0; j < PTRS_PER_PUD; ++j)
    {
        pud_t *pud = (pud_t *)pgd_page_vaddr(*pgd) + j;
        if (pud_none(*pud) || pud_bad(*pud))
            continue;
        for (k = 0; k < PTRS_PER_PMD; ++k)
        {
            pmd_t *pmd = (pmd_t *)pud_page_vaddr(*pud) + k;
            if (pmd_none(*pmd) || pmd_bad(*pmd))
                continue;

            for (l = 0; l < PTRS_PER_PTE; ++l)
            {
                pte_t *pte = (pte_t *)pmd_page_vaddr(*pmd) + l;
                if (!pte || pte_none(*pte))
                    continue;
                struct page *p = pte_page(*pte);
                unsigned long phys = page_to_phys(p);
                printk(KERN_NOTICE "addr %lx", phys);
            }
        }
    }
}

The output looks a bit strange (in particular, there are serieses of identical addresses), so I'd like to ask whether the above is correct, in theory.

like image 339
Igor R. Avatar asked Oct 15 '25 22:10

Igor R.


2 Answers

A better approach would be to traverse process' VMAs and translate each VMA to physical pages/addresses by means of the page directory:

struct vm_area_struct *vma = 0;
unsigned long vpage;
if (task->mm && task->mm->mmap)
    for (vma = task->mm->mmap; vma; vma = vma->vm_next)
        for (vpage = vma->vm_start; vpage < vma->vm_end; vpage += PAGE_SIZE)
            unsigned long phys = virt2phys(task->mm, vpage);
//...

Where virt2phys would look like this:

//...
pgd_t *pgd = pgd_offset(mm, virt);
if (pgd_none(*pgd) || pgd_bad(*pgd))
    return 0;
pud = pud_offset(pgd, virt);
if (pud_none(*pud) || pud_bad(*pud))
    return 0;
pmd = pmd_offset(pud, virt);
if (pmd_none(*pmd) || pmd_bad(*pmd))
    return 0;
if (!(pte = pte_offset_map(pmd, virt)))
    return 0;
if (!(page = pte_page(*pte)))
    return 0;
phys = page_to_phys(page);
pte_unmap(pte);
return phys;
like image 92
Igor R. Avatar answered Oct 19 '25 11:10

Igor R.


Since the code from the previous answer is pretty old and we now have 5-level paging on some CPUs here is an updated version:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/kvm_host.h>
#include <asm/virtext.h>
#include <asm/cpu.h>
#include <asm/tdx.h>
#include <kvm/iodev.h>
#include <linux/kvm_host.h>
#include <linux/kvm.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/percpu.h>
#include <linux/mm.h>
#include <linux/miscdevice.h>
#include <linux/vmalloc.h>
#include <linux/reboot.h>
#include <linux/debugfs.h>
#include <linux/highmem.h>
#include <linux/file.h>
#include <linux/syscore_ops.h>
#include <linux/cpu.h>
#include <linux/sched/signal.h>
#include <linux/sched/mm.h>
#include <linux/sched/stat.h>
#include <linux/cpumask.h>
#include <linux/smp.h>
#include <linux/anon_inodes.h>
#include <linux/profile.h>
#include <linux/kvm_para.h>
#include <linux/pagemap.h>
#include <linux/mman.h>
#include <linux/swap.h>
#include <linux/bitops.h>
#include <linux/spinlock.h>
#include <linux/compat.h>
#include <linux/srcu.h>
#include <linux/hugetlb.h>
#include <linux/slab.h>
#include <linux/sort.h>
#include <linux/bsearch.h>
#include <linux/io.h>
#include <linux/lockdep.h>
#include <linux/kthread.h>
#include <linux/suspend.h>
#include <linux/version.h>
#include <asm/processor.h>
#include <asm/ioctl.h>
#include <linux/uaccess.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/kprobes.h>


MODULE_LICENSE("GPL");

unsigned long virt2phys(struct mm_struct *mm, unsigned long virt){
    pgd_t *pgd;
    p4d_t *p4d;
    pud_t *pud;
    pmd_t *pmd;
    pte_t *pte;
    unsigned long phys;
    struct page *page;
    pgd = pgd_offset(mm, virt);
    if (pgd_none(*pgd) || pgd_bad(*pgd))
        return 0;
    p4d = p4d_offset(pgd, virt);
    if (p4d_none(*p4d) || p4d_bad(*p4d))
        return 0;
    pud = pud_offset(p4d, virt);
    if (pud_none(*pud) || pud_bad(*pud))
        return 0;
    pmd = pmd_offset(pud, virt);
    if (pmd_none(*pmd) || pmd_bad(*pmd))
        return 0;
    if (!(pte = pte_offset_map(pmd, virt)))
        return 0;
    if (!(page = pte_page(*pte)))
        return 0;
    phys = page_to_phys(page);
    pte_unmap(pte);
    return phys;
}

static int __init
lkm_example_init(void)
{
    int pid = 1;
    struct task_struct *tsk;
    struct mm_struct *mm;
    struct vm_area_struct *vma = 0;
    unsigned long vpage;
    unsigned long phys;

    tsk = pid_task(find_vpid(pid), PIDTYPE_PID);
    mm = tsk->mm;

    if (mm && mm->mmap){
        for (vma = mm->mmap; vma; vma = vma->vm_next){
            for (vpage = vma->vm_start; vpage < vma->vm_end; vpage += PAGE_SIZE){
                phys = virt2phys(mm, vpage);
                pr_info("phys 0x%lx",phys);
            }
        }
    }
    return -1;
}

static void __exit
lkm_example_exit(void)
{
    pr_info("Goodbye, World!\n");
    return;
}

module_init(lkm_example_init);
module_exit(lkm_example_exit);
like image 34
Benedict Schlüter Avatar answered Oct 19 '25 13:10

Benedict Schlüter



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!