Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 338b522c authored by Kan Liang's avatar Kan Liang Committed by Ingo Molnar
Browse files

perf/x86/intel: Protect LBR and extra_regs against KVM lying



With -cpu host, KVM reports LBR and extra_regs support, if the host has
support.

When the guest perf driver tries to access LBR or extra_regs MSR,
it #GPs all MSR accesses,since KVM doesn't handle LBR and extra_regs support.
So check the related MSRs access right once at initialization time to avoid
the error access at runtime.

For reproducing the issue, please build the kernel with CONFIG_KVM_INTEL = y
(for host kernel).
And CONFIG_PARAVIRT = n and CONFIG_KVM_GUEST = n (for guest kernel).
Start the guest with -cpu host.
Run perf record with --branch-any or --branch-filter in guest to trigger LBR
Run perf stat offcore events (E.g. LLC-loads/LLC-load-misses ...) in guest to
trigger offcore_rsp #GP

Signed-off-by: default avatarKan Liang <kan.liang@intel.com>
Signed-off-by: default avatarPeter Zijlstra <peterz@infradead.org>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Maria Dimakopoulou <maria.n.dimakopoulou@gmail.com>
Cc: Mark Davies <junk@eslaf.co.uk>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Stephane Eranian <eranian@google.com>
Cc: Yan, Zheng <zheng.z.yan@intel.com>
Link: http://lkml.kernel.org/r/1405365957-20202-1-git-send-email-kan.liang@intel.com


Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parent 4a1c0f26
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -118,6 +118,9 @@ static int x86_pmu_extra_regs(u64 config, struct perf_event *event)
			continue;
		if (event->attr.config1 & ~er->valid_mask)
			return -EINVAL;
		/* Check if the extra msrs can be safely accessed*/
		if (!er->extra_msr_access)
			return -ENXIO;

		reg->idx = er->idx;
		reg->config = event->attr.config1;
+7 −5
Original line number Diff line number Diff line
@@ -295,6 +295,7 @@ struct extra_reg {
	u64			config_mask;
	u64			valid_mask;
	int			idx;  /* per_xxx->regs[] reg index */
	bool			extra_msr_access;
};

#define EVENT_EXTRA_REG(e, ms, m, vm, i) {	\
@@ -303,6 +304,7 @@ struct extra_reg {
	.config_mask = (m),		\
	.valid_mask = (vm),		\
	.idx = EXTRA_REG_##i,		\
	.extra_msr_access = true,	\
	}

#define INTEL_EVENT_EXTRA_REG(event, msr, vm, idx)	\
+65 −1
Original line number Diff line number Diff line
@@ -2182,6 +2182,41 @@ static void intel_snb_check_microcode(void)
	}
}

/*
 * Under certain circumstances, access certain MSR may cause #GP.
 * The function tests if the input MSR can be safely accessed.
 */
static bool check_msr(unsigned long msr, u64 mask)
{
	u64 val_old, val_new, val_tmp;

	/*
	 * Read the current value, change it and read it back to see if it
	 * matches, this is needed to detect certain hardware emulators
	 * (qemu/kvm) that don't trap on the MSR access and always return 0s.
	 */
	if (rdmsrl_safe(msr, &val_old))
		return false;

	/*
	 * Only change the bits which can be updated by wrmsrl.
	 */
	val_tmp = val_old ^ mask;
	if (wrmsrl_safe(msr, val_tmp) ||
	    rdmsrl_safe(msr, &val_new))
		return false;

	if (val_new != val_tmp)
		return false;

	/* Here it's sure that the MSR can be safely accessed.
	 * Restore the old value and return.
	 */
	wrmsrl(msr, val_old);

	return true;
}

static __init void intel_sandybridge_quirk(void)
{
	x86_pmu.check_microcode = intel_snb_check_microcode;
@@ -2271,7 +2306,8 @@ __init int intel_pmu_init(void)
	union cpuid10_ebx ebx;
	struct event_constraint *c;
	unsigned int unused;
	int version;
	struct extra_reg *er;
	int version, i;

	if (!cpu_has(&boot_cpu_data, X86_FEATURE_ARCH_PERFMON)) {
		switch (boot_cpu_data.x86) {
@@ -2577,6 +2613,34 @@ __init int intel_pmu_init(void)
		}
	}

	/*
	 * Access LBR MSR may cause #GP under certain circumstances.
	 * E.g. KVM doesn't support LBR MSR
	 * Check all LBT MSR here.
	 * Disable LBR access if any LBR MSRs can not be accessed.
	 */
	if (x86_pmu.lbr_nr && !check_msr(x86_pmu.lbr_tos, 0x3UL))
		x86_pmu.lbr_nr = 0;
	for (i = 0; i < x86_pmu.lbr_nr; i++) {
		if (!(check_msr(x86_pmu.lbr_from + i, 0xffffUL) &&
		      check_msr(x86_pmu.lbr_to + i, 0xffffUL)))
			x86_pmu.lbr_nr = 0;
	}

	/*
	 * Access extra MSR may cause #GP under certain circumstances.
	 * E.g. KVM doesn't support offcore event
	 * Check all extra_regs here.
	 */
	if (x86_pmu.extra_regs) {
		for (er = x86_pmu.extra_regs; er->msr; er++) {
			er->extra_msr_access = check_msr(er->msr, 0x1ffUL);
			/* Disable LBR select mapping */
			if ((er->idx == EXTRA_REG_LBR) && !er->extra_msr_access)
				x86_pmu.lbr_sel_map = NULL;
		}
	}

	/* Support full width counters using alternative MSR range */
	if (x86_pmu.intel_cap.full_width_write) {
		x86_pmu.max_period = x86_pmu.cntval_mask;