David Brazdil | 09cf57e | 2020-06-25 14:14:14 +0100 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0-only |
| 2 | /* |
| 3 | * Copyright (C) 2015 - ARM Ltd |
| 4 | * Author: Marc Zyngier <marc.zyngier@arm.com> |
| 5 | */ |
| 6 | |
| 7 | #include <hyp/switch.h> |
David Brazdil | 13aeb9b | 2020-06-25 14:14:16 +0100 | [diff] [blame] | 8 | #include <hyp/sysreg-sr.h> |
David Brazdil | 09cf57e | 2020-06-25 14:14:14 +0100 | [diff] [blame] | 9 | |
| 10 | #include <linux/arm-smccc.h> |
| 11 | #include <linux/kvm_host.h> |
| 12 | #include <linux/types.h> |
| 13 | #include <linux/jump_label.h> |
| 14 | #include <uapi/linux/psci.h> |
| 15 | |
| 16 | #include <kvm/arm_psci.h> |
| 17 | |
| 18 | #include <asm/barrier.h> |
| 19 | #include <asm/cpufeature.h> |
| 20 | #include <asm/kprobes.h> |
| 21 | #include <asm/kvm_asm.h> |
| 22 | #include <asm/kvm_emulate.h> |
| 23 | #include <asm/kvm_hyp.h> |
| 24 | #include <asm/kvm_mmu.h> |
| 25 | #include <asm/fpsimd.h> |
| 26 | #include <asm/debug-monitors.h> |
| 27 | #include <asm/processor.h> |
| 28 | #include <asm/thread_info.h> |
| 29 | |
David Brazdil | c50cb04 | 2020-06-25 14:14:19 +0100 | [diff] [blame] | 30 | static void __activate_traps(struct kvm_vcpu *vcpu) |
David Brazdil | 09cf57e | 2020-06-25 14:14:14 +0100 | [diff] [blame] | 31 | { |
| 32 | u64 val; |
| 33 | |
| 34 | ___activate_traps(vcpu); |
| 35 | __activate_traps_common(vcpu); |
| 36 | |
| 37 | val = CPTR_EL2_DEFAULT; |
| 38 | val |= CPTR_EL2_TTA | CPTR_EL2_TZ | CPTR_EL2_TAM; |
| 39 | if (!update_fp_enabled(vcpu)) { |
| 40 | val |= CPTR_EL2_TFP; |
| 41 | __activate_traps_fpsimd32(vcpu); |
| 42 | } |
| 43 | |
| 44 | write_sysreg(val, cptr_el2); |
Andrew Scull | 6e3bfbb | 2020-09-15 11:46:30 +0100 | [diff] [blame^] | 45 | write_sysreg(__hyp_this_cpu_read(kvm_hyp_vector), vbar_el2); |
David Brazdil | 09cf57e | 2020-06-25 14:14:14 +0100 | [diff] [blame] | 46 | |
| 47 | if (cpus_have_final_cap(ARM64_WORKAROUND_SPECULATIVE_AT)) { |
| 48 | struct kvm_cpu_context *ctxt = &vcpu->arch.ctxt; |
| 49 | |
| 50 | isb(); |
| 51 | /* |
| 52 | * At this stage, and thanks to the above isb(), S2 is |
| 53 | * configured and enabled. We can now restore the guest's S1 |
| 54 | * configuration: SCTLR, and only then TCR. |
| 55 | */ |
Marc Zyngier | 71071ac | 2020-04-12 14:00:43 +0100 | [diff] [blame] | 56 | write_sysreg_el1(ctxt_sys_reg(ctxt, SCTLR_EL1), SYS_SCTLR); |
David Brazdil | 09cf57e | 2020-06-25 14:14:14 +0100 | [diff] [blame] | 57 | isb(); |
Marc Zyngier | 71071ac | 2020-04-12 14:00:43 +0100 | [diff] [blame] | 58 | write_sysreg_el1(ctxt_sys_reg(ctxt, TCR_EL1), SYS_TCR); |
David Brazdil | 09cf57e | 2020-06-25 14:14:14 +0100 | [diff] [blame] | 59 | } |
| 60 | } |
| 61 | |
David Brazdil | c50cb04 | 2020-06-25 14:14:19 +0100 | [diff] [blame] | 62 | static void __deactivate_traps(struct kvm_vcpu *vcpu) |
David Brazdil | 09cf57e | 2020-06-25 14:14:14 +0100 | [diff] [blame] | 63 | { |
Andrew Scull | 6e3bfbb | 2020-09-15 11:46:30 +0100 | [diff] [blame^] | 64 | extern char __kvm_hyp_host_vector[]; |
David Brazdil | 09cf57e | 2020-06-25 14:14:14 +0100 | [diff] [blame] | 65 | u64 mdcr_el2; |
| 66 | |
| 67 | ___deactivate_traps(vcpu); |
| 68 | |
| 69 | mdcr_el2 = read_sysreg(mdcr_el2); |
| 70 | |
| 71 | if (cpus_have_final_cap(ARM64_WORKAROUND_SPECULATIVE_AT)) { |
| 72 | u64 val; |
| 73 | |
| 74 | /* |
| 75 | * Set the TCR and SCTLR registers in the exact opposite |
| 76 | * sequence as __activate_traps (first prevent walks, |
| 77 | * then force the MMU on). A generous sprinkling of isb() |
| 78 | * ensure that things happen in this exact order. |
| 79 | */ |
| 80 | val = read_sysreg_el1(SYS_TCR); |
| 81 | write_sysreg_el1(val | TCR_EPD1_MASK | TCR_EPD0_MASK, SYS_TCR); |
| 82 | isb(); |
| 83 | val = read_sysreg_el1(SYS_SCTLR); |
| 84 | write_sysreg_el1(val | SCTLR_ELx_M, SYS_SCTLR); |
| 85 | isb(); |
| 86 | } |
| 87 | |
| 88 | __deactivate_traps_common(); |
| 89 | |
| 90 | mdcr_el2 &= MDCR_EL2_HPMN_MASK; |
| 91 | mdcr_el2 |= MDCR_EL2_E2PB_MASK << MDCR_EL2_E2PB_SHIFT; |
| 92 | |
| 93 | write_sysreg(mdcr_el2, mdcr_el2); |
| 94 | write_sysreg(HCR_HOST_NVHE_FLAGS, hcr_el2); |
| 95 | write_sysreg(CPTR_EL2_DEFAULT, cptr_el2); |
Andrew Scull | 6e3bfbb | 2020-09-15 11:46:30 +0100 | [diff] [blame^] | 96 | write_sysreg(__kvm_hyp_host_vector, vbar_el2); |
David Brazdil | 09cf57e | 2020-06-25 14:14:14 +0100 | [diff] [blame] | 97 | } |
| 98 | |
Andrew Scull | 501a67a | 2020-09-15 11:46:25 +0100 | [diff] [blame] | 99 | static void __load_host_stage2(void) |
David Brazdil | 09cf57e | 2020-06-25 14:14:14 +0100 | [diff] [blame] | 100 | { |
| 101 | write_sysreg(0, vttbr_el2); |
| 102 | } |
| 103 | |
| 104 | /* Save VGICv3 state on non-VHE systems */ |
David Brazdil | c50cb04 | 2020-06-25 14:14:19 +0100 | [diff] [blame] | 105 | static void __hyp_vgic_save_state(struct kvm_vcpu *vcpu) |
David Brazdil | 09cf57e | 2020-06-25 14:14:14 +0100 | [diff] [blame] | 106 | { |
| 107 | if (static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif)) { |
| 108 | __vgic_v3_save_state(&vcpu->arch.vgic_cpu.vgic_v3); |
| 109 | __vgic_v3_deactivate_traps(&vcpu->arch.vgic_cpu.vgic_v3); |
| 110 | } |
| 111 | } |
| 112 | |
| 113 | /* Restore VGICv3 state on non_VEH systems */ |
David Brazdil | c50cb04 | 2020-06-25 14:14:19 +0100 | [diff] [blame] | 114 | static void __hyp_vgic_restore_state(struct kvm_vcpu *vcpu) |
David Brazdil | 09cf57e | 2020-06-25 14:14:14 +0100 | [diff] [blame] | 115 | { |
| 116 | if (static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif)) { |
| 117 | __vgic_v3_activate_traps(&vcpu->arch.vgic_cpu.vgic_v3); |
| 118 | __vgic_v3_restore_state(&vcpu->arch.vgic_cpu.vgic_v3); |
| 119 | } |
| 120 | } |
| 121 | |
| 122 | /** |
| 123 | * Disable host events, enable guest events |
| 124 | */ |
David Brazdil | c50cb04 | 2020-06-25 14:14:19 +0100 | [diff] [blame] | 125 | static bool __pmu_switch_to_guest(struct kvm_cpu_context *host_ctxt) |
David Brazdil | 09cf57e | 2020-06-25 14:14:14 +0100 | [diff] [blame] | 126 | { |
| 127 | struct kvm_host_data *host; |
| 128 | struct kvm_pmu_events *pmu; |
| 129 | |
| 130 | host = container_of(host_ctxt, struct kvm_host_data, host_ctxt); |
| 131 | pmu = &host->pmu_events; |
| 132 | |
| 133 | if (pmu->events_host) |
| 134 | write_sysreg(pmu->events_host, pmcntenclr_el0); |
| 135 | |
| 136 | if (pmu->events_guest) |
| 137 | write_sysreg(pmu->events_guest, pmcntenset_el0); |
| 138 | |
| 139 | return (pmu->events_host || pmu->events_guest); |
| 140 | } |
| 141 | |
| 142 | /** |
| 143 | * Disable guest events, enable host events |
| 144 | */ |
David Brazdil | c50cb04 | 2020-06-25 14:14:19 +0100 | [diff] [blame] | 145 | static void __pmu_switch_to_host(struct kvm_cpu_context *host_ctxt) |
David Brazdil | 09cf57e | 2020-06-25 14:14:14 +0100 | [diff] [blame] | 146 | { |
| 147 | struct kvm_host_data *host; |
| 148 | struct kvm_pmu_events *pmu; |
| 149 | |
| 150 | host = container_of(host_ctxt, struct kvm_host_data, host_ctxt); |
| 151 | pmu = &host->pmu_events; |
| 152 | |
| 153 | if (pmu->events_guest) |
| 154 | write_sysreg(pmu->events_guest, pmcntenclr_el0); |
| 155 | |
| 156 | if (pmu->events_host) |
| 157 | write_sysreg(pmu->events_host, pmcntenset_el0); |
| 158 | } |
| 159 | |
| 160 | /* Switch to the guest for legacy non-VHE systems */ |
David Brazdil | c50cb04 | 2020-06-25 14:14:19 +0100 | [diff] [blame] | 161 | int __kvm_vcpu_run(struct kvm_vcpu *vcpu) |
David Brazdil | 09cf57e | 2020-06-25 14:14:14 +0100 | [diff] [blame] | 162 | { |
| 163 | struct kvm_cpu_context *host_ctxt; |
| 164 | struct kvm_cpu_context *guest_ctxt; |
| 165 | bool pmu_switch_needed; |
| 166 | u64 exit_code; |
| 167 | |
| 168 | /* |
| 169 | * Having IRQs masked via PMR when entering the guest means the GIC |
| 170 | * will not signal the CPU of interrupts of lower priority, and the |
| 171 | * only way to get out will be via guest exceptions. |
| 172 | * Naturally, we want to avoid this. |
| 173 | */ |
| 174 | if (system_uses_irq_prio_masking()) { |
| 175 | gic_write_pmr(GIC_PRIO_IRQON | GIC_PRIO_PSR_I_SET); |
| 176 | pmr_sync(); |
| 177 | } |
| 178 | |
| 179 | vcpu = kern_hyp_va(vcpu); |
| 180 | |
| 181 | host_ctxt = &__hyp_this_cpu_ptr(kvm_host_data)->host_ctxt; |
| 182 | host_ctxt->__hyp_running_vcpu = vcpu; |
| 183 | guest_ctxt = &vcpu->arch.ctxt; |
| 184 | |
| 185 | pmu_switch_needed = __pmu_switch_to_guest(host_ctxt); |
| 186 | |
| 187 | __sysreg_save_state_nvhe(host_ctxt); |
| 188 | |
| 189 | /* |
| 190 | * We must restore the 32-bit state before the sysregs, thanks |
| 191 | * to erratum #852523 (Cortex-A57) or #853709 (Cortex-A72). |
| 192 | * |
| 193 | * Also, and in order to be able to deal with erratum #1319537 (A57) |
| 194 | * and #1319367 (A72), we must ensure that all VM-related sysreg are |
| 195 | * restored before we enable S2 translation. |
| 196 | */ |
| 197 | __sysreg32_restore_state(vcpu); |
| 198 | __sysreg_restore_state_nvhe(guest_ctxt); |
| 199 | |
Andrew Scull | 501a67a | 2020-09-15 11:46:25 +0100 | [diff] [blame] | 200 | __load_guest_stage2(kern_hyp_va(vcpu->arch.hw_mmu)); |
David Brazdil | 09cf57e | 2020-06-25 14:14:14 +0100 | [diff] [blame] | 201 | __activate_traps(vcpu); |
| 202 | |
| 203 | __hyp_vgic_restore_state(vcpu); |
| 204 | __timer_enable_traps(vcpu); |
| 205 | |
| 206 | __debug_switch_to_guest(vcpu); |
| 207 | |
| 208 | __set_guest_arch_workaround_state(vcpu); |
| 209 | |
| 210 | do { |
| 211 | /* Jump in the fire! */ |
| 212 | exit_code = __guest_enter(vcpu, host_ctxt); |
| 213 | |
| 214 | /* And we're baaack! */ |
| 215 | } while (fixup_guest_exit(vcpu, &exit_code)); |
| 216 | |
| 217 | __set_host_arch_workaround_state(vcpu); |
| 218 | |
| 219 | __sysreg_save_state_nvhe(guest_ctxt); |
| 220 | __sysreg32_save_state(vcpu); |
| 221 | __timer_disable_traps(vcpu); |
| 222 | __hyp_vgic_save_state(vcpu); |
| 223 | |
| 224 | __deactivate_traps(vcpu); |
Andrew Scull | 501a67a | 2020-09-15 11:46:25 +0100 | [diff] [blame] | 225 | __load_host_stage2(); |
David Brazdil | 09cf57e | 2020-06-25 14:14:14 +0100 | [diff] [blame] | 226 | |
| 227 | __sysreg_restore_state_nvhe(host_ctxt); |
| 228 | |
| 229 | if (vcpu->arch.flags & KVM_ARM64_FP_ENABLED) |
| 230 | __fpsimd_save_fpexc32(vcpu); |
| 231 | |
| 232 | /* |
| 233 | * This must come after restoring the host sysregs, since a non-VHE |
| 234 | * system may enable SPE here and make use of the TTBRs. |
| 235 | */ |
| 236 | __debug_switch_to_host(vcpu); |
| 237 | |
| 238 | if (pmu_switch_needed) |
| 239 | __pmu_switch_to_host(host_ctxt); |
| 240 | |
| 241 | /* Returning to host will clear PSR.I, remask PMR if needed */ |
| 242 | if (system_uses_irq_prio_masking()) |
| 243 | gic_write_pmr(GIC_PRIO_IRQOFF); |
| 244 | |
| 245 | return exit_code; |
| 246 | } |
| 247 | |
Andrew Scull | 6a0259e | 2020-09-15 11:46:26 +0100 | [diff] [blame] | 248 | void __noreturn hyp_panic(void) |
David Brazdil | 09cf57e | 2020-06-25 14:14:14 +0100 | [diff] [blame] | 249 | { |
| 250 | u64 spsr = read_sysreg_el2(SYS_SPSR); |
| 251 | u64 elr = read_sysreg_el2(SYS_ELR); |
| 252 | u64 par = read_sysreg(par_el1); |
Andrew Scull | 6a0259e | 2020-09-15 11:46:26 +0100 | [diff] [blame] | 253 | struct kvm_cpu_context *host_ctxt; |
| 254 | struct kvm_vcpu *vcpu; |
David Brazdil | 09cf57e | 2020-06-25 14:14:14 +0100 | [diff] [blame] | 255 | unsigned long str_va; |
| 256 | |
Andrew Scull | 6a0259e | 2020-09-15 11:46:26 +0100 | [diff] [blame] | 257 | host_ctxt = &__hyp_this_cpu_ptr(kvm_host_data)->host_ctxt; |
| 258 | vcpu = host_ctxt->__hyp_running_vcpu; |
| 259 | |
David Brazdil | 09cf57e | 2020-06-25 14:14:14 +0100 | [diff] [blame] | 260 | if (read_sysreg(vttbr_el2)) { |
| 261 | __timer_disable_traps(vcpu); |
| 262 | __deactivate_traps(vcpu); |
Andrew Scull | 501a67a | 2020-09-15 11:46:25 +0100 | [diff] [blame] | 263 | __load_host_stage2(); |
David Brazdil | 09cf57e | 2020-06-25 14:14:14 +0100 | [diff] [blame] | 264 | __sysreg_restore_state_nvhe(host_ctxt); |
| 265 | } |
| 266 | |
| 267 | /* |
| 268 | * Force the panic string to be loaded from the literal pool, |
| 269 | * making sure it is a kernel address and not a PC-relative |
| 270 | * reference. |
| 271 | */ |
| 272 | asm volatile("ldr %0, =%1" : "=r" (str_va) : "S" (__hyp_panic_string)); |
| 273 | |
| 274 | __hyp_do_panic(str_va, |
| 275 | spsr, elr, |
| 276 | read_sysreg(esr_el2), read_sysreg_el2(SYS_FAR), |
| 277 | read_sysreg(hpfar_el2), par, vcpu); |
| 278 | unreachable(); |
| 279 | } |
James Morse | e9ee186 | 2020-08-21 15:07:05 +0100 | [diff] [blame] | 280 | |
| 281 | asmlinkage void kvm_unexpected_el2_exception(void) |
| 282 | { |
| 283 | return __kvm_unexpected_el2_exception(); |
| 284 | } |