3
votes

I have read multiple articles on this topic including below but things are still hazy to me: http://elinux.org/Tims_Notes_on_ARM_memory_allocation

ARM Linux kernel page table

Linux kernel ARM Translation table base (TTB0 and TTB1)

ARM hardware has 4096 entries of 4 byte each in L1 translation table. each entry translates a 1MB region in memory. At second level it has 256 entries of 4 bytes each. And each of second level entry translates a 4KB page in memory. So according to this any virtual address has to be divided into 12-8-12 to map to above scheme.

But on 32 bit ARM linux side this division is 11-9-12. Where L1 translation table consists of 2048 entries where each entry is 8 bytes. Here two 4 byte entries are clubbed together and the pointed second level translation tables are laid out one after the other in memory, so that at second level instead of 256 there are 512 entries. Additionally since Linux memory management expects various flags non native to ARM we define 512 more entries for linux page table(one for each 2nd level HW page table).

Now the question is Linux does not enforce PGD/PMD/PTE size (however it enforces page size to be 4K. Thus PAGE_SHIFT is set to 12), then why do we select 11-9-12 layout(i.e. 11 bits for PGD and 9 bits for HW PTE). Is it just to make sure that 512HW +512Linux PTE are aligned to a Page boundary ?

If someone could explain the logic behind this division in detail would be great....

2

2 Answers

3
votes

As you say, in the ARM short-descriptor format each second-level page table is 1KB in size. Even with the associated shadow page table that only makes 2KB, meaning 50% of every page allocated for second-level tables would be entirely wasted.

Linux just pretends that the section size is 2MB, rather than the actual 1MB of the hardware, by allocating first-level entries in pairs, so that the corresponding pair of second-level tables can be kept together in a single page, avoid that wastage, and keep the management of page table memory really simple.

2
votes

The ARM Linux and dirty bits should have all the answers. Mainly, the PTE tables have extra info to emulate bits resulting in the layout you observe.

I think a misconception is the memory an L2 table occupies versus what it maps. You must allocate physical memory for an L2 table and having it symmetric (4K size) make it the same as all pages. Now this 4k page could be four ARM MMU L2 page tables. However, we need some additional information to emulate dirty, young and accessed bits that the Linux generic MMU code requires. So the layout of the Linux L2 (PTE directory) is,

  1. Linux PTE [n]
  2. Linux PTE [n+1]
  3. ARM PTE [n]
  4. ARM PTE [n+1]

At the L1 level each entry is paired (n/n+1) so that it points to item 3 and 4 above. The pgtable-2level.h file has detailed comments on the layout (which should be correct for your version of Linux).

See: Tim's notes on ARM MM
         Page table entry (PTE) descriptor in Linux kernel for ARM