18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2015 - ARM Ltd
48c2ecf20Sopenharmony_ci * Author: Marc Zyngier <marc.zyngier@arm.com>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <hyp/debug-sr.h>
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/compiler.h>
108c2ecf20Sopenharmony_ci#include <linux/kvm_host.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <asm/debug-monitors.h>
138c2ecf20Sopenharmony_ci#include <asm/kvm_asm.h>
148c2ecf20Sopenharmony_ci#include <asm/kvm_hyp.h>
158c2ecf20Sopenharmony_ci#include <asm/kvm_mmu.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_cistatic void __debug_save_spe(u64 *pmscr_el1)
188c2ecf20Sopenharmony_ci{
198c2ecf20Sopenharmony_ci	u64 reg;
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci	/* Clear pmscr in case of early return */
228c2ecf20Sopenharmony_ci	*pmscr_el1 = 0;
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci	/* SPE present on this CPU? */
258c2ecf20Sopenharmony_ci	if (!cpuid_feature_extract_unsigned_field(read_sysreg(id_aa64dfr0_el1),
268c2ecf20Sopenharmony_ci						  ID_AA64DFR0_PMSVER_SHIFT))
278c2ecf20Sopenharmony_ci		return;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci	/* Yes; is it owned by EL3? */
308c2ecf20Sopenharmony_ci	reg = read_sysreg_s(SYS_PMBIDR_EL1);
318c2ecf20Sopenharmony_ci	if (reg & BIT(SYS_PMBIDR_EL1_P_SHIFT))
328c2ecf20Sopenharmony_ci		return;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	/* No; is the host actually using the thing? */
358c2ecf20Sopenharmony_ci	reg = read_sysreg_s(SYS_PMBLIMITR_EL1);
368c2ecf20Sopenharmony_ci	if (!(reg & BIT(SYS_PMBLIMITR_EL1_E_SHIFT)))
378c2ecf20Sopenharmony_ci		return;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	/* Yes; save the control register and disable data generation */
408c2ecf20Sopenharmony_ci	*pmscr_el1 = read_sysreg_s(SYS_PMSCR_EL1);
418c2ecf20Sopenharmony_ci	write_sysreg_s(0, SYS_PMSCR_EL1);
428c2ecf20Sopenharmony_ci	isb();
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	/* Now drain all buffered data to memory */
458c2ecf20Sopenharmony_ci	psb_csync();
468c2ecf20Sopenharmony_ci	dsb(nsh);
478c2ecf20Sopenharmony_ci}
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistatic void __debug_restore_spe(u64 pmscr_el1)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	if (!pmscr_el1)
528c2ecf20Sopenharmony_ci		return;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	/* The host page table is installed, but not yet synchronised */
558c2ecf20Sopenharmony_ci	isb();
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	/* Re-enable data generation */
588c2ecf20Sopenharmony_ci	write_sysreg_s(pmscr_el1, SYS_PMSCR_EL1);
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_civoid __debug_save_host_buffers_nvhe(struct kvm_vcpu *vcpu)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	/* Disable and flush SPE data generation */
648c2ecf20Sopenharmony_ci	__debug_save_spe(&vcpu->arch.host_debug_state.pmscr_el1);
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_civoid __debug_switch_to_guest(struct kvm_vcpu *vcpu)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	__debug_switch_to_guest_common(vcpu);
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_civoid __debug_restore_host_buffers_nvhe(struct kvm_vcpu *vcpu)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	__debug_restore_spe(vcpu->arch.host_debug_state.pmscr_el1);
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_civoid __debug_switch_to_host(struct kvm_vcpu *vcpu)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	__debug_switch_to_host_common(vcpu);
808c2ecf20Sopenharmony_ci}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ciu32 __kvm_get_mdcr_el2(void)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	return read_sysreg(mdcr_el2);
858c2ecf20Sopenharmony_ci}
86