December 13, 2025

Address map and the MMU

This is a break from the focus on the GICv3 driver and interrupts.

My RK3399 board (the Orange Pi 4) has 4G of ram (in a single chip -- amazing). I know that 32 bit addresses cover exactly 4G, but we have IO addresses in the low 4G range. The TRM is clear. The DRAM area is 4G minus 128M.
So it goes from 0000_0000 to F7ff_ffff.

This is a 64 bit machine, but the TRM does not show any addresses outside of the range that can be addressed using 32 bits.

The MMU

I read and display the MMU registers:
TCR_el2 =   80803520
TTBR0_el2 = 00000000_F7FF0000
TTBR1_el2 -- gives a synch abort
Notice that TTBR1_el2 gives a synch abort. Only EL1 has both TTBR0 and TTBR1.

Note that TTBR0 points to page tables in the last 64K of memory. I find it surprising that we can fit our map into 64K, but we haven't actually examined it in detail yet, so there may be surprises.

Once again "Mike" gives a pleasant and easy to read discussion of the aarch64 MMU

I dump the area pointed to be TTBR0 and see:
F7FF0000:  00000711 00000000 40000711 00000000
F7FF0010:  80000711 00000000 F7FF1003 00000000
F7FF0020:  00000000 00000000 00000000 00000000
F7FF0030:  00000000 00000000 00000000 00000000
F7FF0040:  00000000 00000000 00000000 00000000
....
....
F7FF0FE0:  00000000 00000000 00000000 00000000
F7FF0FF0:  00000000 00000000 00000000 00000000
F7FF1000:  C0000711 00000000 C0200711 00000000
F7FF1010:  C0400711 00000000 C0600711 00000000
F7FF1020:  C0800711 00000000 C0A00711 00000000
F7FF1030:  C0C00711 00000000 C0E00711 00000000
F7FF1040:  C1000711 00000000 C1200711 00000000
F7FF1050:  C1400711 00000000 C1600711 00000000
F7FF1060:  C1800711 00000000 C1A00711 00000000
F7FF1070:  C1C00711 00000000 C1E00711 00000000
F7FF1080:  C2000711 00000000 C2200711 00000000
F7FF1090:  C2400711 00000000 C2600711 00000000
F7FF10A0:  C2800711 00000000 C2A00711 00000000
F7FF10B0:  C2C00711 00000000 C2E00711 00000000
F7FF10C0:  C3000711 00000000 C3200711 00000000
F7FF10D0:  C3400711 00000000 C3600711 00000000
F7FF10E0:  C3800711 00000000 C3A00711 00000000
....
....
F7FF1DD0:  F7400711 00000000 F7600711 00000000
F7FF1DE0:  F7800711 00000000 F7A00711 00000000
F7FF1DF0:  F7C00711 00000000 F7E00711 00000000
F7FF1E00:  F8000401 00600000 F8200401 00600000
F7FF1E10:  F8400401 00600000 F8600401 00600000
F7FF1E20:  F8800401 00600000 F8A00401 00600000
....
....
F7FF1FC0:  FF000401 00600000 FF200401 00600000
F7FF1FD0:  FF400401 00600000 FF600401 00600000
F7FF1FE0:  FF800401 00600000 FFA00401 00600000
F7FF1FF0:  FFC00401 00600000 FFE00401 00600000
F7FF2000:  00000711 00000000 40000711 00000000
F7FF2010:  80000711 00000000 F7FF3003 00000000
F7FF2020:  00000000 00000000 00000000 00000000
F7FF2030:  00000000 00000000 00000000 00000000
F7FF2040:  00000000 00000000 00000000 00000000
F7FF2050:  00000000 00000000 00000000 00000000
....
....
F7FF2FE0:  00000000 00000000 00000000 00000000
F7FF2FF0:  00000000 00000000 00000000 00000000
F7FF3000:  C0000711 00000000 C0200711 00000000
F7FF3010:  C0400711 00000000 C0600711 00000000
F7FF3020:  C0800711 00000000 C0A00711 00000000
F7FF3030:  C0C00711 00000000 C0E00711 00000000
F7FF3040:  C1000711 00000000 C1200711 00000000
F7FF3050:  C1400711 00000000 C1600711 00000000
F7FF3060:  C1800711 00000000 C1A00711 00000000
....
....
F7FF3DD0:  F7400711 00000000 F7600711 00000000
F7FF3DE0:  F7800711 00000000 F7A00711 00000000
F7FF3DF0:  F7C00711 00000000 F7E00711 00000000
F7FF3E00:  F8000401 00600000 F8200401 00600000
F7FF3E10:  F8400401 00600000 F8600401 00600000
F7FF3E20:  F8800401 00600000 F8A00401 00600000
F7FF3E30:  F8C00401 00600000 F8E00401 00600000
....
....
F7FF3FC0:  FF000401 00600000 FF200401 00600000
F7FF3FD0:  FF400401 00600000 FF600401 00600000
F7FF3FE0:  FF800401 00600000 FFA00401 00600000
F7FF3FF0:  FFC00401 00600000 FFE00401 00600000
F7FF4000:  FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF
F7FF4010:  FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF
F7FF4020:  FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF
F7FF4030:  FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF
....
....
F7FFFFC0:  FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF
F7FFFFD0:  FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF
F7FFFFE0:  FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF
F7FFFFF0:  FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF
How about that? It looks to me like all those FFFFFFFF at the end are simply ram than did not get used. If so, the MMU tables are only 1/4 of what I dumped, namely 16K bytes in size.

Notice that there are what look like two almost identical regions that start with C0000711.

Translation tables

Translation table entries are 64 bits (two of the 32 bit words dumped above). A table entry can be one of 3 things:

So the table is a tree, but it only can go 3 levels deep.

In the official docs an "entry" is used to describe a page, a "descriptor" points to another table.

Consider the 3 level table scheme. You start with L0 and there are L0, L1, and L2. L0 and L1 consist entirely of "descriptors" and never contain "entries". L2 consists entirely of "entries". So "they" say -- is this always true or only in specific cases. However this fact means that the same encoding used for descriptors in L0 and L1 can be used for entries in L2.

The TCR register

The TCR (translation control register) defines how the game is played. This is a 64 bit register (but in our case none of the upper 32 bits are set).
TCR_el2 = 00000000_80803520

Details can vary for each Cortex-Axx processor, so it is important to find the documentation for exactly the A53 core we are now working with.
For the A53, the entire upper 32 bits are reserved.

The low 6 bits are 0x20 = 16. This tells us the size of the address space mapped by the level 0 table. It maps 64-16 bits, i.e. 48 bits. I know you were hoping for 64 bits, but this reduces the size of the table, which is a good thing.

Bits 15:14 in the TCR give the granule size. I see 0b00 which is 4K (which I find surprising). The only other size available for the A53 is 64K (indicated by 0b10).

Bits 18:16 give the physical address size. I see 0b000 which is 32 bits (4G) -- sounds right. The other options are 36 bits (001) and 40 bits (010)

Address translation

Here is a nice 32 page document from ARM describing address translation on Aarch64:

Our L1 table

I read that this is 512 entries long (and each entry is 8 bytes), so the table is 4K in size or 0000 to 0FFF.
In our case we see:
F7FF0000:  00000711 00000000 40000711 00000000
F7FF0010:  80000711 00000000 F7FF1003 00000000
F7FF0020:  00000000 00000000 00000000 00000000
F7FF0030:  00000000 00000000 00000000 00000000
F7FF0040:  00000000 00000000 00000000 00000000
.....
F7FF0FFF:  00000000 00000000 00000000 00000000
All but the first 4 entries are 00000000 or "invalid".
Each table entry must map 1G (4 entries thus map 4G)
The entire table maps 512G. (This doesn't fit well with the idea of 48 bit addresses based on the low bits of TCR discussed above).
32 bits -- 4G
39 bits -- 512G
40 bits -- 1024G
48 bits -- 262144G

Have any comments? Questions? Drop me a line!

Tom's electronics pages / tom@mmto.org