AArch64 EL2 hypervisor for QEMU virt that boots at EL2
1

Configure Feed

Select the types of activity you want to include in your feed.

Initial commit

author vm.fail date (Mar 20, 2026, 2:37 PM UTC) commit eabe8fa2
+5629
+4
.gitignore
··· 1 + build/ 2 + build-host/ 3 + build-linux/ 4 + dist/
+161
Makefile
··· 1 + PROJECT := ariel 2 + TARGET := aarch64-none-elf 3 + BUILD := build 4 + HV_ELF := $(BUILD)/hypervisor.elf 5 + HV_BIN := $(BUILD)/hypervisor.bin 6 + GUEST_ELF := $(BUILD)/guest/diag.elf 7 + GUEST_BIN := $(BUILD)/guest/diag.bin 8 + MAP := $(BUILD)/hypervisor.map 9 + OBJDUMP := $(BUILD)/hypervisor.objdump 10 + READELF := $(BUILD)/hypervisor.readelf 11 + DIST := dist 12 + RELEASE_DIR := $(DIST)/$(PROJECT)-qemu-virt-aarch64 13 + RELEASE_TAR := $(DIST)/$(PROJECT)-qemu-virt-aarch64.tar.gz 14 + 15 + CC := clang 16 + LD := ld.lld 17 + OBJCOPY := llvm-objcopy 18 + OBJDUMP_TOOL := llvm-objdump 19 + READELF_TOOL := llvm-readelf 20 + PYTHON ?= python3 21 + QEMU ?= qemu-system-aarch64 22 + QEMU_RAM ?= 1G 23 + comma := , 24 + LINUX_DIR ?= /tmp/ariel-linux 25 + LINUX_IMAGE_URL ?= http://deb.debian.org/debian/dists/bookworm/main/installer-arm64/current/images/netboot/debian-installer/arm64/linux 26 + LINUX_INITRD_URL ?= http://deb.debian.org/debian/dists/bookworm/main/installer-arm64/current/images/netboot/debian-installer/arm64/initrd.gz 27 + LINUX_INITRD_BYTES := $(if $(LINUX_INITRD),$(shell stat -c %s "$(LINUX_INITRD)" 2>/dev/null),) 28 + LINUX_INITRD_SIZE := $(if $(LINUX_INITRD),$(if $(LINUX_INITRD_BYTES),$(LINUX_INITRD_BYTES),0),0) 29 + LINUX_INITRD_DEVICE := $(if $(LINUX_INITRD),-device loader$(comma)file=$(LINUX_INITRD)$(comma)addr=0x46000000$(comma)force-raw=on,) 30 + 31 + COMMON_CFLAGS := --target=$(TARGET) -std=c11 -ffreestanding -fno-builtin \ 32 + -fno-stack-protector -fno-unwind-tables -fno-asynchronous-unwind-tables \ 33 + -fno-exceptions -mgeneral-regs-only -mstrict-align -Wall -Wextra -Werror \ 34 + -Wconversion -Wsign-conversion -Wmissing-prototypes -Wstrict-prototypes \ 35 + -Iinclude -I. 36 + EXTRA_CFLAGS ?= 37 + HV_CFLAGS := $(COMMON_CFLAGS) -O2 -g -D__HYPERVISOR__ $(EXTRA_CFLAGS) 38 + GUEST_CFLAGS := $(COMMON_CFLAGS) -O2 -g -D__GUEST__ 39 + ASFLAGS := --target=$(TARGET) -g -Iinclude -I. 40 + LDFLAGS := -nostdlib -static --fatal-warnings 41 + 42 + HV_C_SRCS := \ 43 + arch/aarch64/arch.c \ 44 + core/main.c core/el2_mmu.c core/fdt.c core/guest_loader.c core/log.c core/mmio_policy.c core/panic.c core/policy.c core/psci.c core/selftest.c \ 45 + core/string.c core/sysreg.c core/trap.c core/vcpu.c core/monitor.c \ 46 + drivers/gicv3.c drivers/pl011.c drivers/timer.c \ 47 + mm/allocator.c mm/stage2.c 48 + HV_S_SRCS := arch/aarch64/entry.S arch/aarch64/vectors.S arch/aarch64/vcpu.S arch/aarch64/guest_blob.S 49 + GUEST_C_SRCS := guest/main.c 50 + GUEST_S_SRCS := guest/start.S 51 + 52 + HV_OBJS := $(HV_C_SRCS:%.c=$(BUILD)/%.o) $(HV_S_SRCS:%.S=$(BUILD)/%.o) 53 + GUEST_OBJS := $(GUEST_C_SRCS:%.c=$(BUILD)/%.o) $(GUEST_S_SRCS:%.S=$(BUILD)/%.o) 54 + 55 + .PHONY: all clean run run-linux linux-artifacts test-linux-smoke test-diagnostic-smoke test-host test ci release debug objdump readelf 56 + all: $(HV_ELF) $(HV_BIN) $(OBJDUMP) $(READELF) 57 + 58 + $(BUILD): 59 + mkdir -p $(BUILD) 60 + 61 + $(BUILD)/guest/%.o: guest/%.c 62 + mkdir -p $(dir $@) 63 + $(CC) $(GUEST_CFLAGS) -c $< -o $@ 64 + 65 + $(BUILD)/%.o: %.c 66 + mkdir -p $(dir $@) 67 + $(CC) $(HV_CFLAGS) -c $< -o $@ 68 + 69 + $(BUILD)/%.o: %.S 70 + mkdir -p $(dir $@) 71 + $(CC) $(ASFLAGS) -c $< -o $@ 72 + 73 + $(BUILD)/arch/aarch64/guest_blob.o: arch/aarch64/guest_blob.S $(GUEST_BIN) 74 + mkdir -p $(dir $@) 75 + $(CC) $(ASFLAGS) -c $< -o $@ 76 + 77 + $(GUEST_ELF): $(GUEST_OBJS) guest/linker.ld 78 + mkdir -p $(dir $@) 79 + $(LD) $(LDFLAGS) -T guest/linker.ld $(GUEST_OBJS) -o $@ 80 + 81 + $(GUEST_BIN): $(GUEST_ELF) 82 + $(OBJCOPY) -O binary $< $@ 83 + 84 + $(HV_ELF): $(HV_OBJS) linker.ld 85 + mkdir -p $(dir $@) 86 + $(LD) $(LDFLAGS) -T linker.ld -Map=$(MAP) $(HV_OBJS) -o $@ 87 + 88 + $(HV_BIN): $(HV_ELF) 89 + $(OBJCOPY) -O binary $< $@ 90 + 91 + $(OBJDUMP): $(HV_ELF) 92 + $(OBJDUMP_TOOL) -d -S $< > $@ 93 + 94 + $(READELF): $(HV_ELF) 95 + $(READELF_TOOL) -a $< > $@ 96 + 97 + run: $(HV_ELF) 98 + $(QEMU) -M virt,virtualization=on,gic-version=3 -cpu cortex-a57 -m $(QEMU_RAM) \ 99 + -nographic -serial mon:stdio -kernel $(HV_ELF) 100 + 101 + run-linux: 102 + @if [ -z "$(LINUX_IMAGE)" ] || [ -z "$(LINUX_DTB)" ]; then \ 103 + echo "usage: make run-linux LINUX_IMAGE=/path/Image LINUX_DTB=/path/qemu-virt.dtb [LINUX_INITRD=/path/initrd]"; \ 104 + echo "builds a Linux-profile hypervisor and loads Image+DTB into guest RAM"; \ 105 + else \ 106 + $(MAKE) -B BUILD=build-linux EXTRA_CFLAGS="-DCONFIG_BOOT_LINUX=1 -DCONFIG_LINUX_INITRD_SIZE=$(LINUX_INITRD_SIZE)ull" all && \ 107 + $(QEMU) -M virt,virtualization=on,gic-version=3 -cpu cortex-a57 -m $(QEMU_RAM) \ 108 + -nographic -serial mon:stdio -kernel build-linux/hypervisor.elf \ 109 + -device loader,file=$(LINUX_IMAGE),addr=0x42000000,force-raw=on \ 110 + -device loader,file=$(LINUX_DTB),addr=0x71e00000,force-raw=on \ 111 + $(LINUX_INITRD_DEVICE); \ 112 + fi 113 + 114 + linux-artifacts: 115 + mkdir -p $(LINUX_DIR) 116 + $(QEMU) -M virt,virtualization=on,gic-version=3,dumpdtb=$(LINUX_DIR)/virt.dtb -cpu cortex-a57 -m $(QEMU_RAM) -nographic 117 + curl -L --fail --show-error --output $(LINUX_DIR)/debian-arm64-linux $(LINUX_IMAGE_URL) 118 + curl -L --fail --show-error --output $(LINUX_DIR)/debian-arm64-initrd.gz $(LINUX_INITRD_URL) 119 + 120 + test-linux-smoke: linux-artifacts 121 + bash -o pipefail -c 'timeout 180s $(MAKE) run-linux LINUX_IMAGE=$(LINUX_DIR)/debian-arm64-linux LINUX_DTB=$(LINUX_DIR)/virt.dtb LINUX_INITRD=$(LINUX_DIR)/debian-arm64-initrd.gz 2>&1 | tee $(LINUX_DIR)/linux-smoke.log; status=$${PIPESTATUS[0]}; test $$status -eq 0 -o $$status -eq 124' 122 + grep -q 'Booting Linux on physical CPU' $(LINUX_DIR)/linux-smoke.log 123 + grep -q 'Kernel command line: console=ttyAMA0 earlycon' $(LINUX_DIR)/linux-smoke.log 124 + grep -q 'CPU: All CPU(s) started at EL1' $(LINUX_DIR)/linux-smoke.log 125 + grep -q 'Trying to unpack rootfs image as initramfs' $(LINUX_DIR)/linux-smoke.log 126 + grep -q 'Freeing initrd memory' $(LINUX_DIR)/linux-smoke.log 127 + grep -q 'Run /init as init process' $(LINUX_DIR)/linux-smoke.log 128 + ! grep -q 'Initramfs unpacking failed' $(LINUX_DIR)/linux-smoke.log 129 + ! grep -q 'Kernel panic' $(LINUX_DIR)/linux-smoke.log 130 + 131 + test-diagnostic-smoke: $(HV_ELF) 132 + bash -o pipefail -c 'printf "status\n" | timeout 14s $(MAKE) run 2>&1 | tee $(BUILD)/diagnostic-smoke.log; status=$${PIPESTATUS[1]}; test $$status -eq 0 -o $$status -eq 124' 133 + grep -q 'ariel qemu-virt-aarch64' $(BUILD)/diagnostic-smoke.log 134 + grep -q 'guest: diagnostic guest: requested hv id' $(BUILD)/diagnostic-smoke.log 135 + grep -q 'monitor entered reason=guest requested pause' $(BUILD)/diagnostic-smoke.log 136 + ! grep -q 'panic:' $(BUILD)/diagnostic-smoke.log 137 + 138 + test-host: 139 + mkdir -p build-host 140 + $(CC) -std=c11 -Wall -Wextra -Werror -Iinclude tests/host/test_hv_types.c -o build-host/test_hv_types 141 + build-host/test_hv_types 142 + $(PYTHON) tests/test_log_decode.py 143 + $(PYTHON) scripts/check_release.py 144 + 145 + test: all test-host test-diagnostic-smoke 146 + 147 + ci: clean release 148 + 149 + release: test 150 + scripts/release.sh 151 + 152 + debug: $(HV_ELF) 153 + @echo "gdb command: gdb-multiarch $(HV_ELF) -ex 'target remote :1234'" 154 + $(QEMU) -M virt,virtualization=on,gic-version=3 -cpu cortex-a57 -m $(QEMU_RAM) \ 155 + -nographic -serial mon:stdio -S -gdb tcp::1234 -kernel $(HV_ELF) 156 + 157 + objdump: $(OBJDUMP) 158 + readelf: $(READELF) 159 + 160 + clean: 161 + rm -rf $(BUILD)
+147
README.md
··· 1 + # ariel 2 + 3 + AArch64 EL2 hypervisor for QEMU virt that boots at EL2, sets up exception vectors, writes a stage 2 guest IPA space and loads a bundled EL1 diagnostic guest/an external linux image and traps privileged guest activity as well as applying compiled policy tables and exposes a UART monitor over QEMU stdio 4 + 5 + ## Build/Run 6 + 7 + ```sh 8 + make clean 9 + make 10 + ``` 11 + 12 + ```sh 13 + make run 14 + ``` 15 + 16 + Equivalent QEMU shape 17 + 18 + ```sh 19 + qemu-system-aarch64 \ 20 + -M virt,virtualization=on,gic-version=3 \ 21 + -cpu cortex-a57 \ 22 + -m 1G \ 23 + -nographic \ 24 + -serial mon:stdio \ 25 + -kernel build/hypervisor.elf 26 + ``` 27 + 28 + Run a Linux arm64 `Image` with a QEMU `virt` DTB 29 + 30 + ```sh 31 + make run-linux \ 32 + LINUX_IMAGE=/path/Image \ 33 + LINUX_DTB=/path/qemu-virt.dtb 34 + ``` 35 + 36 + Or just do it with an initrd 37 + 38 + ```sh 39 + make run-linux \ 40 + LINUX_IMAGE=/path/Image \ 41 + LINUX_DTB=/path/qemu-virt.dtb \ 42 + LINUX_INITRD=/path/initrd.gz 43 + ``` 44 + 45 + Fetch the tested Debian installer artifacts and smoke test the Linux path 46 + 47 + ```sh 48 + make test-linux-smoke 49 + ``` 50 + and complete the local gate 51 + 52 + ```sh 53 + make test 54 + make release 55 + ``` 56 + 57 + ## Memory 58 + 59 + ```text 60 + hypervisor link PA 0x40080000 61 + guest IPA base 0x40000000 62 + guest backing PA 0x42000000 63 + guest RAM size 0x30000000 768 MiB 64 + diagnostic entry IPA 0x40000000 65 + diagnostic stack top 0x40100000 66 + Linux Image load PA 0x42000000 67 + Linux Image entry IPA Image text_offset + 0x40000000 68 + Linux initrd PA 0x46000000 69 + Linux initrd IPA 0x44000000 70 + Linux DTB PA 0x71e00000 71 + Linux DTB IPA 0x6fe00000 72 + PL011 UART PA 0x09000000 73 + GICD PA 0x08000000 74 + GICR PA 0x080a0000 75 + ``` 76 + 77 + S2 uses 2 MiB block mappings for guest RAM. The guest sees the IPA range `0x40000000..0x70000000` which EL2 maps onto the PA range `0x42000000..0x72000000`. MMIO isn't passed through by default andkKnown device ranges are handled through policy or emulation layers 78 + 79 + ## Trap Path 80 + 81 + EL1 traps enter the EL2 vector table and the assembly path saves general registers and exception state into `struct trap_frame` 82 + 83 + ```text 84 + x0-x30 85 + sp_el0 86 + sp_el1 87 + elr_el2 88 + spsr_el2 89 + esr_el2 90 + far_el2 91 + hpfar_el2 92 + ``` 93 + 94 + ## Hypercalls 95 + 96 + 97 + ```c 98 + enum { 99 + HVC_GET_HV_ID = 0, 100 + HVC_DUMP_LOG = 1, 101 + HVC_GET_STATUS = 2, 102 + HVC_PAUSE = 3, 103 + HVC_GUEST_CONSOLE_WRITE = 4, 104 + HVC_REPORT_EXCEPTION = 5, 105 + }; 106 + ``` 107 + 108 + Guest side call pattern 109 + 110 + ```c 111 + static inline unsigned long hvc(unsigned long op, 112 + unsigned long a1, 113 + unsigned long a2, 114 + unsigned long a3) 115 + { 116 + register unsigned long x0 asm("x0") = op; 117 + register unsigned long x1 asm("x1") = a1; 118 + register unsigned long x2 asm("x2") = a2; 119 + register unsigned long x3 asm("x3") = a3; 120 + 121 + asm volatile("hvc #0" 122 + : "+r"(x0) 123 + : "r"(x1), "r"(x2), "r"(x3) 124 + : "memory"); 125 + return x0; 126 + } 127 + ``` 128 + 129 + Console write from guest IPA memory 130 + 131 + ```c 132 + const char msg[] = "guest online"; 133 + hvc(HVC_GUEST_CONSOLE_WRITE, (unsigned long)msg, sizeof(msg) - 1, 0); 134 + ``` 135 + 136 + Pause into the EL2 monitor: 137 + 138 + ```c 139 + hvc(HVC_PAUSE, 0, 0, 0); 140 + ``` 141 + 142 + ## Logs 143 + 144 + ```sh 145 + make run 2>&1 | tee uart.log 146 + python3 tools/log_decode.py --summary < uart.log 147 + ```
+261
arch/aarch64/arch.c
··· 1 + #include "hv/arch.h" 2 + #include "hv/config.h" 3 + #include "hv/mmio.h" 4 + #include "hv/panic.h" 5 + #include "hv/uart.h" 6 + #include "hv/vcpu.h" 7 + 8 + uint64_t arch_current_el(void) 9 + { 10 + uint64_t v; 11 + __asm__ volatile("mrs %0, CurrentEL" : "=r"(v)); 12 + return (v >> 2u) & 0x3u; 13 + } 14 + 15 + uint64_t arch_mpidr_el1(void) 16 + { 17 + uint64_t v; 18 + __asm__ volatile("mrs %0, mpidr_el1" : "=r"(v)); 19 + return v; 20 + } 21 + 22 + uint64_t arch_cntpct_el0(void) 23 + { 24 + uint64_t v; 25 + __asm__ volatile("mrs %0, cntpct_el0" : "=r"(v)); 26 + return v; 27 + } 28 + 29 + uint64_t arch_cntfrq_el0(void) 30 + { 31 + uint64_t v; 32 + __asm__ volatile("mrs %0, cntfrq_el0" : "=r"(v)); 33 + return v; 34 + } 35 + 36 + uint64_t arch_irq_save(void) 37 + { 38 + uint64_t flags; 39 + __asm__ volatile("mrs %0, daif\nmsr daifset, #0xf\nisb" : "=r"(flags) :: "memory"); 40 + return flags; 41 + } 42 + 43 + void arch_irq_restore(uint64_t flags) 44 + { 45 + __asm__ volatile("msr daif, %0\nisb" :: "r"(flags) : "memory"); 46 + } 47 + 48 + void arch_dump_el2_state(void) 49 + { 50 + uint64_t current_el; 51 + uint64_t hcr; 52 + uint64_t vtcr; 53 + uint64_t vttbr; 54 + uint64_t vbar; 55 + uint64_t sctlr; 56 + uint64_t mair; 57 + uint64_t sp; 58 + 59 + __asm__ volatile( 60 + "mrs %0, CurrentEL\n" 61 + "mrs %1, hcr_el2\n" 62 + "mrs %2, vtcr_el2\n" 63 + "mrs %3, vttbr_el2\n" 64 + "mrs %4, vbar_el2\n" 65 + "mrs %5, sctlr_el2\n" 66 + "mrs %6, mair_el2\n" 67 + "mov %7, sp\n" 68 + : "=r"(current_el), "=r"(hcr), "=r"(vtcr), "=r"(vttbr), 69 + "=r"(vbar), "=r"(sctlr), "=r"(mair), "=r"(sp)); 70 + 71 + uart_puts("cpu current_el="); 72 + uart_put_hex64(current_el); 73 + uart_puts(" hcr_el2="); 74 + uart_put_hex64(hcr); 75 + uart_puts(" vtcr_el2="); 76 + uart_put_hex64(vtcr); 77 + uart_puts(" vttbr_el2="); 78 + uart_put_hex64(vttbr); 79 + uart_puts("\n"); 80 + uart_puts("cpu vbar_el2="); 81 + uart_put_hex64(vbar); 82 + uart_puts(" sctlr_el2="); 83 + uart_put_hex64(sctlr); 84 + uart_puts(" mair_el2="); 85 + uart_put_hex64(mair); 86 + uart_puts(" sp="); 87 + uart_put_hex64(sp); 88 + uart_puts("\n"); 89 + } 90 + 91 + void arch_install_vectors(void) 92 + { 93 + if (!hv_is_aligned_u64((uint64_t)(uintptr_t)__vectors_el2, 2048u)) { 94 + panic("VBAR_EL2 vector table is not 2KB aligned"); 95 + } 96 + __asm__ volatile("msr vbar_el2, %0\nisb" :: "r"(__vectors_el2) : "memory"); 97 + } 98 + 99 + void arch_tlbi_vmalle1is(void) 100 + { 101 + __asm__ volatile("dsb ish\n tlbi vmalle1is\n dsb ish\n isb" ::: "memory"); 102 + } 103 + 104 + void arch_enable_el2_mmu(uint64_t ttbr0, uint64_t tcr, uint64_t mair) 105 + { 106 + uint64_t sctlr; 107 + 108 + __asm__ volatile( 109 + "dsb sy\n" 110 + "msr ttbr0_el2, %0\n" 111 + "msr tcr_el2, %1\n" 112 + "msr mair_el2, %2\n" 113 + "isb\n" 114 + "tlbi alle2\n" 115 + "dsb sy\n" 116 + "isb\n" 117 + :: "r"(ttbr0), "r"(tcr), "r"(mair) 118 + : "memory"); 119 + 120 + __asm__ volatile("mrs %0, sctlr_el2" : "=r"(sctlr)); 121 + sctlr |= HV_BIT(0) | HV_BIT(2) | HV_BIT(12); 122 + __asm__ volatile( 123 + "msr sctlr_el2, %0\n" 124 + "isb\n" 125 + :: "r"(sctlr) 126 + : "memory"); 127 + } 128 + 129 + void arch_el2_configure(uint64_t vttbr, uint64_t vtcr) 130 + { 131 + const uint64_t mair = 0x000000000000ff00ull | 0x0000000000000004ull; 132 + uint64_t hcr = 0; 133 + 134 + hcr |= HV_BIT(31); 135 + hcr |= HV_BIT(0); 136 + #if !CONFIG_BOOT_LINUX 137 + hcr |= HV_BIT(26); 138 + #endif 139 + hcr |= HV_BIT(22); 140 + hcr |= HV_BIT(21); 141 + hcr |= HV_BIT(19); 142 + #if !CONFIG_BOOT_LINUX 143 + hcr |= HV_BIT(20); 144 + hcr |= HV_BIT(18) | HV_BIT(17) | HV_BIT(16) | HV_BIT(15); 145 + #endif 146 + #if CONFIG_ENABLE_WFI_TRAPS 147 + hcr |= HV_BIT(14) | HV_BIT(13); 148 + #endif 149 + 150 + __asm__ volatile( 151 + "msr mair_el2, %0\n" 152 + "msr vtcr_el2, %1\n" 153 + "msr vttbr_el2, %2\n" 154 + "msr mdcr_el2, %3\n" 155 + "msr cptr_el2, %4\n" 156 + "msr cnthctl_el2, %5\n" 157 + "msr hcr_el2, %6\n" 158 + "isb\n" 159 + :: "r"(mair), "r"(vtcr), "r"(vttbr), 160 + "r"(0ull), "r"(0x33ffull), "r"(3ull), "r"(hcr) 161 + : "memory"); 162 + } 163 + 164 + void arch_prepare_guest_entry(struct vcpu *vcpu) 165 + { 166 + BUG_ON(vcpu == (struct vcpu *)0); 167 + __asm__ volatile( 168 + "msr sctlr_el1, %0\n" 169 + "msr ttbr0_el1, %1\n" 170 + "msr ttbr1_el1, %2\n" 171 + "msr tcr_el1, %3\n" 172 + "msr mair_el1, %4\n" 173 + "msr vbar_el1, %5\n" 174 + "msr cntkctl_el1, %6\n" 175 + "isb\n" 176 + :: "r"(vcpu->sysregs.sctlr_el1), "r"(vcpu->sysregs.ttbr0_el1), 177 + "r"(vcpu->sysregs.ttbr1_el1), "r"(vcpu->sysregs.tcr_el1), 178 + "r"(vcpu->sysregs.mair_el1), "r"(vcpu->sysregs.vbar_el1), 179 + "r"(vcpu->sysregs.cntkctl_el1) 180 + : "memory"); 181 + } 182 + 183 + void arch_capture_el1_sysregs(struct vcpu *vcpu) 184 + { 185 + BUG_ON(vcpu == (struct vcpu *)0); 186 + __asm__ volatile( 187 + "mrs %0, vbar_el1\n" 188 + "mrs %1, elr_el1\n" 189 + "mrs %2, spsr_el1\n" 190 + "mrs %3, esr_el1\n" 191 + "mrs %4, far_el1\n" 192 + : "=r"(vcpu->sysregs.vbar_el1), "=r"(vcpu->sysregs.elr_el1), 193 + "=r"(vcpu->sysregs.spsr_el1), "=r"(vcpu->sysregs.esr_el1), 194 + "=r"(vcpu->sysregs.far_el1)); 195 + } 196 + 197 + void arch_sync_el1_sysregs(const struct vcpu *vcpu) 198 + { 199 + BUG_ON(vcpu == (const struct vcpu *)0); 200 + __asm__ volatile( 201 + "dsb sy\n" 202 + "msr mair_el1, %0\n" 203 + "msr tcr_el1, %1\n" 204 + "msr ttbr0_el1, %2\n" 205 + "msr ttbr1_el1, %3\n" 206 + "msr vbar_el1, %4\n" 207 + "msr cntkctl_el1, %5\n" 208 + "msr cpacr_el1, %6\n" 209 + "isb\n" 210 + "msr sctlr_el1, %7\n" 211 + "isb\n" 212 + :: "r"(vcpu->sysregs.mair_el1), "r"(vcpu->sysregs.tcr_el1), 213 + "r"(vcpu->sysregs.ttbr0_el1), "r"(vcpu->sysregs.ttbr1_el1), 214 + "r"(vcpu->sysregs.vbar_el1), "r"(vcpu->sysregs.cntkctl_el1), 215 + "r"(vcpu->sysregs.cpacr_el1), "r"(vcpu->sysregs.sctlr_el1) 216 + : "memory"); 217 + } 218 + 219 + void arch_advance_guest_pc(struct vcpu *vcpu) 220 + { 221 + BUG_ON(vcpu == (struct vcpu *)0); 222 + vcpu->pc += 4u; 223 + } 224 + 225 + void arch_inject_guest_undef(struct vcpu *vcpu) 226 + { 227 + const uint64_t current_el_spx_sync = 0x200u; 228 + const uint64_t pstate_el1h_daif = 0x3c5u; 229 + 230 + BUG_ON(vcpu == (struct vcpu *)0); 231 + arch_capture_el1_sysregs(vcpu); 232 + if (!hv_is_aligned_u64(vcpu->sysregs.vbar_el1, 2048u)) { 233 + vcpu->state = VCPU_PAUSED; 234 + return; 235 + } 236 + 237 + vcpu->sysregs.elr_el1 = vcpu->pc; 238 + vcpu->sysregs.spsr_el1 = vcpu->pstate; 239 + vcpu->sysregs.esr_el1 = 0u; 240 + vcpu->sysregs.far_el1 = 0u; 241 + vcpu->pc = vcpu->sysregs.vbar_el1 + current_el_spx_sync; 242 + vcpu->pstate = pstate_el1h_daif; 243 + vcpu->stats.injected_undefs++; 244 + 245 + __asm__ volatile( 246 + "msr elr_el1, %0\n" 247 + "msr spsr_el1, %1\n" 248 + "msr esr_el1, %2\n" 249 + "msr far_el1, %3\n" 250 + "isb\n" 251 + :: "r"(vcpu->sysregs.elr_el1), "r"(vcpu->sysregs.spsr_el1), 252 + "r"(vcpu->sysregs.esr_el1), "r"(vcpu->sysregs.far_el1) 253 + : "memory"); 254 + } 255 + 256 + void arch_halt(void) 257 + { 258 + for (;;) { 259 + __asm__ volatile("wfe"); 260 + } 261 + }
+29
arch/aarch64/entry.S
··· 1 + .section .text.entry, "ax" 2 + .global _start 3 + .type _start, %function 4 + _start: 5 + mov x19, x0 6 + mrs x0, mpidr_el1 7 + and x0, x0, #0xff 8 + cbz x0, 1f 9 + 0: wfe 10 + b 0b 11 + 12 + 1: ldr x0, =__boot_stack_top 13 + mov sp, x0 14 + ldr x0, =__bss_start 15 + ldr x1, =__bss_end 16 + 2: cmp x0, x1 17 + b.hs 3f 18 + str xzr, [x0], #8 19 + b 2b 20 + 3: mov x0, x19 21 + bl hv_main 22 + 4: wfe 23 + b 4b 24 + 25 + .section .bss.stack, "aw", %nobits 26 + .align 12 27 + __boot_stack: 28 + .skip 0x10000 29 + __boot_stack_top:
+8
arch/aarch64/guest_blob.S
··· 1 + .section .rodata.guest, "a" 2 + .balign 16 3 + .global __guest_image_start 4 + __guest_image_start: 5 + .incbin "build/guest/diag.bin" 6 + .global __guest_image_end 7 + __guest_image_end: 8 + .balign 16
+31
arch/aarch64/vcpu.S
··· 1 + .section .text, "ax" 2 + .global arch_vcpu_enter 3 + .type arch_vcpu_enter, %function 4 + arch_vcpu_enter: 5 + msr daifset, #0xf 6 + mov x30, x0 7 + ldr x1, [x30, #248] 8 + msr sp_el0, x1 9 + ldr x1, [x30, #256] 10 + msr sp_el1, x1 11 + ldr x1, [x30, #264] 12 + msr elr_el2, x1 13 + ldr x1, [x30, #272] 14 + msr spsr_el2, x1 15 + ldp x0, x1, [x30, #0] 16 + ldp x2, x3, [x30, #16] 17 + ldp x4, x5, [x30, #32] 18 + ldp x6, x7, [x30, #48] 19 + ldp x8, x9, [x30, #64] 20 + ldp x10, x11, [x30, #80] 21 + ldp x12, x13, [x30, #96] 22 + ldp x14, x15, [x30, #112] 23 + ldp x16, x17, [x30, #128] 24 + ldp x18, x19, [x30, #144] 25 + ldp x20, x21, [x30, #160] 26 + ldp x22, x23, [x30, #176] 27 + ldp x24, x25, [x30, #192] 28 + ldp x26, x27, [x30, #208] 29 + ldp x28, x29, [x30, #224] 30 + ldr x30, [x30, #240] 31 + eret
+123
arch/aarch64/vectors.S
··· 1 + .section .text.vectors, "ax" 2 + .align 11 3 + .global __vectors_el2 4 + __vectors_el2: 5 + b vector_current_sync 6 + .org __vectors_el2 + 0x080 7 + b vector_current_irq 8 + .org __vectors_el2 + 0x100 9 + b vector_current_fiq 10 + .org __vectors_el2 + 0x180 11 + b vector_current_serror 12 + .org __vectors_el2 + 0x200 13 + b vector_current_sync 14 + .org __vectors_el2 + 0x280 15 + b vector_current_irq 16 + .org __vectors_el2 + 0x300 17 + b vector_current_fiq 18 + .org __vectors_el2 + 0x380 19 + b vector_current_serror 20 + .org __vectors_el2 + 0x400 21 + b vector_lower_sync 22 + .org __vectors_el2 + 0x480 23 + b vector_lower_irq 24 + .org __vectors_el2 + 0x500 25 + b vector_lower_fiq 26 + .org __vectors_el2 + 0x580 27 + b vector_lower_serror 28 + .org __vectors_el2 + 0x600 29 + b vector_lower_sync 30 + .org __vectors_el2 + 0x680 31 + b vector_lower_irq 32 + .org __vectors_el2 + 0x700 33 + b vector_lower_fiq 34 + .org __vectors_el2 + 0x780 35 + b vector_lower_serror 36 + 37 + .macro SAVE_FRAME 38 + sub sp, sp, #304 39 + stp x0, x1, [sp, #0] 40 + stp x2, x3, [sp, #16] 41 + stp x4, x5, [sp, #32] 42 + stp x6, x7, [sp, #48] 43 + stp x8, x9, [sp, #64] 44 + stp x10, x11, [sp, #80] 45 + stp x12, x13, [sp, #96] 46 + stp x14, x15, [sp, #112] 47 + stp x16, x17, [sp, #128] 48 + stp x18, x19, [sp, #144] 49 + stp x20, x21, [sp, #160] 50 + stp x22, x23, [sp, #176] 51 + stp x24, x25, [sp, #192] 52 + stp x26, x27, [sp, #208] 53 + stp x28, x29, [sp, #224] 54 + str x30, [sp, #240] 55 + mrs x1, sp_el0 56 + str x1, [sp, #248] 57 + mrs x1, sp_el1 58 + str x1, [sp, #256] 59 + mrs x1, elr_el2 60 + str x1, [sp, #264] 61 + mrs x1, spsr_el2 62 + str x1, [sp, #272] 63 + mrs x1, esr_el2 64 + str x1, [sp, #280] 65 + mrs x1, far_el2 66 + str x1, [sp, #288] 67 + mrs x1, hpfar_el2 68 + str x1, [sp, #296] 69 + .endm 70 + 71 + .macro RESTORE_FRAME 72 + ldr x1, [sp, #248] 73 + msr sp_el0, x1 74 + ldr x1, [sp, #256] 75 + msr sp_el1, x1 76 + ldr x1, [sp, #264] 77 + msr elr_el2, x1 78 + ldr x1, [sp, #272] 79 + msr spsr_el2, x1 80 + ldp x0, x1, [sp, #0] 81 + ldp x2, x3, [sp, #16] 82 + ldp x4, x5, [sp, #32] 83 + ldp x6, x7, [sp, #48] 84 + ldp x8, x9, [sp, #64] 85 + ldp x10, x11, [sp, #80] 86 + ldp x12, x13, [sp, #96] 87 + ldp x14, x15, [sp, #112] 88 + ldp x16, x17, [sp, #128] 89 + ldp x18, x19, [sp, #144] 90 + ldp x20, x21, [sp, #160] 91 + ldp x22, x23, [sp, #176] 92 + ldp x24, x25, [sp, #192] 93 + ldp x26, x27, [sp, #208] 94 + ldp x28, x29, [sp, #224] 95 + ldr x30, [sp, #240] 96 + add sp, sp, #304 97 + .endm 98 + 99 + vector_lower_sync: 100 + SAVE_FRAME 101 + mov x0, sp 102 + bl trap_handle_lower_sync 103 + RESTORE_FRAME 104 + eret 105 + 106 + vector_lower_irq: 107 + vector_lower_fiq: 108 + vector_lower_serror: 109 + vector_current_irq: 110 + vector_current_fiq: 111 + vector_current_serror: 112 + SAVE_FRAME 113 + mov x0, sp 114 + bl trap_handle_irq 115 + RESTORE_FRAME 116 + eret 117 + 118 + vector_current_sync: 119 + SAVE_FRAME 120 + mov x0, sp 121 + bl trap_handle_current_sync 122 + RESTORE_FRAME 123 + eret
+138
core/el2_mmu.c
··· 1 + #include "hv/arch.h" 2 + #include "hv/config.h" 3 + #include "hv/el2_mmu.h" 4 + #include "hv/string.h" 5 + #include "hv/uart.h" 6 + 7 + #define EL2_PT_ENTRIES 512u 8 + #define EL2_L1_SHIFT 30u 9 + #define EL2_L2_SHIFT 21u 10 + #define EL2_L2_SIZE (1ull << EL2_L2_SHIFT) 11 + #define EL2_DESC_VALID HV_BIT(0) 12 + #define EL2_DESC_TABLE HV_BIT(1) 13 + #define EL2_DESC_AF HV_BIT(10) 14 + #define EL2_DESC_SH_INNER (3ull << 8u) 15 + #define EL2_DESC_ATTR_NORMAL (1ull << 2u) 16 + #define EL2_DESC_ATTR_DEVICE (0ull << 2u) 17 + #define EL2_DESC_PXN HV_BIT(53) 18 + #define EL2_DESC_UXN HV_BIT(54) 19 + #define EL2_MAIR ((0xffull << 8u) | 0x04ull) 20 + 21 + static uint64_t g_el2_l1[EL2_PT_ENTRIES] __attribute__((aligned(4096))); 22 + static uint64_t g_el2_l2[2][EL2_PT_ENTRIES] __attribute__((aligned(4096))); 23 + static uint64_t g_guard_low; 24 + static uint64_t g_guard_high; 25 + static bool g_ready; 26 + static bool g_enabled; 27 + 28 + static uint64_t block_desc(uint64_t pa, bool device, bool executable) 29 + { 30 + uint64_t desc = (pa & 0x0000ffffffe00000ull) | EL2_DESC_VALID | EL2_DESC_AF; 31 + desc |= device ? EL2_DESC_ATTR_DEVICE : (EL2_DESC_ATTR_NORMAL | EL2_DESC_SH_INNER); 32 + if (!executable) { 33 + desc |= EL2_DESC_PXN | EL2_DESC_UXN; 34 + } 35 + return desc; 36 + } 37 + 38 + static bool install_block(uint64_t pa, bool device, bool executable) 39 + { 40 + uint64_t l1 = (pa >> EL2_L1_SHIFT) & 0x1ffu; 41 + uint64_t l2 = (pa >> EL2_L2_SHIFT) & 0x1ffu; 42 + if (l1 >= HV_ARRAY_SIZE(g_el2_l2)) { 43 + return false; 44 + } 45 + g_el2_l1[l1] = ((uint64_t)(uintptr_t)g_el2_l2[l1] & 0x0000fffffffff000ull) | 46 + EL2_DESC_TABLE | EL2_DESC_VALID; 47 + g_el2_l2[l1][l2] = block_desc(pa, device, executable); 48 + return true; 49 + } 50 + 51 + void el2_mmu_init_plan(void) 52 + { 53 + uint64_t image_start = HV_ALIGN_DOWN((uint64_t)(uintptr_t)__image_start, EL2_L2_SIZE); 54 + uint64_t image_end = HV_ALIGN_UP((uint64_t)(uintptr_t)__image_end, EL2_L2_SIZE); 55 + 56 + hv_memset(g_el2_l1, 0, sizeof(g_el2_l1)); 57 + hv_memset(g_el2_l2, 0, sizeof(g_el2_l2)); 58 + g_guard_low = image_start >= EL2_L2_SIZE ? image_start - EL2_L2_SIZE : 0u; 59 + g_guard_high = image_end; 60 + g_ready = true; 61 + g_enabled = false; 62 + 63 + for (uint64_t pa = image_start; pa < image_end; pa += EL2_L2_SIZE) { 64 + if (!install_block(pa, false, true)) { 65 + g_ready = false; 66 + } 67 + } 68 + for (uint64_t pa = CONFIG_GUEST_RAM_BASE; 69 + pa < CONFIG_GUEST_RAM_BASE + CONFIG_GUEST_RAM_SIZE; 70 + pa += EL2_L2_SIZE) { 71 + if (!install_block(pa, false, false)) { 72 + g_ready = false; 73 + } 74 + } 75 + if (!install_block(CONFIG_UART_BASE, true, false) || 76 + !install_block(CONFIG_GICD_BASE, true, false) || 77 + !install_block(CONFIG_GICR_BASE, true, false) || 78 + !install_block(0x0a000000ull, true, false)) { 79 + g_ready = false; 80 + } 81 + } 82 + 83 + uint64_t el2_mmu_root(void) 84 + { 85 + return (uint64_t)(uintptr_t)g_el2_l1; 86 + } 87 + 88 + uint64_t el2_mmu_tcr(void) 89 + { 90 + uint64_t tcr = 0u; 91 + tcr |= 25ull; 92 + tcr |= (1ull << 8u); 93 + tcr |= (1ull << 10u); 94 + tcr |= (3ull << 12u); 95 + tcr |= (2ull << 16u); 96 + return tcr; 97 + } 98 + 99 + uint64_t el2_mmu_mair(void) 100 + { 101 + return EL2_MAIR; 102 + } 103 + 104 + void el2_mmu_enable(void) 105 + { 106 + if (!g_ready) { 107 + return; 108 + } 109 + arch_enable_el2_mmu(el2_mmu_root(), el2_mmu_tcr(), el2_mmu_mair()); 110 + g_enabled = true; 111 + } 112 + 113 + void el2_mmu_dump(void) 114 + { 115 + uart_puts("el2mmu planned="); 116 + uart_puts(g_ready ? "yes" : "no"); 117 + uart_puts(" enabled="); 118 + uart_puts(g_enabled ? "yes" : "no"); 119 + uart_puts(" root="); 120 + uart_put_hex64(el2_mmu_root()); 121 + uart_puts(" tcr="); 122 + uart_put_hex64(el2_mmu_tcr()); 123 + uart_puts(" mair="); 124 + uart_put_hex64(el2_mmu_mair()); 125 + uart_puts(" guard_low="); 126 + uart_put_hex64(g_guard_low); 127 + uart_puts(" guard_high="); 128 + uart_put_hex64(g_guard_high); 129 + uart_puts("\n"); 130 + } 131 + 132 + bool el2_mmu_selftest(void) 133 + { 134 + return g_ready && 135 + g_enabled && 136 + hv_is_aligned_u64(el2_mmu_root(), CONFIG_PAGE_SIZE) && 137 + (g_guard_high == 0u || (g_guard_high & (EL2_L2_SIZE - 1u)) == 0u); 138 + }
+696
core/fdt.c
··· 1 + #include "hv/fdt.h" 2 + #include "hv/string.h" 3 + #include "hv/uart.h" 4 + 5 + #define FDT_MAGIC 0xd00dfeedu 6 + #define FDT_BEGIN_NODE 1u 7 + #define FDT_END_NODE 2u 8 + #define FDT_PROP 3u 9 + #define FDT_NOP 4u 10 + #define FDT_END 9u 11 + #define FDT_MAX_SIZE 0x00200000u 12 + #define FDT_MAX_DEPTH 16u 13 + 14 + struct fdt_header_view { 15 + uint32_t totalsize; 16 + uint32_t off_dt_struct; 17 + uint32_t off_dt_strings; 18 + uint32_t size_dt_strings; 19 + uint32_t size_dt_struct; 20 + }; 21 + 22 + struct fdt_property_ref { 23 + uint8_t *value; 24 + uint32_t len; 25 + }; 26 + 27 + static uint32_t be32(const void *ptr) 28 + { 29 + const uint8_t *p = (const uint8_t *)ptr; 30 + return ((uint32_t)p[0] << 24u) | ((uint32_t)p[1] << 16u) | 31 + ((uint32_t)p[2] << 8u) | (uint32_t)p[3]; 32 + } 33 + 34 + static uint64_t read_cells(const uint32_t *cells, uint32_t count) 35 + { 36 + uint64_t value = 0; 37 + for (uint32_t i = 0; i < count; i++) { 38 + value = (value << 32u) | (uint64_t)be32(&cells[i]); 39 + } 40 + return value; 41 + } 42 + 43 + static uint32_t align4_u32(uint32_t value) 44 + { 45 + return (value + 3u) & ~3u; 46 + } 47 + 48 + static const uint8_t *align4(const uint8_t *p) 49 + { 50 + uintptr_t v = (uintptr_t)p; 51 + v = (v + 3u) & ~(uintptr_t)3u; 52 + return (const uint8_t *)v; 53 + } 54 + 55 + static bool read_header(const uint8_t *fdt, struct fdt_header_view *h) 56 + { 57 + if (fdt == (const uint8_t *)0 || h == (struct fdt_header_view *)0) { 58 + return false; 59 + } 60 + if (be32(fdt) != FDT_MAGIC) { 61 + return false; 62 + } 63 + 64 + h->totalsize = be32(fdt + 4u); 65 + h->off_dt_struct = be32(fdt + 8u); 66 + h->off_dt_strings = be32(fdt + 12u); 67 + h->size_dt_strings = be32(fdt + 32u); 68 + h->size_dt_struct = be32(fdt + 36u); 69 + if (h->totalsize < 40u || h->totalsize > FDT_MAX_SIZE) { 70 + return false; 71 + } 72 + if (h->off_dt_struct >= h->totalsize || h->off_dt_strings >= h->totalsize) { 73 + return false; 74 + } 75 + if (h->size_dt_struct > h->totalsize - h->off_dt_struct || 76 + h->size_dt_strings > h->totalsize - h->off_dt_strings) { 77 + return false; 78 + } 79 + return true; 80 + } 81 + 82 + static bool string_in_block(const char *s, const uint8_t *start, const uint8_t *end) 83 + { 84 + const uint8_t *p = (const uint8_t *)s; 85 + if (p < start || p >= end) { 86 + return false; 87 + } 88 + while (p < end) { 89 + if (*p == 0u) { 90 + return true; 91 + } 92 + p++; 93 + } 94 + return false; 95 + } 96 + 97 + static bool node_name_is_memory(const char *name) 98 + { 99 + return hv_strncmp(name, "memory", 6u) == 0; 100 + } 101 + 102 + static bool find_property(uint64_t fdt_pa, const char *node_name, 103 + const char *prop_name, struct fdt_property_ref *ref) 104 + { 105 + uint8_t *fdt = (uint8_t *)(uintptr_t)fdt_pa; 106 + struct fdt_header_view h; 107 + uint8_t *struct_p; 108 + uint8_t *struct_end; 109 + const uint8_t *strings; 110 + const uint8_t *strings_end; 111 + bool wanted_node[FDT_MAX_DEPTH]; 112 + uint32_t depth = 0; 113 + 114 + if (node_name == (const char *)0 || prop_name == (const char *)0 || 115 + ref == (struct fdt_property_ref *)0 || !read_header(fdt, &h)) { 116 + return false; 117 + } 118 + hv_memset(wanted_node, 0, sizeof(wanted_node)); 119 + struct_p = fdt + h.off_dt_struct; 120 + struct_end = struct_p + h.size_dt_struct; 121 + strings = fdt + h.off_dt_strings; 122 + strings_end = strings + h.size_dt_strings; 123 + 124 + while (struct_p + 4u <= struct_end) { 125 + uint32_t token = be32(struct_p); 126 + struct_p += 4u; 127 + if (token == FDT_BEGIN_NODE) { 128 + const char *name = (const char *)struct_p; 129 + if (!string_in_block(name, struct_p, struct_end) || depth + 1u >= FDT_MAX_DEPTH) { 130 + return false; 131 + } 132 + wanted_node[depth] = hv_strcmp(name, node_name) == 0; 133 + while (struct_p < struct_end && *struct_p != 0u) { 134 + struct_p++; 135 + } 136 + if (struct_p >= struct_end) { 137 + return false; 138 + } 139 + struct_p = (uint8_t *)align4(struct_p + 1u); 140 + depth++; 141 + } else if (token == FDT_END_NODE) { 142 + if (depth == 0u) { 143 + return false; 144 + } 145 + depth--; 146 + wanted_node[depth] = false; 147 + } else if (token == FDT_PROP) { 148 + uint32_t len; 149 + uint32_t nameoff; 150 + const char *name; 151 + if (struct_p + 8u > struct_end) { 152 + return false; 153 + } 154 + len = be32(struct_p); 155 + nameoff = be32(struct_p + 4u); 156 + struct_p += 8u; 157 + if (nameoff >= h.size_dt_strings || len > (uint32_t)(struct_end - struct_p)) { 158 + return false; 159 + } 160 + name = (const char *)(strings + nameoff); 161 + if (!string_in_block(name, strings, strings_end)) { 162 + return false; 163 + } 164 + if (depth != 0u && wanted_node[depth - 1u] && hv_strcmp(name, prop_name) == 0) { 165 + ref->value = struct_p; 166 + ref->len = len; 167 + return true; 168 + } 169 + struct_p = (uint8_t *)align4(struct_p + len); 170 + } else if (token == FDT_NOP) { 171 + continue; 172 + } else if (token == FDT_END) { 173 + return false; 174 + } else { 175 + return false; 176 + } 177 + } 178 + return false; 179 + } 180 + 181 + static bool find_memory_property(uint64_t fdt_pa, const char *prop_name, 182 + struct fdt_property_ref *ref) 183 + { 184 + uint8_t *fdt = (uint8_t *)(uintptr_t)fdt_pa; 185 + struct fdt_header_view h; 186 + uint8_t *struct_p; 187 + uint8_t *struct_end; 188 + const uint8_t *strings; 189 + const uint8_t *strings_end; 190 + bool memory_node[FDT_MAX_DEPTH]; 191 + uint32_t depth = 0; 192 + 193 + if (prop_name == (const char *)0 || ref == (struct fdt_property_ref *)0 || 194 + !read_header(fdt, &h)) { 195 + return false; 196 + } 197 + hv_memset(memory_node, 0, sizeof(memory_node)); 198 + struct_p = fdt + h.off_dt_struct; 199 + struct_end = struct_p + h.size_dt_struct; 200 + strings = fdt + h.off_dt_strings; 201 + strings_end = strings + h.size_dt_strings; 202 + 203 + while (struct_p + 4u <= struct_end) { 204 + uint32_t token = be32(struct_p); 205 + struct_p += 4u; 206 + if (token == FDT_BEGIN_NODE) { 207 + const char *name = (const char *)struct_p; 208 + if (!string_in_block(name, struct_p, struct_end) || depth + 1u >= FDT_MAX_DEPTH) { 209 + return false; 210 + } 211 + memory_node[depth] = node_name_is_memory(name); 212 + while (struct_p < struct_end && *struct_p != 0u) { 213 + struct_p++; 214 + } 215 + if (struct_p >= struct_end) { 216 + return false; 217 + } 218 + struct_p = (uint8_t *)align4(struct_p + 1u); 219 + depth++; 220 + } else if (token == FDT_END_NODE) { 221 + if (depth == 0u) { 222 + return false; 223 + } 224 + depth--; 225 + memory_node[depth] = false; 226 + } else if (token == FDT_PROP) { 227 + uint32_t len; 228 + uint32_t nameoff; 229 + const char *name; 230 + if (struct_p + 8u > struct_end) { 231 + return false; 232 + } 233 + len = be32(struct_p); 234 + nameoff = be32(struct_p + 4u); 235 + struct_p += 8u; 236 + if (nameoff >= h.size_dt_strings || len > (uint32_t)(struct_end - struct_p)) { 237 + return false; 238 + } 239 + name = (const char *)(strings + nameoff); 240 + if (!string_in_block(name, strings, strings_end)) { 241 + return false; 242 + } 243 + if (depth != 0u && memory_node[depth - 1u] && 244 + hv_strcmp(name, prop_name) == 0) { 245 + ref->value = struct_p; 246 + ref->len = len; 247 + return true; 248 + } 249 + struct_p = (uint8_t *)align4(struct_p + len); 250 + } else if (token == FDT_NOP) { 251 + continue; 252 + } else if (token == FDT_END) { 253 + return false; 254 + } else { 255 + return false; 256 + } 257 + } 258 + return false; 259 + } 260 + 261 + static void write_be64(uint8_t *p, uint64_t value) 262 + { 263 + for (uint32_t i = 0; i < 8u; i++) { 264 + p[i] = (uint8_t)(value >> (56u - (i * 8u))); 265 + } 266 + } 267 + 268 + static void write_be32(uint8_t *p, uint32_t value) 269 + { 270 + p[0] = (uint8_t)(value >> 24u); 271 + p[1] = (uint8_t)(value >> 16u); 272 + p[2] = (uint8_t)(value >> 8u); 273 + p[3] = (uint8_t)value; 274 + } 275 + 276 + static bool patch_string_prop(uint64_t fdt_pa, const char *node, const char *prop, 277 + const char *value) 278 + { 279 + struct fdt_property_ref ref; 280 + size_t len; 281 + if (value == (const char *)0 || !find_property(fdt_pa, node, prop, &ref)) { 282 + return false; 283 + } 284 + len = hv_strlen(value) + 1u; 285 + if (len > ref.len) { 286 + return false; 287 + } 288 + hv_memset(ref.value, 0, ref.len); 289 + hv_memcpy(ref.value, value, len); 290 + return true; 291 + } 292 + 293 + static bool repurpose_string_prop(uint64_t fdt_pa, const char *node_name, 294 + const char *prop_name, const char *value) 295 + { 296 + uint8_t *fdt = (uint8_t *)(uintptr_t)fdt_pa; 297 + struct fdt_header_view h; 298 + uint8_t *struct_p; 299 + uint8_t *struct_end; 300 + uint8_t *strings; 301 + const uint8_t *strings_end; 302 + bool wanted_node[FDT_MAX_DEPTH]; 303 + uint32_t depth = 0; 304 + size_t value_len; 305 + size_t prop_len; 306 + 307 + if (node_name == (const char *)0 || prop_name == (const char *)0 || 308 + value == (const char *)0 || !read_header(fdt, &h)) { 309 + return false; 310 + } 311 + 312 + value_len = hv_strlen(value) + 1u; 313 + prop_len = hv_strlen(prop_name) + 1u; 314 + hv_memset(wanted_node, 0, sizeof(wanted_node)); 315 + struct_p = fdt + h.off_dt_struct; 316 + struct_end = struct_p + h.size_dt_struct; 317 + strings = fdt + h.off_dt_strings; 318 + strings_end = strings + h.size_dt_strings; 319 + 320 + while (struct_p + 4u <= struct_end) { 321 + uint32_t token = be32(struct_p); 322 + struct_p += 4u; 323 + if (token == FDT_BEGIN_NODE) { 324 + const char *name = (const char *)struct_p; 325 + if (!string_in_block(name, struct_p, struct_end) || depth + 1u >= FDT_MAX_DEPTH) { 326 + return false; 327 + } 328 + wanted_node[depth] = hv_strcmp(name, node_name) == 0; 329 + while (struct_p < struct_end && *struct_p != 0u) { 330 + struct_p++; 331 + } 332 + if (struct_p >= struct_end) { 333 + return false; 334 + } 335 + struct_p = (uint8_t *)align4(struct_p + 1u); 336 + depth++; 337 + } else if (token == FDT_END_NODE) { 338 + if (depth == 0u) { 339 + return false; 340 + } 341 + depth--; 342 + wanted_node[depth] = false; 343 + } else if (token == FDT_PROP) { 344 + uint32_t len; 345 + uint32_t nameoff; 346 + char *name; 347 + if (struct_p + 8u > struct_end) { 348 + return false; 349 + } 350 + len = be32(struct_p); 351 + nameoff = be32(struct_p + 4u); 352 + struct_p += 8u; 353 + if (nameoff >= h.size_dt_strings || len > (uint32_t)(struct_end - struct_p)) { 354 + return false; 355 + } 356 + name = (char *)(strings + nameoff); 357 + if (!string_in_block(name, strings, strings_end)) { 358 + return false; 359 + } 360 + if (depth != 0u && wanted_node[depth - 1u] && 361 + len >= value_len && hv_strlen(name) + 1u >= prop_len) { 362 + hv_memset(name, 0, hv_strlen(name) + 1u); 363 + hv_memcpy(name, prop_name, prop_len); 364 + hv_memset(struct_p, 0, len); 365 + hv_memcpy(struct_p, value, value_len); 366 + return true; 367 + } 368 + struct_p = (uint8_t *)align4(struct_p + len); 369 + } else if (token == FDT_NOP) { 370 + continue; 371 + } else if (token == FDT_END) { 372 + return false; 373 + } else { 374 + return false; 375 + } 376 + } 377 + return false; 378 + } 379 + 380 + static bool patch_u64_prop(uint64_t fdt_pa, const char *node, const char *prop, 381 + uint64_t value) 382 + { 383 + struct fdt_property_ref ref; 384 + if (!find_property(fdt_pa, node, prop, &ref) || ref.len < 8u) { 385 + return false; 386 + } 387 + write_be64(ref.value, value); 388 + return true; 389 + } 390 + 391 + static bool find_node_end(uint64_t fdt_pa, const char *node_name, uint8_t **out) 392 + { 393 + uint8_t *fdt = (uint8_t *)(uintptr_t)fdt_pa; 394 + struct fdt_header_view h; 395 + uint8_t *struct_p; 396 + uint8_t *struct_end; 397 + bool wanted_node[FDT_MAX_DEPTH]; 398 + uint32_t depth = 0; 399 + 400 + if (node_name == (const char *)0 || out == (uint8_t **)0 || !read_header(fdt, &h)) { 401 + return false; 402 + } 403 + hv_memset(wanted_node, 0, sizeof(wanted_node)); 404 + struct_p = fdt + h.off_dt_struct; 405 + struct_end = struct_p + h.size_dt_struct; 406 + 407 + while (struct_p + 4u <= struct_end) { 408 + uint8_t *token_p = struct_p; 409 + uint32_t token = be32(struct_p); 410 + struct_p += 4u; 411 + if (token == FDT_BEGIN_NODE) { 412 + const char *name = (const char *)struct_p; 413 + if (!string_in_block(name, struct_p, struct_end) || depth + 1u >= FDT_MAX_DEPTH) { 414 + return false; 415 + } 416 + wanted_node[depth] = hv_strcmp(name, node_name) == 0; 417 + while (struct_p < struct_end && *struct_p != 0u) { 418 + struct_p++; 419 + } 420 + if (struct_p >= struct_end) { 421 + return false; 422 + } 423 + struct_p = (uint8_t *)align4(struct_p + 1u); 424 + depth++; 425 + } else if (token == FDT_END_NODE) { 426 + if (depth == 0u) { 427 + return false; 428 + } 429 + if (wanted_node[depth - 1u]) { 430 + *out = token_p; 431 + return true; 432 + } 433 + depth--; 434 + wanted_node[depth] = false; 435 + } else if (token == FDT_PROP) { 436 + uint32_t len; 437 + if (struct_p + 8u > struct_end) { 438 + return false; 439 + } 440 + len = be32(struct_p); 441 + struct_p += 8u; 442 + if (len > (uint32_t)(struct_end - struct_p)) { 443 + return false; 444 + } 445 + struct_p = (uint8_t *)align4(struct_p + len); 446 + } else if (token == FDT_NOP) { 447 + continue; 448 + } else if (token == FDT_END) { 449 + return false; 450 + } else { 451 + return false; 452 + } 453 + } 454 + return false; 455 + } 456 + 457 + static bool append_chosen_prop(uint64_t fdt_pa, uint64_t max_size, const char *prop, 458 + const uint8_t *value, uint32_t len) 459 + { 460 + uint8_t *fdt = (uint8_t *)(uintptr_t)fdt_pa; 461 + struct fdt_header_view h; 462 + uint8_t *insert; 463 + uint8_t *strings; 464 + uint32_t prop_name_len; 465 + uint32_t value_len; 466 + uint32_t record_size; 467 + uint32_t growth; 468 + uint32_t nameoff; 469 + 470 + if (prop == (const char *)0 || value == (const uint8_t *)0 || 471 + max_size > FDT_MAX_SIZE || !read_header(fdt, &h) || 472 + !find_node_end(fdt_pa, "chosen", &insert)) { 473 + return false; 474 + } 475 + 476 + prop_name_len = (uint32_t)hv_strlen(prop) + 1u; 477 + value_len = align4_u32(len); 478 + record_size = 12u + value_len; 479 + growth = record_size + prop_name_len; 480 + if (max_size < h.totalsize || growth > max_size - h.totalsize) { 481 + return false; 482 + } 483 + 484 + nameoff = h.size_dt_strings; 485 + hv_memmove(insert + record_size, insert, h.totalsize - (uint32_t)(insert - fdt)); 486 + write_be32(insert, FDT_PROP); 487 + write_be32(insert + 4u, len); 488 + write_be32(insert + 8u, nameoff); 489 + hv_memset(insert + 12u, 0, value_len); 490 + hv_memcpy(insert + 12u, value, len); 491 + 492 + strings = fdt + h.off_dt_strings + record_size; 493 + hv_memcpy(strings + h.size_dt_strings, prop, prop_name_len); 494 + write_be32(fdt + 4u, h.totalsize + growth); 495 + write_be32(fdt + 12u, h.off_dt_strings + record_size); 496 + write_be32(fdt + 32u, h.size_dt_strings + prop_name_len); 497 + write_be32(fdt + 36u, h.size_dt_struct + record_size); 498 + return true; 499 + } 500 + 501 + static bool ensure_chosen_u64(uint64_t fdt_pa, uint64_t max_size, const char *prop, 502 + uint64_t value) 503 + { 504 + uint8_t buf[8]; 505 + if (patch_u64_prop(fdt_pa, "chosen", prop, value)) { 506 + return true; 507 + } 508 + write_be64(buf, value); 509 + return append_chosen_prop(fdt_pa, max_size, prop, buf, sizeof(buf)); 510 + } 511 + 512 + bool fdt_patch_memory(uint64_t fdt_pa, uint64_t base, uint64_t size) 513 + { 514 + struct fdt_property_ref ref; 515 + 516 + if (size == 0u || !find_memory_property(fdt_pa, "reg", &ref) || ref.len < 16u) { 517 + return false; 518 + } 519 + 520 + write_be64(ref.value, base); 521 + write_be64(ref.value + 8u, size); 522 + if (ref.len > 16u) { 523 + hv_memset(ref.value + 16u, 0, ref.len - 16u); 524 + } 525 + return true; 526 + } 527 + 528 + bool fdt_probe(uint64_t fdt_pa, struct fdt_platform_info *info) 529 + { 530 + const uint8_t *fdt = (const uint8_t *)(uintptr_t)fdt_pa; 531 + struct fdt_header_view h; 532 + const uint8_t *struct_p; 533 + const uint8_t *struct_end; 534 + const uint8_t *strings; 535 + const uint8_t *strings_end; 536 + bool memory_node[FDT_MAX_DEPTH]; 537 + bool chosen_node[FDT_MAX_DEPTH]; 538 + uint32_t depth = 0; 539 + uint32_t address_cells = 2u; 540 + uint32_t size_cells = 2u; 541 + 542 + if (info == (struct fdt_platform_info *)0) { 543 + return false; 544 + } 545 + hv_memset(info, 0, sizeof(*info)); 546 + if (fdt_pa == 0u || !hv_is_aligned_u64(fdt_pa, 8u)) { 547 + return false; 548 + } 549 + if (!read_header(fdt, &h)) { 550 + return false; 551 + } 552 + 553 + hv_memset(memory_node, 0, sizeof(memory_node)); 554 + hv_memset(chosen_node, 0, sizeof(chosen_node)); 555 + struct_p = fdt + h.off_dt_struct; 556 + struct_end = struct_p + h.size_dt_struct; 557 + strings = fdt + h.off_dt_strings; 558 + strings_end = strings + h.size_dt_strings; 559 + 560 + while (struct_p + 4u <= struct_end) { 561 + uint32_t token = be32(struct_p); 562 + struct_p += 4u; 563 + 564 + if (token == FDT_BEGIN_NODE) { 565 + const char *name = (const char *)struct_p; 566 + if (!string_in_block(name, struct_p, struct_end) || depth + 1u >= FDT_MAX_DEPTH) { 567 + return false; 568 + } 569 + memory_node[depth] = node_name_is_memory(name); 570 + chosen_node[depth] = hv_strcmp(name, "chosen") == 0; 571 + while (struct_p < struct_end && *struct_p != 0u) { 572 + struct_p++; 573 + } 574 + if (struct_p >= struct_end) { 575 + return false; 576 + } 577 + struct_p = align4(struct_p + 1u); 578 + depth++; 579 + } else if (token == FDT_END_NODE) { 580 + if (depth == 0u) { 581 + return false; 582 + } 583 + depth--; 584 + memory_node[depth] = false; 585 + chosen_node[depth] = false; 586 + } else if (token == FDT_PROP) { 587 + uint32_t len; 588 + uint32_t nameoff; 589 + const char *prop_name; 590 + const uint8_t *value; 591 + 592 + if (struct_p + 8u > struct_end) { 593 + return false; 594 + } 595 + len = be32(struct_p); 596 + nameoff = be32(struct_p + 4u); 597 + struct_p += 8u; 598 + if (nameoff >= h.size_dt_strings || len > (uint32_t)(struct_end - struct_p)) { 599 + return false; 600 + } 601 + prop_name = (const char *)(strings + nameoff); 602 + if (!string_in_block(prop_name, strings, strings_end)) { 603 + return false; 604 + } 605 + value = struct_p; 606 + 607 + if (depth == 1u && hv_strcmp(prop_name, "#address-cells") == 0 && len >= 4u) { 608 + address_cells = be32(value); 609 + } else if (depth == 1u && hv_strcmp(prop_name, "#size-cells") == 0 && len >= 4u) { 610 + size_cells = be32(value); 611 + } else if (depth != 0u && hv_strcmp(prop_name, "device_type") == 0 && 612 + len >= 7u && hv_strcmp((const char *)value, "memory") == 0) { 613 + memory_node[depth - 1u] = true; 614 + } else if (depth != 0u && memory_node[depth - 1u] && 615 + hv_strcmp(prop_name, "reg") == 0 && 616 + address_cells <= 2u && size_cells <= 2u && 617 + len >= (address_cells + size_cells) * 4u) { 618 + const uint32_t *cells = (const uint32_t *)value; 619 + info->memory_base = read_cells(cells, address_cells); 620 + info->memory_size = read_cells(cells + address_cells, size_cells); 621 + } else if (depth != 0u && chosen_node[depth - 1u]) { 622 + if (hv_strcmp(prop_name, "bootargs") == 0 && len != 0u) { 623 + info->bootargs = (const char *)value; 624 + } else if (hv_strcmp(prop_name, "linux,initrd-start") == 0 && len >= 4u) { 625 + info->initrd_start = read_cells((const uint32_t *)value, len >= 8u ? 2u : 1u); 626 + } else if (hv_strcmp(prop_name, "linux,initrd-end") == 0 && len >= 4u) { 627 + info->initrd_end = read_cells((const uint32_t *)value, len >= 8u ? 2u : 1u); 628 + } 629 + } 630 + 631 + struct_p = align4(struct_p + len); 632 + } else if (token == FDT_NOP) { 633 + continue; 634 + } else if (token == FDT_END) { 635 + info->valid = true; 636 + info->fdt_pa = fdt_pa; 637 + info->size = h.totalsize; 638 + return true; 639 + } else { 640 + return false; 641 + } 642 + } 643 + 644 + return false; 645 + } 646 + 647 + bool fdt_patch_linux_guest(uint64_t fdt_pa, uint64_t max_size, const char *bootargs, 648 + uint64_t initrd_start, uint64_t initrd_end) 649 + { 650 + struct fdt_platform_info info; 651 + bool ok = true; 652 + 653 + if (!fdt_probe(fdt_pa, &info)) { 654 + return false; 655 + } 656 + if (bootargs != (const char *)0) { 657 + ok = patch_string_prop(fdt_pa, "chosen", "bootargs", bootargs) || 658 + repurpose_string_prop(fdt_pa, "chosen", "bootargs", bootargs) || 659 + append_chosen_prop(fdt_pa, max_size, "bootargs", 660 + (const uint8_t *)bootargs, 661 + (uint32_t)hv_strlen(bootargs) + 1u); 662 + } 663 + if (initrd_start != 0u && initrd_end > initrd_start) { 664 + ok = ok && ensure_chosen_u64(fdt_pa, max_size, "linux,initrd-start", initrd_start); 665 + ok = ok && ensure_chosen_u64(fdt_pa, max_size, "linux,initrd-end", initrd_end); 666 + } 667 + return ok; 668 + } 669 + 670 + void fdt_dump(const struct fdt_platform_info *info) 671 + { 672 + if (info == (const struct fdt_platform_info *)0 || !info->valid) { 673 + uart_puts("fdt unavailable\n"); 674 + return; 675 + } 676 + uart_puts("fdt pa="); 677 + uart_put_hex64(info->fdt_pa); 678 + uart_puts(" size="); 679 + uart_put_dec64(info->size); 680 + uart_puts(" memory="); 681 + uart_put_hex64(info->memory_base); 682 + uart_puts("+"); 683 + uart_put_hex64(info->memory_size); 684 + if (info->initrd_start != 0u || info->initrd_end != 0u) { 685 + uart_puts(" initrd="); 686 + uart_put_hex64(info->initrd_start); 687 + uart_puts("-"); 688 + uart_put_hex64(info->initrd_end); 689 + } 690 + uart_puts("\n"); 691 + if (info->bootargs != (const char *)0) { 692 + uart_puts("bootargs "); 693 + uart_puts(info->bootargs); 694 + uart_puts("\n"); 695 + } 696 + }
+146
core/guest_loader.c
··· 1 + #include "hv/arch.h" 2 + #include "hv/config.h" 3 + #include "hv/fdt.h" 4 + #include "hv/guest_loader.h" 5 + #include "hv/string.h" 6 + 7 + #define ARM64_IMAGE_MAGIC 0x644d5241u 8 + #define ARM64_IMAGE_MAGIC_OFFSET 0x38u 9 + #define ARM64_IMAGE_TEXT_OFFSET 0x08u 10 + #define ARM64_IMAGE_SIZE_OFFSET 0x10u 11 + #define LINUX_BOOTARGS "console=ttyAMA0 earlycon" 12 + 13 + static uint32_t read_le32(const uint8_t *p) 14 + { 15 + return (uint32_t)p[0] | ((uint32_t)p[1] << 8u) | 16 + ((uint32_t)p[2] << 16u) | ((uint32_t)p[3] << 24u); 17 + } 18 + 19 + static uint64_t read_le64(const uint8_t *p) 20 + { 21 + return (uint64_t)read_le32(p) | ((uint64_t)read_le32(p + 4u) << 32u); 22 + } 23 + 24 + bool guest_load_diagnostic(struct guest_image_info *info) 25 + { 26 + uintptr_t guest_start; 27 + uintptr_t guest_end; 28 + uint64_t guest_size; 29 + 30 + if (info == (struct guest_image_info *)0) { 31 + return false; 32 + } 33 + 34 + guest_start = (uintptr_t)__guest_image_start; 35 + guest_end = (uintptr_t)__guest_image_end; 36 + guest_size = (uint64_t)(guest_end - guest_start); 37 + 38 + if (guest_size == 0u || guest_size > CONFIG_GUEST_RAM_SIZE) { 39 + return false; 40 + } 41 + if (hv_range_overlaps(CONFIG_GUEST_RAM_BASE, CONFIG_GUEST_RAM_SIZE, 42 + (uint64_t)(uintptr_t)__image_start, 43 + (uint64_t)((uintptr_t)__image_end - (uintptr_t)__image_start))) { 44 + return false; 45 + } 46 + 47 + hv_memset((void *)(uintptr_t)CONFIG_GUEST_RAM_BASE, 0, CONFIG_GUEST_RAM_SIZE); 48 + hv_memcpy((void *)(uintptr_t)CONFIG_GUEST_RAM_BASE, (const void *)guest_start, (size_t)guest_size); 49 + 50 + info->kind = GUEST_PAYLOAD_DIAGNOSTIC; 51 + info->ipa_entry = CONFIG_GUEST_ENTRY; 52 + info->ipa_load = CONFIG_GUEST_IPA_BASE; 53 + info->size = guest_size; 54 + info->dtb_ipa = 0u; 55 + info->initrd_ipa = 0u; 56 + info->initrd_size = 0u; 57 + return true; 58 + } 59 + 60 + bool guest_validate_linux_image_header(const void *image, uint64_t size, 61 + struct guest_image_info *info) 62 + { 63 + const uint8_t *p = (const uint8_t *)image; 64 + uint64_t text_offset; 65 + uint64_t image_size; 66 + uint64_t entry; 67 + 68 + if (image == (const void *)0 || info == (struct guest_image_info *)0 || size < 0x40u) { 69 + return false; 70 + } 71 + if (read_le32(p + ARM64_IMAGE_MAGIC_OFFSET) != ARM64_IMAGE_MAGIC) { 72 + return false; 73 + } 74 + 75 + text_offset = read_le64(p + ARM64_IMAGE_TEXT_OFFSET); 76 + image_size = read_le64(p + ARM64_IMAGE_SIZE_OFFSET); 77 + if (image_size == 0u) { 78 + image_size = size; 79 + } 80 + if (image_size > CONFIG_GUEST_RAM_SIZE || text_offset >= CONFIG_GUEST_RAM_SIZE) { 81 + return false; 82 + } 83 + if (hv_add_overflow_u64(CONFIG_GUEST_IPA_BASE, text_offset, &entry)) { 84 + return false; 85 + } 86 + 87 + info->kind = GUEST_PAYLOAD_LINUX_IMAGE; 88 + info->ipa_entry = entry; 89 + info->ipa_load = entry; 90 + info->size = image_size; 91 + info->dtb_ipa = 0u; 92 + info->initrd_ipa = 0u; 93 + info->initrd_size = 0u; 94 + return true; 95 + } 96 + 97 + bool guest_load_external_linux(struct guest_image_info *info, paddr_t image_pa, 98 + uint64_t image_max_size, paddr_t dtb_pa, 99 + uint64_t dtb_max_size, paddr_t initrd_pa, 100 + uint64_t initrd_size) 101 + { 102 + struct fdt_platform_info dtb_info; 103 + uint64_t initrd_start = 0u; 104 + uint64_t initrd_end = 0u; 105 + 106 + if (info == (struct guest_image_info *)0 || 107 + image_max_size == 0u || image_max_size > CONFIG_GUEST_RAM_SIZE) { 108 + return false; 109 + } 110 + if (!guest_validate_linux_image_header((const void *)(uintptr_t)image_pa, 111 + image_max_size, info)) { 112 + return false; 113 + } 114 + if (!fdt_probe(dtb_pa, &dtb_info) || dtb_info.size > dtb_max_size) { 115 + return false; 116 + } 117 + if (initrd_size != 0u) { 118 + if (initrd_size > CONFIG_LINUX_INITRD_MAX_SIZE || 119 + initrd_pa != CONFIG_LINUX_INITRD_PA || 120 + hv_add_overflow_u64(CONFIG_LINUX_INITRD_IPA, initrd_size, &initrd_end) || 121 + initrd_end > CONFIG_LINUX_DTB_IPA) { 122 + return false; 123 + } 124 + initrd_start = CONFIG_LINUX_INITRD_IPA; 125 + } 126 + if (!fdt_patch_memory(dtb_pa, CONFIG_GUEST_IPA_BASE, CONFIG_GUEST_RAM_SIZE)) { 127 + return false; 128 + } 129 + if (!fdt_patch_linux_guest(dtb_pa, dtb_max_size, LINUX_BOOTARGS, 130 + initrd_start, initrd_end)) { 131 + return false; 132 + } 133 + if (!fdt_probe(dtb_pa, &dtb_info)) { 134 + return false; 135 + } 136 + fdt_dump(&dtb_info); 137 + 138 + info->kind = GUEST_PAYLOAD_LINUX_IMAGE; 139 + info->ipa_load = CONFIG_GUEST_IPA_BASE; 140 + info->dtb_ipa = CONFIG_LINUX_DTB_IPA; 141 + if (initrd_start != 0u && initrd_end > initrd_start) { 142 + info->initrd_ipa = initrd_start; 143 + info->initrd_size = initrd_end - initrd_start; 144 + } 145 + return true; 146 + }
+164
core/log.c
··· 1 + #include "hv/arch.h" 2 + #include "hv/config.h" 3 + #include "hv/log.h" 4 + #include "hv/string.h" 5 + #include "hv/timer.h" 6 + #include "hv/uart.h" 7 + 8 + struct log_ring { 9 + struct log_event events[CONFIG_LOG_CAPACITY]; 10 + volatile unsigned lock; 11 + uint64_t next_seq; 12 + uint32_t write_index; 13 + bool wrapped; 14 + }; 15 + 16 + static struct log_ring g_log; 17 + static struct log_ring g_log_selftest_backup; 18 + 19 + static void lock_log(void) 20 + { 21 + while (__atomic_test_and_set(&g_log.lock, __ATOMIC_ACQUIRE)) { 22 + } 23 + } 24 + 25 + static void unlock_log(void) 26 + { 27 + __atomic_clear(&g_log.lock, __ATOMIC_RELEASE); 28 + } 29 + 30 + void log_init(void) 31 + { 32 + hv_memset(&g_log, 0, sizeof(g_log)); 33 + } 34 + 35 + void log_event_commit(const struct log_event *event) 36 + { 37 + if (event == (const struct log_event *)0) { 38 + return; 39 + } 40 + 41 + uint64_t irq_flags = arch_irq_save(); 42 + lock_log(); 43 + struct log_event *slot = &g_log.events[g_log.write_index]; 44 + *slot = *event; 45 + slot->seq = g_log.next_seq++; 46 + slot->cpu = arch_mpidr_el1() & 0xffu; 47 + slot->timestamp = timer_now(); 48 + g_log.write_index++; 49 + if (g_log.write_index == CONFIG_LOG_CAPACITY) { 50 + g_log.write_index = 0; 51 + g_log.wrapped = true; 52 + } 53 + unlock_log(); 54 + arch_irq_restore(irq_flags); 55 + } 56 + 57 + void log_simple(enum log_kind kind, uint32_t reason, uint64_t a, uint64_t b) 58 + { 59 + struct log_event ev; 60 + hv_memset(&ev, 0, sizeof(ev)); 61 + ev.kind = (uint32_t)kind; 62 + ev.reason = reason; 63 + ev.iss = a; 64 + ev.elr = b; 65 + log_event_commit(&ev); 66 + } 67 + 68 + static const char *kind_name(uint32_t kind) 69 + { 70 + switch (kind) { 71 + case LOG_BOOT: return "boot"; 72 + case LOG_TRAP: return "trap"; 73 + case LOG_POLICY: return "policy"; 74 + case LOG_HVC: return "hvc"; 75 + case LOG_ABORT: return "abort"; 76 + case LOG_MONITOR: return "monitor"; 77 + case LOG_PANIC: return "panic"; 78 + case LOG_SELFTEST: return "selftest"; 79 + default: return "unknown"; 80 + } 81 + } 82 + 83 + void log_dump(unsigned limit) 84 + { 85 + uint64_t irq_flags = arch_irq_save(); 86 + lock_log(); 87 + uint32_t count = g_log.wrapped ? CONFIG_LOG_CAPACITY : g_log.write_index; 88 + if (limit != 0u && limit < count) { 89 + count = limit; 90 + } 91 + 92 + uint32_t start = g_log.wrapped ? g_log.write_index : 0u; 93 + if (g_log.wrapped && count < CONFIG_LOG_CAPACITY) { 94 + start = (g_log.write_index + CONFIG_LOG_CAPACITY - count) % CONFIG_LOG_CAPACITY; 95 + } 96 + 97 + for (uint32_t i = 0; i < count; i++) { 98 + const struct log_event *ev = &g_log.events[(start + i) % CONFIG_LOG_CAPACITY]; 99 + uart_puts("event seq="); 100 + uart_put_dec64(ev->seq); 101 + uart_puts(" cpu="); 102 + uart_put_dec64(ev->cpu); 103 + uart_puts(" vcpu="); 104 + uart_put_dec64(ev->vcpu); 105 + uart_puts(" ts="); 106 + uart_put_dec64(ev->timestamp); 107 + uart_puts(" kind="); 108 + uart_puts(kind_name(ev->kind)); 109 + uart_puts(" ec="); 110 + uart_put_hex64(ev->ec); 111 + uart_puts(" iss="); 112 + uart_put_hex64(ev->iss); 113 + uart_puts(" elr="); 114 + uart_put_hex64(ev->elr); 115 + uart_puts(" far="); 116 + uart_put_hex64(ev->far); 117 + uart_puts(" hpfar="); 118 + uart_put_hex64(ev->hpfar); 119 + uart_puts(" sysreg="); 120 + uart_put_hex64(ev->sysreg_key); 121 + uart_puts(" name="); 122 + uart_puts(ev->name != (const char *)0 ? ev->name : "-"); 123 + uart_puts(" dir="); 124 + uart_puts(ev->is_read != 0u ? "read" : "write"); 125 + uart_puts(" rt="); 126 + uart_put_dec64(ev->rt); 127 + uart_puts(" operand="); 128 + uart_put_hex64(ev->operand); 129 + uart_puts(" result="); 130 + uart_put_hex64(ev->result); 131 + uart_puts(" action="); 132 + uart_put_dec64(ev->action); 133 + uart_puts(" pstate="); 134 + uart_put_hex64(ev->pstate); 135 + uart_puts(" reason="); 136 + uart_put_dec64(ev->reason); 137 + uart_puts("\n"); 138 + } 139 + unlock_log(); 140 + arch_irq_restore(irq_flags); 141 + } 142 + 143 + void log_clear(void) 144 + { 145 + uint64_t irq_flags = arch_irq_save(); 146 + lock_log(); 147 + hv_memset(g_log.events, 0, sizeof(g_log.events)); 148 + g_log.write_index = 0; 149 + g_log.wrapped = false; 150 + unlock_log(); 151 + arch_irq_restore(irq_flags); 152 + } 153 + 154 + bool log_selftest(void) 155 + { 156 + g_log_selftest_backup = g_log; 157 + log_init(); 158 + for (uint32_t i = 0; i < CONFIG_LOG_CAPACITY + 3u; i++) { 159 + log_simple(LOG_SELFTEST, i, i, i + 1u); 160 + } 161 + bool ok = g_log.wrapped && g_log.next_seq == (uint64_t)CONFIG_LOG_CAPACITY + 3u; 162 + g_log = g_log_selftest_backup; 163 + return ok; 164 + }
+107
core/main.c
··· 1 + #include "hv/allocator.h" 2 + #include "hv/arch.h" 3 + #include "hv/config.h" 4 + #include "hv/el2_mmu.h" 5 + #include "hv/fdt.h" 6 + #include "hv/gicv3.h" 7 + #include "hv/guest_loader.h" 8 + #include "hv/log.h" 9 + #include "hv/monitor.h" 10 + #include "hv/panic.h" 11 + #include "hv/platform.h" 12 + #include "hv/policy.h" 13 + #include "hv/selftest.h" 14 + #include "hv/stage2.h" 15 + #include "hv/string.h" 16 + #include "hv/timer.h" 17 + #include "hv/uart.h" 18 + #include "hv/vcpu.h" 19 + 20 + static struct vcpu g_boot_vcpu; 21 + static uint8_t g_allocator_pool[CONFIG_PAGE_SIZE * 16u] __attribute__((aligned(4096))); 22 + static struct fdt_platform_info g_platform_info; 23 + 24 + const struct fdt_platform_info *platform_info(void) 25 + { 26 + return &g_platform_info; 27 + } 28 + 29 + static void print_banner(void) 30 + { 31 + uart_puts("\n"); 32 + uart_puts(HV_PROJECT_NAME " " HV_BUILD_MODE "\n"); 33 + uart_puts("build id=" HV_BUILD_ID "\n"); 34 + } 35 + 36 + void hv_main(uint64_t boot_dtb_pa) 37 + { 38 + struct guest_image_info guest_info; 39 + 40 + uart_init(); 41 + print_banner(); 42 + log_init(); 43 + timer_init(); 44 + monitor_init(); 45 + 46 + if (arch_current_el() != 2u) { 47 + panic_with_code("hypervisor must start at EL2", arch_current_el()); 48 + } 49 + if (fdt_probe(boot_dtb_pa, &g_platform_info)) { 50 + log_simple(LOG_BOOT, 30u, g_platform_info.fdt_pa, g_platform_info.size); 51 + } else { 52 + hv_memset(&g_platform_info, 0, sizeof(g_platform_info)); 53 + log_simple(LOG_BOOT, 31u, boot_dtb_pa, 0u); 54 + } 55 + 56 + arch_install_vectors(); 57 + el2_mmu_init_plan(); 58 + el2_mmu_enable(); 59 + allocator_init((paddr_t)(uintptr_t)g_allocator_pool, sizeof(g_allocator_pool)); 60 + policy_init(); 61 + #if CONFIG_BOOT_LINUX 62 + (void)policy_set_profile(POLICY_PROFILE_LINUX_BOOT); 63 + #endif 64 + stage2_init(); 65 + #if CONFIG_BOOT_LINUX 66 + if (!guest_load_external_linux(&guest_info, CONFIG_LINUX_IMAGE_LOAD_PA, 67 + CONFIG_LINUX_IMAGE_MAX_SIZE, CONFIG_LINUX_DTB_PA, 68 + CONFIG_LINUX_DTB_MAX_SIZE, CONFIG_LINUX_INITRD_PA, 69 + CONFIG_LINUX_INITRD_SIZE)) { 70 + panic("external Linux Image load failed"); 71 + } 72 + #else 73 + if (!guest_load_diagnostic(&guest_info)) { 74 + panic("diagnostic guest load failed"); 75 + } 76 + #endif 77 + log_simple(LOG_BOOT, 10u, guest_info.ipa_load, guest_info.size); 78 + if (!stage2_map_range(CONFIG_GUEST_IPA_BASE, CONFIG_GUEST_RAM_BASE, CONFIG_GUEST_RAM_SIZE, 79 + S2_MEM_NORMAL, S2_PERM_R | S2_PERM_W | S2_PERM_X)) { 80 + panic("stage2 guest RAM map failed"); 81 + } 82 + 83 + gicv3_init(); 84 + arch_el2_configure(stage2_vttbr(), stage2_vtcr()); 85 + selftests_run_or_panic(); 86 + 87 + vcpu_init(&g_boot_vcpu, 0u, guest_info.ipa_entry, CONFIG_GUEST_STACK_TOP); 88 + vcpu_set_current(&g_boot_vcpu); 89 + arch_prepare_guest_entry(&g_boot_vcpu); 90 + if (guest_info.kind == GUEST_PAYLOAD_LINUX_IMAGE) { 91 + vcpu_write_gpr(&g_boot_vcpu, 0u, guest_info.dtb_ipa); 92 + vcpu_write_gpr(&g_boot_vcpu, 1u, 0u); 93 + vcpu_write_gpr(&g_boot_vcpu, 2u, 0u); 94 + vcpu_write_gpr(&g_boot_vcpu, 3u, 0u); 95 + } 96 + 97 + uart_puts("launching guest entry="); 98 + uart_put_hex64(g_boot_vcpu.pc); 99 + uart_puts(" stack="); 100 + uart_put_hex64(g_boot_vcpu.sp_el1); 101 + uart_puts("\n"); 102 + g_boot_vcpu.state = VCPU_RUNNING; 103 + timer_vcpu_sync(&g_boot_vcpu); 104 + gicv3_flush_vcpu_state(&g_boot_vcpu); 105 + arch_vcpu_enter(&g_boot_vcpu); 106 + panic("arch_vcpu_enter returned"); 107 + }
+120
core/mmio_policy.c
··· 1 + #include "hv/config.h" 2 + #include "hv/mmio.h" 3 + #include "hv/mmio_policy.h" 4 + #include "hv/uart.h" 5 + #include "hv/vcpu.h" 6 + 7 + static const struct mmio_policy_region g_mmio_regions[] = { 8 + { CONFIG_UART_BASE, CONFIG_UART_SIZE, "pl011-uart", MMIO_POLICY_EMULATE_PL011 }, 9 + { CONFIG_GICD_BASE, CONFIG_GICD_SIZE, "gicd", MMIO_POLICY_EMULATE_READONLY_DEVICE }, 10 + { CONFIG_GITS_BASE, CONFIG_GITS_SIZE, "gits", MMIO_POLICY_EMULATE_READONLY_DEVICE }, 11 + { CONFIG_GICR_BASE, CONFIG_GICR_SIZE, "gicr", MMIO_POLICY_EMULATE_READONLY_DEVICE }, 12 + { CONFIG_RTC_BASE, CONFIG_RTC_SIZE, "pl031-rtc", MMIO_POLICY_EMULATE_READONLY_DEVICE }, 13 + { CONFIG_FW_CFG_BASE, CONFIG_FW_CFG_SIZE, "fw-cfg", MMIO_POLICY_EMULATE_READONLY_DEVICE }, 14 + { CONFIG_GPIO_BASE, CONFIG_GPIO_SIZE, "pl061-gpio", MMIO_POLICY_EMULATE_READONLY_DEVICE }, 15 + { CONFIG_PCI_ECAM_BASE, CONFIG_PCI_ECAM_SIZE, "pci-ecam", MMIO_POLICY_EMULATE_ZERO }, 16 + { 0x0a000000ull, 0x00004000ull, "virtio-mmio", MMIO_POLICY_EMULATE_VIRTIO_MMIO }, 17 + }; 18 + 19 + const struct mmio_policy_region *mmio_policy_lookup(ipa_t ipa) 20 + { 21 + for (uint32_t i = 0; i < HV_ARRAY_SIZE(g_mmio_regions); i++) { 22 + uint64_t end; 23 + if (hv_add_overflow_u64(g_mmio_regions[i].base, g_mmio_regions[i].size, &end)) { 24 + continue; 25 + } 26 + if (ipa >= g_mmio_regions[i].base && ipa < end) { 27 + return &g_mmio_regions[i]; 28 + } 29 + } 30 + return (const struct mmio_policy_region *)0; 31 + } 32 + 33 + static bool decode_abort_access(uint64_t esr, bool *is_write, uint32_t *rt) 34 + { 35 + if ((esr & HV_BIT(24)) == 0u) { 36 + return false; 37 + } 38 + *rt = (uint32_t)((esr >> 16u) & 0x1fu); 39 + *is_write = (esr & HV_BIT(6)) != 0u; 40 + return true; 41 + } 42 + 43 + static uint64_t virtio_read(uint64_t off) 44 + { 45 + if ((off & ~0x1ffull) != 0u) { 46 + return 0u; 47 + } 48 + off &= 0x1ffu; 49 + switch (off) { 50 + case 0x000u: return 0x74726976u; 51 + case 0x004u: return 2u; 52 + case 0x008u: return 3u; 53 + case 0x00cu: return 0x41524945u; 54 + case 0x010u: return 0u; 55 + case 0x034u: return 0u; 56 + case 0x070u: return 0u; 57 + default: return 0u; 58 + } 59 + } 60 + 61 + static uint64_t readonly_device_read32(ipa_t ipa) 62 + { 63 + if (!hv_is_aligned_u64(ipa, 4u)) { 64 + return 0u; 65 + } 66 + return (uint64_t)mmio_read32((uintptr_t)ipa); 67 + } 68 + 69 + bool mmio_policy_emulate(struct vcpu *vcpu, ipa_t ipa, uint64_t esr_el2) 70 + { 71 + const struct mmio_policy_region *region = mmio_policy_lookup(ipa); 72 + bool is_write; 73 + uint32_t rt; 74 + uint64_t off; 75 + uint64_t value = 0u; 76 + 77 + if (vcpu == (struct vcpu *)0 || region == (const struct mmio_policy_region *)0 || 78 + !decode_abort_access(esr_el2, &is_write, &rt)) { 79 + return false; 80 + } 81 + 82 + off = ipa - region->base; 83 + if (region->action == MMIO_POLICY_EMULATE_PL011) { 84 + if (is_write && off == 0u) { 85 + uart_putc((char)(vcpu_read_gpr(vcpu, rt) & 0xffu)); 86 + } else if (!is_write && off == 0x18u) { 87 + value = 0x10u; 88 + } 89 + } else if (region->action == MMIO_POLICY_EMULATE_VIRTIO_MMIO) { 90 + if (!is_write) { 91 + value = virtio_read(off); 92 + } 93 + } else if (region->action == MMIO_POLICY_EMULATE_READONLY_DEVICE) { 94 + if (!is_write) { 95 + value = readonly_device_read32(ipa); 96 + } 97 + } else if (region->action != MMIO_POLICY_EMULATE_ZERO) { 98 + return false; 99 + } 100 + 101 + if (!is_write) { 102 + vcpu_write_gpr(vcpu, rt, value); 103 + } 104 + return true; 105 + } 106 + 107 + void mmio_policy_dump(void) 108 + { 109 + for (uint32_t i = 0; i < HV_ARRAY_SIZE(g_mmio_regions); i++) { 110 + uart_puts("mmio name="); 111 + uart_puts(g_mmio_regions[i].name); 112 + uart_puts(" base="); 113 + uart_put_hex64(g_mmio_regions[i].base); 114 + uart_puts(" size="); 115 + uart_put_hex64(g_mmio_regions[i].size); 116 + uart_puts(" action="); 117 + uart_put_dec64((uint64_t)g_mmio_regions[i].action); 118 + uart_puts("\n"); 119 + } 120 + }
+477
core/monitor.c
··· 1 + #include "hv/allocator.h" 2 + #include "hv/gicv3.h" 3 + #include "hv/el2_mmu.h" 4 + #include "hv/log.h" 5 + #include "hv/monitor.h" 6 + #include "hv/mmio_policy.h" 7 + #include "hv/platform.h" 8 + #include "hv/policy.h" 9 + #include "hv/psci.h" 10 + #include "hv/stage2.h" 11 + #include "hv/string.h" 12 + #include "hv/timer.h" 13 + #include "hv/uart.h" 14 + 15 + static char g_line[96]; 16 + static uint32_t g_pos; 17 + static bool g_paused; 18 + static struct vcpu g_snapshot; 19 + static bool g_snapshot_valid; 20 + 21 + static void help(void) 22 + { 23 + #if CONFIG_MONITOR_MUTATION 24 + uart_puts("commands: help status vcpu regs sysregs snapshot restore log log clear policy profile psci mappings mmio el2mmu fdt translate read64 write64 dump unmap irq irq clear resume pause reboot shutdown\n"); 25 + #else 26 + uart_puts("commands: help status vcpu regs sysregs snapshot snapshot show log policy psci mappings mmio el2mmu fdt translate read64 dump resume pause\n"); 27 + #endif 28 + } 29 + 30 + static bool is_space(char c) 31 + { 32 + return c == ' ' || c == '\t'; 33 + } 34 + 35 + static const char *skip_spaces(const char *s) 36 + { 37 + while (is_space(*s)) { 38 + s++; 39 + } 40 + return s; 41 + } 42 + 43 + static bool starts_with_command(const char *line, const char *cmd) 44 + { 45 + size_t n = hv_strlen(cmd); 46 + return hv_strncmp(line, cmd, n) == 0 && (line[n] == '\0' || is_space(line[n])); 47 + } 48 + 49 + static bool digit_value(char c, uint64_t *out) 50 + { 51 + if (c >= '0' && c <= '9') { 52 + *out = (uint64_t)(c - '0'); 53 + return true; 54 + } 55 + if (c >= 'a' && c <= 'f') { 56 + *out = 10u + (uint64_t)(c - 'a'); 57 + return true; 58 + } 59 + if (c >= 'A' && c <= 'F') { 60 + *out = 10u + (uint64_t)(c - 'A'); 61 + return true; 62 + } 63 + return false; 64 + } 65 + 66 + static bool parse_u64(const char **cursor, uint64_t *out) 67 + { 68 + const char *s = skip_spaces(*cursor); 69 + uint64_t base = 10u; 70 + uint64_t value = 0u; 71 + uint64_t digit; 72 + bool any = false; 73 + 74 + if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) { 75 + base = 16u; 76 + s += 2; 77 + } 78 + while (digit_value(*s, &digit)) { 79 + if (digit >= base) { 80 + return false; 81 + } 82 + if (value > (UINT64_MAX - digit) / base) { 83 + return false; 84 + } 85 + value = (value * base) + digit; 86 + any = true; 87 + s++; 88 + } 89 + if (!any) { 90 + return false; 91 + } 92 + *cursor = s; 93 + *out = value; 94 + return true; 95 + } 96 + 97 + static bool ipa_to_host(uint64_t ipa, uint64_t len, uintptr_t *host) 98 + { 99 + struct stage2_translation tr; 100 + uint64_t end; 101 + uint64_t tr_end; 102 + uint64_t off; 103 + 104 + if (host == (uintptr_t *)0 || len == 0u || hv_add_overflow_u64(ipa, len, &end)) { 105 + return false; 106 + } 107 + if (!stage2_translate(ipa, &tr) || 108 + hv_add_overflow_u64(tr.ipa_base, tr.size, &tr_end) || 109 + end > tr_end) { 110 + return false; 111 + } 112 + off = ipa - tr.ipa_base; 113 + *host = (uintptr_t)(tr.pa_base + off); 114 + return true; 115 + } 116 + 117 + static void monitor_translate(const char *args) 118 + { 119 + uint64_t ipa; 120 + struct stage2_translation tr; 121 + if (!parse_u64(&args, &ipa)) { 122 + uart_puts("usage translate <ipa>\n"); 123 + return; 124 + } 125 + if (!stage2_translate(ipa, &tr)) { 126 + uart_puts("translate unmapped ipa="); 127 + uart_put_hex64(ipa); 128 + uart_puts("\n"); 129 + return; 130 + } 131 + uart_puts("translate ipa="); 132 + uart_put_hex64(ipa); 133 + uart_puts(" block="); 134 + uart_put_hex64(tr.ipa_base); 135 + uart_puts(" pa="); 136 + uart_put_hex64(tr.pa_base + (ipa - tr.ipa_base)); 137 + uart_puts(" perms="); 138 + uart_put_hex64(tr.perms); 139 + uart_puts(" desc="); 140 + uart_put_hex64(tr.desc); 141 + uart_puts("\n"); 142 + } 143 + 144 + static void monitor_read64(const char *args) 145 + { 146 + uint64_t ipa; 147 + uintptr_t host; 148 + if (!parse_u64(&args, &ipa) || !hv_is_aligned_u64(ipa, 8u)) { 149 + uart_puts("usage read64 <aligned-ipa>\n"); 150 + return; 151 + } 152 + if (!ipa_to_host(ipa, 8u, &host)) { 153 + uart_puts("read64 unmapped\n"); 154 + return; 155 + } 156 + uart_puts("read64 "); 157 + uart_put_hex64(ipa); 158 + uart_puts("="); 159 + uart_put_hex64(*(volatile uint64_t *)host); 160 + uart_puts("\n"); 161 + } 162 + 163 + static void monitor_write64(const char *args) 164 + { 165 + #if CONFIG_MONITOR_MUTATION 166 + uint64_t ipa; 167 + uint64_t value; 168 + uintptr_t host; 169 + if (!parse_u64(&args, &ipa) || !parse_u64(&args, &value) || !hv_is_aligned_u64(ipa, 8u)) { 170 + uart_puts("usage write64 <aligned-ipa> <value>\n"); 171 + return; 172 + } 173 + if (!ipa_to_host(ipa, 8u, &host)) { 174 + uart_puts("write64 unmapped\n"); 175 + return; 176 + } 177 + *(volatile uint64_t *)host = value; 178 + uart_puts("write64 ok\n"); 179 + #else 180 + (void)args; 181 + uart_puts("write64 disabled\n"); 182 + #endif 183 + } 184 + 185 + static void monitor_dump(const char *args) 186 + { 187 + uint64_t ipa; 188 + uint64_t len; 189 + uintptr_t host; 190 + if (!parse_u64(&args, &ipa) || !parse_u64(&args, &len) || len > 256u) { 191 + uart_puts("usage dump <ipa> <len<=256>\n"); 192 + return; 193 + } 194 + if (!ipa_to_host(ipa, len, &host)) { 195 + uart_puts("dump unmapped\n"); 196 + return; 197 + } 198 + for (uint64_t i = 0; i < len; i++) { 199 + if ((i & 0x0full) == 0u) { 200 + uart_puts("\n"); 201 + uart_put_hex64(ipa + i); 202 + uart_puts(":"); 203 + } 204 + uart_puts(" "); 205 + uint8_t b = *(volatile uint8_t *)(host + i); 206 + static const char hex[] = "0123456789abcdef"; 207 + uart_putc(hex[b >> 4u]); 208 + uart_putc(hex[b & 0x0fu]); 209 + } 210 + uart_puts("\n"); 211 + } 212 + 213 + static void monitor_unmap(const char *args) 214 + { 215 + #if CONFIG_MONITOR_MUTATION 216 + uint64_t ipa; 217 + if (!parse_u64(&args, &ipa)) { 218 + uart_puts("usage unmap <2MiB-aligned-ipa>\n"); 219 + return; 220 + } 221 + if (!stage2_unmap_range(HV_ALIGN_DOWN(ipa, 0x200000ull), 0x200000ull)) { 222 + uart_puts("unmap failed\n"); 223 + return; 224 + } 225 + uart_puts("unmap ok\n"); 226 + #else 227 + (void)args; 228 + uart_puts("unmap disabled\n"); 229 + #endif 230 + } 231 + 232 + static void monitor_irq(const char *args, struct vcpu *vcpu) 233 + { 234 + #if CONFIG_MONITOR_MUTATION 235 + uint64_t intid; 236 + args = skip_spaces(args); 237 + if (hv_strncmp(args, "clear", 5u) == 0 && is_space(args[5])) { 238 + args = skip_spaces(args + 5u); 239 + if (vcpu == (struct vcpu *)0 || !parse_u64(&args, &intid) || intid >= 64u) { 240 + uart_puts("usage irq clear <intid<64>\n"); 241 + return; 242 + } 243 + vcpu_clear_pending_irq(vcpu, (uint32_t)intid); 244 + gicv3_flush_vcpu_state(vcpu); 245 + uart_puts("irq cleared intid="); 246 + uart_put_dec64(intid); 247 + uart_puts("\n"); 248 + return; 249 + } 250 + if (vcpu == (struct vcpu *)0 || !parse_u64(&args, &intid) || intid >= 64u) { 251 + uart_puts("usage irq <intid<64> | irq clear <intid<64>\n"); 252 + return; 253 + } 254 + vcpu_set_pending_irq(vcpu, (uint32_t)intid); 255 + gicv3_flush_vcpu_state(vcpu); 256 + uart_puts("irq pending intid="); 257 + uart_put_dec64(intid); 258 + uart_puts("\n"); 259 + #else 260 + (void)args; 261 + (void)vcpu; 262 + uart_puts("irq disabled\n"); 263 + #endif 264 + } 265 + 266 + static void monitor_profile(const char *args) 267 + { 268 + #if CONFIG_MONITOR_MUTATION 269 + args = skip_spaces(args); 270 + if (*args == '\0') { 271 + uart_puts("policy profile="); 272 + uart_puts(policy_profile_name(policy_get_profile())); 273 + uart_puts("\n"); 274 + return; 275 + } 276 + if (!policy_set_profile_name(args)) { 277 + uart_puts("usage profile strict|diagnostic|linux-boot|debug\n"); 278 + return; 279 + } 280 + uart_puts("policy profile="); 281 + uart_puts(policy_profile_name(policy_get_profile())); 282 + uart_puts("\n"); 283 + #else 284 + (void)args; 285 + uart_puts("profile disabled\n"); 286 + #endif 287 + } 288 + 289 + static void monitor_snapshot(struct vcpu *vcpu) 290 + { 291 + if (vcpu == (struct vcpu *)0) { 292 + uart_puts("snapshot no-vcpu\n"); 293 + return; 294 + } 295 + g_snapshot = *vcpu; 296 + g_snapshot_valid = true; 297 + log_simple(LOG_MONITOR, 610u, vcpu->pc, vcpu->pstate); 298 + uart_puts("snapshot saved pc="); 299 + uart_put_hex64(g_snapshot.pc); 300 + uart_puts(" pstate="); 301 + uart_put_hex64(g_snapshot.pstate); 302 + uart_puts("\n"); 303 + } 304 + 305 + static void monitor_snapshot_show(void) 306 + { 307 + if (!g_snapshot_valid) { 308 + uart_puts("snapshot empty\n"); 309 + return; 310 + } 311 + uart_puts("snapshot pc="); 312 + uart_put_hex64(g_snapshot.pc); 313 + uart_puts(" pstate="); 314 + uart_put_hex64(g_snapshot.pstate); 315 + uart_puts(" sp_el1="); 316 + uart_put_hex64(g_snapshot.sp_el1); 317 + uart_puts(" traps="); 318 + uart_put_dec64(g_snapshot.stats.traps); 319 + uart_puts("\n"); 320 + } 321 + 322 + static void monitor_restore(struct vcpu *vcpu) 323 + { 324 + #if CONFIG_MONITOR_MUTATION 325 + if (vcpu == (struct vcpu *)0 || !g_snapshot_valid) { 326 + uart_puts("restore no-snapshot\n"); 327 + return; 328 + } 329 + *vcpu = g_snapshot; 330 + vcpu_set_current(vcpu); 331 + log_simple(LOG_MONITOR, 611u, vcpu->pc, vcpu->pstate); 332 + uart_puts("restore ok pc="); 333 + uart_put_hex64(vcpu->pc); 334 + uart_puts("\n"); 335 + #else 336 + (void)vcpu; 337 + uart_puts("restore disabled\n"); 338 + #endif 339 + } 340 + 341 + static void show_sysregs(const struct vcpu *vcpu) 342 + { 343 + if (vcpu == (const struct vcpu *)0) { 344 + return; 345 + } 346 + uart_puts("sysregs sctlr="); 347 + uart_put_hex64(vcpu->sysregs.sctlr_el1); 348 + uart_puts(" ttbr0="); 349 + uart_put_hex64(vcpu->sysregs.ttbr0_el1); 350 + uart_puts(" ttbr1="); 351 + uart_put_hex64(vcpu->sysregs.ttbr1_el1); 352 + uart_puts(" tcr="); 353 + uart_put_hex64(vcpu->sysregs.tcr_el1); 354 + uart_puts(" mair="); 355 + uart_put_hex64(vcpu->sysregs.mair_el1); 356 + uart_puts(" vbar="); 357 + uart_put_hex64(vcpu->sysregs.vbar_el1); 358 + uart_puts("\n"); 359 + } 360 + 361 + static bool command(const char *line, struct vcpu *vcpu) 362 + { 363 + if (hv_strcmp(line, "help") == 0) { 364 + help(); 365 + } else if (hv_strcmp(line, "status") == 0) { 366 + vcpu_dump(vcpu); 367 + timer_dump(); 368 + gicv3_dump(); 369 + } else if (hv_strcmp(line, "vcpu") == 0 || hv_strcmp(line, "regs") == 0) { 370 + vcpu_dump(vcpu); 371 + } else if (hv_strcmp(line, "snapshot") == 0) { 372 + monitor_snapshot(vcpu); 373 + } else if (hv_strcmp(line, "snapshot show") == 0) { 374 + monitor_snapshot_show(); 375 + } else if (hv_strcmp(line, "restore") == 0) { 376 + monitor_restore(vcpu); 377 + } else if (hv_strcmp(line, "sysregs") == 0) { 378 + show_sysregs(vcpu); 379 + } else if (hv_strcmp(line, "log") == 0) { 380 + log_dump(128u); 381 + } else if (hv_strcmp(line, "log clear") == 0) { 382 + #if CONFIG_MONITOR_MUTATION 383 + log_clear(); 384 + #else 385 + uart_puts("log clear disabled\n"); 386 + #endif 387 + } else if (hv_strcmp(line, "policy") == 0) { 388 + policy_dump(); 389 + } else if (starts_with_command(line, "profile")) { 390 + monitor_profile(line + hv_strlen("profile")); 391 + } else if (hv_strcmp(line, "psci") == 0) { 392 + psci_dump(); 393 + } else if (hv_strcmp(line, "mappings") == 0) { 394 + stage2_dump(); 395 + allocator_dump(); 396 + } else if (hv_strcmp(line, "mmio") == 0) { 397 + mmio_policy_dump(); 398 + } else if (hv_strcmp(line, "el2mmu") == 0) { 399 + el2_mmu_dump(); 400 + } else if (hv_strcmp(line, "fdt") == 0) { 401 + fdt_dump(platform_info()); 402 + } else if (starts_with_command(line, "translate")) { 403 + monitor_translate(line + hv_strlen("translate")); 404 + } else if (starts_with_command(line, "read64")) { 405 + monitor_read64(line + hv_strlen("read64")); 406 + } else if (starts_with_command(line, "write64")) { 407 + monitor_write64(line + hv_strlen("write64")); 408 + } else if (starts_with_command(line, "dump")) { 409 + monitor_dump(line + hv_strlen("dump")); 410 + } else if (starts_with_command(line, "unmap")) { 411 + monitor_unmap(line + hv_strlen("unmap")); 412 + } else if (starts_with_command(line, "irq")) { 413 + monitor_irq(line + hv_strlen("irq"), vcpu); 414 + } else if (hv_strcmp(line, "resume") == 0) { 415 + if (vcpu != (struct vcpu *)0) { 416 + vcpu->state = VCPU_RUNNING; 417 + } 418 + g_paused = false; 419 + return true; 420 + } else if (hv_strcmp(line, "pause") == 0) { 421 + g_paused = true; 422 + } else if (hv_strcmp(line, "reboot") == 0 || hv_strcmp(line, "shutdown") == 0) { 423 + #if CONFIG_MONITOR_MUTATION 424 + uart_puts("platform power control is not implemented; halting\n"); 425 + for (;;) { 426 + __asm__ volatile("wfe"); 427 + } 428 + #else 429 + uart_puts("power control disabled\n"); 430 + #endif 431 + } else if (line[0] != '\0') { 432 + uart_puts("error unknown-command\n"); 433 + } 434 + return false; 435 + } 436 + 437 + void monitor_init(void) 438 + { 439 + g_pos = 0; 440 + g_paused = false; 441 + g_snapshot_valid = false; 442 + } 443 + 444 + void monitor_poll(void) 445 + { 446 + char c; 447 + while (uart_getc_nonblocking(&c)) { 448 + if (c == '\r' || c == '\n') { 449 + g_line[g_pos] = '\0'; 450 + (void)command(g_line, vcpu_current()); 451 + g_pos = 0; 452 + uart_puts("hv> "); 453 + } else if ((c == '\b' || c == 0x7f) && g_pos != 0u) { 454 + g_pos--; 455 + } else if (g_pos + 1u < sizeof(g_line) && c >= ' ' && c <= '~') { 456 + g_line[g_pos++] = c; 457 + } else { 458 + uart_puts("error input-too-long\n"); 459 + g_pos = 0; 460 + } 461 + } 462 + } 463 + 464 + void monitor_loop(struct vcpu *vcpu, const char *reason) 465 + { 466 + g_paused = true; 467 + if (vcpu != (struct vcpu *)0) { 468 + vcpu->state = VCPU_PAUSED; 469 + } 470 + uart_puts("monitor entered reason="); 471 + uart_puts(reason); 472 + uart_puts("\nhv> "); 473 + while (g_paused) { 474 + monitor_poll(); 475 + __asm__ volatile("wfe"); 476 + } 477 + }
+48
core/panic.c
··· 1 + #include "hv/arch.h" 2 + #include "hv/log.h" 3 + #include "hv/panic.h" 4 + #include "hv/uart.h" 5 + #include "hv/vcpu.h" 6 + 7 + void panic_with_code(const char *reason, uint64_t code) 8 + { 9 + struct log_event ev; 10 + ev.seq = 0; 11 + ev.cpu = 0; 12 + ev.vcpu = vcpu_current() != (struct vcpu *)0 ? vcpu_current()->id : UINT64_MAX; 13 + ev.timestamp = 0; 14 + ev.kind = LOG_PANIC; 15 + ev.ec = 0; 16 + ev.iss = code; 17 + ev.elr = 0; 18 + ev.far = 0; 19 + ev.hpfar = 0; 20 + ev.sysreg_key = 0; 21 + ev.is_read = 0; 22 + ev.rt = 0; 23 + ev.operand = code; 24 + ev.result = 0; 25 + ev.action = LOG_ACT_PANIC; 26 + ev.reason = 999u; 27 + ev.pstate = 0; 28 + ev.name = reason; 29 + log_event_commit(&ev); 30 + 31 + uart_puts("\nPANIC reason="); 32 + uart_puts(reason); 33 + uart_puts(" code="); 34 + uart_put_hex64(code); 35 + uart_puts(" current_el="); 36 + uart_put_dec64(arch_current_el()); 37 + uart_puts("\n"); 38 + arch_dump_el2_state(); 39 + vcpu_dump(vcpu_current()); 40 + uart_puts("recent log:\n"); 41 + log_dump(32u); 42 + arch_halt(); 43 + } 44 + 45 + void panic(const char *reason) 46 + { 47 + panic_with_code(reason, 0u); 48 + }
+286
core/policy.c
··· 1 + #include "hv/arch.h" 2 + #include "hv/config.h" 3 + #include "hv/policy.h" 4 + #include "hv/string.h" 5 + #include "hv/timer.h" 6 + #include "hv/uart.h" 7 + 8 + #define KEY(op0, op1, crn, crm, op2) SYSREG_KEY_CONST((op0), (op1), (crn), (crm), (op2)) 9 + #define RO_ID(name, op0, op1, crn, crm, op2, val) \ 10 + { KEY(op0, op1, crn, crm, op2), name, POLICY_CLASS_ID, POLICY_EMULATE_READ, POLICY_DENY_UNDEF, val, 0, 1, 100 } 11 + #define SHADOW_RW(name, op0, op1, crn, crm, op2, field, mask) \ 12 + { KEY(op0, op1, crn, crm, op2), name, POLICY_CLASS_MMU, POLICY_EMULATE_READ, POLICY_WRITE_MASK, 0, mask, 1, 101 } 13 + #define SHADOW_THREAD(name, op0, op1, crn, crm, op2) \ 14 + { KEY(op0, op1, crn, crm, op2), name, POLICY_CLASS_THREAD, POLICY_EMULATE_READ, POLICY_EMULATE_WRITE, 0, UINT64_MAX, 1, 102 } 15 + #define DENY_REG(name, op0, op1, crn, crm, op2, cls) \ 16 + { KEY(op0, op1, crn, crm, op2), name, cls, POLICY_DENY_UNDEF, POLICY_DENY_UNDEF, 0, 0, 1, 200 } 17 + 18 + static const struct policy_entry g_policy[] = { 19 + RO_ID("midr_el1", 3, 0, 0, 0, 0, 0x00000000410fd034ull), 20 + RO_ID("mpidr_el1", 3, 0, 0, 0, 5, 0x80000000ull), 21 + RO_ID("id_aa64pfr0_el1", 3, 0, 0, 4, 0, 0x0000000000000011ull), 22 + RO_ID("id_aa64pfr1_el1", 3, 0, 0, 4, 1, 0x0ull), 23 + RO_ID("id_aa64isar0_el1", 3, 0, 0, 6, 0, 0x0ull), 24 + RO_ID("id_aa64isar1_el1", 3, 0, 0, 6, 1, 0x0ull), 25 + RO_ID("id_aa64mmfr0_el1", 3, 0, 0, 7, 0, 0x0000000000001122ull), 26 + RO_ID("id_aa64mmfr1_el1", 3, 0, 0, 7, 1, 0x0ull), 27 + RO_ID("clidr_el1", 3, 1, 0, 0, 1, 0x0ull), 28 + { KEY(3, 2, 0, 0, 0), "csselr_el1", POLICY_CLASS_MISC, POLICY_EMULATE_READ, POLICY_EMULATE_WRITE, 0, 0xfu, 1, 103 }, 29 + RO_ID("ctr_el0", 3, 3, 0, 0, 1, 0x8444c004ull), 30 + RO_ID("dczid_el0", 3, 3, 0, 0, 7, 0x4ull), 31 + { KEY(3, 0, 1, 0, 0), "sctlr_el1", POLICY_CLASS_MMU, POLICY_EMULATE_READ, POLICY_WRITE_MASK, 0x30d00800ull, 0x0000000030d0dfffull, 1, 104 }, 32 + DENY_REG("actlr_el1", 3, 0, 1, 0, 1, POLICY_CLASS_MISC), 33 + { KEY(3, 0, 1, 0, 2), "cpacr_el1", POLICY_CLASS_MMU, POLICY_EMULATE_READ, POLICY_WRITE_MASK, 0, 0x00300000ull, 1, 105 }, 34 + DENY_REG("hcr_el2", 3, 4, 1, 1, 0, POLICY_CLASS_EL2_EL3), 35 + DENY_REG("scr_el3", 3, 6, 1, 1, 0, POLICY_CLASS_EL2_EL3), 36 + { KEY(3, 0, 2, 0, 0), "ttbr0_el1", POLICY_CLASS_MMU, POLICY_EMULATE_READ, POLICY_EMULATE_WRITE, 0, 0x0000fffffffff000ull, 1, 106 }, 37 + { KEY(3, 0, 2, 0, 1), "ttbr1_el1", POLICY_CLASS_MMU, POLICY_EMULATE_READ, POLICY_EMULATE_WRITE, 0, 0x0000fffffffff000ull, 1, 107 }, 38 + { KEY(3, 0, 2, 0, 2), "tcr_el1", POLICY_CLASS_MMU, POLICY_EMULATE_READ, POLICY_WRITE_MASK, 0, 0x00000003ff3fffffull, 1, 108 }, 39 + DENY_REG("vttbr_el2", 3, 4, 2, 1, 0, POLICY_CLASS_EL2_EL3), 40 + DENY_REG("pmcr_el0", 3, 3, 9, 12, 0, POLICY_CLASS_PMU), 41 + { KEY(3, 0, 10, 2, 0), "mair_el1", POLICY_CLASS_MMU, POLICY_EMULATE_READ, POLICY_EMULATE_WRITE, 0xffull, UINT64_MAX, 1, 109 }, 42 + { KEY(3, 0, 12, 0, 0), "vbar_el1", POLICY_CLASS_MMU, POLICY_EMULATE_READ, POLICY_EMULATE_WRITE, 0, 0x0000fffffffff800ull, 1, 110 }, 43 + DENY_REG("rvbar_el1", 3, 0, 12, 0, 1, POLICY_CLASS_MISC), 44 + SHADOW_THREAD("contextidr_el1", 3, 0, 13, 0, 1), 45 + SHADOW_THREAD("tpidr_el1", 3, 0, 13, 0, 4), 46 + SHADOW_THREAD("tpidrro_el0", 3, 3, 13, 0, 3), 47 + { KEY(3, 0, 14, 1, 0), "cntkctl_el1", POLICY_CLASS_TIMER, POLICY_EMULATE_READ, POLICY_WRITE_MASK, 0, 0x3u, 1, 111 }, 48 + { KEY(3, 3, 14, 3, 0), "cntv_tval_el0", POLICY_CLASS_TIMER, POLICY_EMULATE_READ, POLICY_EMULATE_WRITE, 0, UINT64_MAX, 1, 112 }, 49 + { KEY(3, 3, 14, 3, 1), "cntv_ctl_el0", POLICY_CLASS_TIMER, POLICY_EMULATE_READ, POLICY_WRITE_MASK, 0, 0x7u, 1, 113 }, 50 + { KEY(3, 3, 14, 3, 2), "cntv_cval_el0", POLICY_CLASS_TIMER, POLICY_EMULATE_READ, POLICY_EMULATE_WRITE, 0, UINT64_MAX, 1, 114 }, 51 + { KEY(2, 0, 0, 2, 2), "mdscr_el1", POLICY_CLASS_DEBUG, POLICY_EMULATE_READ, POLICY_WRITE_MASK, 0, 0ull, 1, 115 }, 52 + }; 53 + 54 + static enum policy_profile g_profile; 55 + static struct policy_entry g_effective_entry; 56 + 57 + void policy_init(void) 58 + { 59 + g_profile = POLICY_PROFILE_STRICT; 60 + } 61 + 62 + const char *policy_profile_name(enum policy_profile profile) 63 + { 64 + switch (profile) { 65 + case POLICY_PROFILE_STRICT: return "strict"; 66 + case POLICY_PROFILE_DIAGNOSTIC: return "diagnostic"; 67 + case POLICY_PROFILE_LINUX_BOOT: return "linux-boot"; 68 + case POLICY_PROFILE_DEBUG: return "debug"; 69 + default: return "unknown"; 70 + } 71 + } 72 + 73 + bool policy_set_profile(enum policy_profile profile) 74 + { 75 + if (profile > POLICY_PROFILE_DEBUG) { 76 + return false; 77 + } 78 + g_profile = profile; 79 + return true; 80 + } 81 + 82 + bool policy_set_profile_name(const char *name) 83 + { 84 + if (name == (const char *)0) { 85 + return false; 86 + } 87 + if (hv_strcmp(name, "strict") == 0) { 88 + return policy_set_profile(POLICY_PROFILE_STRICT); 89 + } 90 + if (hv_strcmp(name, "diagnostic") == 0) { 91 + return policy_set_profile(POLICY_PROFILE_DIAGNOSTIC); 92 + } 93 + if (hv_strcmp(name, "linux-boot") == 0) { 94 + return policy_set_profile(POLICY_PROFILE_LINUX_BOOT); 95 + } 96 + if (hv_strcmp(name, "debug") == 0) { 97 + return policy_set_profile(POLICY_PROFILE_DEBUG); 98 + } 99 + return false; 100 + } 101 + 102 + enum policy_profile policy_get_profile(void) 103 + { 104 + return g_profile; 105 + } 106 + 107 + const struct policy_entry *sysreg_policy_lookup(uint64_t key) 108 + { 109 + for (uint32_t i = 0; i < HV_ARRAY_SIZE(g_policy); i++) { 110 + if (g_policy[i].key == key) { 111 + return &g_policy[i]; 112 + } 113 + } 114 + return (const struct policy_entry *)0; 115 + } 116 + 117 + static uint64_t *shadow_slot(struct vcpu *vcpu, uint64_t key) 118 + { 119 + if (key == KEY(3, 0, 1, 0, 0)) return &vcpu->sysregs.sctlr_el1; 120 + if (key == KEY(3, 0, 1, 0, 2)) return &vcpu->sysregs.cpacr_el1; 121 + if (key == KEY(3, 0, 2, 0, 0)) return &vcpu->sysregs.ttbr0_el1; 122 + if (key == KEY(3, 0, 2, 0, 1)) return &vcpu->sysregs.ttbr1_el1; 123 + if (key == KEY(3, 0, 2, 0, 2)) return &vcpu->sysregs.tcr_el1; 124 + if (key == KEY(3, 0, 10, 2, 0)) return &vcpu->sysregs.mair_el1; 125 + if (key == KEY(3, 0, 12, 0, 0)) return &vcpu->sysregs.vbar_el1; 126 + if (key == KEY(3, 0, 13, 0, 1)) return &vcpu->sysregs.contextidr_el1; 127 + if (key == KEY(3, 0, 13, 0, 4)) return &vcpu->sysregs.tpidr_el1; 128 + if (key == KEY(3, 3, 13, 0, 3)) return &vcpu->sysregs.tpidrro_el0; 129 + if (key == KEY(3, 0, 14, 1, 0)) return &vcpu->sysregs.cntkctl_el1; 130 + if (key == KEY(3, 3, 14, 3, 0)) return &vcpu->sysregs.cntv_tval_el0; 131 + if (key == KEY(3, 3, 14, 3, 1)) return &vcpu->sysregs.cntv_ctl_el0; 132 + if (key == KEY(3, 3, 14, 3, 2)) return &vcpu->sysregs.cntv_cval_el0; 133 + if (key == KEY(3, 2, 0, 0, 0)) return &vcpu->sysregs.csselr_el1; 134 + if (key == KEY(2, 0, 0, 2, 2)) return &vcpu->sysregs.mdscr_el1; 135 + return (uint64_t *)0; 136 + } 137 + 138 + static bool is_el1_mmu_key(uint64_t key) 139 + { 140 + return key == KEY(3, 0, 1, 0, 0) || 141 + key == KEY(3, 0, 1, 0, 2) || 142 + key == KEY(3, 0, 2, 0, 0) || 143 + key == KEY(3, 0, 2, 0, 1) || 144 + key == KEY(3, 0, 2, 0, 2) || 145 + key == KEY(3, 0, 10, 2, 0) || 146 + key == KEY(3, 0, 12, 0, 0) || 147 + key == KEY(3, 0, 14, 1, 0); 148 + } 149 + 150 + static const struct policy_entry *effective_entry(const struct policy_entry *entry) 151 + { 152 + if (entry == (const struct policy_entry *)0) { 153 + return (const struct policy_entry *)0; 154 + } 155 + 156 + g_effective_entry = *entry; 157 + if (g_profile == POLICY_PROFILE_DIAGNOSTIC) { 158 + if (g_effective_entry.access_class == POLICY_CLASS_PMU || 159 + g_effective_entry.access_class == POLICY_CLASS_MISC) { 160 + g_effective_entry.read_policy = POLICY_DENY_SKIP; 161 + g_effective_entry.write_policy = POLICY_DENY_SKIP; 162 + } 163 + } else if (g_profile == POLICY_PROFILE_LINUX_BOOT) { 164 + if (g_effective_entry.access_class == POLICY_CLASS_PMU || 165 + g_effective_entry.key == KEY(3, 0, 12, 0, 1)) { 166 + g_effective_entry.read_policy = POLICY_DENY_SKIP; 167 + g_effective_entry.write_policy = POLICY_DENY_SKIP; 168 + } 169 + if (g_effective_entry.access_class == POLICY_CLASS_MMU) { 170 + g_effective_entry.writable_mask = UINT64_MAX; 171 + } 172 + if (g_effective_entry.key == KEY(3, 3, 9, 12, 0)) { 173 + g_effective_entry.reset_value = 0u; 174 + } 175 + } else if (g_profile == POLICY_PROFILE_DEBUG) { 176 + if (g_effective_entry.access_class == POLICY_CLASS_DEBUG || 177 + g_effective_entry.access_class == POLICY_CLASS_PMU) { 178 + g_effective_entry.read_policy = POLICY_EMULATE_READ; 179 + g_effective_entry.write_policy = POLICY_WRITE_MASK; 180 + g_effective_entry.writable_mask = 0u; 181 + } 182 + } 183 + return &g_effective_entry; 184 + } 185 + 186 + bool policy_apply_sysreg(struct vcpu *vcpu, const struct sysreg_access *access, 187 + uint64_t operand, struct policy_decision *decision) 188 + { 189 + if (vcpu == (struct vcpu *)0 || access == (const struct sysreg_access *)0 || 190 + decision == (struct policy_decision *)0) { 191 + return false; 192 + } 193 + hv_memset(decision, 0, sizeof(*decision)); 194 + const struct policy_entry *entry = effective_entry(sysreg_policy_lookup(access->key)); 195 + decision->entry = entry; 196 + decision->action = POLICY_DENY_SKIP; 197 + decision->reason = 250u; 198 + 199 + if (entry == (const struct policy_entry *)0) { 200 + return true; 201 + } 202 + 203 + decision->action = access->is_read ? entry->read_policy : entry->write_policy; 204 + decision->reason = entry->violation_reason; 205 + 206 + uint64_t *slot = shadow_slot(vcpu, access->key); 207 + switch (decision->action) { 208 + case POLICY_EMULATE_READ: 209 + decision->value = slot != (uint64_t *)0 ? *slot : entry->reset_value; 210 + return true; 211 + case POLICY_EMULATE_WRITE: 212 + if (slot == (uint64_t *)0) { 213 + decision->action = POLICY_DENY_UNDEF; 214 + return true; 215 + } 216 + *slot = operand & entry->writable_mask; 217 + decision->value = *slot; 218 + if (g_profile == POLICY_PROFILE_LINUX_BOOT && is_el1_mmu_key(access->key)) { 219 + arch_sync_el1_sysregs(vcpu); 220 + } 221 + timer_vcpu_sync(vcpu); 222 + return true; 223 + case POLICY_WRITE_MASK: 224 + if (slot == (uint64_t *)0) { 225 + decision->action = POLICY_DENY_UNDEF; 226 + return true; 227 + } 228 + *slot = (*slot & ~entry->writable_mask) | (operand & entry->writable_mask); 229 + decision->value = *slot; 230 + if (g_profile == POLICY_PROFILE_LINUX_BOOT && is_el1_mmu_key(access->key)) { 231 + arch_sync_el1_sysregs(vcpu); 232 + } 233 + timer_vcpu_sync(vcpu); 234 + return true; 235 + case POLICY_DENY_SKIP: 236 + case POLICY_DENY_UNDEF: 237 + case POLICY_PAUSE: 238 + case POLICY_PANIC: 239 + case POLICY_ALLOW_NATIVE: 240 + default: 241 + return true; 242 + } 243 + } 244 + 245 + void policy_dump(void) 246 + { 247 + uart_puts("policy profile="); 248 + uart_puts(policy_profile_name(g_profile)); 249 + uart_puts("\n"); 250 + for (uint32_t i = 0; i < HV_ARRAY_SIZE(g_policy); i++) { 251 + const struct policy_entry *entry = effective_entry(&g_policy[i]); 252 + uart_puts("policy key="); 253 + uart_put_hex64(entry->key); 254 + uart_puts(" name="); 255 + uart_puts(entry->name); 256 + uart_puts(" read="); 257 + uart_put_dec64((uint64_t)entry->read_policy); 258 + uart_puts(" write="); 259 + uart_put_dec64((uint64_t)entry->write_policy); 260 + uart_puts(" mask="); 261 + uart_put_hex64(entry->writable_mask); 262 + uart_puts("\n"); 263 + } 264 + } 265 + 266 + bool policy_selftest(void) 267 + { 268 + enum policy_profile saved = g_profile; 269 + bool profile_ok; 270 + for (uint32_t i = 0; i < HV_ARRAY_SIZE(g_policy); i++) { 271 + if (sysreg_policy_lookup(g_policy[i].key) != &g_policy[i]) { 272 + return false; 273 + } 274 + for (uint32_t j = i + 1u; j < HV_ARRAY_SIZE(g_policy); j++) { 275 + if (g_policy[i].key == g_policy[j].key) { 276 + return false; 277 + } 278 + } 279 + } 280 + profile_ok = policy_set_profile_name("linux-boot") && 281 + policy_get_profile() == POLICY_PROFILE_LINUX_BOOT && 282 + policy_set_profile(saved); 283 + return sysreg_policy_lookup(KEY(3, 4, 1, 1, 0)) != (const struct policy_entry *)0 && 284 + sysreg_policy_lookup(KEY(3, 7, 15, 15, 7)) == (const struct policy_entry *)0 && 285 + profile_ok; 286 + }
+107
core/psci.c
··· 1 + #include "hv/arch.h" 2 + #include "hv/log.h" 3 + #include "hv/psci.h" 4 + #include "hv/string.h" 5 + #include "hv/uart.h" 6 + 7 + #define PSCI32_VERSION 0x84000000ull 8 + #define PSCI32_CPU_SUSPEND 0x84000001ull 9 + #define PSCI32_CPU_OFF 0x84000002ull 10 + #define PSCI32_CPU_ON 0x84000003ull 11 + #define PSCI32_AFFINITY_INFO 0x84000004ull 12 + #define PSCI32_SYSTEM_OFF 0x84000008ull 13 + #define PSCI32_SYSTEM_RESET 0x84000009ull 14 + #define PSCI32_FEATURES 0x8400000aull 15 + #define PSCI64_CPU_SUSPEND 0xc4000001ull 16 + #define PSCI64_CPU_ON 0xc4000003ull 17 + #define PSCI64_AFFINITY_INFO 0xc4000004ull 18 + 19 + #define PSCI_RET_SUCCESS 0ull 20 + #define PSCI_RET_NOT_SUPPORTED UINT64_MAX 21 + #define PSCI_RET_INVALID_PARAMETERS (~0ull - 1ull) 22 + #define PSCI_RET_DENIED (~0ull - 2ull) 23 + 24 + static uint64_t g_calls; 25 + static uint64_t g_denied_cpu_on; 26 + static uint64_t g_last_function; 27 + 28 + static void log_psci(struct vcpu *vcpu, uint64_t fid, uint64_t ret) 29 + { 30 + struct log_event ev; 31 + hv_memset(&ev, 0, sizeof(ev)); 32 + ev.kind = LOG_HVC; 33 + ev.vcpu = vcpu != (struct vcpu *)0 ? vcpu->id : UINT64_MAX; 34 + ev.operand = fid; 35 + ev.result = ret; 36 + ev.reason = 320u; 37 + ev.name = "psci"; 38 + log_event_commit(&ev); 39 + } 40 + 41 + bool psci_handle_call(struct vcpu *vcpu, uint64_t function_id) 42 + { 43 + uint64_t ret = PSCI_RET_NOT_SUPPORTED; 44 + 45 + if (vcpu == (struct vcpu *)0) { 46 + return false; 47 + } 48 + 49 + g_calls++; 50 + g_last_function = function_id; 51 + switch (function_id) { 52 + case PSCI32_VERSION: 53 + ret = 0x00010001ull; 54 + break; 55 + case PSCI32_FEATURES: { 56 + uint64_t query = vcpu_read_gpr(vcpu, 1u); 57 + if (query == PSCI32_VERSION || query == PSCI32_FEATURES || 58 + query == PSCI32_AFFINITY_INFO || query == PSCI64_AFFINITY_INFO || 59 + query == PSCI32_SYSTEM_OFF || query == PSCI32_SYSTEM_RESET) { 60 + ret = PSCI_RET_SUCCESS; 61 + } 62 + break; 63 + } 64 + case PSCI32_AFFINITY_INFO: 65 + case PSCI64_AFFINITY_INFO: 66 + ret = PSCI_RET_SUCCESS; 67 + break; 68 + case PSCI32_CPU_SUSPEND: 69 + case PSCI64_CPU_SUSPEND: 70 + ret = PSCI_RET_DENIED; 71 + break; 72 + case PSCI32_CPU_ON: 73 + case PSCI64_CPU_ON: 74 + g_denied_cpu_on++; 75 + ret = PSCI_RET_DENIED; 76 + break; 77 + case PSCI32_CPU_OFF: 78 + ret = PSCI_RET_DENIED; 79 + break; 80 + case PSCI32_SYSTEM_OFF: 81 + case PSCI32_SYSTEM_RESET: 82 + log_psci(vcpu, function_id, PSCI_RET_SUCCESS); 83 + uart_puts("psci power request function="); 84 + uart_put_hex64(function_id); 85 + uart_puts("; halting\n"); 86 + arch_halt(); 87 + default: 88 + ret = PSCI_RET_NOT_SUPPORTED; 89 + break; 90 + } 91 + 92 + vcpu_write_gpr(vcpu, 0u, ret); 93 + log_psci(vcpu, function_id, ret); 94 + arch_advance_guest_pc(vcpu); 95 + return true; 96 + } 97 + 98 + void psci_dump(void) 99 + { 100 + uart_puts("psci calls="); 101 + uart_put_dec64(g_calls); 102 + uart_puts(" denied_cpu_on="); 103 + uart_put_dec64(g_denied_cpu_on); 104 + uart_puts(" last="); 105 + uart_put_hex64(g_last_function); 106 + uart_puts("\n"); 107 + }
+42
core/selftest.c
··· 1 + #include "hv/allocator.h" 2 + #include "hv/arch.h" 3 + #include "hv/config.h" 4 + #include "hv/el2_mmu.h" 5 + #include "hv/log.h" 6 + #include "hv/panic.h" 7 + #include "hv/policy.h" 8 + #include "hv/selftest.h" 9 + #include "hv/stage2.h" 10 + #include "hv/sysreg.h" 11 + 12 + static void require(bool ok, const char *name) 13 + { 14 + if (!ok) { 15 + panic(name); 16 + } 17 + log_simple(LOG_SELFTEST, 0u, (uint64_t)(uintptr_t)name, 0u); 18 + } 19 + 20 + void selftests_run_or_panic(void) 21 + { 22 + require(hv_is_aligned_u64((uint64_t)(uintptr_t)__vectors_el2, 2048u), "selftest vectors alignment"); 23 + require(stage2_selftest(), "selftest stage2 descriptors"); 24 + require(el2_mmu_selftest(), "selftest el2 mmu plan"); 25 + require(CONFIG_GUEST_RAM_SIZE != 0u && hv_is_aligned_u64(CONFIG_GUEST_RAM_BASE, CONFIG_PAGE_SIZE), 26 + "selftest guest memory bounds"); 27 + require(policy_selftest(), "selftest policy table"); 28 + require(sysreg_selftest(), "selftest sysreg decoder"); 29 + require(log_selftest(), "selftest log wraparound"); 30 + require(allocator_selftest(), "selftest allocator"); 31 + 32 + uint64_t hcr; 33 + uint64_t sctlr; 34 + uint64_t vttbr; 35 + __asm__ volatile("mrs %0, hcr_el2" : "=r"(hcr)); 36 + __asm__ volatile("mrs %0, sctlr_el2" : "=r"(sctlr)); 37 + __asm__ volatile("mrs %0, vttbr_el2" : "=r"(vttbr)); 38 + require((hcr & HV_BIT(0)) != 0u && (hcr & HV_BIT(31)) != 0u, "selftest hcr trap sanity"); 39 + require((sctlr & (HV_BIT(0) | HV_BIT(2) | HV_BIT(12))) == 40 + (HV_BIT(0) | HV_BIT(2) | HV_BIT(12)), "selftest sctlr el2 mmu enabled"); 41 + require((vttbr & 0x0000fffffffff000ull) == stage2_vttbr(), "selftest vttbr root"); 42 + }
+101
core/string.c
··· 1 + #include "hv/string.h" 2 + 3 + void *hv_memset(void *dst, int value, size_t len) 4 + { 5 + unsigned char *p = (unsigned char *)dst; 6 + while (len-- != 0u) { 7 + *p++ = (unsigned char)value; 8 + } 9 + return dst; 10 + } 11 + 12 + void *hv_memcpy(void *dst, const void *src, size_t len) 13 + { 14 + unsigned char *d = (unsigned char *)dst; 15 + const unsigned char *s = (const unsigned char *)src; 16 + while (len-- != 0u) { 17 + *d++ = *s++; 18 + } 19 + return dst; 20 + } 21 + 22 + void *hv_memmove(void *dst, const void *src, size_t len) 23 + { 24 + unsigned char *d = (unsigned char *)dst; 25 + const unsigned char *s = (const unsigned char *)src; 26 + if (d == s || len == 0u) { 27 + return dst; 28 + } 29 + if (d < s) { 30 + while (len-- != 0u) { 31 + *d++ = *s++; 32 + } 33 + } else { 34 + d += len; 35 + s += len; 36 + while (len-- != 0u) { 37 + *--d = *--s; 38 + } 39 + } 40 + return dst; 41 + } 42 + 43 + int hv_memcmp(const void *a, const void *b, size_t len) 44 + { 45 + const unsigned char *pa = (const unsigned char *)a; 46 + const unsigned char *pb = (const unsigned char *)b; 47 + for (size_t i = 0; i < len; i++) { 48 + if (pa[i] != pb[i]) { 49 + return (int)pa[i] - (int)pb[i]; 50 + } 51 + } 52 + return 0; 53 + } 54 + 55 + size_t hv_strlen(const char *s) 56 + { 57 + size_t n = 0; 58 + while (s[n] != '\0') { 59 + n++; 60 + } 61 + return n; 62 + } 63 + 64 + int hv_strcmp(const char *a, const char *b) 65 + { 66 + while (*a != '\0' && *a == *b) { 67 + a++; 68 + b++; 69 + } 70 + return (int)(unsigned char)*a - (int)(unsigned char)*b; 71 + } 72 + 73 + int hv_strncmp(const char *a, const char *b, size_t n) 74 + { 75 + for (size_t i = 0; i < n; i++) { 76 + if (a[i] == '\0' || a[i] != b[i]) { 77 + return (int)(unsigned char)a[i] - (int)(unsigned char)b[i]; 78 + } 79 + } 80 + return 0; 81 + } 82 + 83 + void *memset(void *dst, int value, size_t len) 84 + { 85 + return hv_memset(dst, value, len); 86 + } 87 + 88 + void *memcpy(void *dst, const void *src, size_t len) 89 + { 90 + return hv_memcpy(dst, src, len); 91 + } 92 + 93 + void *memmove(void *dst, const void *src, size_t len) 94 + { 95 + return hv_memmove(dst, src, len); 96 + } 97 + 98 + int memcmp(const void *a, const void *b, size_t len) 99 + { 100 + return hv_memcmp(a, b, len); 101 + }
+90
core/sysreg.c
··· 1 + #include "hv/sysreg.h" 2 + 3 + uint64_t sysreg_key_make(uint32_t op0, uint32_t op1, uint32_t crn, uint32_t crm, uint32_t op2) 4 + { 5 + return ((uint64_t)(op0 & 0x3u) << 14u) | 6 + ((uint64_t)(op1 & 0x7u) << 11u) | 7 + ((uint64_t)(crn & 0xfu) << 7u) | 8 + ((uint64_t)(crm & 0xfu) << 3u) | 9 + (uint64_t)(op2 & 0x7u); 10 + } 11 + 12 + bool sysreg_decode_from_esr_iss(uint64_t iss, struct sysreg_access *out) 13 + { 14 + if (out == (struct sysreg_access *)0) { 15 + return false; 16 + } 17 + out->op0 = (uint32_t)((iss >> 20u) & 0x3u); 18 + out->op2 = (uint32_t)((iss >> 17u) & 0x7u); 19 + out->op1 = (uint32_t)((iss >> 14u) & 0x7u); 20 + out->crn = (uint32_t)((iss >> 10u) & 0xfu); 21 + out->rt = (uint32_t)((iss >> 5u) & 0x1fu); 22 + out->crm = (uint32_t)((iss >> 1u) & 0xfu); 23 + out->is_read = ((iss & 1u) == ESR_SYSREG_DIR_READ); 24 + out->key = sysreg_key_make(out->op0, out->op1, out->crn, out->crm, out->op2); 25 + return true; 26 + } 27 + 28 + struct sysreg_name { 29 + uint64_t key; 30 + const char *name; 31 + }; 32 + 33 + #define KEY(op0, op1, crn, crm, op2) SYSREG_KEY_CONST((op0), (op1), (crn), (crm), (op2)) 34 + 35 + static const struct sysreg_name g_names[] = { 36 + { KEY(3, 0, 0, 0, 0), "midr_el1" }, 37 + { KEY(3, 0, 0, 0, 5), "mpidr_el1" }, 38 + { KEY(3, 0, 0, 4, 0), "id_aa64pfr0_el1" }, 39 + { KEY(3, 0, 0, 4, 1), "id_aa64pfr1_el1" }, 40 + { KEY(3, 0, 0, 6, 0), "id_aa64isar0_el1" }, 41 + { KEY(3, 0, 0, 6, 1), "id_aa64isar1_el1" }, 42 + { KEY(3, 0, 0, 7, 0), "id_aa64mmfr0_el1" }, 43 + { KEY(3, 0, 0, 7, 1), "id_aa64mmfr1_el1" }, 44 + { KEY(3, 1, 0, 0, 1), "clidr_el1" }, 45 + { KEY(3, 2, 0, 0, 0), "csselr_el1" }, 46 + { KEY(3, 3, 0, 0, 1), "ctr_el0" }, 47 + { KEY(3, 3, 0, 0, 7), "dczid_el0" }, 48 + { KEY(3, 0, 1, 0, 0), "sctlr_el1" }, 49 + { KEY(3, 0, 1, 0, 1), "actlr_el1" }, 50 + { KEY(3, 0, 1, 0, 2), "cpacr_el1" }, 51 + { KEY(3, 4, 1, 1, 0), "hcr_el2" }, 52 + { KEY(3, 6, 1, 1, 0), "scr_el3" }, 53 + { KEY(3, 0, 2, 0, 0), "ttbr0_el1" }, 54 + { KEY(3, 0, 2, 0, 1), "ttbr1_el1" }, 55 + { KEY(3, 0, 2, 0, 2), "tcr_el1" }, 56 + { KEY(3, 4, 2, 1, 0), "vttbr_el2" }, 57 + { KEY(3, 3, 9, 12, 0), "pmcr_el0" }, 58 + { KEY(3, 0, 10, 2, 0), "mair_el1" }, 59 + { KEY(3, 0, 12, 0, 0), "vbar_el1" }, 60 + { KEY(3, 0, 12, 0, 1), "rvbar_el1" }, 61 + { KEY(3, 0, 13, 0, 1), "contextidr_el1" }, 62 + { KEY(3, 0, 13, 0, 4), "tpidr_el1" }, 63 + { KEY(3, 3, 13, 0, 3), "tpidrro_el0" }, 64 + { KEY(3, 0, 14, 1, 0), "cntkctl_el1" }, 65 + { KEY(3, 3, 14, 3, 0), "cntv_tval_el0" }, 66 + { KEY(3, 3, 14, 3, 1), "cntv_ctl_el0" }, 67 + { KEY(3, 3, 14, 3, 2), "cntv_cval_el0" }, 68 + { KEY(2, 0, 0, 2, 2), "mdscr_el1" }, 69 + }; 70 + 71 + const char *sysreg_key_name(uint64_t key) 72 + { 73 + for (uint32_t i = 0; i < HV_ARRAY_SIZE(g_names); i++) { 74 + if (g_names[i].key == key) { 75 + return g_names[i].name; 76 + } 77 + } 78 + return "unknown"; 79 + } 80 + 81 + bool sysreg_selftest(void) 82 + { 83 + struct sysreg_access a; 84 + uint64_t iss = (3ull << 20u) | (0ull << 17u) | (0ull << 14u) | 85 + (0ull << 10u) | (0ull << 5u) | (0ull << 1u) | 1ull; 86 + if (!sysreg_decode_from_esr_iss(iss, &a)) { 87 + return false; 88 + } 89 + return a.is_read && a.rt == 0u && a.key == KEY(3, 0, 0, 0, 0); 90 + }
+316
core/trap.c
··· 1 + #include "hv/arch.h" 2 + #include "hv/config.h" 3 + #include "hv/gicv3.h" 4 + #include "hv/log.h" 5 + #include "hv/monitor.h" 6 + #include "hv/panic.h" 7 + #include "hv/policy.h" 8 + #include "hv/psci.h" 9 + #include "hv/stage2.h" 10 + #include "hv/string.h" 11 + #include "hv/sysreg.h" 12 + #include "hv/timer.h" 13 + #include "hv/trap.h" 14 + #include "hv/uart.h" 15 + 16 + #define HVC_GET_HV_ID 0u 17 + #define HVC_DUMP_LOG 1u 18 + #define HVC_GET_STATUS 2u 19 + #define HVC_PAUSE 3u 20 + #define HVC_GUEST_CONSOLE_WRITE 4u 21 + #define HVC_REPORT_EXCEPTION 5u 22 + #define HVC_ERR_UNKNOWN UINT64_MAX 23 + 24 + static uint32_t action_to_log(enum policy_action action) 25 + { 26 + switch (action) { 27 + case POLICY_EMULATE_READ: return LOG_ACT_EMULATE_READ; 28 + case POLICY_EMULATE_WRITE: return LOG_ACT_EMULATE_WRITE; 29 + case POLICY_WRITE_MASK: return LOG_ACT_WRITE_MASK; 30 + case POLICY_DENY_SKIP: return LOG_ACT_DENY_SKIP; 31 + case POLICY_DENY_UNDEF: return LOG_ACT_DENY_UNDEF; 32 + case POLICY_PAUSE: return LOG_ACT_PAUSE; 33 + case POLICY_PANIC: return LOG_ACT_PANIC; 34 + case POLICY_ALLOW_NATIVE: 35 + default: return LOG_ACT_NONE; 36 + } 37 + } 38 + 39 + static void log_trap_event(const struct vcpu *vcpu, const struct trap_frame *frame, 40 + const struct sysreg_access *access, 41 + const struct policy_decision *decision, 42 + uint64_t operand) 43 + { 44 + struct log_event ev; 45 + hv_memset(&ev, 0, sizeof(ev)); 46 + ev.kind = LOG_TRAP; 47 + ev.vcpu = vcpu->id; 48 + ev.ec = esr_ec(frame->esr_el2); 49 + ev.iss = frame->esr_el2 & 0x01ffffffu; 50 + ev.elr = frame->elr_el2; 51 + ev.far = frame->far_el2; 52 + ev.hpfar = frame->hpfar_el2; 53 + ev.pstate = frame->spsr_el2; 54 + if (access != (const struct sysreg_access *)0) { 55 + ev.sysreg_key = access->key; 56 + ev.is_read = access->is_read ? 1u : 0u; 57 + ev.rt = access->rt; 58 + ev.name = decision->entry != (const struct policy_entry *)0 ? 59 + decision->entry->name : sysreg_key_name(access->key); 60 + } 61 + if (decision != (const struct policy_decision *)0) { 62 + ev.action = action_to_log(decision->action); 63 + ev.result = decision->value; 64 + ev.reason = decision->reason; 65 + } 66 + ev.operand = operand; 67 + log_event_commit(&ev); 68 + } 69 + 70 + static bool guest_ptr_to_host(uint64_t ipa, uint64_t len, const char **out) 71 + { 72 + uint64_t end; 73 + if (out == (const char **)0 || hv_add_overflow_u64(ipa, len, &end)) { 74 + return false; 75 + } 76 + if (ipa < CONFIG_GUEST_IPA_BASE || 77 + end > (CONFIG_GUEST_IPA_BASE + CONFIG_GUEST_RAM_SIZE)) { 78 + return false; 79 + } 80 + *out = (const char *)(uintptr_t)(CONFIG_GUEST_RAM_BASE + (ipa - CONFIG_GUEST_IPA_BASE)); 81 + return true; 82 + } 83 + 84 + static void handle_hvc(struct vcpu *vcpu) 85 + { 86 + uint64_t num = vcpu_read_gpr(vcpu, 0u); 87 + vcpu->stats.hvc_calls++; 88 + 89 + struct log_event ev; 90 + hv_memset(&ev, 0, sizeof(ev)); 91 + ev.kind = LOG_HVC; 92 + ev.vcpu = vcpu->id; 93 + ev.ec = ESR_EC_HVC64; 94 + ev.iss = vcpu->last_esr & 0x01ffffffu; 95 + ev.elr = vcpu->pc; 96 + ev.far = vcpu->last_far; 97 + ev.hpfar = vcpu->last_hpfar; 98 + ev.pstate = vcpu->pstate; 99 + ev.operand = num; 100 + ev.action = LOG_ACT_NONE; 101 + ev.reason = 300u; 102 + 103 + switch (num) { 104 + case HVC_GET_HV_ID: 105 + vcpu_write_gpr(vcpu, 0u, 0x415249454cull); 106 + ev.result = 0x415249454cull; 107 + break; 108 + case HVC_DUMP_LOG: 109 + log_dump(64u); 110 + vcpu_write_gpr(vcpu, 0u, 0u); 111 + break; 112 + case HVC_GET_STATUS: 113 + vcpu_write_gpr(vcpu, 0u, (uint64_t)vcpu->state); 114 + ev.result = (uint64_t)vcpu->state; 115 + break; 116 + case HVC_PAUSE: 117 + vcpu->state = VCPU_PAUSED; 118 + vcpu_write_gpr(vcpu, 0u, 0u); 119 + log_event_commit(&ev); 120 + monitor_loop(vcpu, "guest requested pause"); 121 + return; 122 + case HVC_GUEST_CONSOLE_WRITE: { 123 + const char *s = (const char *)0; 124 + uint64_t ptr = vcpu_read_gpr(vcpu, 1u); 125 + uint64_t len = vcpu_read_gpr(vcpu, 2u); 126 + if (len > 256u || !guest_ptr_to_host(ptr, len, &s)) { 127 + vcpu_write_gpr(vcpu, 0u, HVC_ERR_UNKNOWN); 128 + ev.reason = 301u; 129 + break; 130 + } 131 + uart_puts("guest: "); 132 + for (uint64_t i = 0; i < len; i++) { 133 + uart_putc(s[i]); 134 + } 135 + uart_puts("\n"); 136 + vcpu_write_gpr(vcpu, 0u, 0u); 137 + break; 138 + } 139 + case HVC_REPORT_EXCEPTION: { 140 + uint64_t guest_esr = vcpu_read_gpr(vcpu, 1u); 141 + uint64_t guest_elr = vcpu_read_gpr(vcpu, 2u); 142 + uint64_t guest_far = vcpu_read_gpr(vcpu, 3u); 143 + struct log_event ge; 144 + hv_memset(&ge, 0, sizeof(ge)); 145 + ge.kind = LOG_ABORT; 146 + ge.vcpu = vcpu->id; 147 + ge.ec = esr_ec(guest_esr); 148 + ge.iss = guest_esr & 0x01ffffffu; 149 + ge.elr = guest_elr; 150 + ge.far = guest_far; 151 + ge.action = LOG_ACT_NONE; 152 + ge.reason = 303u; 153 + ge.pstate = vcpu->pstate; 154 + ge.name = "guest_exception_report"; 155 + log_event_commit(&ge); 156 + vcpu_write_gpr(vcpu, 0u, 0u); 157 + break; 158 + } 159 + default: 160 + if ((num & 0xffff0000ull) == 0x84000000ull || 161 + (num & 0xffff0000ull) == 0xc4000000ull) { 162 + log_event_commit(&ev); 163 + (void)psci_handle_call(vcpu, num); 164 + return; 165 + } 166 + vcpu_write_gpr(vcpu, 0u, HVC_ERR_UNKNOWN); 167 + ev.reason = 302u; 168 + break; 169 + } 170 + 171 + log_event_commit(&ev); 172 + } 173 + 174 + static void handle_sysreg(struct vcpu *vcpu, const struct trap_frame *frame) 175 + { 176 + struct sysreg_access access; 177 + struct policy_decision decision; 178 + uint64_t operand = 0; 179 + 180 + if (!sysreg_decode_from_esr_iss(frame->esr_el2 & 0x01ffffffu, &access)) { 181 + panic("failed to decode sysreg iss"); 182 + } 183 + 184 + if (!access.is_read) { 185 + operand = vcpu_read_gpr(vcpu, access.rt); 186 + } 187 + 188 + if (!policy_apply_sysreg(vcpu, &access, operand, &decision)) { 189 + panic("policy engine failed"); 190 + } 191 + 192 + if (access.is_read && decision.action == POLICY_EMULATE_READ) { 193 + vcpu_write_gpr(vcpu, access.rt, decision.value); 194 + } 195 + 196 + log_trap_event(vcpu, frame, &access, &decision, operand); 197 + vcpu->stats.sysreg_traps++; 198 + 199 + switch (decision.action) { 200 + case POLICY_EMULATE_READ: 201 + case POLICY_EMULATE_WRITE: 202 + case POLICY_WRITE_MASK: 203 + case POLICY_DENY_SKIP: 204 + arch_advance_guest_pc(vcpu); 205 + break; 206 + case POLICY_DENY_UNDEF: 207 + arch_inject_guest_undef(vcpu); 208 + if (vcpu->state == VCPU_PAUSED) { 209 + monitor_loop(vcpu, "policy could not inject undefined instruction"); 210 + } 211 + break; 212 + case POLICY_PAUSE: 213 + vcpu->state = VCPU_PAUSED; 214 + monitor_loop(vcpu, "policy pause"); 215 + break; 216 + case POLICY_PANIC: 217 + panic("policy panic action"); 218 + case POLICY_ALLOW_NATIVE: 219 + default: 220 + panic("unsafe native sysreg policy"); 221 + } 222 + } 223 + 224 + void trap_handle_lower_sync(struct trap_frame *frame) 225 + { 226 + struct vcpu *vcpu = vcpu_current(); 227 + if (vcpu == (struct vcpu *)0) { 228 + panic("trap without current vcpu"); 229 + } 230 + vcpu_load_frame(vcpu, frame); 231 + gicv3_sync_vcpu_state(vcpu); 232 + vcpu->stats.traps++; 233 + vcpu->state = VCPU_RUNNING; 234 + 235 + uint32_t ec = esr_ec(frame->esr_el2); 236 + switch (ec) { 237 + case ESR_EC_SYSREG: 238 + handle_sysreg(vcpu, frame); 239 + break; 240 + case ESR_EC_HVC64: 241 + handle_hvc(vcpu); 242 + break; 243 + case ESR_EC_SMC64: { 244 + uint64_t function_id = vcpu_read_gpr(vcpu, 0u); 245 + if ((function_id & 0xffff0000ull) == 0x84000000ull || 246 + (function_id & 0xffff0000ull) == 0xc4000000ull) { 247 + (void)psci_handle_call(vcpu, function_id); 248 + } else { 249 + vcpu->stats.smc_denied++; 250 + struct policy_decision d; 251 + hv_memset(&d, 0, sizeof(d)); 252 + d.action = POLICY_DENY_UNDEF; 253 + d.reason = 400u; 254 + log_trap_event(vcpu, frame, (const struct sysreg_access *)0, &d, 0u); 255 + arch_inject_guest_undef(vcpu); 256 + if (vcpu->state == VCPU_PAUSED) { 257 + monitor_loop(vcpu, "guest smc denied"); 258 + } 259 + } 260 + break; 261 + } 262 + case ESR_EC_WFI_WFE: 263 + vcpu->stats.wfx_traps++; 264 + log_simple(LOG_TRAP, 401u, frame->esr_el2, frame->elr_el2); 265 + arch_advance_guest_pc(vcpu); 266 + break; 267 + case ESR_EC_IABT_LOWER: 268 + vcpu->stats.instruction_aborts++; 269 + if (stage2_handle_abort(vcpu, frame, true)) { 270 + break; 271 + } 272 + monitor_loop(vcpu, "guest instruction abort"); 273 + break; 274 + case ESR_EC_DABT_LOWER: 275 + vcpu->stats.data_aborts++; 276 + if (stage2_handle_abort(vcpu, frame, false)) { 277 + break; 278 + } 279 + monitor_loop(vcpu, "guest data abort"); 280 + break; 281 + case ESR_EC_BRK64: 282 + case ESR_EC_UNKNOWN: 283 + default: 284 + vcpu->stats.unknown_exceptions++; 285 + log_simple(LOG_TRAP, 404u, frame->esr_el2, frame->elr_el2); 286 + monitor_loop(vcpu, "guest unknown or brk exception"); 287 + break; 288 + } 289 + 290 + timer_vcpu_sync(vcpu); 291 + gicv3_flush_vcpu_state(vcpu); 292 + vcpu_store_frame(vcpu, frame); 293 + } 294 + 295 + void trap_handle_current_sync(struct trap_frame *frame) 296 + { 297 + panic_with_code("unexpected EL2 synchronous exception", frame != (struct trap_frame *)0 ? frame->esr_el2 : 0u); 298 + } 299 + 300 + void trap_handle_irq(struct trap_frame *frame) 301 + { 302 + uint32_t irq = gicv3_ack_irq(); 303 + struct log_event ev; 304 + hv_memset(&ev, 0, sizeof(ev)); 305 + ev.kind = LOG_TRAP; 306 + ev.vcpu = vcpu_current() != (struct vcpu *)0 ? vcpu_current()->id : UINT64_MAX; 307 + ev.ec = 0xffu; 308 + ev.iss = irq; 309 + ev.elr = frame != (struct trap_frame *)0 ? frame->elr_el2 : 0u; 310 + ev.pstate = frame != (struct trap_frame *)0 ? frame->spsr_el2 : 0u; 311 + ev.reason = irq >= 1020u ? 501u : 500u; 312 + log_event_commit(&ev); 313 + if (irq < 1020u) { 314 + gicv3_eoi(irq); 315 + } 316 + }
+149
core/vcpu.c
··· 1 + #include "hv/config.h" 2 + #include "hv/arch.h" 3 + #include "hv/panic.h" 4 + #include "hv/string.h" 5 + #include "hv/uart.h" 6 + #include "hv/vcpu.h" 7 + 8 + _Static_assert(offsetof(struct trap_frame, x) == 0u, "trap_frame x offset"); 9 + _Static_assert(offsetof(struct trap_frame, sp_el0) == 248u, "trap_frame sp_el0 offset"); 10 + _Static_assert(offsetof(struct trap_frame, sp_el1) == 256u, "trap_frame sp_el1 offset"); 11 + _Static_assert(offsetof(struct trap_frame, elr_el2) == 264u, "trap_frame elr offset"); 12 + _Static_assert(offsetof(struct trap_frame, spsr_el2) == 272u, "trap_frame spsr offset"); 13 + _Static_assert(offsetof(struct trap_frame, esr_el2) == 280u, "trap_frame esr offset"); 14 + _Static_assert(offsetof(struct trap_frame, far_el2) == 288u, "trap_frame far offset"); 15 + _Static_assert(offsetof(struct trap_frame, hpfar_el2) == 296u, "trap_frame hpfar offset"); 16 + _Static_assert(sizeof(struct trap_frame) == 304u, "trap_frame size"); 17 + 18 + _Static_assert(offsetof(struct vcpu, x) == 0u, "vcpu x offset"); 19 + _Static_assert(offsetof(struct vcpu, sp_el0) == 248u, "vcpu sp_el0 offset"); 20 + _Static_assert(offsetof(struct vcpu, sp_el1) == 256u, "vcpu sp_el1 offset"); 21 + _Static_assert(offsetof(struct vcpu, pc) == 264u, "vcpu pc offset"); 22 + _Static_assert(offsetof(struct vcpu, pstate) == 272u, "vcpu pstate offset"); 23 + 24 + static struct vcpu *g_current_vcpu; 25 + 26 + void vcpu_init(struct vcpu *vcpu, uint32_t id, uint64_t entry, uint64_t stack_top) 27 + { 28 + BUG_ON(vcpu == (struct vcpu *)0); 29 + hv_memset(vcpu, 0, sizeof(*vcpu)); 30 + vcpu->id = id; 31 + vcpu->state = VCPU_CREATED; 32 + vcpu->pc = entry; 33 + vcpu->pstate = 0x3c5u; 34 + vcpu->sp_el1 = stack_top; 35 + vcpu->sysregs.sctlr_el1 = 0x30d00800ull; 36 + vcpu->sysregs.mair_el1 = 0xffull; 37 + vcpu->sysregs.cntkctl_el1 = 0u; 38 + vcpu->pending_virtual_irq = 0u; 39 + vcpu->active_virtual_irq = 0u; 40 + vcpu->state = VCPU_READY; 41 + } 42 + 43 + void vcpu_load_frame(struct vcpu *vcpu, const struct trap_frame *frame) 44 + { 45 + BUG_ON(vcpu == (struct vcpu *)0 || frame == (const struct trap_frame *)0); 46 + for (uint32_t i = 0; i < 31u; i++) { 47 + vcpu->x[i] = frame->x[i]; 48 + } 49 + vcpu->sp_el0 = frame->sp_el0; 50 + vcpu->sp_el1 = frame->sp_el1; 51 + vcpu->pc = frame->elr_el2; 52 + vcpu->pstate = frame->spsr_el2; 53 + vcpu->last_esr = frame->esr_el2; 54 + vcpu->last_far = frame->far_el2; 55 + vcpu->last_hpfar = frame->hpfar_el2; 56 + arch_capture_el1_sysregs(vcpu); 57 + } 58 + 59 + void vcpu_store_frame(const struct vcpu *vcpu, struct trap_frame *frame) 60 + { 61 + BUG_ON(vcpu == (const struct vcpu *)0 || frame == (struct trap_frame *)0); 62 + for (uint32_t i = 0; i < 31u; i++) { 63 + frame->x[i] = vcpu->x[i]; 64 + } 65 + frame->sp_el0 = vcpu->sp_el0; 66 + frame->sp_el1 = vcpu->sp_el1; 67 + frame->elr_el2 = vcpu->pc; 68 + frame->spsr_el2 = vcpu->pstate; 69 + } 70 + 71 + uint64_t vcpu_read_gpr(const struct vcpu *vcpu, uint32_t rt) 72 + { 73 + BUG_ON(vcpu == (const struct vcpu *)0); 74 + if (rt >= 31u) { 75 + return 0u; 76 + } 77 + return vcpu->x[rt]; 78 + } 79 + 80 + void vcpu_write_gpr(struct vcpu *vcpu, uint32_t rt, uint64_t value) 81 + { 82 + BUG_ON(vcpu == (struct vcpu *)0); 83 + if (rt < 31u) { 84 + vcpu->x[rt] = value; 85 + } 86 + } 87 + 88 + void vcpu_set_pending_irq(struct vcpu *vcpu, uint32_t intid) 89 + { 90 + BUG_ON(vcpu == (struct vcpu *)0); 91 + if (intid < 64u) { 92 + vcpu->pending_virtual_irq |= (1ull << intid); 93 + } 94 + } 95 + 96 + void vcpu_clear_pending_irq(struct vcpu *vcpu, uint32_t intid) 97 + { 98 + BUG_ON(vcpu == (struct vcpu *)0); 99 + if (intid < 64u) { 100 + vcpu->pending_virtual_irq &= ~(1ull << intid); 101 + } 102 + } 103 + 104 + struct vcpu *vcpu_current(void) 105 + { 106 + return g_current_vcpu; 107 + } 108 + 109 + void vcpu_set_current(struct vcpu *vcpu) 110 + { 111 + g_current_vcpu = vcpu; 112 + } 113 + 114 + void vcpu_dump(const struct vcpu *vcpu) 115 + { 116 + if (vcpu == (const struct vcpu *)0) { 117 + uart_puts("vcpu none\n"); 118 + return; 119 + } 120 + uart_puts("vcpu id="); 121 + uart_put_dec64(vcpu->id); 122 + uart_puts(" state="); 123 + uart_put_dec64((uint64_t)vcpu->state); 124 + uart_puts(" pc="); 125 + uart_put_hex64(vcpu->pc); 126 + uart_puts(" pstate="); 127 + uart_put_hex64(vcpu->pstate); 128 + uart_puts(" sp_el1="); 129 + uart_put_hex64(vcpu->sp_el1); 130 + uart_puts(" traps="); 131 + uart_put_dec64(vcpu->stats.traps); 132 + uart_puts(" injected_undefs="); 133 + uart_put_dec64(vcpu->stats.injected_undefs); 134 + uart_puts(" pending_irq="); 135 + uart_put_hex64(vcpu->pending_virtual_irq); 136 + uart_puts(" virq_inj="); 137 + uart_put_dec64(vcpu->stats.virtual_irqs_injected); 138 + uart_puts(" virq_done="); 139 + uart_put_dec64(vcpu->stats.virtual_irqs_completed); 140 + uart_puts("\n"); 141 + for (uint32_t i = 0; i < 31u; i++) { 142 + uart_puts("x"); 143 + uart_put_dec64(i); 144 + uart_puts("="); 145 + uart_put_hex64(vcpu->x[i]); 146 + uart_puts((i % 4u) == 3u ? "\n" : " "); 147 + } 148 + uart_puts("\n"); 149 + }
+125
drivers/gicv3.c
··· 1 + #include "hv/config.h" 2 + #include "hv/gicv3.h" 3 + #include "hv/log.h" 4 + #include "hv/mmio.h" 5 + #include "hv/uart.h" 6 + #include "hv/vcpu.h" 7 + 8 + #define GICD_CTLR 0x0000u 9 + #define GICD_TYPER 0x0004u 10 + #define GICR_CTLR 0x0000u 11 + #define GICR_WAKER 0x0014u 12 + #define GICR_WAKER_PROCESSOR_SLEEP (1u << 1) 13 + #define GICR_WAKER_CHILDREN_ASLEEP (1u << 2) 14 + 15 + static uintptr_t gicd(uint32_t off) 16 + { 17 + return (uintptr_t)(CONFIG_GICD_BASE + (uint64_t)off); 18 + } 19 + 20 + static uintptr_t gicr(uint32_t off) 21 + { 22 + return (uintptr_t)(CONFIG_GICR_BASE + (uint64_t)off); 23 + } 24 + 25 + void gicv3_init(void) 26 + { 27 + uint32_t typer = mmio_read32(gicd(GICD_TYPER)); 28 + mmio_write32(gicd(GICD_CTLR), 0u); 29 + 30 + uint32_t waker = mmio_read32(gicr(GICR_WAKER)); 31 + waker &= ~GICR_WAKER_PROCESSOR_SLEEP; 32 + mmio_write32(gicr(GICR_WAKER), waker); 33 + for (uint32_t i = 0; i < 100000u; i++) { 34 + if ((mmio_read32(gicr(GICR_WAKER)) & GICR_WAKER_CHILDREN_ASLEEP) == 0u) { 35 + break; 36 + } 37 + } 38 + 39 + __asm__ volatile( 40 + "msr ICC_SRE_EL2, %0\n" 41 + "isb\n" 42 + "msr ICC_SRE_EL1, %0\n" 43 + "isb\n" 44 + "msr ICC_PMR_EL1, %1\n" 45 + "msr ICC_IGRPEN1_EL1, %0\n" 46 + "isb\n" 47 + :: "r"(1ull), "r"(0xffull) 48 + : "memory"); 49 + 50 + mmio_write32(gicd(GICD_CTLR), 0x3u); 51 + log_simple(LOG_BOOT, 20u, typer, 0u); 52 + gicv3_vcpu_init(); 53 + } 54 + 55 + void gicv3_dump(void) 56 + { 57 + uart_puts("gicv3 gicd="); 58 + uart_put_hex64(CONFIG_GICD_BASE); 59 + uart_puts(" gicr="); 60 + uart_put_hex64(CONFIG_GICR_BASE); 61 + uart_puts(" typer="); 62 + uart_put_hex64(mmio_read32(gicd(GICD_TYPER))); 63 + uart_puts("\n"); 64 + } 65 + 66 + uint32_t gicv3_ack_irq(void) 67 + { 68 + uint64_t irq; 69 + __asm__ volatile("mrs %0, ICC_IAR1_EL1" : "=r"(irq)); 70 + return (uint32_t)(irq & 0x3ffu); 71 + } 72 + 73 + void gicv3_eoi(uint32_t irq) 74 + { 75 + __asm__ volatile("msr ICC_EOIR1_EL1, %0\nisb" :: "r"((uint64_t)irq) : "memory"); 76 + } 77 + 78 + void gicv3_vcpu_init(void) 79 + { 80 + __asm__ volatile( 81 + "msr ICH_HCR_EL2, %0\n" 82 + "msr ICH_VMCR_EL2, %1\n" 83 + "msr ICH_LR0_EL2, xzr\n" 84 + "isb\n" 85 + :: "r"(1ull), "r"(0xff0000ull) 86 + : "memory"); 87 + } 88 + 89 + void gicv3_sync_vcpu_state(struct vcpu *vcpu) 90 + { 91 + uint64_t lr; 92 + uint64_t state; 93 + 94 + if (vcpu == (struct vcpu *)0 || vcpu->active_virtual_irq == 0u) { 95 + return; 96 + } 97 + 98 + __asm__ volatile("mrs %0, ICH_LR0_EL2" : "=r"(lr)); 99 + state = (lr >> 62u) & 0x3u; 100 + if (state == 0u) { 101 + vcpu_clear_pending_irq(vcpu, (uint32_t)vcpu->active_virtual_irq); 102 + vcpu->active_virtual_irq = 0u; 103 + vcpu->stats.virtual_irqs_completed++; 104 + } 105 + } 106 + 107 + void gicv3_flush_vcpu_state(struct vcpu *vcpu) 108 + { 109 + if (vcpu == (struct vcpu *)0) { 110 + return; 111 + } 112 + 113 + if ((vcpu->pending_virtual_irq & (1ull << CONFIG_VTIMER_IRQ)) == 0u) { 114 + vcpu->active_virtual_irq = 0u; 115 + __asm__ volatile("msr ICH_LR0_EL2, xzr\nisb" ::: "memory"); 116 + return; 117 + } 118 + 119 + uint64_t lr = (uint64_t)CONFIG_VTIMER_IRQ | (1ull << 62u) | (1ull << 60u); 120 + if (vcpu->active_virtual_irq != CONFIG_VTIMER_IRQ) { 121 + vcpu->stats.virtual_irqs_injected++; 122 + } 123 + vcpu->active_virtual_irq = CONFIG_VTIMER_IRQ; 124 + __asm__ volatile("msr ICH_LR0_EL2, %0\nisb" :: "r"(lr) : "memory"); 125 + }
+82
drivers/pl011.c
··· 1 + #include "hv/config.h" 2 + #include "hv/mmio.h" 3 + #include "hv/uart.h" 4 + 5 + #define UART_DR 0x000u 6 + #define UART_FR 0x018u 7 + #define UART_IBRD 0x024u 8 + #define UART_FBRD 0x028u 9 + #define UART_LCRH 0x02cu 10 + #define UART_CR 0x030u 11 + #define UART_IMSC 0x038u 12 + #define UART_ICR 0x044u 13 + #define UART_FR_TXFF (1u << 5) 14 + #define UART_FR_RXFE (1u << 4) 15 + 16 + static uintptr_t uart_addr(uint32_t off) 17 + { 18 + return (uintptr_t)(CONFIG_UART_BASE + (uint64_t)off); 19 + } 20 + 21 + void uart_init(void) 22 + { 23 + mmio_write32(uart_addr(UART_CR), 0u); 24 + mmio_write32(uart_addr(UART_ICR), 0x7ffu); 25 + mmio_write32(uart_addr(UART_IBRD), 1u); 26 + mmio_write32(uart_addr(UART_FBRD), 40u); 27 + mmio_write32(uart_addr(UART_LCRH), (3u << 5)); 28 + mmio_write32(uart_addr(UART_IMSC), 0u); 29 + mmio_write32(uart_addr(UART_CR), (1u << 9) | (1u << 8) | 1u); 30 + } 31 + 32 + void uart_putc(char c) 33 + { 34 + if (c == '\n') { 35 + uart_putc('\r'); 36 + } 37 + while ((mmio_read32(uart_addr(UART_FR)) & UART_FR_TXFF) != 0u) { 38 + } 39 + mmio_write32(uart_addr(UART_DR), (uint32_t)(unsigned char)c); 40 + } 41 + 42 + void uart_puts(const char *s) 43 + { 44 + while (*s != '\0') { 45 + uart_putc(*s++); 46 + } 47 + } 48 + 49 + void uart_put_hex64(uint64_t value) 50 + { 51 + static const char hex[] = "0123456789abcdef"; 52 + uart_puts("0x"); 53 + for (int shift = 60; shift >= 0; shift -= 4) { 54 + uart_putc(hex[(value >> (unsigned)shift) & 0xfu]); 55 + } 56 + } 57 + 58 + void uart_put_dec64(uint64_t value) 59 + { 60 + char buf[21]; 61 + unsigned pos = 0; 62 + if (value == 0u) { 63 + uart_putc('0'); 64 + return; 65 + } 66 + while (value != 0u && pos < sizeof(buf)) { 67 + buf[pos++] = (char)('0' + (value % 10u)); 68 + value /= 10u; 69 + } 70 + while (pos != 0u) { 71 + uart_putc(buf[--pos]); 72 + } 73 + } 74 + 75 + bool uart_getc_nonblocking(char *out) 76 + { 77 + if ((mmio_read32(uart_addr(UART_FR)) & UART_FR_RXFE) != 0u) { 78 + return false; 79 + } 80 + *out = (char)(mmio_read32(uart_addr(UART_DR)) & 0xffu); 81 + return true; 82 + }
+49
drivers/timer.c
··· 1 + #include "hv/arch.h" 2 + #include "hv/config.h" 3 + #include "hv/timer.h" 4 + #include "hv/uart.h" 5 + #include "hv/vcpu.h" 6 + 7 + static uint64_t g_cntfrq; 8 + 9 + void timer_init(void) 10 + { 11 + g_cntfrq = arch_cntfrq_el0(); 12 + } 13 + 14 + uint64_t timer_now(void) 15 + { 16 + return arch_cntpct_el0(); 17 + } 18 + 19 + void timer_dump(void) 20 + { 21 + uart_puts("timer cntfrq="); 22 + uart_put_dec64(g_cntfrq); 23 + uart_puts(" cntpct="); 24 + uart_put_dec64(timer_now()); 25 + uart_puts("\n"); 26 + } 27 + 28 + void timer_vcpu_sync(struct vcpu *vcpu) 29 + { 30 + if (vcpu == (struct vcpu *)0) { 31 + return; 32 + } 33 + 34 + uint64_t ctl = vcpu->sysregs.cntv_ctl_el0; 35 + bool enabled = (ctl & 1u) != 0u; 36 + bool masked = (ctl & 2u) != 0u; 37 + if (!enabled || masked) { 38 + vcpu_clear_pending_irq(vcpu, CONFIG_VTIMER_IRQ); 39 + return; 40 + } 41 + 42 + if (timer_now() >= vcpu->sysregs.cntv_cval_el0) { 43 + vcpu->sysregs.cntv_ctl_el0 |= 4u; 44 + vcpu_set_pending_irq(vcpu, CONFIG_VTIMER_IRQ); 45 + } else { 46 + vcpu->sysregs.cntv_ctl_el0 &= ~4ull; 47 + vcpu_clear_pending_irq(vcpu, CONFIG_VTIMER_IRQ); 48 + } 49 + }
+33
guest/linker.ld
··· 1 + OUTPUT_ARCH(aarch64) 2 + ENTRY(_guest_start) 3 + 4 + GUEST_BASE = 0x40000000; 5 + 6 + SECTIONS 7 + { 8 + . = GUEST_BASE; 9 + __guest_start_addr = .; 10 + 11 + .text : ALIGN(4096) { 12 + KEEP(*(.text.entry)) 13 + KEEP(*(.text.vectors)) 14 + *(.text .text.*) 15 + } 16 + 17 + .rodata : ALIGN(16) { 18 + *(.rodata .rodata.*) 19 + } 20 + 21 + .data : ALIGN(16) { 22 + *(.data .data.*) 23 + } 24 + 25 + .bss : ALIGN(16) { 26 + __guest_bss_start = .; 27 + *(.bss .bss.* COMMON) 28 + __guest_bss_end = .; 29 + } 30 + 31 + . = ALIGN(4096); 32 + __guest_image_end = .; 33 + }
+134
guest/main.c
··· 1 + #include <stdint.h> 2 + #include <stddef.h> 3 + 4 + void guest_main(void); 5 + uint64_t guest_hvc_call(uint64_t num, uint64_t a1, uint64_t a2); 6 + 7 + static size_t guest_strlen(const char *s) 8 + { 9 + size_t n = 0; 10 + while (s[n] != '\0') { 11 + n++; 12 + } 13 + return n; 14 + } 15 + 16 + static uint64_t hvc_call(uint64_t num, uint64_t a1, uint64_t a2) 17 + { 18 + return guest_hvc_call(num, a1, a2); 19 + } 20 + 21 + static void console(const char *s) 22 + { 23 + (void)hvc_call(4u, (uint64_t)(uintptr_t)s, guest_strlen(s)); 24 + } 25 + 26 + static uint64_t read_midr(void) 27 + { 28 + uint64_t v; 29 + __asm__ volatile("mrs %0, midr_el1" : "=r"(v)); 30 + return v; 31 + } 32 + 33 + static uint64_t read_pfr0(void) 34 + { 35 + uint64_t v; 36 + __asm__ volatile("mrs %0, id_aa64pfr0_el1" : "=r"(v)); 37 + return v; 38 + } 39 + 40 + static uint64_t read_cntv_ctl(void) 41 + { 42 + uint64_t v; 43 + __asm__ volatile("mrs %0, cntv_ctl_el0" : "=r"(v)); 44 + return v; 45 + } 46 + 47 + static void write_sctlr(uint64_t v) 48 + { 49 + __asm__ volatile("msr sctlr_el1, %0" :: "r"(v) : "memory"); 50 + } 51 + 52 + static void write_vbar(uint64_t v) 53 + { 54 + __asm__ volatile("msr vbar_el1, %0" :: "r"(v) : "memory"); 55 + } 56 + 57 + static void write_ttbr0(uint64_t v) 58 + { 59 + __asm__ volatile("msr ttbr0_el1, %0" :: "r"(v) : "memory"); 60 + } 61 + 62 + static void write_tcr(uint64_t v) 63 + { 64 + __asm__ volatile("msr tcr_el1, %0" :: "r"(v) : "memory"); 65 + } 66 + 67 + static void write_actlr(uint64_t v) 68 + { 69 + __asm__ volatile("msr actlr_el1, %0" :: "r"(v) : "memory"); 70 + } 71 + 72 + static uint64_t attempt_hcr_el2_read(void) 73 + { 74 + uint64_t v; 75 + __asm__ volatile("mrs %0, hcr_el2" : "=r"(v)); 76 + return v; 77 + } 78 + 79 + static uint64_t read_unknown_sysreg(void) 80 + { 81 + uint64_t v; 82 + __asm__ volatile("mrs %0, S3_7_C15_C15_7" : "=r"(v)); 83 + return v; 84 + } 85 + 86 + void guest_main(void) 87 + { 88 + console("diagnostic guest: start"); 89 + uint64_t midr = read_midr(); 90 + (void)midr; 91 + console("diagnostic guest: read midr_el1"); 92 + 93 + uint64_t pfr0 = read_pfr0(); 94 + (void)pfr0; 95 + console("diagnostic guest: read id_aa64pfr0_el1"); 96 + 97 + write_sctlr(0x30d00800u); 98 + console("diagnostic guest: wrote sctlr_el1 shadow"); 99 + 100 + write_vbar(0x40000800u); 101 + console("diagnostic guest: wrote vbar_el1 vector base"); 102 + 103 + write_ttbr0(0u); 104 + console("diagnostic guest: wrote ttbr0_el1 shadow"); 105 + 106 + write_tcr(0u); 107 + console("diagnostic guest: wrote tcr_el1 shadow"); 108 + 109 + uint64_t cntv = read_cntv_ctl(); 110 + (void)cntv; 111 + console("diagnostic guest: read cntv_ctl_el0"); 112 + 113 + (void)hvc_call(0u, 0u, 0u); 114 + console("diagnostic guest: requested hv id"); 115 + 116 + (void)hvc_call(1u, 0u, 0u); 117 + console("diagnostic guest: requested log dump"); 118 + 119 + console("diagnostic guest: attempting denied hcr_el2 read"); 120 + (void)attempt_hcr_el2_read(); 121 + console("diagnostic guest: returned through guest undefined vector"); 122 + 123 + __asm__ volatile("brk #0"); 124 + console("diagnostic guest: returned through guest brk vector"); 125 + 126 + console("diagnostic guest: attempting unknown sysreg read"); 127 + (void)read_unknown_sysreg(); 128 + console("diagnostic guest: returned from unknown sysreg read"); 129 + 130 + console("diagnostic guest: attempting denied actlr_el1 write"); 131 + write_actlr(1u); 132 + console("diagnostic guest: unexpected return from denied actlr_el1"); 133 + (void)hvc_call(3u, 0u, 0u); 134 + }
+89
guest/start.S
··· 1 + .section .text.entry, "ax" 2 + .global _guest_start 3 + .type _guest_start, %function 4 + _guest_start: 5 + ldr x0, =__guest_stack_top 6 + mov sp, x0 7 + ldr x0, =__guest_bss_start 8 + ldr x1, =__guest_bss_end 9 + 1: cmp x0, x1 10 + b.hs 2f 11 + str xzr, [x0], #8 12 + b 1b 13 + 2: ldr x0, =guest_vectors 14 + msr vbar_el1, x0 15 + isb 16 + bl guest_main 17 + 3: hvc #3 18 + wfe 19 + b 3b 20 + 21 + .global guest_hvc_call 22 + .type guest_hvc_call, %function 23 + guest_hvc_call: 24 + hvc #0 25 + ret 26 + 27 + .section .text.vectors, "ax" 28 + .align 11 29 + guest_vectors: 30 + b guest_exception 31 + .org guest_vectors + 0x080 32 + b guest_exception 33 + .org guest_vectors + 0x100 34 + b guest_exception 35 + .org guest_vectors + 0x180 36 + b guest_exception 37 + .org guest_vectors + 0x200 38 + b guest_exception 39 + .org guest_vectors + 0x280 40 + b guest_exception 41 + .org guest_vectors + 0x300 42 + b guest_exception 43 + .org guest_vectors + 0x380 44 + b guest_exception 45 + .org guest_vectors + 0x400 46 + b guest_exception 47 + .org guest_vectors + 0x480 48 + b guest_exception 49 + .org guest_vectors + 0x500 50 + b guest_exception 51 + .org guest_vectors + 0x580 52 + b guest_exception 53 + .org guest_vectors + 0x600 54 + b guest_exception 55 + .org guest_vectors + 0x680 56 + b guest_exception 57 + .org guest_vectors + 0x700 58 + b guest_exception 59 + .org guest_vectors + 0x780 60 + b guest_exception 61 + 62 + guest_exception: 63 + mrs x4, esr_el1 64 + mrs x5, elr_el1 65 + mrs x6, far_el1 66 + mov x0, #5 67 + mov x1, x4 68 + mov x2, x5 69 + mov x3, x6 70 + hvc #0 71 + mov x0, #4 72 + ldr x1, =guest_exception_msg 73 + mov x2, #(guest_exception_msg_end - guest_exception_msg) 74 + hvc #0 75 + add x5, x5, #4 76 + msr elr_el1, x5 77 + isb 78 + eret 79 + 80 + .section .rodata 81 + guest_exception_msg: 82 + .ascii "guest exception vector" 83 + guest_exception_msg_end: 84 + 85 + .section .bss.stack, "aw", %nobits 86 + .align 12 87 + __guest_stack: 88 + .skip 0x10000 89 + __guest_stack_top:
+12
include/hv/allocator.h
··· 1 + #ifndef HV_ALLOCATOR_H 2 + #define HV_ALLOCATOR_H 3 + 4 + #include "hv/types.h" 5 + 6 + void allocator_init(paddr_t base, uint64_t size); 7 + void *alloc_page(void); 8 + bool allocator_owns(paddr_t addr, uint64_t size); 9 + bool allocator_selftest(void); 10 + void allocator_dump(void); 11 + 12 + #endif
+37
include/hv/arch.h
··· 1 + #ifndef HV_ARCH_H 2 + #define HV_ARCH_H 3 + 4 + #include "hv/types.h" 5 + 6 + struct trap_frame; 7 + struct vcpu; 8 + 9 + extern char __bss_start[]; 10 + extern char __bss_end[]; 11 + extern char __image_start[]; 12 + extern char __image_end[]; 13 + extern char __vectors_el2[]; 14 + extern char __guest_image_start[]; 15 + extern char __guest_image_end[]; 16 + 17 + uint64_t arch_current_el(void); 18 + uint64_t arch_mpidr_el1(void); 19 + uint64_t arch_cntpct_el0(void); 20 + uint64_t arch_cntfrq_el0(void); 21 + uint64_t arch_irq_save(void); 22 + void arch_irq_restore(uint64_t flags); 23 + void arch_dump_el2_state(void); 24 + void arch_el2_configure(uint64_t vttbr, uint64_t vtcr); 25 + void arch_enable_el2_mmu(uint64_t ttbr0, uint64_t tcr, uint64_t mair); 26 + void arch_install_vectors(void); 27 + void arch_prepare_guest_entry(struct vcpu *vcpu); 28 + void arch_capture_el1_sysregs(struct vcpu *vcpu); 29 + void arch_sync_el1_sysregs(const struct vcpu *vcpu); 30 + void arch_vcpu_enter(struct vcpu *vcpu); 31 + void arch_halt(void) __attribute__((noreturn)); 32 + void arch_inject_guest_undef(struct vcpu *vcpu); 33 + void arch_advance_guest_pc(struct vcpu *vcpu); 34 + void arch_tlbi_vmalle1is(void); 35 + void hv_main(uint64_t boot_dtb_pa); 36 + 37 + #endif
+61
include/hv/config.h
··· 1 + #ifndef HV_CONFIG_H 2 + #define HV_CONFIG_H 3 + 4 + #define HV_PROJECT_NAME "ariel" 5 + #define HV_BUILD_MODE "qemu-virt-aarch64" 6 + #define HV_BUILD_ID "reproducible-local" 7 + 8 + #define CONFIG_PLATFORM_QEMU_VIRT 1 9 + #define CONFIG_HV_LOAD_ADDRESS 0x40080000ull 10 + #define CONFIG_UART_BASE 0x09000000ull 11 + #define CONFIG_UART_SIZE 0x00001000ull 12 + #define CONFIG_RTC_BASE 0x09010000ull 13 + #define CONFIG_RTC_SIZE 0x00001000ull 14 + #define CONFIG_FW_CFG_BASE 0x09020000ull 15 + #define CONFIG_FW_CFG_SIZE 0x00001000ull 16 + #define CONFIG_GPIO_BASE 0x09030000ull 17 + #define CONFIG_GPIO_SIZE 0x00001000ull 18 + #define CONFIG_PCI_ECAM_BASE 0x4010000000ull 19 + #define CONFIG_PCI_ECAM_SIZE 0x10000000ull 20 + #define CONFIG_GICD_BASE 0x08000000ull 21 + #define CONFIG_GICD_SIZE 0x00010000ull 22 + #define CONFIG_GITS_BASE 0x08080000ull 23 + #define CONFIG_GITS_SIZE 0x00020000ull 24 + #define CONFIG_GICR_BASE 0x080A0000ull 25 + #define CONFIG_GICR_SIZE 0x00F60000ull 26 + 27 + #define CONFIG_PAGE_SIZE 4096ull 28 + #define CONFIG_HV_STACK_SIZE 0x10000ull 29 + #define CONFIG_GUEST_IPA_BASE 0x40000000ull 30 + #define CONFIG_GUEST_RAM_BASE 0x42000000ull 31 + #define CONFIG_GUEST_RAM_SIZE 0x30000000ull 32 + #define CONFIG_GUEST_STACK_TOP (CONFIG_GUEST_IPA_BASE + 0x00100000ull) 33 + #define CONFIG_GUEST_ENTRY CONFIG_GUEST_IPA_BASE 34 + #define CONFIG_LINUX_IMAGE_LOAD_PA CONFIG_GUEST_RAM_BASE 35 + #define CONFIG_LINUX_IMAGE_MAX_SIZE CONFIG_GUEST_RAM_SIZE 36 + #define CONFIG_LINUX_INITRD_PA (CONFIG_GUEST_RAM_BASE + 0x04000000ull) 37 + #define CONFIG_LINUX_INITRD_IPA (CONFIG_GUEST_IPA_BASE + 0x04000000ull) 38 + #define CONFIG_LINUX_INITRD_MAX_SIZE 0x2be00000ull 39 + #ifndef CONFIG_LINUX_INITRD_SIZE 40 + #define CONFIG_LINUX_INITRD_SIZE 0ull 41 + #endif 42 + #define CONFIG_LINUX_DTB_PA (CONFIG_GUEST_RAM_BASE + 0x2fe00000ull) 43 + #define CONFIG_LINUX_DTB_IPA (CONFIG_GUEST_IPA_BASE + 0x2fe00000ull) 44 + #define CONFIG_LINUX_DTB_MAX_SIZE 0x00200000ull 45 + 46 + #ifndef CONFIG_BOOT_LINUX 47 + #define CONFIG_BOOT_LINUX 0 48 + #endif 49 + 50 + #ifndef CONFIG_MONITOR_MUTATION 51 + #define CONFIG_MONITOR_MUTATION 0 52 + #endif 53 + 54 + #define CONFIG_LOG_CAPACITY 256u 55 + #define CONFIG_POLICY_STRICT 1 56 + #define CONFIG_ENABLE_WFI_TRAPS 1 57 + #define CONFIG_ENABLE_SMC_TRAPS 1 58 + #define CONFIG_ENABLE_DEBUG_LOG 1 59 + #define CONFIG_VTIMER_IRQ 27u 60 + 61 + #endif
+14
include/hv/el2_mmu.h
··· 1 + #ifndef HV_EL2_MMU_H 2 + #define HV_EL2_MMU_H 3 + 4 + #include "hv/types.h" 5 + 6 + void el2_mmu_init_plan(void); 7 + void el2_mmu_enable(void); 8 + uint64_t el2_mmu_root(void); 9 + uint64_t el2_mmu_tcr(void); 10 + uint64_t el2_mmu_mair(void); 11 + void el2_mmu_dump(void); 12 + bool el2_mmu_selftest(void); 13 + 14 + #endif
+23
include/hv/fdt.h
··· 1 + #ifndef HV_FDT_H 2 + #define HV_FDT_H 3 + 4 + #include "hv/types.h" 5 + 6 + struct fdt_platform_info { 7 + bool valid; 8 + uint64_t fdt_pa; 9 + uint64_t size; 10 + uint64_t memory_base; 11 + uint64_t memory_size; 12 + uint64_t initrd_start; 13 + uint64_t initrd_end; 14 + const char *bootargs; 15 + }; 16 + 17 + bool fdt_probe(uint64_t fdt_pa, struct fdt_platform_info *info); 18 + bool fdt_patch_memory(uint64_t fdt_pa, uint64_t base, uint64_t size); 19 + bool fdt_patch_linux_guest(uint64_t fdt_pa, uint64_t max_size, const char *bootargs, 20 + uint64_t initrd_start, uint64_t initrd_end); 21 + void fdt_dump(const struct fdt_platform_info *info); 22 + 23 + #endif
+16
include/hv/gicv3.h
··· 1 + #ifndef HV_GICV3_H 2 + #define HV_GICV3_H 3 + 4 + #include "hv/types.h" 5 + 6 + struct vcpu; 7 + 8 + void gicv3_init(void); 9 + void gicv3_dump(void); 10 + uint32_t gicv3_ack_irq(void); 11 + void gicv3_eoi(uint32_t irq); 12 + void gicv3_vcpu_init(void); 13 + void gicv3_sync_vcpu_state(struct vcpu *vcpu); 14 + void gicv3_flush_vcpu_state(struct vcpu *vcpu); 15 + 16 + #endif
+29
include/hv/guest_loader.h
··· 1 + #ifndef HV_GUEST_LOADER_H 2 + #define HV_GUEST_LOADER_H 3 + 4 + #include "hv/types.h" 5 + 6 + enum guest_payload_kind { 7 + GUEST_PAYLOAD_DIAGNOSTIC = 0, 8 + GUEST_PAYLOAD_LINUX_IMAGE = 1, 9 + }; 10 + 11 + struct guest_image_info { 12 + enum guest_payload_kind kind; 13 + uint64_t ipa_entry; 14 + uint64_t ipa_load; 15 + uint64_t size; 16 + uint64_t dtb_ipa; 17 + uint64_t initrd_ipa; 18 + uint64_t initrd_size; 19 + }; 20 + 21 + bool guest_load_diagnostic(struct guest_image_info *info); 22 + bool guest_load_external_linux(struct guest_image_info *info, paddr_t image_pa, 23 + uint64_t image_max_size, paddr_t dtb_pa, 24 + uint64_t dtb_max_size, paddr_t initrd_pa, 25 + uint64_t initrd_size); 26 + bool guest_validate_linux_image_header(const void *image, uint64_t size, 27 + struct guest_image_info *info); 28 + 29 + #endif
+57
include/hv/log.h
··· 1 + #ifndef HV_LOG_H 2 + #define HV_LOG_H 3 + 4 + #include "hv/types.h" 5 + 6 + enum log_kind { 7 + LOG_BOOT = 1, 8 + LOG_TRAP = 2, 9 + LOG_POLICY = 3, 10 + LOG_HVC = 4, 11 + LOG_ABORT = 5, 12 + LOG_MONITOR = 6, 13 + LOG_PANIC = 7, 14 + LOG_SELFTEST = 8, 15 + }; 16 + 17 + enum log_action { 18 + LOG_ACT_NONE = 0, 19 + LOG_ACT_EMULATE_READ, 20 + LOG_ACT_EMULATE_WRITE, 21 + LOG_ACT_WRITE_MASK, 22 + LOG_ACT_DENY_SKIP, 23 + LOG_ACT_DENY_UNDEF, 24 + LOG_ACT_PAUSE, 25 + LOG_ACT_PANIC, 26 + }; 27 + 28 + struct log_event { 29 + uint64_t seq; 30 + uint64_t cpu; 31 + uint64_t vcpu; 32 + uint64_t timestamp; 33 + uint32_t kind; 34 + uint32_t ec; 35 + uint64_t iss; 36 + uint64_t elr; 37 + uint64_t far; 38 + uint64_t hpfar; 39 + uint64_t sysreg_key; 40 + uint32_t is_read; 41 + uint32_t rt; 42 + uint64_t operand; 43 + uint64_t result; 44 + uint32_t action; 45 + uint32_t reason; 46 + uint64_t pstate; 47 + const char *name; 48 + }; 49 + 50 + void log_init(void); 51 + void log_event_commit(const struct log_event *event); 52 + void log_simple(enum log_kind kind, uint32_t reason, uint64_t a, uint64_t b); 53 + void log_dump(unsigned limit); 54 + void log_clear(void); 55 + bool log_selftest(void); 56 + 57 + #endif
+40
include/hv/mmio.h
··· 1 + #ifndef HV_MMIO_H 2 + #define HV_MMIO_H 3 + 4 + #include "hv/types.h" 5 + 6 + static inline void hv_dmb_ish(void) 7 + { 8 + __asm__ volatile("dmb ish" ::: "memory"); 9 + } 10 + 11 + static inline void hv_dsb_ish(void) 12 + { 13 + __asm__ volatile("dsb ish" ::: "memory"); 14 + } 15 + 16 + static inline void hv_dsb_sy(void) 17 + { 18 + __asm__ volatile("dsb sy" ::: "memory"); 19 + } 20 + 21 + static inline void hv_isb(void) 22 + { 23 + __asm__ volatile("isb" ::: "memory"); 24 + } 25 + 26 + static inline uint32_t mmio_read32(uintptr_t addr) 27 + { 28 + uint32_t value = *(volatile uint32_t *)addr; 29 + hv_dmb_ish(); 30 + return value; 31 + } 32 + 33 + static inline void mmio_write32(uintptr_t addr, uint32_t value) 34 + { 35 + hv_dmb_ish(); 36 + *(volatile uint32_t *)addr = value; 37 + hv_dmb_ish(); 38 + } 39 + 40 + #endif
+28
include/hv/mmio_policy.h
··· 1 + #ifndef HV_MMIO_POLICY_H 2 + #define HV_MMIO_POLICY_H 3 + 4 + #include "hv/types.h" 5 + 6 + struct vcpu; 7 + 8 + enum mmio_policy_action { 9 + MMIO_POLICY_DENY = 0, 10 + MMIO_POLICY_EMULATE_ZERO, 11 + MMIO_POLICY_EMULATE_PL011, 12 + MMIO_POLICY_EMULATE_VIRTIO_MMIO, 13 + MMIO_POLICY_EMULATE_READONLY_DEVICE, 14 + MMIO_POLICY_ALLOW_STAGE2, 15 + }; 16 + 17 + struct mmio_policy_region { 18 + ipa_t base; 19 + uint64_t size; 20 + const char *name; 21 + enum mmio_policy_action action; 22 + }; 23 + 24 + const struct mmio_policy_region *mmio_policy_lookup(ipa_t ipa); 25 + bool mmio_policy_emulate(struct vcpu *vcpu, ipa_t ipa, uint64_t esr_el2); 26 + void mmio_policy_dump(void); 27 + 28 + #endif
+10
include/hv/monitor.h
··· 1 + #ifndef HV_MONITOR_H 2 + #define HV_MONITOR_H 3 + 4 + #include "hv/vcpu.h" 5 + 6 + void monitor_init(void); 7 + void monitor_poll(void); 8 + void monitor_loop(struct vcpu *vcpu, const char *reason); 9 + 10 + #endif
+11
include/hv/panic.h
··· 1 + #ifndef HV_PANIC_H 2 + #define HV_PANIC_H 3 + 4 + #include "hv/types.h" 5 + 6 + void panic(const char *reason) __attribute__((noreturn)); 7 + void panic_with_code(const char *reason, uint64_t code) __attribute__((noreturn)); 8 + 9 + #define BUG_ON(cond) do { if (cond) { panic("BUG_ON: " #cond); } } while (0) 10 + 11 + #endif
+8
include/hv/platform.h
··· 1 + #ifndef HV_PLATFORM_H 2 + #define HV_PLATFORM_H 3 + 4 + #include "hv/fdt.h" 5 + 6 + const struct fdt_platform_info *platform_info(void); 7 + 8 + #endif
+67
include/hv/policy.h
··· 1 + #ifndef HV_POLICY_H 2 + #define HV_POLICY_H 3 + 4 + #include "hv/log.h" 5 + #include "hv/sysreg.h" 6 + #include "hv/vcpu.h" 7 + 8 + enum policy_access_class { 9 + POLICY_CLASS_ID = 1, 10 + POLICY_CLASS_MMU, 11 + POLICY_CLASS_TIMER, 12 + POLICY_CLASS_THREAD, 13 + POLICY_CLASS_DEBUG, 14 + POLICY_CLASS_PMU, 15 + POLICY_CLASS_EL2_EL3, 16 + POLICY_CLASS_MISC, 17 + }; 18 + 19 + enum policy_action { 20 + POLICY_ALLOW_NATIVE = 0, 21 + POLICY_EMULATE_READ, 22 + POLICY_EMULATE_WRITE, 23 + POLICY_WRITE_MASK, 24 + POLICY_DENY_SKIP, 25 + POLICY_DENY_UNDEF, 26 + POLICY_PAUSE, 27 + POLICY_PANIC, 28 + }; 29 + 30 + struct policy_entry { 31 + uint64_t key; 32 + const char *name; 33 + enum policy_access_class access_class; 34 + enum policy_action read_policy; 35 + enum policy_action write_policy; 36 + uint64_t reset_value; 37 + uint64_t writable_mask; 38 + uint32_t logging_level; 39 + uint32_t violation_reason; 40 + }; 41 + 42 + struct policy_decision { 43 + const struct policy_entry *entry; 44 + enum policy_action action; 45 + uint64_t value; 46 + uint32_t reason; 47 + }; 48 + 49 + enum policy_profile { 50 + POLICY_PROFILE_STRICT = 0, 51 + POLICY_PROFILE_DIAGNOSTIC, 52 + POLICY_PROFILE_LINUX_BOOT, 53 + POLICY_PROFILE_DEBUG, 54 + }; 55 + 56 + void policy_init(void); 57 + bool policy_set_profile(enum policy_profile profile); 58 + bool policy_set_profile_name(const char *name); 59 + enum policy_profile policy_get_profile(void); 60 + const char *policy_profile_name(enum policy_profile profile); 61 + const struct policy_entry *sysreg_policy_lookup(uint64_t key); 62 + bool policy_apply_sysreg(struct vcpu *vcpu, const struct sysreg_access *access, 63 + uint64_t operand, struct policy_decision *decision); 64 + void policy_dump(void); 65 + bool policy_selftest(void); 66 + 67 + #endif
+9
include/hv/psci.h
··· 1 + #ifndef HV_PSCI_H 2 + #define HV_PSCI_H 3 + 4 + #include "hv/vcpu.h" 5 + 6 + bool psci_handle_call(struct vcpu *vcpu, uint64_t function_id); 7 + void psci_dump(void); 8 + 9 + #endif
+6
include/hv/selftest.h
··· 1 + #ifndef HV_SELFTEST_H 2 + #define HV_SELFTEST_H 3 + 4 + void selftests_run_or_panic(void); 5 + 6 + #endif
+41
include/hv/stage2.h
··· 1 + #ifndef HV_STAGE2_H 2 + #define HV_STAGE2_H 3 + 4 + #include "hv/types.h" 5 + 6 + enum stage2_mem_type { 7 + S2_MEM_NORMAL = 0, 8 + S2_MEM_DEVICE = 1, 9 + }; 10 + 11 + enum stage2_perm { 12 + S2_PERM_R = 1u, 13 + S2_PERM_W = 2u, 14 + S2_PERM_X = 4u, 15 + }; 16 + 17 + struct stage2_translation { 18 + bool mapped; 19 + ipa_t ipa_base; 20 + paddr_t pa_base; 21 + uint64_t size; 22 + uint64_t desc; 23 + uint32_t perms; 24 + enum stage2_mem_type type; 25 + }; 26 + 27 + struct trap_frame; 28 + struct vcpu; 29 + 30 + void stage2_init(void); 31 + bool stage2_map_range(ipa_t ipa, paddr_t pa, uint64_t size, enum stage2_mem_type type, uint32_t perms); 32 + bool stage2_unmap_range(ipa_t ipa, uint64_t size); 33 + bool stage2_translate(ipa_t ipa, struct stage2_translation *out); 34 + ipa_t stage2_ipa_from_abort(uint64_t far_el2, uint64_t hpfar_el2); 35 + bool stage2_handle_abort(struct vcpu *vcpu, const struct trap_frame *frame, bool instruction); 36 + uint64_t stage2_vttbr(void); 37 + uint64_t stage2_vtcr(void); 38 + void stage2_dump(void); 39 + bool stage2_selftest(void); 40 + 41 + #endif
+19
include/hv/string.h
··· 1 + #ifndef HV_STRING_H 2 + #define HV_STRING_H 3 + 4 + #include <stddef.h> 5 + #include <stdint.h> 6 + 7 + void *hv_memset(void *dst, int value, size_t len); 8 + void *hv_memcpy(void *dst, const void *src, size_t len); 9 + void *hv_memmove(void *dst, const void *src, size_t len); 10 + int hv_memcmp(const void *a, const void *b, size_t len); 11 + void *memset(void *dst, int value, size_t len); 12 + void *memcpy(void *dst, const void *src, size_t len); 13 + void *memmove(void *dst, const void *src, size_t len); 14 + int memcmp(const void *a, const void *b, size_t len); 15 + size_t hv_strlen(const char *s); 16 + int hv_strcmp(const char *a, const char *b); 17 + int hv_strncmp(const char *a, const char *b, size_t n); 18 + 19 + #endif
+48
include/hv/sysreg.h
··· 1 + #ifndef HV_SYSREG_H 2 + #define HV_SYSREG_H 3 + 4 + #include "hv/types.h" 5 + 6 + #define ESR_EC_SHIFT 26u 7 + #define ESR_EC_MASK 0x3fu 8 + #define ESR_EC_UNKNOWN 0x00u 9 + #define ESR_EC_WFI_WFE 0x01u 10 + #define ESR_EC_SMC64 0x17u 11 + #define ESR_EC_HVC64 0x16u 12 + #define ESR_EC_SYSREG 0x18u 13 + #define ESR_EC_IABT_LOWER 0x20u 14 + #define ESR_EC_DABT_LOWER 0x24u 15 + #define ESR_EC_BRK64 0x3cu 16 + 17 + #define ESR_SYSREG_DIR_READ 1u 18 + #define ESR_SYSREG_DIR_WRITE 0u 19 + 20 + struct sysreg_access { 21 + uint32_t op0; 22 + uint32_t op1; 23 + uint32_t crn; 24 + uint32_t crm; 25 + uint32_t op2; 26 + uint32_t rt; 27 + bool is_read; 28 + uint64_t key; 29 + }; 30 + 31 + #define SYSREG_KEY_CONST(op0, op1, crn, crm, op2) \ 32 + ((((uint64_t)((op0) & 0x3u)) << 14u) | \ 33 + (((uint64_t)((op1) & 0x7u)) << 11u) | \ 34 + (((uint64_t)((crn) & 0xfu)) << 7u) | \ 35 + (((uint64_t)((crm) & 0xfu)) << 3u) | \ 36 + ((uint64_t)((op2) & 0x7u))) 37 + 38 + static inline uint32_t esr_ec(uint64_t esr) 39 + { 40 + return (uint32_t)((esr >> ESR_EC_SHIFT) & ESR_EC_MASK); 41 + } 42 + 43 + uint64_t sysreg_key_make(uint32_t op0, uint32_t op1, uint32_t crn, uint32_t crm, uint32_t op2); 44 + bool sysreg_decode_from_esr_iss(uint64_t iss, struct sysreg_access *out); 45 + const char *sysreg_key_name(uint64_t key); 46 + bool sysreg_selftest(void); 47 + 48 + #endif
+13
include/hv/timer.h
··· 1 + #ifndef HV_TIMER_H 2 + #define HV_TIMER_H 3 + 4 + #include "hv/types.h" 5 + 6 + struct vcpu; 7 + 8 + void timer_init(void); 9 + uint64_t timer_now(void); 10 + void timer_dump(void); 11 + void timer_vcpu_sync(struct vcpu *vcpu); 12 + 13 + #endif
+10
include/hv/trap.h
··· 1 + #ifndef HV_TRAP_H 2 + #define HV_TRAP_H 3 + 4 + #include "hv/vcpu.h" 5 + 6 + void trap_handle_lower_sync(struct trap_frame *frame); 7 + void trap_handle_current_sync(struct trap_frame *frame); 8 + void trap_handle_irq(struct trap_frame *frame); 9 + 10 + #endif
+45
include/hv/types.h
··· 1 + #ifndef HV_TYPES_H 2 + #define HV_TYPES_H 3 + 4 + #include <stdbool.h> 5 + #include <stddef.h> 6 + #include <stdint.h> 7 + 8 + #define HV_ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 9 + #define HV_BIT(n) (1ull << (n)) 10 + #define HV_MASK(width) ((width) == 64u ? UINT64_MAX : ((1ull << (width)) - 1ull)) 11 + #define HV_ALIGN_UP(x, a) (((x) + ((a) - 1ull)) & ~((a) - 1ull)) 12 + #define HV_ALIGN_DOWN(x, a) ((x) & ~((a) - 1ull)) 13 + 14 + typedef uint64_t paddr_t; 15 + typedef uint64_t vaddr_t; 16 + typedef uint64_t ipa_t; 17 + 18 + static inline bool hv_is_aligned_u64(uint64_t value, uint64_t alignment) 19 + { 20 + return alignment != 0u && (value & (alignment - 1u)) == 0u; 21 + } 22 + 23 + static inline bool hv_add_overflow_u64(uint64_t a, uint64_t b, uint64_t *out) 24 + { 25 + *out = a + b; 26 + return *out < a; 27 + } 28 + 29 + static inline bool hv_range_overlaps(uint64_t a_base, uint64_t a_size, 30 + uint64_t b_base, uint64_t b_size) 31 + { 32 + uint64_t a_end; 33 + uint64_t b_end; 34 + 35 + if (a_size == 0u || b_size == 0u) { 36 + return false; 37 + } 38 + if (hv_add_overflow_u64(a_base, a_size, &a_end) || 39 + hv_add_overflow_u64(b_base, b_size, &b_end)) { 40 + return true; 41 + } 42 + return a_base < b_end && b_base < a_end; 43 + } 44 + 45 + #endif
+13
include/hv/uart.h
··· 1 + #ifndef HV_UART_H 2 + #define HV_UART_H 3 + 4 + #include "hv/types.h" 5 + 6 + void uart_init(void); 7 + void uart_putc(char c); 8 + void uart_puts(const char *s); 9 + void uart_put_hex64(uint64_t value); 10 + void uart_put_dec64(uint64_t value); 11 + bool uart_getc_nonblocking(char *out); 12 + 13 + #endif
+90
include/hv/vcpu.h
··· 1 + #ifndef HV_VCPU_H 2 + #define HV_VCPU_H 3 + 4 + #include "hv/types.h" 5 + 6 + enum vcpu_run_state { 7 + VCPU_CREATED = 0, 8 + VCPU_READY, 9 + VCPU_RUNNING, 10 + VCPU_PAUSED, 11 + VCPU_PANICKED, 12 + }; 13 + 14 + struct el1_sysreg_shadow { 15 + uint64_t sctlr_el1; 16 + uint64_t ttbr0_el1; 17 + uint64_t ttbr1_el1; 18 + uint64_t tcr_el1; 19 + uint64_t mair_el1; 20 + uint64_t vbar_el1; 21 + uint64_t contextidr_el1; 22 + uint64_t tpidr_el1; 23 + uint64_t tpidrro_el0; 24 + uint64_t cntv_ctl_el0; 25 + uint64_t cntv_tval_el0; 26 + uint64_t cntv_cval_el0; 27 + uint64_t cntkctl_el1; 28 + uint64_t cpacr_el1; 29 + uint64_t mdscr_el1; 30 + uint64_t csselr_el1; 31 + uint64_t elr_el1; 32 + uint64_t spsr_el1; 33 + uint64_t esr_el1; 34 + uint64_t far_el1; 35 + }; 36 + 37 + struct vcpu_stats { 38 + uint64_t traps; 39 + uint64_t sysreg_traps; 40 + uint64_t hvc_calls; 41 + uint64_t smc_denied; 42 + uint64_t wfx_traps; 43 + uint64_t data_aborts; 44 + uint64_t instruction_aborts; 45 + uint64_t unknown_exceptions; 46 + uint64_t injected_undefs; 47 + uint64_t virtual_irqs_injected; 48 + uint64_t virtual_irqs_completed; 49 + }; 50 + 51 + struct vcpu { 52 + uint64_t x[31]; 53 + uint64_t sp_el0; 54 + uint64_t sp_el1; 55 + uint64_t pc; 56 + uint64_t pstate; 57 + struct el1_sysreg_shadow sysregs; 58 + uint64_t pending_virtual_irq; 59 + uint64_t active_virtual_irq; 60 + uint32_t id; 61 + enum vcpu_run_state state; 62 + uint64_t last_esr; 63 + uint64_t last_far; 64 + uint64_t last_hpfar; 65 + struct vcpu_stats stats; 66 + }; 67 + 68 + struct trap_frame { 69 + uint64_t x[31]; 70 + uint64_t sp_el0; 71 + uint64_t sp_el1; 72 + uint64_t elr_el2; 73 + uint64_t spsr_el2; 74 + uint64_t esr_el2; 75 + uint64_t far_el2; 76 + uint64_t hpfar_el2; 77 + }; 78 + 79 + void vcpu_init(struct vcpu *vcpu, uint32_t id, uint64_t entry, uint64_t stack_top); 80 + void vcpu_load_frame(struct vcpu *vcpu, const struct trap_frame *frame); 81 + void vcpu_store_frame(const struct vcpu *vcpu, struct trap_frame *frame); 82 + uint64_t vcpu_read_gpr(const struct vcpu *vcpu, uint32_t rt); 83 + void vcpu_write_gpr(struct vcpu *vcpu, uint32_t rt, uint64_t value); 84 + void vcpu_set_pending_irq(struct vcpu *vcpu, uint32_t intid); 85 + void vcpu_clear_pending_irq(struct vcpu *vcpu, uint32_t intid); 86 + struct vcpu *vcpu_current(void); 87 + void vcpu_set_current(struct vcpu *vcpu); 88 + void vcpu_dump(const struct vcpu *vcpu); 89 + 90 + #endif
+33
linker.ld
··· 1 + OUTPUT_ARCH(aarch64) 2 + ENTRY(_start) 3 + 4 + HV_BASE = 0x40080000; 5 + 6 + SECTIONS 7 + { 8 + . = HV_BASE; 9 + __image_start = .; 10 + 11 + .text : ALIGN(4096) { 12 + KEEP(*(.text.entry)) 13 + KEEP(*(.text.vectors)) 14 + *(.text .text.*) 15 + } 16 + 17 + .rodata : ALIGN(4096) { 18 + *(.rodata .rodata.*) 19 + } 20 + 21 + .data : ALIGN(4096) { 22 + *(.data .data.*) 23 + } 24 + 25 + .bss : ALIGN(4096) { 26 + __bss_start = .; 27 + *(.bss .bss.* COMMON) 28 + __bss_end = .; 29 + } 30 + 31 + . = ALIGN(4096); 32 + __image_end = .; 33 + }
+78
mm/allocator.c
··· 1 + #include "hv/allocator.h" 2 + #include "hv/config.h" 3 + #include "hv/panic.h" 4 + #include "hv/string.h" 5 + #include "hv/uart.h" 6 + 7 + #define MAX_ALLOC_PAGES 1024u 8 + 9 + static paddr_t g_base; 10 + static uint64_t g_size; 11 + static uint8_t g_used[MAX_ALLOC_PAGES]; 12 + static uint64_t g_pages; 13 + 14 + void allocator_init(paddr_t base, uint64_t size) 15 + { 16 + BUG_ON(!hv_is_aligned_u64(base, CONFIG_PAGE_SIZE)); 17 + BUG_ON(!hv_is_aligned_u64(size, CONFIG_PAGE_SIZE)); 18 + g_base = base; 19 + g_size = size; 20 + g_pages = size / CONFIG_PAGE_SIZE; 21 + BUG_ON(g_pages > MAX_ALLOC_PAGES); 22 + hv_memset(g_used, 0, sizeof(g_used)); 23 + } 24 + 25 + void *alloc_page(void) 26 + { 27 + for (uint64_t i = 0; i < g_pages; i++) { 28 + if (g_used[i] == 0u) { 29 + g_used[i] = 1u; 30 + return (void *)(uintptr_t)(g_base + (i * CONFIG_PAGE_SIZE)); 31 + } 32 + } 33 + return (void *)0; 34 + } 35 + 36 + bool allocator_owns(paddr_t addr, uint64_t size) 37 + { 38 + uint64_t end; 39 + uint64_t alloc_end; 40 + if (hv_add_overflow_u64(addr, size, &end) || 41 + hv_add_overflow_u64(g_base, g_size, &alloc_end)) { 42 + return false; 43 + } 44 + return addr >= g_base && end <= alloc_end; 45 + } 46 + 47 + bool allocator_selftest(void) 48 + { 49 + if (!hv_is_aligned_u64(g_base, CONFIG_PAGE_SIZE) || g_pages == 0u) { 50 + return false; 51 + } 52 + uint64_t free_count = 0; 53 + for (uint64_t i = 0; i < g_pages; i++) { 54 + if (g_used[i] == 0u) { 55 + free_count++; 56 + } 57 + } 58 + return free_count != 0u && allocator_owns(g_base, CONFIG_PAGE_SIZE); 59 + } 60 + 61 + void allocator_dump(void) 62 + { 63 + uint64_t used = 0; 64 + for (uint64_t i = 0; i < g_pages; i++) { 65 + if (g_used[i] != 0u) { 66 + used++; 67 + } 68 + } 69 + uart_puts("allocator base="); 70 + uart_put_hex64(g_base); 71 + uart_puts(" size="); 72 + uart_put_hex64(g_size); 73 + uart_puts(" pages="); 74 + uart_put_dec64(g_pages); 75 + uart_puts(" used="); 76 + uart_put_dec64(used); 77 + uart_puts("\n"); 78 + }
+343
mm/stage2.c
··· 1 + #include "hv/allocator.h" 2 + #include "hv/arch.h" 3 + #include "hv/config.h" 4 + #include "hv/log.h" 5 + #include "hv/mmio_policy.h" 6 + #include "hv/panic.h" 7 + #include "hv/stage2.h" 8 + #include "hv/string.h" 9 + #include "hv/sysreg.h" 10 + #include "hv/trap.h" 11 + #include "hv/uart.h" 12 + #include "hv/vcpu.h" 13 + 14 + #define S2_ENTRIES 512u 15 + #define S2_L1_SHIFT 30u 16 + #define S2_L2_SHIFT 21u 17 + #define S2_L1_SIZE (1ull << S2_L1_SHIFT) 18 + #define S2_L2_SIZE (1ull << S2_L2_SHIFT) 19 + #define S2_DESC_VALID HV_BIT(0) 20 + #define S2_DESC_TABLE HV_BIT(1) 21 + #define S2_DESC_AF HV_BIT(10) 22 + #define S2_DESC_SH_INNER (3ull << 8u) 23 + #define S2_DESC_MEMATTR_NORMAL (0xfull << 2u) 24 + #define S2_DESC_MEMATTR_DEVICE (0x0ull << 2u) 25 + #define S2_DESC_S2AP_R (1ull << 6u) 26 + #define S2_DESC_S2AP_W (1ull << 7u) 27 + #define S2_DESC_XN (3ull << 53u) 28 + 29 + static uint64_t g_l1[S2_ENTRIES] __attribute__((aligned(65536))); 30 + static uint64_t g_l2_tables[4][S2_ENTRIES] __attribute__((aligned(4096))); 31 + static uint32_t g_l2_used; 32 + 33 + static uint64_t desc_addr(uint64_t addr) 34 + { 35 + return addr & 0x0000fffffffff000ull; 36 + } 37 + 38 + void stage2_init(void) 39 + { 40 + hv_memset(g_l1, 0, sizeof(g_l1)); 41 + hv_memset(g_l2_tables, 0, sizeof(g_l2_tables)); 42 + g_l2_used = 0; 43 + } 44 + 45 + static uint64_t *ensure_l2(uint64_t l1_index) 46 + { 47 + if (l1_index >= S2_ENTRIES) { 48 + return (uint64_t *)0; 49 + } 50 + if ((g_l1[l1_index] & S2_DESC_VALID) != 0u) { 51 + return (uint64_t *)(uintptr_t)desc_addr(g_l1[l1_index]); 52 + } 53 + if (g_l2_used >= HV_ARRAY_SIZE(g_l2_tables)) { 54 + return (uint64_t *)0; 55 + } 56 + uint64_t *table = g_l2_tables[g_l2_used++]; 57 + g_l1[l1_index] = ((uint64_t)(uintptr_t)table & 0x0000fffffffff000ull) | 58 + S2_DESC_TABLE | S2_DESC_VALID; 59 + return table; 60 + } 61 + 62 + static uint64_t block_desc(paddr_t pa, enum stage2_mem_type type, uint32_t perms) 63 + { 64 + uint64_t desc = (pa & 0x0000ffffffe00000ull) | S2_DESC_VALID | S2_DESC_AF; 65 + desc |= (type == S2_MEM_NORMAL) ? (S2_DESC_MEMATTR_NORMAL | S2_DESC_SH_INNER) : 66 + S2_DESC_MEMATTR_DEVICE; 67 + if ((perms & S2_PERM_R) != 0u) { 68 + desc |= S2_DESC_S2AP_R; 69 + } 70 + if ((perms & S2_PERM_W) != 0u) { 71 + desc |= S2_DESC_S2AP_W; 72 + } 73 + if ((perms & S2_PERM_X) == 0u) { 74 + desc |= S2_DESC_XN; 75 + } 76 + return desc; 77 + } 78 + 79 + static bool descriptor_valid(uint64_t desc) 80 + { 81 + if ((desc & S2_DESC_VALID) == 0u) { 82 + return false; 83 + } 84 + if ((desc & S2_DESC_AF) == 0u) { 85 + return false; 86 + } 87 + if (!hv_is_aligned_u64(desc_addr(desc), S2_L2_SIZE)) { 88 + return false; 89 + } 90 + return true; 91 + } 92 + 93 + static uint64_t *lookup_l2(uint64_t l1_index) 94 + { 95 + if (l1_index >= S2_ENTRIES || (g_l1[l1_index] & S2_DESC_VALID) == 0u) { 96 + return (uint64_t *)0; 97 + } 98 + if ((g_l1[l1_index] & S2_DESC_TABLE) == 0u) { 99 + return (uint64_t *)0; 100 + } 101 + return (uint64_t *)(uintptr_t)desc_addr(g_l1[l1_index]); 102 + } 103 + 104 + bool stage2_map_range(ipa_t ipa, paddr_t pa, uint64_t size, enum stage2_mem_type type, uint32_t perms) 105 + { 106 + uint64_t ipa_end; 107 + uint64_t pa_end; 108 + 109 + if (size == 0u || perms == 0u) { 110 + return false; 111 + } 112 + if (!hv_is_aligned_u64(ipa, S2_L2_SIZE) || !hv_is_aligned_u64(pa, S2_L2_SIZE) || 113 + !hv_is_aligned_u64(size, S2_L2_SIZE)) { 114 + return false; 115 + } 116 + if (hv_add_overflow_u64(ipa, size, &ipa_end) || 117 + hv_add_overflow_u64(pa, size, &pa_end)) { 118 + return false; 119 + } 120 + if (ipa_end <= ipa || pa_end <= pa) { 121 + return false; 122 + } 123 + if (hv_range_overlaps(pa, size, (uint64_t)(uintptr_t)__image_start, 124 + (uint64_t)((uintptr_t)__image_end - (uintptr_t)__image_start))) { 125 + return false; 126 + } 127 + 128 + for (uint64_t off = 0; off < size; off += S2_L2_SIZE) { 129 + uint64_t cur_ipa = ipa + off; 130 + uint64_t l1_index = (cur_ipa >> S2_L1_SHIFT) & 0x1ffu; 131 + uint64_t l2_index = (cur_ipa >> S2_L2_SHIFT) & 0x1ffu; 132 + uint64_t *l2 = ensure_l2(l1_index); 133 + if (l2 == (uint64_t *)0 || (l2[l2_index] & S2_DESC_VALID) != 0u) { 134 + return false; 135 + } 136 + uint64_t desc = block_desc(pa + off, type, perms); 137 + if (!descriptor_valid(desc)) { 138 + return false; 139 + } 140 + l2[l2_index] = desc; 141 + } 142 + arch_tlbi_vmalle1is(); 143 + return true; 144 + } 145 + 146 + bool stage2_unmap_range(ipa_t ipa, uint64_t size) 147 + { 148 + if (size == 0u || !hv_is_aligned_u64(ipa, S2_L2_SIZE) || 149 + !hv_is_aligned_u64(size, S2_L2_SIZE)) { 150 + return false; 151 + } 152 + 153 + for (uint64_t off = 0; off < size; off += S2_L2_SIZE) { 154 + uint64_t cur_ipa = ipa + off; 155 + uint64_t l1_index = (cur_ipa >> S2_L1_SHIFT) & 0x1ffu; 156 + uint64_t l2_index = (cur_ipa >> S2_L2_SHIFT) & 0x1ffu; 157 + uint64_t *l2 = lookup_l2(l1_index); 158 + if (l2 == (uint64_t *)0 || (l2[l2_index] & S2_DESC_VALID) == 0u) { 159 + return false; 160 + } 161 + } 162 + 163 + for (uint64_t off = 0; off < size; off += S2_L2_SIZE) { 164 + uint64_t cur_ipa = ipa + off; 165 + uint64_t l1_index = (cur_ipa >> S2_L1_SHIFT) & 0x1ffu; 166 + uint64_t l2_index = (cur_ipa >> S2_L2_SHIFT) & 0x1ffu; 167 + uint64_t *l2 = lookup_l2(l1_index); 168 + l2[l2_index] = 0u; 169 + } 170 + arch_tlbi_vmalle1is(); 171 + return true; 172 + } 173 + 174 + bool stage2_translate(ipa_t ipa, struct stage2_translation *out) 175 + { 176 + uint64_t l1_index = (ipa >> S2_L1_SHIFT) & 0x1ffu; 177 + uint64_t l2_index = (ipa >> S2_L2_SHIFT) & 0x1ffu; 178 + uint64_t *l2 = lookup_l2(l1_index); 179 + uint64_t desc; 180 + 181 + if (out != (struct stage2_translation *)0) { 182 + hv_memset(out, 0, sizeof(*out)); 183 + } 184 + if (l2 == (uint64_t *)0 || (l2[l2_index] & S2_DESC_VALID) == 0u) { 185 + return false; 186 + } 187 + 188 + desc = l2[l2_index]; 189 + if (out != (struct stage2_translation *)0) { 190 + out->mapped = true; 191 + out->ipa_base = HV_ALIGN_DOWN(ipa, S2_L2_SIZE); 192 + out->pa_base = desc_addr(desc); 193 + out->size = S2_L2_SIZE; 194 + out->desc = desc; 195 + if ((desc & S2_DESC_S2AP_R) != 0u) { 196 + out->perms |= S2_PERM_R; 197 + } 198 + if ((desc & S2_DESC_S2AP_W) != 0u) { 199 + out->perms |= S2_PERM_W; 200 + } 201 + if ((desc & S2_DESC_XN) == 0u) { 202 + out->perms |= S2_PERM_X; 203 + } 204 + out->type = (desc & S2_DESC_MEMATTR_NORMAL) == S2_DESC_MEMATTR_NORMAL ? 205 + S2_MEM_NORMAL : S2_MEM_DEVICE; 206 + } 207 + return true; 208 + } 209 + 210 + ipa_t stage2_ipa_from_abort(uint64_t far_el2, uint64_t hpfar_el2) 211 + { 212 + return ((hpfar_el2 & 0x0000000ffffffff0ull) << 8u) | (far_el2 & 0xfffull); 213 + } 214 + 215 + static bool dfsc_is_translation(uint32_t dfsc) 216 + { 217 + return dfsc >= 0x04u && dfsc <= 0x07u; 218 + } 219 + 220 + bool stage2_handle_abort(struct vcpu *vcpu, const struct trap_frame *frame, bool instruction) 221 + { 222 + ipa_t ipa; 223 + ipa_t block_ipa; 224 + paddr_t block_pa; 225 + uint32_t dfsc; 226 + struct log_event ev; 227 + 228 + if (vcpu == (struct vcpu *)0 || frame == (const struct trap_frame *)0) { 229 + return false; 230 + } 231 + 232 + ipa = stage2_ipa_from_abort(frame->far_el2, frame->hpfar_el2); 233 + dfsc = (uint32_t)(frame->esr_el2 & 0x3fu); 234 + hv_memset(&ev, 0, sizeof(ev)); 235 + ev.kind = LOG_ABORT; 236 + ev.vcpu = vcpu->id; 237 + ev.ec = esr_ec(frame->esr_el2); 238 + ev.iss = frame->esr_el2 & 0x01ffffffu; 239 + ev.elr = frame->elr_el2; 240 + ev.far = ipa; 241 + ev.hpfar = frame->hpfar_el2; 242 + ev.pstate = frame->spsr_el2; 243 + ev.reason = instruction ? 406u : 407u; 244 + 245 + const struct mmio_policy_region *mmio = mmio_policy_lookup(ipa); 246 + if (mmio != (const struct mmio_policy_region *)0) { 247 + ev.reason = 410u; 248 + ev.name = mmio->name; 249 + ev.action = (uint32_t)mmio->action; 250 + if (mmio_policy_emulate(vcpu, ipa, frame->esr_el2)) { 251 + ev.reason = 411u; 252 + log_event_commit(&ev); 253 + arch_advance_guest_pc(vcpu); 254 + return true; 255 + } 256 + log_event_commit(&ev); 257 + return false; 258 + } 259 + 260 + if (!dfsc_is_translation(dfsc) || 261 + ipa < CONFIG_GUEST_IPA_BASE || 262 + ipa >= CONFIG_GUEST_IPA_BASE + CONFIG_GUEST_RAM_SIZE) { 263 + log_event_commit(&ev); 264 + return false; 265 + } 266 + 267 + block_ipa = HV_ALIGN_DOWN(ipa, S2_L2_SIZE); 268 + block_pa = CONFIG_GUEST_RAM_BASE + (block_ipa - CONFIG_GUEST_IPA_BASE); 269 + if (!stage2_map_range(block_ipa, block_pa, S2_L2_SIZE, S2_MEM_NORMAL, 270 + S2_PERM_R | S2_PERM_W | S2_PERM_X)) { 271 + log_event_commit(&ev); 272 + return false; 273 + } 274 + 275 + ev.reason = instruction ? 408u : 409u; 276 + ev.result = block_pa; 277 + log_event_commit(&ev); 278 + return true; 279 + } 280 + 281 + uint64_t stage2_vttbr(void) 282 + { 283 + return (uint64_t)(uintptr_t)g_l1; 284 + } 285 + 286 + uint64_t stage2_vtcr(void) 287 + { 288 + uint64_t vtcr = 0; 289 + vtcr |= 24ull; 290 + vtcr |= (1ull << 6u); 291 + vtcr |= (1ull << 8u); 292 + vtcr |= (1ull << 10u); 293 + vtcr |= (3ull << 12u); 294 + vtcr |= (2ull << 16u); 295 + return vtcr; 296 + } 297 + 298 + void stage2_dump(void) 299 + { 300 + uart_puts("stage2 root="); 301 + uart_put_hex64(stage2_vttbr()); 302 + uart_puts(" vtcr="); 303 + uart_put_hex64(stage2_vtcr()); 304 + uart_puts(" l2_tables="); 305 + uart_put_dec64(g_l2_used); 306 + uart_puts("\n"); 307 + for (uint32_t l1 = 0; l1 < S2_ENTRIES; l1++) { 308 + if ((g_l1[l1] & S2_DESC_VALID) == 0u) { 309 + continue; 310 + } 311 + uint64_t *l2 = (uint64_t *)(uintptr_t)desc_addr(g_l1[l1]); 312 + for (uint32_t l2i = 0; l2i < S2_ENTRIES; l2i++) { 313 + if ((l2[l2i] & S2_DESC_VALID) != 0u) { 314 + uart_puts("mapping ipa="); 315 + uart_put_hex64(((uint64_t)l1 << S2_L1_SHIFT) | ((uint64_t)l2i << S2_L2_SHIFT)); 316 + uart_puts(" desc="); 317 + uart_put_hex64(l2[l2i]); 318 + uart_puts("\n"); 319 + } 320 + } 321 + } 322 + } 323 + 324 + bool stage2_selftest(void) 325 + { 326 + if (!hv_is_aligned_u64((uint64_t)(uintptr_t)g_l1, 65536u)) { 327 + return false; 328 + } 329 + for (uint32_t i = 0; i < S2_ENTRIES; i++) { 330 + if ((g_l1[i] & S2_DESC_VALID) != 0u && (g_l1[i] & S2_DESC_TABLE) == 0u) { 331 + return false; 332 + } 333 + if ((g_l1[i] & S2_DESC_VALID) != 0u) { 334 + uint64_t *l2 = (uint64_t *)(uintptr_t)desc_addr(g_l1[i]); 335 + for (uint32_t j = 0; j < S2_ENTRIES; j++) { 336 + if ((l2[j] & S2_DESC_VALID) != 0u && !descriptor_valid(l2[j])) { 337 + return false; 338 + } 339 + } 340 + } 341 + } 342 + return stage2_vttbr() == (uint64_t)(uintptr_t)g_l1; 343 + }
+58
scripts/check_release.py
··· 1 + from pathlib import Path 2 + import re 3 + import sys 4 + 5 + 6 + root = Path(__file__).resolve().parents[1] 7 + 8 + text_files = [ 9 + *root.glob("core/**/*.c"), 10 + *root.glob("drivers/**/*.c"), 11 + *root.glob("mm/**/*.c"), 12 + *root.glob("arch/**/*.c"), 13 + *root.glob("guest/**/*.c"), 14 + *root.glob("include/**/*.h"), 15 + *root.glob("tools/**/*.py"), 16 + *root.glob("scripts/**/*.py"), 17 + root / "Makefile", 18 + root / "README.md", 19 + ] 20 + 21 + blocked = ["HV_" + "VERSION", "0" + ".1.0", "get_hv_" + "version"] 22 + 23 + for path in text_files: 24 + text = path.read_text() 25 + if any(item in text for item in blocked): 26 + print(f"project versioning residue in {path.relative_to(root)}") 27 + sys.exit(1) 28 + 29 + for path in [*root.glob("core/**/*.c"), *root.glob("drivers/**/*.c"), *root.glob("mm/**/*.c"), *root.glob("arch/**/*.c"), *root.glob("guest/**/*.c"), *root.glob("include/**/*.h")]: 30 + text = path.read_text() 31 + if re.search(r"/\*|//", text): 32 + print(f"source comment residue in {path.relative_to(root)}") 33 + sys.exit(1) 34 + 35 + config = (root / "include/hv/config.h").read_text() 36 + if "#define CONFIG_MONITOR_MUTATION 0" not in config: 37 + print("monitor mutation must default off") 38 + sys.exit(1) 39 + 40 + monitor = (root / "core/monitor.c").read_text() 41 + required = [ 42 + "write64 disabled", 43 + "unmap disabled", 44 + "irq disabled", 45 + "profile disabled", 46 + "restore disabled", 47 + "log clear disabled", 48 + ] 49 + for item in required: 50 + if item not in monitor: 51 + print(f"missing monitor gate: {item}") 52 + sys.exit(1) 53 + 54 + makefile = (root / "Makefile").read_text() 55 + for target in ["test-host", "test-diagnostic-smoke", "test-linux-smoke", "ci", "release"]: 56 + if f"{target}:" not in makefile: 57 + print(f"missing target {target}") 58 + sys.exit(1)
+28
scripts/release.sh
··· 1 + #!/usr/bin/env bash 2 + set -eu 3 + 4 + project=ariel 5 + profile=qemu-virt-aarch64 6 + dist=dist 7 + release_dir="$dist/$project-$profile" 8 + archive="$dist/$project-$profile.tar.gz" 9 + 10 + rm -rf "$release_dir" "$archive" "$archive.sha256" 11 + mkdir -p "$release_dir/bin" "$release_dir/tools" "$release_dir/manifests" 12 + 13 + cp build/hypervisor.elf "$release_dir/bin/" 14 + cp build/hypervisor.bin "$release_dir/bin/" 15 + cp build/hypervisor.map "$release_dir/manifests/" 16 + cp build/hypervisor.readelf "$release_dir/manifests/" 17 + cp build/hypervisor.objdump "$release_dir/manifests/" 18 + cp README.md "$release_dir/" 19 + cp Makefile "$release_dir/" 20 + cp tools/log_decode.py "$release_dir/tools/" 21 + 22 + find "$release_dir" -type f -print | sort | while read -r file; do 23 + sha256sum "$file" 24 + done > "$release_dir/manifests/SHA256SUMS" 25 + 26 + tar --sort=name --owner=0 --group=0 --numeric-owner -czf "$archive" -C "$dist" "$project-$profile" 27 + sha256sum "$archive" > "$archive.sha256" 28 + printf '%s\n' "$archive"
+24
tests/host/test_hv_types.c
··· 1 + #include "hv/types.h" 2 + 3 + #include <assert.h> 4 + #include <stdint.h> 5 + 6 + int main(void) 7 + { 8 + uint64_t out = 0; 9 + 10 + assert(!hv_add_overflow_u64(1u, 2u, &out)); 11 + assert(out == 3u); 12 + assert(hv_add_overflow_u64(UINT64_MAX, 1u, &out)); 13 + assert(hv_range_overlaps(10u, 5u, 14u, 3u)); 14 + assert(!hv_range_overlaps(10u, 4u, 14u, 3u)); 15 + assert(hv_range_overlaps(UINT64_MAX - 1u, 4u, 0u, 1u)); 16 + assert(hv_is_aligned_u64(0x4000u, 0x1000u)); 17 + assert(!hv_is_aligned_u64(0x4001u, 0x1000u)); 18 + assert(!hv_is_aligned_u64(0x4000u, 0u)); 19 + assert(HV_ALIGN_DOWN(0x12345ull, 0x1000ull) == 0x12000ull); 20 + assert(HV_ALIGN_UP(0x12345ull, 0x1000ull) == 0x13000ull); 21 + assert(HV_MASK(64u) == UINT64_MAX); 22 + assert(HV_MASK(12u) == 0xfffull); 23 + return 0; 24 + }
+25
tests/test_log_decode.py
··· 1 + import subprocess 2 + import sys 3 + 4 + 5 + def run_log_decode(input_text: str, *args: str) -> str: 6 + proc = subprocess.run( 7 + [sys.executable, "tools/log_decode.py", *args], 8 + input=input_text, 9 + text=True, 10 + check=True, 11 + capture_output=True, 12 + ) 13 + return proc.stdout 14 + 15 + 16 + sample = "event seq=7 vcpu=0 kind=trap ec=0x18 name=sctlr_el1 dir=write action=3 reason=104\n" 17 + 18 + plain = run_log_decode(sample) 19 + assert "seq=7" in plain 20 + assert "name=sctlr_el1" in plain 21 + 22 + summary = run_log_decode(sample, "--summary") 23 + assert "events: 1" in summary 24 + assert "trap: 1" in summary 25 + assert "sctlr_el1: 1" in summary
+70
tools/log_decode.py
··· 1 + #!/usr/bin/env python3 2 + from __future__ import annotations 3 + 4 + import argparse 5 + from collections import Counter 6 + import shlex 7 + import sys 8 + 9 + 10 + def parse(line: str) -> dict[str, str]: 11 + parts = shlex.split(line.strip()) 12 + if not parts or parts[0] != "event": 13 + return {} 14 + out: dict[str, str] = {} 15 + for part in parts[1:]: 16 + if "=" in part: 17 + key, value = part.split("=", 1) 18 + out[key] = value 19 + return out 20 + 21 + 22 + def emit_event(event: dict[str, str]) -> None: 23 + print( 24 + "seq={seq} vcpu={vcpu} kind={kind} ec={ec} name={name} " 25 + "dir={dir} action={action} reason={reason}".format( 26 + seq=event.get("seq", "-"), 27 + vcpu=event.get("vcpu", "-"), 28 + kind=event.get("kind", "-"), 29 + ec=event.get("ec", "-"), 30 + name=event.get("name", "-"), 31 + dir=event.get("dir", "-"), 32 + action=event.get("action", "-"), 33 + reason=event.get("reason", "-"), 34 + ) 35 + ) 36 + 37 + 38 + def print_counter(title: str, counter: Counter[str]) -> None: 39 + print(title) 40 + for key, count in counter.most_common(): 41 + print(f" {key}: {count}") 42 + 43 + 44 + def main() -> int: 45 + parser = argparse.ArgumentParser() 46 + parser.add_argument("--summary", action="store_true", help="print aggregate trace counters") 47 + parser.add_argument("--events", action="store_true", help="print normalized events with --summary") 48 + args = parser.parse_args() 49 + 50 + events: list[dict[str, str]] = [] 51 + for line in sys.stdin: 52 + event = parse(line) 53 + if not event: 54 + continue 55 + events.append(event) 56 + if not args.summary or args.events: 57 + emit_event(event) 58 + 59 + if args.summary: 60 + print(f"events: {len(events)}") 61 + print_counter("by-kind", Counter(e.get("kind", "-") for e in events)) 62 + print_counter("by-ec", Counter(e.get("ec", "-") for e in events)) 63 + print_counter("by-sysreg", Counter(e.get("name", "-") for e in events if e.get("kind") == "trap")) 64 + print_counter("by-action", Counter(e.get("action", "-") for e in events)) 65 + print_counter("by-reason", Counter(e.get("reason", "-") for e in events)) 66 + return 0 67 + 68 + 69 + if __name__ == "__main__": 70 + raise SystemExit(main())