mirror of
https://github.com/torvalds/linux.git
synced 2024-09-21 07:23:06 +00:00
[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>
This commit is contained in:
parent
faee9a5dc9
commit
240cd6a806
|
@ -242,6 +242,8 @@ static void __init init_amd(struct cpuinfo_x86 *c)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (cpuid_eax(0x80000000) >= 0x80000006)
|
||||||
|
num_cache_leaves = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned int amd_size_cache(struct cpuinfo_x86 * c, unsigned int size)
|
static unsigned int amd_size_cache(struct cpuinfo_x86 * c, unsigned int size)
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
* Changes:
|
* Changes:
|
||||||
* Venkatesh Pallipadi : Adding cache identification through cpuid(4)
|
* Venkatesh Pallipadi : Adding cache identification through cpuid(4)
|
||||||
* Ashok Raj <ashok.raj@intel.com>: Work with CPU hotplug infrastructure.
|
* Ashok Raj <ashok.raj@intel.com>: Work with CPU hotplug infrastructure.
|
||||||
|
* Andi Kleen : CPUID4 emulation on AMD.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
|
@ -130,25 +131,111 @@ struct _cpuid4_info {
|
||||||
cpumask_t shared_cpu_map;
|
cpumask_t shared_cpu_map;
|
||||||
};
|
};
|
||||||
|
|
||||||
static unsigned short num_cache_leaves;
|
unsigned short num_cache_leaves;
|
||||||
|
|
||||||
|
/* AMD doesn't have CPUID4. Emulate it here to report the same
|
||||||
|
information to the user. This makes some assumptions about the machine:
|
||||||
|
No L3, L2 not shared, no SMT etc. that is currently true on AMD CPUs.
|
||||||
|
|
||||||
|
In theory the TLBs could be reported as fake type (they are in "dummy").
|
||||||
|
Maybe later */
|
||||||
|
union l1_cache {
|
||||||
|
struct {
|
||||||
|
unsigned line_size : 8;
|
||||||
|
unsigned lines_per_tag : 8;
|
||||||
|
unsigned assoc : 8;
|
||||||
|
unsigned size_in_kb : 8;
|
||||||
|
};
|
||||||
|
unsigned val;
|
||||||
|
};
|
||||||
|
|
||||||
|
union l2_cache {
|
||||||
|
struct {
|
||||||
|
unsigned line_size : 8;
|
||||||
|
unsigned lines_per_tag : 4;
|
||||||
|
unsigned assoc : 4;
|
||||||
|
unsigned size_in_kb : 16;
|
||||||
|
};
|
||||||
|
unsigned val;
|
||||||
|
};
|
||||||
|
|
||||||
|
static unsigned short assocs[] = {
|
||||||
|
[1] = 1, [2] = 2, [4] = 4, [6] = 8,
|
||||||
|
[8] = 16,
|
||||||
|
[0xf] = 0xffff // ??
|
||||||
|
};
|
||||||
|
static unsigned char levels[] = { 1, 1, 2 };
|
||||||
|
static unsigned char types[] = { 1, 2, 3 };
|
||||||
|
|
||||||
|
static void __cpuinit amd_cpuid4(int leaf, union _cpuid4_leaf_eax *eax,
|
||||||
|
union _cpuid4_leaf_ebx *ebx,
|
||||||
|
union _cpuid4_leaf_ecx *ecx)
|
||||||
|
{
|
||||||
|
unsigned dummy;
|
||||||
|
unsigned line_size, lines_per_tag, assoc, size_in_kb;
|
||||||
|
union l1_cache l1i, l1d;
|
||||||
|
union l2_cache l2;
|
||||||
|
|
||||||
|
eax->full = 0;
|
||||||
|
ebx->full = 0;
|
||||||
|
ecx->full = 0;
|
||||||
|
|
||||||
|
cpuid(0x80000005, &dummy, &dummy, &l1d.val, &l1i.val);
|
||||||
|
cpuid(0x80000006, &dummy, &dummy, &l2.val, &dummy);
|
||||||
|
|
||||||
|
if (leaf > 2 || !l1d.val || !l1i.val || !l2.val)
|
||||||
|
return;
|
||||||
|
|
||||||
|
eax->split.is_self_initializing = 1;
|
||||||
|
eax->split.type = types[leaf];
|
||||||
|
eax->split.level = levels[leaf];
|
||||||
|
eax->split.num_threads_sharing = 0;
|
||||||
|
eax->split.num_cores_on_die = current_cpu_data.x86_max_cores - 1;
|
||||||
|
|
||||||
|
if (leaf <= 1) {
|
||||||
|
union l1_cache *l1 = leaf == 0 ? &l1d : &l1i;
|
||||||
|
assoc = l1->assoc;
|
||||||
|
line_size = l1->line_size;
|
||||||
|
lines_per_tag = l1->lines_per_tag;
|
||||||
|
size_in_kb = l1->size_in_kb;
|
||||||
|
} else {
|
||||||
|
assoc = l2.assoc;
|
||||||
|
line_size = l2.line_size;
|
||||||
|
lines_per_tag = l2.lines_per_tag;
|
||||||
|
/* cpu_data has errata corrections for K7 applied */
|
||||||
|
size_in_kb = current_cpu_data.x86_cache_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (assoc == 0xf)
|
||||||
|
eax->split.is_fully_associative = 1;
|
||||||
|
ebx->split.coherency_line_size = line_size - 1;
|
||||||
|
ebx->split.ways_of_associativity = assocs[assoc] - 1;
|
||||||
|
ebx->split.physical_line_partition = lines_per_tag - 1;
|
||||||
|
ecx->split.number_of_sets = (size_in_kb * 1024) / line_size /
|
||||||
|
(ebx->split.ways_of_associativity + 1) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
static int __cpuinit cpuid4_cache_lookup(int index, struct _cpuid4_info *this_leaf)
|
static int __cpuinit cpuid4_cache_lookup(int index, struct _cpuid4_info *this_leaf)
|
||||||
{
|
{
|
||||||
unsigned int eax, ebx, ecx, edx;
|
union _cpuid4_leaf_eax eax;
|
||||||
union _cpuid4_leaf_eax cache_eax;
|
union _cpuid4_leaf_ebx ebx;
|
||||||
|
union _cpuid4_leaf_ecx ecx;
|
||||||
|
unsigned edx;
|
||||||
|
|
||||||
cpuid_count(4, index, &eax, &ebx, &ecx, &edx);
|
if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD)
|
||||||
cache_eax.full = eax;
|
amd_cpuid4(index, &eax, &ebx, &ecx);
|
||||||
if (cache_eax.split.type == CACHE_TYPE_NULL)
|
else
|
||||||
|
cpuid_count(4, index, &eax.full, &ebx.full, &ecx.full, &edx);
|
||||||
|
if (eax.split.type == CACHE_TYPE_NULL)
|
||||||
return -EIO; /* better error ? */
|
return -EIO; /* better error ? */
|
||||||
|
|
||||||
this_leaf->eax.full = eax;
|
this_leaf->eax = eax;
|
||||||
this_leaf->ebx.full = ebx;
|
this_leaf->ebx = ebx;
|
||||||
this_leaf->ecx.full = ecx;
|
this_leaf->ecx = ecx;
|
||||||
this_leaf->size = (this_leaf->ecx.split.number_of_sets + 1) *
|
this_leaf->size = (ecx.split.number_of_sets + 1) *
|
||||||
(this_leaf->ebx.split.coherency_line_size + 1) *
|
(ebx.split.coherency_line_size + 1) *
|
||||||
(this_leaf->ebx.split.physical_line_partition + 1) *
|
(ebx.split.physical_line_partition + 1) *
|
||||||
(this_leaf->ebx.split.ways_of_associativity + 1);
|
(ebx.split.ways_of_associativity + 1);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -976,6 +976,9 @@ static int __init init_amd(struct cpuinfo_x86 *c)
|
||||||
if (c->extended_cpuid_level >= 0x80000008)
|
if (c->extended_cpuid_level >= 0x80000008)
|
||||||
amd_detect_cmp(c);
|
amd_detect_cmp(c);
|
||||||
|
|
||||||
|
/* Fix cpuid4 emulation for more */
|
||||||
|
num_cache_leaves = 3;
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -112,6 +112,7 @@ extern char ignore_fpu_irq;
|
||||||
extern void identify_cpu(struct cpuinfo_x86 *);
|
extern void identify_cpu(struct cpuinfo_x86 *);
|
||||||
extern void print_cpu_info(struct cpuinfo_x86 *);
|
extern void print_cpu_info(struct cpuinfo_x86 *);
|
||||||
extern unsigned int init_intel_cacheinfo(struct cpuinfo_x86 *c);
|
extern unsigned int init_intel_cacheinfo(struct cpuinfo_x86 *c);
|
||||||
|
extern unsigned short num_cache_leaves;
|
||||||
|
|
||||||
#ifdef CONFIG_X86_HT
|
#ifdef CONFIG_X86_HT
|
||||||
extern void detect_ht(struct cpuinfo_x86 *c);
|
extern void detect_ht(struct cpuinfo_x86 *c);
|
||||||
|
|
|
@ -96,6 +96,7 @@ extern char ignore_irq13;
|
||||||
extern void identify_cpu(struct cpuinfo_x86 *);
|
extern void identify_cpu(struct cpuinfo_x86 *);
|
||||||
extern void print_cpu_info(struct cpuinfo_x86 *);
|
extern void print_cpu_info(struct cpuinfo_x86 *);
|
||||||
extern unsigned int init_intel_cacheinfo(struct cpuinfo_x86 *c);
|
extern unsigned int init_intel_cacheinfo(struct cpuinfo_x86 *c);
|
||||||
|
extern unsigned short num_cache_leaves;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* EFLAGS bits
|
* EFLAGS bits
|
||||||
|
|
Loading…
Reference in New Issue
Block a user