Ben Cheng | 5a4eb4e | 2009-09-14 16:00:41 -0700 | [diff] [blame] | 1 | /* |
| 2 | * @file architecture specific interfaces |
| 3 | * @remark Copyright 2008 Intel Corporation |
| 4 | * @remark Read the file COPYING |
| 5 | * @author Andi Kleen |
| 6 | */ |
| 7 | |
Jeff Brown | b415fab | 2011-01-11 12:38:32 -0800 | [diff] [blame] | 8 | #if (defined(__i386__) || defined(__x86_64__)) && !defined(ANDROID_HOST) |
Ben Cheng | 5a4eb4e | 2009-09-14 16:00:41 -0700 | [diff] [blame] | 9 | |
| 10 | /* Assume we run on the same host as the profilee */ |
| 11 | |
| 12 | #define num_to_mask(x) ((1U << (x)) - 1) |
| 13 | |
| 14 | static inline int cpuid_vendor(char *vnd) |
| 15 | { |
| 16 | union { |
| 17 | struct { |
| 18 | unsigned b,d,c; |
| 19 | }; |
| 20 | char v[12]; |
| 21 | } v; |
| 22 | unsigned eax; |
David 'Digit' Turner | 02636c6 | 2011-02-01 23:09:00 +0100 | [diff] [blame] | 23 | #ifdef __PIC__ |
| 24 | __asm__ __volatile__( |
| 25 | "pushl %%ebx\n" /* must be preserved due to PIC code */ |
| 26 | "cpuid\n" |
| 27 | "mov %%ebx, 0(%%edi)\n" |
| 28 | "mov %%ecx, 4(%%edi)\n" |
| 29 | "mov %%edx, 8(%%edi)\n" |
| 30 | "popl %%ebx\n" |
| 31 | : "=a" (eax) |
| 32 | : "a"(0), "D"(v.v) |
| 33 | : "%ecx", "%edx" |
| 34 | ); |
| 35 | #else |
Ben Cheng | 5bbbe46 | 2010-09-02 21:48:01 -0700 | [diff] [blame] | 36 | asm("cpuid" : "=a" (eax), "=b" (v.b), "=c" (v.c), "=d" (v.d) : "0" (0)); |
David 'Digit' Turner | 02636c6 | 2011-02-01 23:09:00 +0100 | [diff] [blame] | 37 | #endif |
Ben Cheng | 5a4eb4e | 2009-09-14 16:00:41 -0700 | [diff] [blame] | 38 | return !strncmp(v.v, vnd, 12); |
| 39 | } |
| 40 | |
David 'Digit' Turner | 02636c6 | 2011-02-01 23:09:00 +0100 | [diff] [blame] | 41 | static inline unsigned arch_cpuid_1(int code) |
| 42 | { |
| 43 | unsigned val; |
| 44 | #ifdef __PIC__ |
| 45 | __asm__ __volatile__ ( |
| 46 | "pushl %%ebx\n" |
| 47 | "cpuid\n" |
| 48 | "popl %%ebx\n" |
| 49 | : "=a" (val) |
| 50 | : "a" (code) |
| 51 | : "ecx", "edx" |
| 52 | ); |
| 53 | #else |
| 54 | asm("cpuid" : "=a" (v.eax) : "a" (code) : "ecx","ebx","edx"); |
| 55 | #endif |
| 56 | return val; |
| 57 | } |
| 58 | |
Jeff Brown | 7a33c86 | 2011-02-02 14:00:44 -0800 | [diff] [blame] | 59 | static inline unsigned int cpuid_signature() |
| 60 | { |
| 61 | return arch_cpuid_1(1); |
| 62 | } |
| 63 | |
| 64 | static inline unsigned int cpu_model(unsigned int eax) |
| 65 | { |
| 66 | unsigned model = (eax & 0xf0) >> 4; |
| 67 | unsigned ext_model = (eax & 0xf0000) >> 12; |
| 68 | return ext_model + model; |
| 69 | } |
| 70 | |
| 71 | static inline unsigned int cpu_family(unsigned int eax) |
| 72 | { |
| 73 | unsigned family = (eax & 0xf00) >> 8; |
| 74 | unsigned ext_family = (eax & 0xff00000) >> 20; |
| 75 | return ext_family + family; |
| 76 | } |
| 77 | |
| 78 | static inline unsigned int cpu_stepping(unsigned int eax) |
| 79 | { |
| 80 | return (eax & 0xf); |
| 81 | } |
| 82 | |
David 'Digit' Turner | 02636c6 | 2011-02-01 23:09:00 +0100 | [diff] [blame] | 83 | |
Ben Cheng | 5a4eb4e | 2009-09-14 16:00:41 -0700 | [diff] [blame] | 84 | /* Work around Nehalem spec update AAJ79: CPUID incorrectly indicates |
| 85 | unhalted reference cycle architectural event is supported. We assume |
| 86 | steppings after C0 report correct data in CPUID. */ |
| 87 | static inline void workaround_nehalem_aaj79(unsigned *ebx) |
| 88 | { |
Jeff Brown | 7a33c86 | 2011-02-02 14:00:44 -0800 | [diff] [blame] | 89 | unsigned eax; |
Ben Cheng | 5a4eb4e | 2009-09-14 16:00:41 -0700 | [diff] [blame] | 90 | |
| 91 | if (!cpuid_vendor("GenuineIntel")) |
| 92 | return; |
Jeff Brown | 7a33c86 | 2011-02-02 14:00:44 -0800 | [diff] [blame] | 93 | eax = cpuid_signature(); |
| 94 | if (cpu_family(eax) != 6 || cpu_model(eax) != 26 |
| 95 | || cpu_stepping(eax) > 4) |
Ben Cheng | 5a4eb4e | 2009-09-14 16:00:41 -0700 | [diff] [blame] | 96 | return; |
| 97 | *ebx |= (1 << 2); /* disable unsupported event */ |
| 98 | } |
| 99 | |
| 100 | static inline unsigned arch_get_filter(op_cpu cpu_type) |
| 101 | { |
Jeff Brown | 7a33c86 | 2011-02-02 14:00:44 -0800 | [diff] [blame] | 102 | if (op_cpu_base_type(cpu_type) == CPU_ARCH_PERFMON) { |
Ben Cheng | 5a4eb4e | 2009-09-14 16:00:41 -0700 | [diff] [blame] | 103 | unsigned ebx, eax; |
David 'Digit' Turner | 02636c6 | 2011-02-01 23:09:00 +0100 | [diff] [blame] | 104 | #ifdef __PIC__ |
| 105 | __asm__ __volatile__ ( |
| 106 | "pushl %%ebx\n" |
| 107 | "cpuid\n" |
| 108 | "mov %%ebx, %%ecx\n" |
| 109 | "popl %%ebx" |
| 110 | : "=a" (eax), "=c" (ebx) |
| 111 | : "a" (0xa) |
| 112 | : "edx" |
| 113 | ); |
| 114 | #else |
Ben Cheng | 5bbbe46 | 2010-09-02 21:48:01 -0700 | [diff] [blame] | 115 | asm("cpuid" : "=a" (eax), "=b" (ebx) : "0" (0xa) : "ecx","edx"); |
David 'Digit' Turner | 02636c6 | 2011-02-01 23:09:00 +0100 | [diff] [blame] | 116 | #endif |
Ben Cheng | 5a4eb4e | 2009-09-14 16:00:41 -0700 | [diff] [blame] | 117 | workaround_nehalem_aaj79(&ebx); |
| 118 | return ebx & num_to_mask(eax >> 24); |
| 119 | } |
| 120 | return -1U; |
| 121 | } |
| 122 | |
| 123 | static inline int arch_num_counters(op_cpu cpu_type) |
| 124 | { |
Jeff Brown | 7a33c86 | 2011-02-02 14:00:44 -0800 | [diff] [blame] | 125 | if (op_cpu_base_type(cpu_type) == CPU_ARCH_PERFMON) { |
David 'Digit' Turner | 02636c6 | 2011-02-01 23:09:00 +0100 | [diff] [blame] | 126 | unsigned v = arch_cpuid_1(0xa); |
Ben Cheng | 5a4eb4e | 2009-09-14 16:00:41 -0700 | [diff] [blame] | 127 | return (v >> 8) & 0xff; |
| 128 | } |
| 129 | return -1; |
| 130 | } |
| 131 | |
| 132 | static inline unsigned arch_get_counter_mask(void) |
| 133 | { |
David 'Digit' Turner | 02636c6 | 2011-02-01 23:09:00 +0100 | [diff] [blame] | 134 | unsigned v = arch_cpuid_1(0xa); |
| 135 | return num_to_mask((v >> 8) & 0xff); |
Ben Cheng | 5a4eb4e | 2009-09-14 16:00:41 -0700 | [diff] [blame] | 136 | } |
| 137 | |
Jeff Brown | 7a33c86 | 2011-02-02 14:00:44 -0800 | [diff] [blame] | 138 | static inline op_cpu op_cpu_specific_type(op_cpu cpu_type) |
| 139 | { |
| 140 | if (cpu_type == CPU_ARCH_PERFMON) { |
| 141 | /* Already know is Intel family 6, so just check the model. */ |
| 142 | int model = cpu_model(cpuid_signature()); |
| 143 | switch(model) { |
| 144 | case 0x0f: |
| 145 | case 0x16: |
| 146 | case 0x17: |
| 147 | case 0x1d: |
| 148 | return CPU_CORE_2; |
| 149 | case 0x1a: |
| 150 | case 0x1e: |
| 151 | case 0x2e: |
| 152 | return CPU_CORE_I7; |
| 153 | case 0x1c: |
| 154 | return CPU_ATOM; |
| 155 | case 0x25: |
| 156 | return CPU_WESTMERE; |
| 157 | } |
| 158 | } |
| 159 | return cpu_type; |
| 160 | } |
| 161 | |
Ben Cheng | 5a4eb4e | 2009-09-14 16:00:41 -0700 | [diff] [blame] | 162 | #else |
| 163 | |
| 164 | static inline unsigned arch_get_filter(op_cpu cpu_type) |
| 165 | { |
| 166 | /* Do something with passed arg to shut up the compiler warning */ |
| 167 | if (cpu_type != CPU_NO_GOOD) |
| 168 | return 0; |
| 169 | return 0; |
| 170 | } |
| 171 | |
| 172 | static inline int arch_num_counters(op_cpu cpu_type) |
| 173 | { |
| 174 | /* Do something with passed arg to shut up the compiler warning */ |
| 175 | if (cpu_type != CPU_NO_GOOD) |
| 176 | return -1; |
| 177 | return -1; |
| 178 | } |
| 179 | |
| 180 | static inline unsigned arch_get_counter_mask(void) |
| 181 | { |
| 182 | return 0; |
| 183 | } |
| 184 | |
Jeff Brown | 7a33c86 | 2011-02-02 14:00:44 -0800 | [diff] [blame] | 185 | static inline op_cpu op_cpu_specific_type(op_cpu cpu_type) |
| 186 | { |
| 187 | return cpu_type; |
| 188 | } |
Ben Cheng | 5a4eb4e | 2009-09-14 16:00:41 -0700 | [diff] [blame] | 189 | #endif |