blob: d9f505f4bf15605a53c0b5c824c73e61a8813917 [file] [log] [blame]
/*
* @file architecture specific interfaces
* @remark Copyright 2008 Intel Corporation
* @remark Read the file COPYING
* @author Andi Kleen
*/
#if defined(__i386__) || defined(__x86_64__)
/* Assume we run on the same host as the profilee */
#define num_to_mask(x) ((1U << (x)) - 1)
typedef struct {
unsigned eax, ebx, ecx, edx;
} cpuid_data;
#if defined(__i386__)
static inline void cpuid(int func, cpuid_data * p)
{
asm("push %%ebx; cpuid; mov %%ebx, %%esi; pop %%ebx"
: "=a" (p->eax), "=S" (p->ebx), "=c" (p->ecx), "=d" (p->edx)
: "0" (func));
}
#else
static inline void cpuid(int func, cpuid_data * p)
{
asm("cpuid"
: "=a" (p->eax), "=b" (p->ebx), "=c" (p->ecx), "=d" (p->edx)
: "0" (func));
}
#endif
static inline int cpuid_vendor(char *vnd)
{
union {
struct {
unsigned b,d,c;
};
char v[12];
} v;
cpuid_data data;
cpuid(0, &data);
v.b = data.ebx; v.c = data.ecx; v.d = data.edx;
return !strncmp(v.v, vnd, 12);
}
static inline unsigned int cpuid_signature()
{
cpuid_data data;
cpuid(1, &data);
return data.eax;
}
static inline unsigned int cpu_model(unsigned int eax)
{
unsigned model = (eax & 0xf0) >> 4;
unsigned ext_model = (eax & 0xf0000) >> 12;
return ext_model + model;
}
static inline unsigned int cpu_family(unsigned int eax)
{
unsigned family = (eax & 0xf00) >> 8;
unsigned ext_family = (eax & 0xff00000) >> 20;
return ext_family + family;
}
static inline unsigned int cpu_stepping(unsigned int eax)
{
return (eax & 0xf);
}
/* Work around Nehalem spec update AAJ79: CPUID incorrectly indicates
unhalted reference cycle architectural event is supported. We assume
steppings after C0 report correct data in CPUID. */
static inline void workaround_nehalem_aaj79(unsigned *ebx)
{
unsigned eax;
if (!cpuid_vendor("GenuineIntel"))
return;
eax = cpuid_signature();
if (cpu_family(eax) != 6 || cpu_model(eax) != 26
|| cpu_stepping(eax) > 4)
return;
*ebx |= (1 << 2); /* disable unsupported event */
}
static inline unsigned arch_get_filter(op_cpu cpu_type)
{
if (op_cpu_base_type(cpu_type) == CPU_ARCH_PERFMON) {
cpuid_data data;
cpuid(0xa, &data);
workaround_nehalem_aaj79(&data.ebx);
return data.ebx & num_to_mask(data.eax >> 24);
}
return -1U;
}
static inline int arch_num_counters(op_cpu cpu_type)
{
if (op_cpu_base_type(cpu_type) == CPU_ARCH_PERFMON) {
cpuid_data data;
cpuid(0xa, &data);
return (data.eax >> 8) & 0xff;
}
return -1;
}
static inline unsigned arch_get_counter_mask(void)
{
cpuid_data data;
cpuid(0xa, &data);
return num_to_mask((data.eax >> 8) & 0xff);
}
static inline op_cpu op_cpu_specific_type(op_cpu cpu_type)
{
if (cpu_type == CPU_ARCH_PERFMON) {
/* Already know is Intel family 6, so just check the model. */
int model = cpu_model(cpuid_signature());
switch(model) {
case 0x0f:
case 0x16:
case 0x17:
case 0x1d:
return CPU_CORE_2;
case 0x1a:
case 0x1e:
case 0x1f:
case 0x2e:
return CPU_CORE_I7;
case 0x1c:
return CPU_ATOM;
case 0x25: /* Westmere mobile/desktop/entry level server */
case 0x2c: /* Westmere-EP (Intel Xeon 5600 series) */
case 0x2f: /* Westmere-EX */
return CPU_WESTMERE;
case 0x2a:
case 0x2d:
return CPU_SANDYBRIDGE;
}
}
return cpu_type;
}
#else
static inline unsigned arch_get_filter(op_cpu cpu_type)
{
/* Do something with passed arg to shut up the compiler warning */
if (cpu_type != CPU_NO_GOOD)
return 0;
return 0;
}
static inline int arch_num_counters(op_cpu cpu_type)
{
/* Do something with passed arg to shut up the compiler warning */
if (cpu_type != CPU_NO_GOOD)
return -1;
return -1;
}
static inline unsigned arch_get_counter_mask(void)
{
return 0;
}
static inline op_cpu op_cpu_specific_type(op_cpu cpu_type)
{
return cpu_type;
}
#endif