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.

ariel / mm / stage2.c
10 kB 343 lines
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 29static uint64_t g_l1[S2_ENTRIES] __attribute__((aligned(65536))); 30static uint64_t g_l2_tables[4][S2_ENTRIES] __attribute__((aligned(4096))); 31static uint32_t g_l2_used; 32 33static uint64_t desc_addr(uint64_t addr) 34{ 35 return addr & 0x0000fffffffff000ull; 36} 37 38void 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 45static 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 62static 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 79static 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 93static 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 104bool 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 146bool 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 174bool 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 210ipa_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 215static bool dfsc_is_translation(uint32_t dfsc) 216{ 217 return dfsc >= 0x04u && dfsc <= 0x07u; 218} 219 220bool 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 281uint64_t stage2_vttbr(void) 282{ 283 return (uint64_t)(uintptr_t)g_l1; 284} 285 286uint64_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 298void 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 324bool 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}