/* * @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) static inline int cpuid_vendor(char *vnd) { union { struct { unsigned b,d,c; }; char v[12]; } v; unsigned eax; asm volatile( "pushl %%ebx; cpuid; movl %%ebx, %1; popl %%ebx" : "=a" (eax), "=S" (v.b), "=c" (v.c), "=d" (v.d) : "0" (0)); return !strncmp(v.v, vnd, 12); } /* 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) { union { unsigned eax; struct { unsigned stepping : 4; unsigned model : 4; unsigned family : 4; unsigned type : 2; unsigned res : 2; unsigned ext_model : 4; unsigned ext_family : 8; unsigned res2 : 4; }; } v; unsigned model; if (!cpuid_vendor("GenuineIntel")) return; asm volatile( "pushl %%ebx; cpuid; movl %%ebx, %1; popl %%ebx" : "=a" (v.eax) : "0" (1) : "ecx","edx"); model = (v.ext_model << 4) + v.model; if (v.family != 6 || model != 26 || v.stepping > 4) return; *ebx |= (1 << 2); /* disable unsupported event */ } static inline unsigned arch_get_filter(op_cpu cpu_type) { if (cpu_type == CPU_ARCH_PERFMON) { unsigned ebx, eax; asm volatile( "pushl %%ebx; cpuid; movl %%ebx, %1; popl %%ebx" : "=a" (eax), "=S" (ebx) : "0" (0xa) : "ecx","edx"); workaround_nehalem_aaj79(&ebx); return ebx & num_to_mask(eax >> 24); } return -1U; } static inline int arch_num_counters(op_cpu cpu_type) { if (cpu_type == CPU_ARCH_PERFMON) { unsigned v; asm volatile( "pushl %%ebx; cpuid; movl %%eax, %1; popl %%ebx" : "=a" (v) : "0" (0xa) : "ecx","edx"); return (v >> 8) & 0xff; } return -1; } static inline unsigned arch_get_counter_mask(void) { unsigned v; asm volatile( "pushl %%ebx; cpuid; movl %%ebx, %1; popl %%ebx" : "=a" (v) : "0" (0xa) : "ecx","edx"); return num_to_mask((v >> 8) & 0xff); } #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; } #endif