- C 87.9%
- Makefile 4.2%
- Assembly 3.5%
- Python 3.4%
- Shell 0.5%
- Other 0.5%
| arch/aarch64 | ||
| core | ||
| drivers | ||
| guest | ||
| include/hv | ||
| mm | ||
| scripts | ||
| tests | ||
| tools | ||
| .gitignore | ||
| linker.ld | ||
| Makefile | ||
| README.md | ||
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
make clean
make
make run
Equivalent QEMU shape
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
make run-linux \
LINUX_IMAGE=/path/Image \
LINUX_DTB=/path/qemu-virt.dtb
Or just do it with an initrd
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
make test-linux-smoke
and complete the local gate
make test
make release
Memory
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
x0-x30
sp_el0
sp_el1
elr_el2
spsr_el2
esr_el2
far_el2
hpfar_el2
Hypercalls
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
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
const char msg[] = "guest online";
hvc(HVC_GUEST_CONSOLE_WRITE, (unsigned long)msg, sizeof(msg) - 1, 0);
Pause into the EL2 monitor:
hvc(HVC_PAUSE, 0, 0, 0);
Logs
make run 2>&1 | tee uart.log
python3 tools/log_decode.py --summary < uart.log