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 / core / policy.c
14 kB 344 lines
1#include "hv/arch.h" 2#include "hv/config.h" 3#include "hv/policy.h" 4#include "hv/string.h" 5#include "hv/timer.h" 6#include "hv/uart.h" 7 8#define KEY(op0, op1, crn, crm, op2) SYSREG_KEY_CONST((op0), (op1), (crn), (crm), (op2)) 9#define RO_ID(name, op0, op1, crn, crm, op2, val) \ 10 { KEY(op0, op1, crn, crm, op2), name, POLICY_CLASS_ID, POLICY_EMULATE_READ, POLICY_DENY_UNDEF, val, 0, 1, 100 } 11#define SHADOW_RW(name, op0, op1, crn, crm, op2, field, mask) \ 12 { KEY(op0, op1, crn, crm, op2), name, POLICY_CLASS_MMU, POLICY_EMULATE_READ, POLICY_WRITE_MASK, 0, mask, 1, 101 } 13#define SHADOW_THREAD(name, op0, op1, crn, crm, op2) \ 14 { KEY(op0, op1, crn, crm, op2), name, POLICY_CLASS_THREAD, POLICY_EMULATE_READ, POLICY_EMULATE_WRITE, 0, UINT64_MAX, 1, 102 } 15#define DENY_REG(name, op0, op1, crn, crm, op2, cls) \ 16 { KEY(op0, op1, crn, crm, op2), name, cls, POLICY_DENY_UNDEF, POLICY_DENY_UNDEF, 0, 0, 1, 200 } 17 18static const struct policy_entry g_policy[] = { 19 RO_ID("midr_el1", 3, 0, 0, 0, 0, 0x00000000410fd034ull), 20 RO_ID("mpidr_el1", 3, 0, 0, 0, 5, 0x80000000ull), 21 RO_ID("id_aa64pfr0_el1", 3, 0, 0, 4, 0, 0x0000000000000011ull), 22 RO_ID("id_aa64pfr1_el1", 3, 0, 0, 4, 1, 0x0ull), 23 RO_ID("id_aa64isar0_el1", 3, 0, 0, 6, 0, 0x0ull), 24 RO_ID("id_aa64isar1_el1", 3, 0, 0, 6, 1, 0x0ull), 25 RO_ID("id_aa64mmfr0_el1", 3, 0, 0, 7, 0, 0x0000000000001122ull), 26 RO_ID("id_aa64mmfr1_el1", 3, 0, 0, 7, 1, 0x0ull), 27 RO_ID("clidr_el1", 3, 1, 0, 0, 1, 0x0ull), 28 { KEY(3, 2, 0, 0, 0), "csselr_el1", POLICY_CLASS_MISC, POLICY_EMULATE_READ, POLICY_EMULATE_WRITE, 0, 0xfu, 1, 103 }, 29 RO_ID("ctr_el0", 3, 3, 0, 0, 1, 0x8444c004ull), 30 RO_ID("dczid_el0", 3, 3, 0, 0, 7, 0x4ull), 31 { KEY(3, 0, 1, 0, 0), "sctlr_el1", POLICY_CLASS_MMU, POLICY_EMULATE_READ, POLICY_WRITE_MASK, 0x30d00800ull, 0x0000000030d0dfffull, 1, 104 }, 32 DENY_REG("actlr_el1", 3, 0, 1, 0, 1, POLICY_CLASS_MISC), 33 { KEY(3, 0, 1, 0, 2), "cpacr_el1", POLICY_CLASS_MMU, POLICY_EMULATE_READ, POLICY_WRITE_MASK, 0, 0x00300000ull, 1, 105 }, 34 DENY_REG("hcr_el2", 3, 4, 1, 1, 0, POLICY_CLASS_EL2_EL3), 35 DENY_REG("scr_el3", 3, 6, 1, 1, 0, POLICY_CLASS_EL2_EL3), 36 { KEY(3, 0, 2, 0, 0), "ttbr0_el1", POLICY_CLASS_MMU, POLICY_EMULATE_READ, POLICY_EMULATE_WRITE, 0, 0x0000fffffffff000ull, 1, 106 }, 37 { KEY(3, 0, 2, 0, 1), "ttbr1_el1", POLICY_CLASS_MMU, POLICY_EMULATE_READ, POLICY_EMULATE_WRITE, 0, 0x0000fffffffff000ull, 1, 107 }, 38 { KEY(3, 0, 2, 0, 2), "tcr_el1", POLICY_CLASS_MMU, POLICY_EMULATE_READ, POLICY_WRITE_MASK, 0, 0x00000003ff3fffffull, 1, 108 }, 39 DENY_REG("vttbr_el2", 3, 4, 2, 1, 0, POLICY_CLASS_EL2_EL3), 40 DENY_REG("pmcr_el0", 3, 3, 9, 12, 0, POLICY_CLASS_PMU), 41 { KEY(3, 0, 10, 2, 0), "mair_el1", POLICY_CLASS_MMU, POLICY_EMULATE_READ, POLICY_EMULATE_WRITE, 0xffull, UINT64_MAX, 1, 109 }, 42 { KEY(3, 0, 12, 0, 0), "vbar_el1", POLICY_CLASS_MMU, POLICY_EMULATE_READ, POLICY_EMULATE_WRITE, 0, 0x0000fffffffff800ull, 1, 110 }, 43 DENY_REG("rvbar_el1", 3, 0, 12, 0, 1, POLICY_CLASS_MISC), 44 SHADOW_THREAD("contextidr_el1", 3, 0, 13, 0, 1), 45 SHADOW_THREAD("tpidr_el1", 3, 0, 13, 0, 4), 46 SHADOW_THREAD("tpidrro_el0", 3, 3, 13, 0, 3), 47 { KEY(3, 0, 14, 1, 0), "cntkctl_el1", POLICY_CLASS_TIMER, POLICY_EMULATE_READ, POLICY_WRITE_MASK, 0, 0x3u, 1, 111 }, 48 { KEY(3, 3, 14, 3, 0), "cntv_tval_el0", POLICY_CLASS_TIMER, POLICY_EMULATE_READ, POLICY_EMULATE_WRITE, 0, UINT64_MAX, 1, 112 }, 49 { KEY(3, 3, 14, 3, 1), "cntv_ctl_el0", POLICY_CLASS_TIMER, POLICY_EMULATE_READ, POLICY_WRITE_MASK, 0, 0x7u, 1, 113 }, 50 { KEY(3, 3, 14, 3, 2), "cntv_cval_el0", POLICY_CLASS_TIMER, POLICY_EMULATE_READ, POLICY_EMULATE_WRITE, 0, UINT64_MAX, 1, 114 }, 51 { KEY(2, 0, 0, 2, 2), "mdscr_el1", POLICY_CLASS_DEBUG, POLICY_EMULATE_READ, POLICY_WRITE_MASK, 0, 0ull, 1, 115 }, 52}; 53 54static enum policy_profile g_profile; 55static struct policy_entry g_effective_entry; 56 57static bool policy_action_valid(enum policy_action action) 58{ 59 return action == POLICY_ALLOW_NATIVE || 60 action == POLICY_EMULATE_READ || 61 action == POLICY_EMULATE_WRITE || 62 action == POLICY_WRITE_MASK || 63 action == POLICY_DENY_SKIP || 64 action == POLICY_DENY_UNDEF || 65 action == POLICY_PAUSE || 66 action == POLICY_PANIC; 67} 68 69static bool policy_class_valid(enum policy_class access_class) 70{ 71 return access_class == POLICY_CLASS_ID || 72 access_class == POLICY_CLASS_MMU || 73 access_class == POLICY_CLASS_TIMER || 74 access_class == POLICY_CLASS_THREAD || 75 access_class == POLICY_CLASS_PMU || 76 access_class == POLICY_CLASS_DEBUG || 77 access_class == POLICY_CLASS_EL2_EL3 || 78 access_class == POLICY_CLASS_MISC; 79} 80 81static bool policy_lattice_valid(const struct policy_entry *entry) 82{ 83 if (entry == (const struct policy_entry *)0 || entry->name == (const char *)0) { 84 return false; 85 } 86 if (!policy_class_valid(entry->access_class) || 87 !policy_action_valid(entry->read_policy) || 88 !policy_action_valid(entry->write_policy)) { 89 return false; 90 } 91 if (entry->access_class == POLICY_CLASS_EL2_EL3 && 92 (entry->read_policy != POLICY_DENY_UNDEF || 93 entry->write_policy != POLICY_DENY_UNDEF)) { 94 return false; 95 } 96 if (entry->write_policy == POLICY_WRITE_MASK && entry->writable_mask == 0u && 97 entry->access_class != POLICY_CLASS_DEBUG) { 98 return false; 99 } 100 return true; 101} 102 103static uint64_t policy_mask_value(const struct policy_entry *entry, uint64_t current, uint64_t operand) 104{ 105 uint64_t preserve = current & ~entry->writable_mask; 106 if (preserve == 0u) { 107 preserve = entry->reset_value & ~entry->writable_mask; 108 } 109 return preserve | (operand & entry->writable_mask); 110} 111 112void policy_init(void) 113{ 114 g_profile = POLICY_PROFILE_STRICT; 115} 116 117const char *policy_profile_name(enum policy_profile profile) 118{ 119 switch (profile) { 120 case POLICY_PROFILE_STRICT: return "strict"; 121 case POLICY_PROFILE_DIAGNOSTIC: return "diagnostic"; 122 case POLICY_PROFILE_LINUX_BOOT: return "linux-boot"; 123 case POLICY_PROFILE_DEBUG: return "debug"; 124 default: return "unknown"; 125 } 126} 127 128bool policy_set_profile(enum policy_profile profile) 129{ 130 if (profile > POLICY_PROFILE_DEBUG) { 131 return false; 132 } 133 g_profile = profile; 134 return true; 135} 136 137bool policy_set_profile_name(const char *name) 138{ 139 if (name == (const char *)0) { 140 return false; 141 } 142 if (hv_strcmp(name, "strict") == 0) { 143 return policy_set_profile(POLICY_PROFILE_STRICT); 144 } 145 if (hv_strcmp(name, "diagnostic") == 0) { 146 return policy_set_profile(POLICY_PROFILE_DIAGNOSTIC); 147 } 148 if (hv_strcmp(name, "linux-boot") == 0) { 149 return policy_set_profile(POLICY_PROFILE_LINUX_BOOT); 150 } 151 if (hv_strcmp(name, "debug") == 0) { 152 return policy_set_profile(POLICY_PROFILE_DEBUG); 153 } 154 return false; 155} 156 157enum policy_profile policy_get_profile(void) 158{ 159 return g_profile; 160} 161 162const struct policy_entry *sysreg_policy_lookup(uint64_t key) 163{ 164 for (uint32_t i = 0; i < HV_ARRAY_SIZE(g_policy); i++) { 165 if (g_policy[i].key == key) { 166 return &g_policy[i]; 167 } 168 } 169 return (const struct policy_entry *)0; 170} 171 172static uint64_t *shadow_slot(struct vcpu *vcpu, uint64_t key) 173{ 174 if (key == KEY(3, 0, 1, 0, 0)) return &vcpu->sysregs.sctlr_el1; 175 if (key == KEY(3, 0, 1, 0, 2)) return &vcpu->sysregs.cpacr_el1; 176 if (key == KEY(3, 0, 2, 0, 0)) return &vcpu->sysregs.ttbr0_el1; 177 if (key == KEY(3, 0, 2, 0, 1)) return &vcpu->sysregs.ttbr1_el1; 178 if (key == KEY(3, 0, 2, 0, 2)) return &vcpu->sysregs.tcr_el1; 179 if (key == KEY(3, 0, 10, 2, 0)) return &vcpu->sysregs.mair_el1; 180 if (key == KEY(3, 0, 12, 0, 0)) return &vcpu->sysregs.vbar_el1; 181 if (key == KEY(3, 0, 13, 0, 1)) return &vcpu->sysregs.contextidr_el1; 182 if (key == KEY(3, 0, 13, 0, 4)) return &vcpu->sysregs.tpidr_el1; 183 if (key == KEY(3, 3, 13, 0, 3)) return &vcpu->sysregs.tpidrro_el0; 184 if (key == KEY(3, 0, 14, 1, 0)) return &vcpu->sysregs.cntkctl_el1; 185 if (key == KEY(3, 3, 14, 3, 0)) return &vcpu->sysregs.cntv_tval_el0; 186 if (key == KEY(3, 3, 14, 3, 1)) return &vcpu->sysregs.cntv_ctl_el0; 187 if (key == KEY(3, 3, 14, 3, 2)) return &vcpu->sysregs.cntv_cval_el0; 188 if (key == KEY(3, 2, 0, 0, 0)) return &vcpu->sysregs.csselr_el1; 189 if (key == KEY(2, 0, 0, 2, 2)) return &vcpu->sysregs.mdscr_el1; 190 return (uint64_t *)0; 191} 192 193static bool is_el1_mmu_key(uint64_t key) 194{ 195 return key == KEY(3, 0, 1, 0, 0) || 196 key == KEY(3, 0, 1, 0, 2) || 197 key == KEY(3, 0, 2, 0, 0) || 198 key == KEY(3, 0, 2, 0, 1) || 199 key == KEY(3, 0, 2, 0, 2) || 200 key == KEY(3, 0, 10, 2, 0) || 201 key == KEY(3, 0, 12, 0, 0) || 202 key == KEY(3, 0, 14, 1, 0); 203} 204 205static const struct policy_entry *effective_entry(const struct policy_entry *entry) 206{ 207 if (entry == (const struct policy_entry *)0) { 208 return (const struct policy_entry *)0; 209 } 210 211 g_effective_entry = *entry; 212 if (g_profile == POLICY_PROFILE_DIAGNOSTIC) { 213 if (g_effective_entry.access_class == POLICY_CLASS_PMU || 214 g_effective_entry.access_class == POLICY_CLASS_MISC) { 215 g_effective_entry.read_policy = POLICY_DENY_SKIP; 216 g_effective_entry.write_policy = POLICY_DENY_SKIP; 217 } 218 } else if (g_profile == POLICY_PROFILE_LINUX_BOOT) { 219 if (g_effective_entry.access_class == POLICY_CLASS_PMU || 220 g_effective_entry.key == KEY(3, 0, 12, 0, 1)) { 221 g_effective_entry.read_policy = POLICY_DENY_SKIP; 222 g_effective_entry.write_policy = POLICY_DENY_SKIP; 223 } 224 if (g_effective_entry.access_class == POLICY_CLASS_MMU) { 225 g_effective_entry.writable_mask = UINT64_MAX; 226 } 227 if (g_effective_entry.key == KEY(3, 3, 9, 12, 0)) { 228 g_effective_entry.reset_value = 0u; 229 } 230 } else if (g_profile == POLICY_PROFILE_DEBUG) { 231 if (g_effective_entry.access_class == POLICY_CLASS_DEBUG || 232 g_effective_entry.access_class == POLICY_CLASS_PMU) { 233 g_effective_entry.read_policy = POLICY_EMULATE_READ; 234 g_effective_entry.write_policy = POLICY_WRITE_MASK; 235 g_effective_entry.writable_mask = 0u; 236 } 237 } 238 return &g_effective_entry; 239} 240 241bool policy_apply_sysreg(struct vcpu *vcpu, const struct sysreg_access *access, 242 uint64_t operand, struct policy_decision *decision) 243{ 244 if (vcpu == (struct vcpu *)0 || access == (const struct sysreg_access *)0 || 245 decision == (struct policy_decision *)0) { 246 return false; 247 } 248 hv_memset(decision, 0, sizeof(*decision)); 249 const struct policy_entry *entry = effective_entry(sysreg_policy_lookup(access->key)); 250 decision->entry = entry; 251 decision->action = POLICY_DENY_SKIP; 252 decision->reason = 250u; 253 254 if (entry == (const struct policy_entry *)0) { 255 return true; 256 } 257 258 decision->action = access->is_read ? entry->read_policy : entry->write_policy; 259 decision->reason = entry->violation_reason; 260 261 uint64_t *slot = shadow_slot(vcpu, access->key); 262 switch (decision->action) { 263 case POLICY_EMULATE_READ: 264 decision->value = slot != (uint64_t *)0 ? *slot : entry->reset_value; 265 return true; 266 case POLICY_EMULATE_WRITE: 267 if (slot == (uint64_t *)0) { 268 decision->action = POLICY_DENY_UNDEF; 269 return true; 270 } 271 *slot = policy_mask_value(entry, *slot, operand); 272 decision->value = *slot; 273 if (g_profile == POLICY_PROFILE_LINUX_BOOT && is_el1_mmu_key(access->key)) { 274 arch_sync_el1_sysregs(vcpu); 275 } 276 timer_vcpu_sync(vcpu); 277 return true; 278 case POLICY_WRITE_MASK: 279 if (slot == (uint64_t *)0) { 280 decision->action = POLICY_DENY_UNDEF; 281 return true; 282 } 283 *slot = policy_mask_value(entry, *slot, operand); 284 decision->value = *slot; 285 if (g_profile == POLICY_PROFILE_LINUX_BOOT && is_el1_mmu_key(access->key)) { 286 arch_sync_el1_sysregs(vcpu); 287 } 288 timer_vcpu_sync(vcpu); 289 return true; 290 case POLICY_DENY_SKIP: 291 case POLICY_DENY_UNDEF: 292 case POLICY_PAUSE: 293 case POLICY_PANIC: 294 case POLICY_ALLOW_NATIVE: 295 default: 296 return true; 297 } 298} 299 300void policy_dump(void) 301{ 302 uart_puts("policy profile="); 303 uart_puts(policy_profile_name(g_profile)); 304 uart_puts("\n"); 305 for (uint32_t i = 0; i < HV_ARRAY_SIZE(g_policy); i++) { 306 const struct policy_entry *entry = effective_entry(&g_policy[i]); 307 uart_puts("policy key="); 308 uart_put_hex64(entry->key); 309 uart_puts(" name="); 310 uart_puts(entry->name); 311 uart_puts(" read="); 312 uart_put_dec64((uint64_t)entry->read_policy); 313 uart_puts(" write="); 314 uart_put_dec64((uint64_t)entry->write_policy); 315 uart_puts(" mask="); 316 uart_put_hex64(entry->writable_mask); 317 uart_puts("\n"); 318 } 319} 320 321bool policy_selftest(void) 322{ 323 enum policy_profile saved = g_profile; 324 bool profile_ok; 325 for (uint32_t i = 0; i < HV_ARRAY_SIZE(g_policy); i++) { 326 if (!policy_lattice_valid(&g_policy[i])) { 327 return false; 328 } 329 if (sysreg_policy_lookup(g_policy[i].key) != &g_policy[i]) { 330 return false; 331 } 332 for (uint32_t j = i + 1u; j < HV_ARRAY_SIZE(g_policy); j++) { 333 if (g_policy[i].key == g_policy[j].key) { 334 return false; 335 } 336 } 337 } 338 profile_ok = policy_set_profile_name("linux-boot") && 339 policy_get_profile() == POLICY_PROFILE_LINUX_BOOT && 340 policy_set_profile(saved); 341 return sysreg_policy_lookup(KEY(3, 4, 1, 1, 0)) != (const struct policy_entry *)0 && 342 sysreg_policy_lookup(KEY(3, 7, 15, 15, 7)) == (const struct policy_entry *)0 && 343 profile_ok; 344}