Viewing page tables and some other questions regarding the MMU

Leo Barnes barnes.leo at
Tue Apr 6 12:59:01 EDT 2010

On Thu, Apr 1, 2010 at 9:45 AM, Christoffer Dall <cd2436 at> wrote:
> On Thu, Apr 1, 2010 at 9:03 AM, Leo Barnes <barnes.leo at> wrote:
>>> The MMU does not have to be disabled. To manage the page tables, the
>>> tables themselves must be mapped at some virtual address. In ARM
>>> Linux, this is already done, and you can access the page table entry
>>> for addr by doing something like this:
>>>        pgd_t *pgd;
>>>        pud_t *pud;
>>>        pmd_t *pmd;
>>>        pte_t *pte;
>>>        pgd = pgd_offset(mm, addr);
>>>        pud = pud_alloc(mm, pgd, addr);
>>>        if (!pud) {
>>>                printk(KERN_ERR "Could not alloc pud!\n");
>>>                return NULL;
>>>        }
>>>        pmd = pmd_alloc(mm, pud, addr);
>>>        if (!pmd) {
>>>                printk(KERN_ERR "Could not alloc pmd!\n");
>>>                return NULL;
>>>        }
>>>        pte = pte_alloc_map(mm, pmd, addr);
>>>        if (!pte) {
>>>                printk(KERN_ERR "Could not alloc pte!\n");
>>>                return NULL;
>>>        }
>> Thanks, this looks useful. Which headers does this code rely on? I
>> assume linux/mm.h, but anything else?
> Try grepping for definitions or use cscope or ctags and you'll quickly
> find out which files you need when you try to compile.
>> Is the mm argument for the
>> functions used a global variable or something I have to create (and if
>> so, how to create it? the mm_struct is pretty damn large and filled
>> with fields that I know very little about)?
> For the current process you can do current->mm. There's a pointer from
> each task_struct to that thread's mm_struct.
> I recommend you look at a book like Understanding the Linux Kernel or
> similar for these general kernel coding questions and I don't think
> they're relevant for an ARM-linux mailing list.

Thanks for the suggestion. Now I have read up on kernel memory
management and have some follow-up questions.

What I am trying to do is basically to understand how ioremap_cached
works on ARM Linux. From what I understand, the MMU controls cacheing,
which means that in order to enable cacheing on a memory region, the
memory mapping has to be changed. Here is what I have tried:

I am using a Nokia N810 as a test rig. It contains an ARM1136 and a
OneNAND chip for storage. The OneNAND chip contains a bufferRAM that
can be programmed via the OneNAND control registers and then accessed
as normal RAM.

Physical address of OneNAND bufferRAM: 0x4000400 - 0x4001400
Virtual address of OneNAND bufferRAM as used by the OneNAND drivers: 0xC8880000
Virtual address returned by ioremap_cached(0x4000400, 0x1000): 0xC88A4400

The cache is definitely enabled for the remapped virtual address when
measuring byte reads, but when I manually try to see what has happened
to the page tables I get

	pgd_t *pgd = pgd_offset(current->mm, addr);
	pud_t *pud = (pud_t *)pud_offset(pgd, addr);
	pmd_t *pmd = pmd_offset(pud, addr);
	pte_t *pte = pte_offset_map(pmd, addr);

**pgd: 0x80464011 (= coarse page table)
*pte: 0x040000A3 (= extended small page table, TEX bits = b010
(device), C bit = 0, B bit = 0)

for both the original virtual address and the remapped address. I
would have expected *pte to be set to uncached device memory for the
original virtual address, but how can it be set to uncached device
memory for the ioremapped virtual address when the cache is most
definitely enabled?

(As a side note, the reason I am investigating this is because I read
somewhere in an ARM reference manual that having multiple maps to the
same physical region with differing memory attributes is illegal, but
ioremap_cached obviously manages it somehow.)

Hope someone can shed some light on this.
Best regards,

More information about the linux-arm mailing list