Commit | Line | Data |
---|---|---|
051ff581 SZ |
1 | /* |
2 | * Copyright (C) 2015 Linaro Ltd. | |
3 | * Author: Shannon Zhao <shannon.zhao@linaro.org> | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License version 2 as | |
7 | * published by the Free Software Foundation. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public License | |
15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
16 | */ | |
17 | ||
18 | #include <linux/cpu.h> | |
19 | #include <linux/kvm.h> | |
20 | #include <linux/kvm_host.h> | |
21 | #include <linux/perf_event.h> | |
22 | #include <asm/kvm_emulate.h> | |
23 | #include <kvm/arm_pmu.h> | |
24 | ||
25 | /** | |
26 | * kvm_pmu_get_counter_value - get PMU counter value | |
27 | * @vcpu: The vcpu pointer | |
28 | * @select_idx: The counter index | |
29 | */ | |
30 | u64 kvm_pmu_get_counter_value(struct kvm_vcpu *vcpu, u64 select_idx) | |
31 | { | |
32 | u64 counter, reg, enabled, running; | |
33 | struct kvm_pmu *pmu = &vcpu->arch.pmu; | |
34 | struct kvm_pmc *pmc = &pmu->pmc[select_idx]; | |
35 | ||
36 | reg = (select_idx == ARMV8_PMU_CYCLE_IDX) | |
37 | ? PMCCNTR_EL0 : PMEVCNTR0_EL0 + select_idx; | |
38 | counter = vcpu_sys_reg(vcpu, reg); | |
39 | ||
40 | /* The real counter value is equal to the value of counter register plus | |
41 | * the value perf event counts. | |
42 | */ | |
43 | if (pmc->perf_event) | |
44 | counter += perf_event_read_value(pmc->perf_event, &enabled, | |
45 | &running); | |
46 | ||
47 | return counter & pmc->bitmask; | |
48 | } | |
49 | ||
50 | /** | |
51 | * kvm_pmu_set_counter_value - set PMU counter value | |
52 | * @vcpu: The vcpu pointer | |
53 | * @select_idx: The counter index | |
54 | * @val: The counter value | |
55 | */ | |
56 | void kvm_pmu_set_counter_value(struct kvm_vcpu *vcpu, u64 select_idx, u64 val) | |
57 | { | |
58 | u64 reg; | |
59 | ||
60 | reg = (select_idx == ARMV8_PMU_CYCLE_IDX) | |
61 | ? PMCCNTR_EL0 : PMEVCNTR0_EL0 + select_idx; | |
62 | vcpu_sys_reg(vcpu, reg) += (s64)val - kvm_pmu_get_counter_value(vcpu, select_idx); | |
63 | } | |
96b0eebc SZ |
64 | |
65 | u64 kvm_pmu_valid_counter_mask(struct kvm_vcpu *vcpu) | |
66 | { | |
67 | u64 val = vcpu_sys_reg(vcpu, PMCR_EL0) >> ARMV8_PMU_PMCR_N_SHIFT; | |
68 | ||
69 | val &= ARMV8_PMU_PMCR_N_MASK; | |
70 | if (val == 0) | |
71 | return BIT(ARMV8_PMU_CYCLE_IDX); | |
72 | else | |
73 | return GENMASK(val - 1, 0) | BIT(ARMV8_PMU_CYCLE_IDX); | |
74 | } | |
75 | ||
76 | /** | |
77 | * kvm_pmu_enable_counter - enable selected PMU counter | |
78 | * @vcpu: The vcpu pointer | |
79 | * @val: the value guest writes to PMCNTENSET register | |
80 | * | |
81 | * Call perf_event_enable to start counting the perf event | |
82 | */ | |
83 | void kvm_pmu_enable_counter(struct kvm_vcpu *vcpu, u64 val) | |
84 | { | |
85 | int i; | |
86 | struct kvm_pmu *pmu = &vcpu->arch.pmu; | |
87 | struct kvm_pmc *pmc; | |
88 | ||
89 | if (!(vcpu_sys_reg(vcpu, PMCR_EL0) & ARMV8_PMU_PMCR_E) || !val) | |
90 | return; | |
91 | ||
92 | for (i = 0; i < ARMV8_PMU_MAX_COUNTERS; i++) { | |
93 | if (!(val & BIT(i))) | |
94 | continue; | |
95 | ||
96 | pmc = &pmu->pmc[i]; | |
97 | if (pmc->perf_event) { | |
98 | perf_event_enable(pmc->perf_event); | |
99 | if (pmc->perf_event->state != PERF_EVENT_STATE_ACTIVE) | |
100 | kvm_debug("fail to enable perf event\n"); | |
101 | } | |
102 | } | |
103 | } | |
104 | ||
105 | /** | |
106 | * kvm_pmu_disable_counter - disable selected PMU counter | |
107 | * @vcpu: The vcpu pointer | |
108 | * @val: the value guest writes to PMCNTENCLR register | |
109 | * | |
110 | * Call perf_event_disable to stop counting the perf event | |
111 | */ | |
112 | void kvm_pmu_disable_counter(struct kvm_vcpu *vcpu, u64 val) | |
113 | { | |
114 | int i; | |
115 | struct kvm_pmu *pmu = &vcpu->arch.pmu; | |
116 | struct kvm_pmc *pmc; | |
117 | ||
118 | if (!val) | |
119 | return; | |
120 | ||
121 | for (i = 0; i < ARMV8_PMU_MAX_COUNTERS; i++) { | |
122 | if (!(val & BIT(i))) | |
123 | continue; | |
124 | ||
125 | pmc = &pmu->pmc[i]; | |
126 | if (pmc->perf_event) | |
127 | perf_event_disable(pmc->perf_event); | |
128 | } | |
129 | } |