# ariel 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 ## Build/Run ```sh make clean make ``` ```sh make run ``` Equivalent QEMU shape ```sh qemu-system-aarch64 \ -M virt,virtualization=on,gic-version=3 \ -cpu cortex-a57 \ -m 1G \ -nographic \ -serial mon:stdio \ -kernel build/hypervisor.elf ``` Run a Linux arm64 `Image` with a QEMU `virt` DTB ```sh make run-linux \ LINUX_IMAGE=/path/Image \ LINUX_DTB=/path/qemu-virt.dtb ``` Or just do it with an initrd ```sh make run-linux \ LINUX_IMAGE=/path/Image \ LINUX_DTB=/path/qemu-virt.dtb \ LINUX_INITRD=/path/initrd.gz ``` Fetch the tested Debian installer artifacts and smoke test the Linux path ```sh make test-linux-smoke ``` and complete the local gate ```sh make test make release ``` ## Memory ```text hypervisor link PA 0x40080000 guest IPA base 0x40000000 guest backing PA 0x42000000 guest RAM size 0x30000000 768 MiB diagnostic entry IPA 0x40000000 diagnostic stack top 0x40100000 Linux Image load PA 0x42000000 Linux Image entry IPA Image text_offset + 0x40000000 Linux initrd PA 0x46000000 Linux initrd IPA 0x44000000 Linux DTB PA 0x71e00000 Linux DTB IPA 0x6fe00000 PL011 UART PA 0x09000000 GICD PA 0x08000000 GICR PA 0x080a0000 ``` 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 ## Trap Path EL1 traps enter the EL2 vector table and the assembly path saves general registers and exception state into `struct trap_frame` ```text x0-x30 sp_el0 sp_el1 elr_el2 spsr_el2 esr_el2 far_el2 hpfar_el2 ``` ## Hypercalls ```c enum { HVC_GET_HV_ID = 0, HVC_DUMP_LOG = 1, HVC_GET_STATUS = 2, HVC_PAUSE = 3, HVC_GUEST_CONSOLE_WRITE = 4, HVC_REPORT_EXCEPTION = 5, }; ``` Guest side call pattern ```c static inline unsigned long hvc(unsigned long op, unsigned long a1, unsigned long a2, unsigned long a3) { register unsigned long x0 asm("x0") = op; register unsigned long x1 asm("x1") = a1; register unsigned long x2 asm("x2") = a2; register unsigned long x3 asm("x3") = a3; asm volatile("hvc #0" : "+r"(x0) : "r"(x1), "r"(x2), "r"(x3) : "memory"); return x0; } ``` Console write from guest IPA memory ```c const char msg[] = "guest online"; hvc(HVC_GUEST_CONSOLE_WRITE, (unsigned long)msg, sizeof(msg) - 1, 0); ``` Pause into the EL2 monitor: ```c hvc(HVC_PAUSE, 0, 0, 0); ``` ## Logs ```sh make run 2>&1 | tee uart.log python3 tools/log_decode.py --summary < uart.log ```