├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── include ├── asm.h ├── pl011.h └── platform.h ├── linker.ld └── source ├── entry2.S ├── entry3.S ├── main.c ├── pl011.S └── vectors.S /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | out.elf 3 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | 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} linker.ld 83 | ${LD} $^ -o $@ ${LD_FLAGS} 84 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # arm64-hypervisor-tutorial 2 | Code for bare metal 64-bit Arm hypervisor tutorial series on my blog, [ashw.io](https://ashw.io). 3 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 main 18 | B . 19 | endfunc entry2 20 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | *