AArch64 EL2 hypervisor for QEMU virt that boots at EL2
3.2 kB
147 lines
1# ariel
2
3AArch64 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
8make clean
9make
10```
11
12```sh
13make run
14```
15
16Equivalent QEMU shape
17
18```sh
19qemu-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
28Run a Linux arm64 `Image` with a QEMU `virt` DTB
29
30```sh
31make run-linux \
32 LINUX_IMAGE=/path/Image \
33 LINUX_DTB=/path/qemu-virt.dtb
34```
35
36Or just do it with an initrd
37
38```sh
39make run-linux \
40 LINUX_IMAGE=/path/Image \
41 LINUX_DTB=/path/qemu-virt.dtb \
42 LINUX_INITRD=/path/initrd.gz
43```
44
45Fetch the tested Debian installer artifacts and smoke test the Linux path
46
47```sh
48make test-linux-smoke
49```
50and complete the local gate
51
52```sh
53make test
54make release
55```
56
57## Memory
58
59```text
60hypervisor link PA 0x40080000
61guest IPA base 0x40000000
62guest backing PA 0x42000000
63guest RAM size 0x30000000 768 MiB
64diagnostic entry IPA 0x40000000
65diagnostic stack top 0x40100000
66Linux Image load PA 0x42000000
67Linux Image entry IPA Image text_offset + 0x40000000
68Linux initrd PA 0x46000000
69Linux initrd IPA 0x44000000
70Linux DTB PA 0x71e00000
71Linux DTB IPA 0x6fe00000
72PL011 UART PA 0x09000000
73GICD PA 0x08000000
74GICR PA 0x080a0000
75```
76
77S2 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
81EL1 traps enter the EL2 vector table and the assembly path saves general registers and exception state into `struct trap_frame`
82
83```text
84x0-x30
85sp_el0
86sp_el1
87elr_el2
88spsr_el2
89esr_el2
90far_el2
91hpfar_el2
92```
93
94## Hypercalls
95
96
97```c
98enum {
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
108Guest side call pattern
109
110```c
111static 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
129Console write from guest IPA memory
130
131```c
132const char msg[] = "guest online";
133hvc(HVC_GUEST_CONSOLE_WRITE, (unsigned long)msg, sizeof(msg) - 1, 0);
134```
135
136Pause into the EL2 monitor:
137
138```c
139hvc(HVC_PAUSE, 0, 0, 0);
140```
141
142## Logs
143
144```sh
145make run 2>&1 | tee uart.log
146python3 tools/log_decode.py --summary < uart.log
147```