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 368 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 242bool stage2_handle_abort(struct vcpu *vcpu, const struct trap_frame *frame, bool instruction) 243{ 244 ipa_t ipa; 245 ipa_t block_ipa; 246 paddr_t block_pa; 247 uint32_t dfsc; 248 struct log_event ev; 249 250 if (vcpu == (struct vcpu *)0 || frame == (const struct trap_frame *)0) { 251 return false; 252 } 253 254 ipa = stage2_ipa_from_abort(frame->far_el2, frame->hpfar_el2); 255 dfsc = (uint32_t)(frame->esr_el2 & 0x3fu); 256 hv_memset(&ev, 0, sizeof(ev)); 257 ev.kind = LOG_ABORT; 258 ev.vcpu = vcpu->id; 259 ev.ec = esr_ec(frame->esr_el2); 260 ev.iss = frame->esr_el2 & 0x01ffffffu; 261 ev.elr = frame->elr_el2; 262 ev.far = ipa; 263 ev.hpfar = frame->hpfar_el2; 264 ev.pstate = frame->spsr_el2; 265 ev.reason = instruction ? 406u : 407u; 266 267 const struct mmio_policy_region *mmio = mmio_policy_lookup(ipa); 268 if (mmio != (const struct mmio_policy_region *)0) { 269 ev.reason = 410u; 270 ev.name = mmio->name; 271 ev.action = (uint32_t)mmio->action; 272 if (mmio_policy_emulate(vcpu, ipa, frame->esr_el2)) { 273 ev.reason = 411u; 274 log_event_commit(&ev); 275 arch_advance_guest_pc(vcpu); 276 return true; 277 } 278 log_event_commit(&ev); 279 return false; 280 } 281 282 if (!dfsc_is_translation(dfsc) || 283 ipa < CONFIG_GUEST_IPA_BASE || 284 ipa >= CONFIG_GUEST_IPA_BASE + CONFIG_GUEST_RAM_SIZE) { 285 log_event_commit(&ev); 286 return false; 287 } 288 289 block_ipa = HV_ALIGN_DOWN(ipa, S2_L2_SIZE); 290 block_pa = CONFIG_GUEST_RAM_BASE + (block_ipa - CONFIG_GUEST_IPA_BASE); 291 if (!stage2_map_range(block_ipa, block_pa, S2_L2_SIZE, S2_MEM_NORMAL, 292 S2_PERM_R | S2_PERM_W | S2_PERM_X)) { 293 log_event_commit(&ev); 294 return false; 295 } 296 297 ev.reason = instruction ? 408u : 409u; 298 ev.result = block_pa; 299 log_event_commit(&ev); 300 return true; 301} 302 303uint64_t stage2_vttbr(void) 304{ 305 return (uint64_t)(uintptr_t)g_l1; 306} 307 308uint64_t stage2_vtcr(void) 309{ 310 uint64_t vtcr = 0; 311 vtcr |= 24ull; 312 vtcr |= (1ull << 6u); 313 vtcr |= (1ull << 8u); 314 vtcr |= (1ull << 10u); 315 vtcr |= (3ull << 12u); 316 vtcr |= (2ull << 16u); 317 return vtcr; 318} 319 320void stage2_dump(void) 321{ 322 uart_puts("stage2 root="); 323 uart_put_hex64(stage2_vttbr()); 324 uart_puts(" vtcr="); 325 uart_put_hex64(stage2_vtcr()); 326 uart_puts(" l2_tables="); 327 uart_put_dec64(g_l2_used); 328 uart_puts("\n"); 329 for (uint32_t l1 = 0; l1 < S2_ENTRIES; l1++) { 330 if ((g_l1[l1] & S2_DESC_VALID) == 0u) { 331 continue; 332 } 333 uint64_t *l2 = (uint64_t *)(uintptr_t)desc_addr(g_l1[l1]); 334 for (uint32_t l2i = 0; l2i < S2_ENTRIES; l2i++) { 335 if ((l2[l2i] & S2_DESC_VALID) != 0u) { 336 uart_puts("mapping ipa="); 337 uart_put_hex64(((uint64_t)l1 << S2_L1_SHIFT) | ((uint64_t)l2i << S2_L2_SHIFT)); 338 uart_puts(" desc="); 339 uart_put_hex64(l2[l2i]); 340 uart_puts("\n"); 341 } 342 } 343 } 344} 345 346bool stage2_selftest(void) 347{ 348 if (!table_arena_aligned()) { 349 return false; 350 } 351 for (uint32_t i = 0; i < S2_ENTRIES; i++) { 352 if ((g_l1[i] & S2_DESC_VALID) != 0u && (g_l1[i] & S2_DESC_TABLE) == 0u) { 353 return false; 354 } 355 if ((g_l1[i] & S2_DESC_VALID) != 0u) { 356 uint64_t *l2 = (uint64_t *)(uintptr_t)desc_addr(g_l1[i]); 357 if (!l2_table_owned(l2)) { 358 return false; 359 } 360 for (uint32_t j = 0; j < S2_ENTRIES; j++) { 361 if ((l2[j] & S2_DESC_VALID) != 0u && !descriptor_valid(l2[j])) { 362 return false; 363 } 364 } 365 } 366 } 367 return stage2_vttbr() == (uint64_t)(uintptr_t)g_l1; 368}