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
11 kB 388 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 38static bool table_arena_aligned(void) 39{ 40 if (!hv_is_aligned_u64((uint64_t)(uintptr_t)g_l1, 65536u)) { 41 return false; 42 } 43 for (uint32_t i = 0; i < HV_ARRAY_SIZE(g_l2_tables); i++) { 44 if (!hv_is_aligned_u64((uint64_t)(uintptr_t)g_l2_tables[i], CONFIG_PAGE_SIZE)) { 45 return false; 46 } 47 } 48 return true; 49} 50 51void stage2_init(void) 52{ 53 BUG_ON(!table_arena_aligned()); 54 hv_memset(g_l1, 0, sizeof(g_l1)); 55 hv_memset(g_l2_tables, 0, sizeof(g_l2_tables)); 56 g_l2_used = 0; 57} 58 59static uint64_t *ensure_l2(uint64_t l1_index) 60{ 61 if (l1_index >= S2_ENTRIES) { 62 return (uint64_t *)0; 63 } 64 if ((g_l1[l1_index] & S2_DESC_VALID) != 0u) { 65 return (uint64_t *)(uintptr_t)desc_addr(g_l1[l1_index]); 66 } 67 if (g_l2_used >= HV_ARRAY_SIZE(g_l2_tables)) { 68 return (uint64_t *)0; 69 } 70 uint64_t *table = g_l2_tables[g_l2_used++]; 71 g_l1[l1_index] = ((uint64_t)(uintptr_t)table & 0x0000fffffffff000ull) | 72 S2_DESC_TABLE | S2_DESC_VALID; 73 return table; 74} 75 76static uint64_t block_desc(paddr_t pa, enum stage2_mem_type type, uint32_t perms) 77{ 78 uint64_t desc = (pa & 0x0000ffffffe00000ull) | S2_DESC_VALID | S2_DESC_AF; 79 desc |= (type == S2_MEM_NORMAL) ? (S2_DESC_MEMATTR_NORMAL | S2_DESC_SH_INNER) : 80 S2_DESC_MEMATTR_DEVICE; 81 if ((perms & S2_PERM_R) != 0u) { 82 desc |= S2_DESC_S2AP_R; 83 } 84 if ((perms & S2_PERM_W) != 0u) { 85 desc |= S2_DESC_S2AP_W; 86 } 87 if ((perms & S2_PERM_X) == 0u) { 88 desc |= S2_DESC_XN; 89 } 90 return desc; 91} 92 93static bool descriptor_valid(uint64_t desc) 94{ 95 if ((desc & S2_DESC_VALID) == 0u) { 96 return false; 97 } 98 if ((desc & S2_DESC_AF) == 0u) { 99 return false; 100 } 101 if (!hv_is_aligned_u64(desc_addr(desc), S2_L2_SIZE)) { 102 return false; 103 } 104 return true; 105} 106 107static uint64_t *lookup_l2(uint64_t l1_index) 108{ 109 if (l1_index >= S2_ENTRIES || (g_l1[l1_index] & S2_DESC_VALID) == 0u) { 110 return (uint64_t *)0; 111 } 112 if ((g_l1[l1_index] & S2_DESC_TABLE) == 0u) { 113 return (uint64_t *)0; 114 } 115 return (uint64_t *)(uintptr_t)desc_addr(g_l1[l1_index]); 116} 117 118static bool l2_table_owned(const uint64_t *table) 119{ 120 uintptr_t base = (uintptr_t)&g_l2_tables[0][0]; 121 uintptr_t end = (uintptr_t)g_l2_tables + sizeof(g_l2_tables); 122 uintptr_t addr = (uintptr_t)table; 123 return addr >= base && addr < end && hv_is_aligned_u64((uint64_t)addr, CONFIG_PAGE_SIZE); 124} 125 126bool stage2_map_range(ipa_t ipa, paddr_t pa, uint64_t size, enum stage2_mem_type type, uint32_t perms) 127{ 128 uint64_t ipa_end; 129 uint64_t pa_end; 130 131 if (size == 0u || perms == 0u) { 132 return false; 133 } 134 if (!hv_is_aligned_u64(ipa, S2_L2_SIZE) || !hv_is_aligned_u64(pa, S2_L2_SIZE) || 135 !hv_is_aligned_u64(size, S2_L2_SIZE)) { 136 return false; 137 } 138 if (hv_add_overflow_u64(ipa, size, &ipa_end) || 139 hv_add_overflow_u64(pa, size, &pa_end)) { 140 return false; 141 } 142 if (ipa_end <= ipa || pa_end <= pa) { 143 return false; 144 } 145 if (hv_range_overlaps(pa, size, (uint64_t)(uintptr_t)__image_start, 146 (uint64_t)((uintptr_t)__image_end - (uintptr_t)__image_start))) { 147 return false; 148 } 149 150 for (uint64_t off = 0; off < size; off += S2_L2_SIZE) { 151 uint64_t cur_ipa = ipa + off; 152 uint64_t l1_index = (cur_ipa >> S2_L1_SHIFT) & 0x1ffu; 153 uint64_t l2_index = (cur_ipa >> S2_L2_SHIFT) & 0x1ffu; 154 uint64_t *l2 = ensure_l2(l1_index); 155 if (l2 == (uint64_t *)0 || (l2[l2_index] & S2_DESC_VALID) != 0u) { 156 return false; 157 } 158 uint64_t desc = block_desc(pa + off, type, perms); 159 if (!descriptor_valid(desc)) { 160 return false; 161 } 162 l2[l2_index] = desc; 163 } 164 arch_tlbi_vmalle1is(); 165 return true; 166} 167 168bool stage2_unmap_range(ipa_t ipa, uint64_t size) 169{ 170 if (size == 0u || !hv_is_aligned_u64(ipa, S2_L2_SIZE) || 171 !hv_is_aligned_u64(size, S2_L2_SIZE)) { 172 return false; 173 } 174 175 for (uint64_t off = 0; off < size; off += S2_L2_SIZE) { 176 uint64_t cur_ipa = ipa + off; 177 uint64_t l1_index = (cur_ipa >> S2_L1_SHIFT) & 0x1ffu; 178 uint64_t l2_index = (cur_ipa >> S2_L2_SHIFT) & 0x1ffu; 179 uint64_t *l2 = lookup_l2(l1_index); 180 if (l2 == (uint64_t *)0 || (l2[l2_index] & S2_DESC_VALID) == 0u) { 181 return false; 182 } 183 } 184 185 for (uint64_t off = 0; off < size; off += S2_L2_SIZE) { 186 uint64_t cur_ipa = ipa + off; 187 uint64_t l1_index = (cur_ipa >> S2_L1_SHIFT) & 0x1ffu; 188 uint64_t l2_index = (cur_ipa >> S2_L2_SHIFT) & 0x1ffu; 189 uint64_t *l2 = lookup_l2(l1_index); 190 l2[l2_index] = 0u; 191 } 192 arch_tlbi_vmalle1is(); 193 return true; 194} 195 196bool stage2_translate(ipa_t ipa, struct stage2_translation *out) 197{ 198 uint64_t l1_index = (ipa >> S2_L1_SHIFT) & 0x1ffu; 199 uint64_t l2_index = (ipa >> S2_L2_SHIFT) & 0x1ffu; 200 uint64_t *l2 = lookup_l2(l1_index); 201 uint64_t desc; 202 203 if (out != (struct stage2_translation *)0) { 204 hv_memset(out, 0, sizeof(*out)); 205 } 206 if (l2 == (uint64_t *)0 || (l2[l2_index] & S2_DESC_VALID) == 0u) { 207 return false; 208 } 209 210 desc = l2[l2_index]; 211 if (out != (struct stage2_translation *)0) { 212 out->mapped = true; 213 out->ipa_base = HV_ALIGN_DOWN(ipa, S2_L2_SIZE); 214 out->pa_base = desc_addr(desc); 215 out->size = S2_L2_SIZE; 216 out->desc = desc; 217 if ((desc & S2_DESC_S2AP_R) != 0u) { 218 out->perms |= S2_PERM_R; 219 } 220 if ((desc & S2_DESC_S2AP_W) != 0u) { 221 out->perms |= S2_PERM_W; 222 } 223 if ((desc & S2_DESC_XN) == 0u) { 224 out->perms |= S2_PERM_X; 225 } 226 out->type = (desc & S2_DESC_MEMATTR_NORMAL) == S2_DESC_MEMATTR_NORMAL ? 227 S2_MEM_NORMAL : S2_MEM_DEVICE; 228 } 229 return true; 230} 231 232ipa_t stage2_ipa_from_abort(uint64_t far_el2, uint64_t hpfar_el2) 233{ 234 return ((hpfar_el2 & 0x0000000ffffffff0ull) << 8u) | (far_el2 & 0xfffull); 235} 236 237static bool dfsc_is_translation(uint32_t dfsc) 238{ 239 return dfsc >= 0x04u && dfsc <= 0x07u; 240} 241 242enum stage2_fault_intent { 243 S2_FAULT_PAUSE = 0, 244 S2_FAULT_REPAIR, 245 S2_FAULT_MMIO, 246}; 247 248static enum stage2_fault_intent classify_fault(ipa_t ipa, uint32_t dfsc) 249{ 250 if (mmio_policy_lookup(ipa) != (const struct mmio_policy_region *)0) { 251 return S2_FAULT_MMIO; 252 } 253 if (dfsc_is_translation(dfsc) && 254 ipa >= CONFIG_GUEST_IPA_BASE && 255 ipa < CONFIG_GUEST_IPA_BASE + CONFIG_GUEST_RAM_SIZE) { 256 return S2_FAULT_REPAIR; 257 } 258 return S2_FAULT_PAUSE; 259} 260 261bool stage2_handle_abort(struct vcpu *vcpu, const struct trap_frame *frame, bool instruction) 262{ 263 ipa_t ipa; 264 ipa_t block_ipa; 265 paddr_t block_pa; 266 uint32_t dfsc; 267 struct log_event ev; 268 enum stage2_fault_intent intent; 269 270 if (vcpu == (struct vcpu *)0 || frame == (const struct trap_frame *)0) { 271 return false; 272 } 273 274 ipa = stage2_ipa_from_abort(frame->far_el2, frame->hpfar_el2); 275 dfsc = (uint32_t)(frame->esr_el2 & 0x3fu); 276 hv_memset(&ev, 0, sizeof(ev)); 277 ev.kind = LOG_ABORT; 278 ev.vcpu = vcpu->id; 279 ev.ec = esr_ec(frame->esr_el2); 280 ev.iss = frame->esr_el2 & 0x01ffffffu; 281 ev.elr = frame->elr_el2; 282 ev.far = ipa; 283 ev.hpfar = frame->hpfar_el2; 284 ev.pstate = frame->spsr_el2; 285 ev.reason = instruction ? 406u : 407u; 286 287 intent = classify_fault(ipa, dfsc); 288 if (intent == S2_FAULT_MMIO) { 289 const struct mmio_policy_region *mmio = mmio_policy_lookup(ipa); 290 ev.reason = 410u; 291 ev.name = mmio->name; 292 ev.action = (uint32_t)mmio->action; 293 ev.result = mmio->provenance; 294 if (mmio_policy_emulate(vcpu, ipa, frame->esr_el2)) { 295 ev.reason = 411u; 296 log_event_commit(&ev); 297 arch_advance_guest_pc(vcpu); 298 return true; 299 } 300 log_event_commit(&ev); 301 return false; 302 } 303 304 if (intent != S2_FAULT_REPAIR) { 305 log_event_commit(&ev); 306 return false; 307 } 308 309 block_ipa = HV_ALIGN_DOWN(ipa, S2_L2_SIZE); 310 block_pa = CONFIG_GUEST_RAM_BASE + (block_ipa - CONFIG_GUEST_IPA_BASE); 311 if (!stage2_map_range(block_ipa, block_pa, S2_L2_SIZE, S2_MEM_NORMAL, 312 S2_PERM_R | S2_PERM_W | S2_PERM_X)) { 313 log_event_commit(&ev); 314 return false; 315 } 316 317 ev.reason = instruction ? 408u : 409u; 318 ev.result = block_pa; 319 log_event_commit(&ev); 320 return true; 321} 322 323uint64_t stage2_vttbr(void) 324{ 325 return (uint64_t)(uintptr_t)g_l1; 326} 327 328uint64_t stage2_vtcr(void) 329{ 330 uint64_t vtcr = 0; 331 vtcr |= 24ull; 332 vtcr |= (1ull << 6u); 333 vtcr |= (1ull << 8u); 334 vtcr |= (1ull << 10u); 335 vtcr |= (3ull << 12u); 336 vtcr |= (2ull << 16u); 337 return vtcr; 338} 339 340void stage2_dump(void) 341{ 342 uart_puts("stage2 root="); 343 uart_put_hex64(stage2_vttbr()); 344 uart_puts(" vtcr="); 345 uart_put_hex64(stage2_vtcr()); 346 uart_puts(" l2_tables="); 347 uart_put_dec64(g_l2_used); 348 uart_puts("\n"); 349 for (uint32_t l1 = 0; l1 < S2_ENTRIES; l1++) { 350 if ((g_l1[l1] & S2_DESC_VALID) == 0u) { 351 continue; 352 } 353 uint64_t *l2 = (uint64_t *)(uintptr_t)desc_addr(g_l1[l1]); 354 for (uint32_t l2i = 0; l2i < S2_ENTRIES; l2i++) { 355 if ((l2[l2i] & S2_DESC_VALID) != 0u) { 356 uart_puts("mapping ipa="); 357 uart_put_hex64(((uint64_t)l1 << S2_L1_SHIFT) | ((uint64_t)l2i << S2_L2_SHIFT)); 358 uart_puts(" desc="); 359 uart_put_hex64(l2[l2i]); 360 uart_puts("\n"); 361 } 362 } 363 } 364} 365 366bool stage2_selftest(void) 367{ 368 if (!table_arena_aligned()) { 369 return false; 370 } 371 for (uint32_t i = 0; i < S2_ENTRIES; i++) { 372 if ((g_l1[i] & S2_DESC_VALID) != 0u && (g_l1[i] & S2_DESC_TABLE) == 0u) { 373 return false; 374 } 375 if ((g_l1[i] & S2_DESC_VALID) != 0u) { 376 uint64_t *l2 = (uint64_t *)(uintptr_t)desc_addr(g_l1[i]); 377 if (!l2_table_owned(l2)) { 378 return false; 379 } 380 for (uint32_t j = 0; j < S2_ENTRIES; j++) { 381 if ((l2[j] & S2_DESC_VALID) != 0u && !descriptor_valid(l2[j])) { 382 return false; 383 } 384 } 385 } 386 } 387 return stage2_vttbr() == (uint64_t)(uintptr_t)g_l1; 388}