This version has been changed a lot for my personal preferences. For the original version go to
Tool for automatically generating MMU and translation table setup code, whether to drag and drop into your own bare metal arm64 projects or to assist you in your own learning.
For more information see the original blog post.
- Python 3.8+
- chaimleib's IntervalTree
pip install intervaltree
The following command-line options are available:
-h, --help show this help message and exit
-i SRC input memory map file
-no_mmuon Do not generate mmu_on function
-o DST output GNU assembly file (default mmu_setup.S)
-ttbr1 Use TTBR1 instead of TTBR0 (default)
-ttb TTB desired translation table base address as symbol! (default
mmu_table)
-el {1,2,3} exception level (default: 1)
-tg {4K,16K,64K,4k,16k,64k}
translation granule (default: 4K)
-tsz {32,36,40,48} address space size (default: 40)
-l label extend labels with a custom identifier
-v verbose
-d enable debug output
The input memory map file is a simple comma-separated text file with format:
phyAddress,[virtAddress], size, type[:SHARED][:GLOBAL], rights[:SX][:UX][:NS], comment
Empty lines are allowed. Line comments may start with #
or //
.
Where:
phyAddress
is the physical address of a region;virtAddress
is the virtual address of a region, if emptyphyAddress
is used;size
is the size of the region in bytes, usingK
,M
, orG
to specify the unit;type
can beDEVICE
,NO_CACHE
,CACHE_WB?
orCACHE_WT
.:SHARED
sets the shared and:GLOBAL
the global flag;rights
are the access rights (AP[2..1]
). There are four combinations:SRW_UNA
,SRW_URW
,SRO_UNA
andSRO_URO
. By default a region is "XN" with:SX
or:UX
execution rights can be set. With:NS
the non-secure flag will be set.comment
This is a comment added in the output easy tracing.
The address parameter can be a valid python expression.
Memory types are:
DEVICE
: Device (no caching, guarded)CACHE_WB
: Writeback cacheCACHE_WT
: Writethru cacheNO_CACHE
: Not cached
The rights are:
SRW_UNA
: Supervisor Read/Write, User No AccessSRW_URW
: Supervisor Read/Write, User Read/WriteSRO_UNA
: Supervisor Read Only, User No AccessSRO_URO
: Supervisor Read Only, User Read Only
Note: A writable region is automatically SXN
.
Several memory map files are provided in the examples folder.
This must be the base address of a granule aligned buffer that is at least large enough to contain the number of translation tables allocated by the tool.
You can see this in the generated GNU assembly file:
/*
* ...
*
* This memory map requires a total of 7 translation tables.
* Each table occupies 4K of memory (0x1000 bytes).
* The buffer pointed to by "mmu_table" is therefore 7x 4K = 0x7000 bytes long.
*
* ...
*/
It is also your responsibility to ensure the memory region containing the buffer is described as NORMAL
in the input memory map file.
The tool only programs TTBR0_ELn
at the specified exception level. Where two virtual address spaces are available, such as at EL1, the higher virtual address space pointed to by TTBR1_ELn
is disabled.
If EL1 is chosen, the other TTBRx
is cleared.
By default TTBR0
is used. With the option -ttbr1
, one can use TTBR1
, but only in EL1.
The 4K
and 64K
granules have been tested on an i.MX8MQ. Unfortunately the 16K
granule is not supported by this chip so has not been tested.
Running the following command:
python3.8 generate.py -i examples/base-fvp-minimal.txt -no_mmuon
Where examples/base-fvp-minimal.txt
contains:
# Comment
// another comment
0x01C090000,, 4K, DEVICE, SRW_UNA, UART0
0x02C000000,, 8K, DEVICE, SRW_UNA, GICC
0x02E000000,, 64K, CACHE_WB, SRW_UNA, Non-Trusted SRAM
0x02F000000,, 64K, DEVICE, SRW_UNA, GICv3 GICD
0x02F000000+0x100000,, 1M, DEVICE, SRW_UNA, GICv3 GICR
0x080000000,, 1G, CACHE_WT, SRW_UNA, Non-Trusted DRAM
0x0C0000000,, 2M, CACHE_WB, SRO_UNA:SX, CODE
Generates the following mmu_setup.S
GNU assembly file:
/*
* This file was automatically generated using arm64-pgtable-tool.
* See: https://github.com/42Bastian Schick/arm64-pgtable-tool
* Forked from: https://github.com/ashwio/arm64-pgtable-tool
*
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* This code programs the following translation table structure:
*
* Index physical => virtual Type
* -----------------------------------------------------------------------------
* SNC P U P U S G
* shared ___| | r s r s e l
* no cache NC___| i e i e c o
* device DE___| n r v r u b
* write back WB___| r a
* write thru WT___| Exe AP e L
* -----------------------------------------------------------------------------
* level 0 table @ mmu_table + 0x0
* [ 0]-----------------------------\
* level 1 table @ mmu_table + 0x1000
* [ 0]-----------------------------\
* level 2 table @ mmu_table + 0x2000
* [ 224]-----------------------------\
* level 3 table @ mmu_table + 0x3000
* --- UART0 ---
* [ 144] 001c090000-001c090fff=>001c090000-001c090fff, DE XN XN WR UN S -
* [ 352]-----------------------------\
* level 3 table @ mmu_table + 0x4000
* --- GICC ---
* [ 0] 002c000000-002c000fff=>002c000000-002c000fff, DE XN XN WR UN S -
* [ 1] 002c001000-002c001fff=>002c001000-002c001fff, DE XN XN WR UN S -
* [ 368]-----------------------------\
* level 3 table @ mmu_table + 0x5000
* --- Non-Trusted SRAM ---
* [ 0] 002e000000-002e000fff=>002e000000-002e000fff, WB XN XN WR UN S -
* ...
* [ 15] 002e00f000-002e00ffff=>002e00f000-002e00ffff, WB XN XN WR UN S -
* [ 376]-----------------------------\
* level 3 table @ mmu_table + 0x6000
* --- GICv3 GICD ---
* [ 0] 002f000000-002f000fff=>002f000000-002f000fff, DE XN XN WR UN S -
* ...
* [ 15] 002f00f000-002f00ffff=>002f00f000-002f00ffff, DE XN XN WR UN S -
* --- GICv3 GICR ---
* [ 256] 002f100000-002f100fff=>002f100000-002f100fff, DE XN XN WR UN S -
* ...
* [ 511] 002f1ff000-002f1fffff=>002f1ff000-002f1fffff, DE XN XN WR UN S -
* --- Non-Trusted DRAM ---
* [ 2] 0080000000-00bfffffff=>0080000000-00bfffffff, WT XN XN WR UN S -
* [ 3]-----------------------------\
* level 2 table @ mmu_table + 0x7000
* --- CODE ---
* [ 0] 00c0000000-00c01fffff=>00c0000000-00c01fffff, WB X XN RO UN S -
*
* The following command line arguments were passed to arm64-pgtable-tool:
*
* -i examples/base-fvp-minimal.txt
* -ttb mmu_table
* -el 1
* -tg 4K
* -tsz 40
* -no_mmuon
*
*
* This memory map requires a total of 8 translation tables.
* Each table occupies 4K of memory (0x1000 bytes).
* The buffer pointed to by 'mmu_table' is therefore 8x4K = 0x8000 bytes long.
*
* The programmer must also ensure that the virtual memory region containing the
* translation tables is itself marked as NORMAL in the memory map file.
*/
/* some handy macros */
.macro FUNC64 name
.section .text.\name,"ax"
.type \name,%function
.globl \name
\name:
.endm
.macro ENDFUNC name
.align 3
.pool
.globl \name\()_end
\name\()_end:
.size \name,.-\name
.endm
.macro MOV64 reg,value
movz \reg,#\value & 0xffff
.if \value > 0xffff && ((\value>>16) & 0xffff) != 0
movk \reg,#(\value>>16) & 0xffff,lsl #16
.endif
.if \value > 0xffffffff && ((\value>>32) & 0xffff) != 0
movk \reg,#(\value>>32) & 0xffff,lsl #32
.endif
.if \value > 0xffffffffffff && ((\value>>48) & 0xffff) != 0
movk \reg,#(\value>>48) & 0xffff,lsl #48
.endif
.endm
/**
* Setup the page table.
* Not reentrant!
*/
FUNC64 pagetable_init //
adrp x20, mmu_table // base address
/* zero_out_tables */
mov x2,x20 //
MOV64 x3, 0x8000 // combined length of all tables
1:
stp xzr, xzr, [x2] // zero out 2 table entries at a time
subs x3, x3, #16 //
add x2, x2, #16 //
b.ne 1b //
/* program_table_0 */
MOV64 x21, 0x0 // base address of this table
add x21, x21, x20 // add global base
MOV64 x22, 0x8000000000 // chunk size
MOV64 x11, 0x1000 // next-level table address
add x11, x11, x20 // add base address
orr x11, x11, #0x3 // next-level table descriptor
str x11, [x21, #0*8] // write entry[0] into table
/* program_table_1 */
MOV64 x21, 0x1000 // base address of this table
add x21, x21, x20 // add global base
MOV64 x22, 0x40000000 // chunk size
MOV64 x11, 0x2000 // next-level table address
add x11, x11, x20 // add base address
orr x11, x11, #0x3 // next-level table descriptor
str x11, [x21, #0*8] // write entry[0] into table
/* Non-Trusted DRAM */
/* page:0x0 SH:0x0 AF:0x1 nG:0x1 attrindx:0x3 NS:0x0 xn:0x1 pxn:0x1 AP:0x0 */
MOV64 x9, 0x60000000000c0d //
MOV64 x10, 2 // index: 2
MOV64 x12, 0x80000000 // output address of entry[index]
orr x12, x12, x9 // merge output address with template
str x12, [x21, x10, lsl #3] // write entry into table
MOV64 x11, 0x7000 // next-level table address
add x11, x11, x20 // add base address
orr x11, x11, #0x3 // next-level table descriptor
str x11, [x21, #3*8] // write entry[3] into table
/* program_table_2 */
MOV64 x21, 0x2000 // base address of this table
add x21, x21, x20 // add global base
MOV64 x22, 0x200000 // chunk size
MOV64 x11, 0x3000 // next-level table address
add x11, x11, x20 // add base address
orr x11, x11, #0x3 // next-level table descriptor
str x11, [x21, #224*8] // write entry[224] into table
MOV64 x11, 0x4000 // next-level table address
add x11, x11, x20 // add base address
orr x11, x11, #0x3 // next-level table descriptor
str x11, [x21, #352*8] // write entry[352] into table
MOV64 x11, 0x5000 // next-level table address
add x11, x11, x20 // add base address
orr x11, x11, #0x3 // next-level table descriptor
str x11, [x21, #368*8] // write entry[368] into table
MOV64 x11, 0x6000 // next-level table address
add x11, x11, x20 // add base address
orr x11, x11, #0x3 // next-level table descriptor
str x11, [x21, #376*8] // write entry[376] into table
/* program_table_3 */
MOV64 x21, 0x3000 // base address of this table
add x21, x21, x20 // add global base
MOV64 x22, 0x1000 // chunk size
/* UART0 */
/* page:0x1 SH:0x0 AF:0x1 nG:0x1 attrindx:0x0 NS:0x0 xn:0x1 pxn:0x1 AP:0x0 */
MOV64 x9, 0x60000000000c03 //
MOV64 x10, 144 // index: 144
MOV64 x12, 0x1c090000 // output address of entry[index]
orr x12, x12, x9 // merge output address with template
str x12, [x21, x10, lsl #3] // write entry into table
/* program_table_4 */
MOV64 x21, 0x4000 // base address of this table
add x21, x21, x20 // add global base
MOV64 x22, 0x1000 // chunk size
/* GICC */
/* page:0x1 SH:0x0 AF:0x1 nG:0x1 attrindx:0x0 NS:0x0 xn:0x1 pxn:0x1 AP:0x0 */
MOV64 x9, 0x60000000000c03 //
MOV64 x10, 0 // index: 0
MOV64 x11, 2 // to 2 (2 entries)
MOV64 x12, 0x2c000000 // output address of entry[index]
1:
orr x12, x12, x9 // merge output address with template
str X12, [x21, x10, lsl #3] // write entry into table
add x10, x10, #1 // prepare for next entry
add x12, x12, x22 // add chunk to address
cmp x10, x11 // last index?
b.ne 1b //
/* program_table_5 */
MOV64 x21, 0x5000 // base address of this table
add x21, x21, x20 // add global base
MOV64 x22, 0x1000 // chunk size
/* Non-Trusted SRAM */
/* page:0x1 SH:0x0 AF:0x1 nG:0x1 attrindx:0x1 NS:0x0 xn:0x1 pxn:0x1 AP:0x0 */
MOV64 x9, 0x60000000000c07 //
MOV64 x10, 0 // index: 0
MOV64 x11, 16 // to 16 (16 entries)
MOV64 x12, 0x2e000000 // output address of entry[index]
1:
orr x12, x12, x9 // merge output address with template
str X12, [x21, x10, lsl #3] // write entry into table
add x10, x10, #1 // prepare for next entry
add x12, x12, x22 // add chunk to address
cmp x10, x11 // last index?
b.ne 1b //
/* program_table_6 */
MOV64 x21, 0x6000 // base address of this table
add x21, x21, x20 // add global base
MOV64 x22, 0x1000 // chunk size
/* GICv3 GICD */
/* page:0x1 SH:0x0 AF:0x1 nG:0x1 attrindx:0x0 NS:0x0 xn:0x1 pxn:0x1 AP:0x0 */
MOV64 x9, 0x60000000000c03 //
MOV64 x10, 0 // index: 0
MOV64 x11, 16 // to 16 (16 entries)
MOV64 x12, 0x2f000000 // output address of entry[index]
1:
orr x12, x12, x9 // merge output address with template
str X12, [x21, x10, lsl #3] // write entry into table
add x10, x10, #1 // prepare for next entry
add x12, x12, x22 // add chunk to address
cmp x10, x11 // last index?
b.ne 1b //
/* GICv3 GICR */
/* page:0x1 SH:0x0 AF:0x1 nG:0x1 attrindx:0x0 NS:0x0 xn:0x1 pxn:0x1 AP:0x0 */
MOV64 x9, 0x60000000000c03 //
MOV64 x10, 256 // index: 256
MOV64 x11, 512 // to 512 (256 entries)
MOV64 x12, 0x2f100000 // output address of entry[index]
1:
orr x12, x12, x9 // merge output address with template
str X12, [x21, x10, lsl #3] // write entry into table
add x10, x10, #1 // prepare for next entry
add x12, x12, x22 // add chunk to address
cmp x10, x11 // last index?
b.ne 1b //
/* program_table_7 */
MOV64 x21, 0x7000 // base address of this table
add x21, x21, x20 // add global base
MOV64 x22, 0x200000 // chunk size
/* CODE */
/* page:0x0 SH:0x0 AF:0x1 nG:0x1 attrindx:0x1 NS:0x0 xn:0x1 pxn:0x0 AP:0x2 */
MOV64 x9, 0x40000000000c85 //
MOV64 x10, 0 // index: 0
MOV64 x12, 0xc0000000 // output address of entry[index]
orr x12, x12, x9 // merge output address with template
str x12, [x21, x10, lsl #3] // write entry into table
ret // done!
ENDFUNC pagetable_init //
.section .noinit.mmu,"aw",@nobits
.globl mmu_table
.align 12
mmu_table: .space 0x8000