| 1 | |
| 2 | |
| 3 | * Debug and Guest Debug support
|
| 4 | *
|
| 5 | * Copyright (C) 2015 - Linaro Ltd
|
| 6 | * Authors: Alex Bennée <[email protected]>
|
| 7 | * Oliver Upton <[email protected]>
|
| 8 |
|
| 9 |
|
| 10 | #include <linux/kvm_host.h>
|
| 11 | #include <linux/hw_breakpoint.h>
|
| 12 |
|
| 13 | #include <asm/arm_pmuv3.h>
|
| 14 | #include <asm/debug-monitors.h>
|
| 15 | #include <asm/kvm_asm.h>
|
| 16 | #include <asm/kvm_arm.h>
|
| 17 | #include <asm/kvm_emulate.h>
|
| 18 |
|
| 19 | static int cpu_has_spe(u64 dfr0)
|
| 20 | {
|
| 21 | return cpuid_feature_extract_unsigned_field(dfr0, ID_AA64DFR0_EL1_PMSVer_SHIFT) &&
|
| 22 | !(read_sysreg_s(SYS_PMBIDR_EL1) & PMBIDR_EL1_P);
|
| 23 | }
|
| 24 |
|
| 25 | |
| 26 | * kvm_arm_setup_mdcr_el2 - configure vcpu mdcr_el2 value
|
| 27 | *
|
| 28 | * @vcpu: the vcpu pointer
|
| 29 | *
|
| 30 | * This ensures we will trap access to:
|
| 31 | * - Performance monitors (MDCR_EL2_TPM/MDCR_EL2_TPMCR)
|
| 32 | * - Debug ROM Address (MDCR_EL2_TDRA)
|
| 33 | * - OS related registers (MDCR_EL2_TDOSA)
|
| 34 | * - Statistical profiler (MDCR_EL2_TPMS/MDCR_EL2_E2PB)
|
| 35 | * - Self-hosted Trace Filter controls (MDCR_EL2_TTRF)
|
| 36 | * - Self-hosted Trace (MDCR_EL2_TTRF/MDCR_EL2_E2TB)
|
| 37 |
|
| 38 | static void kvm_arm_setup_mdcr_el2(struct kvm_vcpu *vcpu)
|
| 39 | {
|
| 40 | preempt_disable();
|
| 41 |
|
| 42 | |
| 43 | * This also clears MDCR_EL2_E2PB_MASK and MDCR_EL2_E2TB_MASK
|
| 44 | * to disable guest access to the profiling and trace buffers
|
| 45 |
|
| 46 | vcpu->arch.mdcr_el2 = FIELD_PREP(MDCR_EL2_HPMN,
|
| 47 | *host_data_ptr(nr_event_counters));
|
| 48 | vcpu->arch.mdcr_el2 |= (MDCR_EL2_TPM |
|
| 49 | MDCR_EL2_TPMS |
|
| 50 | MDCR_EL2_TTRF |
|
| 51 | MDCR_EL2_TPMCR |
|
| 52 | MDCR_EL2_TDRA |
|
| 53 | MDCR_EL2_TDOSA);
|
| 54 |
|
| 55 |
|
| 56 | if (vcpu->guest_debug)
|
| 57 |
|
| 58 | vcpu->arch.mdcr_el2 |= MDCR_EL2_TDE;
|
| 59 |
|
| 60 | |
| 61 | * Trap debug registers if the guest doesn't have ownership of them.
|
| 62 |
|
| 63 | if (!kvm_guest_owns_debug_regs(vcpu))
|
| 64 | vcpu->arch.mdcr_el2 |= MDCR_EL2_TDA;
|
| 65 |
|
| 66 | if (vcpu_has_nv(vcpu))
|
| 67 | kvm_nested_setup_mdcr_el2(vcpu);
|
| 68 |
|
| 69 |
|
| 70 | if (has_vhe())
|
| 71 | write_sysreg(vcpu->arch.mdcr_el2, mdcr_el2);
|
| 72 |
|
| 73 | preempt_enable();
|
| 74 | }
|
| 75 |
|
| 76 | void kvm_init_host_debug_data(void)
|
| 77 | {
|
| 78 | u64 dfr0 = read_sysreg(id_aa64dfr0_el1);
|
| 79 | unsigned int pmuver = cpuid_feature_extract_unsigned_field(dfr0,
|
| 80 | ID_AA64DFR0_EL1_PMUVer_SHIFT);
|
| 81 |
|
| 82 | if (pmuv3_implemented(pmuver))
|
| 83 | *host_data_ptr(nr_event_counters) = FIELD_GET(ARMV8_PMU_PMCR_N,
|
| 84 | read_sysreg(pmcr_el0));
|
| 85 |
|
| 86 | *host_data_ptr(debug_brps) = SYS_FIELD_GET(ID_AA64DFR0_EL1, BRPs, dfr0);
|
| 87 | *host_data_ptr(debug_wrps) = SYS_FIELD_GET(ID_AA64DFR0_EL1, WRPs, dfr0);
|
| 88 |
|
| 89 | if (cpu_has_spe(dfr0))
|
| 90 | host_data_set_flag(HAS_SPE);
|
| 91 |
|
| 92 | if (has_vhe())
|
| 93 | return;
|
| 94 |
|
| 95 |
|
| 96 | if (cpuid_feature_extract_unsigned_field(dfr0, ID_AA64DFR0_EL1_BRBE_SHIFT))
|
| 97 | host_data_set_flag(HAS_BRBE);
|
| 98 |
|
| 99 | if (cpuid_feature_extract_unsigned_field(dfr0, ID_AA64DFR0_EL1_TraceFilt_SHIFT)) {
|
| 100 |
|
| 101 | if (is_protected_kvm_enabled())
|
| 102 | host_data_set_flag(EL1_TRACING_CONFIGURED);
|
| 103 |
|
| 104 | if (cpuid_feature_extract_unsigned_field(dfr0, ID_AA64DFR0_EL1_TraceBuffer_SHIFT) &&
|
| 105 | !(read_sysreg_s(SYS_TRBIDR_EL1) & TRBIDR_EL1_P))
|
| 106 | host_data_set_flag(HAS_TRBE);
|
| 107 | }
|
| 108 | }
|
| 109 |
|
| 110 | void kvm_debug_init_vhe(void)
|
| 111 | {
|
| 112 |
|
| 113 | if (host_data_test_flag(HAS_SPE))
|
| 114 | write_sysreg_el1(0, SYS_PMSCR);
|
| 115 | }
|
| 116 |
|
| 117 | |
| 118 | * Configures the 'external' MDSCR_EL1 value for the guest, i.e. when the host
|
| 119 | * has taken over MDSCR_EL1.
|
| 120 | *
|
| 121 | * - Userspace is single-stepping the guest, and MDSCR_EL1.SS is forced to 1.
|
| 122 | *
|
| 123 | * - Userspace is using the breakpoint/watchpoint registers to debug the
|
| 124 | * guest, and MDSCR_EL1.MDE is forced to 1.
|
| 125 | *
|
| 126 | * - The guest has enabled the OS Lock, and KVM is forcing MDSCR_EL1.MDE to 0,
|
| 127 | * masking all debug exceptions affected by the OS Lock.
|
| 128 |
|
| 129 | static void setup_external_mdscr(struct kvm_vcpu *vcpu)
|
| 130 | {
|
| 131 | |
| 132 | * Use the guest's MDSCR_EL1 as a starting point, since there are
|
| 133 | * several other features controlled by MDSCR_EL1 that are not relevant
|
| 134 | * to the host.
|
| 135 | *
|
| 136 | * Clear the bits that KVM may use which also satisfies emulation of
|
| 137 | * the OS Lock as MDSCR_EL1.MDE is cleared.
|
| 138 |
|
| 139 | u64 mdscr = vcpu_read_sys_reg(vcpu, MDSCR_EL1) & ~(MDSCR_EL1_SS |
|
| 140 | MDSCR_EL1_MDE |
|
| 141 | MDSCR_EL1_KDE);
|
| 142 |
|
| 143 | if (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP)
|
| 144 | mdscr |= MDSCR_EL1_SS;
|
| 145 |
|
| 146 | if (vcpu->guest_debug & KVM_GUESTDBG_USE_HW)
|
| 147 | mdscr |= MDSCR_EL1_MDE | MDSCR_EL1_KDE;
|
| 148 |
|
| 149 | vcpu->arch.external_mdscr_el1 = mdscr;
|
| 150 | }
|
| 151 |
|
| 152 | void kvm_vcpu_load_debug(struct kvm_vcpu *vcpu)
|
| 153 | {
|
| 154 | u64 mdscr;
|
| 155 |
|
| 156 |
|
| 157 | KVM_BUG_ON(vcpu_get_flag(vcpu, SYSREGS_ON_CPU), vcpu->kvm);
|
| 158 |
|
| 159 | if (has_vhe())
|
| 160 | *host_data_ptr(host_debug_state.mdcr_el2) = read_sysreg(mdcr_el2);
|
| 161 |
|
| 162 | |
| 163 | * Determine which of the possible debug states we're in:
|
| 164 | *
|
| 165 | * - VCPU_DEBUG_HOST_OWNED: KVM has taken ownership of the guest's
|
| 166 | * breakpoint/watchpoint registers, or needs to use MDSCR_EL1 to do
|
| 167 | * software step or emulate the effects of the OS Lock being enabled.
|
| 168 | *
|
| 169 | * - VCPU_DEBUG_GUEST_OWNED: The guest has debug exceptions enabled, and
|
| 170 | * the breakpoint/watchpoint registers need to be loaded eagerly.
|
| 171 | *
|
| 172 | * - VCPU_DEBUG_FREE: Neither of the above apply, no breakpoint/watchpoint
|
| 173 | * context needs to be loaded on the CPU.
|
| 174 |
|
| 175 | if (vcpu->guest_debug || kvm_vcpu_os_lock_enabled(vcpu)) {
|
| 176 | vcpu->arch.debug_owner = VCPU_DEBUG_HOST_OWNED;
|
| 177 | setup_external_mdscr(vcpu);
|
| 178 |
|
| 179 | |
| 180 | * Steal the guest's single-step state machine if userspace wants
|
| 181 | * single-step the guest.
|
| 182 |
|
| 183 | if (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP) {
|
| 184 | if (*vcpu_cpsr(vcpu) & DBG_SPSR_SS)
|
| 185 | vcpu_clear_flag(vcpu, GUEST_SS_ACTIVE_PENDING);
|
| 186 | else
|
| 187 | vcpu_set_flag(vcpu, GUEST_SS_ACTIVE_PENDING);
|
| 188 |
|
| 189 | if (!vcpu_get_flag(vcpu, HOST_SS_ACTIVE_PENDING))
|
| 190 | *vcpu_cpsr(vcpu) |= DBG_SPSR_SS;
|
| 191 | else
|
| 192 | *vcpu_cpsr(vcpu) &= ~DBG_SPSR_SS;
|
| 193 | }
|
| 194 | } else {
|
| 195 | mdscr = vcpu_read_sys_reg(vcpu, MDSCR_EL1);
|
| 196 |
|
| 197 | if (mdscr & (MDSCR_EL1_KDE | MDSCR_EL1_MDE))
|
| 198 | vcpu->arch.debug_owner = VCPU_DEBUG_GUEST_OWNED;
|
| 199 | else
|
| 200 | vcpu->arch.debug_owner = VCPU_DEBUG_FREE;
|
| 201 | }
|
| 202 |
|
| 203 | kvm_arm_setup_mdcr_el2(vcpu);
|
| 204 | }
|
| 205 |
|
| 206 | void kvm_vcpu_put_debug(struct kvm_vcpu *vcpu)
|
| 207 | {
|
| 208 | if (has_vhe())
|
| 209 | write_sysreg(*host_data_ptr(host_debug_state.mdcr_el2), mdcr_el2);
|
| 210 |
|
| 211 | if (likely(!(vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP)))
|
| 212 | return;
|
| 213 |
|
| 214 | |
| 215 | * Save the host's software step state and restore the guest's before
|
| 216 | * potentially returning to userspace.
|
| 217 |
|
| 218 | if (!(*vcpu_cpsr(vcpu) & DBG_SPSR_SS))
|
| 219 | vcpu_set_flag(vcpu, HOST_SS_ACTIVE_PENDING);
|
| 220 | else
|
| 221 | vcpu_clear_flag(vcpu, HOST_SS_ACTIVE_PENDING);
|
| 222 |
|
| 223 | if (vcpu_get_flag(vcpu, GUEST_SS_ACTIVE_PENDING))
|
| 224 | *vcpu_cpsr(vcpu) &= ~DBG_SPSR_SS;
|
| 225 | else
|
| 226 | *vcpu_cpsr(vcpu) |= DBG_SPSR_SS;
|
| 227 | }
|
| 228 |
|
| 229 | |
| 230 | * Updates ownership of the debug registers after a trapped guest access to a
|
| 231 | * breakpoint/watchpoint register. Host ownership of the debug registers is of
|
| 232 | * strictly higher priority, and it is the responsibility of the VMM to emulate
|
| 233 | * guest debug exceptions in this configuration.
|
| 234 |
|
| 235 | void kvm_debug_set_guest_ownership(struct kvm_vcpu *vcpu)
|
| 236 | {
|
| 237 | if (kvm_host_owns_debug_regs(vcpu))
|
| 238 | return;
|
| 239 |
|
| 240 | vcpu->arch.debug_owner = VCPU_DEBUG_GUEST_OWNED;
|
| 241 | kvm_arm_setup_mdcr_el2(vcpu);
|
| 242 | }
|
| 243 |
|
| 244 | void kvm_debug_handle_oslar(struct kvm_vcpu *vcpu, u64 val)
|
| 245 | {
|
| 246 | if (val & OSLAR_EL1_OSLK)
|
| 247 | __vcpu_rmw_sys_reg(vcpu, OSLSR_EL1, |=, OSLSR_EL1_OSLK);
|
| 248 | else
|
| 249 | __vcpu_rmw_sys_reg(vcpu, OSLSR_EL1, &=, ~OSLSR_EL1_OSLK);
|
| 250 |
|
| 251 | preempt_disable();
|
| 252 | kvm_arch_vcpu_put(vcpu);
|
| 253 | kvm_arch_vcpu_load(vcpu, smp_processor_id());
|
| 254 | preempt_enable();
|
| 255 | }
|
| 256 |
|
| 257 | static bool skip_trbe_access(bool skip_condition)
|
| 258 | {
|
| 259 | return (WARN_ON_ONCE(preemptible()) || skip_condition ||
|
| 260 | is_protected_kvm_enabled() || !is_kvm_arm_initialised());
|
| 261 | }
|
| 262 |
|
| 263 | void kvm_enable_trbe(void)
|
| 264 | {
|
| 265 | if (!skip_trbe_access(has_vhe()))
|
| 266 | host_data_set_flag(TRBE_ENABLED);
|
| 267 | }
|
| 268 | EXPORT_SYMBOL_GPL(kvm_enable_trbe);
|
| 269 |
|
| 270 | void kvm_disable_trbe(void)
|
| 271 | {
|
| 272 | if (!skip_trbe_access(has_vhe()))
|
| 273 | host_data_clear_flag(TRBE_ENABLED);
|
| 274 | }
|
| 275 | EXPORT_SYMBOL_GPL(kvm_disable_trbe);
|
| 276 |
|
| 277 | void kvm_tracing_set_el1_configuration(u64 trfcr_while_in_guest)
|
| 278 | {
|
| 279 | if (skip_trbe_access(false))
|
| 280 | return;
|
| 281 |
|
| 282 | if (has_vhe()) {
|
| 283 | write_sysreg_s(trfcr_while_in_guest, SYS_TRFCR_EL12);
|
| 284 | return;
|
| 285 | }
|
| 286 |
|
| 287 | *host_data_ptr(trfcr_while_in_guest) = trfcr_while_in_guest;
|
| 288 | if (read_sysreg_s(SYS_TRFCR_EL1) != trfcr_while_in_guest)
|
| 289 | host_data_set_flag(EL1_TRACING_CONFIGURED);
|
| 290 | else
|
| 291 | host_data_clear_flag(EL1_TRACING_CONFIGURED);
|
| 292 | }
|
| 293 | EXPORT_SYMBOL_GPL(kvm_tracing_set_el1_configuration);
|