AArch64 EL2 hypervisor for QEMU virt that boots at EL2
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
21static uint64_t g_el2_l1[EL2_PT_ENTRIES] __attribute__((aligned(4096)));
22static uint64_t g_el2_l2[2][EL2_PT_ENTRIES] __attribute__((aligned(4096)));
23static uint64_t g_guard_low;
24static uint64_t g_guard_high;
25static bool g_ready;
26static bool g_enabled;
27
28static 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
38static 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
51void 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
83uint64_t el2_mmu_root(void)
84{
85 return (uint64_t)(uintptr_t)g_el2_l1;
86}
87
88uint64_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
99uint64_t el2_mmu_mair(void)
100{
101 return EL2_MAIR;
102}
103
104void 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
113void 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
132bool 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}