i386: Helpers to encode cache information consistently

Instead of having a collection of macros that need to be used in
complex expressions to build CPUID data, define a CPUCacheInfo
struct that can hold information about a given cache.  Helper
functions will take a CPUCacheInfo struct as input to encode
CPUID leaves for a cache.

This will help us ensure consistency between cache information
CPUID leaves, and make the existing inconsistencies in CPUID info
more visible.

Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
Signed-off-by: Babu Moger <babu.moger@amd.com>
Tested-by: Geoffrey McRae <geoff@hostfission.com>
Message-Id: <20180510204148.11687-2-babu.moger@amd.com>
Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
This commit is contained in:
Eduardo Habkost 2018-05-10 15:41:41 -05:00
parent 0da0fb0628
commit 7e3482f824
2 changed files with 424 additions and 124 deletions

View File

@ -56,33 +56,240 @@
#include "disas/capstone.h" #include "disas/capstone.h"
/* Helpers for building CPUID[2] descriptors: */
/* Cache topology CPUID constants: */ struct CPUID2CacheDescriptorInfo {
enum CacheType type;
int level;
int size;
int line_size;
int associativity;
};
/* CPUID Leaf 2 Descriptors */ #define KiB 1024
#define MiB (1024 * 1024)
#define CPUID_2_L1D_32KB_8WAY_64B 0x2c /*
#define CPUID_2_L1I_32KB_8WAY_64B 0x30 * Known CPUID 2 cache descriptors.
#define CPUID_2_L2_2MB_8WAY_64B 0x7d * From Intel SDM Volume 2A, CPUID instruction
#define CPUID_2_L3_16MB_16WAY_64B 0x4d */
struct CPUID2CacheDescriptorInfo cpuid2_cache_descriptors[] = {
[0x06] = { .level = 1, .type = ICACHE, .size = 8 * KiB,
.associativity = 4, .line_size = 32, },
[0x08] = { .level = 1, .type = ICACHE, .size = 16 * KiB,
.associativity = 4, .line_size = 32, },
[0x09] = { .level = 1, .type = ICACHE, .size = 32 * KiB,
.associativity = 4, .line_size = 64, },
[0x0A] = { .level = 1, .type = DCACHE, .size = 8 * KiB,
.associativity = 2, .line_size = 32, },
[0x0C] = { .level = 1, .type = DCACHE, .size = 16 * KiB,
.associativity = 4, .line_size = 32, },
[0x0D] = { .level = 1, .type = DCACHE, .size = 16 * KiB,
.associativity = 4, .line_size = 64, },
[0x0E] = { .level = 1, .type = DCACHE, .size = 24 * KiB,
.associativity = 6, .line_size = 64, },
[0x1D] = { .level = 2, .type = UNIFIED_CACHE, .size = 128 * KiB,
.associativity = 2, .line_size = 64, },
[0x21] = { .level = 2, .type = UNIFIED_CACHE, .size = 256 * KiB,
.associativity = 8, .line_size = 64, },
/* lines per sector is not supported cpuid2_cache_descriptor(),
* so descriptors 0x22, 0x23 are not included
*/
[0x24] = { .level = 2, .type = UNIFIED_CACHE, .size = 1 * MiB,
.associativity = 16, .line_size = 64, },
/* lines per sector is not supported cpuid2_cache_descriptor(),
* so descriptors 0x25, 0x20 are not included
*/
[0x2C] = { .level = 1, .type = DCACHE, .size = 32 * KiB,
.associativity = 8, .line_size = 64, },
[0x30] = { .level = 1, .type = ICACHE, .size = 32 * KiB,
.associativity = 8, .line_size = 64, },
[0x41] = { .level = 2, .type = UNIFIED_CACHE, .size = 128 * KiB,
.associativity = 4, .line_size = 32, },
[0x42] = { .level = 2, .type = UNIFIED_CACHE, .size = 256 * KiB,
.associativity = 4, .line_size = 32, },
[0x43] = { .level = 2, .type = UNIFIED_CACHE, .size = 512 * KiB,
.associativity = 4, .line_size = 32, },
[0x44] = { .level = 2, .type = UNIFIED_CACHE, .size = 1 * MiB,
.associativity = 4, .line_size = 32, },
[0x45] = { .level = 2, .type = UNIFIED_CACHE, .size = 2 * MiB,
.associativity = 4, .line_size = 32, },
[0x46] = { .level = 3, .type = UNIFIED_CACHE, .size = 4 * MiB,
.associativity = 4, .line_size = 64, },
[0x47] = { .level = 3, .type = UNIFIED_CACHE, .size = 8 * MiB,
.associativity = 8, .line_size = 64, },
[0x48] = { .level = 2, .type = UNIFIED_CACHE, .size = 3 * MiB,
.associativity = 12, .line_size = 64, },
/* Descriptor 0x49 depends on CPU family/model, so it is not included */
[0x4A] = { .level = 3, .type = UNIFIED_CACHE, .size = 6 * MiB,
.associativity = 12, .line_size = 64, },
[0x4B] = { .level = 3, .type = UNIFIED_CACHE, .size = 8 * MiB,
.associativity = 16, .line_size = 64, },
[0x4C] = { .level = 3, .type = UNIFIED_CACHE, .size = 12 * MiB,
.associativity = 12, .line_size = 64, },
[0x4D] = { .level = 3, .type = UNIFIED_CACHE, .size = 16 * MiB,
.associativity = 16, .line_size = 64, },
[0x4E] = { .level = 2, .type = UNIFIED_CACHE, .size = 6 * MiB,
.associativity = 24, .line_size = 64, },
[0x60] = { .level = 1, .type = DCACHE, .size = 16 * KiB,
.associativity = 8, .line_size = 64, },
[0x66] = { .level = 1, .type = DCACHE, .size = 8 * KiB,
.associativity = 4, .line_size = 64, },
[0x67] = { .level = 1, .type = DCACHE, .size = 16 * KiB,
.associativity = 4, .line_size = 64, },
[0x68] = { .level = 1, .type = DCACHE, .size = 32 * KiB,
.associativity = 4, .line_size = 64, },
[0x78] = { .level = 2, .type = UNIFIED_CACHE, .size = 1 * MiB,
.associativity = 4, .line_size = 64, },
/* lines per sector is not supported cpuid2_cache_descriptor(),
* so descriptors 0x79, 0x7A, 0x7B, 0x7C are not included.
*/
[0x7D] = { .level = 2, .type = UNIFIED_CACHE, .size = 2 * MiB,
.associativity = 8, .line_size = 64, },
[0x7F] = { .level = 2, .type = UNIFIED_CACHE, .size = 512 * KiB,
.associativity = 2, .line_size = 64, },
[0x80] = { .level = 2, .type = UNIFIED_CACHE, .size = 512 * KiB,
.associativity = 8, .line_size = 64, },
[0x82] = { .level = 2, .type = UNIFIED_CACHE, .size = 256 * KiB,
.associativity = 8, .line_size = 32, },
[0x83] = { .level = 2, .type = UNIFIED_CACHE, .size = 512 * KiB,
.associativity = 8, .line_size = 32, },
[0x84] = { .level = 2, .type = UNIFIED_CACHE, .size = 1 * MiB,
.associativity = 8, .line_size = 32, },
[0x85] = { .level = 2, .type = UNIFIED_CACHE, .size = 2 * MiB,
.associativity = 8, .line_size = 32, },
[0x86] = { .level = 2, .type = UNIFIED_CACHE, .size = 512 * KiB,
.associativity = 4, .line_size = 64, },
[0x87] = { .level = 2, .type = UNIFIED_CACHE, .size = 1 * MiB,
.associativity = 8, .line_size = 64, },
[0xD0] = { .level = 3, .type = UNIFIED_CACHE, .size = 512 * KiB,
.associativity = 4, .line_size = 64, },
[0xD1] = { .level = 3, .type = UNIFIED_CACHE, .size = 1 * MiB,
.associativity = 4, .line_size = 64, },
[0xD2] = { .level = 3, .type = UNIFIED_CACHE, .size = 2 * MiB,
.associativity = 4, .line_size = 64, },
[0xD6] = { .level = 3, .type = UNIFIED_CACHE, .size = 1 * MiB,
.associativity = 8, .line_size = 64, },
[0xD7] = { .level = 3, .type = UNIFIED_CACHE, .size = 2 * MiB,
.associativity = 8, .line_size = 64, },
[0xD8] = { .level = 3, .type = UNIFIED_CACHE, .size = 4 * MiB,
.associativity = 8, .line_size = 64, },
[0xDC] = { .level = 3, .type = UNIFIED_CACHE, .size = 1.5 * MiB,
.associativity = 12, .line_size = 64, },
[0xDD] = { .level = 3, .type = UNIFIED_CACHE, .size = 3 * MiB,
.associativity = 12, .line_size = 64, },
[0xDE] = { .level = 3, .type = UNIFIED_CACHE, .size = 6 * MiB,
.associativity = 12, .line_size = 64, },
[0xE2] = { .level = 3, .type = UNIFIED_CACHE, .size = 2 * MiB,
.associativity = 16, .line_size = 64, },
[0xE3] = { .level = 3, .type = UNIFIED_CACHE, .size = 4 * MiB,
.associativity = 16, .line_size = 64, },
[0xE4] = { .level = 3, .type = UNIFIED_CACHE, .size = 8 * MiB,
.associativity = 16, .line_size = 64, },
[0xEA] = { .level = 3, .type = UNIFIED_CACHE, .size = 12 * MiB,
.associativity = 24, .line_size = 64, },
[0xEB] = { .level = 3, .type = UNIFIED_CACHE, .size = 18 * MiB,
.associativity = 24, .line_size = 64, },
[0xEC] = { .level = 3, .type = UNIFIED_CACHE, .size = 24 * MiB,
.associativity = 24, .line_size = 64, },
};
/*
* "CPUID leaf 2 does not report cache descriptor information,
* use CPUID leaf 4 to query cache parameters"
*/
#define CACHE_DESCRIPTOR_UNAVAILABLE 0xFF
/*
* Return a CPUID 2 cache descriptor for a given cache.
* If no known descriptor is found, return CACHE_DESCRIPTOR_UNAVAILABLE
*/
static uint8_t cpuid2_cache_descriptor(CPUCacheInfo *cache)
{
int i;
assert(cache->size > 0);
assert(cache->level > 0);
assert(cache->line_size > 0);
assert(cache->associativity > 0);
for (i = 0; i < ARRAY_SIZE(cpuid2_cache_descriptors); i++) {
struct CPUID2CacheDescriptorInfo *d = &cpuid2_cache_descriptors[i];
if (d->level == cache->level && d->type == cache->type &&
d->size == cache->size && d->line_size == cache->line_size &&
d->associativity == cache->associativity) {
return i;
}
}
return CACHE_DESCRIPTOR_UNAVAILABLE;
}
/* CPUID Leaf 4 constants: */ /* CPUID Leaf 4 constants: */
/* EAX: */ /* EAX: */
#define CPUID_4_TYPE_DCACHE 1 #define CACHE_TYPE_D 1
#define CPUID_4_TYPE_ICACHE 2 #define CACHE_TYPE_I 2
#define CPUID_4_TYPE_UNIFIED 3 #define CACHE_TYPE_UNIFIED 3
#define CPUID_4_LEVEL(l) ((l) << 5) #define CACHE_LEVEL(l) (l << 5)
#define CPUID_4_SELF_INIT_LEVEL (1 << 8) #define CACHE_SELF_INIT_LEVEL (1 << 8)
#define CPUID_4_FULLY_ASSOC (1 << 9)
/* EDX: */ /* EDX: */
#define CPUID_4_NO_INVD_SHARING (1 << 0) #define CACHE_NO_INVD_SHARING (1 << 0)
#define CPUID_4_INCLUSIVE (1 << 1) #define CACHE_INCLUSIVE (1 << 1)
#define CPUID_4_COMPLEX_IDX (1 << 2) #define CACHE_COMPLEX_IDX (1 << 2)
/* Encode CacheType for CPUID[4].EAX */
#define CACHE_TYPE(t) (((t) == DCACHE) ? CACHE_TYPE_D : \
((t) == ICACHE) ? CACHE_TYPE_I : \
((t) == UNIFIED_CACHE) ? CACHE_TYPE_UNIFIED : \
0 /* Invalid value */)
/* Encode cache info for CPUID[4] */
static void encode_cache_cpuid4(CPUCacheInfo *cache,
int num_apic_ids, int num_cores,
uint32_t *eax, uint32_t *ebx,
uint32_t *ecx, uint32_t *edx)
{
assert(cache->size == cache->line_size * cache->associativity *
cache->partitions * cache->sets);
assert(num_apic_ids > 0);
*eax = CACHE_TYPE(cache->type) |
CACHE_LEVEL(cache->level) |
(cache->self_init ? CACHE_SELF_INIT_LEVEL : 0) |
((num_cores - 1) << 26) |
((num_apic_ids - 1) << 14);
assert(cache->line_size > 0);
assert(cache->partitions > 0);
assert(cache->associativity > 0);
/* We don't implement fully-associative caches */
assert(cache->associativity < cache->sets);
*ebx = (cache->line_size - 1) |
((cache->partitions - 1) << 12) |
((cache->associativity - 1) << 22);
assert(cache->sets > 0);
*ecx = cache->sets - 1;
*edx = (cache->no_invd_sharing ? CACHE_NO_INVD_SHARING : 0) |
(cache->inclusive ? CACHE_INCLUSIVE : 0) |
(cache->complex_indexing ? CACHE_COMPLEX_IDX : 0);
}
/* Encode cache info for CPUID[0x80000005].ECX or CPUID[0x80000005].EDX */
static uint32_t encode_cache_cpuid80000005(CPUCacheInfo *cache)
{
assert(cache->size % 1024 == 0);
assert(cache->lines_per_tag > 0);
assert(cache->associativity > 0);
assert(cache->line_size > 0);
return ((cache->size / 1024) << 24) | (cache->associativity << 16) |
(cache->lines_per_tag << 8) | (cache->line_size);
}
#define ASSOC_FULL 0xFF #define ASSOC_FULL 0xFF
@ -100,57 +307,140 @@
a == ASSOC_FULL ? 0xF : \ a == ASSOC_FULL ? 0xF : \
0 /* invalid value */) 0 /* invalid value */)
/*
* Encode cache info for CPUID[0x80000006].ECX and CPUID[0x80000006].EDX
* @l3 can be NULL.
*/
static void encode_cache_cpuid80000006(CPUCacheInfo *l2,
CPUCacheInfo *l3,
uint32_t *ecx, uint32_t *edx)
{
assert(l2->size % 1024 == 0);
assert(l2->associativity > 0);
assert(l2->lines_per_tag > 0);
assert(l2->line_size > 0);
*ecx = ((l2->size / 1024) << 16) |
(AMD_ENC_ASSOC(l2->associativity) << 12) |
(l2->lines_per_tag << 8) | (l2->line_size);
if (l3) {
assert(l3->size % (512 * 1024) == 0);
assert(l3->associativity > 0);
assert(l3->lines_per_tag > 0);
assert(l3->line_size > 0);
*edx = ((l3->size / (512 * 1024)) << 18) |
(AMD_ENC_ASSOC(l3->associativity) << 12) |
(l3->lines_per_tag << 8) | (l3->line_size);
} else {
*edx = 0;
}
}
/* Definitions of the hardcoded cache entries we expose: */ /* Definitions of the hardcoded cache entries we expose: */
/* L1 data cache: */ /* L1 data cache: */
#define L1D_LINE_SIZE 64 static CPUCacheInfo l1d_cache = {
#define L1D_ASSOCIATIVITY 8 .type = DCACHE,
#define L1D_SETS 64 .level = 1,
#define L1D_PARTITIONS 1 .size = 32 * KiB,
/* Size = LINE_SIZE*ASSOCIATIVITY*SETS*PARTITIONS = 32KiB */ .self_init = 1,
#define L1D_DESCRIPTOR CPUID_2_L1D_32KB_8WAY_64B .line_size = 64,
.associativity = 8,
.sets = 64,
.partitions = 1,
.no_invd_sharing = true,
};
/*FIXME: CPUID leaf 0x80000005 is inconsistent with leaves 2 & 4 */ /*FIXME: CPUID leaf 0x80000005 is inconsistent with leaves 2 & 4 */
#define L1D_LINES_PER_TAG 1 static CPUCacheInfo l1d_cache_amd = {
#define L1D_SIZE_KB_AMD 64 .type = DCACHE,
#define L1D_ASSOCIATIVITY_AMD 2 .level = 1,
.size = 64 * KiB,
.self_init = 1,
.line_size = 64,
.associativity = 2,
.sets = 512,
.partitions = 1,
.lines_per_tag = 1,
.no_invd_sharing = true,
};
/* L1 instruction cache: */ /* L1 instruction cache: */
#define L1I_LINE_SIZE 64 static CPUCacheInfo l1i_cache = {
#define L1I_ASSOCIATIVITY 8 .type = ICACHE,
#define L1I_SETS 64 .level = 1,
#define L1I_PARTITIONS 1 .size = 32 * KiB,
/* Size = LINE_SIZE*ASSOCIATIVITY*SETS*PARTITIONS = 32KiB */ .self_init = 1,
#define L1I_DESCRIPTOR CPUID_2_L1I_32KB_8WAY_64B .line_size = 64,
.associativity = 8,
.sets = 64,
.partitions = 1,
.no_invd_sharing = true,
};
/*FIXME: CPUID leaf 0x80000005 is inconsistent with leaves 2 & 4 */ /*FIXME: CPUID leaf 0x80000005 is inconsistent with leaves 2 & 4 */
#define L1I_LINES_PER_TAG 1 static CPUCacheInfo l1i_cache_amd = {
#define L1I_SIZE_KB_AMD 64 .type = ICACHE,
#define L1I_ASSOCIATIVITY_AMD 2 .level = 1,
.size = 64 * KiB,
.self_init = 1,
.line_size = 64,
.associativity = 2,
.sets = 512,
.partitions = 1,
.lines_per_tag = 1,
.no_invd_sharing = true,
};
/* Level 2 unified cache: */ /* Level 2 unified cache: */
#define L2_LINE_SIZE 64 static CPUCacheInfo l2_cache = {
#define L2_ASSOCIATIVITY 16 .type = UNIFIED_CACHE,
#define L2_SETS 4096 .level = 2,
#define L2_PARTITIONS 1 .size = 4 * MiB,
/* Size = LINE_SIZE*ASSOCIATIVITY*SETS*PARTITIONS = 4MiB */ .self_init = 1,
.line_size = 64,
.associativity = 16,
.sets = 4096,
.partitions = 1,
.no_invd_sharing = true,
};
/*FIXME: CPUID leaf 2 descriptor is inconsistent with CPUID leaf 4 */ /*FIXME: CPUID leaf 2 descriptor is inconsistent with CPUID leaf 4 */
#define L2_DESCRIPTOR CPUID_2_L2_2MB_8WAY_64B static CPUCacheInfo l2_cache_cpuid2 = {
.type = UNIFIED_CACHE,
.level = 2,
.size = 2 * MiB,
.line_size = 64,
.associativity = 8,
};
/*FIXME: CPUID leaf 0x80000006 is inconsistent with leaves 2 & 4 */ /*FIXME: CPUID leaf 0x80000006 is inconsistent with leaves 2 & 4 */
#define L2_LINES_PER_TAG 1 static CPUCacheInfo l2_cache_amd = {
#define L2_SIZE_KB_AMD 512 .type = UNIFIED_CACHE,
.level = 2,
.size = 512 * KiB,
.line_size = 64,
.lines_per_tag = 1,
.associativity = 16,
.sets = 512,
.partitions = 1,
};
/* Level 3 unified cache: */ /* Level 3 unified cache: */
#define L3_SIZE_KB 0 /* disabled */ static CPUCacheInfo l3_cache = {
#define L3_ASSOCIATIVITY 0 /* disabled */ .type = UNIFIED_CACHE,
#define L3_LINES_PER_TAG 0 /* disabled */ .level = 3,
#define L3_LINE_SIZE 0 /* disabled */ .size = 16 * MiB,
#define L3_N_LINE_SIZE 64 .line_size = 64,
#define L3_N_ASSOCIATIVITY 16 .associativity = 16,
#define L3_N_SETS 16384 .sets = 16384,
#define L3_N_PARTITIONS 1 .partitions = 1,
#define L3_N_DESCRIPTOR CPUID_2_L3_16MB_16WAY_64B .lines_per_tag = 1,
#define L3_N_LINES_PER_TAG 1 .self_init = true,
#define L3_N_SIZE_KB_AMD 16384 .inclusive = true,
.complex_indexing = true,
};
/* TLB definitions: */ /* TLB definitions: */
@ -3344,85 +3634,53 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
if (!cpu->enable_l3_cache) { if (!cpu->enable_l3_cache) {
*ecx = 0; *ecx = 0;
} else { } else {
*ecx = L3_N_DESCRIPTOR; *ecx = cpuid2_cache_descriptor(&l3_cache);
} }
*edx = (L1D_DESCRIPTOR << 16) | \ *edx = (cpuid2_cache_descriptor(&l1d_cache) << 16) |
(L1I_DESCRIPTOR << 8) | \ (cpuid2_cache_descriptor(&l1i_cache) << 8) |
(L2_DESCRIPTOR); (cpuid2_cache_descriptor(&l2_cache_cpuid2));
break; break;
case 4: case 4:
/* cache info: needed for Core compatibility */ /* cache info: needed for Core compatibility */
if (cpu->cache_info_passthrough) { if (cpu->cache_info_passthrough) {
host_cpuid(index, count, eax, ebx, ecx, edx); host_cpuid(index, count, eax, ebx, ecx, edx);
/* QEMU gives out its own APIC IDs, never pass down bits 31..26. */
*eax &= ~0xFC000000; *eax &= ~0xFC000000;
if ((*eax & 31) && cs->nr_cores > 1) {
*eax |= (cs->nr_cores - 1) << 26;
}
} else { } else {
*eax = 0; *eax = 0;
switch (count) { switch (count) {
case 0: /* L1 dcache info */ case 0: /* L1 dcache info */
*eax |= CPUID_4_TYPE_DCACHE | \ encode_cache_cpuid4(&l1d_cache,
CPUID_4_LEVEL(1) | \ 1, cs->nr_cores,
CPUID_4_SELF_INIT_LEVEL; eax, ebx, ecx, edx);
*ebx = (L1D_LINE_SIZE - 1) | \
((L1D_PARTITIONS - 1) << 12) | \
((L1D_ASSOCIATIVITY - 1) << 22);
*ecx = L1D_SETS - 1;
*edx = CPUID_4_NO_INVD_SHARING;
break; break;
case 1: /* L1 icache info */ case 1: /* L1 icache info */
*eax |= CPUID_4_TYPE_ICACHE | \ encode_cache_cpuid4(&l1i_cache,
CPUID_4_LEVEL(1) | \ 1, cs->nr_cores,
CPUID_4_SELF_INIT_LEVEL; eax, ebx, ecx, edx);
*ebx = (L1I_LINE_SIZE - 1) | \
((L1I_PARTITIONS - 1) << 12) | \
((L1I_ASSOCIATIVITY - 1) << 22);
*ecx = L1I_SETS - 1;
*edx = CPUID_4_NO_INVD_SHARING;
break; break;
case 2: /* L2 cache info */ case 2: /* L2 cache info */
*eax |= CPUID_4_TYPE_UNIFIED | \ encode_cache_cpuid4(&l2_cache,
CPUID_4_LEVEL(2) | \ cs->nr_threads, cs->nr_cores,
CPUID_4_SELF_INIT_LEVEL; eax, ebx, ecx, edx);
if (cs->nr_threads > 1) {
*eax |= (cs->nr_threads - 1) << 14;
}
*ebx = (L2_LINE_SIZE - 1) | \
((L2_PARTITIONS - 1) << 12) | \
((L2_ASSOCIATIVITY - 1) << 22);
*ecx = L2_SETS - 1;
*edx = CPUID_4_NO_INVD_SHARING;
break; break;
case 3: /* L3 cache info */ case 3: /* L3 cache info */
if (!cpu->enable_l3_cache) { pkg_offset = apicid_pkg_offset(cs->nr_cores, cs->nr_threads);
*eax = 0; if (cpu->enable_l3_cache) {
*ebx = 0; encode_cache_cpuid4(&l3_cache,
*ecx = 0; (1 << pkg_offset), cs->nr_cores,
*edx = 0; eax, ebx, ecx, edx);
break; break;
} }
*eax |= CPUID_4_TYPE_UNIFIED | \ /* fall through */
CPUID_4_LEVEL(3) | \
CPUID_4_SELF_INIT_LEVEL;
pkg_offset = apicid_pkg_offset(cs->nr_cores, cs->nr_threads);
*eax |= ((1 << pkg_offset) - 1) << 14;
*ebx = (L3_N_LINE_SIZE - 1) | \
((L3_N_PARTITIONS - 1) << 12) | \
((L3_N_ASSOCIATIVITY - 1) << 22);
*ecx = L3_N_SETS - 1;
*edx = CPUID_4_INCLUSIVE | CPUID_4_COMPLEX_IDX;
break;
default: /* end of info */ default: /* end of info */
*eax = 0; *eax = *ebx = *ecx = *edx = 0;
*ebx = 0;
*ecx = 0;
*edx = 0;
break; break;
} }
} }
/* QEMU gives out its own APIC IDs, never pass down bits 31..26. */
if ((*eax & 31) && cs->nr_cores > 1) {
*eax |= (cs->nr_cores - 1) << 26;
}
break; break;
case 5: case 5:
/* mwait info: needed for Core compatibility */ /* mwait info: needed for Core compatibility */
@ -3626,10 +3884,8 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
(L1_ITLB_2M_ASSOC << 8) | (L1_ITLB_2M_ENTRIES); (L1_ITLB_2M_ASSOC << 8) | (L1_ITLB_2M_ENTRIES);
*ebx = (L1_DTLB_4K_ASSOC << 24) | (L1_DTLB_4K_ENTRIES << 16) | \ *ebx = (L1_DTLB_4K_ASSOC << 24) | (L1_DTLB_4K_ENTRIES << 16) | \
(L1_ITLB_4K_ASSOC << 8) | (L1_ITLB_4K_ENTRIES); (L1_ITLB_4K_ASSOC << 8) | (L1_ITLB_4K_ENTRIES);
*ecx = (L1D_SIZE_KB_AMD << 24) | (L1D_ASSOCIATIVITY_AMD << 16) | \ *ecx = encode_cache_cpuid80000005(&l1d_cache_amd);
(L1D_LINES_PER_TAG << 8) | (L1D_LINE_SIZE); *edx = encode_cache_cpuid80000005(&l1i_cache_amd);
*edx = (L1I_SIZE_KB_AMD << 24) | (L1I_ASSOCIATIVITY_AMD << 16) | \
(L1I_LINES_PER_TAG << 8) | (L1I_LINE_SIZE);
break; break;
case 0x80000006: case 0x80000006:
/* cache info (L2 cache) */ /* cache info (L2 cache) */
@ -3645,18 +3901,9 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
(L2_DTLB_4K_ENTRIES << 16) | \ (L2_DTLB_4K_ENTRIES << 16) | \
(AMD_ENC_ASSOC(L2_ITLB_4K_ASSOC) << 12) | \ (AMD_ENC_ASSOC(L2_ITLB_4K_ASSOC) << 12) | \
(L2_ITLB_4K_ENTRIES); (L2_ITLB_4K_ENTRIES);
*ecx = (L2_SIZE_KB_AMD << 16) | \ encode_cache_cpuid80000006(&l2_cache_amd,
(AMD_ENC_ASSOC(L2_ASSOCIATIVITY) << 12) | \ cpu->enable_l3_cache ? &l3_cache : NULL,
(L2_LINES_PER_TAG << 8) | (L2_LINE_SIZE); ecx, edx);
if (!cpu->enable_l3_cache) {
*edx = ((L3_SIZE_KB / 512) << 18) | \
(AMD_ENC_ASSOC(L3_ASSOCIATIVITY) << 12) | \
(L3_LINES_PER_TAG << 8) | (L3_LINE_SIZE);
} else {
*edx = ((L3_N_SIZE_KB_AMD / 512) << 18) | \
(AMD_ENC_ASSOC(L3_N_ASSOCIATIVITY) << 12) | \
(L3_N_LINES_PER_TAG << 8) | (L3_N_LINE_SIZE);
}
break; break;
case 0x80000007: case 0x80000007:
*eax = 0; *eax = 0;

View File

@ -1045,6 +1045,59 @@ typedef enum TPRAccess {
TPR_ACCESS_WRITE, TPR_ACCESS_WRITE,
} TPRAccess; } TPRAccess;
/* Cache information data structures: */
enum CacheType {
DCACHE,
ICACHE,
UNIFIED_CACHE
};
typedef struct CPUCacheInfo {
enum CacheType type;
uint8_t level;
/* Size in bytes */
uint32_t size;
/* Line size, in bytes */
uint16_t line_size;
/*
* Associativity.
* Note: representation of fully-associative caches is not implemented
*/
uint8_t associativity;
/* Physical line partitions. CPUID[0x8000001D].EBX, CPUID[4].EBX */
uint8_t partitions;
/* Number of sets. CPUID[0x8000001D].ECX, CPUID[4].ECX */
uint32_t sets;
/*
* Lines per tag.
* AMD-specific: CPUID[0x80000005], CPUID[0x80000006].
* (Is this synonym to @partitions?)
*/
uint8_t lines_per_tag;
/* Self-initializing cache */
bool self_init;
/*
* WBINVD/INVD is not guaranteed to act upon lower level caches of
* non-originating threads sharing this cache.
* CPUID[4].EDX[bit 0], CPUID[0x8000001D].EDX[bit 0]
*/
bool no_invd_sharing;
/*
* Cache is inclusive of lower cache levels.
* CPUID[4].EDX[bit 1], CPUID[0x8000001D].EDX[bit 1].
*/
bool inclusive;
/*
* A complex function is used to index the cache, potentially using all
* address bits. CPUID[4].EDX[bit 2].
*/
bool complex_indexing;
} CPUCacheInfo;
typedef struct CPUX86State { typedef struct CPUX86State {
/* standard registers */ /* standard registers */
target_ulong regs[CPU_NB_REGS]; target_ulong regs[CPU_NB_REGS];