/*
* Intel(R) Processor Trace PMU driver for perf
* Copyright (c) 2013-2014, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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.
*
* Intel PT is specified in the Intel Architecture Instruction Set Extensions
* Programming Reference:
* http://software.intel.com/en-us/intel-isa-extensions
*/
#undef DEBUG
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <asm/perf_event.h>
#include <asm/insn.h>
#include <asm/io.h>
#include "perf_event.h"
#include "intel_pt.h"
static DEFINE_PER_CPU(struct pt, pt_ctx);
static struct pt_pmu pt_pmu;
enum cpuid_regs {
CR_EAX = 0,
CR_ECX,
CR_EDX,
CR_EBX
};
/*
* Capabilities of Intel PT hardware, such as number of address bits or
* supported output schemes, are cached and exported to userspace as "caps"
* attribute group of pt pmu device
* (/sys/bus/event_source/devices/intel_pt/caps/) so that userspace can store
* relevant bits together with intel_pt traces.
*
* These are necessary for both trace decoding (payloads_lip, contains address
* width encoded in IP-related packets), and event configuration (bitmasks with
* permitted values for certain bit fields).
*/
#define PT_CAP(_n, _l, _r, _m) \
[PT_CAP_ ## _n] = { .name = __stringify(_n), .leaf = _l, \
.reg = _r, .mask = _m }
static struct pt_cap_desc {
const char *name;
u32 leaf;
u8 reg;
u32 mask;
} pt_caps[] = {
PT_CAP(max_subleaf, 0, CR_EAX, 0xffffffff),
PT_CAP(cr3_filtering, 0, CR_EBX, BIT(0)),
PT_CAP(topa_output, 0, CR_ECX, BIT(0)),
PT_CAP(topa_multiple_entries, 0, CR_ECX, BIT(1)),
PT_CAP(payloads_lip, 0, CR_ECX, BIT(31)),
};
static u32 pt_cap_get(enum pt_capabilities cap)
{
struct pt_cap_desc *cd = &pt_caps[cap];
u32 c = pt_pmu.caps[cd->leaf * 4 + cd->reg];
unsigned int shift = __ffs(cd->mask);
return (c & cd->mask) >> shift;
}
static ssize_t pt_cap_show(struct device *cdev,
struct device_attribute *attr,
char *buf)
{
struct dev_ext_attribute *ea =
container_of(attr, struct dev_ext_attribute, attr);
enum pt_capabilities cap = (long)ea->var;
return snprintf(buf, PAGE_SIZE, "%x\n", pt_cap_get(cap));
}
static struct attribute_group pt_cap_group = {
.name = "caps",
};
PMU_FORMAT_ATTR(tsc, "config:10" );
PMU_FORMAT_ATTR(noretcomp, "config:11" );
static struct attribute *pt_formats_attr[] = {
&format_attr_tsc.attr,
&format_attr_noretcomp.attr,
NULL,
};
static struct attribute_group pt_format_group = {
.name = "format",
.attrs = pt_formats_attr,
};
static const struct attribute_group *pt_attr_groups[] = {
&pt_cap_group,
&pt_format_group,
NULL,
};
static int __init pt_pmu_hw_init(void)
{
struct dev_ext_attribute *de_attrs;
struct attribute **attrs;
size_t size;
int ret;
long i;
attrs = NULL;
ret = -ENODEV;
if (!test_cpu_cap(&boot_cpu_data, X86_FEATURE_INTEL_PT))
goto fail;
for (i = 0; i < PT_CPUID_LEAVES; i++) {
cpuid_count(20, i,
&pt_pmu.caps[CR_EAX + i*4],
&pt_pmu.caps[CR_EBX + i*4],
&pt_pmu.caps[CR_ECX + i*4],