| /* |
| * @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 |