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
12 kB 286 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 57void policy_init(void) 58{ 59 g_profile = POLICY_PROFILE_STRICT; 60} 61 62const char *policy_profile_name(enum policy_profile profile) 63{ 64 switch (profile) { 65 case POLICY_PROFILE_STRICT: return "strict"; 66 case POLICY_PROFILE_DIAGNOSTIC: return "diagnostic"; 67 case POLICY_PROFILE_LINUX_BOOT: return "linux-boot"; 68 case POLICY_PROFILE_DEBUG: return "debug"; 69 default: return "unknown"; 70 } 71} 72 73bool policy_set_profile(enum policy_profile profile) 74{ 75 if (profile > POLICY_PROFILE_DEBUG) { 76 return false; 77 } 78 g_profile = profile; 79 return true; 80} 81 82bool policy_set_profile_name(const char *name) 83{ 84 if (name == (const char *)0) { 85 return false; 86 } 87 if (hv_strcmp(name, "strict") == 0) { 88 return policy_set_profile(POLICY_PROFILE_STRICT); 89 } 90 if (hv_strcmp(name, "diagnostic") == 0) { 91 return policy_set_profile(POLICY_PROFILE_DIAGNOSTIC); 92 } 93 if (hv_strcmp(name, "linux-boot") == 0) { 94 return policy_set_profile(POLICY_PROFILE_LINUX_BOOT); 95 } 96 if (hv_strcmp(name, "debug") == 0) { 97 return policy_set_profile(POLICY_PROFILE_DEBUG); 98 } 99 return false; 100} 101 102enum policy_profile policy_get_profile(void) 103{ 104 return g_profile; 105} 106 107const struct policy_entry *sysreg_policy_lookup(uint64_t key) 108{ 109 for (uint32_t i = 0; i < HV_ARRAY_SIZE(g_policy); i++) { 110 if (g_policy[i].key == key) { 111 return &g_policy[i]; 112 } 113 } 114 return (const struct policy_entry *)0; 115} 116 117static uint64_t *shadow_slot(struct vcpu *vcpu, uint64_t key) 118{ 119 if (key == KEY(3, 0, 1, 0, 0)) return &vcpu->sysregs.sctlr_el1; 120 if (key == KEY(3, 0, 1, 0, 2)) return &vcpu->sysregs.cpacr_el1; 121 if (key == KEY(3, 0, 2, 0, 0)) return &vcpu->sysregs.ttbr0_el1; 122 if (key == KEY(3, 0, 2, 0, 1)) return &vcpu->sysregs.ttbr1_el1; 123 if (key == KEY(3, 0, 2, 0, 2)) return &vcpu->sysregs.tcr_el1; 124 if (key == KEY(3, 0, 10, 2, 0)) return &vcpu->sysregs.mair_el1; 125 if (key == KEY(3, 0, 12, 0, 0)) return &vcpu->sysregs.vbar_el1; 126 if (key == KEY(3, 0, 13, 0, 1)) return &vcpu->sysregs.contextidr_el1; 127 if (key == KEY(3, 0, 13, 0, 4)) return &vcpu->sysregs.tpidr_el1; 128 if (key == KEY(3, 3, 13, 0, 3)) return &vcpu->sysregs.tpidrro_el0; 129 if (key == KEY(3, 0, 14, 1, 0)) return &vcpu->sysregs.cntkctl_el1; 130 if (key == KEY(3, 3, 14, 3, 0)) return &vcpu->sysregs.cntv_tval_el0; 131 if (key == KEY(3, 3, 14, 3, 1)) return &vcpu->sysregs.cntv_ctl_el0; 132 if (key == KEY(3, 3, 14, 3, 2)) return &vcpu->sysregs.cntv_cval_el0; 133 if (key == KEY(3, 2, 0, 0, 0)) return &vcpu->sysregs.csselr_el1; 134 if (key == KEY(2, 0, 0, 2, 2)) return &vcpu->sysregs.mdscr_el1; 135 return (uint64_t *)0; 136} 137 138static bool is_el1_mmu_key(uint64_t key) 139{ 140 return key == KEY(3, 0, 1, 0, 0) || 141 key == KEY(3, 0, 1, 0, 2) || 142 key == KEY(3, 0, 2, 0, 0) || 143 key == KEY(3, 0, 2, 0, 1) || 144 key == KEY(3, 0, 2, 0, 2) || 145 key == KEY(3, 0, 10, 2, 0) || 146 key == KEY(3, 0, 12, 0, 0) || 147 key == KEY(3, 0, 14, 1, 0); 148} 149 150static const struct policy_entry *effective_entry(const struct policy_entry *entry) 151{ 152 if (entry == (const struct policy_entry *)0) { 153 return (const struct policy_entry *)0; 154 } 155 156 g_effective_entry = *entry; 157 if (g_profile == POLICY_PROFILE_DIAGNOSTIC) { 158 if (g_effective_entry.access_class == POLICY_CLASS_PMU || 159 g_effective_entry.access_class == POLICY_CLASS_MISC) { 160 g_effective_entry.read_policy = POLICY_DENY_SKIP; 161 g_effective_entry.write_policy = POLICY_DENY_SKIP; 162 } 163 } else if (g_profile == POLICY_PROFILE_LINUX_BOOT) { 164 if (g_effective_entry.access_class == POLICY_CLASS_PMU || 165 g_effective_entry.key == KEY(3, 0, 12, 0, 1)) { 166 g_effective_entry.read_policy = POLICY_DENY_SKIP; 167 g_effective_entry.write_policy = POLICY_DENY_SKIP; 168 } 169 if (g_effective_entry.access_class == POLICY_CLASS_MMU) { 170 g_effective_entry.writable_mask = UINT64_MAX; 171 } 172 if (g_effective_entry.key == KEY(3, 3, 9, 12, 0)) { 173 g_effective_entry.reset_value = 0u; 174 } 175 } else if (g_profile == POLICY_PROFILE_DEBUG) { 176 if (g_effective_entry.access_class == POLICY_CLASS_DEBUG || 177 g_effective_entry.access_class == POLICY_CLASS_PMU) { 178 g_effective_entry.read_policy = POLICY_EMULATE_READ; 179 g_effective_entry.write_policy = POLICY_WRITE_MASK; 180 g_effective_entry.writable_mask = 0u; 181 } 182 } 183 return &g_effective_entry; 184} 185 186bool policy_apply_sysreg(struct vcpu *vcpu, const struct sysreg_access *access, 187 uint64_t operand, struct policy_decision *decision) 188{ 189 if (vcpu == (struct vcpu *)0 || access == (const struct sysreg_access *)0 || 190 decision == (struct policy_decision *)0) { 191 return false; 192 } 193 hv_memset(decision, 0, sizeof(*decision)); 194 const struct policy_entry *entry = effective_entry(sysreg_policy_lookup(access->key)); 195 decision->entry = entry; 196 decision->action = POLICY_DENY_SKIP; 197 decision->reason = 250u; 198 199 if (entry == (const struct policy_entry *)0) { 200 return true; 201 } 202 203 decision->action = access->is_read ? entry->read_policy : entry->write_policy; 204 decision->reason = entry->violation_reason; 205 206 uint64_t *slot = shadow_slot(vcpu, access->key); 207 switch (decision->action) { 208 case POLICY_EMULATE_READ: 209 decision->value = slot != (uint64_t *)0 ? *slot : entry->reset_value; 210 return true; 211 case POLICY_EMULATE_WRITE: 212 if (slot == (uint64_t *)0) { 213 decision->action = POLICY_DENY_UNDEF; 214 return true; 215 } 216 *slot = operand & entry->writable_mask; 217 decision->value = *slot; 218 if (g_profile == POLICY_PROFILE_LINUX_BOOT && is_el1_mmu_key(access->key)) { 219 arch_sync_el1_sysregs(vcpu); 220 } 221 timer_vcpu_sync(vcpu); 222 return true; 223 case POLICY_WRITE_MASK: 224 if (slot == (uint64_t *)0) { 225 decision->action = POLICY_DENY_UNDEF; 226 return true; 227 } 228 *slot = (*slot & ~entry->writable_mask) | (operand & entry->writable_mask); 229 decision->value = *slot; 230 if (g_profile == POLICY_PROFILE_LINUX_BOOT && is_el1_mmu_key(access->key)) { 231 arch_sync_el1_sysregs(vcpu); 232 } 233 timer_vcpu_sync(vcpu); 234 return true; 235 case POLICY_DENY_SKIP: 236 case POLICY_DENY_UNDEF: 237 case POLICY_PAUSE: 238 case POLICY_PANIC: 239 case POLICY_ALLOW_NATIVE: 240 default: 241 return true; 242 } 243} 244 245void policy_dump(void) 246{ 247 uart_puts("policy profile="); 248 uart_puts(policy_profile_name(g_profile)); 249 uart_puts("\n"); 250 for (uint32_t i = 0; i < HV_ARRAY_SIZE(g_policy); i++) { 251 const struct policy_entry *entry = effective_entry(&g_policy[i]); 252 uart_puts("policy key="); 253 uart_put_hex64(entry->key); 254 uart_puts(" name="); 255 uart_puts(entry->name); 256 uart_puts(" read="); 257 uart_put_dec64((uint64_t)entry->read_policy); 258 uart_puts(" write="); 259 uart_put_dec64((uint64_t)entry->write_policy); 260 uart_puts(" mask="); 261 uart_put_hex64(entry->writable_mask); 262 uart_puts("\n"); 263 } 264} 265 266bool policy_selftest(void) 267{ 268 enum policy_profile saved = g_profile; 269 bool profile_ok; 270 for (uint32_t i = 0; i < HV_ARRAY_SIZE(g_policy); i++) { 271 if (sysreg_policy_lookup(g_policy[i].key) != &g_policy[i]) { 272 return false; 273 } 274 for (uint32_t j = i + 1u; j < HV_ARRAY_SIZE(g_policy); j++) { 275 if (g_policy[i].key == g_policy[j].key) { 276 return false; 277 } 278 } 279 } 280 profile_ok = policy_set_profile_name("linux-boot") && 281 policy_get_profile() == POLICY_PROFILE_LINUX_BOOT && 282 policy_set_profile(saved); 283 return sysreg_policy_lookup(KEY(3, 4, 1, 1, 0)) != (const struct policy_entry *)0 && 284 sysreg_policy_lookup(KEY(3, 7, 15, 15, 7)) == (const struct policy_entry *)0 && 285 profile_ok; 286}