psr_cmt_op
psr_cat_op
};
+allow dom0_t xen_t:xen2 {
+ pmu_ctrl
+};
allow dom0_t xen_t:mmu memorymap;
# Allow dom0 to use these domctls on itself. For domctls acting on other
if ( is_hvm_domain(prevd) )
{
if (prev != next)
- vpmu_save(prev);
+ vpmu_switch_from(prev);
if ( !list_empty(&prev->arch.hvm_vcpu.tm_list) )
pt_save_timer(prev);
if (is_hvm_domain(nextd) && (prev != next) )
/* Must be done with interrupts enabled */
- vpmu_load(next);
+ vpmu_switch_to(next);
context_saved(prev);
.arch_vpmu_dump = amd_vpmu_dump
};
-int svm_vpmu_initialise(struct vcpu *v, unsigned int vpmu_flags)
+int svm_vpmu_initialise(struct vcpu *v)
{
struct vpmu_struct *vpmu = vcpu_vpmu(v);
uint8_t family = current_cpu_data.x86;
int ret = 0;
/* vpmu enabled? */
- if ( !vpmu_flags )
+ if ( vpmu_mode == XENPMU_MODE_OFF )
return 0;
switch ( family )
return 1;
}
-static int core2_vpmu_initialise(struct vcpu *v, unsigned int vpmu_flags)
+static int core2_vpmu_initialise(struct vcpu *v)
{
struct vpmu_struct *vpmu = vcpu_vpmu(v);
u64 msr_content;
static bool_t ds_warned;
- if ( !(vpmu_flags & VPMU_BOOT_BTS) )
+ if ( !(vpmu_features & XENPMU_FEATURE_INTEL_BTS) )
goto func_out;
/* Check the 'Debug Store' feature in the CPUID.EAX[1]:EDX[21] */
while ( boot_cpu_has(X86_FEATURE_DS) )
.do_cpuid = core2_no_vpmu_do_cpuid,
};
-int vmx_vpmu_initialise(struct vcpu *v, unsigned int vpmu_flags)
+int vmx_vpmu_initialise(struct vcpu *v)
{
struct vpmu_struct *vpmu = vcpu_vpmu(v);
uint8_t family = current_cpu_data.x86;
int ret = 0;
vpmu->arch_vpmu_ops = &core2_no_vpmu_ops;
- if ( !vpmu_flags )
+ if ( vpmu_mode == XENPMU_MODE_OFF )
return 0;
if ( family == 6 )
/* next gen Xeon Phi */
case 0x57:
- ret = core2_vpmu_initialise(v, vpmu_flags);
+ ret = core2_vpmu_initialise(v);
if ( !ret )
vpmu->arch_vpmu_ops = &core2_vpmu_ops;
return ret;
#include <xen/config.h>
#include <xen/sched.h>
#include <xen/xenoprof.h>
+#include <xen/event.h>
+#include <xen/guest_access.h>
#include <asm/regs.h>
#include <asm/types.h>
#include <asm/msr.h>
#include <asm/hvm/svm/vmcb.h>
#include <asm/apic.h>
#include <public/pmu.h>
+#include <xsm/xsm.h>
#include <compat/pmu.h>
CHECK_pmu_cntr_pair;
CHECK_pmu_data;
+CHECK_pmu_params;
/*
* "vpmu" : vpmu generally enabled
* "vpmu=bts" : vpmu enabled and Intel BTS feature switched on.
*/
static unsigned int __read_mostly opt_vpmu_enabled;
+unsigned int __read_mostly vpmu_mode = XENPMU_MODE_OFF;
+unsigned int __read_mostly vpmu_features = 0;
static void parse_vpmu_param(char *s);
custom_param("vpmu", parse_vpmu_param);
+static DEFINE_SPINLOCK(vpmu_lock);
+static unsigned vpmu_count;
+
static DEFINE_PER_CPU(struct vcpu *, last_vcpu);
static void __init parse_vpmu_param(char *s)
break;
default:
if ( !strcmp(s, "bts") )
- opt_vpmu_enabled |= VPMU_BOOT_BTS;
+ vpmu_features |= XENPMU_FEATURE_INTEL_BTS;
else if ( *s )
{
printk("VPMU: unknown flag: %s - vpmu disabled!\n", s);
}
/* fall through */
case 1:
- opt_vpmu_enabled |= VPMU_BOOT_ENABLED;
+ /* Default VPMU mode */
+ vpmu_mode = XENPMU_MODE_SELF;
+ opt_vpmu_enabled = 1;
break;
}
}
{
struct vpmu_struct *vpmu;
- if ( !opt_vpmu_enabled )
+ if ( vpmu_mode == XENPMU_MODE_OFF )
return;
vpmu = vcpu_vpmu(current);
{
struct vpmu_struct *vpmu = vcpu_vpmu(current);
+ if ( vpmu_mode == XENPMU_MODE_OFF )
+ return 0;
+
if ( vpmu->arch_vpmu_ops && vpmu->arch_vpmu_ops->do_wrmsr )
return vpmu->arch_vpmu_ops->do_wrmsr(msr, msr_content, supported);
return 0;
{
struct vpmu_struct *vpmu = vcpu_vpmu(current);
+ if ( vpmu_mode == XENPMU_MODE_OFF )
+ {
+ *msr_content = 0;
+ return 0;
+ }
+
if ( vpmu->arch_vpmu_ops && vpmu->arch_vpmu_ops->do_rdmsr )
return vpmu->arch_vpmu_ops->do_rdmsr(msr, msr_content);
return 0;
ASSERT(!vpmu->flags && !vpmu->context);
+ /*
+ * Count active VPMUs so that we won't try to change vpmu_mode while
+ * they are in use.
+ */
+ spin_lock(&vpmu_lock);
+ vpmu_count++;
+ spin_unlock(&vpmu_lock);
+
switch ( vendor )
{
case X86_VENDOR_AMD:
- ret = svm_vpmu_initialise(v, opt_vpmu_enabled);
+ ret = svm_vpmu_initialise(v);
break;
case X86_VENDOR_INTEL:
- ret = vmx_vpmu_initialise(v, opt_vpmu_enabled);
+ ret = vmx_vpmu_initialise(v);
break;
default:
- if ( opt_vpmu_enabled )
+ if ( vpmu_mode != XENPMU_MODE_OFF )
{
printk(XENLOG_G_WARNING "VPMU: Unknown CPU vendor %d. "
"Disabling VPMU\n", vendor);
opt_vpmu_enabled = 0;
+ vpmu_mode = XENPMU_MODE_OFF;
}
- return;
+ return; /* Don't bother restoring vpmu_count, VPMU is off forever */
}
if ( ret )
printk(XENLOG_G_WARNING "VPMU: Initialization failed for %pv\n", v);
+
+ /* Intel needs to initialize VPMU ops even if VPMU is not in use */
+ if ( ret || (vpmu_mode == XENPMU_MODE_OFF) )
+ {
+ spin_lock(&vpmu_lock);
+ vpmu_count--;
+ spin_unlock(&vpmu_lock);
+ }
}
static void vpmu_clear_last(void *arg)
if ( vpmu->arch_vpmu_ops && vpmu->arch_vpmu_ops->arch_vpmu_destroy )
vpmu->arch_vpmu_ops->arch_vpmu_destroy(v);
+
+ spin_lock(&vpmu_lock);
+ vpmu_count--;
+ spin_unlock(&vpmu_lock);
}
/* Dump some vpmu informations on console. Used in keyhandler dump_domains(). */
vpmu->arch_vpmu_ops->arch_vpmu_dump(v);
}
+long do_xenpmu_op(unsigned int op, XEN_GUEST_HANDLE_PARAM(xen_pmu_params_t) arg)
+{
+ int ret;
+ struct xen_pmu_params pmu_params = {.val = 0};
+
+ if ( !opt_vpmu_enabled )
+ return -EOPNOTSUPP;
+
+ ret = xsm_pmu_op(XSM_OTHER, current->domain, op);
+ if ( ret )
+ return ret;
+
+ /* Check major version when parameters are specified */
+ switch ( op )
+ {
+ case XENPMU_mode_set:
+ case XENPMU_feature_set:
+ if ( copy_from_guest(&pmu_params, arg, 1) )
+ return -EFAULT;
+
+ if ( pmu_params.version.maj != XENPMU_VER_MAJ )
+ return -EINVAL;
+ }
+
+ switch ( op )
+ {
+ case XENPMU_mode_set:
+ {
+ if ( (pmu_params.val & ~(XENPMU_MODE_SELF | XENPMU_MODE_HV)) ||
+ (hweight64(pmu_params.val) > 1) )
+ return -EINVAL;
+
+ /* 32-bit dom0 can only sample itself. */
+ if ( is_pv_32bit_vcpu(current) && (pmu_params.val & XENPMU_MODE_HV) )
+ return -EINVAL;
+
+ spin_lock(&vpmu_lock);
+
+ /*
+ * We can always safely switch between XENPMU_MODE_SELF and
+ * XENPMU_MODE_HV while other VPMUs are active.
+ */
+ if ( (vpmu_count == 0) ||
+ ((vpmu_mode ^ pmu_params.val) ==
+ (XENPMU_MODE_SELF | XENPMU_MODE_HV)) )
+ vpmu_mode = pmu_params.val;
+ else if ( vpmu_mode != pmu_params.val )
+ {
+ printk(XENLOG_WARNING
+ "VPMU: Cannot change mode while active VPMUs exist\n");
+ ret = -EBUSY;
+ }
+
+ spin_unlock(&vpmu_lock);
+
+ break;
+ }
+
+ case XENPMU_mode_get:
+ memset(&pmu_params, 0, sizeof(pmu_params));
+ pmu_params.val = vpmu_mode;
+
+ pmu_params.version.maj = XENPMU_VER_MAJ;
+ pmu_params.version.min = XENPMU_VER_MIN;
+
+ if ( copy_to_guest(arg, &pmu_params, 1) )
+ ret = -EFAULT;
+
+ break;
+
+ case XENPMU_feature_set:
+ if ( pmu_params.val & ~XENPMU_FEATURE_INTEL_BTS )
+ return -EINVAL;
+
+ spin_lock(&vpmu_lock);
+
+ if ( (vpmu_count == 0) || (vpmu_features == pmu_params.val) )
+ vpmu_features = pmu_params.val;
+ else
+ {
+ printk(XENLOG_WARNING "VPMU: Cannot change features while"
+ " active VPMUs exist\n");
+ ret = -EBUSY;
+ }
+
+ spin_unlock(&vpmu_lock);
+
+ break;
+
+ case XENPMU_feature_get:
+ pmu_params.val = vpmu_features;
+ if ( copy_field_to_guest(arg, &pmu_params, val) )
+ ret = -EFAULT;
+
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
static int __init vpmu_init(void)
{
/* NMI watchdog uses LVTPC and HW counter */
{
printk(XENLOG_WARNING "NMI watchdog is enabled. Turning VPMU off.\n");
opt_vpmu_enabled = 0;
+ vpmu_mode = XENPMU_MODE_OFF;
}
return 0;
.quad do_domctl
.quad compat_kexec_op
.quad do_tmem_op
+ .quad do_ni_hypercall /* reserved for XenClient */
+ .quad do_xenpmu_op /* 40 */
.rept __HYPERVISOR_arch_0-((.-compat_hypercall_table)/8)
.quad compat_ni_hypercall
.endr
.byte 1 /* do_domctl */
.byte 2 /* compat_kexec_op */
.byte 1 /* do_tmem_op */
+ .byte 0 /* reserved for XenClient */
+ .byte 2 /* do_xenpmu_op */ /* 40 */
.rept __HYPERVISOR_arch_0-(.-compat_hypercall_args_table)
.byte 0 /* compat_ni_hypercall */
.endr
.quad do_domctl
.quad do_kexec_op
.quad do_tmem_op
+ .quad do_ni_hypercall /* reserved for XenClient */
+ .quad do_xenpmu_op /* 40 */
.rept __HYPERVISOR_arch_0-((.-hypercall_table)/8)
.quad do_ni_hypercall
.endr
.byte 1 /* do_domctl */
.byte 2 /* do_kexec */
.byte 1 /* do_tmem_op */
+ .byte 0 /* reserved for XenClient */
+ .byte 2 /* do_xenpmu_op */ /* 40 */
.rept __HYPERVISOR_arch_0-(.-hypercall_args_table)
.byte 0 /* do_ni_hypercall */
.endr
#include <public/pmu.h>
-/*
- * Flag bits given as a string on the hypervisor boot parameter 'vpmu'.
- * See arch/x86/hvm/vpmu.c.
- */
-#define VPMU_BOOT_ENABLED 0x1 /* vpmu generally enabled. */
-#define VPMU_BOOT_BTS 0x2 /* Intel BTS feature wanted. */
-
#define vcpu_vpmu(vcpu) (&(vcpu)->arch.vpmu)
#define vpmu_vcpu(vpmu) container_of((vpmu), struct vcpu, arch.vpmu)
void (*arch_vpmu_dump)(const struct vcpu *);
};
-int vmx_vpmu_initialise(struct vcpu *, unsigned int flags);
-int svm_vpmu_initialise(struct vcpu *, unsigned int flags);
+int vmx_vpmu_initialise(struct vcpu *);
+int svm_vpmu_initialise(struct vcpu *);
struct vpmu_struct {
u32 flags;
extern int acquire_pmu_ownership(int pmu_ownership);
extern void release_pmu_ownership(int pmu_ownership);
+extern unsigned int vpmu_mode;
+extern unsigned int vpmu_features;
+
+/* Context switch */
+static inline void vpmu_switch_from(struct vcpu *prev)
+{
+ if ( vpmu_mode & (XENPMU_MODE_SELF | XENPMU_MODE_HV) )
+ vpmu_save(prev);
+}
+
+static inline void vpmu_switch_to(struct vcpu *next)
+{
+ if ( vpmu_mode & (XENPMU_MODE_SELF | XENPMU_MODE_HV) )
+ vpmu_load(next);
+}
+
#endif /* __ASM_X86_HVM_VPMU_H_*/
#define XENPMU_VER_MAJ 0
#define XENPMU_VER_MIN 1
+/*
+ * ` enum neg_errnoval
+ * ` HYPERVISOR_xenpmu_op(enum xenpmu_op cmd, struct xenpmu_params *args);
+ *
+ * @cmd == XENPMU_* (PMU operation)
+ * @args == struct xenpmu_params
+ */
+/* ` enum xenpmu_op { */
+#define XENPMU_mode_get 0 /* Also used for getting PMU version */
+#define XENPMU_mode_set 1
+#define XENPMU_feature_get 2
+#define XENPMU_feature_set 3
+/* ` } */
+
+/* Parameters structure for HYPERVISOR_xenpmu_op call */
+struct xen_pmu_params {
+ /* IN/OUT parameters */
+ struct {
+ uint32_t maj;
+ uint32_t min;
+ } version;
+ uint64_t val;
+
+ /* IN parameters */
+ uint32_t vcpu;
+ uint32_t pad;
+};
+typedef struct xen_pmu_params xen_pmu_params_t;
+DEFINE_XEN_GUEST_HANDLE(xen_pmu_params_t);
+
+/* PMU modes:
+ * - XENPMU_MODE_OFF: No PMU virtualization
+ * - XENPMU_MODE_SELF: Guests can profile themselves
+ * - XENPMU_MODE_HV: Guests can profile themselves, dom0 profiles
+ * itself and Xen
+ */
+#define XENPMU_MODE_OFF 0
+#define XENPMU_MODE_SELF (1<<0)
+#define XENPMU_MODE_HV (1<<1)
+
+/*
+ * PMU features:
+ * - XENPMU_FEATURE_INTEL_BTS: Intel BTS support (ignored on AMD)
+ */
+#define XENPMU_FEATURE_INTEL_BTS 1
+
/*
* Shared PMU data between hypervisor and PV(H) domains.
*
#define __HYPERVISOR_kexec_op 37
#define __HYPERVISOR_tmem_op 38
#define __HYPERVISOR_xc_reserved_op 39 /* reserved for XenClient */
+#define __HYPERVISOR_xenpmu_op 40
/* Architecture-specific hypercall definitions. */
#define __HYPERVISOR_arch_0 48
#include <public/event_channel.h>
#include <public/tmem.h>
#include <public/version.h>
+#include <public/pmu.h>
#include <asm/hypercall.h>
#include <xsm/xsm.h>
extern long
do_xenoprof_op(int op, XEN_GUEST_HANDLE_PARAM(void) arg);
+extern long
+do_xenpmu_op(unsigned int op, XEN_GUEST_HANDLE_PARAM(xen_pmu_params_t) arg);
+
#ifdef CONFIG_COMPAT
extern int
? xenpf_pcpu_version platform.h
? xenpf_resource_entry platform.h
? pmu_data pmu.h
+? pmu_params pmu.h
! sched_poll sched.h
? sched_remote_shutdown sched.h
? sched_shutdown sched.h
return xsm_default_action(action, current->domain, d);
}
+static XSM_INLINE int xsm_pmu_op (XSM_DEFAULT_ARG struct domain *d, int op)
+{
+ XSM_ASSERT_ACTION(XSM_OTHER);
+ switch ( op )
+ {
+ case XENPMU_mode_set:
+ case XENPMU_mode_get:
+ case XENPMU_feature_set:
+ case XENPMU_feature_get:
+ return xsm_default_action(XSM_PRIV, d, current->domain);
+ default:
+ return -EPERM;
+ }
+}
+
#endif /* CONFIG_X86 */
int (*priv_mapping) (struct domain *d, struct domain *t);
int (*ioport_permission) (struct domain *d, uint32_t s, uint32_t e, uint8_t allow);
int (*ioport_mapping) (struct domain *d, uint32_t s, uint32_t e, uint8_t allow);
+ int (*pmu_op) (struct domain *d, unsigned int op);
#endif
};
return xsm_ops->ioport_mapping(d, s, e, allow);
}
+static inline int xsm_pmu_op (xsm_default_t def, struct domain *d, int op)
+{
+ return xsm_ops->pmu_op(d, op);
+}
+
#endif /* CONFIG_X86 */
#endif /* XSM_NO_WRAPPERS */
set_to_dummy_if_null(ops, priv_mapping);
set_to_dummy_if_null(ops, ioport_permission);
set_to_dummy_if_null(ops, ioport_mapping);
+ set_to_dummy_if_null(ops, pmu_op);
#endif
}
{
return domain_has_perm(d, t, SECCLASS_MMU, MMU__TARGET_HACK);
}
+
+static int flask_pmu_op (struct domain *d, unsigned int op)
+{
+ u32 dsid = domain_sid(d);
+
+ switch ( op )
+ {
+ case XENPMU_mode_set:
+ case XENPMU_mode_get:
+ case XENPMU_feature_set:
+ case XENPMU_feature_get:
+ return avc_has_perm(dsid, SECINITSID_XEN, SECCLASS_XEN2,
+ XEN2__PMU_CTRL, NULL);
+ default:
+ return -EPERM;
+ }
+}
#endif /* CONFIG_X86 */
long do_flask_op(XEN_GUEST_HANDLE_PARAM(xsm_op_t) u_flask_op);
.priv_mapping = flask_priv_mapping,
.ioport_permission = flask_ioport_permission,
.ioport_mapping = flask_ioport_mapping,
+ .pmu_op = flask_pmu_op,
#endif
};
psr_cat_op
# XENPF_get_symbol
get_symbol
+# PMU control
+ pmu_ctrl
}
# Classes domain and domain2 consist of operations that a domain performs on