walk up pagetables on ARM

Oscar Salvador osalvador.vilardaga at gmail.com
Thu Oct 6 10:09:21 EDT 2016


Hi all,

for learning purposes, I wanted to emulate "lookup_address" function for
ARM platforms, but I'm having some troubles which I don't know how to get
over them.

I'll explain it:

I start by retrieving the value of swapper_pg_dir from TTBR1 register
(during head.S executions the physical address of swapper_pg_dir is being
saved in this register).
So far this works, I can compare it with gdb:

  (gdb) p swapper_pg_dir

Cannot access memory at address 0xc0004000
(gdb) p init_mm.pgd
$5 = (pgd_t *) 0xc0004000
(gdb)


And from my code:

static pgd_t *get_global_pgd (void)
{
        pgd_t *pgd;
        unsigned int ttb_reg;

        asm volatile (
        "       mrc     p15, 0, %0, c2, c0, 1"
        : "=r" (ttb_reg));


        ttb_reg &= ~0x3fff;
        pgd = phys_to_virt (ttb_reg);
        return pgd;
}


and the output:

kernel: [18604.342604] mod: get_global_pgd: 0x0 - c0004000

So far, it's ok, but now the fun begins.

I'm walking the page table to get the PTE related to an X address:

static pte_t *lookup_address (unsigned long addr)
{
        pgd_t *pgd;
        pud_t *pud;
        pmd_t *pmd;

        pgd = get_global_pgd() + pgd_index (addr);
        pud = pud_offset (pgd, addr);
        pmd = pmd_offset (pud, addr);
        if (pmd == NULL || pmd_none (*pmd)) {
                pr_info ("%s: pmd == NULL\n", r2_devname);
                return NULL;
        }

        return pte_offset_kernel (pmd, addr);
}

After that, I'd to check if the PTE has the PRESENT flag. Since ARM
processor doesn't provide those flags Linux had to workaround that by
adding

these flags: (f.i:
http://lxr.free-electrons.com/source/arch/arm/include/asm/pgtable-2level.h#L123
)

I'm checking the flags with

pte_present

or

pte_write

But I think I'm getting the wrong PTE's because I've tried with some
address from /proc/kallsyms, and it's saying that the page is not
present, and I'm getting diferent values for different pages

in the same range.

For instance:


for: 0xc0009244:
bananapi kernel: [18604.419142] mod: pte_present: 0
bananapi kernel: [18604.428025] mod: pte_write: 0
bananapi kernel: [18604.436592] mod: pte_young: 2
bananapi kernel: [18604.445318] mod: pte_dirty: 0

and for 0xc0012904:


bananapi kernel: [18883.095749] r2k: pte_present: 0
bananapi kernel: [18883.104607] r2k: pte_write: 1
bananapi kernel: [18883.113159] r2k: pte_young: 0
bananapi kernel: [18883.121670] r2k: pte_dirty: 0

It's saying that is not present, but I can read from those addresses with gdb:


(gdb) x/2x 0xc0009244
0xc0009244 <vfp_flush_hwstate>:	0xe92d4010	0xe1a04000
(gdb) x/2x 0xc0012904
0xc0012904 <__readwrite_bug>:	0xe1a01000	0xe30505ec
(gdb)

What am I doing wrong?

Thanks in advance
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.kernelnewbies.org/pipermail/kernelnewbies/attachments/20161006/a024c702/attachment.html 


More information about the Kernelnewbies mailing list