xref: /kernel/linux/linux-5.10/arch/arm64/kvm/pmu.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright 2019 Arm Limited
48c2ecf20Sopenharmony_ci * Author: Andrew Murray <Andrew.Murray@arm.com>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci#include <linux/kvm_host.h>
78c2ecf20Sopenharmony_ci#include <linux/perf_event.h>
88c2ecf20Sopenharmony_ci#include <asm/kvm_hyp.h>
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci/*
118c2ecf20Sopenharmony_ci * Given the perf event attributes and system type, determine
128c2ecf20Sopenharmony_ci * if we are going to need to switch counters at guest entry/exit.
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_cistatic bool kvm_pmu_switch_needed(struct perf_event_attr *attr)
158c2ecf20Sopenharmony_ci{
168c2ecf20Sopenharmony_ci	/**
178c2ecf20Sopenharmony_ci	 * With VHE the guest kernel runs at EL1 and the host at EL2,
188c2ecf20Sopenharmony_ci	 * where user (EL0) is excluded then we have no reason to switch
198c2ecf20Sopenharmony_ci	 * counters.
208c2ecf20Sopenharmony_ci	 */
218c2ecf20Sopenharmony_ci	if (has_vhe() && attr->exclude_user)
228c2ecf20Sopenharmony_ci		return false;
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci	/* Only switch if attributes are different */
258c2ecf20Sopenharmony_ci	return (attr->exclude_host != attr->exclude_guest);
268c2ecf20Sopenharmony_ci}
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci/*
298c2ecf20Sopenharmony_ci * Add events to track that we may want to switch at guest entry/exit
308c2ecf20Sopenharmony_ci * time.
318c2ecf20Sopenharmony_ci */
328c2ecf20Sopenharmony_civoid kvm_set_pmu_events(u32 set, struct perf_event_attr *attr)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	struct kvm_host_data *ctx = this_cpu_ptr_hyp_sym(kvm_host_data);
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	if (!ctx || !kvm_pmu_switch_needed(attr))
378c2ecf20Sopenharmony_ci		return;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	if (!attr->exclude_host)
408c2ecf20Sopenharmony_ci		ctx->pmu_events.events_host |= set;
418c2ecf20Sopenharmony_ci	if (!attr->exclude_guest)
428c2ecf20Sopenharmony_ci		ctx->pmu_events.events_guest |= set;
438c2ecf20Sopenharmony_ci}
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci/*
468c2ecf20Sopenharmony_ci * Stop tracking events
478c2ecf20Sopenharmony_ci */
488c2ecf20Sopenharmony_civoid kvm_clr_pmu_events(u32 clr)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	struct kvm_host_data *ctx = this_cpu_ptr_hyp_sym(kvm_host_data);
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	if (!ctx)
538c2ecf20Sopenharmony_ci		return;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	ctx->pmu_events.events_host &= ~clr;
568c2ecf20Sopenharmony_ci	ctx->pmu_events.events_guest &= ~clr;
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci#define PMEVTYPER_READ_CASE(idx)				\
608c2ecf20Sopenharmony_ci	case idx:						\
618c2ecf20Sopenharmony_ci		return read_sysreg(pmevtyper##idx##_el0)
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci#define PMEVTYPER_WRITE_CASE(idx)				\
648c2ecf20Sopenharmony_ci	case idx:						\
658c2ecf20Sopenharmony_ci		write_sysreg(val, pmevtyper##idx##_el0);	\
668c2ecf20Sopenharmony_ci		break
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci#define PMEVTYPER_CASES(readwrite)				\
698c2ecf20Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(0);			\
708c2ecf20Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(1);			\
718c2ecf20Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(2);			\
728c2ecf20Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(3);			\
738c2ecf20Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(4);			\
748c2ecf20Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(5);			\
758c2ecf20Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(6);			\
768c2ecf20Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(7);			\
778c2ecf20Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(8);			\
788c2ecf20Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(9);			\
798c2ecf20Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(10);			\
808c2ecf20Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(11);			\
818c2ecf20Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(12);			\
828c2ecf20Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(13);			\
838c2ecf20Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(14);			\
848c2ecf20Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(15);			\
858c2ecf20Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(16);			\
868c2ecf20Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(17);			\
878c2ecf20Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(18);			\
888c2ecf20Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(19);			\
898c2ecf20Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(20);			\
908c2ecf20Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(21);			\
918c2ecf20Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(22);			\
928c2ecf20Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(23);			\
938c2ecf20Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(24);			\
948c2ecf20Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(25);			\
958c2ecf20Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(26);			\
968c2ecf20Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(27);			\
978c2ecf20Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(28);			\
988c2ecf20Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(29);			\
998c2ecf20Sopenharmony_ci	PMEVTYPER_##readwrite##_CASE(30)
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci/*
1028c2ecf20Sopenharmony_ci * Read a value direct from PMEVTYPER<idx> where idx is 0-30
1038c2ecf20Sopenharmony_ci * or PMCCFILTR_EL0 where idx is ARMV8_PMU_CYCLE_IDX (31).
1048c2ecf20Sopenharmony_ci */
1058c2ecf20Sopenharmony_cistatic u64 kvm_vcpu_pmu_read_evtype_direct(int idx)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	switch (idx) {
1088c2ecf20Sopenharmony_ci	PMEVTYPER_CASES(READ);
1098c2ecf20Sopenharmony_ci	case ARMV8_PMU_CYCLE_IDX:
1108c2ecf20Sopenharmony_ci		return read_sysreg(pmccfiltr_el0);
1118c2ecf20Sopenharmony_ci	default:
1128c2ecf20Sopenharmony_ci		WARN_ON(1);
1138c2ecf20Sopenharmony_ci	}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	return 0;
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci/*
1198c2ecf20Sopenharmony_ci * Write a value direct to PMEVTYPER<idx> where idx is 0-30
1208c2ecf20Sopenharmony_ci * or PMCCFILTR_EL0 where idx is ARMV8_PMU_CYCLE_IDX (31).
1218c2ecf20Sopenharmony_ci */
1228c2ecf20Sopenharmony_cistatic void kvm_vcpu_pmu_write_evtype_direct(int idx, u32 val)
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	switch (idx) {
1258c2ecf20Sopenharmony_ci	PMEVTYPER_CASES(WRITE);
1268c2ecf20Sopenharmony_ci	case ARMV8_PMU_CYCLE_IDX:
1278c2ecf20Sopenharmony_ci		write_sysreg(val, pmccfiltr_el0);
1288c2ecf20Sopenharmony_ci		break;
1298c2ecf20Sopenharmony_ci	default:
1308c2ecf20Sopenharmony_ci		WARN_ON(1);
1318c2ecf20Sopenharmony_ci	}
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci/*
1358c2ecf20Sopenharmony_ci * Modify ARMv8 PMU events to include EL0 counting
1368c2ecf20Sopenharmony_ci */
1378c2ecf20Sopenharmony_cistatic void kvm_vcpu_pmu_enable_el0(unsigned long events)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	u64 typer;
1408c2ecf20Sopenharmony_ci	u32 counter;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	for_each_set_bit(counter, &events, 32) {
1438c2ecf20Sopenharmony_ci		typer = kvm_vcpu_pmu_read_evtype_direct(counter);
1448c2ecf20Sopenharmony_ci		typer &= ~ARMV8_PMU_EXCLUDE_EL0;
1458c2ecf20Sopenharmony_ci		kvm_vcpu_pmu_write_evtype_direct(counter, typer);
1468c2ecf20Sopenharmony_ci	}
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci/*
1508c2ecf20Sopenharmony_ci * Modify ARMv8 PMU events to exclude EL0 counting
1518c2ecf20Sopenharmony_ci */
1528c2ecf20Sopenharmony_cistatic void kvm_vcpu_pmu_disable_el0(unsigned long events)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	u64 typer;
1558c2ecf20Sopenharmony_ci	u32 counter;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	for_each_set_bit(counter, &events, 32) {
1588c2ecf20Sopenharmony_ci		typer = kvm_vcpu_pmu_read_evtype_direct(counter);
1598c2ecf20Sopenharmony_ci		typer |= ARMV8_PMU_EXCLUDE_EL0;
1608c2ecf20Sopenharmony_ci		kvm_vcpu_pmu_write_evtype_direct(counter, typer);
1618c2ecf20Sopenharmony_ci	}
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci/*
1658c2ecf20Sopenharmony_ci * On VHE ensure that only guest events have EL0 counting enabled.
1668c2ecf20Sopenharmony_ci * This is called from both vcpu_{load,put} and the sysreg handling.
1678c2ecf20Sopenharmony_ci * Since the latter is preemptible, special care must be taken to
1688c2ecf20Sopenharmony_ci * disable preemption.
1698c2ecf20Sopenharmony_ci */
1708c2ecf20Sopenharmony_civoid kvm_vcpu_pmu_restore_guest(struct kvm_vcpu *vcpu)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	struct kvm_host_data *host;
1738c2ecf20Sopenharmony_ci	u32 events_guest, events_host;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	if (!has_vhe())
1768c2ecf20Sopenharmony_ci		return;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	preempt_disable();
1798c2ecf20Sopenharmony_ci	host = this_cpu_ptr_hyp_sym(kvm_host_data);
1808c2ecf20Sopenharmony_ci	events_guest = host->pmu_events.events_guest;
1818c2ecf20Sopenharmony_ci	events_host = host->pmu_events.events_host;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	kvm_vcpu_pmu_enable_el0(events_guest);
1848c2ecf20Sopenharmony_ci	kvm_vcpu_pmu_disable_el0(events_host);
1858c2ecf20Sopenharmony_ci	preempt_enable();
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci/*
1898c2ecf20Sopenharmony_ci * On VHE ensure that only host events have EL0 counting enabled
1908c2ecf20Sopenharmony_ci */
1918c2ecf20Sopenharmony_civoid kvm_vcpu_pmu_restore_host(struct kvm_vcpu *vcpu)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	struct kvm_host_data *host;
1948c2ecf20Sopenharmony_ci	u32 events_guest, events_host;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	if (!has_vhe())
1978c2ecf20Sopenharmony_ci		return;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	host = this_cpu_ptr_hyp_sym(kvm_host_data);
2008c2ecf20Sopenharmony_ci	events_guest = host->pmu_events.events_guest;
2018c2ecf20Sopenharmony_ci	events_host = host->pmu_events.events_host;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	kvm_vcpu_pmu_enable_el0(events_host);
2048c2ecf20Sopenharmony_ci	kvm_vcpu_pmu_disable_el0(events_guest);
2058c2ecf20Sopenharmony_ci}
206