Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch kvm-arm64/spe-trbe-nvhe into kvmarm-master/next

* kvm-arm64/spe-trbe-nvhe:
: .
: Fix SPE and TRBE nVHE world switch which can otherwise result in
: pretty bad behaviours, as they have the nasty habit of performing
: out of context speculative page table walks.
:
: Patches courtesy of Will Deacon.
: .
KVM: arm64: Don't pass host_debug_state to BRBE world-switch routines
KVM: arm64: Disable SPE Profiling Buffer when running in guest context
KVM: arm64: Disable TRBE Trace Buffer Unit when running in guest context

Signed-off-by: Marc Zyngier <maz@kernel.org>

+95 -25
+2
arch/arm64/include/asm/kvm_host.h
··· 790 790 struct kvm_guest_debug_arch regs; 791 791 /* Statistical profiling extension */ 792 792 u64 pmscr_el1; 793 + u64 pmblimitr_el1; 793 794 /* Self-hosted trace */ 794 795 u64 trfcr_el1; 796 + u64 trblimitr_el1; 795 797 /* Values of trap registers for the host before guest entry. */ 796 798 u64 mdcr_el2; 797 799 u64 brbcr_el1;
+92 -24
arch/arm64/kvm/hyp/nvhe/debug-sr.c
··· 14 14 #include <asm/kvm_hyp.h> 15 15 #include <asm/kvm_mmu.h> 16 16 17 - static void __debug_save_spe(u64 *pmscr_el1) 17 + static void __debug_save_spe(void) 18 18 { 19 - u64 reg; 19 + u64 *pmscr_el1, *pmblimitr_el1; 20 20 21 - /* Clear pmscr in case of early return */ 22 - *pmscr_el1 = 0; 21 + pmscr_el1 = host_data_ptr(host_debug_state.pmscr_el1); 22 + pmblimitr_el1 = host_data_ptr(host_debug_state.pmblimitr_el1); 23 23 24 24 /* 25 25 * At this point, we know that this CPU implements 26 26 * SPE and is available to the host. 27 27 * Check if the host is actually using it ? 28 28 */ 29 - reg = read_sysreg_s(SYS_PMBLIMITR_EL1); 30 - if (!(reg & BIT(PMBLIMITR_EL1_E_SHIFT))) 29 + *pmblimitr_el1 = read_sysreg_s(SYS_PMBLIMITR_EL1); 30 + if (!(*pmblimitr_el1 & BIT(PMBLIMITR_EL1_E_SHIFT))) 31 31 return; 32 32 33 33 /* Yes; save the control register and disable data generation */ ··· 37 37 38 38 /* Now drain all buffered data to memory */ 39 39 psb_csync(); 40 + dsb(nsh); 41 + 42 + /* And disable the profiling buffer */ 43 + write_sysreg_s(0, SYS_PMBLIMITR_EL1); 44 + isb(); 40 45 } 41 46 42 - static void __debug_restore_spe(u64 pmscr_el1) 47 + static void __debug_restore_spe(void) 43 48 { 44 - if (!pmscr_el1) 49 + u64 pmblimitr_el1 = *host_data_ptr(host_debug_state.pmblimitr_el1); 50 + 51 + if (!(pmblimitr_el1 & BIT(PMBLIMITR_EL1_E_SHIFT))) 45 52 return; 46 53 47 54 /* The host page table is installed, but not yet synchronised */ 48 55 isb(); 49 56 57 + /* Re-enable the profiling buffer. */ 58 + write_sysreg_s(pmblimitr_el1, SYS_PMBLIMITR_EL1); 59 + isb(); 60 + 50 61 /* Re-enable data generation */ 51 - write_sysreg_el1(pmscr_el1, SYS_PMSCR); 62 + write_sysreg_el1(*host_data_ptr(host_debug_state.pmscr_el1), SYS_PMSCR); 52 63 } 53 64 54 65 static void __trace_do_switch(u64 *saved_trfcr, u64 new_trfcr) ··· 68 57 write_sysreg_el1(new_trfcr, SYS_TRFCR); 69 58 } 70 59 71 - static bool __trace_needs_drain(void) 60 + static void __trace_drain_and_disable(void) 72 61 { 73 - if (is_protected_kvm_enabled() && host_data_test_flag(HAS_TRBE)) 74 - return read_sysreg_s(SYS_TRBLIMITR_EL1) & TRBLIMITR_EL1_E; 62 + u64 *trblimitr_el1 = host_data_ptr(host_debug_state.trblimitr_el1); 63 + bool needs_drain = is_protected_kvm_enabled() ? 64 + host_data_test_flag(HAS_TRBE) : 65 + host_data_test_flag(TRBE_ENABLED); 75 66 76 - return host_data_test_flag(TRBE_ENABLED); 67 + if (!needs_drain) { 68 + *trblimitr_el1 = 0; 69 + return; 70 + } 71 + 72 + *trblimitr_el1 = read_sysreg_s(SYS_TRBLIMITR_EL1); 73 + if (*trblimitr_el1 & TRBLIMITR_EL1_E) { 74 + /* 75 + * The host has enabled the Trace Buffer Unit so we have 76 + * to beat the CPU with a stick until it stops accessing 77 + * memory. 78 + */ 79 + 80 + /* First, ensure that our prior write to TRFCR has stuck. */ 81 + isb(); 82 + 83 + /* Now synchronise with the trace and drain the buffer. */ 84 + tsb_csync(); 85 + dsb(nsh); 86 + 87 + /* 88 + * With no more trace being generated, we can disable the 89 + * Trace Buffer Unit. 90 + */ 91 + write_sysreg_s(0, SYS_TRBLIMITR_EL1); 92 + if (cpus_have_final_cap(ARM64_WORKAROUND_2064142)) { 93 + /* 94 + * Some CPUs are so good, we have to drain 'em 95 + * twice. 96 + */ 97 + tsb_csync(); 98 + dsb(nsh); 99 + } 100 + 101 + /* 102 + * Ensure that the Trace Buffer Unit is disabled before 103 + * we start mucking with the stage-2 and trap 104 + * configuration. 105 + */ 106 + isb(); 107 + } 77 108 } 78 109 79 110 static bool __trace_needs_switch(void) ··· 132 79 133 80 __trace_do_switch(host_data_ptr(host_debug_state.trfcr_el1), 134 81 *host_data_ptr(trfcr_while_in_guest)); 135 - 136 - if (__trace_needs_drain()) { 137 - isb(); 138 - tsb_csync(); 139 - } 82 + __trace_drain_and_disable(); 140 83 } 141 84 142 85 static void __trace_switch_to_host(void) 143 86 { 87 + u64 trblimitr_el1 = *host_data_ptr(host_debug_state.trblimitr_el1); 88 + 89 + if (trblimitr_el1 & TRBLIMITR_EL1_E) { 90 + /* Re-enable the Trace Buffer Unit for the host. */ 91 + write_sysreg_s(trblimitr_el1, SYS_TRBLIMITR_EL1); 92 + isb(); 93 + if (cpus_have_final_cap(ARM64_WORKAROUND_2038923)) { 94 + /* 95 + * Make sure the unit is re-enabled before we 96 + * poke TRFCR. 97 + */ 98 + isb(); 99 + } 100 + } 101 + 144 102 __trace_do_switch(host_data_ptr(trfcr_while_in_guest), 145 103 *host_data_ptr(host_debug_state.trfcr_el1)); 146 104 } 147 105 148 - static void __debug_save_brbe(u64 *brbcr_el1) 106 + static void __debug_save_brbe(void) 149 107 { 108 + u64 *brbcr_el1 = host_data_ptr(host_debug_state.brbcr_el1); 109 + 150 110 *brbcr_el1 = 0; 151 111 152 112 /* Check if the BRBE is enabled */ ··· 175 109 write_sysreg_el1(0, SYS_BRBCR); 176 110 } 177 111 178 - static void __debug_restore_brbe(u64 brbcr_el1) 112 + static void __debug_restore_brbe(void) 179 113 { 114 + u64 brbcr_el1 = *host_data_ptr(host_debug_state.brbcr_el1); 115 + 180 116 if (!brbcr_el1) 181 117 return; 182 118 ··· 190 122 { 191 123 /* Disable and flush SPE data generation */ 192 124 if (host_data_test_flag(HAS_SPE)) 193 - __debug_save_spe(host_data_ptr(host_debug_state.pmscr_el1)); 125 + __debug_save_spe(); 194 126 195 127 /* Disable BRBE branch records */ 196 128 if (host_data_test_flag(HAS_BRBE)) 197 - __debug_save_brbe(host_data_ptr(host_debug_state.brbcr_el1)); 129 + __debug_save_brbe(); 198 130 199 131 if (__trace_needs_switch()) 200 132 __trace_switch_to_guest(); ··· 208 140 void __debug_restore_host_buffers_nvhe(struct kvm_vcpu *vcpu) 209 141 { 210 142 if (host_data_test_flag(HAS_SPE)) 211 - __debug_restore_spe(*host_data_ptr(host_debug_state.pmscr_el1)); 143 + __debug_restore_spe(); 212 144 if (host_data_test_flag(HAS_BRBE)) 213 - __debug_restore_brbe(*host_data_ptr(host_debug_state.brbcr_el1)); 145 + __debug_restore_brbe(); 214 146 if (__trace_needs_switch()) 215 147 __trace_switch_to_host(); 216 148 }
+1 -1
arch/arm64/kvm/hyp/nvhe/switch.c
··· 293 293 * We're about to restore some new MMU state. Make sure 294 294 * ongoing page-table walks that have started before we 295 295 * trapped to EL2 have completed. This also synchronises the 296 - * above disabling of BRBE, SPE and TRBE. 296 + * above disabling of BRBE. 297 297 * 298 298 * See DDI0487I.a D8.1.5 "Out-of-context translation regimes", 299 299 * rule R_LFHQG and subsequent information statements.