Commit 2e02cbb2 authored by Marc Zyngier's avatar Marc Zyngier
Browse files

Merge branch 'kvm-arm64/pmu-5.9' into kvmarm-master/next



Signed-off-by: default avatarMarc Zyngier <maz@kernel.org>
parents 41fa0f59 51dd2eb9
Loading
Loading
Loading
Loading
+53 −4
Original line number Diff line number Diff line
@@ -25,8 +25,10 @@ Returns:

	 =======  ========================================================
	 -EBUSY   The PMU overflow interrupt is already set
	 -ENXIO   The overflow interrupt not set when attempting to get it
	 -ENODEV  PMUv3 not supported
	 -EFAULT  Error reading interrupt number
	 -ENXIO   PMUv3 not supported or the overflow interrupt not set
		  when attempting to get it
	 -ENODEV  KVM_ARM_VCPU_PMU_V3 feature missing from VCPU
	 -EINVAL  Invalid PMU overflow interrupt number supplied or
		  trying to set the IRQ number without using an in-kernel
		  irqchip.
@@ -45,9 +47,10 @@ all vcpus, while as an SPI it must be a separate number per vcpu.
Returns:

	 =======  ======================================================
	 -EEXIST  Interrupt number already used
	 -ENODEV  PMUv3 not supported or GIC not initialized
	 -ENXIO   PMUv3 not properly configured or in-kernel irqchip not
		  configured as required prior to calling this attribute
	 -ENXIO   PMUv3 not supported, missing VCPU feature or interrupt
		  number not set
	 -EBUSY   PMUv3 already initialized
	 =======  ======================================================

@@ -55,6 +58,52 @@ Request the initialization of the PMUv3. If using the PMUv3 with an in-kernel
virtual GIC implementation, this must be done after initializing the in-kernel
irqchip.

1.3 ATTRIBUTE: KVM_ARM_VCPU_PMU_V3_FILTER
-----------------------------------------

:Parameters: in kvm_device_attr.addr the address for a PMU event filter is a
             pointer to a struct kvm_pmu_event_filter

:Returns:

	 =======  ======================================================
	 -ENODEV: PMUv3 not supported or GIC not initialized
	 -ENXIO:  PMUv3 not properly configured or in-kernel irqchip not
	 	  configured as required prior to calling this attribute
	 -EBUSY:  PMUv3 already initialized
	 -EINVAL: Invalid filter range
	 =======  ======================================================

Request the installation of a PMU event filter described as follows:

struct kvm_pmu_event_filter {
	__u16	base_event;
	__u16	nevents;

#define KVM_PMU_EVENT_ALLOW	0
#define KVM_PMU_EVENT_DENY	1

	__u8	action;
	__u8	pad[3];
};

A filter range is defined as the range [@base_event, @base_event + @nevents),
together with an @action (KVM_PMU_EVENT_ALLOW or KVM_PMU_EVENT_DENY). The
first registered range defines the global policy (global ALLOW if the first
@action is DENY, global DENY if the first @action is ALLOW). Multiple ranges
can be programmed, and must fit within the event space defined by the PMU
architecture (10 bits on ARMv8.0, 16 bits from ARMv8.1 onwards).

Note: "Cancelling" a filter by registering the opposite action for the same
range doesn't change the default action. For example, installing an ALLOW
filter for event range [0:10) as the first filter and then applying a DENY
action for the same range will leave the whole range as disabled.

Restrictions: Event 0 (SW_INCR) is never filtered, as it doesn't count a
hardware event. Filtering event 0x1E (CHAIN) has no effect either, as it
isn't strictly speaking an event. Filtering the cycle counter is possible
using event 0x11 (CPU_CYCLES).


2. GROUP: KVM_ARM_VCPU_TIMER_CTRL
=================================
+7 −0
Original line number Diff line number Diff line
@@ -111,6 +111,13 @@ struct kvm_arch {
	 * supported.
	 */
	bool return_nisv_io_abort_to_user;

	/*
	 * VM-wide PMU filter, implemented as a bitmap and big enough for
	 * up to 2^10 events (ARMv8.0) or 2^16 events (ARMv8.1+).
	 */
	unsigned long *pmu_filter;
	unsigned int pmuver;
};

struct kvm_vcpu_fault_info {
+16 −0
Original line number Diff line number Diff line
@@ -159,6 +159,21 @@ struct kvm_sync_regs {
struct kvm_arch_memory_slot {
};

/*
 * PMU filter structure. Describe a range of events with a particular
 * action. To be used with KVM_ARM_VCPU_PMU_V3_FILTER.
 */
struct kvm_pmu_event_filter {
	__u16	base_event;
	__u16	nevents;

#define KVM_PMU_EVENT_ALLOW	0
#define KVM_PMU_EVENT_DENY	1

	__u8	action;
	__u8	pad[3];
};

/* for KVM_GET/SET_VCPU_EVENTS */
struct kvm_vcpu_events {
	struct {
@@ -329,6 +344,7 @@ struct kvm_vcpu_events {
#define KVM_ARM_VCPU_PMU_V3_CTRL	0
#define   KVM_ARM_VCPU_PMU_V3_IRQ	0
#define   KVM_ARM_VCPU_PMU_V3_INIT	1
#define   KVM_ARM_VCPU_PMU_V3_FILTER	2
#define KVM_ARM_VCPU_TIMER_CTRL		1
#define   KVM_ARM_VCPU_TIMER_IRQ_VTIMER		0
#define   KVM_ARM_VCPU_TIMER_IRQ_PTIMER		1
+2 −0
Original line number Diff line number Diff line
@@ -147,6 +147,8 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
{
	int i;

	bitmap_free(kvm->arch.pmu_filter);

	kvm_vgic_destroy(kvm);

	for (i = 0; i < KVM_MAX_VCPUS; ++i) {
+173 −22
Original line number Diff line number Diff line
@@ -20,6 +20,21 @@ static void kvm_pmu_stop_counter(struct kvm_vcpu *vcpu, struct kvm_pmc *pmc);

#define PERF_ATTR_CFG1_KVM_PMU_CHAINED 0x1

static u32 kvm_pmu_event_mask(struct kvm *kvm)
{
	switch (kvm->arch.pmuver) {
	case 1:			/* ARMv8.0 */
		return GENMASK(9, 0);
	case 4:			/* ARMv8.1 */
	case 5:			/* ARMv8.4 */
	case 6:			/* ARMv8.5 */
		return GENMASK(15, 0);
	default:		/* Shouldn't be here, just for sanity */
		WARN_ONCE(1, "Unknown PMU version %d\n", kvm->arch.pmuver);
		return 0;
	}
}

/**
 * kvm_pmu_idx_is_64bit - determine if select_idx is a 64bit counter
 * @vcpu: The vcpu pointer
@@ -100,7 +115,7 @@ static bool kvm_pmu_idx_has_chain_evtype(struct kvm_vcpu *vcpu, u64 select_idx)
		return false;

	reg = PMEVTYPER0_EL0 + select_idx;
	eventsel = __vcpu_sys_reg(vcpu, reg) & ARMV8_PMU_EVTYPE_EVENT;
	eventsel = __vcpu_sys_reg(vcpu, reg) & kvm_pmu_event_mask(vcpu->kvm);

	return eventsel == ARMV8_PMUV3_PERFCTR_CHAIN;
}
@@ -495,7 +510,7 @@ void kvm_pmu_software_increment(struct kvm_vcpu *vcpu, u64 val)

		/* PMSWINC only applies to ... SW_INC! */
		type = __vcpu_sys_reg(vcpu, PMEVTYPER0_EL0 + i);
		type &= ARMV8_PMU_EVTYPE_EVENT;
		type &= kvm_pmu_event_mask(vcpu->kvm);
		if (type != ARMV8_PMUV3_PERFCTR_SW_INCR)
			continue;

@@ -578,11 +593,21 @@ static void kvm_pmu_create_perf_event(struct kvm_vcpu *vcpu, u64 select_idx)
	data = __vcpu_sys_reg(vcpu, reg);

	kvm_pmu_stop_counter(vcpu, pmc);
	eventsel = data & ARMV8_PMU_EVTYPE_EVENT;
	if (pmc->idx == ARMV8_PMU_CYCLE_IDX)
		eventsel = ARMV8_PMUV3_PERFCTR_CPU_CYCLES;
	else
		eventsel = data & kvm_pmu_event_mask(vcpu->kvm);

	/* Software increment event does't need to be backed by a perf event */
	if (eventsel == ARMV8_PMUV3_PERFCTR_SW_INCR &&
	    pmc->idx != ARMV8_PMU_CYCLE_IDX)
	/* Software increment event doesn't need to be backed by a perf event */
	if (eventsel == ARMV8_PMUV3_PERFCTR_SW_INCR)
		return;

	/*
	 * If we have a filter in place and that the event isn't allowed, do
	 * not install a perf event either.
	 */
	if (vcpu->kvm->arch.pmu_filter &&
	    !test_bit(eventsel, vcpu->kvm->arch.pmu_filter))
		return;

	memset(&attr, 0, sizeof(struct perf_event_attr));
@@ -594,8 +619,7 @@ static void kvm_pmu_create_perf_event(struct kvm_vcpu *vcpu, u64 select_idx)
	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 */
	attr.config = (pmc->idx == ARMV8_PMU_CYCLE_IDX) ?
		ARMV8_PMUV3_PERFCTR_CPU_CYCLES : eventsel;
	attr.config = eventsel;

	counter = kvm_pmu_get_pair_counter_value(vcpu, pmc);

@@ -679,17 +703,95 @@ static void kvm_pmu_update_pmc_chained(struct kvm_vcpu *vcpu, u64 select_idx)
void kvm_pmu_set_counter_event_type(struct kvm_vcpu *vcpu, u64 data,
				    u64 select_idx)
{
	u64 reg, event_type = data & ARMV8_PMU_EVTYPE_MASK;
	u64 reg, mask;

	mask  =  ARMV8_PMU_EVTYPE_MASK;
	mask &= ~ARMV8_PMU_EVTYPE_EVENT;
	mask |= kvm_pmu_event_mask(vcpu->kvm);

	reg = (select_idx == ARMV8_PMU_CYCLE_IDX)
	      ? PMCCFILTR_EL0 : PMEVTYPER0_EL0 + select_idx;

	__vcpu_sys_reg(vcpu, reg) = event_type;
	__vcpu_sys_reg(vcpu, reg) = data & mask;

	kvm_pmu_update_pmc_chained(vcpu, select_idx);
	kvm_pmu_create_perf_event(vcpu, select_idx);
}

static int kvm_pmu_probe_pmuver(void)
{
	struct perf_event_attr attr = { };
	struct perf_event *event;
	struct arm_pmu *pmu;
	int pmuver = 0xf;

	/*
	 * Create a dummy event that only counts user cycles. As we'll never
	 * leave this function with the event being live, it will never
	 * count anything. But it allows us to probe some of the PMU
	 * details. Yes, this is terrible.
	 */
	attr.type = PERF_TYPE_RAW;
	attr.size = sizeof(attr);
	attr.pinned = 1;
	attr.disabled = 0;
	attr.exclude_user = 0;
	attr.exclude_kernel = 1;
	attr.exclude_hv = 1;
	attr.exclude_host = 1;
	attr.config = ARMV8_PMUV3_PERFCTR_CPU_CYCLES;
	attr.sample_period = GENMASK(63, 0);

	event = perf_event_create_kernel_counter(&attr, -1, current,
						 kvm_pmu_perf_overflow, &attr);

	if (IS_ERR(event)) {
		pr_err_once("kvm: pmu event creation failed %ld\n",
			    PTR_ERR(event));
		return 0xf;
	}

	if (event->pmu) {
		pmu = to_arm_pmu(event->pmu);
		if (pmu->pmuver)
			pmuver = pmu->pmuver;
	}

	perf_event_disable(event);
	perf_event_release_kernel(event);

	return pmuver;
}

u64 kvm_pmu_get_pmceid(struct kvm_vcpu *vcpu, bool pmceid1)
{
	unsigned long *bmap = vcpu->kvm->arch.pmu_filter;
	u64 val, mask = 0;
	int base, i;

	if (!pmceid1) {
		val = read_sysreg(pmceid0_el0);
		base = 0;
	} else {
		val = read_sysreg(pmceid1_el0);
		base = 32;
	}

	if (!bmap)
		return val;

	for (i = 0; i < 32; i += 8) {
		u64 byte;

		byte = bitmap_get_value8(bmap, base + i);
		mask |= byte << i;
		byte = bitmap_get_value8(bmap, 0x4000 + base + i);
		mask |= byte << (32 + i);
	}

	return val & mask;
}

bool kvm_arm_support_pmu_v3(void)
{
	/*
@@ -735,15 +837,6 @@ int kvm_arm_pmu_v3_enable(struct kvm_vcpu *vcpu)

static int kvm_arm_pmu_v3_init(struct kvm_vcpu *vcpu)
{
	if (!kvm_arm_support_pmu_v3())
		return -ENODEV;

	if (!test_bit(KVM_ARM_VCPU_PMU_V3, vcpu->arch.features))
		return -ENXIO;

	if (vcpu->arch.pmu.created)
		return -EBUSY;

	if (irqchip_in_kernel(vcpu->kvm)) {
		int ret;

@@ -796,6 +889,19 @@ static bool pmu_irq_is_valid(struct kvm *kvm, int irq)

int kvm_arm_pmu_v3_set_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr)
{
	if (!kvm_arm_support_pmu_v3() ||
	    !test_bit(KVM_ARM_VCPU_PMU_V3, vcpu->arch.features))
		return -ENODEV;

	if (vcpu->arch.pmu.created)
		return -EBUSY;

	if (!vcpu->kvm->arch.pmuver)
		vcpu->kvm->arch.pmuver = kvm_pmu_probe_pmuver();

	if (vcpu->kvm->arch.pmuver == 0xf)
		return -ENODEV;

	switch (attr->attr) {
	case KVM_ARM_VCPU_PMU_V3_IRQ: {
		int __user *uaddr = (int __user *)(long)attr->addr;
@@ -804,9 +910,6 @@ int kvm_arm_pmu_v3_set_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr)
		if (!irqchip_in_kernel(vcpu->kvm))
			return -EINVAL;

		if (!test_bit(KVM_ARM_VCPU_PMU_V3, vcpu->arch.features))
			return -ENODEV;

		if (get_user(irq, uaddr))
			return -EFAULT;

@@ -824,6 +927,53 @@ int kvm_arm_pmu_v3_set_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr)
		vcpu->arch.pmu.irq_num = irq;
		return 0;
	}
	case KVM_ARM_VCPU_PMU_V3_FILTER: {
		struct kvm_pmu_event_filter __user *uaddr;
		struct kvm_pmu_event_filter filter;
		int nr_events;

		nr_events = kvm_pmu_event_mask(vcpu->kvm) + 1;

		uaddr = (struct kvm_pmu_event_filter __user *)(long)attr->addr;

		if (copy_from_user(&filter, uaddr, sizeof(filter)))
			return -EFAULT;

		if (((u32)filter.base_event + filter.nevents) > nr_events ||
		    (filter.action != KVM_PMU_EVENT_ALLOW &&
		     filter.action != KVM_PMU_EVENT_DENY))
			return -EINVAL;

		mutex_lock(&vcpu->kvm->lock);

		if (!vcpu->kvm->arch.pmu_filter) {
			vcpu->kvm->arch.pmu_filter = bitmap_alloc(nr_events, GFP_KERNEL);
			if (!vcpu->kvm->arch.pmu_filter) {
				mutex_unlock(&vcpu->kvm->lock);
				return -ENOMEM;
			}

			/*
			 * The default depends on the first applied filter.
			 * If it allows events, the default is to deny.
			 * Conversely, if the first filter denies a set of
			 * events, the default is to allow.
			 */
			if (filter.action == KVM_PMU_EVENT_ALLOW)
				bitmap_zero(vcpu->kvm->arch.pmu_filter, nr_events);
			else
				bitmap_fill(vcpu->kvm->arch.pmu_filter, nr_events);
		}

		if (filter.action == KVM_PMU_EVENT_ALLOW)
			bitmap_set(vcpu->kvm->arch.pmu_filter, filter.base_event, filter.nevents);
		else
			bitmap_clear(vcpu->kvm->arch.pmu_filter, filter.base_event, filter.nevents);

		mutex_unlock(&vcpu->kvm->lock);

		return 0;
	}
	case KVM_ARM_VCPU_PMU_V3_INIT:
		return kvm_arm_pmu_v3_init(vcpu);
	}
@@ -860,6 +1010,7 @@ 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:
	case KVM_ARM_VCPU_PMU_V3_FILTER:
		if (kvm_arm_support_pmu_v3() &&
		    test_bit(KVM_ARM_VCPU_PMU_V3, vcpu->arch.features))
			return 0;
Loading