├── .gitignore ├── README.md ├── source ├── main.c ├── entry2.S ├── entry3.S ├── pl011.S ├── vectors.S └── fvp.S ├── include ├── platform.h ├── pl011.h └── asm.h ├── linker.ld ├── LICENSE └── Makefile /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | out.elf 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # arm64-mmu 详细理解教程 2 | 3 | 目前通过文档查阅MMU相关资料, 发现有很多优秀的文章, 但是对于如何完全的理解MMU, 还是非常抽象. 4 | 5 | 纸上得来终觉浅, 觉知此事要躬行. 这个示例程序可以详细演示MMU的转换和配置过程, 最终通过手动计算理解MMU的用意. 6 | 7 | # Thanks 8 | 9 | https://github.com/ashwio/arm64-hypervisor-tutorial -------------------------------------------------------------------------------- /source/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Ash Wilding. All rights reserved. 3 | * 4 | * SPDX-License-Identifier: MIT 5 | * 6 | * 7 | * EL2 hypervisor main(). 8 | */ 9 | 10 | #include "pl011.h" 11 | 12 | void main( void ) { 13 | pl011_init(); 14 | pl011_puts("Hello, world!\0"); 15 | } 16 | -------------------------------------------------------------------------------- /include/platform.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Ash Wilding. All rights reserved. 3 | * 4 | * SPDX-License-Identifier: MIT 5 | * 6 | * 7 | * Platform-specific definitions for Base Platform FVP. 8 | */ 9 | 10 | #ifndef H_PLATFORM_DEFINED 11 | #define H_PLATFORM_DEFINED 12 | 13 | #define PLATFORM_PL011_BASE 0x1C090000 14 | 15 | /* H_PLATFORM_DEFINED */ 16 | #endif 17 | -------------------------------------------------------------------------------- /source/entry2.S: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Ash Wilding. All rights reserved. 3 | * 4 | * SPDX-License-Identifier: MIT 5 | * 6 | * 7 | * EL2 hypervisor entry point. 8 | */ 9 | 10 | #include "asm.h" 11 | 12 | globalfunc entry2 13 | ADRP x0, dummy_vectors 14 | MSR VBAR_EL2, x0 15 | ADRP x0, _stack_start 16 | MOV sp, x0 17 | BL mmu_on 18 | BL main 19 | B . 20 | endfunc entry2 21 | -------------------------------------------------------------------------------- /include/pl011.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Ash Wilding. All rights reserved. 3 | * 4 | * SPDX-License-Identifier: MIT 5 | * 6 | * 7 | * Simple PL011 UART driver using polled I/O with no error handling. 8 | * For documentation, see ARM DDI 0183. 9 | */ 10 | 11 | #ifndef H_PL011_DEFINED 12 | #define H_PL011_DEFINED 13 | 14 | 15 | #include "platform.h" 16 | 17 | #ifndef __ASSEMBLER__ 18 | void pl011_init( void ); 19 | 20 | void pl011_putc( char c ); 21 | 22 | void pl011_puts( const char * s ); 23 | 24 | char pl011_getc( void ); 25 | #endif 26 | 27 | 28 | /* H_PL011_DEFINED */ 29 | #endif 30 | -------------------------------------------------------------------------------- /linker.ld: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Ash Wilding. All rights reserved. 3 | * 4 | * SPDX-License-Identifier: MIT 5 | */ 6 | 7 | 8 | ENTRY(entry3) 9 | 10 | SECTIONS 11 | { 12 | . = 0x80000000; 13 | _stack_end = .; 14 | 15 | .stack (NOLOAD): { 16 | . = . + 16K; 17 | } 18 | 19 | _stack_start = .; 20 | . = ALIGN(4K); 21 | _rw_start = .; 22 | 23 | .data : { 24 | *(.data); 25 | } 26 | 27 | .bss : { 28 | *(.bss); 29 | } 30 | 31 | . = ALIGN(4K); 32 | _rw_end = .; 33 | _exec_start = .; 34 | 35 | .text : { 36 | out/entry3.o(.text); 37 | out/entry2.o(.text); 38 | *(.text); 39 | } 40 | 41 | . = ALIGN(4K); 42 | _exec_end = .; 43 | 44 | . = ALIGN(2M); 45 | 46 | _heap_start = .; 47 | .heap (NOLOAD): { 48 | . = . + 2M; 49 | } 50 | _heap_end = .; 51 | 52 | _vm1_start = .; 53 | .vm1 (NOLOAD): { 54 | . = . + 2M; 55 | } 56 | _vm1_end = .; 57 | 58 | _vm2_start = .; 59 | .vm2 (NOLOAD): { 60 | . = . + 2M; 61 | } 62 | _vm2_end = .; 63 | } 64 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Ash Wilding 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /include/asm.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Ash Wilding. All rights reserved. 3 | * 4 | * SPDX-License-Identifier: MIT 5 | * 6 | * 7 | * Useful macros for use in assembly source files. 8 | */ 9 | 10 | #ifndef H_ASM_DEFINED 11 | #define H_ASM_DEFINED 12 | 13 | 14 | /* Begin function declaration with static scope. */ 15 | .macro staticfunc name 16 | .section .text.\name, "ax" 17 | .type \name, @function 18 | .func \name 19 | \name: 20 | .endm 21 | 22 | 23 | /* Begin function declaration with global scope. */ 24 | .macro globalfunc name 25 | .global \name 26 | staticfunc \name 27 | .endm 28 | 29 | 30 | /* End function declaration. */ 31 | .macro endfunc name 32 | .endfunc 33 | .size \name, .-\name 34 | .endm 35 | 36 | 37 | /* Stack 1-2 registers. */ 38 | .macro push reg1:req, reg2:vararg 39 | .ifnb \reg2 40 | STP \reg1, \reg2, [sp, #-16]! 41 | .else 42 | STP \reg1, xzr, [sp, #-16]! 43 | .endif 44 | .endm 45 | 46 | 47 | /* Pop 1-2 registers. */ 48 | .macro pop reg1:req, reg2:vararg 49 | .ifnb \reg2 50 | LDP \reg1, \reg2, [sp], #16 51 | .else 52 | LDP \reg1, xzr, [sp], #16 53 | .endif 54 | .endm 55 | 56 | 57 | /* H_ASM_DEFINED */ 58 | #endif 59 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2019 Ash Wilding. All rights reserved. 3 | # 4 | # SPDX-License-Identifier: MIT 5 | # 6 | 7 | 8 | # 9 | # OS specific tools 10 | # fRM removes a file, dRM removes a directory, dMK makes a directory 11 | # 12 | ifeq ($(OS), Windows_NT) 13 | fRM := cmd.exe /c del /f /q 14 | dRM := cmd.exe /c rd /s /q 15 | dMK := cmd.exe /c mkdir 16 | else 17 | fRM := rm -f 18 | dRM := rm -rf 19 | dMK := mkdir -p 20 | endif 21 | 22 | 23 | # 24 | # Directories and output file 25 | # 26 | dSRC := source 27 | dINC := include 28 | dOUT := out 29 | fOUT := out.elf 30 | 31 | 32 | # 33 | # Compiler, assembler, linker 34 | # ${CROSS_COMPILE} needs be /path/to/gcc/bin/aarch64-elf- 35 | # Where "/path/to/gcc/" is where you installed aarch64-elf-gcc 36 | # 37 | CC := ${CROSS_COMPILE}gcc 38 | AS := ${CROSS_COMPILE}gcc 39 | LD := ${CROSS_COMPILE}ld 40 | OBJDUMP := ${CROSS_COMPILE}objdump 41 | 42 | # 43 | # Compiler, assembler, and linker flags 44 | # 45 | CC_FLAGS := -I$(dINC) -g -O1 46 | AS_FLAGS := -I$(dINC) -g -O1 47 | LD_FLAGS := -T linker.ld 48 | 49 | 50 | # 51 | # Generate list of objects to build 52 | # 53 | C_SOURCES := $(wildcard ${dSRC}/*.c) 54 | ASM_SOURCES := $(wildcard ${dSRC}/*.S) 55 | C_OBJECTS := $(C_SOURCES:${dSRC}/%.c=${dOUT}/%.o) 56 | ASM_OBJECTS := $(ASM_SOURCES:${dSRC}/%.S=${dOUT}/%.o) 57 | OBJECTS := $(C_OBJECTS) $(ASM_OBJECTS) 58 | 59 | 60 | # 61 | # Rules to build the project 62 | # 63 | .PHONY: all clean rebuild 64 | 65 | all: ${dOUT} ${fOUT} 66 | 67 | ${dOUT}: 68 | @${dMK} $@ 69 | 70 | clean: 71 | @${dRM} ${dOUT} 72 | @${fRM} ${fOUT} 73 | 74 | rebuild: clean all 75 | 76 | ${dOUT}/%.o: ${dSRC}/%.c 77 | $(CC) $(CC_FLAGS) -c $< -o $@ 78 | 79 | ${dOUT}/%.o: ${dSRC}/%.S 80 | $(AS) $(AS_FLAGS) -c $< -o $@ 81 | 82 | ${fOUT}: ${OBJECTS} 83 | ${LD} $^ -o $@ ${LD_FLAGS} 84 | ${OBJDUMP} -D -m aarch64 out.elf > mmu.dis 85 | -------------------------------------------------------------------------------- /source/entry3.S: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Ash Wilding. All rights reserved. 3 | * 4 | * SPDX-License-Identifier: MIT 5 | * 6 | * 7 | * EL3 firmware entry point. 8 | * We perform minimal setup before dropping to the hypervisor at EL2. 9 | */ 10 | 11 | #include "asm.h" 12 | 13 | globalfunc entry3 14 | // Install dummy vector table; each entry branches-to-self 15 | ADRP x0, dummy_vectors 16 | MSR VBAR_EL3, x0 17 | 18 | // 19 | // Configure SCR_EL3 20 | // 21 | // 10:10 RW x1 make EL2 be 64-bit 22 | // 08:08 HCE x1 enable HVC instructions 23 | // 05:04 RES1 x3 reserved 24 | // 00:00 NS x1 switch to Normal world 25 | // 26 | MOV w0, #0x531 27 | MSR SCR_EL3, x0 28 | 29 | // 30 | // Configure SCTLR_EL2 31 | // 32 | // 29:28 RES1 x3 reserved 33 | // 23:22 RES1 x3 reserved 34 | // 18:18 RES1 x1 reserved 35 | // 16:16 RES1 x1 reserved 36 | // 12:12 I x0 disable allocation of instrs into unified $s 37 | // 11:11 RES1 x1 reserved 38 | // 05:04 RES1 x3 reserved 39 | // 02:02 C x0 disable allocation of data into data/unified $s 40 | // 00:00 M x0 disable MMU 41 | // 42 | LDR w0, =0x30C50830 43 | MSR SCTLR_EL2, x0 44 | 45 | // 46 | // Prepare to drop to EL2h with all asynchronous exceptions masked 47 | // 48 | // 09:09 D x1 Mask debug exceptions 49 | // 08:08 A x1 Mask SErrors 50 | // 07:07 I x1 Mask IRQs 51 | // 06:06 F x1 Mask FIQs 52 | // 04:04 M[4] x0 Bits 03:00 define an AArch64 state 53 | // 03:00 M[3:0] x9 EL2h 54 | // 55 | MOV w0, #0x3C9 56 | MSR SPSR_EL3, x0 57 | 58 | // Drop to hypervisor code 59 | ADR x0, entry2 60 | MSR ELR_EL3, x0 61 | ERET 62 | endfunc entry3 63 | -------------------------------------------------------------------------------- /source/pl011.S: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Ash Wilding. All rights reserved. 3 | * 4 | * SPDX-License-Identifier: MIT 5 | * 6 | * 7 | * Simple PL011 UART driver using polled I/O with no error handling. 8 | * For documentation, see ARM DDI 0183. 9 | */ 10 | 11 | #include "asm.h" 12 | #include "pl011.h" 13 | 14 | #define FR_TXFF (1 << 5) 15 | #define FR_RXFE (1 << 4) 16 | #define FR_BUSY (1 << 3) 17 | 18 | #define DR 0x00 19 | #define ECR 0x04 20 | #define FR 0x18 21 | #define CR 0x30 22 | 23 | #define ASCII_NL 10 24 | #define ASCII_CR 13 25 | 26 | 27 | /* 28 | * Initialise the PL011 UART with Tx and Rx enabled. 29 | * Note: We don't program LCR/IBRD/FBRD as they have no effect on FVP. 30 | */ 31 | globalfunc pl011_init 32 | LDR x0, =PLATFORM_PL011_BASE 33 | STR wzr, [x0, #CR] 34 | STR wzr, [x0, #ECR] 35 | STR wzr, [x0, #DR] 36 | LDR w1, =0x301 37 | STR w1, [x0, #CR] 38 | RET 39 | endfunc pl011_init 40 | 41 | 42 | /* 43 | * Transmit a character; if the character is '\n', first transmit '\r'. 44 | */ 45 | globalfunc pl011_putc 46 | LDR x1, =PLATFORM_PL011_BASE 47 | .macro wait_for_tx_ready 48 | 1: 49 | LDR w2, [x1, #FR] 50 | MOV w3, #(FR_TXFF | FR_BUSY) 51 | AND w2, w2, w3 52 | CBNZ w2, 1b 53 | .endm 54 | wait_for_tx_ready 55 | CMP w0, #ASCII_NL 56 | B.NE 2f 57 | MOV w2, #ASCII_CR 58 | STR w2, [x1, #DR] 59 | wait_for_tx_ready 60 | 2: 61 | STR w0, [x1, #DR] 62 | RET 63 | endfunc pl011_putc 64 | 65 | 66 | /* 67 | * Transmit a null-terminated string. 68 | */ 69 | globalfunc pl011_puts 70 | push x20, x30 71 | MOV x20, x0 72 | 1: 73 | LDRB w0, [x20], #1 74 | CBZ w0, 2f 75 | BL pl011_putc 76 | B 1b 77 | 2: 78 | pop x20, x30 79 | RET 80 | endfunc pl011_puts 81 | 82 | 83 | /* 84 | * Poll the PL011 UART for a character. 85 | */ 86 | globalfunc pl011_getc 87 | LDR x1, =PLATFORM_PL011_BASE 88 | 1: 89 | LDR w2, [x1, #FR] 90 | AND w2, w2, #FR_RXFE 91 | CBNZ w2, 1b 92 | LDRB w0, [x1, #DR] 93 | RET 94 | endfunc pl011_getc 95 | -------------------------------------------------------------------------------- /source/vectors.S: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Ash Wilding. All rights reserved. 3 | * 4 | * SPDX-License-Identifier: MIT 5 | * 6 | * 7 | * The Armv8-A architecture defines: 8 | * - Vector table base addresses are aligned to 4KB 9 | * - Each entry is 32 instructions (128 bytes) long 10 | * 11 | * The vectors in this file use the following taxonomy: 12 | * __[_] 13 | * 14 | * Where is one of: 15 | * - "c" for current exception level (EL) 16 | * - "l64" for a lower EL, where one or more ELs below this one is 64-bit 17 | * - "l32" for a lower EL, where all ELs below this one are 32-bit 18 | * 19 | * Where is one of: 20 | * - "sync" for synchronous exceptions 21 | * - "irq" for IRQs 22 | * - "fiq" for FIQs 23 | * - "serror" for System Errors 24 | * 25 | * Where is one of: 26 | * - "sp0" for thread mode 27 | * - "spx" for handler mode 28 | * 29 | * Example: "dummy_c_sync_sp0" is dummy table handler for synchronous 30 | * exceptions take from the current EL when we were in thread mode. 31 | */ 32 | 33 | 34 | /* 35 | * Dummy vector table where all entries are branches-to-self; useful to 36 | * safely spin on unexpected exceptions: 37 | * - During early development before we've written a proper table 38 | * - During early boot before we've had a chance to install a proper table 39 | * 40 | * We can also install this table at EL3 when not using actual firmware; 41 | * this way we safely spin on SMC instructions. 42 | */ 43 | .align 12 44 | .global dummy_vectors 45 | dummy_vectors: 46 | dummy_c_sync_sp0: B . 47 | .align 7 48 | dummy_c_irq_sp0: B . 49 | .align 7 50 | dummy_c_fiq_sp0: B . 51 | .align 7 52 | dummy_c_serror_sp0: B . 53 | .align 7 54 | dummy_c_sync_spx: RET 55 | .align 7 56 | dummy_c_irq_spx: B . 57 | .align 7 58 | dummy_c_fiq_spx: B . 59 | .align 7 60 | dummy_c_serror_spx: B . 61 | .align 7 62 | dummy_l64_sync: B . 63 | .align 7 64 | dummy_l64_irq: B . 65 | .align 7 66 | dummy_l64_fiq: B . 67 | .align 7 68 | dummy_l64_serror: B . 69 | .align 7 70 | dummy_l32_sync: B . 71 | .align 7 72 | dummy_l32_irq: B . 73 | .align 7 74 | dummy_l32_fiq: B . 75 | .align 7 76 | dummy_l32_serror: B . 77 | -------------------------------------------------------------------------------- /source/fvp.S: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * This file was automatically generated using arm64-pgtable-tool. 4 | * See: https://github.com/ashwio/arm64-pgtable-tool 5 | * 6 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 7 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 8 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 9 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 10 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 11 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 12 | * SOFTWARE. 13 | * 14 | * This code programs the following translation table structure: 15 | * 16 | * level 2 table @ 0x90000000 17 | * [# 0]---------------------------\ 18 | * level 3 table @ 0x90010000 19 | * [#7177] 0x00001c090000-0x00001c09ffff, Device, UART0 20 | * [# 1]---------------------------\ 21 | * level 3 table @ 0x90020000 22 | * [#3072] 0x00002c000000-0x00002c00ffff, Device, GICC 23 | * [#3584] 0x00002e000000-0x00002e00ffff, RW_Data, Non-Trusted SRAM 24 | * [#3840] 0x00002f000000-0x00002f00ffff, Device, GICv3 GICD 25 | * [#3856] 0x00002f100000-0x00002f10ffff, Device, GICv3 GICR 26 | * [#3857] 0x00002f110000-0x00002f11ffff, Device, GICv3 GICR 27 | * [#3858] 0x00002f120000-0x00002f12ffff, Device, GICv3 GICR 28 | * [#3859] 0x00002f130000-0x00002f13ffff, Device, GICv3 GICR 29 | * [#3860] 0x00002f140000-0x00002f14ffff, Device, GICv3 GICR 30 | * [#3861] 0x00002f150000-0x00002f15ffff, Device, GICv3 GICR 31 | * [#3862] 0x00002f160000-0x00002f16ffff, Device, GICv3 GICR 32 | * [#3863] 0x00002f170000-0x00002f17ffff, Device, GICv3 GICR 33 | * [#3864] 0x00002f180000-0x00002f18ffff, Device, GICv3 GICR 34 | * [#3865] 0x00002f190000-0x00002f19ffff, Device, GICv3 GICR 35 | * [#3866] 0x00002f1a0000-0x00002f1affff, Device, GICv3 GICR 36 | * [#3867] 0x00002f1b0000-0x00002f1bffff, Device, GICv3 GICR 37 | * [#3868] 0x00002f1c0000-0x00002f1cffff, Device, GICv3 GICR 38 | * [#3869] 0x00002f1d0000-0x00002f1dffff, Device, GICv3 GICR 39 | * [#3870] 0x00002f1e0000-0x00002f1effff, Device, GICv3 GICR 40 | * [#3871] 0x00002f1f0000-0x00002f1fffff, Device, GICv3 GICR 41 | * [# 4] 0x000080000000-0x00009fffffff, RW_Data, Non-Trusted DRAM 42 | * [# 5] 0x0000a0000000-0x0000bfffffff, RW_Data, Non-Trusted DRAM 43 | * [# 6] 0x0000c0000000-0x0000dfffffff, RW_Data, Non-Trusted DRAM 44 | * [# 7] 0x0000e0000000-0x0000ffffffff, RW_Data, Non-Trusted DRAM 45 | * 46 | * The following command line arguments were passed to arm64-pgtable-tool: 47 | * 48 | * -i examples/base-fvp-minimal.txt 49 | * -ttb 0x90000000 50 | * -el 2 51 | * -tg 64K 52 | * -tsz 32 53 | * 54 | * This memory map requires a total of 3 translation tables. 55 | * Each table occupies 64K of memory (0x10000 bytes). 56 | * The buffer pointed to by 0x90000000 must therefore be 3x 64K = 0x30000 bytes long. 57 | * It is the programmer's responsibility to guarantee this. 58 | * 59 | * The programmer must also ensure that the virtual memory region containing the 60 | * translation tables is itself marked as NORMAL in the memory map file. 61 | */ 62 | 63 | .section .data.mmu 64 | .balign 2 65 | 66 | mmu_lock: .4byte 0 // lock to ensure only 1 CPU runs init 67 | #define LOCKED 1 68 | 69 | mmu_init: .4byte 0 // whether init has been run 70 | #define INITIALISED 1 71 | 72 | .section .text.mmu_on 73 | .balign 2 74 | .global mmu_on 75 | .type mmu_on, @function 76 | 77 | mmu_on: 78 | 79 | ADRP x0, mmu_lock // get 4KB page containing mmu_lock 80 | ADD x0, x0, :lo12:mmu_lock // restore low 12 bits lost by ADRP 81 | MOV w1, #LOCKED 82 | SEVL // first pass won't sleep 83 | 1: 84 | WFE // sleep on retry 85 | LDAXR w2, [x0] // read mmu_lock 86 | CBNZ w2, 1b // not available, go back to sleep 87 | STXR w3, w1, [x0] // try to acquire mmu_lock 88 | CBNZ w3, 1b // failed, go back to sleep 89 | 90 | check_already_initialised: 91 | 92 | ADRP x1, mmu_init // get 4KB page containing mmu_init 93 | ADD x1, x1, :lo12:mmu_init // restore low 12 bits lost by ADRP 94 | LDR w2, [x1] // read mmu_init 95 | CBNZ w2, end // init already done, skip to the end 96 | 97 | zero_out_tables: 98 | 99 | LDR x2, =0x90000000 // address of first table 100 | LDR x3, =0x30000 // combined length of all tables 101 | LSR x3, x3, #5 // number of required STP instructions 102 | FMOV d0, xzr // clear q0 103 | 1: 104 | STP q0, q0, [x2], #32 // zero out 4 table entries at a time 105 | SUBS x3, x3, #1 106 | B.NE 1b 107 | 108 | load_descriptor_templates: 109 | 110 | LDR x2, =0x40000000000705 // Device block 111 | LDR x3, =0x40000000000707 // Device page 112 | LDR x4, =0x00000000000701 // RW data block 113 | LDR x5, =0x00000000000703 // RW data page 114 | LDR x20, =0x781 // code block 115 | LDR x21, =0x783 // code page 116 | 117 | 118 | program_table_0: 119 | 120 | LDR x8, =0x90000000 // base address of this table 121 | LDR x9, =0x20000000 // chunk size 122 | 123 | program_table_0_entry_0: 124 | 125 | LDR x10, =0 // idx 126 | LDR x11, =0x90010000 // next-level table address 127 | ORR x11, x11, #0x3 // next-level table descriptor 128 | STR x11, [x8, x10, lsl #3] // write entry into table 129 | 130 | program_table_0_entry_1: 131 | 132 | LDR x10, =1 // idx 133 | LDR x11, =0x90020000 // next-level table address 134 | ORR x11, x11, #0x3 // next-level table descriptor 135 | STR x11, [x8, x10, lsl #3] // write entry into table 136 | 137 | program_table_0_entry_4_to_7: 138 | 139 | LDR x10, =4 // idx 140 | LDR x11, =4 // number of contiguous entries 141 | LDR x12, =0x80000000 // output address of entry[idx] 142 | 1: 143 | ORR x12, x12, x4 // merge output address with template 144 | STR X12, [x8, x10, lsl #3] // write entry into table 145 | ADD x10, x10, #1 // prepare for next entry idx+1 146 | ADD x12, x12, x9 // add chunk to address 147 | SUBS x11, x11, #1 // loop as required 148 | B.NE 1b 149 | program_table_1: 150 | 151 | LDR x8, =0x90010000 // base address of this table 152 | LDR x9, =0x10000 // chunk size 153 | 154 | program_table_1_entry_7177: 155 | 156 | LDR x10, =7177 // idx 157 | LDR x11, =1 // number of contiguous entries 158 | LDR x12, =0x1c090000 // output address of entry[idx] 159 | 1: 160 | ORR x12, x12, x3 // merge output address with template 161 | STR X12, [x8, x10, lsl #3] // write entry into table 162 | ADD x10, x10, #1 // prepare for next entry idx+1 163 | ADD x12, x12, x9 // add chunk to address 164 | SUBS x11, x11, #1 // loop as required 165 | B.NE 1b 166 | program_table_2: 167 | 168 | LDR x8, =0x90020000 // base address of this table 169 | LDR x9, =0x10000 // chunk size 170 | 171 | program_table_2_entry_3072: 172 | 173 | LDR x10, =3072 // idx 174 | LDR x11, =1 // number of contiguous entries 175 | LDR x12, =0x2c000000 // output address of entry[idx] 176 | 1: 177 | ORR x12, x12, x3 // merge output address with template 178 | STR X12, [x8, x10, lsl #3] // write entry into table 179 | ADD x10, x10, #1 // prepare for next entry idx+1 180 | ADD x12, x12, x9 // add chunk to address 181 | SUBS x11, x11, #1 // loop as required 182 | B.NE 1b 183 | 184 | program_table_2_entry_3584: 185 | 186 | LDR x10, =3584 // idx 187 | LDR x11, =1 // number of contiguous entries 188 | LDR x12, =0x2e000000 // output address of entry[idx] 189 | 1: 190 | ORR x12, x12, x5 // merge output address with template 191 | STR X12, [x8, x10, lsl #3] // write entry into table 192 | ADD x10, x10, #1 // prepare for next entry idx+1 193 | ADD x12, x12, x9 // add chunk to address 194 | SUBS x11, x11, #1 // loop as required 195 | B.NE 1b 196 | 197 | program_table_2_entry_3840: 198 | 199 | LDR x10, =3840 // idx 200 | LDR x11, =1 // number of contiguous entries 201 | LDR x12, =0x2f000000 // output address of entry[idx] 202 | 1: 203 | ORR x12, x12, x3 // merge output address with template 204 | STR X12, [x8, x10, lsl #3] // write entry into table 205 | ADD x10, x10, #1 // prepare for next entry idx+1 206 | ADD x12, x12, x9 // add chunk to address 207 | SUBS x11, x11, #1 // loop as required 208 | B.NE 1b 209 | 210 | program_table_2_entry_3856_to_3871: 211 | 212 | LDR x10, =3856 // idx 213 | LDR x11, =16 // number of contiguous entries 214 | LDR x12, =0x2f100000 // output address of entry[idx] 215 | 1: 216 | ORR x12, x12, x3 // merge output address with template 217 | STR X12, [x8, x10, lsl #3] // write entry into table 218 | ADD x10, x10, #1 // prepare for next entry idx+1 219 | ADD x12, x12, x9 // add chunk to address 220 | SUBS x11, x11, #1 // loop as required 221 | B.NE 1b 222 | 223 | init_done: 224 | 225 | MOV w2, #INITIALISED 226 | STR w2, [x1] 227 | 228 | end: 229 | 230 | LDR x1, =0x90000000 // program ttbr0 on this CPU 231 | MSR ttbr0_el2, x1 232 | LDR x1, =0xff // program mair on this CPU 233 | MSR mair_el2, x1 234 | LDR x1, =0x80807520 // program tcr on this CPU 235 | MSR tcr_el2, x1 236 | ISB 237 | MRS x2, tcr_el2 // verify CPU supports desired config 238 | CMP x2, x1 239 | B.NE . 240 | LDR x1, =0x1005 // program sctlr on this CPU 241 | MSR sctlr_el2, x1 242 | ISB // synchronize context on this CPU 243 | STLR wzr, [x0] // release mmu_lock 244 | RET // done! 245 | --------------------------------------------------------------------------------