Blame view

virt/kvm/arm/pmu.c 15 KB
051ff581c   Shannon Zhao   arm64: KVM: Add a...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  /*
   * Copyright (C) 2015 Linaro Ltd.
   * Author: Shannon Zhao <shannon.zhao@linaro.org>
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 as
   * published by the Free Software Foundation.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   */
  
  #include <linux/cpu.h>
  #include <linux/kvm.h>
  #include <linux/kvm_host.h>
  #include <linux/perf_event.h>
bb0c70bcc   Shannon Zhao   arm64: KVM: Add a...
22
  #include <linux/uaccess.h>
051ff581c   Shannon Zhao   arm64: KVM: Add a...
23
24
  #include <asm/kvm_emulate.h>
  #include <kvm/arm_pmu.h>
b02386eb7   Shannon Zhao   arm64: KVM: Add P...
25
  #include <kvm/arm_vgic.h>
051ff581c   Shannon Zhao   arm64: KVM: Add a...
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
  
  /**
   * kvm_pmu_get_counter_value - get PMU counter value
   * @vcpu: The vcpu pointer
   * @select_idx: The counter index
   */
  u64 kvm_pmu_get_counter_value(struct kvm_vcpu *vcpu, u64 select_idx)
  {
  	u64 counter, reg, enabled, running;
  	struct kvm_pmu *pmu = &vcpu->arch.pmu;
  	struct kvm_pmc *pmc = &pmu->pmc[select_idx];
  
  	reg = (select_idx == ARMV8_PMU_CYCLE_IDX)
  	      ? PMCCNTR_EL0 : PMEVCNTR0_EL0 + select_idx;
  	counter = vcpu_sys_reg(vcpu, reg);
  
  	/* The real counter value is equal to the value of counter register plus
  	 * the value perf event counts.
  	 */
  	if (pmc->perf_event)
  		counter += perf_event_read_value(pmc->perf_event, &enabled,
  						 &running);
  
  	return counter & pmc->bitmask;
  }
  
  /**
   * kvm_pmu_set_counter_value - set PMU counter value
   * @vcpu: The vcpu pointer
   * @select_idx: The counter index
   * @val: The counter value
   */
  void kvm_pmu_set_counter_value(struct kvm_vcpu *vcpu, u64 select_idx, u64 val)
  {
  	u64 reg;
  
  	reg = (select_idx == ARMV8_PMU_CYCLE_IDX)
  	      ? PMCCNTR_EL0 : PMEVCNTR0_EL0 + select_idx;
  	vcpu_sys_reg(vcpu, reg) += (s64)val - kvm_pmu_get_counter_value(vcpu, select_idx);
  }
96b0eebcc   Shannon Zhao   arm64: KVM: Add a...
66

7f7663587   Shannon Zhao   arm64: KVM: PMU: ...
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
  /**
   * kvm_pmu_stop_counter - stop PMU counter
   * @pmc: The PMU counter pointer
   *
   * If this counter has been configured to monitor some event, release it here.
   */
  static void kvm_pmu_stop_counter(struct kvm_vcpu *vcpu, struct kvm_pmc *pmc)
  {
  	u64 counter, reg;
  
  	if (pmc->perf_event) {
  		counter = kvm_pmu_get_counter_value(vcpu, pmc->idx);
  		reg = (pmc->idx == ARMV8_PMU_CYCLE_IDX)
  		       ? PMCCNTR_EL0 : PMEVCNTR0_EL0 + pmc->idx;
  		vcpu_sys_reg(vcpu, reg) = counter;
  		perf_event_disable(pmc->perf_event);
  		perf_event_release_kernel(pmc->perf_event);
  		pmc->perf_event = NULL;
  	}
  }
2aa36e984   Shannon Zhao   arm64: KVM: Reset...
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
  /**
   * kvm_pmu_vcpu_reset - reset pmu state for cpu
   * @vcpu: The vcpu pointer
   *
   */
  void kvm_pmu_vcpu_reset(struct kvm_vcpu *vcpu)
  {
  	int i;
  	struct kvm_pmu *pmu = &vcpu->arch.pmu;
  
  	for (i = 0; i < ARMV8_PMU_MAX_COUNTERS; i++) {
  		kvm_pmu_stop_counter(vcpu, &pmu->pmc[i]);
  		pmu->pmc[i].idx = i;
  		pmu->pmc[i].bitmask = 0xffffffffUL;
  	}
  }
5f0a714a2   Shannon Zhao   arm64: KVM: Free ...
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
  /**
   * kvm_pmu_vcpu_destroy - free perf event of PMU for cpu
   * @vcpu: The vcpu pointer
   *
   */
  void kvm_pmu_vcpu_destroy(struct kvm_vcpu *vcpu)
  {
  	int i;
  	struct kvm_pmu *pmu = &vcpu->arch.pmu;
  
  	for (i = 0; i < ARMV8_PMU_MAX_COUNTERS; i++) {
  		struct kvm_pmc *pmc = &pmu->pmc[i];
  
  		if (pmc->perf_event) {
  			perf_event_disable(pmc->perf_event);
  			perf_event_release_kernel(pmc->perf_event);
  			pmc->perf_event = NULL;
  		}
  	}
  }
96b0eebcc   Shannon Zhao   arm64: KVM: Add a...
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
  u64 kvm_pmu_valid_counter_mask(struct kvm_vcpu *vcpu)
  {
  	u64 val = vcpu_sys_reg(vcpu, PMCR_EL0) >> ARMV8_PMU_PMCR_N_SHIFT;
  
  	val &= ARMV8_PMU_PMCR_N_MASK;
  	if (val == 0)
  		return BIT(ARMV8_PMU_CYCLE_IDX);
  	else
  		return GENMASK(val - 1, 0) | BIT(ARMV8_PMU_CYCLE_IDX);
  }
  
  /**
   * kvm_pmu_enable_counter - enable selected PMU counter
   * @vcpu: The vcpu pointer
   * @val: the value guest writes to PMCNTENSET register
   *
   * Call perf_event_enable to start counting the perf event
   */
  void kvm_pmu_enable_counter(struct kvm_vcpu *vcpu, u64 val)
  {
  	int i;
  	struct kvm_pmu *pmu = &vcpu->arch.pmu;
  	struct kvm_pmc *pmc;
  
  	if (!(vcpu_sys_reg(vcpu, PMCR_EL0) & ARMV8_PMU_PMCR_E) || !val)
  		return;
  
  	for (i = 0; i < ARMV8_PMU_MAX_COUNTERS; i++) {
  		if (!(val & BIT(i)))
  			continue;
  
  		pmc = &pmu->pmc[i];
  		if (pmc->perf_event) {
  			perf_event_enable(pmc->perf_event);
  			if (pmc->perf_event->state != PERF_EVENT_STATE_ACTIVE)
  				kvm_debug("fail to enable perf event
  ");
  		}
  	}
  }
  
  /**
   * kvm_pmu_disable_counter - disable selected PMU counter
   * @vcpu: The vcpu pointer
   * @val: the value guest writes to PMCNTENCLR register
   *
   * Call perf_event_disable to stop counting the perf event
   */
  void kvm_pmu_disable_counter(struct kvm_vcpu *vcpu, u64 val)
  {
  	int i;
  	struct kvm_pmu *pmu = &vcpu->arch.pmu;
  	struct kvm_pmc *pmc;
  
  	if (!val)
  		return;
  
  	for (i = 0; i < ARMV8_PMU_MAX_COUNTERS; i++) {
  		if (!(val & BIT(i)))
  			continue;
  
  		pmc = &pmu->pmc[i];
  		if (pmc->perf_event)
  			perf_event_disable(pmc->perf_event);
  	}
  }
7f7663587   Shannon Zhao   arm64: KVM: PMU: ...
189

76d883c4e   Shannon Zhao   arm64: KVM: Add a...
190
191
192
  static u64 kvm_pmu_overflow_status(struct kvm_vcpu *vcpu)
  {
  	u64 reg = 0;
7d4bd1d28   Will Deacon   arm64: KVM: Add b...
193
  	if ((vcpu_sys_reg(vcpu, PMCR_EL0) & ARMV8_PMU_PMCR_E)) {
76d883c4e   Shannon Zhao   arm64: KVM: Add a...
194
195
196
197
  		reg = vcpu_sys_reg(vcpu, PMOVSSET_EL0);
  		reg &= vcpu_sys_reg(vcpu, PMCNTENSET_EL0);
  		reg &= vcpu_sys_reg(vcpu, PMINTENSET_EL1);
  		reg &= kvm_pmu_valid_counter_mask(vcpu);
7d4bd1d28   Will Deacon   arm64: KVM: Add b...
198
  	}
76d883c4e   Shannon Zhao   arm64: KVM: Add a...
199
200
201
  
  	return reg;
  }
d9f89b4e9   Andrew Jones   KVM: arm/arm64: P...
202
  static void kvm_pmu_update_state(struct kvm_vcpu *vcpu)
b7484931e   Andrew Jones   KVM: arm/arm64: P...
203
204
  {
  	struct kvm_pmu *pmu = &vcpu->arch.pmu;
d9f89b4e9   Andrew Jones   KVM: arm/arm64: P...
205
206
207
208
  	bool overflow;
  
  	if (!kvm_arm_pmu_v3_ready(vcpu))
  		return;
b7484931e   Andrew Jones   KVM: arm/arm64: P...
209

d9f89b4e9   Andrew Jones   KVM: arm/arm64: P...
210
  	overflow = !!kvm_pmu_overflow_status(vcpu);
b7484931e   Andrew Jones   KVM: arm/arm64: P...
211
212
213
214
215
216
217
  	if (pmu->irq_level == overflow)
  		return;
  
  	pmu->irq_level = overflow;
  
  	if (likely(irqchip_in_kernel(vcpu->kvm))) {
  		int ret = kvm_vgic_inject_irq(vcpu->kvm, vcpu->vcpu_id,
d9f89b4e9   Andrew Jones   KVM: arm/arm64: P...
218
  					      pmu->irq_num, overflow, pmu);
b7484931e   Andrew Jones   KVM: arm/arm64: P...
219
220
221
  		WARN_ON(ret);
  	}
  }
3dbbdf786   Christoffer Dall   KVM: arm/arm64: R...
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
  bool kvm_pmu_should_notify_user(struct kvm_vcpu *vcpu)
  {
  	struct kvm_pmu *pmu = &vcpu->arch.pmu;
  	struct kvm_sync_regs *sregs = &vcpu->run->s.regs;
  	bool run_level = sregs->device_irq_level & KVM_ARM_DEV_PMU;
  
  	if (likely(irqchip_in_kernel(vcpu->kvm)))
  		return false;
  
  	return pmu->irq_level != run_level;
  }
  
  /*
   * Reflect the PMU overflow interrupt output level into the kvm_run structure
   */
  void kvm_pmu_update_run(struct kvm_vcpu *vcpu)
  {
  	struct kvm_sync_regs *regs = &vcpu->run->s.regs;
  
  	/* Populate the timer bitmap for user space */
  	regs->device_irq_level &= ~KVM_ARM_DEV_PMU;
  	if (vcpu->arch.pmu.irq_level)
  		regs->device_irq_level |= KVM_ARM_DEV_PMU;
  }
b02386eb7   Shannon Zhao   arm64: KVM: Add P...
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
  /**
   * kvm_pmu_flush_hwstate - flush pmu state to cpu
   * @vcpu: The vcpu pointer
   *
   * Check if the PMU has overflowed while we were running in the host, and inject
   * an interrupt if that was the case.
   */
  void kvm_pmu_flush_hwstate(struct kvm_vcpu *vcpu)
  {
  	kvm_pmu_update_state(vcpu);
  }
  
  /**
   * kvm_pmu_sync_hwstate - sync pmu state from cpu
   * @vcpu: The vcpu pointer
   *
   * Check if the PMU has overflowed while we were running in the guest, and
   * inject an interrupt if that was the case.
   */
  void kvm_pmu_sync_hwstate(struct kvm_vcpu *vcpu)
  {
  	kvm_pmu_update_state(vcpu);
  }
  
  static inline struct kvm_vcpu *kvm_pmc_to_vcpu(struct kvm_pmc *pmc)
  {
  	struct kvm_pmu *pmu;
  	struct kvm_vcpu_arch *vcpu_arch;
  
  	pmc -= pmc->idx;
  	pmu = container_of(pmc, struct kvm_pmu, pmc[0]);
  	vcpu_arch = container_of(pmu, struct kvm_vcpu_arch, pmu);
  	return container_of(vcpu_arch, struct kvm_vcpu, arch);
  }
  
  /**
d9f89b4e9   Andrew Jones   KVM: arm/arm64: P...
282
   * When the perf event overflows, set the overflow status and inform the vcpu.
b02386eb7   Shannon Zhao   arm64: KVM: Add P...
283
284
285
286
287
288
289
290
   */
  static void kvm_pmu_perf_overflow(struct perf_event *perf_event,
  				  struct perf_sample_data *data,
  				  struct pt_regs *regs)
  {
  	struct kvm_pmc *pmc = perf_event->overflow_handler_context;
  	struct kvm_vcpu *vcpu = kvm_pmc_to_vcpu(pmc);
  	int idx = pmc->idx;
d9f89b4e9   Andrew Jones   KVM: arm/arm64: P...
291
292
293
294
295
296
  	vcpu_sys_reg(vcpu, PMOVSSET_EL0) |= BIT(idx);
  
  	if (kvm_pmu_overflow_status(vcpu)) {
  		kvm_make_request(KVM_REQ_IRQ_PENDING, vcpu);
  		kvm_vcpu_kick(vcpu);
  	}
b02386eb7   Shannon Zhao   arm64: KVM: Add P...
297
  }
7a0adc706   Shannon Zhao   arm64: KVM: Add a...
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
  /**
   * kvm_pmu_software_increment - do software increment
   * @vcpu: The vcpu pointer
   * @val: the value guest writes to PMSWINC register
   */
  void kvm_pmu_software_increment(struct kvm_vcpu *vcpu, u64 val)
  {
  	int i;
  	u64 type, enable, reg;
  
  	if (val == 0)
  		return;
  
  	enable = vcpu_sys_reg(vcpu, PMCNTENSET_EL0);
  	for (i = 0; i < ARMV8_PMU_CYCLE_IDX; i++) {
  		if (!(val & BIT(i)))
  			continue;
  		type = vcpu_sys_reg(vcpu, PMEVTYPER0_EL0 + i)
  		       & ARMV8_PMU_EVTYPE_EVENT;
b112c84a6   Wei Huang   KVM: arm64: Fix t...
317
  		if ((type == ARMV8_PMUV3_PERFCTR_SW_INCR)
7a0adc706   Shannon Zhao   arm64: KVM: Add a...
318
319
320
321
322
  		    && (enable & BIT(i))) {
  			reg = vcpu_sys_reg(vcpu, PMEVCNTR0_EL0 + i) + 1;
  			reg = lower_32_bits(reg);
  			vcpu_sys_reg(vcpu, PMEVCNTR0_EL0 + i) = reg;
  			if (!reg)
d9f89b4e9   Andrew Jones   KVM: arm/arm64: P...
323
  				vcpu_sys_reg(vcpu, PMOVSSET_EL0) |= BIT(i);
7a0adc706   Shannon Zhao   arm64: KVM: Add a...
324
325
326
  		}
  	}
  }
76993739c   Shannon Zhao   arm64: KVM: Add h...
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
  /**
   * kvm_pmu_handle_pmcr - handle PMCR register
   * @vcpu: The vcpu pointer
   * @val: the value guest writes to PMCR register
   */
  void kvm_pmu_handle_pmcr(struct kvm_vcpu *vcpu, u64 val)
  {
  	struct kvm_pmu *pmu = &vcpu->arch.pmu;
  	struct kvm_pmc *pmc;
  	u64 mask;
  	int i;
  
  	mask = kvm_pmu_valid_counter_mask(vcpu);
  	if (val & ARMV8_PMU_PMCR_E) {
  		kvm_pmu_enable_counter(vcpu,
  				vcpu_sys_reg(vcpu, PMCNTENSET_EL0) & mask);
  	} else {
  		kvm_pmu_disable_counter(vcpu, mask);
  	}
  
  	if (val & ARMV8_PMU_PMCR_C)
  		kvm_pmu_set_counter_value(vcpu, ARMV8_PMU_CYCLE_IDX, 0);
  
  	if (val & ARMV8_PMU_PMCR_P) {
  		for (i = 0; i < ARMV8_PMU_CYCLE_IDX; i++)
  			kvm_pmu_set_counter_value(vcpu, i, 0);
  	}
  
  	if (val & ARMV8_PMU_PMCR_LC) {
  		pmc = &pmu->pmc[ARMV8_PMU_CYCLE_IDX];
  		pmc->bitmask = 0xffffffffffffffffUL;
  	}
  }
7f7663587   Shannon Zhao   arm64: KVM: PMU: ...
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
  static bool kvm_pmu_counter_is_enabled(struct kvm_vcpu *vcpu, u64 select_idx)
  {
  	return (vcpu_sys_reg(vcpu, PMCR_EL0) & ARMV8_PMU_PMCR_E) &&
  	       (vcpu_sys_reg(vcpu, PMCNTENSET_EL0) & BIT(select_idx));
  }
  
  /**
   * kvm_pmu_set_counter_event_type - set selected counter to monitor some event
   * @vcpu: The vcpu pointer
   * @data: The data guest writes to PMXEVTYPER_EL0
   * @select_idx: The number of selected counter
   *
   * When OS accesses PMXEVTYPER_EL0, that means it wants to set a PMC to count an
   * event with given hardware event number. Here we call perf_event API to
   * emulate this action and create a kernel perf event for it.
   */
  void kvm_pmu_set_counter_event_type(struct kvm_vcpu *vcpu, u64 data,
  				    u64 select_idx)
  {
  	struct kvm_pmu *pmu = &vcpu->arch.pmu;
  	struct kvm_pmc *pmc = &pmu->pmc[select_idx];
  	struct perf_event *event;
  	struct perf_event_attr attr;
  	u64 eventsel, counter;
  
  	kvm_pmu_stop_counter(vcpu, pmc);
  	eventsel = data & ARMV8_PMU_EVTYPE_EVENT;
7a0adc706   Shannon Zhao   arm64: KVM: Add a...
387
  	/* Software increment event does't need to be backed by a perf event */
b112c84a6   Wei Huang   KVM: arm64: Fix t...
388
389
  	if (eventsel == ARMV8_PMUV3_PERFCTR_SW_INCR &&
  	    select_idx != ARMV8_PMU_CYCLE_IDX)
7a0adc706   Shannon Zhao   arm64: KVM: Add a...
390
  		return;
7f7663587   Shannon Zhao   arm64: KVM: PMU: ...
391
392
393
394
395
396
397
398
399
  	memset(&attr, 0, sizeof(struct perf_event_attr));
  	attr.type = PERF_TYPE_RAW;
  	attr.size = sizeof(attr);
  	attr.pinned = 1;
  	attr.disabled = !kvm_pmu_counter_is_enabled(vcpu, select_idx);
  	attr.exclude_user = data & ARMV8_PMU_EXCLUDE_EL0 ? 1 : 0;
  	attr.exclude_kernel = data & ARMV8_PMU_EXCLUDE_EL1 ? 1 : 0;
  	attr.exclude_hv = 1; /* Don't count EL2 events */
  	attr.exclude_host = 1; /* Don't count host events */
b112c84a6   Wei Huang   KVM: arm64: Fix t...
400
401
  	attr.config = (select_idx == ARMV8_PMU_CYCLE_IDX) ?
  		ARMV8_PMUV3_PERFCTR_CPU_CYCLES : eventsel;
7f7663587   Shannon Zhao   arm64: KVM: PMU: ...
402
403
404
405
  
  	counter = kvm_pmu_get_counter_value(vcpu, select_idx);
  	/* The initial sample period (overflow count) of an event. */
  	attr.sample_period = (-counter) & pmc->bitmask;
b02386eb7   Shannon Zhao   arm64: KVM: Add P...
406
407
  	event = perf_event_create_kernel_counter(&attr, -1, current,
  						 kvm_pmu_perf_overflow, pmc);
7f7663587   Shannon Zhao   arm64: KVM: PMU: ...
408
409
410
411
412
413
414
415
416
  	if (IS_ERR(event)) {
  		pr_err_once("kvm: pmu event creation failed %ld
  ",
  			    PTR_ERR(event));
  		return;
  	}
  
  	pmc->perf_event = event;
  }
808e73814   Shannon Zhao   arm64: KVM: Add a...
417
418
419
420
421
422
423
424
425
426
  
  bool kvm_arm_support_pmu_v3(void)
  {
  	/*
  	 * Check if HW_PERF_EVENTS are supported by checking the number of
  	 * hardware performance counters. This could ensure the presence of
  	 * a physical PMU and CONFIG_PERF_EVENT is selected.
  	 */
  	return (perf_num_counters() > 0);
  }
bb0c70bcc   Shannon Zhao   arm64: KVM: Add a...
427

a2befacf5   Christoffer Dall   KVM: arm64: Allow...
428
  int kvm_arm_pmu_v3_enable(struct kvm_vcpu *vcpu)
bb0c70bcc   Shannon Zhao   arm64: KVM: Add a...
429
  {
a2befacf5   Christoffer Dall   KVM: arm64: Allow...
430
431
  	if (!vcpu->arch.pmu.created)
  		return 0;
bb0c70bcc   Shannon Zhao   arm64: KVM: Add a...
432

6fe407f2d   Christoffer Dall   KVM: arm64: Requi...
433
  	/*
a2befacf5   Christoffer Dall   KVM: arm64: Allow...
434
435
  	 * A valid interrupt configuration for the PMU is either to have a
  	 * properly configured interrupt number and using an in-kernel
ebb127f2d   Christoffer Dall   KVM: arm/arm64: D...
436
  	 * irqchip, or to not have an in-kernel GIC and not set an IRQ.
6fe407f2d   Christoffer Dall   KVM: arm64: Requi...
437
  	 */
ebb127f2d   Christoffer Dall   KVM: arm/arm64: D...
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
  	if (irqchip_in_kernel(vcpu->kvm)) {
  		int irq = vcpu->arch.pmu.irq_num;
  		if (!kvm_arm_pmu_irq_initialized(vcpu))
  			return -EINVAL;
  
  		/*
  		 * If we are using an in-kernel vgic, at this point we know
  		 * the vgic will be initialized, so we can check the PMU irq
  		 * number against the dimensions of the vgic and make sure
  		 * it's valid.
  		 */
  		if (!irq_is_ppi(irq) && !vgic_valid_spi(vcpu->kvm, irq))
  			return -EINVAL;
  	} else if (kvm_arm_pmu_irq_initialized(vcpu)) {
  		   return -EINVAL;
  	}
a2befacf5   Christoffer Dall   KVM: arm64: Allow...
454
455
456
457
458
459
460
461
462
463
  
  	kvm_pmu_vcpu_reset(vcpu);
  	vcpu->arch.pmu.ready = true;
  
  	return 0;
  }
  
  static int kvm_arm_pmu_v3_init(struct kvm_vcpu *vcpu)
  {
  	if (!kvm_arm_support_pmu_v3())
6fe407f2d   Christoffer Dall   KVM: arm64: Requi...
464
  		return -ENODEV;
a2befacf5   Christoffer Dall   KVM: arm64: Allow...
465
  	if (!test_bit(KVM_ARM_VCPU_PMU_V3, vcpu->arch.features))
bb0c70bcc   Shannon Zhao   arm64: KVM: Add a...
466
  		return -ENXIO;
a2befacf5   Christoffer Dall   KVM: arm64: Allow...
467
  	if (vcpu->arch.pmu.created)
bb0c70bcc   Shannon Zhao   arm64: KVM: Add a...
468
  		return -EBUSY;
a2befacf5   Christoffer Dall   KVM: arm64: Allow...
469
  	if (irqchip_in_kernel(vcpu->kvm)) {
abcb851da   Christoffer Dall   KVM: arm/arm64: C...
470
  		int ret;
a2befacf5   Christoffer Dall   KVM: arm64: Allow...
471
472
473
474
475
476
477
478
479
480
  		/*
  		 * If using the PMU with an in-kernel virtual GIC
  		 * implementation, we require the GIC to be already
  		 * initialized when initializing the PMU.
  		 */
  		if (!vgic_initialized(vcpu->kvm))
  			return -ENODEV;
  
  		if (!kvm_arm_pmu_irq_initialized(vcpu))
  			return -ENXIO;
abcb851da   Christoffer Dall   KVM: arm/arm64: C...
481
482
483
484
485
  
  		ret = kvm_vgic_set_owner(vcpu, vcpu->arch.pmu.irq_num,
  					 &vcpu->arch.pmu);
  		if (ret)
  			return ret;
a2befacf5   Christoffer Dall   KVM: arm64: Allow...
486
  	}
bb0c70bcc   Shannon Zhao   arm64: KVM: Add a...
487

a2befacf5   Christoffer Dall   KVM: arm64: Allow...
488
  	vcpu->arch.pmu.created = true;
bb0c70bcc   Shannon Zhao   arm64: KVM: Add a...
489
490
  	return 0;
  }
2defaff48   Andre Przywara   KVM: arm/arm64: p...
491
492
493
494
495
496
  /*
   * For one VM the interrupt type must be same for each vcpu.
   * As a PPI, the interrupt number is the same for all vcpus,
   * while as an SPI it must be a separate number per vcpu.
   */
  static bool pmu_irq_is_valid(struct kvm *kvm, int irq)
bb0c70bcc   Shannon Zhao   arm64: KVM: Add a...
497
498
499
500
501
502
503
  {
  	int i;
  	struct kvm_vcpu *vcpu;
  
  	kvm_for_each_vcpu(i, vcpu, kvm) {
  		if (!kvm_arm_pmu_irq_initialized(vcpu))
  			continue;
2defaff48   Andre Przywara   KVM: arm/arm64: p...
504
  		if (irq_is_ppi(irq)) {
bb0c70bcc   Shannon Zhao   arm64: KVM: Add a...
505
506
507
508
509
510
511
512
513
514
  			if (vcpu->arch.pmu.irq_num != irq)
  				return false;
  		} else {
  			if (vcpu->arch.pmu.irq_num == irq)
  				return false;
  		}
  	}
  
  	return true;
  }
bb0c70bcc   Shannon Zhao   arm64: KVM: Add a...
515
516
517
518
519
520
  int kvm_arm_pmu_v3_set_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr)
  {
  	switch (attr->attr) {
  	case KVM_ARM_VCPU_PMU_V3_IRQ: {
  		int __user *uaddr = (int __user *)(long)attr->addr;
  		int irq;
a2befacf5   Christoffer Dall   KVM: arm64: Allow...
521
522
  		if (!irqchip_in_kernel(vcpu->kvm))
  			return -EINVAL;
bb0c70bcc   Shannon Zhao   arm64: KVM: Add a...
523
524
525
526
527
  		if (!test_bit(KVM_ARM_VCPU_PMU_V3, vcpu->arch.features))
  			return -ENODEV;
  
  		if (get_user(irq, uaddr))
  			return -EFAULT;
2defaff48   Andre Przywara   KVM: arm/arm64: p...
528
  		/* The PMU overflow interrupt can be a PPI or a valid SPI. */
ebb127f2d   Christoffer Dall   KVM: arm/arm64: D...
529
  		if (!(irq_is_ppi(irq) || irq_is_spi(irq)))
2defaff48   Andre Przywara   KVM: arm/arm64: p...
530
531
532
  			return -EINVAL;
  
  		if (!pmu_irq_is_valid(vcpu->kvm, irq))
bb0c70bcc   Shannon Zhao   arm64: KVM: Add a...
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
  			return -EINVAL;
  
  		if (kvm_arm_pmu_irq_initialized(vcpu))
  			return -EBUSY;
  
  		kvm_debug("Set kvm ARM PMU irq: %d
  ", irq);
  		vcpu->arch.pmu.irq_num = irq;
  		return 0;
  	}
  	case KVM_ARM_VCPU_PMU_V3_INIT:
  		return kvm_arm_pmu_v3_init(vcpu);
  	}
  
  	return -ENXIO;
  }
  
  int kvm_arm_pmu_v3_get_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr)
  {
  	switch (attr->attr) {
  	case KVM_ARM_VCPU_PMU_V3_IRQ: {
  		int __user *uaddr = (int __user *)(long)attr->addr;
  		int irq;
a2befacf5   Christoffer Dall   KVM: arm64: Allow...
556
557
  		if (!irqchip_in_kernel(vcpu->kvm))
  			return -EINVAL;
bb0c70bcc   Shannon Zhao   arm64: KVM: Add a...
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
  		if (!test_bit(KVM_ARM_VCPU_PMU_V3, vcpu->arch.features))
  			return -ENODEV;
  
  		if (!kvm_arm_pmu_irq_initialized(vcpu))
  			return -ENXIO;
  
  		irq = vcpu->arch.pmu.irq_num;
  		return put_user(irq, uaddr);
  	}
  	}
  
  	return -ENXIO;
  }
  
  int kvm_arm_pmu_v3_has_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr)
  {
  	switch (attr->attr) {
  	case KVM_ARM_VCPU_PMU_V3_IRQ:
  	case KVM_ARM_VCPU_PMU_V3_INIT:
  		if (kvm_arm_support_pmu_v3() &&
  		    test_bit(KVM_ARM_VCPU_PMU_V3, vcpu->arch.features))
  			return 0;
  	}
  
  	return -ENXIO;
  }