diff options
| author | Andi Kleen <ak@suse.de> | 2006-06-26 07:56:13 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-06-26 13:48:14 -0400 |
| commit | 240cd6a80642da528bfa382ec2ae4e3cb8991ea7 (patch) | |
| tree | 6c24052ea167a2fd7e0be93c167e9e7da5bddd2b | |
| parent | faee9a5dc9d8399cc3b1b8e18b6d7ff7b17f1af1 (diff) | |
[PATCH] i386/x86-64: Emulate CPUID4 on AMD
Intel systems report the cache level data from CPUID 4 in sysfs.
Add a CPUID 4 emulation for AMD CPUs to report the same
information for them. This allows programs to read this
information in a uniform way.
The AMD way to report this is less flexible so some assumptions
are hardcoded (e.g. no L3)
Signed-off-by: Andi Kleen <ak@suse.de>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
| -rw-r--r-- | arch/i386/kernel/cpu/amd.c | 2 | ||||
| -rw-r--r-- | arch/i386/kernel/cpu/intel_cacheinfo.c | 113 | ||||
| -rw-r--r-- | arch/x86_64/kernel/setup.c | 3 | ||||
| -rw-r--r-- | include/asm-i386/processor.h | 1 | ||||
| -rw-r--r-- | include/asm-x86_64/processor.h | 1 |
5 files changed, 107 insertions, 13 deletions
diff --git a/arch/i386/kernel/cpu/amd.c b/arch/i386/kernel/cpu/amd.c index b9c93d9789e7..fd0457c9c827 100644 --- a/arch/i386/kernel/cpu/amd.c +++ b/arch/i386/kernel/cpu/amd.c | |||
| @@ -242,6 +242,8 @@ static void __init init_amd(struct cpuinfo_x86 *c) | |||
| 242 | } | 242 | } |
| 243 | #endif | 243 | #endif |
| 244 | 244 | ||
| 245 | if (cpuid_eax(0x80000000) >= 0x80000006) | ||
| 246 | num_cache_leaves = 3; | ||
| 245 | } | 247 | } |
| 246 | 248 | ||
| 247 | static unsigned int amd_size_cache(struct cpuinfo_x86 * c, unsigned int size) | 249 | static unsigned int amd_size_cache(struct cpuinfo_x86 * c, unsigned int size) |
diff --git a/arch/i386/kernel/cpu/intel_cacheinfo.c b/arch/i386/kernel/cpu/intel_cacheinfo.c index c8547a6fa7e6..6c37b4fd8ce2 100644 --- a/arch/i386/kernel/cpu/intel_cacheinfo.c +++ b/arch/i386/kernel/cpu/intel_cacheinfo.c | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | * Changes: | 4 | * Changes: |
| 5 | * Venkatesh Pallipadi : Adding cache identification through cpuid(4) | 5 | * Venkatesh Pallipadi : Adding cache identification through cpuid(4) |
| 6 | * Ashok Raj <ashok.raj@intel.com>: Work with CPU hotplug infrastructure. | 6 | * Ashok Raj <ashok.raj@intel.com>: Work with CPU hotplug infrastructure. |
| 7 | * Andi Kleen : CPUID4 emulation on AMD. | ||
| 7 | */ | 8 | */ |
| 8 | 9 | ||
| 9 | #include <linux/init.h> | 10 | #include <linux/init.h> |
| @@ -130,25 +131,111 @@ struct _cpuid4_info { | |||
| 130 | cpumask_t shared_cpu_map; | 131 | cpumask_t shared_cpu_map; |
| 131 | }; | 132 | }; |
| 132 | 133 | ||
| 133 | static unsigned short num_cache_leaves; | 134 | unsigned short num_cache_leaves; |
| 135 | |||
| 136 | /* AMD doesn't have CPUID4. Emulate it here to report the same | ||
| 137 | information to the user. This makes some assumptions about the machine: | ||
| 138 | No L3, L2 not shared, no SMT etc. that is currently true on AMD CPUs. | ||
| 139 | |||
| 140 | In theory the TLBs could be reported as fake type (they are in "dummy"). | ||
| 141 | Maybe later */ | ||
| 142 | union l1_cache { | ||
| 143 | struct { | ||
| 144 | unsigned line_size : 8; | ||
| 145 | unsigned lines_per_tag : 8; | ||
| 146 | unsigned assoc : 8; | ||
| 147 | unsigned size_in_kb : 8; | ||
| 148 | }; | ||
| 149 | unsigned val; | ||
| 150 | }; | ||
| 151 | |||
| 152 | union l2_cache { | ||
| 153 | struct { | ||
| 154 | unsigned line_size : 8; | ||
| 155 | unsigned lines_per_tag : 4; | ||
| 156 | unsigned assoc : 4; | ||
| 157 | unsigned size_in_kb : 16; | ||
| 158 | }; | ||
| 159 | unsigned val; | ||
| 160 | }; | ||
| 161 | |||
| 162 | static unsigned short assocs[] = { | ||
| 163 | [1] = 1, [2] = 2, [4] = 4, [6] = 8, | ||
| 164 | [8] = 16, | ||
| 165 | [0xf] = 0xffff // ?? | ||
| 166 | }; | ||
| 167 | static unsigned char levels[] = { 1, 1, 2 }; | ||
| 168 | static unsigned char types[] = { 1, 2, 3 }; | ||
| 169 | |||
| 170 | static void __cpuinit amd_cpuid4(int leaf, union _cpuid4_leaf_eax *eax, | ||
| 171 | union _cpuid4_leaf_ebx *ebx, | ||
| 172 | union _cpuid4_leaf_ecx *ecx) | ||
| 173 | { | ||
| 174 | unsigned dummy; | ||
| 175 | unsigned line_size, lines_per_tag, assoc, size_in_kb; | ||
| 176 | union l1_cache l1i, l1d; | ||
| 177 | union l2_cache l2; | ||
| 178 | |||
| 179 | eax->full = 0; | ||
| 180 | ebx->full = 0; | ||
| 181 | ecx->full = 0; | ||
| 182 | |||
| 183 | cpuid(0x80000005, &dummy, &dummy, &l1d.val, &l1i.val); | ||
| 184 | cpuid(0x80000006, &dummy, &dummy, &l2.val, &dummy); | ||
| 185 | |||
| 186 | if (leaf > 2 || !l1d.val || !l1i.val || !l2.val) | ||
| 187 | return; | ||
| 188 | |||
| 189 | eax->split.is_self_initializing = 1; | ||
| 190 | eax->split.type = types[leaf]; | ||
| 191 | eax->split.level = levels[leaf]; | ||
| 192 | eax->split.num_threads_sharing = 0; | ||
| 193 | eax->split.num_cores_on_die = current_cpu_data.x86_max_cores - 1; | ||
| 194 | |||
| 195 | if (leaf <= 1) { | ||
| 196 | union l1_cache *l1 = leaf == 0 ? &l1d : &l1i; | ||
| 197 | assoc = l1->assoc; | ||
| 198 | line_size = l1->line_size; | ||
| 199 | lines_per_tag = l1->lines_per_tag; | ||
| 200 | size_in_kb = l1->size_in_kb; | ||
| 201 | } else { | ||
| 202 | assoc = l2.assoc; | ||
| 203 | line_size = l2.line_size; | ||
| 204 | lines_per_tag = l2.lines_per_tag; | ||
| 205 | /* cpu_data has errata corrections for K7 applied */ | ||
| 206 | size_in_kb = current_cpu_data.x86_cache_size; | ||
| 207 | } | ||
| 208 | |||
| 209 | if (assoc == 0xf) | ||
| 210 | eax->split.is_fully_associative = 1; | ||
| 211 | ebx->split.coherency_line_size = line_size - 1; | ||
| 212 | ebx->split.ways_of_associativity = assocs[assoc] - 1; | ||
| 213 | ebx->split.physical_line_partition = lines_per_tag - 1; | ||
| 214 | ecx->split.number_of_sets = (size_in_kb * 1024) / line_size / | ||
| 215 | (ebx->split.ways_of_associativity + 1) - 1; | ||
| 216 | } | ||
| 134 | 217 | ||
| 135 | static int __cpuinit cpuid4_cache_lookup(int index, struct _cpuid4_info *this_leaf) | 218 | static int __cpuinit cpuid4_cache_lookup(int index, struct _cpuid4_info *this_leaf) |
| 136 | { | 219 | { |
| 137 | unsigned int eax, ebx, ecx, edx; | 220 | union _cpuid4_leaf_eax eax; |
| 138 | union _cpuid4_leaf_eax cache_eax; | 221 | union _cpuid4_leaf_ebx ebx; |
| 222 | union _cpuid4_leaf_ecx ecx; | ||
| 223 | unsigned edx; | ||
| 139 | 224 | ||
| 140 | cpuid_count(4, index, &eax, &ebx, &ecx, &edx); | 225 | if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) |
| 141 | cache_eax.full = eax; | 226 | amd_cpuid4(index, &eax, &ebx, &ecx); |
| 142 | if (cache_eax.split.type == CACHE_TYPE_NULL) | 227 | else |
| 228 | cpuid_count(4, index, &eax.full, &ebx.full, &ecx.full, &edx); | ||
| 229 | if (eax.split.type == CACHE_TYPE_NULL) | ||
| 143 | return -EIO; /* better error ? */ | 230 | return -EIO; /* better error ? */ |
| 144 | 231 | ||
| 145 | this_leaf->eax.full = eax; | 232 | this_leaf->eax = eax; |
| 146 | this_leaf->ebx.full = ebx; | 233 | this_leaf->ebx = ebx; |
| 147 | this_leaf->ecx.full = ecx; | 234 | this_leaf->ecx = ecx; |
| 148 | this_leaf->size = (this_leaf->ecx.split.number_of_sets + 1) * | 235 | this_leaf->size = (ecx.split.number_of_sets + 1) * |
| 149 | (this_leaf->ebx.split.coherency_line_size + 1) * | 236 | (ebx.split.coherency_line_size + 1) * |
| 150 | (this_leaf->ebx.split.physical_line_partition + 1) * | 237 | (ebx.split.physical_line_partition + 1) * |
| 151 | (this_leaf->ebx.split.ways_of_associativity + 1); | 238 | (ebx.split.ways_of_associativity + 1); |
| 152 | return 0; | 239 | return 0; |
| 153 | } | 240 | } |
| 154 | 241 | ||
diff --git a/arch/x86_64/kernel/setup.c b/arch/x86_64/kernel/setup.c index 1cb3e21c571a..4b7e02216970 100644 --- a/arch/x86_64/kernel/setup.c +++ b/arch/x86_64/kernel/setup.c | |||
| @@ -976,6 +976,9 @@ static int __init init_amd(struct cpuinfo_x86 *c) | |||
| 976 | if (c->extended_cpuid_level >= 0x80000008) | 976 | if (c->extended_cpuid_level >= 0x80000008) |
| 977 | amd_detect_cmp(c); | 977 | amd_detect_cmp(c); |
| 978 | 978 | ||
| 979 | /* Fix cpuid4 emulation for more */ | ||
| 980 | num_cache_leaves = 3; | ||
| 981 | |||
| 979 | return r; | 982 | return r; |
| 980 | } | 983 | } |
| 981 | 984 | ||
diff --git a/include/asm-i386/processor.h b/include/asm-i386/processor.h index 0c83cf12eec9..b796210c0f5c 100644 --- a/include/asm-i386/processor.h +++ b/include/asm-i386/processor.h | |||
| @@ -112,6 +112,7 @@ extern char ignore_fpu_irq; | |||
| 112 | extern void identify_cpu(struct cpuinfo_x86 *); | 112 | extern void identify_cpu(struct cpuinfo_x86 *); |
| 113 | extern void print_cpu_info(struct cpuinfo_x86 *); | 113 | extern void print_cpu_info(struct cpuinfo_x86 *); |
| 114 | extern unsigned int init_intel_cacheinfo(struct cpuinfo_x86 *c); | 114 | extern unsigned int init_intel_cacheinfo(struct cpuinfo_x86 *c); |
| 115 | extern unsigned short num_cache_leaves; | ||
| 115 | 116 | ||
| 116 | #ifdef CONFIG_X86_HT | 117 | #ifdef CONFIG_X86_HT |
| 117 | extern void detect_ht(struct cpuinfo_x86 *c); | 118 | extern void detect_ht(struct cpuinfo_x86 *c); |
diff --git a/include/asm-x86_64/processor.h b/include/asm-x86_64/processor.h index 3061a38a3b1d..e583f0d95209 100644 --- a/include/asm-x86_64/processor.h +++ b/include/asm-x86_64/processor.h | |||
| @@ -96,6 +96,7 @@ extern char ignore_irq13; | |||
| 96 | extern void identify_cpu(struct cpuinfo_x86 *); | 96 | extern void identify_cpu(struct cpuinfo_x86 *); |
| 97 | extern void print_cpu_info(struct cpuinfo_x86 *); | 97 | extern void print_cpu_info(struct cpuinfo_x86 *); |
| 98 | extern unsigned int init_intel_cacheinfo(struct cpuinfo_x86 *c); | 98 | extern unsigned int init_intel_cacheinfo(struct cpuinfo_x86 *c); |
| 99 | extern unsigned short num_cache_leaves; | ||
| 99 | 100 | ||
| 100 | /* | 101 | /* |
| 101 | * EFLAGS bits | 102 | * EFLAGS bits |
