arm64: KVM: Add a new vcpu device control group for PMUv3
To configure the virtual PMUv3 overflow interrupt number, we use the vcpu kvm_device ioctl, encapsulating the KVM_ARM_VCPU_PMU_V3_IRQ attribute within the KVM_ARM_VCPU_PMU_V3_CTRL group. After configuring the PMUv3, call the vcpu ioctl with attribute KVM_ARM_VCPU_PMU_V3_INIT to initialize the PMUv3. Signed-off-by: Shannon Zhao <shannon.zhao@linaro.org> Acked-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Andrew Jones <drjones@redhat.com> Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
This commit is contained in:
committed by
Marc Zyngier
parent
f577f6c2a6
commit
bb0c70bcca
@@ -6,3 +6,28 @@ KVM_GET_DEVICE_ATTR, and KVM_HAS_DEVICE_ATTR. The interface uses the same struct
|
|||||||
kvm_device_attr as other devices, but targets VCPU-wide settings and controls.
|
kvm_device_attr as other devices, but targets VCPU-wide settings and controls.
|
||||||
|
|
||||||
The groups and attributes per virtual cpu, if any, are architecture specific.
|
The groups and attributes per virtual cpu, if any, are architecture specific.
|
||||||
|
|
||||||
|
1. GROUP: KVM_ARM_VCPU_PMU_V3_CTRL
|
||||||
|
Architectures: ARM64
|
||||||
|
|
||||||
|
1.1. ATTRIBUTE: KVM_ARM_VCPU_PMU_V3_IRQ
|
||||||
|
Parameters: in kvm_device_attr.addr the address for PMU overflow interrupt is a
|
||||||
|
pointer to an int
|
||||||
|
Returns: -EBUSY: The PMU overflow interrupt is already set
|
||||||
|
-ENXIO: The overflow interrupt not set when attempting to get it
|
||||||
|
-ENODEV: PMUv3 not supported
|
||||||
|
-EINVAL: Invalid PMU overflow interrupt number supplied
|
||||||
|
|
||||||
|
A value describing the PMUv3 (Performance Monitor Unit v3) overflow interrupt
|
||||||
|
number for this vcpu. This interrupt could be a PPI or SPI, but 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.
|
||||||
|
|
||||||
|
1.2 ATTRIBUTE: KVM_ARM_VCPU_PMU_V3_INIT
|
||||||
|
Parameters: no additional parameter in kvm_device_attr.addr
|
||||||
|
Returns: -ENODEV: PMUv3 not supported
|
||||||
|
-ENXIO: PMUv3 not properly configured as required prior to calling this
|
||||||
|
attribute
|
||||||
|
-EBUSY: PMUv3 already initialized
|
||||||
|
|
||||||
|
Request the initialization of the PMUv3.
|
||||||
|
|||||||
@@ -287,5 +287,20 @@ static inline void kvm_arm_init_debug(void) {}
|
|||||||
static inline void kvm_arm_setup_debug(struct kvm_vcpu *vcpu) {}
|
static inline void kvm_arm_setup_debug(struct kvm_vcpu *vcpu) {}
|
||||||
static inline void kvm_arm_clear_debug(struct kvm_vcpu *vcpu) {}
|
static inline void kvm_arm_clear_debug(struct kvm_vcpu *vcpu) {}
|
||||||
static inline void kvm_arm_reset_debug_ptr(struct kvm_vcpu *vcpu) {}
|
static inline void kvm_arm_reset_debug_ptr(struct kvm_vcpu *vcpu) {}
|
||||||
|
static inline int kvm_arm_vcpu_arch_set_attr(struct kvm_vcpu *vcpu,
|
||||||
|
struct kvm_device_attr *attr)
|
||||||
|
{
|
||||||
|
return -ENXIO;
|
||||||
|
}
|
||||||
|
static inline int kvm_arm_vcpu_arch_get_attr(struct kvm_vcpu *vcpu,
|
||||||
|
struct kvm_device_attr *attr)
|
||||||
|
{
|
||||||
|
return -ENXIO;
|
||||||
|
}
|
||||||
|
static inline int kvm_arm_vcpu_arch_has_attr(struct kvm_vcpu *vcpu,
|
||||||
|
struct kvm_device_attr *attr)
|
||||||
|
{
|
||||||
|
return -ENXIO;
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* __ARM_KVM_HOST_H__ */
|
#endif /* __ARM_KVM_HOST_H__ */
|
||||||
|
|||||||
@@ -835,6 +835,7 @@ static int kvm_arm_vcpu_set_attr(struct kvm_vcpu *vcpu,
|
|||||||
|
|
||||||
switch (attr->group) {
|
switch (attr->group) {
|
||||||
default:
|
default:
|
||||||
|
ret = kvm_arm_vcpu_arch_set_attr(vcpu, attr);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -848,6 +849,7 @@ static int kvm_arm_vcpu_get_attr(struct kvm_vcpu *vcpu,
|
|||||||
|
|
||||||
switch (attr->group) {
|
switch (attr->group) {
|
||||||
default:
|
default:
|
||||||
|
ret = kvm_arm_vcpu_arch_get_attr(vcpu, attr);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -861,6 +863,7 @@ static int kvm_arm_vcpu_has_attr(struct kvm_vcpu *vcpu,
|
|||||||
|
|
||||||
switch (attr->group) {
|
switch (attr->group) {
|
||||||
default:
|
default:
|
||||||
|
ret = kvm_arm_vcpu_arch_has_attr(vcpu, attr);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -361,6 +361,12 @@ void kvm_arm_init_debug(void);
|
|||||||
void kvm_arm_setup_debug(struct kvm_vcpu *vcpu);
|
void kvm_arm_setup_debug(struct kvm_vcpu *vcpu);
|
||||||
void kvm_arm_clear_debug(struct kvm_vcpu *vcpu);
|
void kvm_arm_clear_debug(struct kvm_vcpu *vcpu);
|
||||||
void kvm_arm_reset_debug_ptr(struct kvm_vcpu *vcpu);
|
void kvm_arm_reset_debug_ptr(struct kvm_vcpu *vcpu);
|
||||||
|
int kvm_arm_vcpu_arch_set_attr(struct kvm_vcpu *vcpu,
|
||||||
|
struct kvm_device_attr *attr);
|
||||||
|
int kvm_arm_vcpu_arch_get_attr(struct kvm_vcpu *vcpu,
|
||||||
|
struct kvm_device_attr *attr);
|
||||||
|
int kvm_arm_vcpu_arch_has_attr(struct kvm_vcpu *vcpu,
|
||||||
|
struct kvm_device_attr *attr);
|
||||||
|
|
||||||
/* #define kvm_call_hyp(f, ...) __kvm_call_hyp(kvm_ksym_ref(f), ##__VA_ARGS__) */
|
/* #define kvm_call_hyp(f, ...) __kvm_call_hyp(kvm_ksym_ref(f), ##__VA_ARGS__) */
|
||||||
|
|
||||||
|
|||||||
@@ -205,6 +205,11 @@ struct kvm_arch_memory_slot {
|
|||||||
#define KVM_DEV_ARM_VGIC_GRP_CTRL 4
|
#define KVM_DEV_ARM_VGIC_GRP_CTRL 4
|
||||||
#define KVM_DEV_ARM_VGIC_CTRL_INIT 0
|
#define KVM_DEV_ARM_VGIC_CTRL_INIT 0
|
||||||
|
|
||||||
|
/* Device Control API on vcpu fd */
|
||||||
|
#define KVM_ARM_VCPU_PMU_V3_CTRL 0
|
||||||
|
#define KVM_ARM_VCPU_PMU_V3_IRQ 0
|
||||||
|
#define KVM_ARM_VCPU_PMU_V3_INIT 1
|
||||||
|
|
||||||
/* KVM_IRQ_LINE irq field index values */
|
/* KVM_IRQ_LINE irq field index values */
|
||||||
#define KVM_ARM_IRQ_TYPE_SHIFT 24
|
#define KVM_ARM_IRQ_TYPE_SHIFT 24
|
||||||
#define KVM_ARM_IRQ_TYPE_MASK 0xff
|
#define KVM_ARM_IRQ_TYPE_MASK 0xff
|
||||||
|
|||||||
@@ -380,3 +380,54 @@ int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu,
|
|||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int kvm_arm_vcpu_arch_set_attr(struct kvm_vcpu *vcpu,
|
||||||
|
struct kvm_device_attr *attr)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
switch (attr->group) {
|
||||||
|
case KVM_ARM_VCPU_PMU_V3_CTRL:
|
||||||
|
ret = kvm_arm_pmu_v3_set_attr(vcpu, attr);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = -ENXIO;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int kvm_arm_vcpu_arch_get_attr(struct kvm_vcpu *vcpu,
|
||||||
|
struct kvm_device_attr *attr)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
switch (attr->group) {
|
||||||
|
case KVM_ARM_VCPU_PMU_V3_CTRL:
|
||||||
|
ret = kvm_arm_pmu_v3_get_attr(vcpu, attr);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = -ENXIO;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int kvm_arm_vcpu_arch_has_attr(struct kvm_vcpu *vcpu,
|
||||||
|
struct kvm_device_attr *attr)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
switch (attr->group) {
|
||||||
|
case KVM_ARM_VCPU_PMU_V3_CTRL:
|
||||||
|
ret = kvm_arm_pmu_v3_has_attr(vcpu, attr);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = -ENXIO;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ struct kvm_pmu {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#define kvm_arm_pmu_v3_ready(v) ((v)->arch.pmu.ready)
|
#define kvm_arm_pmu_v3_ready(v) ((v)->arch.pmu.ready)
|
||||||
|
#define kvm_arm_pmu_irq_initialized(v) ((v)->arch.pmu.irq_num >= VGIC_NR_SGIS)
|
||||||
u64 kvm_pmu_get_counter_value(struct kvm_vcpu *vcpu, u64 select_idx);
|
u64 kvm_pmu_get_counter_value(struct kvm_vcpu *vcpu, u64 select_idx);
|
||||||
void kvm_pmu_set_counter_value(struct kvm_vcpu *vcpu, u64 select_idx, u64 val);
|
void kvm_pmu_set_counter_value(struct kvm_vcpu *vcpu, u64 select_idx, u64 val);
|
||||||
u64 kvm_pmu_valid_counter_mask(struct kvm_vcpu *vcpu);
|
u64 kvm_pmu_valid_counter_mask(struct kvm_vcpu *vcpu);
|
||||||
@@ -54,11 +55,18 @@ void kvm_pmu_handle_pmcr(struct kvm_vcpu *vcpu, u64 val);
|
|||||||
void kvm_pmu_set_counter_event_type(struct kvm_vcpu *vcpu, u64 data,
|
void kvm_pmu_set_counter_event_type(struct kvm_vcpu *vcpu, u64 data,
|
||||||
u64 select_idx);
|
u64 select_idx);
|
||||||
bool kvm_arm_support_pmu_v3(void);
|
bool kvm_arm_support_pmu_v3(void);
|
||||||
|
int kvm_arm_pmu_v3_set_attr(struct kvm_vcpu *vcpu,
|
||||||
|
struct kvm_device_attr *attr);
|
||||||
|
int kvm_arm_pmu_v3_get_attr(struct kvm_vcpu *vcpu,
|
||||||
|
struct kvm_device_attr *attr);
|
||||||
|
int kvm_arm_pmu_v3_has_attr(struct kvm_vcpu *vcpu,
|
||||||
|
struct kvm_device_attr *attr);
|
||||||
#else
|
#else
|
||||||
struct kvm_pmu {
|
struct kvm_pmu {
|
||||||
};
|
};
|
||||||
|
|
||||||
#define kvm_arm_pmu_v3_ready(v) (false)
|
#define kvm_arm_pmu_v3_ready(v) (false)
|
||||||
|
#define kvm_arm_pmu_irq_initialized(v) (false)
|
||||||
static inline u64 kvm_pmu_get_counter_value(struct kvm_vcpu *vcpu,
|
static inline u64 kvm_pmu_get_counter_value(struct kvm_vcpu *vcpu,
|
||||||
u64 select_idx)
|
u64 select_idx)
|
||||||
{
|
{
|
||||||
@@ -82,6 +90,21 @@ static inline void kvm_pmu_handle_pmcr(struct kvm_vcpu *vcpu, u64 val) {}
|
|||||||
static inline void kvm_pmu_set_counter_event_type(struct kvm_vcpu *vcpu,
|
static inline void kvm_pmu_set_counter_event_type(struct kvm_vcpu *vcpu,
|
||||||
u64 data, u64 select_idx) {}
|
u64 data, u64 select_idx) {}
|
||||||
static inline bool kvm_arm_support_pmu_v3(void) { return false; }
|
static inline bool kvm_arm_support_pmu_v3(void) { return false; }
|
||||||
|
static inline int kvm_arm_pmu_v3_set_attr(struct kvm_vcpu *vcpu,
|
||||||
|
struct kvm_device_attr *attr)
|
||||||
|
{
|
||||||
|
return -ENXIO;
|
||||||
|
}
|
||||||
|
static inline int kvm_arm_pmu_v3_get_attr(struct kvm_vcpu *vcpu,
|
||||||
|
struct kvm_device_attr *attr)
|
||||||
|
{
|
||||||
|
return -ENXIO;
|
||||||
|
}
|
||||||
|
static inline int kvm_arm_pmu_v3_has_attr(struct kvm_vcpu *vcpu,
|
||||||
|
struct kvm_device_attr *attr)
|
||||||
|
{
|
||||||
|
return -ENXIO;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
#include <linux/kvm.h>
|
#include <linux/kvm.h>
|
||||||
#include <linux/kvm_host.h>
|
#include <linux/kvm_host.h>
|
||||||
#include <linux/perf_event.h>
|
#include <linux/perf_event.h>
|
||||||
|
#include <linux/uaccess.h>
|
||||||
#include <asm/kvm_emulate.h>
|
#include <asm/kvm_emulate.h>
|
||||||
#include <kvm/arm_pmu.h>
|
#include <kvm/arm_pmu.h>
|
||||||
#include <kvm/arm_vgic.h>
|
#include <kvm/arm_vgic.h>
|
||||||
@@ -415,3 +416,114 @@ bool kvm_arm_support_pmu_v3(void)
|
|||||||
*/
|
*/
|
||||||
return (perf_num_counters() > 0);
|
return (perf_num_counters() > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) ||
|
||||||
|
!kvm_arm_pmu_irq_initialized(vcpu))
|
||||||
|
return -ENXIO;
|
||||||
|
|
||||||
|
if (kvm_arm_pmu_v3_ready(vcpu))
|
||||||
|
return -EBUSY;
|
||||||
|
|
||||||
|
kvm_pmu_vcpu_reset(vcpu);
|
||||||
|
vcpu->arch.pmu.ready = true;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool irq_is_valid(struct kvm *kvm, int irq, bool is_ppi)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
struct kvm_vcpu *vcpu;
|
||||||
|
|
||||||
|
kvm_for_each_vcpu(i, vcpu, kvm) {
|
||||||
|
if (!kvm_arm_pmu_irq_initialized(vcpu))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (is_ppi) {
|
||||||
|
if (vcpu->arch.pmu.irq_num != irq)
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
if (vcpu->arch.pmu.irq_num == irq)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
if (!test_bit(KVM_ARM_VCPU_PMU_V3, vcpu->arch.features))
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
if (get_user(irq, uaddr))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The PMU overflow interrupt could be a PPI or SPI, but 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.
|
||||||
|
*/
|
||||||
|
if (irq < VGIC_NR_SGIS || irq >= vcpu->kvm->arch.vgic.nr_irqs ||
|
||||||
|
!irq_is_valid(vcpu->kvm, irq, irq < VGIC_NR_PRIVATE_IRQS))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (kvm_arm_pmu_irq_initialized(vcpu))
|
||||||
|
return -EBUSY;
|
||||||
|
|
||||||
|
kvm_debug("Set kvm ARM PMU irq: %d\n", 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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user