Commit | Line | Data |
---|---|---|
d400c5b2 DB |
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/debug-sr.h> | |
8 | ||
9 | #include <linux/compiler.h> | |
10 | #include <linux/kvm_host.h> | |
11 | ||
12 | #include <asm/debug-monitors.h> | |
13 | #include <asm/kvm_asm.h> | |
14 | #include <asm/kvm_hyp.h> | |
15 | #include <asm/kvm_mmu.h> | |
16 | ||
c50cb043 | 17 | static void __debug_save_spe(u64 *pmscr_el1) |
d400c5b2 DB |
18 | { |
19 | u64 reg; | |
20 | ||
21 | /* Clear pmscr in case of early return */ | |
22 | *pmscr_el1 = 0; | |
23 | ||
d2602bb4 SP |
24 | /* |
25 | * At this point, we know that this CPU implements | |
26 | * SPE and is available to the host. | |
27 | * Check if the host is actually using it ? | |
28 | */ | |
d400c5b2 | 29 | reg = read_sysreg_s(SYS_PMBLIMITR_EL1); |
c759ec85 | 30 | if (!(reg & BIT(PMBLIMITR_EL1_E_SHIFT))) |
d400c5b2 DB |
31 | return; |
32 | ||
33 | /* Yes; save the control register and disable data generation */ | |
34 | *pmscr_el1 = read_sysreg_s(SYS_PMSCR_EL1); | |
35 | write_sysreg_s(0, SYS_PMSCR_EL1); | |
36 | isb(); | |
37 | ||
38 | /* Now drain all buffered data to memory */ | |
39 | psb_csync(); | |
d400c5b2 DB |
40 | } |
41 | ||
c50cb043 | 42 | static void __debug_restore_spe(u64 pmscr_el1) |
d400c5b2 DB |
43 | { |
44 | if (!pmscr_el1) | |
45 | return; | |
46 | ||
47 | /* The host page table is installed, but not yet synchronised */ | |
48 | isb(); | |
49 | ||
50 | /* Re-enable data generation */ | |
51 | write_sysreg_s(pmscr_el1, SYS_PMSCR_EL1); | |
52 | } | |
53 | ||
a1319260 SP |
54 | static void __debug_save_trace(u64 *trfcr_el1) |
55 | { | |
56 | *trfcr_el1 = 0; | |
57 | ||
58 | /* Check if the TRBE is enabled */ | |
59 | if (!(read_sysreg_s(SYS_TRBLIMITR_EL1) & TRBLIMITR_ENABLE)) | |
60 | return; | |
61 | /* | |
62 | * Prohibit trace generation while we are in guest. | |
63 | * Since access to TRFCR_EL1 is trapped, the guest can't | |
64 | * modify the filtering set by the host. | |
65 | */ | |
66 | *trfcr_el1 = read_sysreg_s(SYS_TRFCR_EL1); | |
67 | write_sysreg_s(0, SYS_TRFCR_EL1); | |
68 | isb(); | |
69 | /* Drain the trace buffer to memory */ | |
70 | tsb_csync(); | |
a1319260 SP |
71 | } |
72 | ||
73 | static void __debug_restore_trace(u64 trfcr_el1) | |
74 | { | |
75 | if (!trfcr_el1) | |
76 | return; | |
77 | ||
78 | /* Restore trace filter controls */ | |
79 | write_sysreg_s(trfcr_el1, SYS_TRFCR_EL1); | |
80 | } | |
81 | ||
b96b0c5d | 82 | void __debug_save_host_buffers_nvhe(struct kvm_vcpu *vcpu) |
d400c5b2 DB |
83 | { |
84 | /* Disable and flush SPE data generation */ | |
b1da4908 | 85 | if (vcpu_get_flag(vcpu, DEBUG_STATE_SAVE_SPE)) |
d2602bb4 | 86 | __debug_save_spe(&vcpu->arch.host_debug_state.pmscr_el1); |
a1319260 | 87 | /* Disable and flush Self-Hosted Trace generation */ |
b1da4908 | 88 | if (vcpu_get_flag(vcpu, DEBUG_STATE_SAVE_TRBE)) |
a1319260 | 89 | __debug_save_trace(&vcpu->arch.host_debug_state.trfcr_el1); |
b96b0c5d SP |
90 | } |
91 | ||
92 | void __debug_switch_to_guest(struct kvm_vcpu *vcpu) | |
93 | { | |
d400c5b2 DB |
94 | __debug_switch_to_guest_common(vcpu); |
95 | } | |
96 | ||
b96b0c5d | 97 | void __debug_restore_host_buffers_nvhe(struct kvm_vcpu *vcpu) |
d400c5b2 | 98 | { |
b1da4908 | 99 | if (vcpu_get_flag(vcpu, DEBUG_STATE_SAVE_SPE)) |
d2602bb4 | 100 | __debug_restore_spe(vcpu->arch.host_debug_state.pmscr_el1); |
b1da4908 | 101 | if (vcpu_get_flag(vcpu, DEBUG_STATE_SAVE_TRBE)) |
a1319260 | 102 | __debug_restore_trace(vcpu->arch.host_debug_state.trfcr_el1); |
b96b0c5d SP |
103 | } |
104 | ||
105 | void __debug_switch_to_host(struct kvm_vcpu *vcpu) | |
106 | { | |
d400c5b2 DB |
107 | __debug_switch_to_host_common(vcpu); |
108 | } | |
109 | ||
d6c850dd | 110 | u64 __kvm_get_mdcr_el2(void) |
d400c5b2 DB |
111 | { |
112 | return read_sysreg(mdcr_el2); | |
113 | } |