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 335 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 103void policy_init(void) 104{ 105 g_profile = POLICY_PROFILE_STRICT; 106} 107 108const char *policy_profile_name(enum policy_profile profile) 109{ 110 switch (profile) { 111 case POLICY_PROFILE_STRICT: return "strict"; 112 case POLICY_PROFILE_DIAGNOSTIC: return "diagnostic"; 113 case POLICY_PROFILE_LINUX_BOOT: return "linux-boot"; 114 case POLICY_PROFILE_DEBUG: return "debug"; 115 default: return "unknown"; 116 } 117} 118 119bool policy_set_profile(enum policy_profile profile) 120{ 121 if (profile > POLICY_PROFILE_DEBUG) { 122 return false; 123 } 124 g_profile = profile; 125 return true; 126} 127 128bool policy_set_profile_name(const char *name) 129{ 130 if (name == (const char *)0) { 131 return false; 132 } 133 if (hv_strcmp(name, "strict") == 0) { 134 return policy_set_profile(POLICY_PROFILE_STRICT); 135 } 136 if (hv_strcmp(name, "diagnostic") == 0) { 137 return policy_set_profile(POLICY_PROFILE_DIAGNOSTIC); 138 } 139 if (hv_strcmp(name, "linux-boot") == 0) { 140 return policy_set_profile(POLICY_PROFILE_LINUX_BOOT); 141 } 142 if (hv_strcmp(name, "debug") == 0) { 143 return policy_set_profile(POLICY_PROFILE_DEBUG); 144 } 145 return false; 146} 147 148enum policy_profile policy_get_profile(void) 149{ 150 return g_profile; 151} 152 153const struct policy_entry *sysreg_policy_lookup(uint64_t key) 154{ 155 for (uint32_t i = 0; i < HV_ARRAY_SIZE(g_policy); i++) { 156 if (g_policy[i].key == key) { 157 return &g_policy[i]; 158 } 159 } 160 return (const struct policy_entry *)0; 161} 162 163static uint64_t *shadow_slot(struct vcpu *vcpu, uint64_t key) 164{ 165 if (key == KEY(3, 0, 1, 0, 0)) return &vcpu->sysregs.sctlr_el1; 166 if (key == KEY(3, 0, 1, 0, 2)) return &vcpu->sysregs.cpacr_el1; 167 if (key == KEY(3, 0, 2, 0, 0)) return &vcpu->sysregs.ttbr0_el1; 168 if (key == KEY(3, 0, 2, 0, 1)) return &vcpu->sysregs.ttbr1_el1; 169 if (key == KEY(3, 0, 2, 0, 2)) return &vcpu->sysregs.tcr_el1; 170 if (key == KEY(3, 0, 10, 2, 0)) return &vcpu->sysregs.mair_el1; 171 if (key == KEY(3, 0, 12, 0, 0)) return &vcpu->sysregs.vbar_el1; 172 if (key == KEY(3, 0, 13, 0, 1)) return &vcpu->sysregs.contextidr_el1; 173 if (key == KEY(3, 0, 13, 0, 4)) return &vcpu->sysregs.tpidr_el1; 174 if (key == KEY(3, 3, 13, 0, 3)) return &vcpu->sysregs.tpidrro_el0; 175 if (key == KEY(3, 0, 14, 1, 0)) return &vcpu->sysregs.cntkctl_el1; 176 if (key == KEY(3, 3, 14, 3, 0)) return &vcpu->sysregs.cntv_tval_el0; 177 if (key == KEY(3, 3, 14, 3, 1)) return &vcpu->sysregs.cntv_ctl_el0; 178 if (key == KEY(3, 3, 14, 3, 2)) return &vcpu->sysregs.cntv_cval_el0; 179 if (key == KEY(3, 2, 0, 0, 0)) return &vcpu->sysregs.csselr_el1; 180 if (key == KEY(2, 0, 0, 2, 2)) return &vcpu->sysregs.mdscr_el1; 181 return (uint64_t *)0; 182} 183 184static bool is_el1_mmu_key(uint64_t key) 185{ 186 return key == KEY(3, 0, 1, 0, 0) || 187 key == KEY(3, 0, 1, 0, 2) || 188 key == KEY(3, 0, 2, 0, 0) || 189 key == KEY(3, 0, 2, 0, 1) || 190 key == KEY(3, 0, 2, 0, 2) || 191 key == KEY(3, 0, 10, 2, 0) || 192 key == KEY(3, 0, 12, 0, 0) || 193 key == KEY(3, 0, 14, 1, 0); 194} 195 196static const struct policy_entry *effective_entry(const struct policy_entry *entry) 197{ 198 if (entry == (const struct policy_entry *)0) { 199 return (const struct policy_entry *)0; 200 } 201 202 g_effective_entry = *entry; 203 if (g_profile == POLICY_PROFILE_DIAGNOSTIC) { 204 if (g_effective_entry.access_class == POLICY_CLASS_PMU || 205 g_effective_entry.access_class == POLICY_CLASS_MISC) { 206 g_effective_entry.read_policy = POLICY_DENY_SKIP; 207 g_effective_entry.write_policy = POLICY_DENY_SKIP; 208 } 209 } else if (g_profile == POLICY_PROFILE_LINUX_BOOT) { 210 if (g_effective_entry.access_class == POLICY_CLASS_PMU || 211 g_effective_entry.key == KEY(3, 0, 12, 0, 1)) { 212 g_effective_entry.read_policy = POLICY_DENY_SKIP; 213 g_effective_entry.write_policy = POLICY_DENY_SKIP; 214 } 215 if (g_effective_entry.access_class == POLICY_CLASS_MMU) { 216 g_effective_entry.writable_mask = UINT64_MAX; 217 } 218 if (g_effective_entry.key == KEY(3, 3, 9, 12, 0)) { 219 g_effective_entry.reset_value = 0u; 220 } 221 } else if (g_profile == POLICY_PROFILE_DEBUG) { 222 if (g_effective_entry.access_class == POLICY_CLASS_DEBUG || 223 g_effective_entry.access_class == POLICY_CLASS_PMU) { 224 g_effective_entry.read_policy = POLICY_EMULATE_READ; 225 g_effective_entry.write_policy = POLICY_WRITE_MASK; 226 g_effective_entry.writable_mask = 0u; 227 } 228 } 229 return &g_effective_entry; 230} 231 232bool policy_apply_sysreg(struct vcpu *vcpu, const struct sysreg_access *access, 233 uint64_t operand, struct policy_decision *decision) 234{ 235 if (vcpu == (struct vcpu *)0 || access == (const struct sysreg_access *)0 || 236 decision == (struct policy_decision *)0) { 237 return false; 238 } 239 hv_memset(decision, 0, sizeof(*decision)); 240 const struct policy_entry *entry = effective_entry(sysreg_policy_lookup(access->key)); 241 decision->entry = entry; 242 decision->action = POLICY_DENY_SKIP; 243 decision->reason = 250u; 244 245 if (entry == (const struct policy_entry *)0) { 246 return true; 247 } 248 249 decision->action = access->is_read ? entry->read_policy : entry->write_policy; 250 decision->reason = entry->violation_reason; 251 252 uint64_t *slot = shadow_slot(vcpu, access->key); 253 switch (decision->action) { 254 case POLICY_EMULATE_READ: 255 decision->value = slot != (uint64_t *)0 ? *slot : entry->reset_value; 256 return true; 257 case POLICY_EMULATE_WRITE: 258 if (slot == (uint64_t *)0) { 259 decision->action = POLICY_DENY_UNDEF; 260 return true; 261 } 262 *slot = operand & entry->writable_mask; 263 decision->value = *slot; 264 if (g_profile == POLICY_PROFILE_LINUX_BOOT && is_el1_mmu_key(access->key)) { 265 arch_sync_el1_sysregs(vcpu); 266 } 267 timer_vcpu_sync(vcpu); 268 return true; 269 case POLICY_WRITE_MASK: 270 if (slot == (uint64_t *)0) { 271 decision->action = POLICY_DENY_UNDEF; 272 return true; 273 } 274 *slot = (*slot & ~entry->writable_mask) | (operand & entry->writable_mask); 275 decision->value = *slot; 276 if (g_profile == POLICY_PROFILE_LINUX_BOOT && is_el1_mmu_key(access->key)) { 277 arch_sync_el1_sysregs(vcpu); 278 } 279 timer_vcpu_sync(vcpu); 280 return true; 281 case POLICY_DENY_SKIP: 282 case POLICY_DENY_UNDEF: 283 case POLICY_PAUSE: 284 case POLICY_PANIC: 285 case POLICY_ALLOW_NATIVE: 286 default: 287 return true; 288 } 289} 290 291void policy_dump(void) 292{ 293 uart_puts("policy profile="); 294 uart_puts(policy_profile_name(g_profile)); 295 uart_puts("\n"); 296 for (uint32_t i = 0; i < HV_ARRAY_SIZE(g_policy); i++) { 297 const struct policy_entry *entry = effective_entry(&g_policy[i]); 298 uart_puts("policy key="); 299 uart_put_hex64(entry->key); 300 uart_puts(" name="); 301 uart_puts(entry->name); 302 uart_puts(" read="); 303 uart_put_dec64((uint64_t)entry->read_policy); 304 uart_puts(" write="); 305 uart_put_dec64((uint64_t)entry->write_policy); 306 uart_puts(" mask="); 307 uart_put_hex64(entry->writable_mask); 308 uart_puts("\n"); 309 } 310} 311 312bool policy_selftest(void) 313{ 314 enum policy_profile saved = g_profile; 315 bool profile_ok; 316 for (uint32_t i = 0; i < HV_ARRAY_SIZE(g_policy); i++) { 317 if (!policy_lattice_valid(&g_policy[i])) { 318 return false; 319 } 320 if (sysreg_policy_lookup(g_policy[i].key) != &g_policy[i]) { 321 return false; 322 } 323 for (uint32_t j = i + 1u; j < HV_ARRAY_SIZE(g_policy); j++) { 324 if (g_policy[i].key == g_policy[j].key) { 325 return false; 326 } 327 } 328 } 329 profile_ok = policy_set_profile_name("linux-boot") && 330 policy_get_profile() == POLICY_PROFILE_LINUX_BOOT && 331 policy_set_profile(saved); 332 return sysreg_policy_lookup(KEY(3, 4, 1, 1, 0)) != (const struct policy_entry *)0 && 333 sysreg_policy_lookup(KEY(3, 7, 15, 15, 7)) == (const struct policy_entry *)0 && 334 profile_ok; 335}