Walking Page Tables of a Process in Linux

Walking page tables of a process in Linux

pte_unmap(ptep); 

is missing just before the label out. Try to change the code in this way:

    ...
page = pte_page(pte);
if (page)
printk(KERN_INFO "page frame struct is @ %p", page);

pte_unmap(ptep);

out:

Page Table Walk in Linux

I fixed the problem thanks to TonyTannous. The page table entry doesn't contain just the physical address, but also some other bits used for access rights and the like. The physical address can be obtained by masking it with PTE_PFN_MASK:

    addr = ptep->pte & PTE_PFN_MASK

I can then dereference it with __va.

Arm64 Linux Page Table Walk

I finally solved the problem.

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

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.

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

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.

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:

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

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

Can I walk through my process' page table in userspace?

You'll probably find the information you want in the proc filesystem, under /proc/self. In particular /proc/self/maps contains the list of memory mappings in the process, with an indication of their permission and file when applicable. There is a little more information in /proc/self/smaps. You'll have to look in the kernel documentation for the format, specifically Documentation/filesystems/proc.txt.

page table walk in armv7 linux by S/W leads to which version of page table ARM PTE or Linux PTE

I Got the answer. I understood that, all these macro will leads to the Linux PTE only , since the L2 level page table of 4kb size and from 0th offset the linux pte0 starts and 1024th offset linux pte1 starts. while calculating pte_index it mask the lower 12 bits of PMD value thus PMD will always point to the start of page and there are Linux PTE stored.



Related Topics



Leave a reply



Submit