162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2015 - ARM Ltd
462306a36Sopenharmony_ci * Author: Marc Zyngier <marc.zyngier@arm.com>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <hyp/debug-sr.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/compiler.h>
1062306a36Sopenharmony_ci#include <linux/kvm_host.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <asm/debug-monitors.h>
1362306a36Sopenharmony_ci#include <asm/kvm_asm.h>
1462306a36Sopenharmony_ci#include <asm/kvm_hyp.h>
1562306a36Sopenharmony_ci#include <asm/kvm_mmu.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistatic void __debug_save_spe(u64 *pmscr_el1)
1862306a36Sopenharmony_ci{
1962306a36Sopenharmony_ci	u64 reg;
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci	/* Clear pmscr in case of early return */
2262306a36Sopenharmony_ci	*pmscr_el1 = 0;
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci	/*
2562306a36Sopenharmony_ci	 * At this point, we know that this CPU implements
2662306a36Sopenharmony_ci	 * SPE and is available to the host.
2762306a36Sopenharmony_ci	 * Check if the host is actually using it ?
2862306a36Sopenharmony_ci	 */
2962306a36Sopenharmony_ci	reg = read_sysreg_s(SYS_PMBLIMITR_EL1);
3062306a36Sopenharmony_ci	if (!(reg & BIT(PMBLIMITR_EL1_E_SHIFT)))
3162306a36Sopenharmony_ci		return;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	/* Yes; save the control register and disable data generation */
3462306a36Sopenharmony_ci	*pmscr_el1 = read_sysreg_s(SYS_PMSCR_EL1);
3562306a36Sopenharmony_ci	write_sysreg_s(0, SYS_PMSCR_EL1);
3662306a36Sopenharmony_ci	isb();
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	/* Now drain all buffered data to memory */
3962306a36Sopenharmony_ci	psb_csync();
4062306a36Sopenharmony_ci}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic void __debug_restore_spe(u64 pmscr_el1)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	if (!pmscr_el1)
4562306a36Sopenharmony_ci		return;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	/* The host page table is installed, but not yet synchronised */
4862306a36Sopenharmony_ci	isb();
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	/* Re-enable data generation */
5162306a36Sopenharmony_ci	write_sysreg_s(pmscr_el1, SYS_PMSCR_EL1);
5262306a36Sopenharmony_ci}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic void __debug_save_trace(u64 *trfcr_el1)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	*trfcr_el1 = 0;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	/* Check if the TRBE is enabled */
5962306a36Sopenharmony_ci	if (!(read_sysreg_s(SYS_TRBLIMITR_EL1) & TRBLIMITR_EL1_E))
6062306a36Sopenharmony_ci		return;
6162306a36Sopenharmony_ci	/*
6262306a36Sopenharmony_ci	 * Prohibit trace generation while we are in guest.
6362306a36Sopenharmony_ci	 * Since access to TRFCR_EL1 is trapped, the guest can't
6462306a36Sopenharmony_ci	 * modify the filtering set by the host.
6562306a36Sopenharmony_ci	 */
6662306a36Sopenharmony_ci	*trfcr_el1 = read_sysreg_s(SYS_TRFCR_EL1);
6762306a36Sopenharmony_ci	write_sysreg_s(0, SYS_TRFCR_EL1);
6862306a36Sopenharmony_ci	isb();
6962306a36Sopenharmony_ci	/* Drain the trace buffer to memory */
7062306a36Sopenharmony_ci	tsb_csync();
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic void __debug_restore_trace(u64 trfcr_el1)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	if (!trfcr_el1)
7662306a36Sopenharmony_ci		return;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	/* Restore trace filter controls */
7962306a36Sopenharmony_ci	write_sysreg_s(trfcr_el1, SYS_TRFCR_EL1);
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_civoid __debug_save_host_buffers_nvhe(struct kvm_vcpu *vcpu)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	/* Disable and flush SPE data generation */
8562306a36Sopenharmony_ci	if (vcpu_get_flag(vcpu, DEBUG_STATE_SAVE_SPE))
8662306a36Sopenharmony_ci		__debug_save_spe(&vcpu->arch.host_debug_state.pmscr_el1);
8762306a36Sopenharmony_ci	/* Disable and flush Self-Hosted Trace generation */
8862306a36Sopenharmony_ci	if (vcpu_get_flag(vcpu, DEBUG_STATE_SAVE_TRBE))
8962306a36Sopenharmony_ci		__debug_save_trace(&vcpu->arch.host_debug_state.trfcr_el1);
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_civoid __debug_switch_to_guest(struct kvm_vcpu *vcpu)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	__debug_switch_to_guest_common(vcpu);
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_civoid __debug_restore_host_buffers_nvhe(struct kvm_vcpu *vcpu)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	if (vcpu_get_flag(vcpu, DEBUG_STATE_SAVE_SPE))
10062306a36Sopenharmony_ci		__debug_restore_spe(vcpu->arch.host_debug_state.pmscr_el1);
10162306a36Sopenharmony_ci	if (vcpu_get_flag(vcpu, DEBUG_STATE_SAVE_TRBE))
10262306a36Sopenharmony_ci		__debug_restore_trace(vcpu->arch.host_debug_state.trfcr_el1);
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_civoid __debug_switch_to_host(struct kvm_vcpu *vcpu)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	__debug_switch_to_host_common(vcpu);
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ciu64 __kvm_get_mdcr_el2(void)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	return read_sysreg(mdcr_el2);
11362306a36Sopenharmony_ci}
114